| 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 holdingPercentagefrom 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()