Overall Statistics
Total Trades
1706
Average Win
0.09%
Average Loss
-0.18%
Compounding Annual Return
-59.419%
Drawdown
87.200%
Expectancy
-0.971
Net Profit
-87.212%
Sharpe Ratio
-1.395
Probabilistic Sharpe Ratio
0%
Loss Rate
98%
Win Rate
2%
Profit-Loss Ratio
0.49
Alpha
-0.433
Beta
0.001
Annual Standard Deviation
0.311
Annual Variance
0.096
Information Ratio
-1.371
Tracking Error
0.348
Treynor Ratio
-474.469
Total Fees
$1706.00
Estimated Strategy Capacity
$14000000.00
Lowest Capacity Asset
QQQ RIWIV7K5Z9LX
Portfolio Turnover
186.14%
#region imports
from AlgorithmImports import *

import datetime as dt
#endregion

## Percentage of investment according to Total Portfolio Value
FUTURE1_ORDER_QUANTITY = 2
FUTURE2_ORDER_QUANTITY = 3
EQUITY1_ORDER_QUANTITY = 100
EQUITY2_ORDER_QUANTITY = 300

# Limit Order margin
# https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/order-types/limit-orders
LIMIT_ORDER = 0.005 # If positive Marketable, unmarketable otherwise

## Retracement levels
K_AVERAGE = 6
K_STD = 16 

## LOGIC
B1 = -0.75
B2 = 0.55
B3 = 0.61
B4 = -0.65

#region imports
from AlgorithmImports import *

from collections import deque
#endregion

class CustomMainIndicator(PythonIndicator):
## INITIALIZATION
    def __init__(self,name,period,resolution=None):
        """ 
        The parameters accompanied by a * must exist in every custom indicator.

        Inputs:
        -- Name* (String): A characteristic name for the definition of the indicator.
        
        -- Period [int, float,timedelta]: Number of timeframes to account.
                            When time delta, the resolution is necessary.

        -- Resolution [QC Attr Resolution or timdelta]: The resolution 
                            parameter allow the period computation.

        Initialization Parameters:
        -- Time* (datetime.datetime object): Inherits the time of the creation and,
                                             on the update method, the moment of update.
        -- Value* (float non-NonTypeObject): Actual value of the indicator. Where the arrows will be created.
        """
        
        self.Name = name
        
        # Number of periods to make the computation
        if isinstance(period, (int,float)):
            self.Period = int(period)
        else:
            assert isinstance(period, timedelta), 'Period recibe Int, float or timedelta.'
            assert isinstance(resolution, (int,timedelta)), 'If period is timedelta type, a resolution should be passed\
                                            to compute the periods. resolution can be the Resolution attribute\
                                            from QC or a timedelta.'
            self.Period = period//self.SetResolution(resolution)
        
        self.Time = datetime.min
        
        # The following parametters are necessary for the creation of the figures
        self.Value = 0
        self.Date = datetime.min

    def SetResolution(self,resolution):
        '''Return a resolution that the model undestand for the period computation.'''
        if resolution == 1: # Resolution.Second:
            return timedelta(seconds=1)
        elif resolution == 2: # Resolution.Minute:
            return timedelta(minutes=1)
        elif resolution == 3: # Resolution.Hour:
            return timedelta(hours=1)
        elif resolution == 4: # Resolution.Daily:
            return timedelta(days=1)
        else:
            assert isinstance(resolution, timedelta), 'resolution can be the Resolution attribute\
                                            from QC or a timedelta.'
            return resolution
  
class ModZIndicator(CustomMainIndicator):

## INITIALIZATION
    def __init__(self, name,
                    k_average,k_std,
                    **kwargs):

        '''
        Parameters:
            - 

        '''

        super().__init__(name, max(k_average,k_std),**kwargs)
        
        self.WarmUpPeriod = self.Period

        self.K_mean = k_average
        self.K_std = k_std
        self.window = deque(maxlen=self.Period)
    
    @property
    def IsReady(self):
        return len(self.window) >= self.window.maxlen

