Overall Statistics
class OptimizedHorizontalCircuit(QCAlgorithm):

    def Initialize(self):
        '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
        
        self.SetStartDate(2020,4,10)  #Set Start Date
        self.SetEndDate(2020,6,3)   #Set End Date
        self.SetCash(10000000)
        
        # returns a equity object, which has .Symbol property
        self.spy = self.AddEquity("SPY", Resolution.Minute)
        
        # required for dealing with options
        self.spy.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
        # this determines our fees
        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage)
        
        # creating a 30 min consolidator 
        spy_thirtyMinuteConsolidator = TradeBarConsolidator(timedelta(minutes=30))
        
        # this is a event handler method, each time there is a new 30 minute bar, "self.ThirtyMinuteBarHandler" will be called
        spy_thirtyMinuteConsolidator.DataConsolidated += self.ThirtyMinuteBarHandler
        
        # our consolidator is registered to consolidate spy data
        self.SubscriptionManager.AddConsolidator("SPY", spy_thirtyMinuteConsolidator)
        
        
        
        self.spy_rsi = RelativeStrengthIndex(20, MovingAverageType.Simple)
        self.RegisterIndicator("SPY", self.spy_rsi, spy_thirtyMinuteConsolidator)
        
        
        
        #Aroon 13 Added - rolling window setup
        self.spy_aroon13 = AroonOscillator(13,13)
        self.RegisterIndicator("SPY", self.spy_aroon13, spy_thirtyMinuteConsolidator)        
        self.spy_aroon13.Updated += self.spy_Aroon13Updated
        self.spy_aroon13win = RollingWindow[IndicatorDataPoint](10)
        self.spy__aroon13_AroonUp = RollingWindow[float](10)
        
        
        #Ema 13 Added rolling window setup
        self.spy_ema13 = ExponentialMovingAverage(13)
        self.RegisterIndicator("SPY", self.spy_ema13, timedelta(minutes=30))
        self.spy_ema13.Updated += self.spy_Ema13Updated
        self.spy_ema13Window = RollingWindow[IndicatorDataPoint](5)
        
        
        # self.cci = CommodityChannelIndex(20,MovingAverageType.Simple)
        # self.RegisterIndicator("SPY", self.cci, thirtyMinuteConsolidator)
        
        self.underlying_rsi = {}
        self.underlying_cci = {}
        
        self.underlying_rsi[self.spy.Symbol] = self.spy_rsi
        #self.underlying_cci[self.spy.Symbol] = self.spy_cci
        
        #tickers = ["QQQ", "IWM", "GLD", "TLT", "VXX"]
        tickers = ["QQQ"]
        for ticker in tickers:
            security = self.AddEquity(ticker, Resolution.Minute)
            symbol = security.Symbol
            
            security.SetDataNormalizationMode(DataNormalizationMode.Raw)
            
            # creating a 30 min consolidator 
            thirtyMinuteConsolidator = TradeBarConsolidator(timedelta(minutes=30))
            # our consolidator is registered to consolidate spy data
            self.SubscriptionManager.AddConsolidator(symbol, thirtyMinuteConsolidator)
            # create a RSI indicator for that ticker
            
            rsi = RelativeStrengthIndex(20, MovingAverageType.Simple)
            self.RegisterIndicator(symbol, rsi, thirtyMinuteConsolidator)
            # save a reference to our RSI indicator for that underlying
            self.underlying_rsi[symbol] = rsi
            
            cci = CommodityChannelIndex(20, MovingAverageType.Simple)
            self.RegisterIndicator(symbol, cci, thirtyMinuteConsolidator)
            
            self.underlying_cci[symbol] = cci
        
        
            #Aroon 13 Added - rolling window setup
            aroon13 = AroonOscillator(13,13)
            self.RegisterIndicator(symbol, aroon13, spy_thirtyMinuteConsolidator)        
            self.aroon13.Updated += self.Aroon13Updated
            self.aroon13win = RollingWindow[IndicatorDataPoint](10)
            self._aroon13_AroonUp = RollingWindow[float](10)
        
        
            #Ema 13 Added rolling window setup
            ema13 = ExponentialMovingAverage(13)
            self.RegisterIndicator(symbol, ema13, timedelta(minutes=30))
            self.ema13.Updated += self.Ema13Updated
            self.ema13Window = RollingWindow[IndicatorDataPoint](5)    
            
            
            
        # self.AddUniverse(self.SelectCoarse)    
        
        self.outtime = 10080

        self.symbolDataBySymbolRSI = {}
        self.symbolDataBySymbolCCI = {}
        
        self.SetSecurityInitializer(self.CustomSecurityInitializer)
    
    
    ######Rolling Windows for SPY
    def spy_Ema13Updated(self, sender, updated):
        self.spy_ema13Window.Add(updated)
    
    
    def spy_Aroon13Updated(self, sender, updated):
        '''Adds updated values to rolling window'''
        self.spy_aroon13win.Add(updated)
        self.spy__aroon13_AroonUp.Add(self.spy_aroon13.AroonUp.Current.Value)
        
    ######Rolling Windows for non-SPY symbols in list
    def Ema13Updated(self, sender, updated):
        self.ema13Window.Add(updated)
    
    
    def Aroon13Updated(self, sender, updated):
        '''Adds updated values to rolling window'''
        self.aroon13win.Add(updated)
        self._aroon13_AroonUp.Add(self.aroon13.AroonUp.Current.Value)
    
    
    
    def CustomSecurityInitializer(self, security):
        bar = self.GetLastKnownPrice(security)
        security.SetMarketPrice(bar)
    
    
    # Is called each time a new security is added to our universe
    def OnSecuritiesChanged(self, changes):
        
        # for each new underlyingsecurity, we can create an RSI, and save a refernce
        # in the self.underlying_rsi dictionary
        
        pass
    
    def ThirtyMinuteBarHandler(self, sender, bar):
        '''This is our event handler for our 30-minute trade bar defined above in Initialize(). So each time the consolidator produces a new 30-minute bar, this function will be called automatically. The sender parameter will be the instance of the IDataConsolidator that invoked the event '''
        
        
        for underlying_symbol in self.underlying_rsi:
            
            rsi = self.underlying_rsi[underlying_symbol]
            
            if not rsi.IsReady: 
                continue
            
            holdings = self.Portfolio[underlying_symbol].Quantity
        
            if holdings > 10000:
                # do nothing
                return
            
            #if rsi.Current.Value < 30 and self.spy_ema13Window[0].Value < self.spy_ema13Window[1].Value and self.spy__aroon13_AroonUp[0] < self.spy__aroon13_AroonUp[1]:
            if rsi.Current.Value < 30 and self.ema13Window[0].Value < self.ema13Window[1].Value and self._aroon13_AroonUp[0] < self._aroon13_AroonUp[1]:
                contract_call_symbol = self.OptionsFilter(underlying_symbol, 10, OptionRight.Call)
                
                # if there are no contracts do nothing
                if contract_call_symbol is None:
                    return
                
                self.MarketOrder(contract_call_symbol, 1)
                
                self.Log(f"RSI<30 >> {self.spy__aroon13_AroonUp[0]} ,   {self.spy__aroon13_AroonUp[1]}")
                
                symbolDataObject = SymbolDataRSI(contract_call_symbol, self)
                self.symbolDataBySymbolRSI[contract_call_symbol] = symbolDataObject
                
                symbolDataObject.NewPosition(self.Time, 1)
                
        
        # SELLING
        ################
        
        # we have a reference to each option we've traded
        # every thirty minutes loop over all open option contracts positions
        for option_symbol in self.symbolDataBySymbolRSI:
            
            symbolData = self.symbolDataBySymbolRSI[option_symbol]
            
            if not self.Portfolio[option_symbol].Invested:
                continue
            
            # do we have an open position for contract? # if outtime time as passed
            option_entry_time = symbolData.entry_time
            current_time = self.Time
            if (current_time - option_entry_time) >= timedelta(minutes=self.outtime):
                self.Liquidate(option_symbol)
                self.Log(f"Sell Call: {option_symbol}, entry_time:{option_entry_time}, exit_time:{current_time}")
        
            underlying_symbol = self.Securities[option_symbol].Underlying.Symbol
            
            if self.underlying_rsi[underlying_symbol].Current.Value > 70:
                self.Log(f"Sell RSI>70  >> {option_symbol}")
     
    
    # Given an underlying, Identify an slightly OTM call contract expiry closest to X days but greater than X days
    def OptionsFilter(self, underlying_symbol, expiry_days, option_right):
        # option_right = [OptionRight.Call, OptionRight.Put]
    
        contracts = self.OptionChainProvider.GetOptionContractList(underlying_symbol, self.Time)
       
        underlyingPrice = self.Securities[underlying_symbol].Price
     
        contracts_filtered_for_right_and_expiry = [i for i in contracts if i.ID.OptionRight == option_right and  \
                                    i.ID.Date >= self.Time + timedelta(days = expiry_days)]
        
        otm_contracts = None
        
        if option_right == OptionRight.Call:
            otm_contracts = [i for i in contracts_filtered_for_right_and_expiry if i.ID.StrikePrice > underlyingPrice]
        else:
            otm_contracts = [i for i in contracts_filtered_for_right_and_expiry if i.ID.StrikePrice < underlyingPrice]
        
        # if there are no contrats
        if otm_contracts is None or len(otm_contracts) == 0:
            return None
        
        sorted_by_strike = sorted(otm_contracts, key=lambda x: abs((underlyingPrice+1) - x.ID.StrikePrice))
        closest_strike = sorted_by_strike[0].ID.StrikePrice
        
        # all contracts slightly OTM
        contracts_with_closest_strike = [i for i in sorted_by_strike if i.ID.StrikePrice == closest_strike]
        
        # Sort slightly OTM contracts by Expiry and pick 
        sorted_by_expiry = sorted(contracts_with_closest_strike, key=lambda x: x.ID.Date)
        
        contract = sorted_by_expiry[0]
       
        self.AddOptionContract(contract, Resolution.Minute)
            
        return contract  
        
        
