| Overall Statistics |
|
Total Trades 365 Average Win 0.33% Average Loss -0.68% Compounding Annual Return 4.598% Drawdown 8.100% Expectancy 0.235 Net Profit 46.980% Sharpe Ratio 0.661 Probabilistic Sharpe Ratio 13.103% Loss Rate 17% Win Rate 83% Profit-Loss Ratio 0.49 Alpha 0.037 Beta 0.026 Annual Standard Deviation 0.06 Annual Variance 0.004 Information Ratio -0.472 Tracking Error 0.159 Treynor Ratio 1.5 Total Fees $514.61 |
class MovingAverageAlphaModel(AlphaModel):
'''Alpha model based on Price crossing an SMA'''
def __init__(self, smaLength=200, resolution=Resolution.Daily, predictionInterval=31):
'''Initializes a new instance of the SmaAlphaModel class
Args:
period: The SMA period
resolution: The reolution for the SMA'''
self.smaLength = smaLength
self.resolution = resolution
self.predictionInterval = predictionInterval
self.symbolDataBySymbol = {}
self.month = -1
self.riskOffAsset = "IEF"
def Update(self, algorithm, data):
'''This is called each time the algorithm receives data for (@resolution of) subscribed securities
Returns: The new insights generated.
THIS: analysis only occurs at month start, so any signals intra-month are disregarded.'''
if self.month == algorithm.Time.month:
return []
self.month = algorithm.Time.month
insights = []
riskOffWeight = riskOnWeight = 1 / len(self.symbolDataBySymbol)
for symbol, symbolData in self.symbolDataBySymbol.items():
if symbol.Value == self.riskOffAsset:
continue
price = algorithm.Securities[symbol].Price
# Reset indicator, get fresh historical data, pump into indicator
symbolData.MovingAverage.Reset()
history = algorithm.History([symbol], self.smaLength, self.resolution)
for time, row in history.loc[symbol].iterrows():
symbolData.MovingAverage.Update(time, row["close"])
if price != 0 and symbolData.MovingAverage.IsReady:
if price > symbolData.MovingAverage.Current.Value:
insights.append( Insight.Price(symbol, timedelta(days=self.predictionInterval), InsightDirection.Up, None, None, None, riskOnWeight))
elif price < symbolData.MovingAverage.Current.Value:
insights.append( Insight.Price(symbol, timedelta(days=self.predictionInterval), InsightDirection.Flat, None, None, None, 0) )
riskOffWeight += riskOnWeight
insights.append( Insight.Price(self.riskOffAsset, timedelta(days=self.predictionInterval), InsightDirection.Up, None, None, None, riskOffWeight) )
return insights
def OnSecuritiesChanged(self, algorithm, changes):
for added in changes.AddedSecurities:
# Get historical data & check for existence in symbolData
history = algorithm.History([added.Symbol], self.smaLength, self.resolution)
symbolData = self.symbolDataBySymbol.get(added.Symbol)
if symbolData is None:
# Create an instance, initialise Indicator, pump in history
symbolData = SymbolData(added)
symbolData.MovingAverage = algorithm.SMA(added.Symbol, self.smaLength, self.resolution)
for time, row in history.loc[added.Symbol].iterrows():
symbolData.MovingAverage.Update(time, row["close"])
self.symbolDataBySymbol[added.Symbol] = symbolData
else:
# The security existed: reset indicators, restore history
symbolData.MovingAverage.Reset()
for time, row in history.loc[added.Symbol].iterrows():
symbolData.MovingAverage.Update(time, row["close"])
# TODO: Needs to be amended for when securites are removed.
class SymbolData:
def __init__(self, security):
self.Security = security
self.Symbol = security.Symbol
self.MovingAverage = None'''An implementation of Meb Faber's base model: Global Tactical Asset Allocation model (GTAA)(5)
with 10-month SimpleMovingAverage Filter (200day) and (monthly rebalance), as found in the paper:
https://papers.ssrn.com/sol3/papers.cfm?abstract_id=962461
"A Quantitative Approach to Tactical Asset Allocation" published May 2006.
Analysis only occurs at month End/Start, signals are NOT generated intra-month.
'''
# self.Debug(str(dir( x )))
from alpha_model import MovingAverageAlphaModel
class GlobalTacticalAssetAllocation(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2011, 10, 29) #
self.SetEndDate(2020, 5, 20)
self.SetCash(100000)
self.Settings.FreePortfolioValuePercentage = 0.02
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
self.UniverseSettings.Resolution = Resolution.Daily
symbols = [Symbol.Create(ticker, SecurityType.Equity, Market.USA)
for ticker in [ "SPY", # US Large Cap ETF
"VEA", # Developed Foreign Stocks (TradedSince: 2007/8)ETF
"IEF", # US 10Y Gov.Bonds ETF
"DBC", # GSCI Commodities ETF (TradedSince: 2006/3)
"VNQ" # US RealEstate ETF
]]
self.AddUniverseSelection( ManualUniverseSelectionModel(symbols) )
self.AddAlpha( MovingAverageAlphaModel() )
self.Settings.RebalancePortfolioOnInsightChanges = False
self.Settings.RebalancePortfolioOnSecurityChanges = False
self.SetPortfolioConstruction( InsightWeightingPortfolioConstructionModel(self.DateRules.MonthStart('SPY'), PortfolioBias.Long) )
self.SetExecution( ImmediateExecutionModel() )
self.AddRiskManagement( NullRiskManagementModel() )class YourAlgorithm(QCAlgorithm):
def Initialize(self):
# 1) Setting a Benchmark to plot with equity
self.benchmarkTicker = 'SPY'
self.SetBenchmark(self.benchmarkTicker)
self.initBenchmarkPrice = None
def UpdateBenchmarkValue(self):
''' Simulate buy and hold the Benchmark '''
if self.initBenchmarkPrice is None:
self.initBenchmarkCash = self.Portfolio.Cash
self.initBenchmarkPrice = self.Benchmark.Evaluate(self.Time)
self.benchmarkValue = self.initBenchmarkCash
else:
currentBenchmarkPrice = self.Benchmark.Evaluate(self.Time)
self.benchmarkValue = (currentBenchmarkPrice / self.initBenchmarkPrice) * self.initBenchmarkCash
def OnData(self, data):
# 2) simulate buy and hold the benchmark and plot its daily value as we are using daily data.
# Otherwise schedule when to call this function!
self.UpdateBenchmarkValue()
self.Plot('Strategy Equity', self.benchmarkTicker, self.benchmarkValue)
### _________________________________________________
# Plotting HoldingValues
for kvp in self.Portfolio:
symbol = kvp.Key
holding = kvp.Value
self.Debug(str(holding.Symbol))
self.Plot(f"{str(holding.Symbol)}HoldingValues", holding.HoldingsValue)
### _________________________________________________