## INDICATOR COMPUTATION

    def Update(self, time, inputA, inputB) -> bool:

        '''QC required function to get the data and update the indicator'''

        self.Time = time
        self.EndTime = time
        self.Ratio = inputA.Close/(inputB.Close + 1e-18)
        
        self.window.append(self.Ratio)

        if len(self.window) < self.window.maxlen:
            return False

        window = np.array(self.window)

        self.Average = np.mean(window[-self.K_mean])
        self.STD = np.std(window[-self.K_std:]) + 1e-18

        self.Value = (self.Ratio-self.Average)/self.STD

        return len(self.window) >= self.window.maxlen


##       
#region imports
from AlgorithmImports import *
#endregion
from Selection.FutureUniverseSelectionModel import FutureUniverseSelectionModel

# Your New Python File
class FrontMonthFutureUniverseSelectionModel(FutureUniverseSelectionModel):
    def __init__(self,) -> None:
        super().__init__(timedelta(1), self.select_future_chain_symbols)

    def select_future_chain_symbols(self, utcTime: datetime) -> List[Symbol]:
        return [ 
            Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME),
            Symbol.Create(Futures.Indices.NASDAQ100EMini, SecurityType.Future, Market.CME),
        ]

    def Filter(self, filter: FutureFilterUniverse) -> FutureFilterUniverse:
        return filter.FrontMonth().OnlyApplyFilterAtMarketOpen()
#region imports
from AlgorithmImports import *

from collections import deque

import CustomIndicators as ci
from ControlParameters import *
#endregion

class SymbolData:
    # Object to Keep track of the securities

## INITIALIZATION
    def __init__(self,symbol, time):
        '''
        Inputs:
            - Symbol [QC Symbol]: Reference to the underlying security.
            - time [Main Algo function]: This function returns the time of the main algorithm.
        '''
        self.__Symbol = symbol
        self.get_Time = time
        self.__Order = None 
    
    @property
    def Time(self):
        # Allow access to the Time object directly
        return self.get_Time()

## MANAGEMENT
    @property
    def Symbol(self):
        return self.__Symbol

    @Symbol.setter
    def Symbol(self,NewSymbol):
        self.__Order = None
        self.__Symbol = NewSymbol
    
    @property
    def Order(self):
        return self.__Order

    @Order.setter
    def Order(self, order):
        self.__Order = order

    def GetOrderQuantity(self, portfolio, target):
        return target - portfolio

# ---------------------------------------------------------------------------

class PairData:
    # Object to Keep track of the pairs

## INITIALIZATION
    def __init__(self,future1, future2, 
                underlying1, underlying2,
                time
                ):

        '''
        Inputs:
            - Symbol [QC Symbol]: Reference to the underlying security.
            - Security [QC Security]: Reference to the security object to access data.
            - time [Main Algo function]: This function returns the time of the main algorithm.
            - Indicator objects: MACD, STOCH, Fast_MA, Slow_MA and BB
        '''
        self.Future1 = future1
        self.Future2 = future2
        self.Underlying1 = underlying1
        self.Underlying2 = underlying2

        # Helper dictionaries
        self.Futures = {self.Future1.Symbol: self.Future1,
                        self.Future2.Symbol: self.Future2}
        self.Underlyings = {self.Underlying1.Symbol : self.Underlying1,
                        self.Underlying2.Symbol : self.Underlying2}

        self.MyItems = {**self.Futures,**self.Underlyings}

        self.ModZ = self.InitIndicators()

        self.get_Time = time       

    def InitIndicators(self):
        '''
        Create the required indicators.
        '''
        # This procets repeat itself per security
        modz = ci.ModZIndicator('MyModZ',K_AVERAGE, K_STD)#timedelta(days=10), resolution=Resolution.Daily)

        '''The following line make the indicator update automatically,
        but because we use a ratio we have to update it manually'''
        # main.RegisterIndicator(contract, modz, Resolution.Minite) # Associate the indicator to a ticker and a update resolution 
                                                                    # (resolution has to be equal or lower than security resolution)

        return modz

    @property
    def Time(self):
        # Allow access to the Time object directly
        return  self.get_Time()

    @property
    def IsReady(self):
        # Tells if all the indicators assciated are ready
        return self.ModZ.IsReady
        
