Overall Statistics
Total Trades
106
Average Win
6.05%
Average Loss
-2.35%
Compounding Annual Return
102.295%
Drawdown
36.200%
Expectancy
1.234
Net Profit
294.496%
Sharpe Ratio
1.797
Probabilistic Sharpe Ratio
58.991%
Loss Rate
38%
Win Rate
62%
Profit-Loss Ratio
2.57
Alpha
1.236
Beta
0.068
Annual Standard Deviation
0.743
Annual Variance
0.552
Information Ratio
-0.139
Tracking Error
1.016
Treynor Ratio
19.751
Total Fees
$287.51
from StrategyCalculator import StrategyCalculatorUtility
class StrategyManagerUtility:
    
    def __init__(self, preferences):
        self.strategyCalculator = StrategyCalculatorUtility()
        self.investorPreferences = preferences
        self.quantScenario = self.investorPreferences.quantScenario
        self.data = []
        self.preRecessionPurchaseBufferCounter = 0
        self.defaultPreRecessionHoldingPercentage = .1
        self.preRecessionHoldingPercentage = self.defaultPreRecessionHoldingPercentage
        self.smaOneSlope = 0
        self.preRecessionHoldingFlag = False
        #Rolling windows is used to compare current and previous stock prices accross multiple trading days
        self.window = RollingWindow[TradeBar](2)
        
    def attemptToCalculateSlopeOfSMAWithinRange(self, dailySlopeInput, rangeInDays):
        self.smaOneSlope = self.strategyCalculator.attemptToCalculateSlopeWithinRange(self.data, dailySlopeInput, rangeInDays)

    def downWardTrendingAllocation(self):
        
        if "2020-02-24" in str(self.quantScenario.UtcTime):
            #self.quantScenario.Debug("Here")
            pass
        
        if self.investorPreferences.spSMAFive.Current.Value > self.investorPreferences.smaBenchmarkEquity.Price:
           #self.quantScenario.Debug("Cash: " + str(self.quantScenario.Portfolio.Cash) + ", Number of SOXL Shares: " + str(self.quantScenario.Portfolio["SOXL"].Quantity) + ", Stock Price: " + str(self.quantScenario.Portfolio["SOXL"].Price))
           if self.isRecessionaryDip:
                self.attemptAgressivelyPurchaseEquityWithHedging(self.investorPreferences.slowETFSymbol)
       
        elif self.investorPreferences.spSMAFour.Current.Value > self.investorPreferences.smaBenchmarkEquity.Price:
            
           if not self.preRecessionHoldingFlag:
               #self.quantScenario.Debug("Below --------------------------->")
              self.preRecessionHoldingFlag = True
              self.quantScenario.SetHoldings([PortfolioTarget("TQQQ", .25), PortfolioTarget("SQQQ", 0), PortfolioTarget("SOXL", 0), PortfolioTarget("SOXS", 0)], True)
            
        elif self.investorPreferences.slowSMAThree.Current.Value > self.investorPreferences.slowETF.Price:
            if not self.preRecessionHoldingFlag:
                self.quantScenario.SetHoldings(self.investorPreferences.slowETFSymbol, .45, True)
            #self.quantScenario.Debug(str(self.quantScenario.UtcTime) + ", "+ "SOXL Shares: " + str(self.quantScenario.Portfolio["SOXL"].Quantity) + ", SOXS Shares: " + str(self.quantScenario.Portfolio["SOXS"].Quantity) + ", TQQQ Shares: " + str(self.quantScenario.Portfolio["TQQQ"].Quantity) + ", Cash: " +str(self.quantScenario.Portfolio.Cash) + "PercentageHolding: " + str(self.preRecessionHoldingPercentage))
               
        elif self.investorPreferences.slowSMATwo.Current.Value > self.investorPreferences.slowETF.Price:
           #self.quantScenario.Debug("< ---------> Between <--------->")
           self.quantScenario.SetHoldings(self.investorPreferences.slowETFSymbol, .95, True)
           self.preRecessionHoldingFlag = False
        
        elif (self.investorPreferences.slowETF.Price > self.investorPreferences.slowSMAOne.Current.Value) and (self.investorPreferences.slowETF.Price > self.investorPreferences.slowSMAThree.Current.Value * 1):
           #self.quantScenario.Debug("<--------------------------- Above")
           
           self.quantScenario.SetHoldings(self.investorPreferences.slowETFSymbol, 1, True)
           #self.quantScenario.Debug("SOXL: " + str(self.quantScenario.Portfolio["SOXL"].Quantity) + ", Price: " + str(self.quantScenario.Portfolio["SOXL"].Price) + 
           #", TQQQ: " + str(self.quantScenario.Portfolio["TQQQ"].Quantity) + ", Price: " + str(self.quantScenario.Portfolio["TQQQ"].Price))
           
           self.preRecessionHoldingFlag = False
           preRecessionPurchaseBufferCounter = 0
           self.preRecessionHoldingPercentage = self.defaultPreRecessionHoldingPercentage
    
    def isRecessionaryDip(self):
        etfWithIncreasedDepth = self.investorPreferences.slowETF.Price + self.investorPreferences.slowETF.Price*.16 # This multipler has a big impact on the gains during a recession ---------------------------->
        magnifyDifferenceInSMAAndPrice = self.investorPreferences.slowETF.Price * 1.05
        
        if self.smaOneSlope > 0:
            #self.quantScenario.Debug("here")
            pass
        
        if (magnifyDifferenceInSMAAndPrice > self.investorPreferences.slowSMAOne.Current.Value) and ( self.investorPreferences.slowSMAFour.Current.Value > etfWithIncreasedDepth) and (self.smaOneSlope > 0.4) :
            return True
    
    def attemptAgressivelyPurchaseEquityWithHedging(self, etfSymbol):

        self.preRecessionPurchaseBufferCounter = self.preRecessionPurchaseBufferCounter + 1
        if self.preRecessionPurchaseBufferCounter % 3 == 0 and self.preRecessionHoldingPercentage < 1 :
            self.preRecessionHoldingPercentage = self.strategyCalculator.rankPurchase(self.investorPreferences.slowETF, self.preRecessionHoldingPercentage, self.investorPreferences.slowSMAFive.Current.Value)
            self.quantScenario.SetHoldings([PortfolioTarget(self.investorPreferences.fastETFSymbol, self.preRecessionHoldingPercentage), PortfolioTarget(self.investorPreferences.fastHedgeSymbl, 1 - self.preRecessionHoldingPercentage), PortfolioTarget(self.investorPreferences.slowETFSymbol, 0)])
            self.quantScenario.Debug(str(self.quantScenario.UtcTime) + ", "+ "SOXL Shares: " + str(self.quantScenario.Portfolio["SOXL"].Quantity) + ", SOXS Shares: " + str(self.quantScenario.Portfolio["SOXS"].Quantity) + ", TQQQ Shares: " + str(self.quantScenario.Portfolio["TQQQ"].Quantity) + ", Cash: " +str(self.quantScenario.Portfolio.Cash) + "PercentageHolding: " + str(self.preRecessionHoldingPercentage))
            
    def attemptToPurchaseEquity(self, etfSymbol):

        self.preRecessionPurchaseBufferCounter = self.preRecessionPurchaseBufferCounter + 1
        if self.preRecessionPurchaseBufferCounter % 3 == 0 and self.preRecessionHoldingPercentage < 1 :
            self.preRecessionHoldingPercentage = self.strategyCalculator.rankPurchase(self.investorPreferences.slowETF, self.preRecessionHoldingPercentage, self.investorPreferences.slowSMAFive.Current.Value)
            self.quantScenario.SetHoldings(self.investorPreferences.fastETFSymbol, self.preRecessionHoldingPercentage , True)
             
            #self.quantScenario.Debug("$$$$ Purchased Dip: " + str(self.investorPreferences.fastETF.Price) + ", Cash after purchase: " + str(self.quantScenario.Portfolio.Cash))
        elif self.preRecessionHoldingPercentage >= 1:
            self.quantScenario.SetHoldings(self.investorPreferences.fastETFSymbol, self.preRecessionHoldingPercentage , True)
