Overall Statistics
Total Trades
88
Average Win
2.60%
Average Loss
-2.18%
Compounding Annual Return
62.863%
Drawdown
28.600%
Expectancy
0.397
Net Profit
61.562%
Sharpe Ratio
1.448
Probabilistic Sharpe Ratio
54.726%
Loss Rate
36%
Win Rate
64%
Profit-Loss Ratio
1.20
Alpha
0.672
Beta
-0.074
Annual Standard Deviation
0.455
Annual Variance
0.207
Information Ratio
0.838
Tracking Error
0.56
Treynor Ratio
-8.871
Total Fees
$218.18
from arch import arch_model
import numpy as np

class StrategyManagerUtility:
    def __init__(self, quantScenario):
        self.quantScenario = quantScenario
    
    def setupModel(self, preference):
    # GJR GARCH model

        # Fit the model
        try:    
            egarch_gm = arch_model(preference.garchData, p = 1, q = 1, o = 1, vol = 'GARCH', dist = 't')
                
            egarch_result = egarch_gm.fit(disp = 'off')
                
            # Make 1-period ahead forecast
            gm_forecast = egarch_result.forecast(horizon = 1)
            preference.egarchMean = gm_forecast.mean.values[-1 ]
            if preference.egarchMean:
                preference.egarchMeanArray = np.append( preference.egarchMeanArray, preference.egarchMean)
            else:
                pass
            
            #printing the logs
            self.genericGarchLog(preference, gm_forecast)
                
            garchOutput = gm_forecast.variance[-1:].values
            
            if preference.quantScenario.IsWarmingUp:
                return
            else:
                self.setHoldingsBasedOnEGarch(garchOutput[0], preference)
        except Exception:
            pass
        
        #self.Debug("----------> " + str(self.gm_result.summary()))
    
    def setHoldingsBasedOnEGarch(self, garchOutput, preference):
        
        self.multipler = 1
        
        if garchOutput[0] < preference.egarchMean:
            preference.quantScenario.SetHoldings(preference.tradedETFSymbol, 1)
        elif garchOutput[0] > preference.egarchMean*self.multipler and  garchOutput[0] > 0:
            #self.SetHoldings(self.tradedETFSymbol, (self.egarchMean)/garchOutput[0])
            sdVariance = (preference.egarchMean)/garchOutput[0]
            preference.quantScenario.SetHoldings(preference.tradedETFSymbol, sdVariance)
            #self.SetHoldings([PortfolioTarget("TQQQ", 0), PortfolioTarget("ETF", int(sdVariance))], True)
            
    def genericGarchLog(self, preference, inputGm_forecast):
        
        preference.quantScenario.Debug(str(preference.quantScenario.Time))
        preference.quantScenario.Debug("Mean, based on variance: "+ str(preference.egarchMean))
        preference.quantScenario.Debug("Forecast Variance based on Garch Data: "+ str(inputGm_forecast.variance[-1:].values))
        preference.quantScenario.Debug("Standard Deviation, today's Garch element: "+str(preference.garchData[-1]))
        preference.quantScenario.Debug("Garch Data: "+ str(preference.garchData[:5]))
from InvestorPreferenceUtility import InvestorPreferenceUtility

class SOXLStandardDeviation(QCAlgorithm):
    
    def Initialize(self):
        
        self.investorPrefernce = InvestorPreferenceUtility(self)    
    
    def OnData(self, data):
        '''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
        '''
from StrategyManagerUtility import StrategyManagerUtility
import numpy as np
    
class InvestorPreferenceUtility:
    
    def __init__(self, quantconnectObject):
        self.quantScenario = quantconnectObject
        self.strategyManager = StrategyManagerUtility(self)
        
        self.initalizeAttributes()
        self.setupIndicators()
        self.setupScheduler()
        
    def initalizeAttributes(self):
        self.quantScenario.SetStartDate(2020, 1, 1)
        self.quantScenario.SetEndDate(2020, 12, 31) 
        self.quantScenario.SetCash(100000)  # Set Strategy Cash
        self.leveragedETFSymbol = "VOO"
        self.tradedETFSymbol = "UPRO"
        self.minutesBeforeMarketClose = 1
        self.counter = 0
        self.investmentInterval = 14
        self.indicatorPeriod = 7
        self.garchData = []
        self.priceHistory = []
        self.egarchMean = 0
        list = []
        self.egarchMeanArray = np.array(list)
        self.resolution = Resolution.Daily
        
    def setupIndicators(self):
        #Warming up the function
        self.warmUpTimeForBacktestScenario = self.indicatorPeriod
        self.quantScenario.SetWarmUp(self.warmUpTimeForBacktestScenario)
        
        #1. Update the AddEquity command to request TQQQ data
        self.leveragedETF = self.quantScenario.AddEquity(self.leveragedETFSymbol, self.resolution)
        self.leveragedETF.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
        #1. Update the AddEquity command to request SOXL data
        self.tradedETF = self.quantScenario.AddEquity(self.tradedETFSymbol, self.resolution)
        self.tradedETF.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
        self.hedge = self.quantScenario.AddEquity("SQQQ", self.resolution)
        self.hedge.SetDataNormalizationMode(DataNormalizationMode.Raw)
        
        #Setup standard deviation indicator
        self.quantScenario.stdIndicator = self.quantScenario.STD(self.leveragedETFSymbol, self.indicatorPeriod, self.resolution)
    
    def setupScheduler(self):
        self.quantScenario.Schedule.On(self.quantScenario.DateRules.EveryDay(self.leveragedETFSymbol), self.quantScenario.TimeRules.BeforeMarketClose(self.leveragedETFSymbol, self.minutesBeforeMarketClose), self.dailyComputation)
        
        #self.quantScenario.Schedule.On(self.quantScenario.DateRules.EveryDay(self.leveragedETFSymbol), self.quantScenario.TimeRules.BeforeMarketClose(self.leveragedETFSymbol, self.minutesBeforeMarketClose), self.dollarCostAverage)

    def dollarCostAverage(self):
        self.counter += 1
        
        if self.counter % self.investmentInterval:
            investment = 1666
            self.quantScenario.Portfolio.SetCash(self.quantScenario.Portfolio.Cash + investment);
    
    def dailyComputation(self):
        
        #calculate the percentage change
        self.garchData.append(self.quantScenario.stdIndicator.Current.Value)
        
        self.counter += 1
        
        self.strategyManager.setupModel(self)
        
        #if "2020-02-10" in str(self.quantScenario.Time):
        #    self.injectTodaysStandardDeviationValue(1.2)
        
        if len(self.garchData) > 60:
            self.garchData.pop(0)
        
        #self.Debug("Garch Data: " + str(data))
    
    def injectTodaysStandardDeviationValue(self, inputStandardDeviation):
        
        self.garchData.append(inputStandardDeviation)
        self.strategyManager.setupModel(self)
        #mean =  np.mean(preference.egarchMeanArray)
        #preference.quantScenario.Debug("egarch mean: " + str(mean))