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 |
|
| # | 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 `<=` |
|
| 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 |
|
| 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 |
|
| 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'
|
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
|
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**.
|
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)
|
amp_pt = np.where(ok, iv_need - nxt_iv, np.nan)
|
||||||
|
|
||||||
room = amp_pt[ok] # <0 = margine, >0 = violazione
|
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_n": int(ok.sum()), "i_viol": int(viol_i.sum()),
|
||||||
"i_quota": float(viol_i.sum() / max(1, ok.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,
|
"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}
|
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?
|
"""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
|
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 = bf[(bf["asset"] == asset)].dropna(subset=["mid", "mid_p", "mid_n"]).copy()
|
||||||
x = x[np.isclose(x["lam"], 0.5, atol=0.02)]
|
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]
|
lot = MIN_LOT_USDC[asset]
|
||||||
out = {}
|
out = {}
|
||||||
for nome, rel in (("inverse osservato", None), (f"{asset}_USDC controfattuale", rel_usdc)):
|
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))
|
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()
|
netto = (cred - f) * x["spot_feed"].to_numpy()
|
||||||
out[nome] = {"n": int(len(x)), "pos": int(np.nansum(netto > 0)),
|
out[nome] = {"n": int(len(x)), "pos": int(np.nansum(netto > 0)),
|
||||||
"max": float(np.nanmax(netto)), "p999": float(np.nanpercentile(netto, 99.9)),
|
"pos1": int(np.nansum(netto > 1.0)), "somma": float(np.nansum(netto[netto > 0])),
|
||||||
"lotto_usd": float(np.nanmedian(x["spot_feed"]) * l)}
|
"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
|
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} "
|
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}")
|
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 "
|
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")
|
f"-> la superficie non e' nemmeno VICINA al bordo")
|
||||||
|
|
||||||
print("\n" + "=" * 100)
|
print("\n" + "=" * 100)
|
||||||
@@ -717,10 +728,22 @@ def main() -> None:
|
|||||||
for a, rel in (("BTC", 0.082), ("ETH", 0.026)):
|
for a, rel in (("BTC", 0.082), ("ETH", 0.026)):
|
||||||
if a not in set(bf["asset"]):
|
if a not in set(bf["asset"]):
|
||||||
continue
|
continue
|
||||||
cf = controfattuale_usdc(bf, a, rel)
|
for strette, et in ((False, "tutta la superficie"), (True, "solo quote gia' strette")):
|
||||||
for nome, r in cf.items():
|
cf = controfattuale_usdc(bf, a, rel, solo_gia_strette=strette)
|
||||||
print(f" {a} {nome:<28}: lotto ${r['lotto_usd']:,.0f} · farfalle a credito netto>0 "
|
for nome, r in cf.items():
|
||||||
f"{r['pos']:,}/{r['n']:,} · migliore ${r['max']:.2f} · p99.9 ${r['p999']:.2f}")
|
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):")
|
print("\n QUANTO MANCA per essere eseguibile (near-miss, triple equispaziate):")
|
||||||
for a in sorted(bf["asset"].unique()):
|
for a in sorted(bf["asset"].unique()):
|
||||||
|
|||||||
Reference in New Issue
Block a user