Overall Statistics
Total Trades
406
Average Win
0.00%
Average Loss
0.00%
Compounding Annual Return
-1.094%
Drawdown
0.200%
Expectancy
-0.360
Net Profit
-0.160%
Sharpe Ratio
-4.649
Probabilistic Sharpe Ratio
0.484%
Loss Rate
59%
Win Rate
41%
Profit-Loss Ratio
0.55
Alpha
-0.006
Beta
-0.004
Annual Standard Deviation
0.002
Annual Variance
0
Information Ratio
-8.059
Tracking Error
0.095
Treynor Ratio
2.224
Total Fees
$409.50
#######   Session 3 Objectives in order of importance
########  1.) Rolling Window Indicators on 30 Min bars - for looping through symbols  - done
########  2.) Keeping track of individual 'lots' in order to sell specific # of contracts instead of Liquidate all 
########          layer out if needed, timing of multiple strategies as once on a given symbol
#######   3.) Tracking for overlapping bracket orders (muptliple bracket orders on at the same time for a given option ) 
######            issue arises when it cancels all open orders
#######   4.) Accessing Bid/Ask and Greeks within the Options selection function - desire is to be a certain % ITM or ATM instead of a fixed $value
            
            
            # bid = self.Securities[option_symbol].BidClose
            # In order to access greeks, we need to use AddOption(symbol), right now we're using the OptionChainProvider(selectively subscribes to data - efficient backtests)
            # https://www.quantconnect.com/docs/data-library/options
            # Example: https://github.com/QuantConnect/Lean/blob/43dba33b8913adacc56da9de299b36c0bbafb022/Algorithm.Python/BasicTemplateOptionsHistoryAlgorithm.py
            

