Overall Statistics |
Total Trades 2044 Average Win 0.28% Average Loss -0.39% Compounding Annual Return 14.589% Drawdown 15.800% Expectancy 0.408 Net Profit 488.202% Sharpe Ratio 1.116 Probabilistic Sharpe Ratio 57.961% 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.126 Tracking Error 0.222 Treynor Ratio -5.518 Total Fees $3604.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: if security.Symbol.Value == 'SPY': continue 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 = [] ready = [indicator for symbol, indicator in self.indi.items() if indicator.IsReady] ordered = sorted(ready, key = lambda x: x.Value, reverse = False)[:self.N] for x in ordered: insights.append( Insight.Price(x.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'] symbols = [Symbol.Create(t, SecurityType.Equity, Market.USA) for t in tickers] self.SetUniverseSelection(ManualUniverseSelectionModel(symbols)) 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.mkt = [] self.consolidator = TradeBarConsolidator(timedelta(days=1)) self.consolidator.DataConsolidated += self.consolidation_handler self.SubscriptionManager.AddConsolidator(self.MKT, self.consolidator) self.history = self.History(self.MKT, 2, Resolution.Daily) self.history = self.history['close'].unstack(level=0).dropna() def consolidation_handler(self, sender, consolidated): self.history.loc[consolidated.EndTime, consolidated.Symbol] = consolidated.Close self.history = self.history.iloc[-2:] def OnEndOfDay(self): mkt_price = self.history[[self.MKT]].iloc[-1] # mkt_price = self.Securities[self.MKT].Close # mkt_price = self.History(self.MKT, 2, Resolution.Daily)['close'].unstack(level=0).iloc[-1].dropna() self.mkt.append(mkt_price) mkt_perf = self.mkt[-1] / self.mkt[0] * self.cap self.Plot('Strategy Equity', 'SPY', mkt_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))] reg = stats.linregress(x, y) slope, corr = reg[0], reg[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'])