harness(realism): codifica le 2 lezioni dell'onda intraday (day-boundary + small-cap fills)
Due gate nuovi in altlib.py (test tests/test_harness_realism.py, suite intera verde): 1. day_boundary_robust(target_fn, tf): shifta il confine del giorno UTC e ri-misura l'uplift marginale. INVARIANT (segnale di prezzo, spread 0) / ROBUST (effetto calendario vero, resta positivo) / ARTIFACT-RISK (uplift si inverte = etichettatura). Riproduce da solo il verdetto degli scettici: open_drive +0.23@00:00 -> -0.33@+8h = ARTIFACT-RISK; prevday_breakout = ROBUST. Decoupling chiave: il segnale vede il clock shiftato, il backtest usa il calendario reale. 2. eval_weights_smallcap(df, target, capital=600, min_order=5): salta i ribilanciamenti di nozionale < min_order (la finzione del micro-trading sub-dollaro che eval_weights costa come fee proporzionale su un overlay vol-target), riporta lo Sharpe haircut reale vs modellato. Vale per ogni sleeve a $600, TP01 incluso. CLAUDE.md aggiornato (sezione HARNESS REALISM). La pipeline di falsificazione ora becca da sola artefatti-calendario e finzioni-fee, oltre a hedge/regime-luck/leakage gia' codificati. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
"""Locks the two harness-realism gates codified from the 2026-06-21 intraday wave:
|
||||
* day_boundary_robust — a calendar/session/hour signal whose marginal uplift INVERTS when
|
||||
the UTC day boundary is shifted is a labeling ARTIFACT (this killed open_drive). A price
|
||||
signal that ignores the calendar is INVARIANT; a genuine calendar effect is ROBUST.
|
||||
* eval_weights_smallcap — at ~$600 a sub-min_order rebalance can't execute; the modeled
|
||||
proportional fee on thousands of sub-dollar moves is a fiction. The realistic evaluator
|
||||
skips them.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
||||
import altlib as al # noqa: E402
|
||||
|
||||
|
||||
# --- LESSON 1: day-boundary robustness -------------------------------------------------
|
||||
def test_day_boundary_invariant_for_price_signal():
|
||||
"""A signal that never reads the calendar is INVARIANT to the day-boundary shift."""
|
||||
def mom(df):
|
||||
c = df["close"].values
|
||||
return np.tanh(3 * (c / al.sma(c, 200) - 1))
|
||||
r = al.day_boundary_robust(mom, offsets=(0, 6, 12, 18))
|
||||
assert r["verdict"] == "INVARIANT"
|
||||
assert r["spread"] == 0.0
|
||||
assert r["calendar_sensitive"] is False
|
||||
|
||||
|
||||
def test_day_boundary_flags_calendar_artifact():
|
||||
"""A pure hour-of-day bias is calendar-sensitive: its uplift swings with the boundary
|
||||
(the open_drive failure mode). It must be flagged, never INVARIANT."""
|
||||
def hourbias(df):
|
||||
h = pd.to_datetime(df["datetime"], utc=True).dt.hour.values
|
||||
return np.where(h < 8, 1.0, 0.0)
|
||||
r = al.day_boundary_robust(hourbias, offsets=(0, 6, 12, 18))
|
||||
assert r["calendar_sensitive"] is True
|
||||
assert r["spread"] > 0.05
|
||||
assert r["verdict"] != "INVARIANT"
|
||||
|
||||
|
||||
# --- LESSON 2: small-capital fill realism ----------------------------------------------
|
||||
def test_smallcap_skips_subdollar_rebalances():
|
||||
"""Thousands of sub-min_order wiggles (a vol-target overlay's fiction) do NOT execute."""
|
||||
df = al.get("BTC", "1h")
|
||||
rng = np.random.default_rng(0)
|
||||
micro = 0.5 + 0.001 * rng.standard_normal(len(df)) # tiny drift around 0.5
|
||||
r = al.eval_weights_smallcap(df, micro, capital=600.0, min_order=5.0)
|
||||
modeled_turn = al.eval_weights(df, micro)["turnover_per_year"]
|
||||
assert r["executed_turnover_per_year"] < modeled_turn * 0.2
|
||||
assert r["n_executed_trades"] < len(df) * 0.05
|
||||
|
||||
|
||||
def test_smallcap_keeps_real_trades():
|
||||
"""A low-turnover signal with genuine large moves executes them, ~no Sharpe haircut."""
|
||||
df = al.get("BTC", "1h")
|
||||
step = np.zeros(len(df)); step[: len(df) // 2] = 0.5; step[len(df) // 2:] = -0.5
|
||||
r = al.eval_weights_smallcap(df, step, capital=600.0, min_order=5.0)
|
||||
assert r["n_executed_trades"] >= 2
|
||||
assert abs(r["sharpe_haircut"]) < 0.2
|
||||
Reference in New Issue
Block a user