## STATUS
    def UpdateFuture(self, old, new, security):
        '''
        In case of rollover or change in the symbol data.
        Input:
            old[QC object]: QC Symbol object used within the PairData object.
            new[QC object]: QC current Symbol object.
            security [QC object]: Current Security object from QC.
        '''
        self.Futures[new] = security
        self.Futures.pop(old,None)
        if old == self.Future1.Symbol:
            self.Future1 = security
        elif old == self.Future2.Symbol:
            self.Future2 = security
        else:
            assert self.Future1.Symbol in self.Futures.keys() and\
                 self.Future2.Symbol in self.Futures.keys(), 'One future did not updated.'

    def UpdateStatus(self):
        self.ModZ.Update(self.Time, self.Future1, self.Future2)
        if self.ModZ.IsReady:
            return self.MyChecks()
        return

## MANAGEMENT POSITION LOGIC: Logic specified for the position management
    def MyChecks(self):
        targets = {}
        if self.ModZ.Value > B1 and self.ModZ.Value < B2: 
            targets[self.Future1.Symbol] = FUTURE1_ORDER_QUANTITY
            targets[self.Underlying1.Symbol] = EQUITY1_ORDER_QUANTITY
            targets[self.Future2.Symbol] = 0
            targets[self.Underlying2.Symbol] = 0

        elif self.ModZ.Value < B3 and self.ModZ.Value > B4: 
            targets[self.Future2.Symbol] = FUTURE2_ORDER_QUANTITY
            targets[self.Underlying2.Symbol] = EQUITY2_ORDER_QUANTITY
            targets[self.Future1.Symbol] = 0
            targets[self.Underlying1.Symbol] = 0
        
        else:
            targets[self.Future2.Symbol] = 0
            targets[self.Underlying2.Symbol] = 0
            targets[self.Future1.Symbol] = 0
            targets[self.Underlying1.Symbol] = 0
            
        return targets


# region imports
from AlgorithmImports import *

from collections import deque

from Selection.FutureUniverseSelectionModel import FutureUniverseSelectionModel

import SymbolData
import CustomIndicators as ci
from ControlParameters import *
from MyUniverseSelectionModel import *
# endregion

class AdaptableRedOrangePanda(QCAlgorithm):

## INITIALIZATION
    def Initialize(self):
        self.SetStartDate(2020, 12, 5)  # Set Start Date
        self.SetCash(2000)  # Set Strategy Cash

        self.SetBrokerageModel(BrokerageName.QuantConnectBrokerage, AccountType.Margin)

        self.MyUniverseInitializer()
        self.SetSecurityInitializer(self.MySecurityInitializer) # Add custom modification every time a security is initialized.
                                                                # (i.e. every time the model starts to feed a security data)
        self.InitCharts()

    def InitCharts(self):
        '''Create a chart and series visualize the indicator'''
        chart = Chart("MyIndicator")
        self.AddChart(chart)

        s_b1 = Series('B1',SeriesType.Line)
        chart.AddSeries(s_b1)
        s_b1.AddPoint(datetime(2020,12,5),B1)
        s_b1.AddPoint(datetime(2021,12,5),B1)

        s_b2 = Series('B2',SeriesType.Line)
        chart.AddSeries(s_b2)
        s_b2.AddPoint(datetime(2020,12,5),B2)
        s_b2.AddPoint(datetime(2021,12,5),B2)
        
        s_b3 = Series('B3',SeriesType.Line)
        chart.AddSeries(s_b3)
        s_b3.AddPoint(datetime(2020,12,5),B3)
        s_b3.AddPoint(datetime(2021,12,5),B3)
        
        s_b4 = Series('B4',SeriesType.Line)
        chart.AddSeries(s_b4)
        s_b4.AddPoint(datetime(2020,12,5),B4)
        s_b4.AddPoint(datetime(2021,12,5),B4)

        self.s_modzor = Series('ModZ OR',SeriesType.Scatter)
        chart.AddSeries(self.s_modzor)
        self.s_modzir = Series('ModZ IR',SeriesType.Scatter)
        chart.AddSeries(self.s_modzir)

    def MyUniverseInitializer(self):
        self.SecuritiesTracker = {} # Initilize tracker parameters
        self.PairTracker = {} # 
        self.addedContracts = set()

        # Set the resolution for universal use
        # Even though this is a daily trading strategy, we set the resolution to
        # minute to have a constant flow of data. We feed the data to the algorithm with
        # a minute resolution if not, the update of the state would be too long. 
        self.UniverseSettings.Resolution = Resolution.Minute
        
        # Adding securities
        # https://www.quantconnect.com/docs/v2/writing-algorithms/datasets/algoseek/us-futures#05-Supported-Assets
        # We add the securities as a Universe Selection model so future implementation 
        # can have the adaptability to any security that gets into the Universe.
        equities = [Symbol.Create('SPY', SecurityType.Equity, Market.USA),
                Symbol.Create('QQQ', SecurityType.Equity, Market.USA)]

        # Store desired futures 
        self.__Futures = [Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME),
            Symbol.Create(Futures.Indices.NASDAQ100EMini, SecurityType.Future, Market.CME)]
        # Store current futures chain symbol
        self.__Contracts = {}

        # State the pairs to follow
        self.Pairs = {'Futures':[[1,0]],
                    'Equities':[[equities[1],equities[0]]]}

        # Universe selector for equities
        self.AddUniverseSelection(ManualUniverseSelectionModel(equities))
        # Universe selector for futures
        self.AddUniverseSelection(FrontMonthFutureUniverseSelectionModel())
    
    @property
    def FutureValues(self):
        '''Return current futures chain symbol'''
        return [self.__Contracts.get(i) for i  in self.__Futures]

    def MySecurityInitializer(self, security):  
        # When adding an future we could get 0 as Ask/Bid prices since the data has not been feed. 
        # This solves that issue
        bar = self.GetLastKnownPrice(security)
        security.SetMarketPrice(bar)

