Overall Statistics |
Total Trades 2041 Average Win 0.28% Average Loss -0.39% Compounding Annual Return 14.628% Drawdown 15.800% Expectancy 0.408 Net Profit 488.812% Sharpe Ratio 1.118 Probabilistic Sharpe Ratio 58.202% Loss Rate 18% Win Rate 82% Profit-Loss Ratio 0.71 Alpha 0.128 Beta -0.023 Annual Standard Deviation 0.113 Annual Variance 0.013 Information Ratio 0.132 Tracking Error 0.222 Treynor Ratio -5.541 Total Fees $3601.63 |
# Andreas Clenow Momentum (Static Assets), Framework from datetime import timedelta from collections import deque from scipy import stats import numpy as np class ClenowMomentum(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 if symbol.Value != "SPY": self.indi_Update[symbol] = self.indi[symbol] 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) ) algorithm.Plot('Custom_Slope', 'Value QQQ', list(self.indi.values())[0].Value) algorithm.Plot('Custom_Slope', 'Value FDN', list(self.indi.values())[1].Value) algorithm.Plot('Custom_Slope', 'Value XLP', list(self.indi.values())[2].Value) algorithm.Plot('Custom_Slope', 'Value TLH', list(self.indi.values())[3].Value) algorithm.Plot('Custom_Slope', 'Value TLT', list(self.indi.values())[4].Value) return insights class FrameworkAlgorithm(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.SetUniverseSelection(CustomUniverseSelectionModel('CustomUniverseSelectionModel', lambda time: tickers)) self.UniverseSettings.Resolution = Resolution.Daily self.AddAlpha(ClenowMomentum()) self.Settings.RebalancePortfolioOnInsightChanges = False self.Settings.RebalancePortfolioOnSecurityChanges = True self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel(self.DateRules.Every(DayOfWeek.Monday))) self.SetExecution(ImmediateExecutionModel()) self.MKT = self.AddEquity('SPY', Resolution.Daily).Symbol self.spy = [] 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 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 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 = float(np.power(np.exp(self.Slope), 252) - 1) * 2.00 self.Value = (self.annualized_slope) * float(corr**2) # 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'])