Overall Statistics
class SPY_With_Options(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2019, 1, 1)  # Set Start Date
        self.SetEndDate(2019, 1, 5)  # Set Start Date
        
        self.SetCash(100000)  # Set Strategy Cash
        self.AddEquity("SPY", Resolution.Minute)
        
        option = self.AddOption("SPY")
        option.SetFilter(-10, +10, timedelta(30*35), timedelta(30*36))
        
        self.__consolidateTradeCnt = 0
        self.__consolidateTradeTrigger = False
        self.__consolidateQuoteCnt = 0
        self.__consolidateQuoteTrigger = False
        self.consolidators = dict()

    # -------------------------------------------------------------------------------------------------------
    def PrintOptionContractInfo(self, contract, prefix=""):
        c = contract 
        self.Log(f"{prefix} {c.Symbol}, Strike: {c.Strike} Expiry: {c.Expiry} Right: {c.Right} Price => A:{c.AskPrice} B:{c.BidPrice} L:{c.LastPrice} T:{c.TheoreticalPrice} Intrest: {c.OpenInterest} ")
        return

    def PrintOptionChainInfo(self, chain, prefix=""):
        for contract in chain:
            self.PrintOptionContractInfo(contract, prefix)
        return
    
    # -------------------------------------------------------------------------------------------------------
    def OnData(self, slice):
        '''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 not self.Portfolio.Invested:
            self.SetHoldings("SPY", 0.5)

            for kvp in slice.OptionChains:
                chain = kvp.Value

                # find the second call strike under market price expiring today
                contracts = sorted(sorted(chain, key = lambda x: abs(chain.Underlying.Price - x.Strike)),
                                                 key = lambda x: x.Expiry, reverse=False)
                self.PrintOptionChainInfo(contracts)

                # Purchase options
                if len(contracts) == 0: continue
                if contracts[0] != None:
                    self.Log("Purchased"+'xx'+str(contracts[0].Symbol)+'xx')
                    self.MarketOrder(contracts[0].Symbol, 1)
                    if contracts[0].Symbol == 'SPY 211217C00245000':
                        self.Log("Matched")
                    else:
                        self.Log("Not Matched")
                    
                # self.GetMyOption("SPY", slice)
                # self.MarketOrder('SPY 211217C00245000', 1)
            
                    
    # -------------------------------------------------------------------------------------------------------
    # Function that helps consolidation
    def OnOrderEvent(self, orderEvent):
        self.Log(str(orderEvent))
        
    def OnQuoteBarConsolidated(self, sender, quoteBar):
        # self.Log("QBCon:" + str(self.__consolidateQuoteCnt) + " " + str(self.Time) + str(quoteBar))
        
        # Trigger when last of the contract was consolidated
        # 3 year  - "211217P00260" 
        # 1 year  - "200918P00260" 
        # 3 month - "191115P00260"
        # 1 month - "190920P00260"
        if ( str(quoteBar).find("211217P00240") > 0 ):
            # self.Log("OnQuoteBarConsolidated:" + str(self.__consolidateQuoteCnt) + " " + str(self.Time) + str(quoteBar))
            self.__consolidateQuoteTrigger = True
            
        self.__consolidateQuoteCnt += 1

    def OnTradeBarConsolidated(self, sender, tradeBar):
        # self.Log("TBCon:" + str(self.__consolidateTradeCnt) + " " + str(self.Time) + str(tradeBar))
        self.__consolidateTradeCnt += 1
        
    def OnSecuritiesChanged(self, changes):
        for security in changes.AddedSecurities:
            if security.Type == SecurityType.Equity:
                consolidator = TradeBarConsolidator(timedelta(minutes=60))
                consolidator.DataConsolidated += self.OnTradeBarConsolidated
            else:
                consolidator = QuoteBarConsolidator(timedelta(minutes=60))
                consolidator.DataConsolidated += self.OnQuoteBarConsolidated
                
            self.SubscriptionManager.AddConsolidator(security.Symbol, consolidator)
            self.consolidators[security.Symbol] = consolidator
            
        for security in changes.RemovedSecurities:
            consolidator = self.consolidators.pop(security.Symbol)
            self.SubscriptionManager.RemoveConsolidator(security.Symbol, consolidator)
            
            if security.Type == SecurityType.Equity:
                consolidator.DataConsolidated -= self.OnTradeBarConsolidated
            else:
                consolidator.DataConsolidated -= self.OnQuoteBarConsolidated
                
    # -------------------------------------------------------------------------------------------------------