a85289d7c7
Famiglia NUOVA trovata in sessione (dopo aver scartato trend/breakout/seasonal/ opzioni/funding come rumore): ogni 12h long i perdenti relativi / short i vincenti su 8 asset, market-neutral. Scorrelata (~0) da pairs e fade -> diversificatore. - engine canonico scripts/strategies/XS01_cross_sectional.py (no look-ahead, plateau OOS Sharpe 2-3.9, 5/5 anni+, edge concentrato 2025, cost-sensitive ~0.35% RT). - src/live/xsec_worker.py CrossSectionalWorker: validate_xsec_worker == backtest ESATTO (4993/1427 trade). Mirror della cadenza engine (entry-to-entry = hold+1). - gate PORT06: +XS01 -> OOS Sharpe 9.66->10.07, FULL DD 3.68->3.46 (OOS DD +0.17pp, risk-contrib 2.2%). xsec_port06_gate.py. - wiring: _defs XSEC in PORT06 (19 sleeve, family XSEC), build_everything, runner kind=xsec, asset_days da supported (fix fetch alt anche per paper sleeves), paper. - 8 gambe -> niente exec reale -> gira PAPER. Regression-lock 18->19, FULL 7.20->7.34, OOS 9.66->10.07. 93 test verdi. Diario 2026-06-09-xs01-cross-sectional.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
2.3 KiB
Python
55 lines
2.3 KiB
Python
"""Valida il CrossSectionalWorker: replay bar-per-bar == backtest XS01.xsec_sim?
|
|
|
|
Come validate_worker_pairs: alimenta il worker con finestre trailing crescenti del
|
|
pannello 8-asset e confronta capitale finale e n.trade col backtest di riferimento
|
|
scripts.strategies.XS01_cross_sectional.xsec_sim. Se combaciano, la semantica live e' fedele.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import shutil
|
|
import sys
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pandas as pd
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from src.live.xsec_worker import CrossSectionalWorker
|
|
from scripts.strategies.XS01_cross_sectional import aligned_panel, xsec_sim, UNIVERSE, LB, HOLD
|
|
|
|
|
|
def main():
|
|
print("=" * 88)
|
|
print(" VALIDAZIONE CrossSectionalWorker — replay live vs backtest xsec_sim (fee 0.10% RT/book)")
|
|
print("=" * 88)
|
|
M = aligned_panel(UNIVERSE)
|
|
dfs = {a: pd.DataFrame({"timestamp": M.index.values, "close": M[a].values}) for a in UNIVERSE}
|
|
n = len(M)
|
|
tmp = Path(tempfile.mkdtemp(prefix="xsec_val_"))
|
|
try:
|
|
w = CrossSectionalWorker(UNIVERSE, tf="1h", params={"lb": LB, "hold": HOLD},
|
|
fee_rt=0.0005, data_dir=tmp)
|
|
w._save = lambda: None; w._log = lambda *a, **k: None; w._notify = lambda *a, **k: None
|
|
window = LB + 6
|
|
for k in range(LB + 1, n + 1): # prima finestra = lb+1 barre -> ingresso al bar lb
|
|
lo = max(0, k - window)
|
|
w.tick({a: dfs[a].iloc[lo:k] for a in UNIVERSE})
|
|
bt = xsec_sim(UNIVERSE)
|
|
bt_cap = 1000.0 * (1 + bt["ret"] / 100)
|
|
cap_ok = abs(w.capital - bt_cap) / bt_cap < 0.02 if bt_cap else False
|
|
trd_ok = abs(w.total_trades - bt["trades"]) <= max(2, bt["trades"] * 0.02)
|
|
ww = w.total_wins / w.total_trades * 100 if w.total_trades else 0
|
|
print(f"\n {'':<6}{'cap':>14}{'trades':>8}{'win%':>7}")
|
|
print(f" WORKER{w.capital:>14.0f}{w.total_trades:>8d}{ww:>7.1f}")
|
|
print(f" BCKTST{bt_cap:>14.0f}{bt['trades']:>8d}{bt['win']:>7.1f}")
|
|
print(f"\n ESITO: {'OK (replay == backtest)' if (cap_ok and trd_ok) else 'DIFF -> INDAGARE'}")
|
|
print(" (diff minime attese da bar finale aperta / troncamento)")
|
|
finally:
|
|
shutil.rmtree(tmp, ignore_errors=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|