Overall Statistics |
Total Trades 489 Average Win 4.13% Average Loss -2.00% Compounding Annual Return 10.300% Drawdown 18.500% Expectancy 0.517 Net Profit 911.427% Sharpe Ratio 0.709 Probabilistic Sharpe Ratio 3.221% Loss Rate 50% Win Rate 50% Profit-Loss Ratio 2.06 Alpha 0.074 Beta 0.038 Annual Standard Deviation 0.108 Annual Variance 0.012 Information Ratio 0.073 Tracking Error 0.189 Treynor Ratio 2.019 Total Fees $17886.63 Estimated Strategy Capacity $1100000.00 Lowest Capacity Asset BIL TT1EBZ21QWKL |
#region imports from AlgorithmImports import * #endregion import decimal as d from datetime import datetime, timedelta from decimal import Decimal import calendar """ class MyAlgo(QCAlgorithm): def Initialize(self): AddEquity("SPY") self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday, DayOfWeek.Monday), \ self.TimeRules.AfterMarketOpen(self.spy), \ Action(self.open_positions)) self.Schedule.On(self.DateRules.Every(DayOfWeek.Friday, DayOfWeek.Friday), \ self.TimeRules.BeforeMarketClose(self.spy, 30), \ Action(self.close_positions)) def open_positions(self): self.SetHoldings("SPY", 0.10) def close_positions(self): self.Liquidate("SPY") """ class VigilantAssetAllocationAlgorithm(QCAlgorithm): def Initialize(self): #self.SetBrokerageModel(BrokerageName.QuantConnectBrokerage, AccountType.Margin) self.SetWarmUp(126, Resolution.Daily) self.SetCash(100000) self.SetStartDate(1999, 1, 1) #self.SetEndDate(2002, 1, 1) self.LastRotationTime = datetime.min self.RotationInterval = timedelta(days=1) self.first = True self.AddRiskManagement(MaximumDrawdownPercentPerSecurity(0.025)) #self.AddRiskManagement(TrailingStopRiskManagementModel(0.025)) self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol self.sma = self.SMA(self.spy, 20, Resolution.Daily) #These are the growth symbols we'll rotate through GrowthSymbols = ["EFA","SPY","EEM","VTI","QQQ"] #GrowthSymbols = ["VOO","VEU","VNQ"] # these are the safety symbols we go to when things are looking bad for growth SafetySymbols = ["BIL", "TLT"] # I split the indicators into two different sets to make it easier for illustrative purposes below. # Storing all risky asset data into SymbolData object self.SymbolData = [] for symbol in list(GrowthSymbols): self.AddSecurity(SecurityType.Equity, symbol, Resolution.Minute).SetLeverage(10) self.oneMonthPerformance = self.MOMP(symbol, 21, Resolution.Daily) self.threeMonthPerformance = self.MOMP(symbol, 63, Resolution.Daily) self.sixMonthPerformance = self.MOMP(symbol, 126, Resolution.Daily) self.twelveMonthPerformance = self.MOMP(symbol, 252, Resolution.Daily) self.SymbolData.append([symbol, self.oneMonthPerformance, self.threeMonthPerformance, self.sixMonthPerformance, self.twelveMonthPerformance]) # Storing all risk-free data into SafetyData object self.SafetyData = [] for symbol in list(SafetySymbols): self.AddSecurity(SecurityType.Equity, symbol, Resolution.Minute).SetLeverage(10) self.oneMonthPerformance = self.MOMP(symbol, 21, Resolution.Daily) self.threeMonthPerformance = self.MOMP(symbol, 63, Resolution.Daily) self.sixMonthPerformance = self.MOMP(symbol, 126, Resolution.Daily) self.twelveMonthPerformance = self.MOMP(symbol, 252, Resolution.Daily) #self.SymbolData.append([symbol, self.oneMonthPerformance, self.threeMonthPerformance, self.sixMonthPerformance, self.twelveMonthPerformance]) self.SafetyData.append([symbol, self.oneMonthPerformance, self.threeMonthPerformance, self.sixMonthPerformance, self.twelveMonthPerformance]) self.Schedule.On(self.DateRules..EveryDay(), self.TimeRules.BeforeMarketClose("BIL", 1), self.Rebalance) self.rebalance = True def OnData(self, data): if self.first: self.first = False #self.LastRotationTime = self.Time return #delta = self.Time - self.LastRotationTime #if delta > self.RotationInterval: if self.rebalance == True: #self.LastRotationTime = self.Time ##Using the Score class at the bottom, compute the score for each risky asset. ##This approach overweights the front month momentum value and progressively underweights older momentum values #currentHoldings = all_symbols = [ x for x in self.Portfolio.Keys ] orderedObjScores = sorted(self.SymbolData, key=lambda x: Score(x[1].Current.Value,x[2].Current.Value,x[3].Current.Value,x[4].Current.Value).ObjectiveScore(), reverse=True) ##Using the Score class at the bottom, compute the score for each risk-free asset. orderedSafeScores = sorted(self.SafetyData, key=lambda x: Score(x[1].Current.Value,x[2].Current.Value,x[3].Current.Value,x[4].Current.Value).ObjectiveScore(), reverse=True) ##Count the number of risky assets with negative momentum scores and store in N. If all four of the offensive assets exhibit positive momentum scores, ##select the offensive asset with the highest score and allocate 100% of the portfolio to that asset at the close N = 0 for x in orderedObjScores: self.Log(">>SCORE>>" + x[0] + ">>" + str(Score(x[1].Current.Value,x[2].Current.Value,x[3].Current.Value,x[4].Current.Value).ObjectiveScore())) if Score(x[1].Current.Value,x[2].Current.Value,x[3].Current.Value,x[4].Current.Value).ObjectiveScore() < 0: N += 1 # pick which one is best from risky and risk-free symbols and store for use below bestGrowth = orderedObjScores[0] secondGrowth = orderedObjScores[1] bestSafe = orderedSafeScores[0] #secondSafe = orderedSafeScores[1] ## If any of the four risky assets exhibit negative momentum scores, select the risk-free asset (LQD, IEF or SHY) with the highest score ## (regardless of whether the score is > 0) and allocate 100% of the portfolio to that asset at the close. if N > 0: self.Log("PREBUY>>LIQUIDATE>>") self.Liquidate() # self.Log(">>BUY>>" + str(bestSafe[0]) + "@" + str(Decimal(100) * bestSafe[1].Current.Value)) self.SetHoldings(bestSafe[0], 1) #self.SetHoldings(currentHoldings[0], .75) #self.SetHoldings(secondSafe[0], .5) self.rebalance = False elif self.Securities[self.spy].Close > self.sma.Current.Value: self.Log("PREBUY>>LIQUIDATE>>") self.Liquidate() # self.Log(">>BUY>>" + str(bestGrowth[0]) + "@" + str(Decimal(100) * bestGrowth[1].Current.Value)) self.SetHoldings(bestGrowth[0], 1) #self.SetHoldings(currentHoldings, .75) #self.SetHoldings(secondGrowth[0], .5) self.rebalance = False def Rebalance(self): self.rebalance = True self.Debug("Rebalance") class Score(object): def __init__(self,oneMonthPerformanceValue,threeMonthPerformanceValue,sixMonthPerformanceValue,twelveMonthPerformanceValue): self.oneMonthPerformance = oneMonthPerformanceValue self.threeMonthPerformance = threeMonthPerformanceValue self.sixMonthPerformance = sixMonthPerformanceValue self.twelveMonthPerformance = twelveMonthPerformanceValue def ObjectiveScore(self): weight1 = 12 weight2 = 4 weight3 = 2 return (weight1 * self.oneMonthPerformance) + (weight2 * self.threeMonthPerformance) + (weight3 * self.sixMonthPerformance) + self.twelveMonthPerformance