Overall Statistics
Total Trades
1592
Average Win
0.48%
Average Loss
-0.55%
Compounding Annual Return
2.579%
Drawdown
19.000%
Expectancy
0.137
Net Profit
67.443%
Sharpe Ratio
0.318
Probabilistic Sharpe Ratio
0.040%
Loss Rate
39%
Win Rate
61%
Profit-Loss Ratio
0.87
Alpha
0.024
Beta
-0.008
Annual Standard Deviation
0.075
Annual Variance
0.006
Information Ratio
-0.13
Tracking Error
0.192
Treynor Ratio
-3.003
Total Fees
$3059.84
# https://quantpedia.com/strategies/multi-asset-market-breadth-momentum/
#
# Investor is invested in stocks during FOMC cycle weeks (going long S&P 500 ETF, fund, future or CFD in weeks 0, 2, 4, 6 only).
# Otherwise he is invested in cash during remaining days. The FOMC cycle starts on the day before a scheduled FOMC announcement day
# and resets at each of the eight times the FOMC meets per year.

from collections import deque

class MultiAssetMarketBreadthMomentum(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2000, 1, 1)
        self.SetCash(100000)
        
        self.symbols = ['SPY', 'QQQ', 'IWM', 'VGK', 'EWJ', 'EEM', 'GSG', 'GLD', 'IYR', 'HYG', 'LQD', 'TLT']
        self.safe_bond = 'IEF'
        
        self.period = 12 * 21
        self.data = {}
        self.sma = {}
        
        for symbol in self.symbols:
            self.AddEquity(symbol, Resolution.Daily)
            self.data[symbol] = deque(maxlen = self.period)
            self.sma[symbol] = self.SMA(symbol, self.period, Resolution.Daily)
        
        self.AddEquity(self.safe_bond, Resolution.Daily)

        self.Schedule.On(self.DateRules.MonthStart(self.symbols[0]), self.TimeRules.AfterMarketOpen(self.symbols[0]), self.Rebalance)
    
    def Rebalance(self):
        mom = {}
        for symbol in self.symbols:
            # SMA data is ready.
            if self.sma[symbol].IsReady:
                price = self.Securities[symbol].Price
                if price != 0:
                    mom[symbol] = price / self.sma[symbol].Current.Value - 1
            else:
                return  # Wait for every asset.
                
        if len(mom) != 0:
            # Bond fraction calc.
            good_assets = sorted([x[0] for x in mom.items() if x[1] > 0], key = lambda x: x[1], reverse = True)[:6]
            N = len(self.symbols)
            n = len(good_assets)
            a = 2
            n1 = a*N/4
            BF = (N-n)/(N-n1)
            
            bond_share = (1-BF)/BF

            # Trade execution.
            self.Liquidate()
            
            # Risky part.
            # "leverage" ratio in case there's two parts of portfolio - risky as well as safe part.
            ratio = 0.5 if bond_share != 0 else 1
            
            for symbol in good_assets:
                self.SetHoldings(symbol, ratio * (1/n))
            
            # Bond part.
            self.SetHoldings(self.safe_bond, ratio * bond_share)