ADR 0002 designs the real vision: a standing fleet of agents continuously breeding regime-specialist strategies into an adaptively-allocated book, with multiple-testing-at-fleet-scale as the constraint the whole architecture is built around (structured search + regime-conditioning + forward-validation + global-N accounting = the moat). domain/regime: VolTrendClassifier (causal trend x volatility state) + evaluate_by_regime (judge a strategy WITHIN each regime). Demo on real NQ: trend(200) is MARGINAL on the full sample but a clear OOS-confirmed SPECIALIST in trend_up|low_vol (DSR 0.984, OOS +0.93), correctly rejected in range/high-vol regimes — the architecture's thesis proven. 16/16 tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
33 lines
1.5 KiB
Python
33 lines
1.5 KiB
Python
"""Regime layer: causal classification + regime-conditional evaluation."""
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
from fxhnt.domain.models import AssetClass, Market, PriceSeries
|
|
from fxhnt.domain.regime import WARMUP, VolTrendClassifier, evaluate_by_regime
|
|
|
|
|
|
def _prices(close: np.ndarray) -> PriceSeries:
|
|
return PriceSeries(market=Market(symbol="X", asset_class=AssetClass.FUTURE),
|
|
dates=tuple(str(i) for i in range(len(close))), close=np.asarray(close, float))
|
|
|
|
|
|
def test_classifier_is_causal_and_labels_an_uptrend() -> None:
|
|
close = 100.0 * np.cumprod(1.0 + np.full(400, 0.002)) # steady uptrend
|
|
labels = VolTrendClassifier(trend_window=100).classify(_prices(close))
|
|
assert len(labels) == 400
|
|
assert all(lbl == WARMUP for lbl in labels[:100]) # warmup region is unlabelled
|
|
post = labels[150:]
|
|
assert sum(lbl.startswith("trend_up") for lbl in post) > 0.8 * len(post)
|
|
|
|
|
|
def test_evaluate_by_regime_finds_the_specialist_regime() -> None:
|
|
n = 600
|
|
labels = tuple("trend_up|low_vol" if i % 2 == 0 else "range|low_vol" for i in range(n))
|
|
drift = np.where(np.arange(n) % 2 == 0, 0.001, 0.0) # edge only in the trend regime
|
|
r = drift + np.random.default_rng(0).normal(0, 0.0005, n)
|
|
verdicts = evaluate_by_regime(r, labels, min_obs=100)
|
|
assert {"trend_up|low_vol", "range|low_vol"} <= set(verdicts)
|
|
# the regime that carries the drift is judged stronger than the flat one
|
|
assert verdicts["trend_up|low_vol"].dsr > verdicts["range|low_vol"].dsr
|