class StrategyCalculatorUtility:
        
    def attemptToCalculateSlopeWithinRange(self, inputData, dailySlopeInput, inputRange):
        #inputData(0, self.investorPreferences.slowSMAOne.Current.Value)
        inputData.insert(0, dailySlopeInput)
        #self.quantScenario.Debug("Historical SMA Data: " +str(inputData))
        
        if len(inputData) > inputRange:
            currentPriceOfMovingAverage = inputData[0]
            currentOffsetForMovingAverage = 1
            pastPriceOfMovingAverage = inputData[inputRange]
            pastOffsetForMovingAverage = inputRange
        
            return self.calcSlope(pastOffsetForMovingAverage, pastPriceOfMovingAverage, currentOffsetForMovingAverage, currentPriceOfMovingAverage)
        else:
            return -1
            
    def calcSlope(self, x1, y1, x2, y2):
        m = (y2-y1)/(x2-x1)
        return m
        #self.quantScenario.Debug("Time" + str(self.UtcTime) + "------------------------------Slope: " + str(self.smaOneSlope) + ", SMA - One: " +str(self.investorPreferences.slowSMAOne.Current.Value) + ", TQQQPrice: " +str(self.investorPreferences.slowETF.Price))
    
    def rankPurchase(self, inputETF, inputHoldingPercentage, benchmarkSMAValue):
        holdingPercentage = inputHoldingPercentage
        sharePrice = inputETF.Price
        benchmark = benchmarkSMAValue
        difference = (benchmark-sharePrice)/benchmark
        percentageOfStockToPurchase = 0
        
        if difference >= .5:
            percentageOfStockToPurchase = .6
        elif difference >= .44:
            percentageOfStockToPurchase = .4
        elif difference >= .35:
            percentageOfStockToPurchase = .2
        '''elif difference >= .25:
            percentageOfStockToPurchase = .1
        elif difference >= .2:
            percentageOfStockToPurchase = .08
        elif difference >= .15:
            percentageOfStockToPurchase = .05
        elif difference >= .1:
            percentageOfStockToPurchase = .03
        elif difference >= .05:
            percentageOfStockToPurchase = .01'''
            
        holdingPercentage = holdingPercentage + percentageOfStockToPurchase
        
        if  holdingPercentage > 1:
             holdingPercentage = 1
        return holdingPercentage
