| Overall Statistics |
|
Total Orders 1203 Average Win 0.38% Average Loss -0.78% Compounding Annual Return 7.315% Drawdown 38.000% Expectancy 0.168 Start Equity 100000 End Equity 214420.08 Net Profit 114.420% Sharpe Ratio 0.331 Sortino Ratio 0.347 Probabilistic Sharpe Ratio 2.500% Loss Rate 22% Win Rate 78% Profit-Loss Ratio 0.49 Alpha -0.003 Beta 0.487 Annual Standard Deviation 0.11 Annual Variance 0.012 Information Ratio -0.398 Tracking Error 0.112 Treynor Ratio 0.074 Total Fees $1611.49 Estimated Strategy Capacity $5500000.00 Lowest Capacity Asset FDN TJPMW3BHNMUD Portfolio Turnover 2.61% |
# region imports
from AlgorithmImports import *
# endregion
# 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(2014, 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'])