| Overall Statistics |
|
Total Orders 6135 Average Win 0.45% Average Loss -0.51% Compounding Annual Return 66.888% Drawdown 64.500% Expectancy 0.248 Start Equity 50000 End Equity 1742287.93 Net Profit 3384.576% Sharpe Ratio 1.197 Sortino Ratio 1.456 Probabilistic Sharpe Ratio 49.494% Loss Rate 34% Win Rate 66% Profit-Loss Ratio 0.89 Alpha 0.387 Beta 1.647 Annual Standard Deviation 0.463 Annual Variance 0.214 Information Ratio 1.161 Tracking Error 0.39 Treynor Ratio 0.336 Total Fees $27139.54 Estimated Strategy Capacity $8700000.00 Lowest Capacity Asset AXTI RAW0VTDPK7L1 Portfolio Turnover 14.15% Drawdown Recovery 753 |
# region imports
from AlgorithmImports import *
import numpy as np
# endregion
class GreedySharpeAIInfrastructure(QCAlgorithm):
"""
Cross-Sectional Momentum Strategy — AI Infrastructure Universe
Source: greedy_sharpe_ai_infrastructure_universe_filtered_20260306_120902
Signal: 180-day lookback, 20-day skip momentum
Select: Top 95th percentile of cross-sectional momentum
Weight: Equal weight among selected tickers
Rebal: Daily at market close
Filter: $2 minimum entry price
"""
# ── Strategy parameters ──────────────────────────────────────────────
LOOKBACK = 180 # momentum lookback window (trading days)
SKIP = 20 # skip most recent N days (mean-reversion filter)
PERCENTILE = 0.95 # select top 5% of momentum scores
MIN_ENTRY_PRICE = 2.0 # minimum price to enter a position
# ── Universe from greedy search results ──────────────────────────────
TICKERS = [
"AAOI", "ACLS", "ADI", "AES", "ALAB", "AMAT", "AMKR", "AMZN",
"ANET", "APH", "APLD", "ASML", "AVGO", "AXTI", "BDC", "CALX",
"CAMT", "CAT", "CDNS", "CLSK", "COHR", "CORZ", "CRM", "CRUS",
"CSCO", "DOV", "DUK", "EME", "ENTG", "ETN", "FIX", "FLEX",
"FN", "GEV", "GFS", "GLW", "GNRC", "GOOG", "HUBB", "HUT",
"IBM", "IESC", "INTC", "IPGP", "J", "KLAC", "LEU", "LITE",
"LRCX", "MARA", "META", "MKSI", "MOD", "MPWR", "MRVL", "MSFT",
"MTSI", "MU", "NEE", "NTAP", "NVDA", "NVMI", "NVT", "NXPI",
"OKLO", "ONTO", "PCG", "PSTG", "PWR", "QCOM", "QRVO", "SMR",
"SNPS", "SO", "STX", "SWKS", "TER", "TSM", "TT", "TXN",
"UCTT", "VIAV", "VRT",
]
def initialize(self):
self.set_start_date(2019, 1, 1)
self.set_end_date(2026, 12, 31)
self.set_cash(50_000)
# Use daily resolution
self.universe_settings.resolution = Resolution.DAILY
# Add all universe symbols
self.symbols = []
for ticker in self.TICKERS:
symbol = self.add_equity(ticker, Resolution.DAILY).symbol
self.symbols.append(symbol)
# History window: need LOOKBACK days of data before we can trade
self.set_warm_up(self.LOOKBACK + self.SKIP + 5, Resolution.DAILY)
# Schedule daily rebalance 5 minutes before market close
self.schedule.on(
self.date_rules.every_day(),
self.time_rules.before_market_close("SPY", 5),
self.rebalance,
)
# Benchmark
self.set_benchmark("SPY")
# Track for logging
self._last_log_date = None
def rebalance(self):
if self.is_warming_up:
return
# ── 1. Get historical adjusted close prices ──────────────────
lookback_total = self.LOOKBACK + self.SKIP + 1
history = self.history(self.symbols, lookback_total, Resolution.DAILY)
if history.empty:
return
# Pivot to get close prices by symbol
try:
close = history["close"].unstack(level=0)
except Exception:
return
if close.shape[0] < lookback_total:
return
# ── 2. Compute cross-sectional momentum ─────────────────────
# momentum = price[t - SKIP] / price[t - LOOKBACK] - 1
end_prices = close.iloc[-(self.SKIP + 1)] # price SKIP days ago
start_prices = close.iloc[0] # price LOOKBACK+SKIP days ago
momentum = {}
for symbol in close.columns:
p_end = end_prices.get(symbol, None)
p_start = start_prices.get(symbol, None)
if p_end is not None and p_start is not None:
if not np.isnan(p_end) and not np.isnan(p_start) and p_start > 0:
momentum[symbol] = p_end / p_start - 1.0
if not momentum:
self.liquidate()
return
# ── 3. Select top percentile ─────────────────────────────────
mom_values = list(momentum.values())
threshold = np.percentile(mom_values, self.PERCENTILE * 100)
# Get current prices for min-price filter
selected = []
for symbol, mom in momentum.items():
if mom >= threshold:
current_price = self.securities[symbol].price
if current_price >= self.MIN_ENTRY_PRICE:
selected.append(symbol)
if not selected:
self.liquidate()
return
# ── 4. Equal weight allocation ───────────────────────────────
weight = 1.0 / len(selected)
selected_set = set(selected)
# Liquidate positions not in the new selection
for holding in self.portfolio.Values:
if holding.invested and holding.symbol not in selected_set:
self.liquidate(holding.symbol)
# Set target allocations
for symbol in selected:
self.set_holdings(symbol, weight)
# ── 5. Monthly logging ───────────────────────────────────────
today = self.time.date()
if self._last_log_date is None or today.month != self._last_log_date.month:
self._last_log_date = today
portfolio_value = self.portfolio.total_portfolio_value
n_holdings = sum(1 for h in self.portfolio.Values if h.invested)
self.log(
f"{today} | Value: ${portfolio_value:,.0f} | "
f"Holdings: {n_holdings}/{len(selected)} selected | "
f"Threshold: {threshold:.4f}"
)
def on_data(self, data: Slice):
pass