Overall Statistics |
Total Trades 628 Average Win 0.03% Average Loss -0.07% Compounding Annual Return -17.049% Drawdown 18.300% Expectancy -0.954 Net Profit -18.300% Sharpe Ratio -8.394 Probabilistic Sharpe Ratio 0% Loss Rate 97% Win Rate 3% Profit-Loss Ratio 0.45 Alpha -0.121 Beta -0.003 Annual Standard Deviation 0.014 Annual Variance 0 Information Ratio -0.992 Tracking Error 0.271 Treynor Ratio 40.207 Total Fees $628.00 Estimated Strategy Capacity $0 Lowest Capacity Asset GME 31LHA2VKCT7EU|GME SC72NCBXXAHX |
import pandas as pd import numpy as np from io import StringIO import json import pickle from datetime import datetime from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel ''' Would it be beneficial to run a universe selection every day based on which stocks have a trading halt? Yes, it increases spead 10 fold ''' class TradingHalt(QCAlgorithm): def Initialize(self): self.SetStartDate(2020, 1, 1) self.SetEndDate(2021, 1, 30) self.SetCash(100000) serializedModel = bytes( self.ObjectStore.ReadBytes("HaltModel") ) self.haltModel = pickle.loads(serializedModel) self.activeSymbols = {} self.symbolDataBySymbol = {} self.tickersT = [] self.tickers = [] # self.Resolution = Resolution.Hour # self.UniverseSettings.Resolution = self.Resolution # self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction)) self.spy = self.AddEquity("SPY", Resolution.Minute).Symbol self.Schedule.On(self.DateRules.EveryDay("SPY"), self.TimeRules.BeforeMarketClose("SPY", 10), self.SellAll) self.todaysHalts = pd.DataFrame() self.prepareTrading = [] self.maxPositions = 10 self.tickers = ["GME"] for ticker in self.tickers: self.AddEquity(ticker, Resolution.Minute) self.lastHour = -1 self.options = {} self.gme = None self.count = 0 def TakeProfit(self): activePositions = [x.Key for x in self.Portfolio if x.Value.Invested] for symbol in activePositions: if self.Portfolio.UnrealizedProfitPercent >= 0.05: self.Liquidate() def OnData(self, slice): if self.Portfolio.TotalUnrealizedProfit >= (self.Portfolio.TotalPortfolioValue*0.01): self.Debug("Profit") self.Liquidate() if self.Portfolio["GME"].Quantity != 0: self.Debug("Liquidating") self.Liquidate() if self.Time.day == self.lastHour: return self.lastHour = self.Time.day for symbol, symbolData in self.symbolDataBySymbol.items(): if not slice.ContainsKey(symbol): continue df = symbolData.Structured() prediction = self.haltModel.predict(df) # if prediction == -1: # if self.count < 0: # self.count = 0 # self.count += 1 # elif prediction == 1: # if self.count > 0: # self.count = 0 # self.count -= 1 # if self.count >= 3 and not self.Portfolio.Invested: if prediction == -1 and not self.Portfolio.Invested: self.Debug(df) self.Debug(self.Time) self.Debug(prediction) #self.SetHoldings(symbol, 0.05) self.TradeOptions(slice) # if self.count <= -3: # self.Liquidate() def TradeOptions(self,slice): # If there is undelying assets in portfolio at expiration, liquidate the stocks in order to roll into new contracts if self.Portfolio["GME"].Quantity != 0: self.Debug("Liquidating 2") self.Liquidate() if not self.Portfolio.Invested and self.Time.hour != 0 and self.Time.minute != 0: for i in slice.OptionChains: chain = i.Value contract_list = [x for x in chain] # if there is no optionchain or no contracts in this optionchain, pass the instance if (slice.OptionChains.Count == 0) or (len(contract_list) == 0): return # sorted the optionchain by expiration date and choose the furthest date expiry = sorted(chain,key = lambda x: x.Expiry)[-1].Expiry # filter the call and put options from the contracts call = [i for i in chain if i.Expiry == expiry and i.Right == 0] put = [i for i in chain if i.Expiry == expiry and i.Right == 1] # sorted the contracts according to their strike prices call_contracts = sorted(call,key = lambda x: x.Strike) put_contracts = sorted(put,key = lambda x: x.Strike) if len(call_contracts) == 0 or len(put_contracts) == 0 : continue # otm_put_lower = put_contracts[3] # otm_put = put_contracts[1] # otm_call = call_contracts[-1] # otm_call_higher = call_contracts[-7] # self.trade_contracts = [otm_call.Symbol,otm_call_higher.Symbol,otm_put.Symbol,otm_put_lower.Symbol] atm_call = sorted(call_contracts,key = lambda x: abs(chain.Underlying.Price - x.Strike))[0] atm_put = sorted(put_contracts,key = lambda x: abs(chain.Underlying.Price - x.Strike))[0] self.Buy(atm_call.Symbol ,1) self.Buy(atm_put.Symbol ,1) self.Debug(f"{atm_call.Expiry} {atm_put.Expiry}") # if there is no securities in portfolio, trade the options # self.Sell(otm_put_lower.Symbol ,1) # self.Buy(otm_put.Symbol ,1) # self.Buy(otm_call.Symbol ,1) # self.Sell(otm_call_higher.Symbol ,1) # self.Debug(f"{otm_put_lower.Expiry} {otm_put.Expiry} {otm_call.Expiry} {otm_call_higher.Expiry}") def TradeButter(self, slice): if self.Portfolio["GME"].Quantity != 0: self.Debug("Liquidating 2") self.Liquidate() if not self.Portfolio.Invested and self.Time.hour != 0 and self.Time.minute != 0: for i in slice.OptionChains: #if i.Key != self.symbol: continue chain = i.Value # sorted the optionchain by expiration date and choose the furthest date expiry = sorted(chain,key = lambda x: x.Expiry, reverse=True)[0].Expiry # filter the call options from the contracts expires on that date call = [i for i in chain if i.Expiry == expiry and i.Right == 0] # sorted the contracts according to their strike prices call_contracts = sorted(call,key = lambda x: x.Strike) if len(call_contracts) == 0: continue # choose OTM call self.otm_call = call_contracts[-1] # choose ITM call self.itm_call = call_contracts[0] # choose ATM call self.atm_call = sorted(call_contracts,key = lambda x: abs(chain.Underlying.Price - x.Strike))[0] self.Sell(self.atm_call.Symbol ,2) self.Buy(self.itm_call.Symbol ,1) self.Buy(self.otm_call.Symbol ,1) def SellAll(self): self.Liquidate() def HourBarHandler(self, consolidated): self.symbolDataBySymbol[self.gme].UpdateVol(consolidated) def OnSecuritiesChanged(self, changes): for x in changes.AddedSecurities: if x.Type==SecurityType.Option: continue symbol = x.Symbol if symbol == self.spy: continue self.gme = symbol self.Securities[symbol].SetSlippageModel(CustomSlippageModel(self)) self.Securities[symbol].SetDataNormalizationMode(DataNormalizationMode.Raw) self.activeSymbols[symbol.Value] = symbol symbolData = SymbolData(self, symbol) self.symbolDataBySymbol[symbol] = symbolData option = self.AddOption(symbol) option.SetFilter(self.UniverseFunc) self.Consolidate(symbol, Resolution.Daily, self.HourBarHandler) for x in changes.RemovedSecurities: symbol = x.Symbol if x.Type==SecurityType.Option: continue self.Liquidate(symbol) self.activeSymbols.pop(symbol.Value, None) if symbol in self.symbolDataBySymbol: self.symbolDataBySymbol.pop(symbol, None) def UniverseFunc(self, universe): #-15 15 #-9 9 return universe.IncludeWeeklys().Strikes(-5, 5).Expiration(timedelta(0), timedelta(60)) class CustomSlippageModel: def __init__(self, algorithm): self.algorithm = algorithm def GetSlippageApproximation(self, asset, order): slippage = asset.Price * float(0.0001 * np.log10(2*float(order.AbsoluteQuantity))) return slippage class SymbolData: def __init__(self, algo, symbol): self.Symbol = symbol self.algo = algo self.rollingData = RollingWindow[TradeBar](300) self.gkhv = 0 self.pctGkhv = 0 history = algo.History(symbol, 300, Resolution.Daily) history = history.loc[symbol] for tuple in history.itertuples(): bar = TradeBar(tuple.Index, symbol, tuple.open, tuple.high, tuple.low, tuple.close, tuple.volume) self.rollingData.Add(bar) self.UpdateVol(None) def UpdateVol(self, data): if data != None: self.rollingData.Add(data) _open = [] _high = [] _low = [] _close = [] for index in range(0, self.rollingData.Count-1): bar = self.rollingData[index] _open.append(bar.Open) _high.append(bar.High) _low.append(bar.Low) _close.append(bar.Close) history = pd.DataFrame() history["open"] = _open history["high"] = _high history["low"] = _low history["close"] = _close gkhv = np.sqrt(1/252 * pd.DataFrame.rolling(0.5 * np.log(history.loc[:, 'high'] / history.loc[:, 'low']) ** 2 - (2 * np.log(2) - 1) * np.log(history.loc[:, 'close'] / history.loc[:, 'open']) ** 2, window=252).sum()) self.gkhv = gkhv.iloc[-1] self.pct10 = gkhv.pct_change(3).iloc[-1] def Structured(self): df = pd.DataFrame() df["PCT"] = [self.pct10] df["GKHV"] = [self.gkhv] return df
#------------------------------------------------------------------------------------------------------------------------------- # import pandas as pd # import numpy as np # from io import StringIO # import json # import pickle # from datetime import datetime # from Selection.FundamentalUniverseSelectionModel import FundamentalUniverseSelectionModel # ''' # Would it be beneficial to run a universe selection every day based on which stocks # have a trading halt? Yes, it increases spead 10 fold # ''' # class TradingHalt(QCAlgorithm): # def Initialize(self): # self.SetStartDate(2018, 1, 1) # #self.SetEndDate(2021, 8, 21) # self.SetCash(100000) # csv = self.Download("https://www.dropbox.com/s/nsirge1gachgjg2/HaltData.csv?dl=1") # self.HaltData = pd.read_csv(StringIO(csv)) # if self.ObjectStore.ContainsKey("TradingHaltObject"): # self.ObjectStore.Delete("TradingHaltObject") # result = self.HaltData.to_json(orient="index") # parsed = json.loads(result) # dump = json.dumps(parsed)#, indent=4) # # dump = json.dumps(self.dict) # self.ObjectStore.Save("TradingHaltObject", dump) # serializedModel = bytes( self.ObjectStore.ReadBytes("HaltModel") ) # self.haltModel = pickle.loads(serializedModel) # self.priorityTypes = ["T1", "T2", "T5", "T6", "LUDP", "LUDS"] #, "MWC1", "MWC2", "MWC3", "MWCO"] # self.ConvertTimes() # self.activeSymbols = {} # self.symbolDataBySymbol = {} # self.tickersT = [] # self.tickers = [] # self.Resolution = Resolution.Hour # self.UniverseSettings.Resolution = self.Resolution # self.SetUniverseSelection(FineFundamentalUniverseSelectionModel(self.CoarseSelectionFunction, self.FineSelectionFunction)) # self.spy = self.AddEquity("SPY", self.Resolution).Symbol # self.Schedule.On(self.DateRules.EveryDay("SPY"), # self.TimeRules.BeforeMarketClose("SPY", 60), # self.SellAll) # self.todaysHalts = pd.DataFrame() # self.prepareTrading = [] # self.maxPositions = 10 # def TakeProfit(self): # activePositions = [x.Key for x in self.Portfolio if x.Value.Invested] # for symbol in activePositions: # if self.Portfolio[symbol].UnrealizedProfitPercent >= 0.05: # self.Liquidate(symbol) # def OnData(self, data): # # self.TakeProfit() # if len(self.todaysHalts.index) == 0: # return # for tuple in self.todaysHalts.itertuples(): # if tuple.Tickers not in list(self.activeSymbols.keys()): # continue # if tuple.Tickers == "UNAM" or tuple.Tickers == "AGBAU": # continue # symbol = self.activeSymbols[tuple.Tickers] # curHaltDate = str(tuple.StringHaltDate) # curHaltTime = str(tuple.HaltTime) # curResumeDate = str(tuple.StringResumedDate) # curResumeTime = str(tuple.ResumedTime) # try: # curHalt = curHaltDate + " " + curHaltTime # curHaltTime = datetime.strptime(curHalt, '%m/%d/%Y %H:%M:%S') # curResume = curResumeDate + " " + curResumeTime # curResumeTime = datetime.strptime(curResume, '%m/%d/%Y %H:%M:%S') # except: # continue # haltDelta = self.Time - curHaltTime # resumeDelta = self.Time - curResumeTime # if haltDelta.total_seconds() >= 0 and resumeDelta.total_seconds() < 0 and tuple.HaltType in self.priorityTypes:# and data.ContainsKey(self.activeSymbols[tuple.Tickers]): # self.HaltOccuring(symbol) # self.symbolDataBySymbol[symbol].gatheredData = True # elif resumeDelta.total_seconds() >= (60*60)*2 and self.symbolDataBySymbol[symbol].gatheredData == True and tuple.HaltType in self.priorityTypes and not self.Portfolio[self.activeSymbols[tuple.Tickers]].Invested:# and data.ContainsKey(self.activeSymbols[tuple.Tickers]): # self.AfterHaltTrades(symbol, resumeDelta) # def ConvertTimes(self): # self.HaltData["StringResumedDate"] = self.HaltData["ResumedDate"] # self.HaltData["ResumedDate"] = [datetime.strptime(x, '%m/%d/%Y') for x in self.HaltData["ResumedDate"]] # self.HaltData["StringHaltDate"] = self.HaltData["HaltDate"] # self.HaltData["HaltDate"] = [datetime.strptime(x, '%m/%d/%Y') for x in self.HaltData["HaltDate"]] # def CurrentHalts(self): # self.todaysHalts = self.HaltData.loc[self.HaltData['HaltDate'] == self.Time.date()] # def SellAll(self): # self.Liquidate() # def CoarseSelectionFunction(self, coarse): # self.CurrentHalts() # tickers = [] # symbols = [] # for tuple in self.todaysHalts.itertuples(): # tickers.append(tuple.Tickers) # for x in coarse: # symbolTicker = x.Symbol.Value # if symbolTicker in tickers: # symbols.append(x.Symbol) # return symbols # def FineSelectionFunction(self, fine): # return [x.Symbol for x in fine] # def OnSecuritiesChanged(self, changes): # for x in changes.AddedSecurities: # symbol = x.Symbol # self.Securities[symbol].SetSlippageModel(CustomSlippageModel(self)) # self.Securities[symbol].SetDataNormalizationMode(DataNormalizationMode.Raw) # self.activeSymbols[symbol.Value] = symbol # symbolData = SymbolData(symbol) # self.symbolDataBySymbol[symbol] = symbolData # for x in changes.RemovedSecurities: # symbol = x.Symbol # self.Liquidate(symbol) # self.activeSymbols.pop(symbol.Value, None) # if symbol in self.symbolDataBySymbol: # self.symbolDataBySymbol.pop(symbol, None) # class CustomSlippageModel: # def __init__(self, algorithm): # self.algorithm = algorithm # def GetSlippageApproximation(self, asset, order): # slippage = asset.Price * float(0.0001 * np.log10(2*float(order.AbsoluteQuantity))) # return slippage # class SymbolData: # def __init__(self, symbol): # self.Symbol = symbol # self.gatheredData = False # self.beforeHaltPriceMOMP = None # self.afterHaltPriceMOMP = None # self.beforeHaltSD = None # self.earlyIndicator = None