Lab del libro · L19
L19 — Le cinque prove da fare al proprio ambiente di lavoro
Lab 19 — Le cinque prove del tuo strumento
Quaderno del capitolo «La cassetta degli attrezzi» di La matematica di chi perde.
Questo quaderno non serve a insegnarti Python. Serve a mostrarti in trenta secondi cosa vuol dire, in pratica, avere le cinque capacità del capitolo — così puoi confrontarle con quello che il tuo strumento attuale ti permette di fare, invece di fidarti del mio confronto.
Cinque celle, una per prova. Eseguile e poi chiediti, per ciascuna: questo, con lo strumento che uso, quanto ci metterei?
Le righe marcate PROVA sono quelle da cambiare: cambiale e riesegui per vedere l'effetto. Il resto — comprese le righe marcate NON TOCCARE — serve a mantenere il risultato confrontabile con quello stampato nel libro.
Espandi lo script di questo passo
import hashlib
import time
import matplotlib.pyplot as plt
import numpy as np
import polars as pl
from cvbook import seed_for
from cvbook.dati import carica, carica_strumento, leggi_registro
from cvbook.metriche import cagr, drawdown_massimo, rendimenti, sharpe, volatilita
from cvbook.simulazioni import bootstrap_traiettorie
ASSET = ["btcusdt", "ethusdt", "solusdt", "lunausdt", "fttusdt"]
# PROVA / TRY: aggiungi "ftsemib" · "eni" · "enel" · "intesa" · "generali" ·
# "eurusd" (aggiungili anche a avvio.prepara([...]))Prova 1 — La stessa metrica su tutti gli asset, in una tabella
Non cinque grafici da guardare uno per uno: una tabella, ordinabile. Se il tuo strumento non lo fa, il capitolo sul cimitero dei token è un capitolo che non avresti potuto scrivere.
Output
shape: (5, 9) ┌──────────┬────────┬────────────┬────────────┬───┬─────────┬────────────┬──────────────┬────────┐ │ asset ┆ giorni ┆ dal ┆ al ┆ … ┆ cagr ┆ volatilita ┆ calo_massimo ┆ sharpe │ │ --- ┆ --- ┆ --- ┆ --- ┆ ┆ --- ┆ --- ┆ --- ┆ --- │ │ str ┆ i64 ┆ str ┆ str ┆ ┆ f64 ┆ f64 ┆ f64 ┆ f64 │ ╞══════════╪════════╪════════════╪════════════╪═══╪═════════╪════════════╪══════════════╪════════╡ │ lunausdt ┆ 631 ┆ 2020-08-21 ┆ 2022-05-13 ┆ … ┆ -0.9948 ┆ 2.222 ┆ -1.0 ┆ 0.98 │ │ fttusdt ┆ 1062 ┆ 2019-12-20 ┆ 2022-11-15 ┆ … ┆ -0.1311 ┆ 1.155 ┆ -0.982 ┆ 0.59 │ │ solusdt ┆ 2150 ┆ 2020-08-11 ┆ 2026-06-30 ┆ … ┆ 0.6948 ┆ 1.162 ┆ -0.963 ┆ 1.03 │ │ ethusdt ┆ 3240 ┆ 2017-08-17 ┆ 2026-06-30 ┆ … ┆ 0.2043 ┆ 0.87 ┆ -0.94 ┆ 0.65 │ │ btcusdt ┆ 3240 ┆ 2017-08-17 ┆ 2026-06-30 ┆ … ┆ 0.3429 ┆ 0.675 ┆ -0.832 ┆ 0.78 │ └──────────┴────────┴────────────┴────────────┴───┴─────────┴────────────┴──────────────┴────────┘ tempo impiegato: 0.03 secondi
Espandi lo script di questo passo
inizio = time.perf_counter()
righe = []
for nome in ASSET:
# `carica_strumento` e non `carica`: LUNAUSDT, dal 31 maggio 2022, quota
# LUNA 2.0. Rendimento, volatilità e calo massimo di un token morto,
# calcolati sulla serie grezza, sono le metriche di due strumenti diversi
# incollati insieme.
d = carica_strumento(nome).sort("data")
p = d["chiusura"].to_numpy()
r = rendimenti(p)
curva = np.concatenate([[1.0], np.cumprod(1 + r)])
righe.append({
"asset": nome,
"giorni": len(p),
"dal": str(d["data"][0]),
"al": str(d["data"][-1]),
"finale": round(float(p[-1] / p[0]), 3),
"cagr": round(cagr(curva), 4),
"volatilita": round(volatilita(r), 3),
"calo_massimo": round(drawdown_massimo(curva), 3),
"sharpe": round(sharpe(r), 2),
})
tabella = pl.DataFrame(righe).sort("calo_massimo")
print(tabella)
print(f"\ntempo impiegato: {time.perf_counter() - inizio:.2f} secondi")Prova 2 — Mille percorsi alternativi, e dove cade il tuo
È l'operazione che trasforma «è andata così» in «così com'è andata sta nel trenta per cento peggiore dei casi possibili». Quasi nessuna piattaforma da trading la fa, e la sua assenza è la ragione per cui quasi nessuno se la chiede.
Istogramma del capitale finale di 2.000 percorsi possibili, con l'asse orizzontale logaritmico da 10 alla meno 2 a 10 alla 4 volte e il conteggio fino a 1.750. La massa e' addensata attorno a 14,4 volte, la mediana; una riga verticale nera segna la storia realmente accaduta a 13,7 volte, che cade al quarantanovesimo percentile — cioe' in mezzo.
Output
la storia capitata: 13.68x → percentile 49 mediana dei possibili: 14.40x
Espandi lo script di questo passo
r = rendimenti(carica("btcusdt").sort("data")["chiusura"].to_numpy())
rng = np.random.default_rng(seed_for("lab-strumenti"))
percorsi = bootstrap_traiettorie(r, n_traiettorie=2000, rng=rng, a_blocchi=20)
# PROVA / TRY: n_traiettorie=500 (veloce) · 2000 · 10000 (coda più precisa)
reale = float(np.prod(1 + r))
finali = percorsi[:, -1]
percentile = float((finali < reale).mean() * 100)
with avvio.figura("schermo"):
fig, ax = plt.subplots()
ax.hist(finali, bins=70)
ax.axvline(reale, linewidth=2.5, color="black")
ax.set_xscale("log")
ax.set_xlabel("Capitale finale (volte, scala log)")
ax.set_ylabel("Su 2.000 percorsi possibili")
plt.show()
print(f"la storia capitata: {reale:.2f}x → percentile {percentile:.0f}")
print(f"mediana dei possibili: {np.median(finali):.2f}x")Prova 3 — Esportare i dati grezzi
Non il grafico: i numeri. Se non puoi, stai delegando a quello strumento non solo l'esecuzione ma anche la verifica.
Output
file scritto: esportazione.csv impronta SHA-256: 2fba4b01a40602f708320c3e6ace15b3… Da questo momento chiunque puo' verificare che i tuoi numeri siano esattamente questi. Non e' pignoleria: e' la differenza fra un risultato e il ricordo di un risultato.
Espandi lo script di questo passo
percorso = "esportazione.csv"
tabella.write_csv(percorso)
with open(percorso, "rb") as f:
impronta = hashlib.sha256(f.read()).hexdigest()
print(f"file scritto: {percorso}")
print(f"impronta SHA-256: {impronta[:32]}…")
print("\nDa questo momento chiunque puo' verificare che i tuoi numeri siano "
"esattamente questi. Non e' pignoleria: e' la differenza fra un risultato "
"e il ricordo di un risultato.")Prova 4 — Rieseguire e ottenere lo stesso identico numero
Il lavoro di sei mesi fa si rifà con un comando? Se vive in una sequenza di clic, la risposta è no per costruzione.
Output
serie righe estratta il impronta btcusdt 3240 2026-08-16 ea75ad84e6e98150… ethusdt 3240 2026-08-16 c2bd0259da905e0f… solusdt 2150 2026-08-16 c7ba2368a3e419b8… lunausdt 846 2026-08-16 10fe10357f76eb40… fttusdt 1062 2026-08-16 7b235709ebb5ae31… I dati di questo libro sono congelati e firmati. Se qualcuno modificasse un file, il codice si RIFIUTEREBBE di eseguire — provaci: apri uno snapshot, cambia un byte, e riesegui la prima cella.
Espandi lo script di questo passo
registro = leggi_registro()
print(f"{'serie':>10s} {'righe':>7s} {'estratta il':>13s} {'impronta':>18s}")
for nome in ASSET:
voce = registro[nome]
print(f"{nome:>10s} {voce.righe:7d} {voce.estratto:>13s} {voce.sha256[:16]:>18s}…")
print("\nI dati di questo libro sono congelati e firmati. Se qualcuno modificasse "
"un file, il codice si RIFIUTEREBBE di eseguire — provaci: apri uno "
"snapshot, cambia un byte, e riesegui la prima cella.")Prova 5 — Quanto ci metti a rifare tutto cambiando un parametro
Se la risposta è «mezz'ora», la maggior parte delle verifiche che dovresti fare non le farai. Se è «trenta secondi», le farai tutte.
Output
24 varianti complete, con costi, calcolate in 0.22 secondi peggiore 1.81x mediana 12.21x migliore 41.00x E' questo il punto del capitolo: non la velocita' del computer, ma il fatto che a questo prezzo le verifiche LE FAI. Il numero di verifiche che NON fai e' esattamente cio' che determina quanto ti stai ingannando.
Espandi lo script di questo passo
from cvbook.regole import esegui, rottura
prezzi = carica("btcusdt").sort("data")["chiusura"].to_numpy()
inizio = time.perf_counter()
griglia = {int(f): esegui(prezzi, rottura(prezzi, int(f)), costo=0.0012)["finale"]
for f in range(5, 121, 5)}
durata = time.perf_counter() - inizio
print(f"{len(griglia)} varianti complete, con costi, calcolate in {durata:.2f} secondi")
print(f"peggiore {min(griglia.values()):.2f}x mediana "
f"{np.median(list(griglia.values())):.2f}x migliore {max(griglia.values()):.2f}x")
print("\nE' questo il punto del capitolo: non la velocita' del computer, ma il "
"fatto che a questo prezzo le verifiche LE FAI. Il numero di verifiche che "
"NON fai e' esattamente cio' che determina quanto ti stai ingannando.")Il punteggio
Rifai mentalmente le cinque prove con il tuo strumento attuale e conta quante ne supera.
- Cinque su cinque: tienilo. La scelta migliore è quella che già usi bene.
- Tre o quattro: sai dove sono i buchi, e adesso sai anche quanto costano.
- Meno di tre: il problema non è che stai lavorando peggio di quanto potresti. È che ci sono domande che non ti stanno venendo in mente, e per definizione non puoi accorgertene dall'interno.
Riproducibilità e download
Eseguito il 2026-08-27 dal quaderno del repository
Il quaderno
lab_19_strumenti.ipynb12,8 KB
sha256 cfa0c835d00b0fabf74fbd438eb448c92ed8825e5fdbefcad2e66a3d074cc7c0
lab_19_strumenti.py9,5 KB
sha256 fe99c53a70fa8541812ec295d733291725b32f2249d8f2e7003ccb8f3071ecb1
I dati
btcusdt.parquet93,2 KB
sha256 ea75ad84e6e981507054df5c622c6b0ec3c8849c1f4dd007721878d4e4c8a329
Fonte: Binance Data Vision · Periodo: 2017-08-17 → 2026-06-30 · 3240 righe · estratto 2026-08-16
ethusdt.parquet87,0 KB
sha256 c2bd0259da905e0fec87235d7a62295532433fb89657726dd2d19558db7c072a
Fonte: Binance Data Vision · Periodo: 2017-08-17 → 2026-06-30 · 3240 righe · estratto 2026-08-16
solusdt.parquet57,5 KB
sha256 c7ba2368a3e419b898fb31ec6d5345b7212b74784b69079d3d43571c2ac63657
Fonte: Binance Data Vision · Periodo: 2020-08-11 → 2026-06-30 · 2150 righe · estratto 2026-08-16
lunausdt.parquet27,6 KB
sha256 10fe10357f76eb408550f4809ce2a87cb1129164f6f6d074ae7eac730ccb7f15
Fonte: Binance Data Vision · Periodo: 2020-08-21 → 2022-12-31 · 846 righe · estratto 2026-08-16
fttusdt.parquet27,5 KB
sha256 7b235709ebb5ae31df0a9c315bd134b95e5249196a6c84b54763d9e63795fc06
Fonte: Binance Data Vision · Periodo: 2019-12-20 → 2022-11-15 · 1062 righe · estratto 2026-08-16