| Overall Statistics |
|
Total Orders 393 Average Win 1.27% Average Loss -0.64% Compounding Annual Return 19.172% Drawdown 12.100% Expectancy 0.702 Start Equity 100000 End Equity 240330.96 Net Profit 140.331% Sharpe Ratio 0.925 Sortino Ratio 0.955 Probabilistic Sharpe Ratio 69.415% Loss Rate 43% Win Rate 57% Profit-Loss Ratio 2.00 Alpha 0.073 Beta 0.362 Annual Standard Deviation 0.107 Annual Variance 0.011 Information Ratio 0.209 Tracking Error 0.13 Treynor Ratio 0.272 Total Fees $1412.59 Estimated Strategy Capacity $160000000.00 Lowest Capacity Asset SGOV XEVHFSIAYBFP Portfolio Turnover 5.84% Drawdown Recovery 188 |
# region imports
from AlgorithmImports import *
import numpy as np
from datetime import datetime
# endregion
class SharpeGodTier_V4_1_Production(QCAlgorithm):
"""
SharpeGodTier V4.1 - SGOV Toggle Build
Changelog v4.1:
- Added USE_CASH_PROXY parameter to toggle SGOV/IBTU yield sweep on or off.
Set to False for live deployment if LSE market data subscription is unavailable.
Set to True for backtesting to capture T-bill yield on idle cash.
When disabled, idle cash earns IBKR's native interest rate automatically.
"""
# ── SGOV TOGGLE ───────────────────────────────────────────────────────
# True = deploy idle cash into SGOV (backtest) or IBTU (live)
# False = leave idle cash with IBKR earning native interest (live fallback)
USE_CASH_PROXY = True
def Initialize(self):
# ── 1. Backtest & Brokerage Settings ──────────────────────────────
#self.SetStartDate(2021, 1, 1)
self.SetEndDate(2026, 1, 1)
self.set_start_date(self.end_date - timedelta(5*365))
self.SetCash(100_000)
self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
self.SetBenchmark("SPY")
# ── 2. Yield Engine Asset (SGOV) ───────────────────────────────────
# Only added if USE_CASH_PROXY is True
# For live UK deployment replace "SGOV" with "IBTU" and add Market.LSE
if self.USE_CASH_PROXY:
self.cash_proxy = self.AddEquity("SGOV", Resolution.Daily).Symbol
else:
self.cash_proxy = None
self.Log("SGOV/IBTU cash proxy DISABLED — idle cash earning IBKR native interest")
# ── 3. Strategy Parameters (WFO-validated) ─────────────────────────
self.max_positions = 5
self.rsi_entry = 41
self.max_trade_days = 50
self.sgov_tolerance = 0.02
# ── 4. Hybrid Crypto Setup ─────────────────────────────────────────
self.ibit_launch = datetime(2024, 1, 11)
self.gbtc = self.AddEquity("GBTC", Resolution.Daily).Symbol
self.ibit = self.AddEquity("IBIT", Resolution.Daily).Symbol
# ── 5. Asset Universe ──────────────────────────────────────────────
self.assets = {
"QQQ" : "Tech",
"NVDA" : "Tech",
"XLF" : "Finance",
"XLV" : "Health",
"GLD" : "Gold",
"TLT" : "Treasury",
"USO" : "Commodity",
"XLE" : "Commodity",
"XLP" : "Equity",
}
self.symbols = []
self.data = {}
for ticker, asset_type in self.assets.items():
symbol = self.AddEquity(ticker, Resolution.Daily).Symbol
self.symbols.append(symbol)
self.data[symbol] = self._MakeIndicatorBundle(symbol, asset_type)
for symbol in [self.gbtc, self.ibit]:
self.data[symbol] = self._MakeIndicatorBundle(symbol, "Crypto")
# ── 6. Market Regime Filter ────────────────────────────────────────
self.spy = self.AddEquity("SPY", Resolution.Daily).Symbol
self.spy_std = self.STD(self.spy, 10, Resolution.Daily)
self.SetWarmUp(200)
# ── Helpers ────────────────────────────────────────────────────────────
def _MakeIndicatorBundle(self, symbol, asset_type):
return {
"rsi" : self.RSI(symbol, 14, MovingAverageType.Wilders, Resolution.Daily),
"sma_200" : self.SMA(symbol, 200, Resolution.Daily),
"atr" : self.ATR(symbol, 14, MovingAverageType.Wilders, Resolution.Daily),
"mom" : self.ROC(symbol, 20, Resolution.Daily),
"hwm" : 0.0,
"entry_date": None,
"type" : asset_type,
}
def GetActiveCryptoSymbol(self):
return self.ibit if self.Time >= self.ibit_launch else self.gbtc
# ── Main Event ─────────────────────────────────────────────────────────
def OnData(self, data: Slice):
if self.IsWarmingUp or not self.spy_std.IsReady:
return
active_crypto = self.GetActiveCryptoSymbol()
inactive_crypto = self.ibit if active_crypto == self.gbtc else self.gbtc
# ── SWITCHOVER GUARD ───────────────────────────────────────────────
if self.Portfolio[inactive_crypto].Invested:
self.Liquidate(inactive_crypto, "Crypto proxy switchover")
self.data[inactive_crypto]["hwm"] = 0.0
self.data[inactive_crypto]["entry_date"] = None
active_symbols = self.symbols + [active_crypto]
# ── STEP 1: EXIT LOGIC ─────────────────────────────────────────────
for s in [s for s in active_symbols if self.Portfolio[s].Invested]:
if not data.Bars.ContainsKey(s):
continue
d = self.data[s]
price = self.Securities[s].Price
d["hwm"] = max(d["hwm"], price)
if d["type"] == "Crypto":
exit_rsi, atr_mult = 85, 3.2
elif d["type"] == "Commodity":
exit_rsi, atr_mult = 80, 3.0
else:
exit_rsi, atr_mult = 75, 2.5
days_held = (self.Time - d["entry_date"]).days
trailing_stop = d["hwm"] - (d["atr"].Current.Value * atr_mult)
if (d["rsi"].Current.Value > exit_rsi or
price < trailing_stop or
days_held > self.max_trade_days):
self.Liquidate(s, "V4.1 Sniper Exit")
d["hwm"] = 0.0
d["entry_date"] = None
# ── STEP 2: ENTRY LOGIC ────────────────────────────────────────────
is_high_vol = self.spy_std.Current.Value > (self.Securities[self.spy].Price * 0.015)
invested_count = sum(1 for s in active_symbols if self.Portfolio[s].Invested)
if invested_count < self.max_positions:
candidates = []
for s in active_symbols:
if self.Portfolio[s].Invested or not data.Bars.ContainsKey(s):
continue
d = self.data[s]
if not d["rsi"].IsReady or not d["sma_200"].IsReady:
continue
if (self.Securities[s].Price > d["sma_200"].Current.Value and
d["rsi"].Current.Value < self.rsi_entry):
candidates.append(s)
candidates.sort(key=lambda x: self.data[x]["mom"].Current.Value, reverse=True)
for s in candidates:
if sum(1 for sym in active_symbols if self.Portfolio[sym].Invested) >= self.max_positions:
break
d = self.data[s]
risk_pct = 0.01 if is_high_vol else 0.03
atr_val = d["atr"].Current.Value
if atr_val <= 0:
continue
qty_factor = (self.Portfolio.TotalPortfolioValue * risk_pct) / (atr_val * 3)
if d["type"] == "Crypto":
max_w = 0.30
elif d["type"] == "Commodity":
max_w = 0.25
else:
max_w = 0.33
target_weight = min(
max_w,
(qty_factor * self.Securities[s].Price) / self.Portfolio.TotalPortfolioValue
)
if target_weight > 0.02:
self.SetHoldings(s, target_weight)
d["hwm"] = self.Securities[s].Price
d["entry_date"] = self.Time
# ── STEP 3: CASH MANAGEMENT (SGOV YIELD SWEEP) ────────────────────
# Skipped entirely if USE_CASH_PROXY is False
# Idle cash automatically earns IBKR native interest in that case
if not self.USE_CASH_PROXY:
return
# Guard: skip if no price data yet for cash proxy
if not data.Bars.ContainsKey(self.cash_proxy):
return
tactical_value = sum(
self.Portfolio[s].HoldingsValue
for s in active_symbols
)
tactical_weight = tactical_value / self.Portfolio.TotalPortfolioValue
sgov_target_weight = max(0, 0.95 - tactical_weight)
sgov_current_weight = self.Portfolio[self.cash_proxy].HoldingsValue / self.Portfolio.TotalPortfolioValue
sgov_deviation = abs(sgov_current_weight - sgov_target_weight)
if sgov_deviation > self.sgov_tolerance:
if sgov_target_weight > 0.05:
self.SetHoldings(self.cash_proxy, sgov_target_weight)
elif tactical_weight > 0.92:
self.Liquidate(self.cash_proxy, "Clearing Space for Tactical Signal")