| Overall Statistics |
|
Total Trades 13 Average Win 213.17% Average Loss -4.54% Compounding Annual Return 23.515% Drawdown 75.800% Expectancy 6.989 Net Profit 137.172% Sharpe Ratio 0.564 Probabilistic Sharpe Ratio 5.924% Loss Rate 83% Win Rate 17% Profit-Loss Ratio 46.93 Alpha 0.532 Beta -1.314 Annual Standard Deviation 0.629 Annual Variance 0.396 Information Ratio 0.311 Tracking Error 0.709 Treynor Ratio -0.27 Total Fees $1562.75 Estimated Strategy Capacity $120000.00 Lowest Capacity Asset VIX XUJS4Z3KE3GU|VIX 31 |
from datetime import timedelta, date
from talib import MACD, EMA
from collections import deque
from numpy import sum
import numpy as np
from scipy import stats
class SmoothBlueBat(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2017, 11, 15)
#self.SetEndDate(2020, 5, 1)
self.SetCash(100000)
self.last_date = datetime(2000, 1, 1) # work around to find options
self.mode = 1 # sell with MACD
#self.mode = 2 # sell with preset limit order
self.lookback = 150
self.SetSecurityInitializer(self.security_initializer)
self.underly = self.AddIndex('VIX', Resolution.Daily).Symbol
self.vix = self.AddData( CBOE, "VIX", Resolution.Daily).Symbol
self.macd = self.MACD(self.vix, 12, 26, 9, MovingAverageType.Exponential, Resolution.Daily)
self.ordered = False
self.contract = str()
self.contract_without_limit_order = str()
self.stop_buy=False
self.options_percentage = 0.05 # percentage of options
self.SetWarmUp(timedelta(self.lookback))
self.Schedule.On(self.DateRules.EveryDay(self.underly), \
self.TimeRules.BeforeMarketClose(self.underly, 30), \
self.Plotting)
def security_initializer(self, security):
if security.Type == SecurityType.Equity:
security.SetDataNormalizationMode(DataNormalizationMode.Raw)
def OnData(self, data):
if(self.IsWarmingUp):
return
# decide when to buy
if self.Securities[self.vix].Close > 25 and self.Securities[self.vix].Close < 30:
for kvp in self.Portfolio:
if self.Securities[kvp.Key].Invested:
symbol = kvp.Key
id = symbol.ID
security_type = id.SecurityType
if security_type == 10:#SecurityType.Option:
if self.last_date < id.Date:
self.last_date = id.Date
if (self.last_date - self.Time) < timedelta(10): # x days overlap of options
if not self.stop_buy:
self.Buy_Option( data)
# decide when to sell
if self.mode == 1:
if (self.Securities[self.vix].Close > 25) and (self.macd.Current.Value < self.macd.Signal.Current.Value):
self.stop_buy=True
for kvp in self.Portfolio:
if self.Securities[kvp.Key].Invested:
symbol = kvp.Key
id = symbol.ID
security_type = id.SecurityType
if security_type == 10: #SecurityType.Option for VIX Calls
self.Liquidate(symbol)
else:
self.stop_buy=False
if self.mode == 2:
# set a limit order
if self.contract_without_limit_order:
self.Set_limit_order(data)
def Buy_Option(self, data):
if not (data.ContainsKey(self.underly) and data[self.underly] is not None):
return
if not self.contract:
#min_strike = data[self.underly].Price + 10
#max_strike = data[self.underly].Price + 15
min_strike = 70
max_strike = 120
min_expiry = self.Time + timedelta(days=75) # 60 around 3 month
max_expiry = self.Time + timedelta(days=95)
#sorted_function = lambda x: x.BidSize
sorted_function = lambda x: x.StrikePrice
direction = True
self.contract = self.GetOption(data, self.underly, OptionRight.Call, min_strike, max_strike, min_expiry, max_expiry, sorted_function, direction)
if not self.contract:
return
if data.ContainsKey(self.contract.Symbol):
contract_value = self.Securities[self.contract.Symbol].AskPrice
if contract_value > 0:
quantity = self.CalculateOrderQuantity(self.contract.Symbol, self.options_percentage)
self.MarketOrder(self.contract.Symbol, quantity)
self.contract_without_limit_order = self.contract
self.contract = str()
self.ordered = True
def Set_limit_order(self, data):
if not self.contract_without_limit_order:
return
number_cont = self.Portfolio[self.contract_without_limit_order.Symbol].Quantity
cost_base = self.Portfolio[self.contract_without_limit_order.Symbol].AveragePrice
self.LimitOrder(self.contract_without_limit_order.Symbol, - number_cont * 1/3, cost_base*1.5)
self.LimitOrder(self.contract_without_limit_order.Symbol, - number_cont * 1/3, cost_base*4)
self.LimitOrder(self.contract_without_limit_order.Symbol, - number_cont * 1/3, cost_base*8)
self.contract_without_limit_order = str()
def GetOption(self,data,underlying,optionright,MinStrike,\
MaxStrike,MinExpiry,MaxExpiry,sortedfunction,reverse):
## Get list of Options Contracts for a specific time
chainProvider = self.OptionChainProvider
contracts = chainProvider.GetOptionContractList(underlying, self.Time)
if len(contracts) == 0: return
filtered_options = [x for x in contracts if x.ID.OptionRight == optionright and\
x.ID.StrikePrice > MinStrike and\
x.ID.StrikePrice < MaxStrike and\
x.ID.Date > MinExpiry and\
x.ID.Date < MaxExpiry]
if len(filtered_options) == 0:
return
added_contracts = []
for x in filtered_options:
option = self.AddOptionContract(x, Resolution.Minute)
optionsymbol = self.Securities[option.Symbol]
if data.ContainsKey(optionsymbol.Symbol):
added_contracts.append(optionsymbol)
sorted_options=sorted(added_contracts,key=sortedfunction,reverse=reverse)
if len(sorted_options) > 0:
selected = sorted_options[0]
else:
selected = None
return selected
def Plotting(self):
# plot underlying's price
self.Plot("Data Chart VIX", self.vix, self.Securities[self.underly].Close)
# plot macd
self.Plot("Macd Signal", "Value", self.macd.Current.Value)
self.Plot("Macd Signal", "Signal", self.macd.Signal.Current.Value)
for kvp in self.Portfolio:
symbol = kvp.Key
id = symbol.ID
security_type = id.SecurityType
if security_type == 10: #SecurityType.Option:
if self.Securities[symbol].Invested:
self.Plot("Options Price", symbol, self.Securities[symbol].Price)
option_invested = [x.Key for x in self.Portfolio if x.Value.Invested and x.Value.Type== 10]
if option_invested:
self.Plot("Data Chart invested", "strike", option_invested[0].ID.StrikePrice)