ricerca: il piano a 10 anni dal conto vero, l'ETF-quando-flat SCARTATO, slippage rifatto
Giornata partita da "stato trades" e finita sul disegno del sistema. Versamento di 1.400 USDC atterrato alle 11:03:35Z (equity $667,88 -> $2.066,88); il giro delle 11:47 ha ribilanciato correttamente e ha ripiazzato i disaster-SL alla taglia nuova -- prima volta che il ramo riparato stamattina gira sul serio. (1) PIANO A 10 ANNI DAL CONTO VERO -- r0825_piano_10a_500.py (nuovo) Le tabelle pubblicate partono da $600/$635: rifatte su $2.067, lente L3 CONGIUNTA. Replica superata: chiede EUR 1.718/m dove la tabella da $635 chiedeva EUR 1.733. EUR 500/mese per 10 anni -> mediana $114.934, rendita 18,35 EUR/g, P(>=50 EUR/g) = 0,0% su 3.000 traiettorie. Il bersaglio con EUR 500/m arriva al 17o anno. L'EQUIVALENZA CHE ORDINA IL PIANO: EUR 100/mese in piu' == +4,07%/anno di drift, cioe' +27% su TUTTO il drift del libro. A 10 anni i bonifici fanno il 59%. Tutta la leva autorizzabile vale quanto EUR 100-150/mese: k=1,25 (+15,6%) vale MENO di EUR 100/mese in piu' (+19,1%), e si porta dietro il peggior giorno al 21,48%. E il libro a k=1 rende MENO dell'S&P (15,19% vs 17,40%): il vantaggio sta nello Sharpe (1,35 vs 0,89) e senza leva NON si converte in rendimento. Senza leva il libro non si giustifica come veicolo di ACCUMULO -- si giustifica come veicolo di RENDITA, dove serve 2,5x meno capitale ($254k contro $646k) perche' la perpetua vive sul DD. (2) "ETF QUANDO IL LIBRO E' FLAT" -- SCARTATO, r0825_capitale_fermo.py (nuovo) Il libro e' flat il 28,0% dei giorni (2,7% nel 2021, 77,0% nel 2022, 75,6% nel 2026); live, esposto 14 giorni su 64, leva lorda mediana 0,00x e max 0,52x su un tetto di 1,0x -- il cap non ha mai morso, il vincolo e' il segnale. A ISO-RISCHIO il dinamico PERDE: Sharpe 1,01 contro 1,40 del 50/50 e 1,35 del libro. E il null a maschera casuale (400 estrazioni, stessa quota, blocchi 20g) lo mette al 30o percentile: fa PEGGIO di commutare a caso. A iso-nozionale sembrava vincere (drift 17,09%, il piu' alto) perche' aveva la vol piu' alta: e' la trappola di M6, il de-levering e' il PRIMO test. E IL MECCANISMO CHE SEMBRAVA OVVIO NON ESISTE. Aggregato: SPY 4,91% nei giorni flat contro 21,48% negli altri (-16,57%), e la storia si scrive da sola. FALSA: scomposta per anno il segno ALTERNA (3 su, 4 giu') e il 2022 -- l'anno che doveva reggerla -- ha il segno OPPOSTO. Artefatto di composizione: i giorni flat stanno negli ANNI brutti per l'azionario, non nei GIORNI brutti. M9 ha fatto il suo lavoro su di me. (3) SLIPPAGE RIFATTO ALLA TAGLIA VERA -- r0822_slip_audit.py riparato 26 fill (erano 18), $6-$319. I due piu' grandi mai eseguiti sono di oggi e hanno preso 1,01% e 0,54% della loro barra 5m, con il print a meta' del range (q=0,50). Il caso peggiore resta un fill da $74 del 18/07 al 21,9%: un sabato, nastro inesistente. L'attrito non e' funzione della TAGLIA ma di taglia/volume-della-barra -- l'estrapolazione lineare che prevedeva ~71% a questo capitale e' refutata dalla misura diretta. NB non e' "assente", e' "non ancora testato": manca un fill grande in una barra sottile, e il weekend e' dove TP01 fa il 38% del proprio gross. DIFETTO P1 RIPARATO: l'equity era CABLATA a 636.0 con un commento che diceva di leggerla dal watermark. Il giorno del versamento avrebbe stampato $636 sbagliando di 3,3x proprio la sezione che esiste per dire QUANDO la misura scade. Riparata leggendo il watermark -- e poi riparata di nuovo, perche' i fill del campione sono stati eseguiti a conti diversi ($597-$2.067) e una base sola e' sbagliata comunque la si scelga. Ora la normalizzazione e' PER FILL, e a $600 riproduce il 22,0% originale. DUE ERRORI MIEI, CATTURATI PRIMA DI PUBBLICARLI - maschera VUOTA vestita da risultato: la prima stesura di r0825_capitale_fermo prendeva i giorni flat dalla serie DE-LUCKATA, ma `deluck` sottrae una costante e lo zero esatto sparisce -> maschera vuota, dinamico identico al book, e lo script ha stampato tabelle piene e un p-value. Lo ha rivelato solo la riga "0 = 0.0%". REGOLA: stampare la CARDINALITA' di una maschera prima di usarla. - il meccanismo falso della (2), demolito dalla scomposizione per anno. Suite: 730 passati, 1 fallito -- quello gia' noto di 5.9 (deriva dati, non codice). Watermark del libro live sopravvissuto intatto alla suite (fixture autouse di stamattina). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
This commit is contained in:
@@ -212,6 +212,54 @@ def _containing(bars: pd.DataFrame, t) -> pd.Series | None:
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------- stats
|
||||
def _equity_per_fill(ts_list) -> "pd.Series":
|
||||
"""Equity del conto AL MOMENTO di ciascun fill, dal libro di bordo (`data/live/trades.db`).
|
||||
|
||||
⚠️ PERCHE' SERVE (2026-08-25). La sezione SCALA divideva TUTTE le partecipazioni per UNA
|
||||
equity sola. Andava bene finche' il conto stava fermo a ~$636; il giorno del versamento
|
||||
($667 -> $2.067) qualunque scelta di quel singolo numero diventa sbagliata, perche' i fill
|
||||
del campione sono stati eseguiti a taglie di conto DIVERSE. Il caso peggiore osservato
|
||||
(22% di una barra 5m) e' un fill da $74 fatto a ~$650: normalizzarlo sull'equity di oggi lo
|
||||
sottostima di 3,2x, sull'equity di allora sovrastima gli altri.
|
||||
La grandezza invariante e' **partecipazione per dollaro di equity**, e si ottiene solo
|
||||
appaiando ogni fill alla SUA equity.
|
||||
"""
|
||||
import sqlite3
|
||||
import pandas as _pd
|
||||
f = ROOT / "data" / "live" / "trades.db"
|
||||
ts = _pd.DatetimeIndex(_pd.to_datetime(list(ts_list), utc=True))
|
||||
try:
|
||||
with sqlite3.connect(f"file:{f}?mode=ro", uri=True) as con:
|
||||
e = _pd.read_sql("SELECT ts_utc, equity FROM equity ORDER BY ts_utc", con)
|
||||
e["ts_utc"] = _pd.to_datetime(e["ts_utc"], utc=True)
|
||||
e = e.dropna().set_index("ts_utc")["equity"].sort_index()
|
||||
# BACKWARD: l'equity in vigore quando il fill e' avvenuto, mai una lettura futura.
|
||||
out = _pd.Series(e.reindex(ts, method="ffill").values, index=ts)
|
||||
if out.notna().any():
|
||||
return out
|
||||
except Exception as ex: # noqa: BLE001
|
||||
print(f" ⚠️ equity per fill non leggibile ({type(ex).__name__}): "
|
||||
f"ricado sull'equity unica")
|
||||
return _pd.Series([float("nan")] * len(ts), index=ts)
|
||||
|
||||
|
||||
def _equity_osservata(default: float = 636.0) -> float:
|
||||
"""L'equity reale dal watermark del libro live — DERIVATA, non ridichiarata (P1).
|
||||
|
||||
Se il file non c'e' o non e' leggibile si ricade sul valore storico e **lo si dice**: un
|
||||
default silenzioso qui rimetterebbe esattamente il difetto che questa funzione ripara.
|
||||
"""
|
||||
import json
|
||||
f = ROOT / "data" / "live" / "equity_seen.json"
|
||||
try:
|
||||
v = float(json.loads(f.read_text())["real_equity"])
|
||||
if v > 0:
|
||||
return v
|
||||
except Exception as e: # noqa: BLE001
|
||||
print(f" ⚠️ watermark non leggibile ({type(e).__name__}): uso il default ${default:,.0f}")
|
||||
return default
|
||||
|
||||
|
||||
def ci_mean(x, boot=True):
|
||||
x = np.asarray(x, float)
|
||||
x = x[np.isfinite(x)]
|
||||
@@ -515,18 +563,34 @@ def main():
|
||||
|
||||
# -- COSA CAMBIA CON IL CAPITALE: la partecipazione scala LINEARE, il conto no.
|
||||
print(f"\n SCALA — l'unica parte di questa misura che ha una data di scadenza:")
|
||||
eq = 636.0 # equity reale osservata (data/live/equity_seen.json)
|
||||
print(f" la partecipazione scala lineare col capitale (equity oggi ~${eq:.0f}).")
|
||||
# ⚠️ 2026-08-25: era CABLATA a 636.0 con un commento che diceva "equity reale osservata
|
||||
# (data/live/equity_seen.json)" — cioe' DICHIARAVA la fonte senza leggerla. E' P1: un
|
||||
# misuratore deve DERIVARE il proprio bersaglio, mai ridichiararlo. Il giorno del versamento
|
||||
# (equity $667 -> $2.067) la riga avrebbe continuato a stampare $636 e l'intera colonna
|
||||
# "scala" sarebbe stata sbagliata di 3,3x senza che nulla lo segnalasse — proprio nella
|
||||
# sezione che esiste per dire quando la misura scade.
|
||||
eq = _equity_osservata()
|
||||
eq_fill = _equity_per_fill(R.loc[pr.index, "fill_ts"])
|
||||
if eq_fill.notna().all():
|
||||
# partecipazione PER DOLLARO di equity: invariante alla taglia del conto al momento
|
||||
ppd = pr.values / eq_fill.values
|
||||
base = "per-fill (ogni fill sulla SUA equity)"
|
||||
else:
|
||||
ppd = pr.values / eq
|
||||
base = f"equity unica ${eq:,.0f} — RIPIEGO, i fill hanno taglie di conto diverse"
|
||||
print(f" la partecipazione scala lineare col capitale. Normalizzazione: {base}.")
|
||||
print(f" equity al momento dei fill: ${np.nanmin(eq_fill):,.0f} - ${np.nanmax(eq_fill):,.0f}"
|
||||
if eq_fill.notna().any() else "")
|
||||
print(f" {'capitale':>10} {'partecip. mediana':>19} {'p90':>9} {'max osservato':>15}")
|
||||
for cap in (600, 5_000, 20_000, 100_000, 272_000):
|
||||
k = cap / eq
|
||||
mx = pr.max() * k * 100
|
||||
print(f" {'$' + format(cap, ','):>10} {pr.median() * k * 100:>18.2f}% "
|
||||
f"{pr.quantile(.9) * k * 100:>8.2f}% "
|
||||
mx = float(np.nanmax(ppd)) * cap * 100
|
||||
print(f" {'$' + format(cap, ','):>10} {float(np.nanmedian(ppd)) * cap * 100:>18.2f}% "
|
||||
f"{float(np.nanpercentile(ppd, 90)) * cap * 100:>8.2f}% "
|
||||
+ (f"{mx:>14.1f}%" if mx <= 100 else f"{'>tutta la barra':>15}"))
|
||||
print(f" => a $600 l'impatto e' strutturalmente impossibile e questa misura lo conferma.")
|
||||
_mx = float(np.nanmax(ppd))
|
||||
print(f" Ma il caso peggiore osservato e' gia' {pr.max() * 100:.0f}% di una barra 5m: a $5k")
|
||||
print(f" diventa {pr.max() * 5000 / eq * 100:.0f}%, a $20k {pr.max() * 20000 / eq * 100:.0f}%. "
|
||||
print(f" diventa {_mx * 5000 * 100:.0f}%, a $20k {_mx * 20000 * 100:.0f}%. "
|
||||
f"**La misura di oggi NON si estrapola**")
|
||||
print(f" al capitale del piano: va RIFATTA a ogni salto di taglia. E' la stessa")
|
||||
print(f" forma del muro di eseguibilita' gia' noto (XS01 ~$20k, XSR01 ~$5k), ma")
|
||||
|
||||
Reference in New Issue
Block a user