application/regime_execution.RegimeConditionalExecutor: reads specialists from the store, detects TODAY's regime per market, activates only those whose regime is live, nets their latest positions into per-market target weights (gross-capped). CLI book. The multi-strategy book is now regime-adaptive — trend specialists run in calm uptrends, mean-reversion in panic, automatically in/out by regime. Live demo: as of 2026-06-08 all 3 discovered specialists are DORMANT (NQ in trend_up|HIGH_vol not its calm-trend regime; GC in calm downtrend not its panic regime) -> book correctly FLAT. Discipline: don't trade out-of-regime. 21/21 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
48 lines
2.7 KiB
Python
48 lines
2.7 KiB
Python
"""Regime-conditional execution: only specialists whose regime is live today are activated and netted."""
|
|
from __future__ import annotations
|
|
|
|
import datetime as dt
|
|
|
|
import numpy as np
|
|
|
|
from fxhnt.adapters.persistence import DuckDbAnalyticalStore, SqlStrategyStore
|
|
from fxhnt.application import RegimeConditionalExecutor
|
|
from fxhnt.config import Settings
|
|
from fxhnt.domain.factory import RegimeFit, StrategyRecord, StrategyStatus, strategy_id
|
|
from fxhnt.domain.models import AssetClass, Market, PriceSeries, StrategySpec
|
|
|
|
_NOW = dt.datetime(2026, 6, 9, tzinfo=dt.timezone.utc)
|
|
|
|
|
|
class FakeData:
|
|
name = "fake"
|
|
|
|
def fetch(self, market: Market, start=None, end=None) -> PriceSeries:
|
|
close = 100.0 * np.cumprod(1.0 + np.full(600, 0.0015)) # steady uptrend -> today's regime is trend_up
|
|
return PriceSeries(market=market, dates=tuple(f"2026-{i}" for i in range(600)), close=close)
|
|
|
|
|
|
def _spec(kind: str) -> StrategySpec:
|
|
return StrategySpec(kind=kind, params={"window": 100.0} if kind == "trend" else {"window": 20.0, "z": 1.0})
|
|
|
|
|
|
def test_only_live_regime_specialists_are_activated(tmp_path) -> None:
|
|
settings = Settings(analytical_path=str(tmp_path / "an.duckdb"), operational_dsn=f"sqlite:///{tmp_path / 's.db'}")
|
|
store = SqlStrategyStore(settings.operational_dsn)
|
|
t, m = _spec("trend"), _spec("mean_reversion")
|
|
# trend specialist for trend-up regimes (matches today); mean-rev specialist for a panic regime (does not)
|
|
store.upsert(StrategyRecord(strategy_id=strategy_id(["AAA"], AssetClass.FUTURE, t), markets=["AAA"],
|
|
asset_class=AssetClass.FUTURE, spec=t, discovered_at=_NOW, updated_at=_NOW,
|
|
regime_fits=[RegimeFit(regime="trend_up|low_vol", dsr=0.97, is_sharpe=1.3, oos_sharpe=0.9, passed=True),
|
|
RegimeFit(regime="trend_up|high_vol", dsr=0.96, is_sharpe=1.2, oos_sharpe=0.8, passed=True)]))
|
|
store.upsert(StrategyRecord(strategy_id=strategy_id(["AAA"], AssetClass.FUTURE, m), markets=["AAA"],
|
|
asset_class=AssetClass.FUTURE, spec=m, discovered_at=_NOW, updated_at=_NOW,
|
|
regime_fits=[RegimeFit(regime="trend_down|high_vol", dsr=0.96, is_sharpe=1.2, oos_sharpe=0.8, passed=True)]))
|
|
|
|
ex = RegimeConditionalExecutor(FakeData(), DuckDbAnalyticalStore(settings.analytical_path), store, settings)
|
|
rb = ex.assemble(status=StrategyStatus.DISCOVERED)
|
|
|
|
assert any("trend" in a.spec and a.market == "AAA" for a in rb.active) # trend is live
|
|
assert any(d.strategy_id.startswith("mean_reversion") for d in rb.dormant) # mean-rev is dormant
|
|
assert rb.target_weights.get("AAA", 0.0) > 0 # long in the uptrend
|