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