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)
### _________________________________________________