from InvestorPreferenceUtility import InvestorPreferenceUtility

class SMAMomentumPreRecessionAllocation(QCAlgorithm):
    
    def setupEquitiesForTrading(self):
        self.movingAverages = {'smaOne': 5, 'smaTwo': 25, 'smaThree': 50, 'smaFour': 90, 'smaFive': 200}
        
        self.preferences.initLeveragedETFs("SOXL", "TQQQ")
        self.preferences.injectInitalBacktestParameters()
        
        self.preferences.initSlowSimpleMovingAverages(self.movingAverages["smaOne"], self.movingAverages["smaTwo"], self.movingAverages["smaThree"], self.movingAverages["smaFour"], self.movingAverages["smaFive"])
        
        self.Schedule.On(self.DateRules.EveryDay(self.preferences.slowETFSymbol), self.TimeRules.BeforeMarketClose(self.preferences.slowETFSymbol, self.preferences.minutesBeforeMarketClose), self.preferences.dailyPortfolioAdjustmentsBeforeMarketClose)

    def Initialize(self):
        self.preferences = InvestorPreferenceUtility(self)
        self.setupEquitiesForTrading()
        
    def OnData(self, data):
        
         # Don't place trades until indicators are warmed up
        if self.IsWarmingUp:
            return
        
        if not self.Portfolio.Invested:
            
            self.SetHoldings(self.preferences.slowETFSymbol, 1)
            # Add TradeBar in rolling window
            self.preferences.strategyManager.window.Add(data[self.preferences.fastETFSymbol])
