| Overall Statistics |
|
Total Trades 114 Average Win 2.31% Average Loss -1.12% Compounding Annual Return -4.016% Drawdown 36.900% Expectancy -0.050 Net Profit -6.474% Sharpe Ratio -0.003 Probabilistic Sharpe Ratio 7.942% Loss Rate 69% Win Rate 31% Profit-Loss Ratio 2.07 Alpha -0.035 Beta 0.153 Annual Standard Deviation 0.258 Annual Variance 0.066 Information Ratio -0.67 Tracking Error 0.331 Treynor Ratio -0.006 Total Fees $162.22 Estimated Strategy Capacity $670000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X |
import datetime
class SuperTrendTester(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2020, 1, 1) # Start Date
#self.SetEndDate(2021, 1, 15) # End Date
self.SetCash(100000) # Set Strategy Cash
self.symbol = 'SPY'
self.res = Resolution.Daily
self.length = 14
self.multiplier = 1
self.SetWarmUp(timedelta(days=30))
self.equity = self.AddEquity(self.symbol, self.res)
self.equity.SetDataNormalizationMode(DataNormalizationMode.Raw)
self.closeWindow = []
self.highWindow = []
self.lowWindow = []
self.atrWindow = []
self.atrDown = []
self.atrUp = []
self.ha = self.HeikinAshi(self.symbol, self.res)
self.atr = self.ATR(self.symbol, self.length, self.res)
consolidator = TradeBarConsolidator(timedelta(minutes=1))
consolidator.DataConsolidated += self.OnDataConsolidated
self.SubscriptionManager.AddConsolidator(self.symbol, consolidator)
self.optres = Resolution.Minute
self.optmaxday = 30
self.optminday = 5
self.optminprice = 1
self.optmaxprice = 1000
self.putsheld = False
self.callsheld = False
self.noneheld = True
def SellPuts(self):
contracts = self.OptionChainProvider.GetOptionContractList(self.symbol, self.Time.date())
if len(contracts) == 0: return
filtered_contracts = self.InitialFilter(self.symbol, contracts, self.optminprice, self.optmaxprice, self.optminday, self.optmaxday)
put = [x for x in filtered_contracts if x.ID.OptionRight == 1]
# sorted the contracts according to their expiration dates and choose the ATM options
contracts = sorted(sorted(put, key = lambda x: abs((self.Securities[self.symbol].Price*0.9) - x.ID.StrikePrice)),
key = lambda x: x.ID.Date, reverse=True)
self.contract = contracts[0]
self.AddOptionContract(self.contract, self.optres)
self.Debug(str(self.Time) + str(self.contract) + "Put")
self.Buy(self.contract, 1)
def SellCalls(self):
contracts = self.OptionChainProvider.GetOptionContractList(self.symbol, self.Time.date())
if len(contracts) == 0: return
filtered_contracts = self.InitialFilter(self.symbol, contracts, self.optminprice, self.optmaxprice, self.optminday, self.optmaxday)
call = [x for x in filtered_contracts if x.ID.OptionRight == 0]
# sorted the contracts according to their expiration dates and choose the ATM options
contracts = sorted(sorted(call, key = lambda x: abs((self.Securities[self.symbol].Price*1.1) - x.ID.StrikePrice)),
key = lambda x: x.ID.Date, reverse=True)
self.contract = contracts[1]
self.AddOptionContract(self.contract, self.optres)
self.Debug(str(self.Time) + str(self.contract) + "Call")
self.Buy(self.contract, 1)
def BuyPuts(self):
contracts = self.OptionChainProvider.GetOptionContractList(self.symbol, self.Time.date())
if len(contracts) == 0: return
filtered_contracts = self.InitialFilter(self.symbol, contracts, self.optminprice, self.optmaxprice, self.optminday, self.optmaxday)
put = [x for x in filtered_contracts if x.ID.OptionRight == 1]
# sorted the contracts according to their expiration dates and choose the ATM options
contracts = sorted(sorted(put, key = lambda x: abs((self.Securities[self.symbol].Price*0.95) - x.ID.StrikePrice)),
key = lambda x: x.ID.Date, reverse=True)
self.contract = contracts[0]
self.AddOptionContract(self.contract, self.optres)
self.Debug(str(self.Time) + str(self.contract) + "Put")
self.Buy(self.contract, 1)
def BuyCalls(self):
contracts = self.OptionChainProvider.GetOptionContractList(self.symbol, self.Time.date())
if len(contracts) == 0: return
filtered_contracts = self.InitialFilter(self.symbol, contracts, self.optminprice, self.optmaxprice, self.optminday, self.optmaxday)
call = [x for x in filtered_contracts if x.ID.OptionRight == 0]
# sorted the contracts according to their expiration dates and choose the ATM options
contracts = sorted(sorted(call, key = lambda x: abs((self.Securities[self.symbol].Price*1.05) - x.ID.StrikePrice)),
key = lambda x: x.ID.Date, reverse=True)
self.contract = contracts[1]
self.AddOptionContract(self.contract, self.optres)
self.Debug(str(self.Time) + str(self.contract) + "Call")
self.Buy(self.contract, 1)
def InitialFilter(self, underlyingsymbol, symbol_list, min_strike_rank, max_strike_rank, min_expiry, max_expiry):
if len(symbol_list) == 0 : return
# fitler the contracts based on the expiry range
contract_list = [i for i in symbol_list if min_expiry < (i.ID.Date.date() - self.Time.date()).days < max_expiry]
# find the strike price of ATM option
atm_strike = sorted(contract_list,
key = lambda x: abs(x.ID.StrikePrice - self.Securities[underlyingsymbol].Price))[0].ID.StrikePrice
strike_list = sorted(set([i.ID.StrikePrice for i in contract_list]))
# find the index of ATM strike in the sorted strike list
atm_strike_rank = strike_list.index(atm_strike)
try:
min_strike = strike_list[atm_strike_rank + min_strike_rank]
max_strike = strike_list[atm_strike_rank + max_strike_rank]
except:
min_strike = strike_list[0]
max_strike = strike_list[-1]
filtered_contracts = [i for i in contract_list if i.ID.StrikePrice >= min_strike and i.ID.StrikePrice <= max_strike]
return filtered_contracts
def OnDataConsolidated(self, sender, data):
if self.IsWarmingUp: return
if not self.ha.IsReady: return
if not self.atr.IsReady: return
self.closeWindow.insert(0,self.ha.Close.Current.Value)
if len(self.closeWindow) > 2:
self.closeWindow.pop()
self.highWindow.insert(0,self.ha.High.Current.Value)
if len(self.highWindow) > 2:
self.highWindow.pop()
self.lowWindow.insert(0,self.ha.Low.Current.Value)
if len(self.lowWindow) > 2:
self.lowWindow.pop()
self.atrWindow.insert(0,self.atr.Current.Value)
if len(self.atrWindow) > 2:
self.atrWindow.pop()
self.Value = None
try:
hl = (self.highWindow[0] + self.lowWindow[0]) / 2
hltwo = (self.highWindow[0] + self.lowWindow[0]) / 2
hltwoPrev = (self.highWindow[1] + self.lowWindow[1]) / 2
downNow = hltwo - self.multiplier * self.atrWindow[0]
downPrev = hltwoPrev - self.multiplier * self.atrWindow[1]
atrDown = max(downPrev, downNow) if self.closeWindow[1] > downPrev else downNow
self.atrDown.insert(0,atrDown)
upNow = hltwo + self.multiplier * self.atrWindow[0]
upPrev = hltwoPrev + self.multiplier * self.atrWindow[1]
atrUp = min(upNow, upPrev) if self.closeWindow[1] < upPrev else upNow
self.atrUp.insert(0,atrUp)
except:
return
try:
if self.closeWindow[0] > self.atrUp[1]:
self.Value = self.atrDown[0]
elif self.closeWindow[0] < self.atrDown[1]:
self.Value = self.atrUp[0]
else:
pass
if self.Securities[self.symbol].Price < self.Value:
self.Liquidate
self.SetHoldings(self.symbol, -1)
self.SellCalls()
self.BuyPuts()
if self.Securities[self.symbol].Price > self.Value:
self.Liquidate
self.SetHoldings(self.symbol, 1)
self.SellPuts()
self.BuyCalls()
except:
return