diff --git a/docs/diary/2026-07-26-versamenti.md b/docs/diary/2026-07-26-versamenti.md index 2af8712..50c31d1 100644 --- a/docs/diary/2026-07-26-versamenti.md +++ b/docs/diary/2026-07-26-versamenti.md @@ -363,3 +363,59 @@ sopra i soldi versati**: il piano non fallirebbe, semplicemente non renderebbe. Nessun cambio a book, pesi o piano. Cambia **cosa si sorveglia**: la domanda "l'edge c'e' ancora?" non ha una risposta nel backtest, ce l'ha solo nel forward — che e' esattamente cio' che i monitor paper e i gate pre-registrati esistono per misurare. + +--- + +## Addendum 3 — "+184% sembra alto, da dove arriva?" + +Obiezione dell'operatore su un numero che avevo citato io. **Aveva ragione, e l'errore era di +metodo, non di calcolo.** + +### Il numero e' vero + +Non e' un bug. Tracciato l'anno estremo: e' composto da 18 blocchi da 20 giorni, e **nessuno di +essi e' negativo in modo significativo**: + +``` ++20% +15% +12% +10% +8% +8% +8% +7% +6% +6% +5% +5% +2% +1% +0% -0% -0% -1% +``` + +La popolazione da cui pesca lo consente: blocchi 20g reali con mediana **−0.1%**, p95 **+8.5%**, +max **+22.4%**, **skew +2.15** — la firma classica di un trend-follower (molte perdite piccole, +poche vincite grandi). E la materia prima esiste davvero: **la miglior finestra 365g della storia +reale e' +89.8%** (il 2020 fece +58% come anno di calendario). + +### Ma citarlo era sbagliato + +``` +su 1.000 anni simulati: max +96.0% +su 5.000 anni simulati: max +135.2% +su 20.000 anni simulati: max +135.2% +su 114.000 anni simulati: max +184.3% +``` + +**Il massimo di N estrazioni cresce con N: descrive la taglia della mia simulazione, non la +strategia.** Con 1.000 percorsi avrei scritto "+96%" e sarebbe stata la stessa strategia. Il numero +che avevo dato all'operatore parlava del mio `N_PATHS`, non del book. + +I numeri corretti sono i **percentili**, che non dipendono dal campione: + +| p0.1 | p1 | p5 | p50 | p95 | p99 | p99.9 | +|---|---|---|---|---|---|---| +| −17.6% | −11.9% | −5.5% | **+16.0%** | +51.2% | +71.8% | +99.9% | + +Vale simmetricamente per il "peggiore": il **−27.4%** che avevo citato e' anch'esso un minimo +campionario (a 1.000 percorsi era −19.1%). Il numero onesto per la coda sinistra e' **p1 = −11.9%**. + +### Cablato + +Corretto anche `r0726_decadimento.py`, che riportava "drawdown PEGGIORE 44.2%" con lo stesso +difetto: ora stampa **p99 = 26.0%** e dichiara esplicitamente che il 44.2% e' un massimo +campionario da non citare come "il caso peggiore". + +### Regola trasferibile + +**Il massimo (o il minimo) di una simulazione non e' una statistica della strategia: e' una +statistica del numero di simulazioni.** Si citano i percentili. E' la stessa famiglia dell'errore +gia' codificato oggi ("un percentile stampato a 0 decimali mente esattamente agli estremi"): gli +estremi sono il posto dove i numeri sembrano piu' informativi e lo sono meno. diff --git a/scripts/research/r0726_decadimento.py b/scripts/research/r0726_decadimento.py index 38c8e73..a6d3a78 100644 --- a/scripts/research/r0726_decadimento.py +++ b/scripts/research/r0726_decadimento.py @@ -117,8 +117,13 @@ def main() -> None: base = simulate(paths, wall) dd = maxdd(base["eq"]) print(f"\n Sui {N_PATHS:,} percorsi simulati (20 anni ciascuno):") + # ⚠️ Il MASSIMO di N estrazioni cresce con N: descrive la taglia della simulazione, non la + # strategia. Si citano i PERCENTILI, che non dipendono dal numero di percorsi. (Lezione del + # 26/07: avevo citato "anno migliore +184%" che a 1.000 percorsi sarebbe stato +96%.) print(f" drawdown massimo mediano {np.median(dd):.1%} p90 {np.percentile(dd,90):.1%}" - f" PEGGIORE {dd.max():.1%}") + f" p99 {np.percentile(dd,99):.1%}") + print(f" (il PEGGIORE dei {N_PATHS:,} percorsi e' {dd.max():.1%}, ma e' un massimo campionario:") + print(f" cresce col numero di simulazioni e non va citato come 'il caso peggiore')") # anni in perdita: quota di anni-calendario chiusi sotto l'anno precedente ann = base["eq"][:, ::365] yr_ret = ann[:, 1:] / ann[:, :-1] - 1.0 @@ -129,7 +134,7 @@ def main() -> None: print(f" percorso PEGGIORE dei {N_PATHS:,}: capitale finale ${base['cap'][worst]:,.0f} " f"contro ${DP.START + 0:,.0f}+versamenti = ${START + DEP*12*YEARS:,.0f} messi dentro") print(f"\n -> No, il modello non assume che non si perda: perde nel {neg_d:.0%} dei giorni,") - print(f" ha drawdown mediani del {np.median(dd):.0%} (peggiore {dd.max():.0%}) e anni") + print(f" ha drawdown mediani del {np.median(dd):.0%} (p99 {np.percentile(dd,99):.0%}) e anni") print(f" negativi nel {float((yr_ret < 0).mean()):.0%} dei casi.") print(f" Cio' che il modello NON contiene e' un'altra cosa -> parte 2.")