from StrategyManager import StrategyManagerUtility
class InvestorPreferenceUtility:
    
    def __init__(self, quantconnectObject):
        self.quantScenario = quantconnectObject
        self.strategyManager = StrategyManagerUtility(self)
    
    def initSMABenchmark(self):
        self.smaBenchmarkSymbol = "VOO"
        self.smaBenchmarkEquity = self.quantScenario.AddEquity(self.smaBenchmarkSymbol, Resolution.Daily)
    
    def initHedge(self):
        self.slowHedgeSymbol = "SQQQ"
        self.fastHedgeSymbl = "SOXS"
        
        self.quantScenario.AddEquity(self.slowHedgeSymbol, Resolution.Daily)
        self.quantScenario.AddEquity(self.fastHedgeSymbl, Resolution.Daily)
        
    def initLeveragedETFs(self, fastSybmbol, slowSybol):
        self.fastETFSymbol = fastSybmbol
        self.slowETFSymbol = slowSybol
        
        self.initETFs(self.fastETFSymbol, self.slowETFSymbol)
        self.initHedge()
        
    def initETFs(self, fastETFSymbol, slowETFSymbol):
        #Setup Agressive ETF
        self.fastETF = self.quantScenario.AddEquity(fastETFSymbol, Resolution.Daily)
        self.fastETF.SetDataNormalizationMode(DataNormalizationMode.Adjusted)
        
        #Setup Conversative ETF
        self.slowETF = self.quantScenario.AddEquity(slowETFSymbol, Resolution.Daily)  
        self.slowETF.SetDataNormalizationMode(DataNormalizationMode.Adjusted)
        
    def injectInitalBacktestParameters(self):
        self.minutesBeforeMarketClose = 60
        
        self.quantScenario.SetStartDate(2019, 1, 1)
        self.quantScenario.SetEndDate(2020, 12, 20)
        self.quantScenario.SetCash(100000)
        self.warmUpTimeForBacktestScenario = 90

        self.quantScenario.SetBenchmark(self.slowETFSymbol) 
        self.quantScenario.SetWarmUp(self.warmUpTimeForBacktestScenario)
        
        #setup cash buffer
        self.quantScenario.Settings.FreePortfolioValuePercentage = 0.1
        
    def initSlowSimpleMovingAverages(self, one, two, three, four, five):
        self.slowSMAOne = self.quantScenario.SMA(self.slowETFSymbol, one, Resolution.Daily) 
        self.slowSMATwo = self.quantScenario.SMA(self.slowETFSymbol, two, Resolution.Daily) 
        self.slowSMAThree = self.quantScenario.SMA(self.slowETFSymbol, three, Resolution.Daily) 
        self.slowSMAFour = self.quantScenario.SMA(self.slowETFSymbol, four, Resolution.Daily)
        self.slowSMAFive = self.quantScenario.SMA(self.slowETFSymbol, five, Resolution.Daily)
        
        #init Benchmark ETF 
        self.initSandPbenchmarkMovingAverages(one, two, three, four, five)
    
    def initSandPbenchmarkMovingAverages(self, one, two, three, four, five):
        
        self.initSMABenchmark()
        
        self.spSMAOne = self.quantScenario.SMA(self.smaBenchmarkSymbol, one, Resolution.Daily) 
        self.spSMATwo = self.quantScenario.SMA(self.smaBenchmarkSymbol, two, Resolution.Daily) 
        self.spSMAThree = self.quantScenario.SMA(self.smaBenchmarkSymbol, three, Resolution.Daily) 
        self.spSMAFour = self.quantScenario.SMA(self.smaBenchmarkSymbol, four, Resolution.Daily)
        self.spSMAFive = self.quantScenario.SMA(self.smaBenchmarkSymbol, five, Resolution.Daily)
    
    def dailyPortfolioAdjustmentsBeforeMarketClose(self):
        
        dailySlopeValueFromSMA = self.slowSMAOne.Current.Value
        slopeCalculationRangeInDays = 20 
        self.strategyManager.attemptToCalculateSlopeOfSMAWithinRange(dailySlopeValueFromSMA, slopeCalculationRangeInDays)
        
        self.executeSMAAllocationBasedStrategy()
    
    def executeSMAAllocationBasedStrategy(self):
        
        #preventingTrades from being made before the algorithm has had a change to warmup
        if self.quantScenario.IsWarmingUp:
            return
        
        #identify Opportunities to Reallocate Investments:
        self.strategyManager.downWardTrendingAllocation()