| Overall Statistics |
|
Total Orders 205 Average Win 2.00% Average Loss -2.41% Compounding Annual Return 33.927% Drawdown 27.200% Expectancy 0.498 Start Equity 100000 End Equity 430406.74 Net Profit 330.407% Sharpe Ratio 0.991 Sortino Ratio 1.268 Probabilistic Sharpe Ratio 56.785% Loss Rate 18% Win Rate 82% Profit-Loss Ratio 0.83 Alpha 0.157 Beta 0.914 Annual Standard Deviation 0.21 Annual Variance 0.044 Information Ratio 0.922 Tracking Error 0.165 Treynor Ratio 0.227 Total Fees $272.63 Estimated Strategy Capacity $520000000.00 Lowest Capacity Asset XLK RGRPZX100F39 Portfolio Turnover 1.66% Drawdown Recovery 541 |
# region imports
from AlgorithmImports import *
# endregion
class TradingCompetitionAlgorithm(QCAlgorithm):
def initialize(self) -> None:
self.set_cash(100000)
self.set_start_date(self.end_date - timedelta(5 * 365))
self._securities = []
self._security_by_ticker = {}
for ticker in ["SPY", "QQQ", "XLK", "SMH", "NVDA", "MSFT", "AAPL", "LLY", "COST", "UNH", "GLD", "SHY"]:
security = self.add_equity(ticker, Resolution.DAILY)
security.mom_3m = self.roc(security, 63)
security.mom_6m = self.roc(security, 126)
security.mom_12m = self.roc(security, 252)
security.sma_200 = self.sma(security, 200)
self._securities.append(security)
self._security_by_ticker[ticker] = security
self.set_warm_up(260, Resolution.DAILY)
self.schedule.on(self.date_rules.month_start("SPY"), self.time_rules.at(8, 0), self._rebalance)
def on_warmup_finished(self) -> None:
self._rebalance()
def _rebalance(self) -> None:
if self.is_warming_up:
return
# Score trend-confirmed names by a weighted blend of 3/6/12-month momentum.
score_by_security = {}
for security in self._securities:
if not security.has_data or security.price <= 0:
continue
if not (security.mom_3m.is_ready and security.mom_6m.is_ready and security.mom_12m.is_ready and security.sma_200.is_ready):
continue
if security.price < security.sma_200.current.value and security.symbol.value != "SHY":
continue
score_by_security[security] = 0.45 * security.mom_3m.current.value + 0.35 * security.mom_6m.current.value + 0.20 * security.mom_12m.current.value
# Take the three strongest positive momentum names, excluding the cash proxy.
selected = []
for security in sorted(score_by_security, key=lambda s: score_by_security[s], reverse=True):
if score_by_security[security] > 0 and security.symbol.value != "SHY":
selected.append(security)
if len(selected) == 3:
break
# Fall back to a defensive SHY/GLD split when nothing qualifies.
if not selected:
selected = [self._security_by_ticker["SHY"], self._security_by_ticker["GLD"]]
target_weight = 0.97 / len(selected)
targets = [PortfolioTarget(security, target_weight if security in selected else 0) for security in self._securities]
self.set_holdings(targets)