From b8bac26b8f877029e26a9a0929544c30448b44f7 Mon Sep 17 00:00:00 2001 From: Gahow Wang Date: Thu, 21 May 2026 20:57:16 +0800 Subject: [PATCH] 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) --- bridge.py | 6 ++++++ trader.py | 27 ++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/bridge.py b/bridge.py index 1b77058..1e127b7 100644 --- a/bridge.py +++ b/bridge.py @@ -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", diff --git a/trader.py b/trader.py index e9d2eab..0a5f944 100644 --- a/trader.py +++ b/trader.py @@ -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)