Overall Statistics |
Total Trades 1975 Average Win 0.33% Average Loss -0.37% Compounding Annual Return 14.920% Drawdown 17.900% Expectancy 0.487 Net Profit 507.642% Sharpe Ratio 1.085 Probabilistic Sharpe Ratio 53.244% Loss Rate 22% Win Rate 78% Profit-Loss Ratio 0.91 Alpha 0.132 Beta -0.024 Annual Standard Deviation 0.119 Annual Variance 0.014 Information Ratio 0.142 Tracking Error 0.225 Treynor Ratio -5.397 Total Fees $3808.40 |
# Andreas Clenow Momentum (Static Assets), Framework from datetime import timedelta from collections import deque from scipy import stats import numpy as np class AndreasClenowMomentumFramework(QCAlgorithm): def Initialize(self): self.SetStartDate(2008, 1, 1) #self.SetEndDate(2020, 12, 17) self.cap = 100000 self.SetCash(self.cap) tickers = ['QQQ','FDN','XLP','TLH','TLT'] self.MKT = self.AddEquity('SPY', Resolution.Daily).Symbol self.spy = [] self.SetUniverseSelection(CustomUniverseSelectionModel('CustomUniverseSelectionModel', lambda time: tickers)) self.UniverseSettings.Resolution = Resolution.Daily self.AddAlpha(MOMAlphaModel()) self.Settings.RebalancePortfolioOnInsightChanges = False self.Settings.RebalancePortfolioOnSecurityChanges = False self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(self.DateRules.Every(DayOfWeek.Monday))) self.SetExecution(ImmediateExecutionModel()) self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose('SPY', 0), self.record_vars) def record_vars(self): hist = self.History([self.MKT], 2, Resolution.Daily)['close'].unstack(level= 0).dropna() self.spy.append(hist[self.MKT].iloc[-1]) spy_perf = self.spy[-1] / self.spy[0] * self.cap self.Plot('Strategy Equity', 'SPY', spy_perf) account_leverage = self.Portfolio.TotalHoldingsValue / self.Portfolio.TotalPortfolioValue self.Plot('Holdings', 'leverage', round(account_leverage, 2)) class MOMAlphaModel(AlphaModel): def __init__(self): self.PERIOD = 50 self.N = 3 self.indi = {} self.indi_Update = {} self.securities = [] def OnSecuritiesChanged(self, algorithm, changes): for security in changes.AddedSecurities: self.securities.append(security) symbol = security.Symbol self.indi[symbol] = My_Custom('My_Custom', symbol, self.PERIOD) algorithm.RegisterIndicator(symbol, self.indi[symbol], Resolution.Daily) history = algorithm.History(symbol, self.PERIOD, Resolution.Daily) self.indi[symbol].Warmup(history) def Update(self, algorithm, data): insights = [] for security in self.securities: symbol = security.Symbol self.indi_Update[symbol] = self.indi[symbol] #if self.indicator == True: ordered = sorted(self.indi_Update.items(), key = lambda x: x[1].Value, reverse = False)[:self.N] for x in ordered: symbol = x[0] insights.append( Insight.Price(symbol, timedelta(1), InsightDirection.Up) ) # for testing algorithm.Plot('Custom_Slope', 'Value QQQ', list(self.indi.values())[0].Value) algorithm.Plot('Custom_Slope', 'Value TLH', list(self.indi.values())[3].Value) return insights # Python implementation of Custom Indicator class My_Custom: def __init__(self, name, symbol, period): self.symbol = symbol self.Name = name self.Time = datetime.min self.Value = 0 self.Slope = 0 self.Corr = 0 self.queue = deque(maxlen=period) self.IsReady = False # Update method is mandatory def Update(self, input): return self.Update2(input.Time, input.Close) def Update2(self, time, value): self.queue.appendleft(value) count = len(self.queue) self.Time = time self.IsReady = count == self.queue.maxlen #### start here the indicator calulation if self.IsReady: y = np.log(self.queue) x = [range(len(y))] slope, corr = stats.linregress(x, y)[0], stats.linregress(x, y)[2] self.Slope = slope self.Corr = corr self.annualized_slope = (np.power(np.exp(self.Slope), 252) - 1) * 100 self.Value = self.annualized_slope * corr**2 #### finish the custom indicator # for testing self.IsReady = False self.IsReady = False return self.IsReady def Warmup(self,history): for index, row in history.loc[self.symbol].iterrows(): self.Update2(index, row['close'])