Overall Statistics
Total Orders
352
Average Win
0.37%
Average Loss
-0.39%
Compounding Annual Return
3.775%
Drawdown
23.100%
Expectancy
0.184
Start Equity
100000
End Equity
120363.31
Net Profit
20.363%
Sharpe Ratio
-0.12
Sortino Ratio
-0.153
Probabilistic Sharpe Ratio
4.893%
Loss Rate
39%
Win Rate
61%
Profit-Loss Ratio
0.94
Alpha
-0.024
Beta
0.228
Annual Standard Deviation
0.074
Annual Variance
0.005
Information Ratio
-0.606
Tracking Error
0.127
Treynor Ratio
-0.039
Total Fees
$420.69
Estimated Strategy Capacity
$2200000.00
Lowest Capacity Asset
DBC TFVSB03UY0DH
Portfolio Turnover
1.99%
Drawdown Recovery
1459
# region imports
from AlgorithmImports import *
# endregion


class ProtectiveAssetAllocationAlgo(QCAlgorithm):

    def initialize(self):
        self.set_start_date(self.end_date - timedelta(5*365))
        self.set_cash(100_000)
        self.settings.automatic_indicator_warm_up = True
        # Define some parameters.
        self._lookback = 4   # Lookback in months
        self._protection = 2  # Protection factor = 0(low), 1, 2 (high)
        self._top_M = 6  # The max number of Equities
        # Add a risk-free asset to move into for protection.
        self._safety_asset = self.add_equity('IEF') 
        # Add some growth stocks to rotate through.
        self._growth_equities = []
        tickers = [
            "SPY", "QQQ", "IWM", "VGK", "EWJ", "EEM",
            "VNQ", "DBC", "GLD", "HYG", "LQD", "TLT"
        ]
        for ticker in tickers:
            equity = self.add_equity(ticker)
            equity.sma = self.sma(equity, 21*self._lookback, Resolution.DAILY)
            self._growth_equities.append(equity)                
        self._n_eq = len(self._growth_equities)
        # Add a Scheduled Event to rebalance the portfolio each month.
        self.schedule.on(
            self.date_rules.month_start('SPY'),
            self.time_rules.after_market_open('SPY', 15),
            self._rebalance
        )

    def _rebalance(self):
        # Calculate the momentum of the growth stocks.
        momentum_by_equity = {
            equity: equity.price / equity.sma.current.value - 1
            for equity in self._growth_equities
            if equity.sma.is_ready
        }
        # Determine the number of assets with positive momentum.
        n = sum(momentum > 0 for momentum in momentum_by_equity.values())
        # Calculate the bond fraction based on _n_eq, _protection, and n.
        # This is the portion to be invested in safe harbor
        # Calculate equity fraction and weight per equity (risky_weight, equity_weight) 
        n1 = int(self._protection * self._n_eq / 4)
        safe_weight = min(1, (self._n_eq - n) / (self._n_eq - n1))
        targets = [PortfolioTarget(self._safety_asset, safe_weight)]
        # Create portfolio targets for the risky assets.
        risky_weight = 1 - safe_weight
        if risky_weight and n:
            n_equities = min(n, self._top_M)
            equity_weight = risky_weight / n_equities
            for equity in sorted(momentum_by_equity, key=lambda equity: momentum_by_equity[equity])[-n_equities:]:
                targets.append(PortfolioTarget(equity, equity_weight))
        # Place trades to rebalance the portfolio.
        self.set_holdings(targets, True)