Overall Statistics |
Total Trades 387 Average Win 0.80% Average Loss -1.01% Compounding Annual Return 7.275% Drawdown 18.500% Expectancy 0.244 Net Profit 66.637% Sharpe Ratio 0.579 Probabilistic Sharpe Ratio 9.744% Loss Rate 31% Win Rate 69% Profit-Loss Ratio 0.79 Alpha 0.062 Beta 0.035 Annual Standard Deviation 0.115 Annual Variance 0.013 Information Ratio -0.341 Tracking Error 0.191 Treynor Ratio 1.886 Total Fees $1168.52 Estimated Strategy Capacity $67000.00 Lowest Capacity Asset DWAS V8DKFGNVD0TH |
""" Rules: The assets are ranked according to an average of the asset's 1, 3, 6, and 12-month total returns (Momentum factor). The model invests 33% of capital into the top 3 assets given that the asset's price is greater than the asset's 10-month SMA (Trend factor), else the allocation is held in cash or T-bills. """ class EmotionalFluorescentYellowAnt(QCAlgorithm): def Initialize(self): self.SetStartDate(2014, 5, 1) self.SetCash(100000) self.leverage = 1.0 #\\\\\ ASSETS //// MTUM=APR2013 assets = ["VTV", "MTUM", "VBR", "DWAS", "EFA", "EEM", "IEF", "IGOV", "LQD", "TLT", "GSG", "IAU", "VNQ"] self.AddEquity("SPY", Resolution.Daily) #SchedulingTasks #\\\\\ SIGNALS //// self.DataBySymbol = {} for ticker in assets: if ticker == "IEF": self.ief = self.AddEquity(ticker, Resolution.Daily).Symbol self.DataBySymbol[self.ief] = SymbolData(self.ief, self) else: symbol = self.AddEquity(ticker, Resolution.Daily).Symbol self.DataBySymbol[symbol] = SymbolData(symbol, self) #\\\\\ TIMING //// Timing = 60 self.Schedule.On(self.DateRules.MonthEnd("SPY"), self.TimeRules.AfterMarketOpen("SPY", Timing), self.MonthlyRebalance) #\\\\\ RISK MANAGEMENT / LOGGING //// #self.SetRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(0.25)) self.hwm = self.Portfolio.TotalPortfolioValue self.marginRemaining = int(self.Portfolio.MarginRemaining) self.ClosingPortValue = int(self.Portfolio.TotalPortfolioValue) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose('SPY', 1), self.CheckDailyPerformance) def OnData(self, data): value = self.Portfolio.TotalPortfolioValue if value > self.hwm: self.hwm = value def MonthlyRebalance(self): # Rank assets according to weighted 1-3-6-12month momentum momentum_scores = [self.DataBySymbol[x].Momentum for x in self.DataBySymbol] momentum_sort = sorted([k for k,v in self.DataBySymbol.items()], key=lambda x: self.DataBySymbol[x].Momentum, reverse=True) targets = momentum_sort[:3] # Check if assets are above or below MovingAverage for symbol in self.DataBySymbol: self.DataBySymbol[symbol].Weight = 0 if symbol in targets: price = self.Securities[symbol].Price # If asset is below, hold bonds 33%, else hold asset 33% if price != 0 and self.DataBySymbol[symbol].MovAvg.IsReady: if price > self.DataBySymbol[symbol].MovAvg.Current.Value: self.DataBySymbol[symbol].Weight = 0.33 else: self.DataBySymbol[self.ief].Weight += 0.33 else: self.DataBySymbol[self.ief].Weight += 0.33 self.PlaceTrades() def PlaceTrades(self): weights = {} for symbol in self.DataBySymbol: weights[symbol] = self.DataBySymbol[symbol].Weight self.SetHoldings([PortfolioTarget(target, weight*self.leverage) for target, weight in weights.items()]) self.Notify.Sms("+61411067329", "GTAA13(3) Rebalancing...") def CheckDailyPerformance(self): ### Closing Plotting leverage = round(self.Portfolio.TotalHoldingsValue / self.Portfolio.TotalPortfolioValue, 2 ) percent_DD = round( ( self.Portfolio.TotalPortfolioValue / self.hwm - 1 ) * 100, 1) perf = round( (self.Portfolio.TotalPortfolioValue / self.ClosingPortValue - 1) * 100, 1) self.ClosingPortValue = int(self.Portfolio.TotalPortfolioValue) for symbol in self.DataBySymbol: if self.DataBySymbol[symbol].Momentum is not None: self.Plot('MovAvg', f'{str(symbol)}', self.DataBySymbol[symbol].MovAvg.Current.Value) self.Plot('Momentum', f'{str(symbol)}', self.DataBySymbol[symbol].Momentum) for kvp in self.Portfolio: symbol = kvp.Key holding = kvp.Value self.Plot('AssetWeights %', f"{str(holding.Symbol)}%", holding.HoldingsValue/self.Portfolio.TotalPortfolioValue) #\\\\\ INDICATORS //// class SymbolData: def __init__(self, symbol, algorithm): self.Symbol = symbol self.Weight = None self.algorithm = algorithm self.MovAvg = SimpleMovingAverage(210) self.Momentum = None self.MOMPone = MomentumPercent(21) self.MOMPthree = MomentumPercent(63) self.MOMPsix = MomentumPercent(126) self.MOMPtwelve = MomentumPercent(252) # Warm up MA history = algorithm.History([self.Symbol], 253, Resolution.Daily).loc[self.Symbol] # Use history to build our SMA for time, row in history.iterrows(): self.MovAvg.Update(time, row["close"]) self.MOMPone.Update(time, row["close"]) self.MOMPthree.Update(time, row["close"]) self.MOMPsix.Update(time, row["close"]) self.MOMPtwelve.Update(time, row["close"]) # Setup indicator consolidator self.consolidator = TradeBarConsolidator(timedelta(1)) self.consolidator.DataConsolidated += self.CustomDailyHandler algorithm.SubscriptionManager.AddConsolidator(self.Symbol, self.consolidator) def CustomDailyHandler(self, sender, consolidated): self.MovAvg.Update(consolidated.Time, consolidated.Close) self.MOMPone.Update(consolidated.Time, consolidated.Close) self.MOMPthree.Update(consolidated.Time, consolidated.Close) self.MOMPsix.Update(consolidated.Time, consolidated.Close) self.MOMPtwelve.Update(consolidated.Time, consolidated.Close) self.Momentum = self.MOMPone.Current.Value * 12 + self.MOMPthree.Current.Value * 4 + \ self.MOMPsix.Current.Value * 2 + self.MOMPtwelve.Current.Value def dispose(self): self.algorithm.SubscriptionManager.RemoveConsolidator(self.Symbol, self.consolidator)