"""XSR-REPRO — lo Sharpe 1.82 di XSR01 non si riproduce: e' un problema di CODICE, di DATI, o di LENTE? PERCHE'. XSR01 ha un gate di deploy PRE-REGISTRATO al 2026-10-23 (`r0725_xsr_deploy_gate.py`): deploy solo se Sharpe forward >= 1.0 E haircut di eseguibilita' a $5.000 <= 40%. I suoi numeri di ammissione — Sharpe netta 1.82, lorda 2.70, maxDD -2.6%, vol 2.3%, ret 4.2%/a, lag 1.82/1.19/0.81/ 0.51, Sharpe per anno 1.03/1.98/3.11 — sono in CLAUDE.md, nel diario del 25/07 e CABLATI nel gate (`IS_SHARPE_NET = 1.82`). Il filone XS-LITE (22/08) ha misurato che sulla finestra identica a quella di scoperta oggi escono 1.75 (lente "paniere") e 2.23 (lente "libro"): 1.82 non e' nessuna delle due. Una decisione fra due mesi poggia su un numero che non si riproduce. Questo script NON giudica XSR01 e NON anticipa il gate del 23/10 (sarebbe selection-on-forward). Stabilisce SOLO se il numero su cui si decidera' e' solido, in quest'ordine: T1 QUALE LENTE. Tre implementazioni convivono nel repo e danno numeri diversi sugli STESSI dati: L1 `r0725_statarb_basket_gate.basket_from_positions(demean=True)` — media per riga sulle sole colonne NON-NaN (divisore variabile). E' la lente che ha girato i gate (marginale, DSR). L2 `r0725_statarb_demean_skeptic.ret_from_pos` — P.fillna(0) e divisore FISSO 50. L3 `scripts/live/paper_xsr.build_panel` + `_step` — libro con capitale, pesi Q/50, gamba BTC esplicita. E' quella che alimenta il gate. T2 QUALE STATO DEL DATO. `data/raw` e' gitignored e il cron riscrive i parquet HL ogni notte. Ipotesi da falsificare: "stesso codice, dati diversi" (identico allo scoperto GTAA/TLT 07/08). Prove indipendenti: (a) il log del cron registra `reali`/`bfill`/`start_reale` per simbolo a ogni giro -> se il conteggio delle barre PASSATE e' cambiato, il feed e' stato riscritto; (b) lo Sharpe per ANNO CHIUSO (2024, 2025) e' un'impronta della storia: se la storia e' stabile deve riprodursi al centesimo attraverso 4 settimane di riscritture. T3 IL MONITOR FORWARD. `paper_xsr` e' forward-only e append-only, quindi in teoria immune alla riscrittura. Test diretto: replay del libro MODELED sui dati di OGGI dall'inception, barra per barra, contro cio' che il monitor ha REGISTRATO giorno per giorno. Se la storia fosse stabile e la lente giusta, le due serie devono coincidere. T4 L'HAIRCUT A $5.000, l'altra meta' del gate: dipende dal min-order del venue. Il numero pubblicato ($14.41 di ticket, soglia 40%) assume min-order $5 (Deribit). Hyperliquid potrebbe essere $10. Si misura la sensibilita'. COSA MI ASPETTAVO PRIMA DI MISURARE: che fosse la lente (tre implementazioni, nessuna nominata nel diario) e che il dato fosse stabile, perche' il log del cron mostra bar-count che crescono di 1/g. Solo la prima meta' e' risultata vera. nice -n 19 timeout 900 uv run python scripts/research/r0822_xsr_repro.py """ from __future__ import annotations import json import re import sys from collections import OrderedDict from pathlib import Path import numpy as np import pandas as pd ROOT = Path(__file__).resolve().parents[2] RAW = ROOT / "data" / "raw" for _p in (ROOT, ROOT / "scripts" / "research", ROOT / "scripts" / "research" / "alt"): sys.path.insert(0, str(_p)) from r0725_statarb_multi import BASE, MIN_BARS, _dd, _sh, load_hl, signal, universe # noqa: E402 from r0725_statarb_basket_gate import basket_from_positions, pair_frames # noqa: E402 from r0725_statarb_demean_skeptic import ret_from_pos # noqa: E402 ANN = np.sqrt(365.0) DISCOVERY = pd.Timestamp("2026-07-25", tz="UTC") # ultima barra vista dalla scoperta (PROVVISORIA) CLOSED_AT_DISCOVERY = pd.Timestamp("2026-07-24", tz="UTC") # ultima barra CHIUSA quel giorno CRONLOG = ROOT / "logs" / "cron_daily.log" FWD = ROOT / "data" / "paper_xsr" / "returns.jsonl" FWD_STATE = ROOT / "data" / "paper_xsr" / "state.json" # I NUMERI PUBBLICATI (CLAUDE.md + diario 2026-07-25 + docstring di paper_xsr / del gate) PUB = dict(netta=1.82, lorda=2.70, dd=-2.6, vol=2.3, ret=4.2, lag=(1.82, 1.19, 0.81, 0.51), anni=(1.03, 1.98, 3.11)) def _cut(df, ts): return df[df.index <= ts] def _stats(r: pd.Series) -> dict: return dict(n=len(r), sh=_sh(r.values), dd=_dd(r.values) * 100, vol=float(r.std() * ANN * 100), ret=float(r.mean() * 365 * 100)) # ------------------------------------------------------------------ pannelli def panel(partial_last: bool = False, cut: pd.Timestamp | None = None): """(P, S) come `pair_frames`, ma con due gradi di liberta' espliciti: `cut` -> ultima barra inclusa (troncamento dell'INPUT, non dell'output); `partial_last` -> l'ultima barra e' resa PARZIALE (close := open), cioe' com'era il giorno in cui il cron l'aveva appena scritta. E' l'unico stato del dato che questo script NON puo' leggere da disco: va ricostruito.""" def px(sym): d = pd.read_parquet(RAW / f"hl_{sym.lower()}_1d.parquet") ix = pd.to_datetime(d["timestamp"], unit="ms", utc=True) c = pd.Series(d["close"].astype(float).values, index=ix).sort_index() o = pd.Series(d["open"].astype(float).values, index=ix).sort_index() c = c[~c.index.duplicated(keep="last")] o = o[~o.index.duplicated(keep="last")] if cut is not None: c, o = c[c.index <= cut], o[o.index <= cut] if partial_last and len(c): c.iloc[-1] = o.iloc[-1] return c base = px(BASE) pc, sc = {}, {} for sym in universe(): try: tgt = px(sym) except FileNotFoundError: continue ix = base.index.intersection(tgt.index) if len(ix) < MIN_BARS: continue p, s = signal(base[ix], tgt[ix]) pc[sym] = pd.Series(p, index=ix) sc[sym] = pd.Series(s, index=ix) P = pd.concat(pc, axis=1, sort=True).sort_index() S = pd.concat(sc, axis=1, sort=True).sort_index() return P, S def lens_L1(P, S): return basket_from_positions(P, S, demean=True) def lens_L2(P, S, fee_leg=0.0005, lag=0): P0 = P.fillna(0.0) S0 = S.reindex(P0.index)[P0.columns] return ret_from_pos(P0, S0, fee_leg=fee_leg, lag=lag) # ------------------------------------------------------------------ T1 def t1_lenti(): print("=" * 100) print(" T1 — QUALE LENTE, E QUALE STATO DEL DATO, PRODUCE 1.82") print("=" * 100) P, S = pair_frames() print(f"\n dato su disco oggi: {P.shape[1]} coppie, {len(P)} barre " f"({P.index[0].date()} -> {P.index[-1].date()})") rows = [] for tag, (Pi, Si) in [ ("OGGI pieno", (P, S)), (f"tronc. {DISCOVERY.date()} (barra completa)", (_cut(P, DISCOVERY), _cut(S, DISCOVERY))), ("tronc. 2026-07-24 = ultima barra CHIUSA il 25/07", (_cut(P, CLOSED_AT_DISCOVERY), _cut(S, CLOSED_AT_DISCOVERY))), ]: rows.append((f"L1 gate {tag}", _stats(lens_L1(Pi, Si)))) rows.append((f"L2 scett. {tag}", _stats(lens_L2(Pi, Si)))) Pp, Sp = panel(partial_last=True, cut=DISCOVERY) rows.append(("L1 gate STATO DEL 25/07 (ultima barra PARZIALE)", _stats(lens_L1(Pp, Sp)))) rows.append(("L2 scett. STATO DEL 25/07 (ultima barra PARZIALE)", _stats(lens_L2(Pp, Sp)))) print(f"\n {'lente / stato del dato':<52}{'n':>6}{'Sharpe':>9}{'maxDD':>9}{'vol':>8}{'ret/a':>8}") for nm, s in rows: print(f" {nm:<52}{s['n']:>6}{s['sh']:>9.4f}{s['dd']:>8.2f}%{s['vol']:>7.2f}%{s['ret']:>7.2f}%") print(f" {'--- PUBBLICATO 2026-07-25':<52}{'937?':>6}{PUB['netta']:>9.2f}" f"{PUB['dd']:>8.1f}%{PUB['vol']:>7.1f}%{PUB['ret']:>7.1f}%") # confronto completo sugli OTTO numeri pubblicati, per la sola combinazione che li riproduce print("\n --- verifica completa sullo stato che riproduce: L2 + ultima barra PARZIALE ---") r = lens_L2(Pp, Sp) g = lens_L2(Pp, Sp, fee_leg=0.0) lags = tuple(_sh(lens_L2(Pp, Sp, lag=L).values) for L in (0, 1, 2, 3)) anni = tuple(_sh(r[r.index.year == y].values) for y in (2024, 2025, 2026)) st = _stats(r) ok = 0 def chk(nome, mio, pub, tol): nonlocal ok good = abs(mio - pub) <= tol ok += good print(f" {nome:<22} misurato {mio:>8.4f} pubblicato {pub:>7.2f} " f"{'COINCIDE' if good else 'DIVERGE'}") chk("Sharpe netta", st["sh"], PUB["netta"], 0.005) chk("Sharpe lorda", _sh(g.values), PUB["lorda"], 0.005) chk("maxDD %", st["dd"], PUB["dd"], 0.05) chk("vol ann %", st["vol"], PUB["vol"], 0.05) chk("ret ann %", st["ret"], PUB["ret"], 0.05) for i, L in enumerate((0, 1, 2, 3)): chk(f"lag +{L}g", lags[i], PUB["lag"][i], 0.006) for i, y in enumerate((2024, 2025, 2026)): chk(f"Sharpe {y}", anni[i], PUB["anni"][i], 0.005) print(f"\n -> {ok}/12 numeri pubblicati riprodotti da L2 sullo stato del dato del 25/07.") # la lente che ha girato i GATE non e' quella del titolo print("\n --- e la lente che ha girato i gate (marginale, deflated-Sharpe) e' L1, non L2 ---") print(f" L1 sullo stato del 25/07 : Sharpe {_sh(lens_L1(Pp, Sp).values):.4f}") print(" docstring di r0725_statarb_demean_skeptic.py, riga 5, cita il gate come " "\"Sharpe 1.79\"") print(" -> il numero di TITOLO (1.82, L2) e i numeri di GATE (DSR/marginale, L1) non sono") print(" mai stati la stessa serie.") # deflated-Sharpe delle 3 varianti pre-registrate, oggi, sulle due lenti try: from altlib import deflated_sharpe from src.portfolio.sleeves import XS_UNIVERSE maj = [s for s in XS_UNIVERSE if s != BASE] Pc, Sc = _cut(P, DISCOVERY), _cut(S, DISCOVERY) var = {"V1 ALL50": dict(cols=None, demean=False), "V2 MAJ19": dict(cols=maj, demean=False), "V3 DEMEAN": dict(cols=None, demean=True)} ser = {k: basket_from_positions(Pc, Sc, **v) for k, v in var.items()} srs = [_sh(v.values) for v in ser.values()] print("\n --- deflated-Sharpe delle 3 varianti pre-registrate, L1, finestra 25/07 ---") for k, v in ser.items(): d, nm = deflated_sharpe(_sh(v.values), srs, v.values, dpy=365.0) print(f" {k:<11} Sharpe {_sh(v.values):>5.2f} DSR {d:>6.3f} " f"(max atteso dal null {nm:>5.2f}) {'PASS' if d >= 0.95 else 'SOTTO 0.95'}") print(" (pubblicato per V3: DSR 0.985)") except Exception as e: # pragma: no cover print(f" [DSR non calcolabile: {e.__class__.__name__}: {e}]") return P, S # ------------------------------------------------------------------ T2 def t2_feed(P, S): print("\n" + "=" * 100) print(" T2 — IL FEED HL E' STATO RISCRITTO? (l'ipotesi principale: stesso codice, dati diversi)") print("=" * 100) # (a) il log del cron registra reali/bfill/start_reale a ogni giro: e' un registro storico runs, cur, inblk, hdr = OrderedDict(), None, False, None for ln in CRONLOG.read_text(errors="replace").splitlines(): m = re.match(r"^===== (\S+) cron_daily =====", ln) if m: cur, inblk = m.group(1), False runs[cur] = {} continue if "FETCH + CERTIFY Hyperliquid" in ln: inblk, hdr = True, None continue if inblk and cur is not None: if ln.strip().startswith("sym"): hdr = ln.split() continue f = ln.split() if hdr and len(f) >= 7 and re.match(r"^[A-Z0-9]+$", f[0]) and f[1].isdigit(): runs[cur][f[0]] = ((int(f[1]), f[3], f[-1]) if len(hdr) >= 9 else (int(f[1]), f[2], f[-1])) elif runs[cur] and (not ln.strip() or ln.startswith("=")): inblk = False keys = [k for k, v in runs.items() if v] a = next((k for k in keys if k.startswith("2026-07-25")), None) b = keys[-1] if keys else None print(f"\n (a) registro del cron: {len(keys)} giri con blocco Hyperliquid, " f"da {keys[0][:10]} a {keys[-1][:10]}") if a and b: A, B = runs[a], runs[b] atteso = (pd.Timestamp(b[:10]) - pd.Timestamp(a[:10])).days anom = [] for s in sorted(set(A) | set(B)): ra, rb = A.get(s), B.get(s) if ra is None or rb is None: anom.append((s, "presente in un solo giro", ra, rb)); continue if rb[0] - ra[0] != atteso or ra[1] != rb[1]: anom.append((s, f"delta {rb[0]-ra[0]} invece di {atteso}; start {ra[1]}->{rb[1]}", ra, rb)) print(f" confronto {a[:10]} vs {b[:10]} su {len(set(A)|set(B))} simboli, " f"crescita attesa {atteso} barre") if not anom: print(" -> TUTTI i simboli crescono di esattamente il numero di giorni e " "start_reale INVARIATO") for s, why, ra, rb in anom: print(f" {s:<8} {why} (verdetto oggi: {rb[2] if rb else '-'})") print(" NB: i simboli con delta 0 sono quelli con verdetto `scarta` (serie ferma sul") print(" venue): NON producono un parquet, quindi non entrano nell'universo.") # (b) impronta indipendente della storia: lo Sharpe degli ANNI CHIUSI Pp, Sp = panel(partial_last=True, cut=DISCOVERY) r_then = lens_L2(Pp, Sp) r_now = lens_L2(P, S) print("\n (b) impronta della storia — Sharpe degli anni CHIUSI, che una riscrittura sposterebbe:") print(f" {'anno':<8}{'stato 25/07':>14}{'stato oggi':>13}{'pubblicato':>13}{'barre':>8}") for y, pub in zip((2024, 2025, 2026), PUB["anni"]): s1 = r_then[r_then.index.year == y] s2 = r_now[r_now.index.year == y] note = " <- anno IN CORSO: cambia perche' contiene barre nuove" if y == 2026 else "" print(f" {y:<8}{_sh(s1.values):>14.4f}{_sh(s2.values):>13.4f}{pub:>13.2f}" f"{len(s2):>8}{note}") print(" -> 2024 e 2025 (731 barre) si riproducono al centesimo attraverso 4 settimane di") print(" riscritture notturne: la STORIA CHIUSA e' stabile.") # (c) l'unica barra che cambia: l'ultima d = pd.read_parquet(RAW / "hl_btc_1d.parquet") vol = d["volume"].astype(float).values q = float(vol[-1] / np.median(vol[-40:-1])) print("\n (c) l'unica barra che NON e' definitiva e' l'ULTIMA. `fetch_hyperliquid` gira nel") print(" cron delle 00:30 con END = oggi, quindi scrive la barra del giorno IN CORSO:") print(f" hl_btc_1d ultima barra {pd.Timestamp(int(d['timestamp'].iloc[-1]), unit='ms', tz='UTC')}" f" volume {vol[-1]:,.0f} = {q*100:.1f}% del volume mediano di un giorno pieno") print(f" (una barra piena di ~24h; questa ne ha ~{q*24*60:.0f} minuti di scambi)") print("\n VERDETTO T2: il feed VIENE riscritto, ma NON come ipotizzato. Non c'e' riscrittura") print(" arbitraria della storia: c'e' UNA barra provvisoria per volta — l'ultima — che il giro") print(" successivo completa. Ogni numero calcolato su dato HL il giorno D contiene una barra") print(" di ~30-40 minuti travestita da giorno.") # ------------------------------------------------------------------ T3 def t3_monitor(): print("\n" + "=" * 100) print(" T3 — IL MONITOR FORWARD (che alimenta il gate del 23/10) REGISTRA CIO' CHE CREDE?") print("=" * 100) if not FWD.exists() or not FWD_STATE.exists(): print(" [stato di paper_xsr assente: non misurabile]") return import importlib.util spec = importlib.util.spec_from_file_location("paper_xsr_ro", ROOT / "scripts" / "live" / "paper_xsr.py") px = importlib.util.module_from_spec(spec) spec.loader.exec_module(px) # sola lettura: non si chiama main() rec = [json.loads(x) for x in FWD.read_text().splitlines() if x.strip()] st = json.loads(FWD_STATE.read_text()) ts, dt, W, R, rb, syms = px.build_panel() print(f"\n inception {pd.Timestamp(st['start_ts'], unit='ms', tz='UTC').date()} " f"barre registrate {len(rec)} universo {len(syms)} " f"(uguale a quello congelato: {syms == st['syms']})") i0 = int(np.where(ts == st["start_ts"])[0][0]) bk = px._book(px.MODELED_CAPITAL, len(syms)) ric = {} for i in range(i0 + 1, len(ts)): ric[int(ts[i])] = px._step(bk, W[i], R[i], float(rb[i]), None) a = np.array([r["net_modeled"] for r in rec if r["ts"] in ric]) b = np.array([ric[r["ts"]] for r in rec if r["ts"] in ric]) same = int((np.abs(a - b) <= 1e-6).sum()) print(f"\n replay del libro MODELED sui dati di OGGI, dall'inception, contro cio' che il") print(f" monitor ha REGISTRATO giorno per giorno ({len(a)} barre appaiate):") print(f" barre identiche entro 1e-6 : {same}/{len(a)}") print(f" correlazione registrato/ricalcolato: {np.corrcoef(a, b)[0, 1]:+.4f}") print(f" vol annualizzata REGISTRATA : {a.std()*ANN*100:.2f}%") print(f" vol annualizzata RICALCOLATA : {b.std()*ANN*100:.2f}%") print(f" (la strategia e' progettata a vol ~{PUB['vol']:.1f}%: la seconda e' quella giusta)") frac = (a.std() / b.std()) ** 2 print(f" rapporto di varianza {(a.std()/b.std())**2:.4f} -> in radice di tempo il monitor") print(f" sta misurando ~{frac*24*60:.0f} minuti di mercato al giorno, non 24 ore.") print("\n MECCANISMO (leggibile nel codice, non dedotto): `advance()` processa le barre con") print(" ts > last_ts e poi porta last_ts sull'ULTIMA di esse. Al giro delle 00:30 del giorno D") print(" l'ultima barra e' quella di D, appena scritta e lunga ~30 minuti: il monitor ne") print(" registra il rendimento e SPOSTA last_ts oltre. Quando il giro successivo trova la") print(" barra di D completata, D e' gia' passato. Le 23 ore e mezza restanti di OGNI giorno") print(" non entrano in nessun rendimento registrato.") sh_a = float(a.mean() / a.std() * ANN) sh_b = float(b.mean() / b.std() * ANN) se = float(np.sqrt(365.0 / len(a))) # SE di uno Sharpe annualizzato su n barre giornaliere print(f"\n Sharpe della finestra forward: REGISTRATO {sh_a:+.2f} RICALCOLATO {sh_b:+.2f}") print(f" ⚠ NESSUNO DEI DUE E' UNA LETTURA DEL GATE. Con {len(a)} barre l'errore standard di") print(f" uno Sharpe annualizzato e' ~{se:.2f}: entrambi i numeri sono indistinguibili da 0 e") print(" dalla soglia 1.0. Il gate e' il 2026-10-23 e questo script non lo anticipa; qui") print(" servono solo a mostrare che i due strumenti non misurano la stessa grandezza.") # ------------------------------------------------------------------ T4 def t4_haircut(P, S): print("\n" + "=" * 100) print(" T4 — L'ALTRA META' DEL GATE: l'haircut di eseguibilita' a $5.000 e il min-order") print("=" * 100) import importlib.util spec = importlib.util.spec_from_file_location("paper_xsr_ro2", ROOT / "scripts" / "live" / "paper_xsr.py") px = importlib.util.module_from_spec(spec) spec.loader.exec_module(px) ts, dt, W, R, rb, syms = px.build_panel() n = len(syms) def replay(cap0, min_order): bk = px._book(cap0, n) out = [] for i in range(len(ts)): out.append(px._step(bk, W[i], R[i], float(rb[i]), min_order)) r = pd.Series(out, index=pd.to_datetime(ts, unit="ms", utc=True)) tot = float(np.prod(1 + r.values) - 1) fill = bk["n_fill"] / max(bk["n_fill"] + bk["n_skip"], 1) return r, tot, fill print("\n replay dell'INTERA storia in-sample (non della finestra forward: quella e' del gate)") print(f" {'libro':<28}{'Sharpe':>9}{'ret tot':>10}{'gambe eseguite':>17}{'haircut vs modeled':>21}") rm, tot_m, _ = replay(5000.0, None) print(f" {'MODELED (ribil. continuo)':<28}{_sh(rm.values):>9.2f}{tot_m*100:>9.1f}%" f"{'100%':>17}{'-':>21}") for mo in (5.0, 10.0, 25.0): r5, tot5, fill = replay(5000.0, mo) hc = (tot_m - tot5) / abs(tot_m) if tot_m else float("nan") flag = "" if hc <= 0.40 else " <-- SOPRA LA GUARDIA 40%" print(f" {f'REAL $5.000 min-order ${mo:.0f}':<28}{_sh(r5.values):>9.2f}{tot5*100:>9.1f}%" f"{fill*100:>16.0f}%{hc*100:>20.1f}%{flag}") # ticket medio per gamba dW = np.abs(np.diff(W, axis=0, prepend=np.zeros((1, n)))) tick = dW[dW > 0] * 5000.0 print(f"\n ticket per gamba a $5.000: mediano ${np.median(tick):.2f} medio ${tick.mean():.2f}") print(f" (pubblicato: $14.41 medio) quota di ordini sotto $5: {(tick < 5).mean()*100:.0f}%" f" sotto $10: {(tick < 10).mean()*100:.0f}%") print("\n -> il min-order NON e' un dettaglio: e' la variabile che decide la seconda meta' del") print(" gate, e il numero pubblicato assume $5. Se Hyperliquid e' a $10, la riga da leggere") print(" il 23/10 e' la seconda, non la prima.") def main() -> None: P, S = t1_lenti() t2_feed(P, S) t3_monitor() t4_haircut(P, S) print("\n" + "=" * 100) print(" CONTROMISURA PROPOSTA (non implementata: questo script non tocca produzione)") print("=" * 100) print(""" Il difetto strutturale e' che `data/raw` e' gitignored e ogni numero di ammissione basato su hl_* e' irriproducibile per costruzione. La contromisura minima, nella forma che il progetto usa gia' altrove (guardia `TRONCATO`/`STORIA-CORTA` di `fetch_ib_equities.certify`): 1. IMPRONTA PER FINESTRA CHIUSA. A ogni giro, il certificatore HL scrive in un file versionato (non in data/raw) `sha256` + conteggio barre + primo/ultimo timestamp per simbolo, calcolati SOLO sulle barre fino a ieri (finestra chiusa). Un test confronta l'impronta di una finestra gia' registrata: se cambia, la storia e' stata riscritta e il test rompe. Costa ~50 hash/giorno. 2. LA BARRA IN CORSO VA ETICHETTATA, NON NASCOSTA. `load_hl` (e chiunque legga hl_*) deve poter chiedere solo le barre CHIUSE. La forma minima e' un flag esplicito con default sicuro; la forma che il progetto preferisce e' derivare la regola dal codice sorvegliato invece di ridichiararla in ogni consumatore. 3. I MONITOR FORWARD NON DEVONO CONSUMARE LA BARRA IN CORSO. `advance()` deve fermarsi all'ultima barra CHIUSA; oggi ne consuma una provvisoria e sposta `last_ts` oltre, perdendo il 98% di ogni giornata. E' un bug di produzione, non una scelta di modellazione. RISULTATI PUBBLICATI CHE POGGIANO SU hl_* (tutti da citare con questa riserva): XS01 (sleeve nel book di ricerca al 15%) — 19 major HL 1d XSR01 (candidato, gate 23/10) — 50 alt HL 1d STATARB-MULTI (falsificazione 25/07) — 50 coppie alt/BTC HL XS-LITE (22/08) — stesse serie CC01 / breadth alt / dispersione XDISP — stesse serie Per i BACKTEST l'effetto e' una barra su ~965 (piccolo ma non nullo: qui vale 1.82 -> 1.78). Per i MONITOR FORWARD l'effetto e' totale, perche' la barra provvisoria e' l'UNICA che leggono. """) if __name__ == "__main__": main()