research(wave-0822): lo scettico RITIRA il difetto di contabilita' (non esiste, bit-exact) e chiude oggi il gate del 22/12 — il meccanismo e' un filtro di frequenza

This commit is contained in:
Adriano Dal Pastro
2026-08-22 17:43:33 +00:00
parent b6c0deb866
commit 1750b5a93b
5 changed files with 240 additions and 25 deletions
+43 -3
View File
@@ -14,7 +14,7 @@ null de-levering superato + eseguibilita' al capitale dichiarato.
| 11 | VRP-QUOTE-VERE | **SCARTATO** + **1 difetto di RACCOLTA in produzione** | il campione contiene l'**astensione** della strategia (0/19 settimane passano IV-rank>0.30) e il titolo non sopravvive all'ora d'ingresso; ma soprattutto: **`collect_chain.py` raccoglie la famiglia di contratti che il conto NON puo' marginare** |
| 7 | TERM-STRUCTURE | **SCARTATO** + 1 LEAD stretto (decisione 2027-02-01) | la pendenza e' un **termometro contemporaneo** (picco CCF a lag 1h/0h); il carry non paga il bid-ask (**18,6 giorni di roll-down per pagare UN round-trip**); come gate e' la **5ª occorrenza** della ridondanza col trend |
| 6 | OI-PIN | **SCARTATO** | il max-pain batte uno strike casuale ma **non batte mai (0/24) la media a 7 giorni dello spot**, un livello che non usa NESSUN dato di opzioni: non e' pinning, e' reversione verso il centro recente con l'OI come stimatore rumoroso di quel centro |
| 8 | VOL-SIZE | **LEAD** (gate 2026-12-22) + 1 falsificazione | dare a SKH01 una size per-trade regge a **23/23 ancore**, 8/8 anni, null di permutazione e trasferimento su V1 — ma vale **+0,07 di Sharpe di libro, un terzo della fortuna d'ancora del libro stesso (+0,196)**. Il vol-target di **libro** e' invece falsificato: compra peso SKH gia' respinto e peggiora l'eseguibilita' |
| 8 | VOL-SIZE | **LEAD RIDIMENSIONATO** gate 22/12 **gia' fallito oggi** | dare a SKH01 una size per-trade regge a **23/23 ancore**, 8/8 anni, null di permutazione e trasferimento su V1 — ma vale **+0,07 di Sharpe di libro, un terzo della fortuna d'ancora del libro stesso (+0,196)**. Il vol-target di **libro** e' invece falsificato: compra peso SKH gia' respinto e peggiora l'eseguibilita' |
| — | **XSR-REPRO** (integrita') | 🚨 **DIFETTO DI PRODUZIONE** | il numero 1.82 e' SPIEGATO e non era sbagliato (era su una **terza** lente, e su una barra non ancora chiusa) — ma cercandone la causa e' emerso che **`paper_xsr` registra ~41 minuti di mercato al giorno**, non un giorno. Tre gate pre-registrati leggono serie costruite cosi' |
| 5 | SKEW | **SCARTATO** (Q1, Q2) + **LEAD** (Q3, gate 2027-02-22) | il prezzo muove lo skew (t 3,1-9,4 su 8/8 test), **non il contrario** (max |t| in avanti 2,35 contro 2,08 atteso dal rumore). Ma Q3 e' grosso: **il f=0,73 di VRP01 e' per il 42% STRUTTURA A TERMINE e solo per il 25% skew** |
| — | **HL-EXEC** (audit di fatto) | **3 falsificazioni misurate** | il pavimento vero e' **$10 (non $5)** e il taker **4,50 bps (non 5,0)** — ma il *"XS01 serve ~$20k"* e' **refutato del tutto** (nessuna soglia da min-order), il *"XSR01 ~$5.000"* e' **conservativo di 1,7x** (vero ~$3.000), e lo **slippage "rischio #1" di XSR01 e' refutato** con margine **21x** |
@@ -252,13 +252,15 @@ recente, e l'OI e' solo uno stimatore rumoroso di quel centro.
### 8 — VOL-SIZE (r0822_vol_size.py, 28 trial + 23 ancore)
✅ Sanity **bit-exact**: ricomposizione trade-by-trade `max|diff| = 0.0` contro `run_asset` e contro
`sleeves._skyhook_returns()`.
🚨 **DIFETTO DI METODO GENERALE, trovato dall'agente nella propria prima stesura e MISURATO invece
**RITIRATO DALLO SCETTICO (vedi in fondo): il difetto qui sotto NON ESISTE su questo codice.**
Lasciato a verbale perche' la regola era stata pubblicata e va vista insieme alla sua smentita.
~~🚨 **DIFETTO DI METODO GENERALE, trovato dall'agente nella propria prima stesura e MISURATO invece
che cancellato: su uno sleeve a equity a GRADINO, un overlay giornaliero e' NON CAUSALE.** Tutto il
P&L di un trade SKH01 e' contabilizzato il giorno di **CHIUSURA**, fino a **3,83 giorni dopo
l'ingresso**; moltiplicare la serie giornaliera per `L_t` (che usa solo dati <= t1) **passa
qualunque controllo di causalita' scritto sulla serie giornaliera** — il look-ahead sta nella
CONTABILITA', non nella formula. Costo misurato: **+0,04 di Sharpe hold-out fantasma**.
**REGOLA: un overlay si applica alla data d'INGRESSO, non a quella in cui il P&L viene contabilizzato.**
**REGOLA: un overlay si applica alla data d'INGRESSO, non a quella in cui il P&L viene contabilizzato.**~~ **<- RITIRATA, vedi sotto**
🚨 **REGOLA 2 — il null del de-levering e' DEGENERE quando la variante e' una ri-scalatura**
(`sh(k*base) === sh(base)`): il test che serve non e' sul DD ma **ISO-PESO sullo Sharpe**, altrimenti
si sta solo misurando di quanto e' cambiato il peso di uno sleeve. Qui era decisivo: quasi ogni
@@ -545,3 +547,41 @@ dall'ora scelta = **un'altra strategia, non un'ancora**; (c) **un gate che stamp
verificato prima di dichiararlo "non girato"** — stampava le chiavi sbagliate.
✅ Disciplina di rete rispettata: <=2 req/s e **sospensione automatica nella finestra :25-:29** di
`cron_chain` (verificato: si e' sospeso da solo per 264s).
### SCETTICO su VOL-SIZE (r0822_volsize_skeptic.py, 43 valutazioni x 23 ancore)
✅ Replica **bit-exact** prima di ogni attacco: gamba 50/50 vs `sleeves._skyhook_returns()`
**max|diff| = 0,0**; e il titolo riprodotto **esattamente** (+0,070 ISO dShFULL 23/23, +0,101 HOLD).
🚨 **RITIRA UNA REGOLA PUBBLICATA POCHE ORE PRIMA — il difetto di contabilita' NON ESISTE.**
`backtest_signals` fa `equity[i_ent:i_ex+1] = capital`: il P&L e' contabilizzato al giorno
d'**INGRESSO** in **291/293 (99,3%)** dei trade multi-giorno, all'uscita in **4**. Troncando i dati
alla barra d'ingresso e ricalcolando le size da zero: **50/50 identiche, max|diff| = 0,0**. E la
"riparazione" e' un **no-op bit-exact** (max|diff| 6,7e-16, Sharpe identico a 4 decimali).
**Il "+0,04 fantasma" era interamente `[L sulla gamba COMBINATA] [L PER ASSET]`** — due varianti
entrambe causali — **e la variante scartata come "non causale" era la MIGLIORE sull'hold-out
(+0,039)**. *Il costo di una regola derivata da un difetto inesistente e' gia' stato pagato.*
📌 **REGOLA CORRETTA (piu' debole e vera):** *"applicare un overlay alla data d'ingresso"* resta un
buon consiglio **generale**, ma su questo motore le due date **coincidono** — e va verificato con un
troncamento bit-exact **prima** di scartare un ramo, non dedotto dalla forma dell'equity.
🚨 **IL GATE PRE-REGISTRATO AL 22/12/2026 E' GIA' FALLITO OGGI, e nel modo piu' netto.** L'agente
aveva scritto: *"se l'effetto e' UGUALE su V1, la spiegazione e' sbagliata anche se il numero
regge"*. Misurato: |effetto| mediano di famiglia **V1 0,067 contro V2-DD 0,032** — l'inverse-vol
funziona **il DOPPIO dove lo stop e' gia' in ATR**, cioe' esattamente al contrario della previsione,
in **8 celle su 8** con **23/23 ancore positive**. E il test **ha potenza** (su V2-DD la famiglia
arriva a +0,067, quanto il LEAD).
📌 **La spiegazione avversaria e' CONFERMATA e la sua forma minima e' un binario.** `BIN L>=med`
(size **0/1** alla mediana espandente, nessuna magnitudine, 194/406 trade scartati) vale
**+0,080 FULL (20/23) = 115% del LEAD**. Controllo di segno superato: il binario **INVERSO** fa
**0,159 (0/23)**. Canali: **FREQUENZA +0,051 (23/23)** contro **MAGNITUDINE +0,003 (17/23)**.
I trade tenuti hanno net medio **2,03% contro 0,36%** degli scartati.
**Un attacco dello scettico e' FALLITO, e lo dichiara:** l'asse della finestra **non e' un picco**
— l'argmax in-sample-only cade su **w=90 = la cella pubblicata**, corr(IS, HOLD) fra le 9 finestre
**+0,63**, plateau da 45 a 270. Sotto w=45 la finestra contiene **1-3 barre attive** (colonna
misurata) e il segno si rovescia: non e' fragilita', e' campione vuoto.
**-> quindi anche la mia nota "picco, non plateau" nel filone 8 va letta come SUPERATA.**
Stima onesta se `w` e' scelto (mediana di famiglia): **+0,025 FULL**.
📌 **CONSEGUENZA:** il LEAD non ha piu' una condizione aperta da attendere — la sua gamba
esplicativa e' chiusa oggi. Se qualcuno vorra' portarlo avanti, **il candidato giusto non e' la
formula ma il binario**: meno gradi di liberta', **dimezza gli ordini** (migliora l'eseguibilita' a
$635), 115% sul FULL — ma paga in fragilita' d'ancora (20/23 contro 23/23, banda che include 0,061,
hold-out +0,072 contro +0,101). **Resta un terzo della fortuna d'ancora del libro stesso (+0,196):
niente qui giustifica di toccare pesi, cron o config.**
+88 -3
View File
@@ -356,9 +356,16 @@ def liquidita(rows: list[dict], tab: dict) -> None:
s = stat[f]
print(f"{f:<10} {s['oi']:>11} {rA[f]:>8} {s['puts']:>10} {s['two']:>10} "
f"{s['pct']:>9.0%} {rB[f]:>8}")
print("\n 📌 LA CLASSIFICA SI ROVESCIA. SOL_USDC e' 1° per open interest e ULTIMO per quote")
print(" presenti; BTC_USDC, dichiarato 'praticamente morto' (5 strumenti con OI>=100), ha")
print(" quasi tutti i put quotati a due lati. L'open interest misura posizioni ACCUMULATE,")
# la correlazione di rango si CALCOLA, non si racconta: sulle sole famiglie lineari,
# cioe' quelle su cui la domanda del filone e' stata posta.
a = np.array([rA[f] for f in LINEAR], float)
c = np.array([rB[f] for f in LINEAR], float)
rho = float(np.corrcoef(a, c)[0, 1])
print(f"\n 📌 LA CLASSIFICA SI ROVESCIA. Correlazione di rango fra i due criteri sulle sei")
print(f" famiglie lineari: {rho:+.2f}. SOL_USDC e' 1ª per open interest fra le lineari e")
print(" ULTIMA di tutte per quote presenti; BTC_USDC, dichiarato 'praticamente morto' (5")
print(" strumenti con OI>=100), ha quasi tutti i put quotati a due lati. L'open interest")
print(" misura posizioni ACCUMULATE,")
print(" non la possibilita' di negoziare ADESSO — e sull'universo lineare le due cose non")
print(" hanno quasi nulla a che vedere.")
print(" ⚠️ Ricaduta sul codice di produzione: `collect_chain.py` prefiltra a OI>=100. Sulle")
@@ -366,6 +373,28 @@ def liquidita(rows: list[dict], tab: dict) -> None:
print(" suo nome lo lascia credere. 4ª occorrenza di 'una riga presente non e' un dato")
print(" presente' — qui in versione: 'una posizione aperta non e' un prezzo negoziabile'.")
print("\n" + "-" * 104)
print("2b. E ALLORA DOV'E' LA LIQUIDITA' CHE SI VEDE NEL VOLUME? Nel mercato a BLOCCHI,")
print(" che ha un minimo d'ordine suo — e non e' raggiungibile con $635.")
print("-" * 104)
print(f"{'famiglia':<10} {'con volume 24h':>15} {'volume 24h $':>14} {'blocco min':>11} "
f"{'blocco min $':>13} {'x il conto':>11}")
for f in FAMS:
sub = [r for r in rows if r["fam"] == f]
if not sub:
continue
nv = sum(1 for r in sub if r["vol"] > 0)
vusd = sum(r["vol_usd"] for r in sub)
bm = sub[0]["blockmin"]
bm_usd = bm * tab[f]["S"] * (1.0 if not is_inv(f) else 1.0)
print(f"{f:<10} {nv:>15} {vusd:>14,.0f} {bm:>11g} {bm_usd:>13,.0f} "
f"{bm_usd/CAPITAL:>10.0f}x")
print("\n 📌 SOL_USDC ha molti piu' strumenti con volume nelle ultime 24h che quotati a due")
print(" lati: quelle opzioni si negoziano, ma via blocchi/RFQ. Il minimo di blocco e' un")
print(" fatto del venue, non un'opinione, e a $635 e' fuori portata di due ordini di")
print(" grandezza. **Non si conclude che il mercato alt non esista: si conclude che la parte")
print(" a cui questo conto ha accesso — il book continuo — e' quella sottile.**")
# ---------------------------------------------------------------- §3 la struttura
@@ -461,9 +490,65 @@ def struttura(rows: list[dict]) -> dict:
f"{len(gi):>12} {len(ge):>11}")
print(" Se la colonna `eseg.` si allontana dal target -0.28/-0.10, la struttura che si")
print(" comprerebbe NON e' quella richiesta: la griglia degli strike quotati la deforma.")
print("\n --- CONTROLLO POSITIVO: questo snapshot e' di SABATO. E' atipico? ---")
print(" Non posso aspettare una settimana, ma posso puntare lo stesso strumento su una")
print(" grandezza che il progetto ha GIA' misurato su 19 settimane di giorni feriali:")
print(" attraversare lo spread sul canonico BTC/ETH inverse costa ~10% del credito (30/07).")
for f in INVERSE:
if f in res:
print(f" {f:<4} inverse: costo di attraversamento oggi = "
f"{(1-res[f]['fv'])*100:5.1f}% del credito (atteso ~10%)")
print(" Se coincide, il sabato non sta degradando i book che il progetto conosce, e la")
print(" lettura sulle famiglie nuove non e' un artefatto del giorno. **E' un controllo")
print(" debole** (una grandezza sola, un istante), non una prova di tipicita'.")
robustezza_dte(rows)
return res
def robustezza_dte(rows: list[dict]) -> None:
"""L'UNICO grado di liberta' di questo file, esposto invece che nascosto.
VRP01 e' settimanale (tenor 7g, finestra 4-10). Qui la finestra e' stata allargata a 4-45
per dare piu' scadenze alle famiglie alt, che ne hanno poche. Allargare una finestra e'
una scelta, e una scelta va misurata: se la conclusione dipendesse da essa, sarebbe la
conclusione a essere stata scelta.
"""
global DTE_HI
print("\n --- ROBUSTEZZA: la finestra DTE e' il mio unico grado di liberta' ---")
print(f" {'famiglia':<10} {'finestra':>9} {'scad':>5} {'f_venue':>8} {'spr% corta':>11} "
f"{'cred eseg$':>11} {'maxloss$':>9}")
orig = DTE_HI
try:
for hi in (10.0, 45.0):
DTE_HI = hi
for f in FAMS:
gg = scegli(rows, f, eseguibile=True)
fv, ss, ce, ml = [], [], [], []
for g in gg:
s, lo = g["short"], g["long"]
cm = usd(s["mark"], s) - usd(lo["mark"], lo)
c = usd(s["bid"], s) - usd(lo["ask"], lo)
if cm <= 0:
continue
fv.append(c / cm); ss.append(rel_spread(s)); ce.append(c)
ml.append(loss_usd(s["K"], lo["K"], s) - c)
w = f"{DTE_LO:.0f}-{hi:.0f}g"
if not fv:
print(f" {f:<10} {w:>9} {len(gg):>5} {'n/d':>8}")
continue
print(f" {f:<10} {w:>9} {len(fv):>5} {np.median(fv):>8.3f} "
f"{np.nanmedian(ss)*100:>10.1f}% {np.median(ce):>11.2f} "
f"{np.median(ml):>9.2f}")
print()
finally:
DTE_HI = orig
print(" 📌 Da leggere sull'ORDINE, non sulla terza cifra: le quote si muovono fra un giro e")
print(" l'altro (su tre snapshot a pochi minuti ETH_USDC ha dato 0.947-0.983, le inverse")
print(" 0.847-0.942, le alt 0.79-0.87, SOL sempre negativo). Cio' che NON si muove e' la")
print(" graduatoria, e alla finestra VERA di VRP01 (4-10g) e' piu' netta, non meno.")
def profondita(rows: list[dict], fams: list[str], b: Budget) -> None:
print("\n" + "=" * 104)
print("3b. PROFONDITA' IN CIMA AL BOOK (public/get_order_book depth=5) sulle gambe scelte")
+47 -8
View File
@@ -129,8 +129,10 @@ def read_equity_csv(p: Path, col: int = 1) -> tuple[list[pd.Timestamp], np.ndarr
# della giornata e' finita nella serie": in radice di tempo il rapporto di VARIANZA fra le due
# serie e' la frazione di giornata misurata (ipotesi: random walk sull'intervallo troncato).
# --------------------------------------------------------------------------------------------
def compare(rec: np.ndarray, rep: np.ndarray) -> dict:
def compare(rec: np.ndarray, rep: np.ndarray, mask: np.ndarray | None = None) -> dict:
ok = np.isfinite(rec) & np.isfinite(rep)
if mask is not None:
ok &= mask
a, b = rec[ok], rep[ok]
n = len(a)
if n < 3:
@@ -145,18 +147,53 @@ def compare(rec: np.ndarray, rep: np.ndarray) -> dict:
sh_rep=float(b.mean() / b.std() * ANN) if b.std() > 0 else float("nan"))
def show(tag: str, c: dict, note: str = "") -> None:
def show(T: pd.DatetimeIndex, rec: np.ndarray, rep: np.ndarray, note: str = "") -> dict:
"""Stampa il confronto e ritorna la statistica sulla finestra PULITA (senza l'incidente)."""
c = compare(rec, rep)
fuori = ~((T >= FREEZE[0]) & (T <= FREEZE[1]))
cf = compare(rec, rep, mask=fuori)
print(f" barre appaiate {c['n']} identiche entro 1e-6: {c['ident']}/{c['n']} "
f"corr {c['corr']:+.4f}")
print(f" vol annualizzata REGISTRATA {c['sd_rec']:6.2f}% RICALCOLATA {c['sd_rep']:6.2f}%"
f" rapporto di varianza {c['vr']:.4f}")
print(f" -> il monitor misura ~{c['minuti']:,.0f} minuti di mercato al giorno (su 1440){note}")
if cf["n"] != c["n"]:
print(f" ESCLUSO l'incidente del feed 09-15/07 ({c['n'] - cf['n']} barre recuperate a "
f"posteriori, quindi CHIUSE):")
print(f" barre {cf['n']} identiche {cf['ident']}/{cf['n']} corr {cf['corr']:+.4f}"
f" vol reg. {cf['sd_rec']:.2f}% vs ric. {cf['sd_rep']:.2f}%"
f" -> ~{cf['minuti']:,.0f} min/giorno")
print(f" Sharpe della finestra: REGISTRATO {c['sh_rec']:+.2f} RICALCOLATO {c['sh_rep']:+.2f}"
f" [SE ~{np.sqrt(365.0 / max(c['n'], 1)):.2f}: NON e' una lettura di gate]")
return dict(all=c, clean=cf)
RES: dict[str, dict] = {} # riepilogo per la tabella finale
# --------------------------------------------------------------------------------------------
# L'INCIDENTE DEL FEED (2026-07-09 -> 2026-07-15). Non e' un'assunzione: e' documentato nel
# sorgente di produzione (`rebuild_history.py`, commento "incidente 2026-07-15: feed fermo 7g" —
# `shutil.copy2` del backup falliva con EPERM e ABORTIVA il rebuild) ed e' verificabile nel log
# del cron. Conta qui per una ragione precisa: durante il congelamento i monitor NON trovavano
# barre nuove, e al ripristino hanno recuperato in un colpo solo barre gia' CHIUSE — cioe' le
# uniche barre corrette delle loro serie. Vanno escluse da ogni stima del difetto, o si finisce
# per attribuire al difetto un'attenuazione che viene da un guasto diverso.
FREEZE = (pd.Timestamp("2026-07-09", tz="UTC"), pd.Timestamp("2026-07-16", tz="UTC"))
CRONLOG = ROOT / "logs" / "cron_daily.log"
def freeze_evidence() -> dict[str, int]:
"""Giorni in cui `rebuild_history` e' ABORTITO nel cron (letto dal log, non assunto)."""
out: dict[str, int] = {}
day = None
lines = CRONLOG.read_text(errors="replace").splitlines() if CRONLOG.exists() else []
for i, ln in enumerate(lines):
if ln.startswith("===== 2026-") and "cron_daily" in ln:
day = ln.split()[1][:10]
if day and "rebuild_history.py" in ln and "Traceback" in "".join(lines[max(0, i - 3):i]):
out[day] = out.get(day, 0) + 1
return out
# ============================================================================================
# A — PROVENIENZA: quanto e' completa l'ultima riga di ogni sorgente
@@ -245,12 +282,14 @@ def b_xsr():
reals={n: px._book(c, len(st["syms"])) for c, n in px.REAL_BOOKS})
px.advance(st0)
rep = {int(r["ts"]): float(r["net_modeled"]) for r in sink.of("returns.jsonl")}
pairs = [(float(r["net_modeled"]), rep[int(r["ts"])]) for r in rec if int(r["ts"]) in rep]
c = compare(np.array([p[0] for p in pairs]), np.array([p[1] for p in pairs]))
print(f"\n pannello: `build_panel` -> `load_hl` -> parquet HL 1d (ultima riga = giorno in corso)")
show("xsr", c)
RES["paper_xsr"] = dict(parziale="SI", cmp=c, gate="XSR01 2026-10-23",
src="hl_*_1d.parquet", cad_h=24.0)
pairs = [(int(r["ts"]), float(r["net_modeled"]), rep[int(r["ts"])])
for r in rec if int(r["ts"]) in rep]
T = pd.DatetimeIndex(pd.to_datetime([p[0] for p in pairs], unit="ms", utc=True))
print("\n pannello: `build_panel` -> `load_hl` -> parquet HL 1d (ultima riga = giorno in corso)")
print(f" inception {T[0].date()} = DOPO l'incidente del feed: la finestra e' gia' pulita.")
out = show(T, np.array([p[1] for p in pairs]), np.array([p[2] for p in pairs]))
RES["paper_xsr"] = dict(parziale="SI", cmp=out["clean"], gate="XSR01 2026-10-23",
src="hl_*_1d.parquet", cad_h=24.0, ric="SI (bit-exact)")
def b_statarb():
+58 -8
View File
@@ -186,10 +186,41 @@ def _d(s: pd.Series) -> pd.Series:
_CRY_CACHE: dict = {}
def _crypto(w_tp: float, w_skh: float) -> pd.DataFrame:
key = (round(w_tp, 6), round(w_skh, 6))
def _crypto_stressed(w_tp: float, w_skh: float, m_tp: float, m_skh: float) -> pd.DataFrame:
"""Come `pc.crypto_daily_tuples` (minimo ESATTO sul path orario condiviso) ma con un taglio
del DRIFT applicato alla singola gamba PRIMA di comporre il path. Serve perche' il minimo
giornaliero non e' separabile per gamba: non si puo' tagliare il drift a valle."""
(J,) = pc._hourly_legs()
legs = {}
for nm, (rc, wc, mult) in {"tp": ("tp_r", "tp_w", m_tp), "sk": ("skh_r", "skh_w", m_skh)}.items():
r = J[rc].values.copy()
w = J[wc].values.copy()
if mult != 1.0:
cut = (1.0 - mult) * r.mean()
r = r - cut
w = w - cut
legs[nm] = (r, w)
r_h = w_tp * legs["tp"][0] + w_skh * legs["sk"][0]
w_h = w_tp * legs["tp"][1] + w_skh * legs["sk"][1]
idx = J.index
days = idx.floor("D")
rows = []
for day, pos in pd.Series(np.arange(len(idx)), index=days).groupby(level=0):
sl = pos.values
r = r_h[sl]
wk = w_h[sl]
cum = np.cumprod(1.0 + r)
cum_prev = np.concatenate(([1.0], cum[:-1]))
m = min(cum.min(), (cum_prev * (1.0 + wk)).min()) - 1.0
rows.append((day, cum[-1] - 1.0, min(m, cum[-1] - 1.0)))
return pd.DataFrame(rows, columns=["day", "R", "m"]).set_index("day")
def _crypto(w_tp: float, w_skh: float, m_tp: float = 1.0, m_skh: float = 1.0) -> pd.DataFrame:
key = (round(w_tp, 6), round(w_skh, 6), round(m_tp, 4), round(m_skh, 4))
if key not in _CRY_CACHE:
_CRY_CACHE[key] = pc.crypto_daily_tuples(w_tp, w_skh)
_CRY_CACHE[key] = (pc.crypto_daily_tuples(w_tp, w_skh) if (m_tp == 1.0 and m_skh == 1.0)
else _crypto_stressed(w_tp, w_skh, m_tp, m_skh))
return _CRY_CACHE[key]
@@ -221,11 +252,8 @@ def book_RG(w: dict, start: str = START_WIN,
R = np.zeros(len(idx))
M = np.zeros(len(idx))
if w_tp > 0 or w_skh > 0:
c = _crypto(w_tp, w_skh).reindex(idx)
cr = c["R"].values
if w_tp > 0 and dm.get("TP01", 1.0) != 1.0 or w_skh > 0 and dm.get("SKH01", 1.0) != 1.0:
raise NotImplementedError("stress sul drift crypto: il minimo e' path-esatto, non separabile")
R += cr
c = _crypto(w_tp, w_skh, dm.get("TP01", 1.0), dm.get("SKH01", 1.0)).reindex(idx)
R += c["R"].values
M += c["m"].values
for nm, fr in (("XS01", pc.xsec_daily_tuples()), ("VRP01", vrp_daily_tuples()),
("GTAA01", gtaa_daily_tuples())):
@@ -702,6 +730,28 @@ def main() -> None:
print(" scoperta; se resta a drift 0, il vantaggio e' pura DECORRELAZIONE e non dipende da")
print(" quell'ipotesi. Il numero, non la narrazione, dice quale delle due.")
hr("E-quater. LA DISTORSIONE CHE MI RIGUARDA — l'ottimo si appoggia a SKH01, e SKH01 e' lo")
print(" sleeve con la FORTUNA D'ANCORA piu' grande da restituire. Il LOO del 26/07: l'ancora")
print(" canonica gli regala 2/3 del contributo FULL (+0.43 -> +0.14) e il 70% dell'hold-out;")
print(" l'audit 02/07 lo trova al 93-98° pctl di 23 offset, 'spike, non plateau'. Questa lente")
print(" gira sull'ancora CANONICA e la banda non e' ri-misurabile qui -> il mio ottimo e'")
print(" distorto A FAVORE di SKH01 e va detto PRIMA di leggerlo. Taglio del drift di SKH01:")
print(f"\n {'drift SKH01':>13} {'config':>16} {'leva':>6} {'J':>7} {'J del LIVE':>11} "
f"{'delta appaiato':>16}")
for mult in (1.00, 0.66, 0.50, 0.33):
dmt = (("SKH01", mult),)
rows = [(w, lv, objective(w, lv, "HYRO", ev_idx, fu_idx, drift_mult=dmt))
for w in top_w for lv in LEVS]
w_, lv_, o_ = max(rows, key=lambda x: x[2]["J"])
lvL, oL = max(((lv, objective({"TP01": 0.75, "SKH01": 0.25}, lv, "HYRO", ev_idx, fu_idx,
drift_mult=dmt)) for lv in LEVS), key=lambda x: x[1]["J"])
dd_, sd_ = paired_delta(w_, lv_, {"TP01": 0.75, "SKH01": 0.25}, lvL, "HYRO",
ev_idx, fu_idx)
print(f" {mult:>13.2f} {wkey(w_):>16} {lv_:>6.3f} {o_['J']:>7.3f} {oL['J']:>11.3f} "
f"{dd_:>+11.3f} +-{sd_:.3f}")
print(" NB: il taglio si applica anche al LIVE 75/25 (che ha SKH01 al 25%), quindi il delta e'")
print(" un confronto onesto: entrambe le colonne perdono, la domanda e' quale perde di piu'.")
hr("E-ter. IL BUCO CHE LA FINESTRA NON PUO' CHIUDERE — 2024-2026 non contiene un CRASH")
print(" L'ottimizzazione toglie peso a TP01 (Sharpe 0.79 su questa finestra). Ma TP01 e' lo")
print(" sleeve DIFENSIVO: il LOO del 26/07 misura il suo contributo hold-out NEGATIVO nel 99.1%")
+4 -3
View File
@@ -252,9 +252,10 @@ def main() -> None:
FUN_V1["VTL tv20 w90 (LEAD)"] = size_vtl(FROZEN)
FUN_V1["BIN L>=1 (0/1)"] = size_bin(FROZEN, "ge1")
NTRIAL = len(FUN_V2) + len(FUN_V1) + 2 # +2 baseline
print(f"\n GRIGLIA DICHIARATA: {len(FUN_V2)} celle su V2-DD + {len(FUN_V1)} su V1 "
f"+ 2 baseline = {NTRIAL} valutazioni x {len(offs)} ancore. Nessuna cella scelta")
NTRIAL = len(FUN_V2) + len(FUN_V1) + 2 + 4 # +2 baseline, +4 controlli 'L combinata' (§1c)
print(f"\n GRIGLIA DICHIARATA (contata al RIALZO): {len(FUN_V2)} celle su V2-DD + "
f"{len(FUN_V1)} su V1 + 4 controlli 'L combinata' (§1c) + 2 baseline = {NTRIAL} "
f"valutazioni x {len(offs)} ancore. Nessuna cella scelta")
print(" dopo aver guardato: IV e' la famiglia PRE-REGISTRATA, VTL/w e' l'asse dichiarato")
print(" fragile dall'agente, BIN/DROP sono controlli miei dichiarati sopra.")