## SECURITIES LOGIC: CREATION, INDICATORS, UPDATE, TACKING

    def ManageAdded(self, added):
        '''
        Logic for securities added. Create the SymbolData objects that track the added securities.
        Inputs:
            added [list]: List of added securities
        '''
        for security in added:
            if self.SecuritiesTracker.get(security.Symbol) is None: # Create SymbolData object
                self.SecuritiesTracker[security.Symbol] = SymbolData.SymbolData(security.Symbol,
                                                                            self.get_Time)
                if security.Type == SecurityType.Future: # Manage futures
                    for i,v in self.PairTracker.items():
                        try: # If the future has already been added and the Symbol change, update It
                            indx = self.Pairs['Futures'][i].index(self.__Contracts[security.Symbol.Canonical])
                            self.Pairs['Futures'][i][indx] = security.Symbol
                        except ValueError:
                            pass

                        # If the future has already been added and the Symbol change, update It
                        if self.__Contracts[security.Symbol.Canonical] in v.Futures.keys():
                            self.PairTracker[i].UpdateFuture(self.__Contracts[security.Symbol.Canonical],
                                                            security.Symbol,
                                                            security)
                    # Create it if added for first time or update it
                    self.__Contracts[security.Symbol.Canonical] = security.Symbol
                              
    def ManageRemoved(self, removed):
        '''
        Logic for securities removed. Remove the SymbolData objects.
        Inputs:
            removed [list]: List of removed securities
        '''
        for security in removed: # Don't track anymore
            if self.Portfolio[security.Symbol].Invested:
                self.Liquidate(security.Symbol) 
            self.SecuritiesTracker.pop(security.Symbol, None)
                
    def ReplacePending(self, pair):
        '''
        The self.Pair["Futures"] created in "MyUniverseInitializer" method initially stores the 
        position of the self.__Futures added, these positions will be replaced by the numbers once
        the current future chain symbols are added to the universe.
        Inputs:
            pair [list]: List of Symbols or integers, in case it was already replaced or is needed respectively.
        '''
        ft_value = self.FutureValues
        if isinstance(pair[0], int) and ft_value[pair[0]] is not None:
            pair[0] = ft_value[pair[0]]
            
        if isinstance(pair[1], int) and ft_value[pair[1]] is not None:
            pair[1] = ft_value[pair[1]]
        
        return pair

    def CheckPairs(self):
        '''
        Create a PairData Object once all the securities needed for the pair have been added.
        '''
        for i in range(len(self.Pairs['Futures'])):
            self.Pairs['Futures'][i] = self.ReplacePending(self.Pairs['Futures'][i])
            if isinstance(self.Pairs['Futures'][i][0], int) or isinstance(self.Pairs['Futures'][i][1], int):
                continue
            f1 = self.Securities.get(self.Pairs['Futures'][i][0])
            f2 = self.Securities.get(self.Pairs['Futures'][i][-1])
            e1 = self.Securities.get(self.Pairs['Equities'][i][0])
            e2 = self.Securities.get(self.Pairs['Equities'][i][-1])
            if not(None in [f1,f2,e1,e2]):
                self.PairTracker[i] = SymbolData.PairData(f1, f2,
                                                        e1,e2,
                                                        self.get_Time)
                 
    def OnSecuritiesChanged(self, changes: SecurityChanges) -> None:
        # Gets an object with the changes in the universe
        # For the added securities we create a SymbolData object that allows us to track the orders associated and the indicators created for it.
        # We do this per equity due to the fact that we can have more than one contract per underlying security.
        self.ManageAdded(changes.AddedSecurities)
        
        # The removed securities are liquidated and removed from the security tracker.
        self.ManageRemoved(changes.RemovedSecurities)

        # Create pairs objects
        self.CheckPairs()    

