| Overall Statistics |
|
Total Orders 1114 Average Win 0.58% Average Loss -0.65% Compounding Annual Return 37.357% Drawdown 17.000% Expectancy 0.389 Start Equity 100000 End Equity 488366.32 Net Profit 388.366% Sharpe Ratio 1.362 Sortino Ratio 1.579 Probabilistic Sharpe Ratio 88.089% Loss Rate 26% Win Rate 74% Profit-Loss Ratio 0.89 Alpha 0.196 Beta 0.401 Annual Standard Deviation 0.161 Annual Variance 0.026 Information Ratio 0.944 Tracking Error 0.173 Treynor Ratio 0.546 Total Fees $1945.63 Estimated Strategy Capacity $120000000.00 Lowest Capacity Asset BIL TT1EBZ21QWKL Portfolio Turnover 6.84% Drawdown Recovery 310 |
# Credits original docs:
# https://www.quantconnect.com/docs/v2/writing-algorithms/scheduled-events
# https://www.quantconnect.com/docs/v2/writing-algorithms/trading-and-orders/position-sizing
# region imports
from AlgorithmImports import *
# endregion
class StrongQqqStockBoostVaaRotation(QCAlgorithm):
def initialize(self):
self.set_start_date(self.end_date - timedelta(5 * 365))
self.set_cash(100000)
self.settings.seed_initial_prices = True
self.settings.free_portfolio_value_percentage = 0.03
self._total_weight = 0.99
self._stock_weight = 0.415
self._weights = [12.0, 4.0, 2.0, 1.0]
periods = [21, 63, 126, 252]
self._stocks = "NVDA AVGO AMD MSFT AMZN META GOOGL TSLA NFLX ORCL PANW CRWD SMCI MSTR ANET MU NOW".split() + "UBER AAPL LLY COST ADBE".split()
self._etfs = "QQQ XLK XLE DBC GLD".split()
self._defs = "GLD BIL".split()
self._security_by_ticker = {}
for ticker in "SPY QQQ XLK XLE DBC GLD BIL".split() + self._stocks:
security = self.add_equity(ticker, Resolution.DAILY)
security.momentum = [self.rocp(security, period) for period in periods]
self._security_by_ticker[ticker] = security
self.set_warm_up(max(periods) + 20, Resolution.DAILY)
self.schedule.on(self.date_rules.week_start(self._security_by_ticker["SPY"]), self.time_rules.at(8, 0), self._rebalance)
def on_warmup_finished(self):
self._rebalance()
def _rebalance(self):
if self.is_warming_up:
return
targets = [PortfolioTarget(self._security_by_ticker[ticker], weight) for ticker, weight in self._allocations().items()]
self.set_holdings(targets, liquidate_existing_holdings=True)
def _allocations(self):
# Boost into the strongest momentum stocks when QQQ momentum clears the threshold.
if self._ready("QQQ") and self._score("QQQ") > 0.35:
picks = self._rank(self._stocks, 4, True)
if picks:
hedge = "GLD" if self._ready("GLD") and self._score("GLD") > 0 else "BIL"
return {**self._equal(picks, self._stock_weight), hedge: self._total_weight - self._stock_weight}
# Otherwise rotate into the best trending ETFs while SPY momentum is positive.
if self._ready("SPY") and self._score("SPY") > 0:
picks = self._rank(self._etfs, 2, True)
if picks:
return self._equal(picks, self._total_weight)
# Fall back to the best defensive asset, defaulting to T-bills.
picks = self._rank(self._defs, 1, True) or self._rank(self._defs, 1, False) or ["BIL"]
return self._equal(picks, self._total_weight)
def _rank(self, tickers, count, require_positive):
score_by_ticker = {}
for ticker in tickers:
if self._ready(ticker):
score = self._score(ticker)
if score > 0 or not require_positive:
score_by_ticker[ticker] = score
return sorted(score_by_ticker, key=lambda ticker: score_by_ticker[ticker])[-count:]
def _equal(self, tickers, total_weight):
return {ticker: total_weight / len(tickers) for ticker in tickers}
def _ready(self, ticker):
return all(indicator.is_ready for indicator in self._security_by_ticker[ticker].momentum)
def _score(self, ticker):
return sum(weight * indicator.current.value for weight, indicator in zip(self._weights, self._security_by_ticker[ticker].momentum))