class SymbolDataRSI:
    
    """
    Motivation: We want to keep track of properties for each specific security (options, equities) - some symbol
    """
    
    def __init__(self, symbol, algorithm):
        
        self.symbol = symbol
        self.algorithm = algorithm
      
        
        self.entry_time = algorithm.Time # self.Time
        
        
        # idea is we want to add structure within SymbolData so we can keep track
        # of individual lots of trades for a specific contract
        # Call June 2020 400 Strike
        # {"05/12/2020" : +3, 05/17/2020 : +5} # we want to treat them indivudally
        # record of each individual position for RSI strategy
        self.positions = {} # key = entrytime , values = size
        
        # 1st trade happens when we initialize
        
    def NewPosition(self, entry_time, size):
        self.positions[entry_time] = size
    
   
    
    
class SymbolDataCCI:
    
    """
    Motivation: We want to keep track of properties for each specific security (options, equities) - some symbol
    """
    
    def __init__(self, symbol, algorithm):
        
        self.symbol = symbol
        self.algorithm = algorithm
        
        self.entry_time = algorithm.Time # self.Time
        
        option_contract_security = algorithm.Securities[symbol]
        
        underlying_security = option_contract_security.Underlying
        
        # creating a 30 min consolidator 
        thirtyMinuteConsolidator = TradeBarConsolidator(timedelta(minutes=30))
        
        # our consolidator is registered to consolidate underlying data
        self.SubscriptionManager.AddConsolidator(underlying_security.Symbol, thirtyMinuteConsolidator)
        
        self.underlying_cci = CommodityChannelIndex(20, MovingAverageType.Simple)
        self.RegisterIndicator(underlying_security.Symbol, self.underlying_cci, thirtyMinuteConsolidator)

        # idea is we want to add structure within SymbolData so we can keep track
        # of individual lots of trades for a specific contract
        # Call June 2020 400 Strike
        # {"05/12/2020" : +3, 05/17/2020 : +5} # we want to treat them indivudally
        self.lot = {} # key = entrytime , values = size