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