feat: register V7+VT36 as SOTA and add monitor hot-reload

- Register trend_rider_v7_vt36 (target_vol=0.36, min_lev=0.75) in
  strategy registry, ETF universe map, and bridge metadata.
  10y backtest: Ann 60.5%, Sharpe 1.87, MaxDD -29.2%.

- Add hot-reload to monitor: each phase re-imports trader module to
  pick up newly registered strategies without restart. New strategies
  are logged on detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-21 20:57:16 +08:00
parent c4ae944345
commit b8bac26b8f
2 changed files with 32 additions and 1 deletions

View File

@@ -380,6 +380,12 @@ STRATEGY_META = {
"params": {},
"markets": ["us"],
},
"trend_rider_v7_vt36": {
"label": "Trend Rider V7 (VT36% + PT30) ★ SOTA",
"category": "tactical_allocation",
"params": {},
"markets": ["us"],
},
# --- Stock-picker ensembles (US S&P 500 universe) ---
"ensemble_alpha_top10": {
"label": "Ensemble Alpha Top 10",

View File

@@ -178,6 +178,7 @@ STRATEGY_REGISTRY = {
"trend_rider_v7": lambda **kw: TrendRiderV7(),
"trend_rider_v7_vt24": lambda **kw: TrendRiderV7(target_vol=0.24, min_lev=0.5),
"trend_rider_v7_vt32": lambda **kw: TrendRiderV7(target_vol=0.32, min_lev=0.7),
"trend_rider_v7_vt36": lambda **kw: TrendRiderV7(target_vol=0.36, min_lev=0.75),
# --- Stock-picker ensemble strategies (S&P 500 universe) ---
"ensemble_alpha_top10": lambda **kw: EnsembleAlphaStrategy(top_n=10),
"ensemble_alpha_top12": lambda **kw: EnsembleAlphaStrategy(top_n=12),
@@ -222,6 +223,7 @@ ETF_STRATEGY_UNIVERSES = {
"trend_rider_v7": sorted(set(ETF_UNIVERSE)),
"trend_rider_v7_vt24": sorted(set(ETF_UNIVERSE)),
"trend_rider_v7_vt32": sorted(set(ETF_UNIVERSE)),
"trend_rider_v7_vt36": sorted(set(ETF_UNIVERSE)),
}
# Strategies that use the market's stock universe PLUS fixed extra ETF tickers.
@@ -1296,6 +1298,7 @@ def cmd_monitor(args):
print(f" Evening: {sched['eve_h']:02d}:{sched['eve_m']:02d} {sched['tz']}")
print(f" Fixed fee: {fee:.2f}/trade")
print(f" Strategies: {', '.join(strategies)}")
print(f" Auto-reload: ON (new strategies picked up each phase)")
print(f"{'='*60}")
# Use UTC as common reference for sleeping
@@ -1328,12 +1331,34 @@ def cmd_monitor(args):
all_candidates.extend(_next_events_for_market(mkt, now_utc))
return min(all_candidates, key=lambda x: x[0])
def _reload_strategies():
"""Re-read STRATEGY_REGISTRY to pick up new strategies without restart."""
import importlib
import trader as _self_mod
importlib.reload(_self_mod)
current = list(_self_mod.STRATEGY_REGISTRY.keys())
return current, _self_mod.STRATEGY_REGISTRY
def _run_phase(market, phase, now_utc):
"""Run all strategies for a market/phase."""
nonlocal strategies
sched = market_schedules[market]
tz = sched["tz"]
now_local = now_utc.astimezone(tz)
# Hot-reload strategy list from registry
try:
reloaded_names, reloaded_reg = _reload_strategies()
new_strats = set(reloaded_names) - set(strategies)
if new_strats:
print(f"[monitor] Hot-reload: +{len(new_strats)} new strategies: "
f"{', '.join(sorted(new_strats))}")
strategies = reloaded_names
# Update the global registry so cmd_morning/cmd_auto use new strategies
globals().update({"STRATEGY_REGISTRY": reloaded_reg})
except Exception as e:
print(f"[monitor] Hot-reload failed (using cached list): {e}")
print(f"\n[monitor] {'='*55}")
print(f"[monitor] {market.upper()} {phase.upper()} at "
f"{now_local.strftime('%Y-%m-%d %H:%M:%S %Z')}")
@@ -1370,7 +1395,7 @@ def cmd_monitor(args):
traceback.print_exc()
print(f"[monitor] {market.upper()} {phase} done — "
f"{len(strategies)} strategies")
f"{len(strategies)} strategies\n")
while True:
now_utc = datetime.now(utc)