Files
PythagorasGoal/scripts/research/intraday/verify_intra.py
T
Adriano Dal Pastro 24565974c0 research(intraday): asse intraday/microstruttura — lead più vicino al reale ma NON deployabile
16 agenti su segnali low-turnover intraday (sessione/funding, reversione post-evento, breakout
range del giorno prima) su feed certificati 1h/15m, giudice = marginal scorer indurito + fee-sweep.
Lab: intra_score.py (wrappa study_marginal a TF scelto + turnover/fee), meta_intra.py (corr-TP01 +
per-cut), verify_intra.py (walk-forward + in-sample-null + drop-one + fee-stress).

Esito: 10/16 "earns_slot" -> 5 genuinamente ortogonali (corr<0.4). Combo dei 5: Sharpe 1.80, corr
0.17, leak-free, passa walk-forward (+0.30/+0.37 dove l'ortho dava -0.07), pre-2025 uplift +0.28,
drop-one e fee-robusto. Sembrava IL lead.

3 scettici: (1) open_drive = ARTEFATTO etichettatura UTC (shift confine 4h -> uplift negativo);
prevday_range_breakout REGGE (unico onesto, eseguibile). (2) combo fallisce il null a corr-zero
(20-24° pctl: aggiunge meno del rumore), è HEDGE (corr -0.57..-0.80 a Sharpe-TP01) + tail-luck
(80% PnL in top-5 giorni delle gambe revert). (3) robust-plateau ma null-pctl 0.20 = diversificazione
di stream ortogonale, non timing-alpha; + finzione fee micro-ribilanciamento a $600.

Verdetto: niente in live, resta solo TP01. Lead forward-monitor: prevday_range_breakout. Lezioni
harness da codificare: test shift-confine-giorno (artefatti calendar), fee discretizzata a piccolo
capitale, causality guard nel lab intraday. Diario 2026-06-21-intraday-microstructure.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 14:20:19 +00:00

89 lines
4.2 KiB
Python

"""verify_intra — adversarial gauntlet on the intraday orthogonal combo, the SAME tests
that killed the ortho relative-value wave. Does the low-turnover intraday combo survive?
1. in-sample (pre-2025) standalone Sharpe + per-cut uplift (is it pre-2025 real or 2025-only?)
2. WALK-FORWARD selection (pick orthogonal positive-uplift signals on PAST data, test forward)
3. drop-one-mechanism (carried by one signal?)
4. fee stress to 0.30% RT
"""
from __future__ import annotations
import importlib.util, sys
from pathlib import Path
import numpy as np, pandas as pd
HERE = Path(__file__).resolve().parent
sys.path.insert(0, "/opt/docker/PythagorasGoal/scripts/research/alt")
import altlib as al # noqa: E402
AG = HERE/"agents"
ORTHO = ["agent_05_open_drive", "agent_09_prevday_range_breakout", "agent_06_vol_event_revert_15m",
"agent_07_volume_spike_revert", "agent_08_gap_fill"]
def _t(name):
p = AG/f"{name}.py"; s = importlib.util.spec_from_file_location(name, p); m = importlib.util.module_from_spec(s); s.loader.exec_module(m); return m.target
def _sh(s):
r = np.asarray(s.dropna().values, float); return float(np.mean(r)/np.std(r)*np.sqrt(365.25)) if len(r) > 2 and np.std(r) > 0 else 0.0
def _u(c, B, cut="2018-01-01", end=None, w=0.25):
J = pd.concat({"B": B, "C": c}, axis=1, join="inner").dropna(); J = J[J.index >= pd.Timestamp(cut, tz="UTC")]
if end: J = J[J.index < pd.Timestamp(end, tz="UTC")]
return _sh((1-w)*J["B"]+w*J["C"]) - _sh(J["B"]) if len(J) > 30 else float("nan")
def daily(name, fee=al.FEE_SIDE):
tf = "15m" if "_15m" in name else "1h"
return al.candidate_daily(_t(name), tf=tf, fee_side=fee)
def main():
B = al.tp01_baseline_daily()
dl = {n: daily(n) for n in ORTHO}
M = pd.concat(dl, axis=1, join="inner").dropna()
combo = M.mean(axis=1)
H = pd.Timestamp("2025-01-01", tz="UTC")
ci = combo[combo.index < H]
print(f"\n COMBO standalone Sharpe full {_sh(combo):.2f} PRE-2025 {_sh(ci):.2f} corrTP {pd.concat({'b':B,'c':combo},axis=1,join='inner').dropna().corr().iloc[0,1]:.2f}")
print(f" per-cut uplift: " + " ".join(f"{c[:4]} {_u(combo,B,c):+.2f}" for c in ["2021-01-01","2022-01-01","2023-01-01","2024-01-01","2025-01-01"]))
# pre-2025-only uplift (exclude the suspect window entirely)
pre = pd.concat({"B": B, "C": combo}, axis=1, join="inner").dropna(); pre = pre[pre.index < H]
print(f" PRE-2025 ONLY uplift (2018->2025): {_sh(0.75*pre['B']+0.25*pre['C'])-_sh(pre['B']):+.3f}")
print("\n WALK-FORWARD SELECTION (pick orthogonal +uplift signals on PAST only, test fwd):")
ALL = sorted(p.stem for p in AG.glob("agent_*.py"))
dlall = {}
for n in ALL:
try: dlall[n] = daily(n)
except Exception: pass
for sel_end in ["2023-01-01", "2024-01-01"]:
picks = []
for n, d in dlall.items():
up = _u(d, B, "2018-01-01", sel_end)
cc = pd.concat({"b": B, "c": d}, axis=1, join="inner").dropna()
cc = cc[cc.index < pd.Timestamp(sel_end, tz="UTC")]
corr = abs(cc.corr().iloc[0, 1]) if len(cc) > 30 else 1
if not np.isnan(up) and up > 0.05 and corr < 0.4:
picks.append(n)
if picks:
cb = pd.concat({n: dlall[n] for n in picks}, axis=1, join="inner").dropna().mean(axis=1)
print(f" select<{sel_end}: {len(picks)} picks {[p.replace('agent_','')[:12] for p in picks]}")
print(f" -> FORWARD uplift {sel_end}->now: {_u(cb, B, sel_end):+.3f}")
else:
print(f" select<{sel_end}: no qualifying picks")
print("\n DROP-ONE-MECHANISM (full & pre-2025 uplift):")
for drop in ORTHO:
keep = [n for n in ORTHO if n != drop]
cb = M[keep].mean(axis=1)
pr = pd.concat({"B": B, "C": cb}, axis=1, join="inner").dropna(); pr = pr[pr.index < H]
print(f" -{drop.replace('agent_',''):<26} full {_u(cb,B):+.3f} pre2025 {_sh(0.75*pr['B']+0.25*pr['C'])-_sh(pr['B']):+.3f}")
print("\n FEE STRESS (combo):")
for fee in [0.0005, 0.001, 0.0015]:
cb = pd.concat({n: daily(n, fee) for n in ORTHO}, axis=1, join="inner").dropna().mean(axis=1)
print(f" {2*fee*100:.2f}%RT: standalone Sh {_sh(cb):.2f} uplift_full {_u(cb,B):+.3f}")
if __name__ == "__main__":
main()