Vai al contenuto
PAPER SIAT 2026Apri la pagina della ricerca
Cryptoverso

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
lab_19_strumenti.py
python
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
lab_19_strumenti.py
python
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.

Dove cade la storia capitata dentro le duemila che potevano capitare.Fonte: Binance Data Vision · Periodo: 2017-08-17 … 2026-06-30 · Metodo: Duemila percorsi ricampionati a blocchi dalle variazioni realmente accadute, e posizione del percorso reale nella loro distribuzione.

Output

la storia capitata: 13.68x  →  percentile 49
mediana dei possibili: 14.40x
Espandi lo script di questo passo
lab_19_strumenti.py
python
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
lab_19_strumenti.py
python
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
lab_19_strumenti.py
python
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
lab_19_strumenti.py
python
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

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

Torna all’indice dei lab