| Overall Statistics |
|
Total Trades 294 Average Win 0.26% Average Loss -0.20% Compounding Annual Return -2.089% Drawdown 6.800% Expectancy 0.031 Net Profit -2.428% Sharpe Ratio -0.427 Loss Rate 56% Win Rate 44% Profit-Loss Ratio 1.34 Alpha -0.04 Beta 0.986 Annual Standard Deviation 0.047 Annual Variance 0.002 Information Ratio -0.853 Tracking Error 0.047 Treynor Ratio -0.02 Total Fees $0.00 |
import pandas as pd
class MomentumAndStyleRotationAlgorithm(QCAlgorithmFramework):
def Initialize(self):
self.SetStartDate(2018, 1, 1)
self.SetEndDate(2019, 3, 1)
self.SetCash(100000) # Set Strategy Cash
self.SetSecurityInitializer(lambda security: security.SetFeeModel(ConstantFeeModel(0)))
symbols = [Symbol.Create(ticker, SecurityType.Equity, Market.USA)
for ticker in [ "IJJ", # iShares S&P MidCap 400 Value Index ETF
"IJS", # iShares S&P SmallCap 600 Value ETF
"IVE", # iShares S&P 500 Value Index ETF
"IVW", # iShares S&P 500 Growth ETF
"IJK", # iShares S&P Mid-Cap 400 Growth ETF
"IJT", # iShares S&P Small-Cap 600 Growth ETF
]]
self.UniverseSettings.Resolution = Resolution.Daily
self.SetUniverseSelection( ManualUniverseSelectionModel(symbols) )
self.SetAlpha(MomentumAndStyleRotationAlphaModel())
self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel())
self.SetExecution(ImmediateExecutionModel())
self.SetRiskManagement(NullRiskManagementModel())
class MomentumAndStyleRotationAlphaModel(AlphaModel):
'''Alpha model that uses the Momentum Indicator to create insights'''
def __init__(self,
period = 12*20,
resolution = Resolution.Daily):
'''
Initializes a new instance of the MomentumAndStyleRotationAlphaModel class
Args:
period: The Momentum lookback period
resolution: The data resolution
'''
self.period = period
self.resolution = resolution
self.MomentumBySymbol = dict()
resolutionString = Extensions.GetEnumString(resolution, Resolution)
self.Name = f'{self.__class__.__name__}({period},{resolutionString})'
self.lastMonth = -1
def Update(self, algorithm, data):
'''
Updates this alpha model with the latest data from the algorithm.
This is called each time the algorithm receives data for subscribed securities
Args:
algorithm: The algorithm instance
data: The new data available
Returns:
The new insights generated
'''
month = algorithm.Time.month
if month == self.lastMonth:
return []
self.lastMonth = month
momentumBySymbol = {symbol: value for symbol, value in self.MomentumBySymbol.items() if value.IsReady}
sortedMomentum = [x[0] for x in sorted(momentumBySymbol.items(), key = lambda kv: kv[1], reverse=True)]
expiry = algorithm.Time.replace(month = month + 1, day = 1) if month < 12 else \
algorithm.Time.replace(year = algorithm.Time.year + 1, month = 1, day = 1)
return Insight.Group(
[
Insight.Price(sortedMomentum[0], expiry, InsightDirection.Up),
Insight.Price(sortedMomentum[-1], expiry, InsightDirection.Down)
])
def OnSecuritiesChanged(self, algorithm, changes):
'''
Event fired each time the we add securities from the data feed
Args:
algorithm: The algorithm instance that experienced the change in securities
changes: The security additions and removals from the algorithm
'''
addedSymbols = [ x.Symbol for x in changes.AddedSecurities ]
history = algorithm.History(addedSymbols, self.period, self.resolution)
history = history.close.unstack(level=0)
for symbol in addedSymbols:
data = self.MomentumBySymbol.setdefault(symbol, algorithm.MOM(symbol, self.period, self.resolution))
for time, value in history[str(symbol)].iteritems():
data.Update(time, value)