Overall Statistics |
Total Trades 163 Average Win 1.38% Average Loss -0.87% Compounding Annual Return -3.041% Drawdown 14.200% Expectancy -0.011 Net Profit -1.944% Sharpe Ratio 0.004 Probabilistic Sharpe Ratio 19.205% Loss Rate 62% Win Rate 38% Profit-Loss Ratio 1.58 Alpha 0.018 Beta -0.064 Annual Standard Deviation 0.239 Annual Variance 0.057 Information Ratio -0.996 Tracking Error 0.269 Treynor Ratio -0.014 Total Fees $1086.41 Estimated Strategy Capacity $68000.00 Lowest Capacity Asset SEED TDJ3UYNCD4KL |
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(2021, 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 >= 20: 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 HaltOccuring(self, symbol): history = self.History(symbol, 20, Resolution.Hour) mompClose = MomentumPercent(20) standardDeviation = history.loc[symbol]["close"].std() for historyTuple in history.loc[symbol].itertuples(): mompClose.Update(historyTuple.Index, historyTuple.close) self.symbolDataBySymbol[symbol].beforeHaltPriceMOMP = mompClose.Current.Value self.symbolDataBySymbol[symbol].beforeHaltSD = standardDeviation def AfterHaltTrades(self, symbol, delta): history = self.History(symbol, 3, Resolution.Hour) closes = history.loc[symbol]["close"] self.symbolDataBySymbol[symbol].afterHaltPriceMOMP = (closes[-1] - closes[-3]) / closes[-3] if self.symbolDataBySymbol[symbol].beforeHaltPriceMOMP == None: return if (self.symbolDataBySymbol[symbol].beforeHaltPriceMOMP >= 0 and self.symbolDataBySymbol[symbol].afterHaltPriceMOMP) >= 0 or (self.symbolDataBySymbol[symbol].beforeHaltPriceMOMP < 0 and self.symbolDataBySymbol[symbol].afterHaltPriceMOMP < 0): self.symbolDataBySymbol[symbol].earlyIndicator = True else: self.symbolDataBySymbol[symbol].earlyIndicator = False direction = -1 if self.symbolDataBySymbol[symbol].beforeHaltPriceMOMP >= 0: direction = 1 tempDict = {} tempDict[symbol] = [self.symbolDataBySymbol[symbol].beforeHaltSD, self.symbolDataBySymbol[symbol].earlyIndicator] testDf = pd.DataFrame.from_dict(tempDict) prediction = self.haltModel.predict(testDf.T) positionSize = (1)/len(self.activeSymbols.keys()) if prediction == 1: self.SetHoldings(symbol, positionSize) else: self.SetHoldings(symbol, -1/positionSize) 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