## CHECK FOR BUYING POWER

    def CheckBuyingPower(self, symbol, quantity):
        '''
        Check for enough buying power.
        If the buying power for the target quantity is not enough,
        It will return the quantity for which the buying power is enough.
        '''
        if quantity < 0:
            order_direction = OrderDirection.Sell
        else:
            order_direction = OrderDirection.Buy
        # Get the buying power depending of the order direction and symbol
        buy_power = self.Portfolio.GetBuyingPower(symbol, order_direction)
        # Compute possible quantity
        q_t = abs(buy_power) / self.Securities[symbol].Price
        # Select minimum quantity
        return round(min(abs(quantity),q_t),8)*np.sign(quantity)

    def CheckOrdeQuatity(self, symbol, quantity):
        '''Check that the quantity of shares computed meets the minimum requirments'''
        q = abs(quantity)
        # There are requirements for the minimum or maximum that can be purchased per security.
        if q > self.Settings.MinAbsolutePortfolioTargetPercentage and q < self.Settings.MaxAbsolutePortfolioTargetPercentage:
            symbol_properties = self.Securities[symbol].SymbolProperties
            if symbol_properties.MinimumOrderSize is None or q > symbol_properties.MinimumOrderSize:
                return True
        return False

## POSITION MANAGEMENT

    def CheckOpenMarket(self, symbol):
        '''Check if the market is open for symbol'''
        last_open = self.Securities[symbol].Exchange.Hours.GetPreviousMarketOpen(self.Time, False)
        next_close = self.Securities[symbol].Exchange.Hours.GetNextMarketClose(self.Time, False)
        return (last_open < self.Time and next_close > self.Time) and \
            (last_open.day == self.Time.day and next_close.day == self.Time.day)

    def OnData(self, data: Slice):
        # The OnData method is called every time the data is feed into the algorithm.
        for i,pair in self.PairTracker.items(): # Iterate Over the pairs on track
            targets = pair.UpdateStatus() # Update ModZ Indicator and get target quantities per security
            # Time between market hours and same day check
            if self.CheckOpenMarket(pair.Future1.Symbol) and self.CheckOpenMarket(pair.Underlying1.Symbol) and\
            self.CheckOpenMarket(pair.Future2.Symbol) and self.CheckOpenMarket(pair.Underlying2.Symbol) and pair.IsReady:
                # Sort It; First sell and then buy
                targets = {v:pair.MyItems[k] for k,v in sorted(targets.items(), key=lambda item: item[1])}
                if np.sum(list(targets.keys())) == 0: # Plot indicator
                    self.s_modzor.AddPoint(pair.ModZ.EndTime ,pair.ModZ.Value)
                else:
                    self.s_modzir.AddPoint(pair.ModZ.EndTime ,pair.ModZ.Value)
            
                for target,s in targets.items():
                    # Check Roll Over
                    if s.Type == SecurityType.Future:
                        symbol = self.CheckRollOver(data, s.Symbol, pair)
                    else:
                        symbol = s.Symbol
                    # Check current holdings and get the quantity to send the order
                    tracker = self.SecuritiesTracker.get(symbol)
                    quantity = self.Portfolio[symbol].Quantity
                    quantity = tracker.GetOrderQuantity(quantity,target)
                    if quantity == 0:
                        continue
                    if s.Type == SecurityType.Future:
                        self.FutureOrders(data, symbol, tracker, quantity)
                    else:
                        self.EquityOrders(data, symbol, tracker, quantity)

                # Check for Buying logic and perform create positions If meets the requirements.
                # self.CreatePositions(data,symbol,tracker)

    def CheckRollOver(self, data, symbol, pair):
        '''In case of roll over, this function will update the data in the security tracker'''
        roll = data.SymbolChangedEvents.get(symbol)
        if roll is not None and roll.OldSymbol in self.SecuritiesTracker.keys():
            # Update Security Tracker
            self.SecuritiesTracker[roll.NewSymbol] = self.SecuritiesTracker[roll.OldSymbol] 
            self.SecuritiesTracker[roll.NewSymbol].Symbol(roll.NewSymbol)
            self.SecuritiesTracker.pop(roll.OldSymbol)

            # Update Pairs
            pair.UpdateFuture(roll.OldSymbol, roll.NewSymbol, self.Securities[roll.NewSymbol])
            return roll.NewSymbol
        return symbol

    def FutureOrders(self, data, symbol, tracker, quantity):
        '''
        Implement the logic to purchase futures.
        Inputs:
            - symbol [Symbol QC object]: QC Symbol identifier of the securities.
            - tracker [SymbolData object]: Tracker created for the specific symbol.
        '''
        if data.ContainsKey(symbol) and data[symbol] is not None: # The indicators are ready and the security has been feeded with data
                chain = data.FuturesChains.get(symbol.Canonical)
                contract = self.FuturesFilter(symbol, chain) # Call the Future Filter funtion and get the better contract
                if contract: # A suitable has been found
                    quantity = self.CheckBuyingPower(contract.Symbol, quantity) # Check for buying power
                    if self.CheckOrdeQuatity(contract.Symbol, quantity): # Check for the minimum quantity
                        buy = self.LimitOrder(contract.Symbol,# Create an order for a call contract
                                            quantity,
                                            round(self.Securities[contract.Symbol].AskPrice * (1 + np.sign(quantity)*LIMIT_ORDER),2)) 
                        tracker.Order = buy # Save in the security tracker the contract and order

    def EquityOrders(self, data, symbol, tracker,quantity):
        '''
        Implement the logic to purchase futures.
        Inputs:
            - symbol [Symbol QC object]: QC Symbol identifier of the securities.
            - tracker [SymbolData object]: Tracker created for the specific symbol.
        '''
        if data.ContainsKey(symbol) and data[symbol] is not None: # The indicators are ready and the security has been feeded with data
            quantity = self.CheckBuyingPower(symbol, quantity) # Check for buying power
            if self.CheckOrdeQuatity(symbol, quantity): # Check for the minimum quantity
                buy = self.MarketOrder(symbol, quantity) # Create an order
                tracker.Order = buy # Save in the security tracker the contract and order
                        
## OPTIONS: Chain Provider

    def FuturesFilter(self, symbol, chain):
        '''
        Inputs: 
            - symbol [Symbol QC object]: QC Symbol identifier of the future.
            - chain [Symbol QC object]: Chain of contracts.

        Return:
            If a contract meets the requirements, Future Contract object, otherwise None.
        '''
        if not chain:
            return False
        
        if len(chain.Contracts) > 0:
            # Filter Contract list
            contract = sorted(chain.Contracts.values(), key=lambda k : k.OpenInterest, reverse=True)[0] # Order by most popular
            
            if contract not in self.addedContracts:
                self.addedContracts.add(contract.Symbol)
                # Use AddFutureContract() to subscribe the data for a specified contract
                future_cont = self.AddFutureContract(contract.Symbol)
                return contract
        
        return None