from BracketOrder import *
from BracketOrderCCI import *
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,7,10)  #Set Start Date
        self.SetEndDate(2020,8,31)   #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)
        
        ###question - Is this doing it correctly?, do I need to do anything in the on data
        #warmup period of 30 
        self.SetWarmup(300)
        
        # 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_symbol_data = {}
        #self.underlying_cci[self.spy.Symbol] = self.spy_cci
        
        tickers = ["QQQ", "IWM"]
        #tickers = ["QQQ","SPY","IWM", "GLD", "VXX","EEM","EFA","XHB","SLV","USO"]
        for ticker in tickers:
            security = self.AddEquity(ticker, Resolution.Minute)
            symbol = security.Symbol
            
            security.SetDataNormalizationMode(DataNormalizationMode.Raw)
            
            symbol_data_object = UnderlyingSymbolData(symbol, self)
            
            self.underlying_symbol_data[symbol] = symbol_data_object
            
            
        # self.AddUniverse(self.SelectCoarse)    
        
        self.outtime = 10080

        self.symbolDataBySymbolRSI = {}
        self.symbolDataBySymbolCCI = {}
        
        self.SetSecurityInitializer(self.CustomSecurityInitializer)
        
    
    def OnOrderEvent(self, orderEvent):
        
        symbol = orderEvent.Symbol
        
        # Is this symbol in our "Option Symbol Data" List
        if symbol in self.symbolDataBySymbolRSI:
            
            bracket_order = self.symbolDataBySymbolRSI[symbol].current_bracket_order
            
            # aka we have some entry position
            if bracket_order is not None:
                # cancel stop loss if take profit filled and vice versa
                bracket_order.Update(orderEvent)
                
                # if our order is complete after the fill
                # let's reset it
                if bracket_order.Closed is True:
                    self.symbolDataBySymbolRSI[symbol].current_bracket_order = None
                    
    def OnOrderEventCCI(self, orderEvent):
        
        symbol = orderEventCCI.Symbol
        
        # Is this symbol in our "Option Symbol Data" List
        if symbol in self.symbolDataBySymbolCCI:
            
            bracket_order = self.symbolDataBySymbolCCI[symbol].current_bracket_order
            
            # aka we have some entry position
            if bracket_order is not None:
                # cancel stop loss if take profit filled and vice versa
                bracket_order.Update(orderEvent)
                
                # if our order is complete after the fill
                # let's reset it
                if bracket_order.Closed is True:
                    self.symbolDataBySymbolCCI[symbol].current_bracket_order = None            
    
    ######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)
        
   
    
    
    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
    
    # ###########################################
    # We're using the SPY 30 minute consolidator event handler to do our position calculations
    # Can we replace this with a scheduled event, so that it's not specific to a security?
    # ##############################################
    #
    
    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_symbol_data:
            
            symbol_data = self.underlying_symbol_data[underlying_symbol]
            
            rsi = symbol_data.rsi
            cci = symbol_data.cci
            aroon13 = symbol_data.aroon13
            ema13 = symbol_data.ema13
            aroon13win = symbol_data.aroon13win
            ema13win = symbol_data.ema13Window
            _aroon13_AroonUp = symbol_data._aroon13_AroonUp
            
            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 ema13win[0].Value < ema13win[1].Value and _aroon13_AroonUp[0] < _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, 3)
                # latest price for symbol, We can use AddOption to access price data/greek data immediately for every option for secondary filtering
                current_market_price = self.Securities[contract_call_symbol].Price
                
                
                ask = self.Securities[contract_call_symbol].AskPrice
                
                
                take_profit = current_market_price * 1.10
                stop_loss = current_market_price * 0.90
                # (algorithm, symbol, quantity, entry, takeProfit, stopLoss)
                
                
                bracket_order = BracketOrder(self, contract_call_symbol, 1, current_market_price, take_profit, stop_loss)
                self.Log(f"RSI<30 >>")
                
                symbolDataObject = SymbolDataRSI(contract_call_symbol, self)
                self.symbolDataBySymbolRSI[contract_call_symbol] = symbolDataObject
                
                # save reference to created bracket order, (is no longer None)
                symbolDataObject.current_bracket_order = bracket_order
                
                symbolDataObject.NewPosition(self.Time, 1)
                
            if cci.Current.Value < -100:
                contract_call_symbol = self.OptionsFilter(underlying_symbol, 10, OptionRight.Call)
                
                # if there are no contracts do nothing
                if contract_call_symbol is None:
                    return
                
                
                # latest price for symbol, We can use AddOption to access price data/greek data immediately for every option for secondary filtering
                current_market_price = self.Securities[contract_call_symbol].Price
                
                
                ask = self.Securities[contract_call_symbol].AskPrice
                
                
                take_profit = current_market_price * 1.10
                stop_loss = current_market_price * 0.90
                # (algorithm, symbol, quantity, entry, takeProfit, stopLoss)
                
                
                bracket_order = BracketOrderCCI(self, contract_call_symbol, 1, current_market_price, take_profit, stop_loss)
                self.Log(f"CCI<30 >>")
                
                symbolDataObject = SymbolDataCCI(contract_call_symbol, self)
                self.symbolDataBySymbolCCI[contract_call_symbol] = symbolDataObject
                
                # save reference to created bracket order, (is no longer None)
                symbolDataObject.current_bracket_order = bracket_order
                
                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
            
            underlying_rsi = self.underlying_symbol_data[underlying_symbol].rsi
            
            if underlying_rsi.Current.Value > 70:
                self.Log(f"Sell RSI>70  >> {option_symbol}")
     
        for option_symbol in self.symbolDataBySymbolCCI:
            
            symbolData = self.symbolDataBySymbolCCI[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
            
            underlying_cci = self.underlying_symbol_data[underlying_symbol].cci
            
            if underlying_cci.Current.Value > 100:
                self.Log(f"Sell CCI>100  >> {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)
        
        # for symbol in contracts:
            # cant access price data, we haven't used AddOptionContract yet
        
        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 UnderlyingSymbolData:
    
    def __init__(self, symbol, algorithm):
        
        self.symbol = symbol
        self.algorithm = algorithm
        
        # creating a 30 min consolidator 
        thirtyMinuteConsolidator = TradeBarConsolidator(timedelta(minutes=30))
        
        # our consolidator is registered to consolidate spy data
        algorithm.SubscriptionManager.AddConsolidator(symbol, thirtyMinuteConsolidator)
        # create a RSI indicator for that ticke
        
        
        self.rsi = RelativeStrengthIndex(20, MovingAverageType.Simple)
        algorithm.RegisterIndicator(symbol, self.rsi, thirtyMinuteConsolidator)
        # save a reference to our RSI indicator for that underlying
        
        
        self.cci = CommodityChannelIndex(20, MovingAverageType.Simple)
        algorithm.RegisterIndicator(symbol, self.cci, thirtyMinuteConsolidator)
    
    
        #Aroon 13 Added - rolling window setup
        # creates new AROON indicator and registers it to the algo
        self.aroon13 = AroonOscillator(13,13)
        algorithm.RegisterIndicator(symbol, self.aroon13, thirtyMinuteConsolidator)        
        self.aroon13.Updated += self.Aroon13Updated
        
        # roll
        self.aroon13win = RollingWindow[IndicatorDataPoint](10)
        self._aroon13_AroonUp = RollingWindow[float](10)
        # self.aroon13win[0].AroonUp.Current.Value

        
        #Ema 13 Added rolling window setup
        self.ema13 = ExponentialMovingAverage(13)
        algorithm.RegisterIndicator(symbol, self.ema13, timedelta(minutes=30))
        self.ema13.Updated += self.Ema13Updated
        self.ema13Window = RollingWindow[IndicatorDataPoint](5)  
        
        
    
    ######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)
    
    
    

# "Option Symbol Data"
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
        
        
        self.current_bracket_order = None
        
        # 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
        
# "Option Symbol Data"
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
        
        
        self.current_bracket_order = None
        
        # 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 BracketOrder:
    
    def __init__(self, algorithm, symbol, quantity, entry, takeProfit, stopLoss, trailing=False):
        
        self.Algorithm = algorithm
        self.Symbol = symbol
        self.Quantity = quantity
        self.Entry = entry
        self.TakeProfit = takeProfit
        self.StopLoss = stopLoss
        self.Trailing = trailing
        
        self.Open = True
        self.Closed = False
        
        self.EntryOrderTicket = algorithm.MarketOrder(symbol, quantity)
        
        self.TakeProfitOrderTicket = self.Algorithm.LimitOrder(self.Symbol, -self.Quantity, self.TakeProfit)
        self.StopLossOrderTicket = self.Algorithm.StopMarketOrder(self.Symbol, -self.Quantity, self.StopLoss)
        
        self.ID = [self.EntryOrderTicket.OrderId,self.TakeProfitOrderTicket.OrderId, self.StopLossOrderTicket.OrderId]
        
    def CancelStopLoss(self):
        response = self.StopLossOrderTicket.Cancel()
        self.StopLossOrderTicket = None
        
        self.Open = False
        self.Closed = True
        
    def CancelTakeProfit(self):
        response = self.TakeProfitOrderTicket.Cancel()
        self.TakeProfitOrderTicket = None
       
        self.Open = False
        self.Closed = True
        
    def Update(self, orderEvent):
        
        if self.TakeProfitOrderTicket != None and orderEvent.OrderId == self.TakeProfitOrderTicket.OrderId:
            if orderEvent.Status == OrderStatus.Filled:
                self.CancelStopLoss()
                
        elif self.StopLossOrderTicket != None and orderEvent.OrderId == self.StopLossOrderTicket.OrderId:
            if orderEvent.Status == OrderStatus.Filled:
                self.CancelTakeProfit()
                
    @property
    def Statistics(self):
        exit = self.TakeProfit if self.StopLossOrderTicket is None else self.StopLoss
        return  f"{self.Symbol}, Return: {(self.Entry - exit)/self.Entry} - ${self.Quantity*(self.Entry - exit)} -- Entry Price: {self.Entry} and Exit Price: {exit}"
class BracketOrderCCI:
    
    def __init__(self, algorithm, symbol, quantity, entry, takeProfit, stopLoss, trailing=False):
        
        self.Algorithm = algorithm
        self.Symbol = symbol
        self.Quantity = quantity
        self.Entry = entry
        self.TakeProfit = takeProfit
        self.StopLoss = stopLoss
        self.Trailing = trailing
        
        self.Open = True
        self.Closed = False
        
        self.EntryOrderTicket = algorithm.MarketOrder(symbol, quantity)
        
        self.TakeProfitOrderTicket = self.Algorithm.LimitOrder(self.Symbol, -self.Quantity, self.TakeProfit)
        self.StopLossOrderTicket = self.Algorithm.StopMarketOrder(self.Symbol, -self.Quantity, self.StopLoss)
        
        self.ID = [self.EntryOrderTicket.OrderId,self.TakeProfitOrderTicket.OrderId, self.StopLossOrderTicket.OrderId]
        
    def CancelStopLoss(self):
        response = self.StopLossOrderTicket.Cancel()
        self.StopLossOrderTicket = None
        
        self.Open = False
        self.Closed = True
        
    def CancelTakeProfit(self):
        response = self.TakeProfitOrderTicket.Cancel()
        self.TakeProfitOrderTicket = None
       
        self.Open = False
        self.Closed = True
        
    def Update(self, orderEvent):
        
        if self.TakeProfitOrderTicket != None and orderEvent.OrderId == self.TakeProfitOrderTicket.OrderId:
            if orderEvent.Status == OrderStatus.Filled:
                self.CancelStopLoss()
                
        elif self.StopLossOrderTicket != None and orderEvent.OrderId == self.StopLossOrderTicket.OrderId:
            if orderEvent.Status == OrderStatus.Filled:
                self.CancelTakeProfit()
                
    @property
    def Statistics(self):
        exit = self.TakeProfit if self.StopLossOrderTicket is None else self.StopLoss
        return  f"{self.Symbol}, Return: {(self.Entry - exit)/self.Entry} - ${self.Quantity*(self.Entry - exit)} -- Entry Price: {self.Entry} and Exit Price: {exit}"