Overall Statistics |
Total Trades 23 Average Win 1.81% Average Loss -1.96% Compounding Annual Return 86.486% Drawdown 26.900% Expectancy 0.202 Net Profit 16.535% Sharpe Ratio 1.431 Probabilistic Sharpe Ratio 51.557% Loss Rate 38% Win Rate 62% Profit-Loss Ratio 0.92 Alpha 0.583 Beta -0.932 Annual Standard Deviation 0.538 Annual Variance 0.29 Information Ratio 0.972 Tracking Error 1 Treynor Ratio -0.827 Total Fees $29.55 Estimated Strategy Capacity $13000000.00 Lowest Capacity Asset TQQQ 31KBXAXD64W06|TQQQ UK280CGTCB51 |
from typing import Dict from datetime import timedelta from AlgorithmImports import * class Centurion(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 2, 2) # Set Start Date self.SetEndDate(2020, 5, 1) # Set Start Date self.SetCash(100000) # Set Strategy Cash # self.AddEquity("SPY", Resolution.Minute) ''' Would it be easier to have a symboldata class? What would it contain? - initialization of Data - dictionaries for stoploss, option types, rebalance weights ''' # Assets===================================== self.bndx = self.AddEquity("BNDX", Resolution.Minute).Symbol self.bnd = self.AddEquity("BND", Resolution.Minute).Symbol spy = self.AddEquity("SPY", Resolution.Minute) spy.SetDataNormalizationMode(DataNormalizationMode.Raw) self.spy = spy.Symbol self.spycontract: Symbol = None tqqq = self.AddEquity("TQQQ", Resolution.Minute) tqqq.SetDataNormalizationMode(DataNormalizationMode.Raw) self.tqqq = tqqq.Symbol self.tqqqcontract: Symbol = None soxl = self.AddEquity("SOXL", Resolution.Minute) soxl.SetDataNormalizationMode(DataNormalizationMode.Raw) self.soxl = soxl.Symbol self.soxlcontract: Symbol = None tecl = self.AddEquity("TECL", Resolution.Minute) tecl.SetDataNormalizationMode(DataNormalizationMode.Raw) self.tecl = tecl.Symbol self.teclcontract: Symbol = None #Scheduled Events============================================ self.Schedule.On(self.DateRules.MonthStart("SPY"), self.TimeRules.AfterMarketOpen("SPY", 1), self.monthlyRebalance) self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.AfterMarketOpen("SPY", 1), self.captureOpening) #Variables needed for stoploss self.stoplosshold = 0 self.dailyOpen: Dict[Symbol,float] = { self.spy: 0, self.bndx:0, self.bnd:0 } # Rebalancing Weights self.weights = { self.spy: .2, self.bndx: .3, self.bnd: .2 } # Underlying & Options (UNOs) self.uNos = { self.spy : self.spycontract, self.tqqq : self.tqqqcontract, self.tecl : self.teclcontract } def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the stock data ''' if self.IsWarmingUp: return if self.Portfolio.Invested: self.stoploss(data) if not self.Portfolio.Invested: self.monthlyRebalance() self.setPuts() self.timeDecayCheck() self.buyPuts() self.exerciseLoki() def setPuts(self): ''' I want to purchase a long puts for every key in dictionary uNos ((u)underlying (N)and (os)Options) ''' for underlying in self.uNos.keys(): if self.uNos[underlying]: continue targetStrike = (self.Securities[underlying].Price * 0.60) - (self.Securities[underlying].Price * 0.60)%5 contracts = self.OptionChainProvider.GetOptionContractList(underlying, self.Time) puts = [x for x in contracts if x.ID.OptionRight == OptionRight.Put] puts = sorted( sorted(puts, key = lambda x: x.ID.Date, reverse = True), key = lambda x: x.ID.StrikePrice) puts = [x for x in puts if x.ID.StrikePrice == targetStrike] puts = [x for x in puts if 270 < (x.ID.Date - self.Time).days <= 420] if len(puts) == 0: puts = sorted(puts, key = lambda x: x.ID.Date, reverse=True) self.Log("No Puts") continue opt = self.AddOptionContract(puts[0], Resolution.Minute).Symbol self.uNos[underlying] = opt def timeDecayCheck(self): #Don't let an options time to expiration be less than 6 months for underlying, contract in self.uNos.items(): if contract is None: continue if (contract.ID.Date - self.Time).days < 180: self.Liquidate(contract) self.RemoveSecurity(contract) self.uNos[contract] = None def buyPuts(self): for _,contract in self.uNos.items(): if contract is None: continue if not self.Portfolio[contract].Invested: self.SetHoldings(contract, 0.02) def exerciseLoki(self): for underlying,contract in self.uNos.items(): if contract is None: continue #Liquidate a contract if its increases to 30% OTM else: if self.Securities[underlying].Price < contract.ID.StrikePrice * 1: # self.ExerciseOption(contract, self.Portfolio[contract].Quantity) self.Liquidate(contract) self.RemoveSecurity(contract) self.uNos[underlying] = None self.Log(f'{contract} has been sold!') x= 0 def captureOpening(self): #Grabs the daily opening price of spy for our stoploss method if self.IsWarmingUp: return for key, value in self.dailyOpen.items(): if self.CurrentSlice.Bars.ContainsKey(key): self.dailyOpen[key] = self.CurrentSlice[key].Open self.stoplosshold = 0 def monthlyRebalance(self): # Rebalance portfolio monthly if self.IsWarmingUp: return for key,value in self.weights.items(): self.SetHoldings(key,value) def stoploss(self, data): ''' Stoploss logic: - If spy drops more than 5% liquidate entire equity portfolio - Change stoplosshold value to 1, this indicates that the portfolios SL has been hit and were going to hold until the next trading day ''' if self.IsWarmingUp: return for symbol, weight in self.weights.items(): if self.Securities[symbol].Price == 0: continue open = self.dailyOpen[symbol] #See if any symbol has decreased more than 5% in a given day, liquidate if true and check next one... if ((self.Securities[symbol].Price-open)/self.Securities[symbol].Price) < -.05: self.SetHoldings(symbol, 0) self.stoplosshold = 1 #self.Log('HIT') self.buyPuts() #self.exerciseLoki()