research(wave-0822): XS01-OOS — l'edge esiste FUORI dalla finestra di scoperta ed e' piu' grande li'; il gate del prop-alloc si sostituisce, non si aspetta
This commit is contained in:
@@ -692,7 +692,7 @@ stessi hanno nominato senza poterli eseguire**. Stesso contratto di consegna, st
|
||||
|
||||
| # | filone | domanda che decide | verdetto |
|
||||
|---|---|---|---|
|
||||
| 21 | XS01-OOS | il vantaggio del PROP-ALLOC poggia sul drift di XS01 misurato sulla sua finestra di scoperta: regge fuori? | _in corso_ |
|
||||
| 21 | XS01-OOS | il vantaggio del PROP-ALLOC poggia sul drift di XS01 misurato sulla sua finestra di scoperta: regge fuori? | **LEAD** — regge, ed e' PIU' GRANDE fuori |
|
||||
| 22 | MAKER | l'esecuzione passiva e' una fonte di ritorno, al netto del costo di non essere eseguiti? | **SCARTATO** — il segno dipende da `<` contro `<=` |
|
||||
| 23 | BOCPD | un rilevatore di cambio di regime vero batte il miglior lookback COSTANTE? | **SCARTATO** — filone chiuso definitivamente |
|
||||
| 24 | CRITICO | cosa NON e' stato misurato, e quale singola misura mancante vale di piu' | **3 correzioni all'ondata**, 1 regola mia ritirata |
|
||||
@@ -889,3 +889,86 @@ tre); il muro di XS01 con **tre** numeri; la leva riceve guida **opposta** da GR
|
||||
PROP-ALLOC; il peso di SKH01 e' **respinto da un agente e raddoppiato da un altro**. Piu' 6 modalita'
|
||||
non esplorate e 6 punti di autocritica — incluso che i **20 MB del dataset futures 2018-2026 (l'unico
|
||||
con la coda 2022) vivono solo in uno scratchpad di sessione**.
|
||||
|
||||
---
|
||||
|
||||
## 21 — XS01-OOS (XS01 fuori dalla sua finestra di scoperta) — **il risultato di testa della serata**
|
||||
|
||||
`scripts/research/r0822b_xs01_oos.py` (78-126 s, cache nello scratchpad, seconda corsa **offline**,
|
||||
numeri identici su due corse). **140 trial dichiarati** (contati al rialzo: le 6 varianti di config
|
||||
sono contate come trial anche se sono la *rivalidazione di una selezione passata*), piu' 1.200
|
||||
estrazioni di null che **non** sono trial.
|
||||
|
||||
**L'idea:** il gate del PROP-ALLOC diceva *"non aprire un funded prima che XS01 abbia una finestra
|
||||
fuori dal 2024-2026"* = un'attesa di anni. **Quella finestra esiste gia'** — gli stessi 19 ticker su
|
||||
**Binance spot USDT 1d, 2021-01 → 2023-12**, che contiene **LUNA e FTX**. Meccanismo **congelato**
|
||||
(blend [30,90], H=10, k=5, gate p30, vol-target 20%), nessuna griglia cercata.
|
||||
|
||||
⚠️ **L'obiezione ovvia — "Binance non e' la verita'" — e' stata misurata, non aggirata:** quella
|
||||
regola del progetto riguarda l'**ancoraggio di prezzo**, non un ranking cross-sezionale, e lo scarto
|
||||
USDT e' un **fattore comune** che sparisce nello z-score. Verificato: corr dei ritorni giornalieri per
|
||||
asset **≥0,9992** (min NEAR), |differenza| mediana **5,1 bps**, e soprattutto lo **stesso sleeve**
|
||||
calcolato sui due venue da' **corr 0,9991, dSharpe −0,025**. Replica dello sleeve ufficiale
|
||||
`max|diff| = 0,0`. Dato certificato: **0 gap, flat 0,00-0,71%, volumi zero 0,00%**, nessun run
|
||||
iniziale a volume 0 (il rivelatore del backfill sintetico, lezione AXS).
|
||||
|
||||
📌 **IL RISULTATO, e va nel verso opposto a quello temuto:**
|
||||
|
||||
| | Sharpe (**mediana di fase**) | banda 10 fasi | fasi >0 | maxDD | CAGR |
|
||||
|---|---|---|---|---|---|
|
||||
| **fuori campione 2021-2023 (U13)** | **+1,12** | [+0,74, +1,54] | **10/10** | **22,0%** | +26,1% |
|
||||
| finestra di scoperta 2024+ (U13) | +0,37 | — | — | 20,5% | +8,9% |
|
||||
| **fuori campione (U19-EXP)** | **+1,17** | [+0,91, +1,78] | **10/10** | 20,9% | +30,0% |
|
||||
| XS01 **ufficiale** (HL, 2024+) | +1,15 | [+0,46, +1,59] | — | **10,8%** | +29,0% |
|
||||
|
||||
**Differenza appaiata per fase (fuori campione − scoperta): +0,668, positiva in 10/10 fasi.**
|
||||
**Il fuori campione e' la finestra MIGLIORE, non peggiore.** Per anno: 2021 **+2,14** · 2022 +0,22 ·
|
||||
2023 +0,84 · 2024 −0,48 · 2025 +0,68 · 2026 +2,26.
|
||||
|
||||
**Sul LIBRO, solo fuori campione** (TP01+SKH01 75/25 + XS01, n=1076, dati che PROP-ALLOC non aveva mai
|
||||
visto): w=0 → Sh **+1,17** / DD 9,4% · w=0,25 → **+1,65 / 6,0%** · w=0,33 → +1,67 / 6,6% ·
|
||||
**w=0,50 (l'argmax di PROP-ALLOC) → +1,56 / 8,9%**. corr(XS01, libro) **−0,019**.
|
||||
|
||||
**Gate:** `causality` leak-free (randomizzando i prezzi dopo la decisione `max|diff| = 0`);
|
||||
`marginal_vs_tp01` **ADDS** su entrambi gli universi (corr +0,031 / −0,01, `is_hedge` **False**);
|
||||
banda di fase `gate_pass=True` **4/4**, e la fortuna d'ancora e' **piccola** (+0,05) contro il **+0,18**
|
||||
dello sleeve ufficiale; null di permutazione **a fee zero** p=0,013 (cross-sez.) e 0,017 (pesi
|
||||
casuali); sopravvive a **30 bps/lato** (+1,01). **`deflated_sharpe` FAIL 0,342** — e l'agente dichiara
|
||||
che il DSR penalizza una **mediana-di-fase** come se fosse un **max-di-k**, *"ma un gate fallito resta
|
||||
fallito"*. Null del de-levering **non pertinente e dichiarato**. Haircut a $600: **non e' il vincolo**
|
||||
(ticket mediano $76, **0,0%** sotto il min-order).
|
||||
|
||||
🚨 **Null "universo dove NON dovrebbe funzionare"** (11 settoriali SPDR, 1998+, meccanismo congelato):
|
||||
**−0,45, 0/10 fasi positive** → **terza conferma indipendente che il cross-sectional e'
|
||||
crypto-specifico** (dopo XSR01-equity 26/07 e STATARB-EQ 25/07).
|
||||
|
||||
🚨 **Il risultato che il progetto non si aspettava: i DUE AFFINAMENTI del 2026-06-19 NON si
|
||||
replicano.** Fuori campione il **blend [30,90] e' PEGGIO del solo L=30** (+1,12 vs +1,28) e il **gate
|
||||
di dispersione p30 vale ZERO** (+1,12 con e senza) — mentre *dentro* la finestra di scoperta il gate
|
||||
vale eccome (+0,37 contro −0,03). **Cio' che sopravvive e' il meccanismo cross-sectional NUDO.**
|
||||
⚠️ Non e' un argomento per cambiarli — cambiarli guardando *questo* risultato sarebbe la stessa
|
||||
selezione spostata di finestra — **e' un argomento per non attribuire loro il valore che la memoria
|
||||
del progetto gli attribuisce.**
|
||||
|
||||
⚠️ **Concentrazione misurata prima di crederci** (lezione SOL, stesso giorno): togliendo il **2021** il
|
||||
fuori campione scende **+1,17 → +0,57**; **leave-one-asset-out**: togliendo **LTC** la mediana di fase
|
||||
crolla **+1,12 → +0,20** (tutte e 13 restano >0) → **ampiezza effettiva bassa**, stessa diagnosi di
|
||||
STATARB-MULTI.
|
||||
|
||||
**VERDETTO: LEAD.** Non promuove niente; **riscrive il gate del PROP-ALLOC.**
|
||||
|
||||
📌 **`IL GATE DEL PROP-ALLOC SI PUO' CHIUDERE OGGI: PARZIALMENTE` — e non si chiude, si SOSTITUISCE.**
|
||||
L'attesa di anni **non serve**. Ma tre cose che il gate non chiedeva sono ora misurate:
|
||||
- **il rendimento sopravvive, il RISCHIO no**: maxDD standalone **10,8% → 20,9-22,0%** — e su un conto
|
||||
funded il vincolo binding e' la **regola di DD**, quindi la correzione va nel verso sbagliato;
|
||||
- **il peso 0,50 dell'argmax-J non sopravvive**: fuori campione a w=0,50 il maxDD di libro risale a
|
||||
**8,9%** contro **6,0%** a w=0,25, cioe' restituisce quasi tutta la protezione che e' l'unica ragione
|
||||
per metterlo su un conto a barriera → l'ottimo fuori campione e' **w ≈ 0,25-0,33**;
|
||||
- **32% dell'universo non e' testabile** (ARB/OP/SUI/APT/SEI/TIA non quotati nel 2021-23) e su di esso
|
||||
vive gran parte del numero di scoperta (**19 gambe 1,31 vs 13 gambe 0,54**, stessa finestra e stesso
|
||||
venue) → *cio' che e' stato validato e' una versione piu' DEBOLE di quella che sta nel libro.*
|
||||
|
||||
⚠️ **Il punto fragile che l'agente dichiara e non ha testato:** la validazione di venue gira **solo sul
|
||||
2024+**, cioe' proprio il periodo **senza depeg USDT**, mentre il fuori campione contiene il **depeg di
|
||||
maggio 2022**. Se i close Binance/USDT 2021-23 divergessero da un venue **USD** in modo *non comune fra
|
||||
gli asset*, il ranking cambierebbe e il risultato con esso.
|
||||
|
||||
@@ -370,7 +370,7 @@ def test_calendario(q: pd.DataFrame, dte_min: float, ogni: int = 4) -> dict:
|
||||
amp_pt = np.where(ok, iv_need - nxt_iv, np.nan)
|
||||
|
||||
room = amp_pt[ok] # <0 = margine, >0 = violazione
|
||||
res = {"room_med_pt": float(np.nanmedian(room)), "room_p5_pt": float(np.nanpercentile(room, 5)),
|
||||
res = {"room_med_pt": float(np.nanmedian(room)), "room_p5_pt": float(np.nanpercentile(room, 95)),
|
||||
"i_n": int(ok.sum()), "i_viol": int(viol_i.sum()),
|
||||
"i_quota": float(viol_i.sum() / max(1, ok.sum())),
|
||||
"i_amp_med_pt": float(np.nanmedian(amp_pt[viol_i])) if viol_i.any() else np.nan,
|
||||
@@ -473,7 +473,8 @@ def persistenza(bf: pd.DataFrame, viol_col: str) -> dict:
|
||||
return {"n": int(m.sum()), "resta": float(nxt[m].mean()) if m.any() else np.nan}
|
||||
|
||||
|
||||
def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict:
|
||||
def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float,
|
||||
solo_gia_strette: bool = False) -> dict:
|
||||
"""SE la famiglia USDC quotasse la STESSA superficie col SUO spread, sarebbe eseguibile?
|
||||
|
||||
Controfattuale DICHIARATO, non una misura: il collettore non raccoglie le USDC (difetto di
|
||||
@@ -484,6 +485,14 @@ def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict:
|
||||
"""
|
||||
x = bf[(bf["asset"] == asset)].dropna(subset=["mid", "mid_p", "mid_n"]).copy()
|
||||
x = x[np.isclose(x["lam"], 0.5, atol=0.02)]
|
||||
if solo_gia_strette:
|
||||
# il controfattuale ingenuo prende il MID di un mercato larghissimo e finge di poterlo
|
||||
# quotare al 2,6%: e' proprio dove il mid e' meno informativo. Qui si tiene solo la parte
|
||||
# di superficie GIA' quotata stretta quanto la famiglia USDC, dove il mid vale qualcosa.
|
||||
rs = np.maximum.reduce([((x["ask"] - x["bid"]) / x["mid"]).to_numpy(),
|
||||
((x["ask_p"] - x["bid_p"]) / x["mid_p"]).to_numpy(),
|
||||
((x["ask_n"] - x["bid_n"]) / x["mid_n"]).to_numpy()])
|
||||
x = x[rs <= rel_usdc]
|
||||
lot = MIN_LOT_USDC[asset]
|
||||
out = {}
|
||||
for nome, rel in (("inverse osservato", None), (f"{asset}_USDC controfattuale", rel_usdc)):
|
||||
@@ -499,8 +508,10 @@ def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict:
|
||||
f = (fee_coin(b.to_numpy(), 2 * l) + fee_coin(a_p.to_numpy(), l) + fee_coin(a_n.to_numpy(), l))
|
||||
netto = (cred - f) * x["spot_feed"].to_numpy()
|
||||
out[nome] = {"n": int(len(x)), "pos": int(np.nansum(netto > 0)),
|
||||
"max": float(np.nanmax(netto)), "p999": float(np.nanpercentile(netto, 99.9)),
|
||||
"lotto_usd": float(np.nanmedian(x["spot_feed"]) * l)}
|
||||
"pos1": int(np.nansum(netto > 1.0)), "somma": float(np.nansum(netto[netto > 0])),
|
||||
"max": float(np.nanmax(netto)) if len(x) else np.nan,
|
||||
"p999": float(np.nanpercentile(netto, 99.9)) if len(x) else np.nan,
|
||||
"lotto_usd": float(np.nanmedian(x["spot_feed"]) * l) if len(x) else np.nan}
|
||||
return out
|
||||
|
||||
|
||||
@@ -694,7 +705,7 @@ def main() -> None:
|
||||
print(f" valore mediano della violazione (i) al lotto minimo ${c['i_val_usd_med']:.2f} "
|
||||
f"contro un costo di 2 gambe (spread + fee) di ${c['i_costo_usd_med']:.2f}")
|
||||
print(f" margine di monotonia sull'intera superficie: la iv lunga sta {-c['room_med_pt']:.2f} pt-vol "
|
||||
f"SOPRA il minimo che la non-arbitraggio richiede (p5 {-c['room_p5_pt']:.2f}) "
|
||||
f"SOPRA il minimo che la non-arbitraggio richiede (5% piu' stretto: {-c['room_p5_pt']:.2f}) "
|
||||
f"-> la superficie non e' nemmeno VICINA al bordo")
|
||||
|
||||
print("\n" + "=" * 100)
|
||||
@@ -717,10 +728,22 @@ def main() -> None:
|
||||
for a, rel in (("BTC", 0.082), ("ETH", 0.026)):
|
||||
if a not in set(bf["asset"]):
|
||||
continue
|
||||
cf = controfattuale_usdc(bf, a, rel)
|
||||
for nome, r in cf.items():
|
||||
print(f" {a} {nome:<28}: lotto ${r['lotto_usd']:,.0f} · farfalle a credito netto>0 "
|
||||
f"{r['pos']:,}/{r['n']:,} · migliore ${r['max']:.2f} · p99.9 ${r['p999']:.2f}")
|
||||
for strette, et in ((False, "tutta la superficie"), (True, "solo quote gia' strette")):
|
||||
cf = controfattuale_usdc(bf, a, rel, solo_gia_strette=strette)
|
||||
for nome, r in cf.items():
|
||||
if strette and "inverse" in nome:
|
||||
continue
|
||||
print(f" {a} {nome:<26} [{et:<24}]: lotto ${r['lotto_usd']:,.0f} · "
|
||||
f"credito netto>0 {r['pos']:,}/{r['n']:,} · >$1 {r['pos1']:,} · "
|
||||
f"migliore ${r['max']:.2f} · somma ${r['somma']:,.0f}")
|
||||
# dove vive l'incoerenza: e' larga dove il mercato e' largo?
|
||||
x = bf[(bf["asset"] == a)].dropna(subset=["iv_resid", "relspread"])
|
||||
x = x[np.isfinite(x["relspread"])]
|
||||
dec = pd.qcut(x["relspread"], 5, labels=False, duplicates="drop")
|
||||
t = x.assign(d=dec).groupby("d").agg(spread=("relspread", "median"),
|
||||
resid=("iv_resid", lambda v: v.abs().median()))
|
||||
print(f" |residuo| per quintile di spread relativo {a}: " +
|
||||
" · ".join(f"{r.spread:.1%}->{r.resid:.3f}pt" for r in t.itertuples()))
|
||||
|
||||
print("\n QUANTO MANCA per essere eseguibile (near-miss, triple equispaziate):")
|
||||
for a in sorted(bf["asset"].unique()):
|
||||
|
||||
Reference in New Issue
Block a user