Calcolatore del libro · C03
C03 — Tempo sotto il massimo e dimensione compatibile con la tua tolleranza
Calcolatore 3 — Il rischio, misurato in tempo
Quaderno del capitolo «Il rischio non è un numero» di La matematica di chi perde.
Il rischio sono quattro domande diverse, e la volatilità risponde solo alla prima. Qui le calcoli tutte e quattro, e poi fai il conto che consiglio prima di aprire qualunque posizione: quale dimensione sarebbe stata compatibile con la tua tolleranza, nel periodo storico peggiore.
Di solito è molto più piccola di quella che si aveva in mente.
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 matplotlib.pyplot as plt
import numpy as np
from cvbook.dati import carica
from cvbook.lingua import t
from cvbook.metriche import drawdown, drawdown_massimo, equity, rendimenti, sharpe, volatilita
SERIE = "btcusdt" # ← PROVA / TRY: "ethusdt" · "solusdt" (le tre preparate nel setup)
# per un'altra delle 11 serie in codice/dati/registro.json
# aggiungila anche a avvio.prepara([...]) qui sopra
df = carica(SERIE).sort("data")
prezzi = df["chiusura"].to_numpy()
date = df["data"].to_list()
r = rendimenti(prezzi)
curva = equity(r)
dd = drawdown(curva)1. Le quattro domande
Quattro numeri diversi, tutti chiamati «rischio». Il primo è quello che compare ovunque; il quarto è quello che decide se molli.
Output
btcusdt · 3240 giorni · 2017-08-17 → 2026-06-30 1. Quanto oscilla? volatilita' annualizzata 67.5% 2. Quanto perdo in un colpo? giorno peggiore -39.5% 3. Quanto scendo in totale? calo massimo dal picco -83.2% 4. Per quanto resto sotto? vedi la tabella qui sotto indicatore rendimento/rischio piu' usato al mondo: 0.78
Espandi lo script di questo passo
print(f"{SERIE} · {len(prezzi)}" + t(" giorni · ", " days · ") + f"{date[0]} → {date[-1]}\n")
print(t("1. Quanto oscilla? volatilita' annualizzata ",
"1. How much does it swing? annualized volatility ") + f"{volatilita(r):>8.1%}")
print(t("2. Quanto perdo in un colpo? giorno peggiore ",
"2. How much in one hit? worst single day ") + f"{r.min():>8.1%}")
print(t("3. Quanto scendo in totale? calo massimo dal picco ",
"3. How far down in total? max drawdown from peak ") + f"{drawdown_massimo(curva):>8.1%}")
print(t("4. Per quanto resto sotto? vedi la tabella qui sotto",
"4. For how long am I down? see the table below"))
print(t("\n indicatore rendimento/rischio piu' usato al mondo: ",
"\n most widely used return/risk indicator in the world: ") + f"{sharpe(r):.2f}")2. Il rischio misurato in tempo
La domanda che nessuno fa, ed è quella che determina se una persona reale esegue il piano fino in fondo.
Due pannelli sovrapposti su 3.240 giorni di btcusdt, dal 17 agosto 2017 al 30 giugno 2026. Sopra, l'area della distanza dal massimo precedente: scende oltre 80 sotto lo zero nel 2018 e nel 2022, e tocca lo zero poche volte. Sotto, la quota di tempo passata ad almeno una certa distanza dal massimo, dal 100% a sinistra fino a sfiorare lo zero a destra: 81% dei giorni ad almeno 10 di distanza, 71% ad almeno 20, 40% ad almeno 50, 10% ad almeno 70.
Output
almeno 10% sotto il massimo: 81.3% dei giorni (~7.2 anni su 8.9) almeno 20% sotto il massimo: 71.5% dei giorni (~6.3 anni su 8.9) almeno 50% sotto il massimo: 40.1% dei giorni (~3.6 anni su 8.9) almeno 70% sotto il massimo: 9.6% dei giorni (~0.9 anni su 8.9) almeno 80% sotto il massimo: 2.2% dei giorni (~0.2 anni su 8.9) giorni al proprio massimo storico: 111 su 3240 (3.4% del tempo)
Espandi lo script di questo passo
with avvio.figura("schermo"):
fig, (a, b) = plt.subplots(2, 1, figsize=(10, 6), height_ratios=[2, 1.4])
a.fill_between(date, dd * 100, 0, step="mid")
a.set_ylabel(t("Distanza dal massimo precedente (%)", "Distance from previous peak (%)"))
soglie = np.arange(0, 0.91, 0.05)
quote = [float((dd <= -s).mean()) * 100 for s in soglie]
b.plot(soglie * 100, quote, marker="o")
b.set_xlabel(t("Almeno questa distanza dal massimo (%)", "At least this far from the peak (%)"))
b.set_ylabel(t("Quota del tempo (%)", "Share of time (%)"))
plt.show()
for s in (0.10, 0.20, 0.50, 0.70, 0.80):
quota = float((dd <= -s).mean())
print(t(f"almeno {s:.0%} sotto il massimo: {quota:6.1%} dei giorni "
f"(~{quota * len(dd) / 365:.1f} anni su {len(dd) / 365:.1f})",
f"at least {s:.0%} below the peak: {quota:6.1%} of the days "
f"(~{quota * len(dd) / 365:.1f} years out of {len(dd) / 365:.1f})"))
sotto_zero = int(np.sum(dd < -0.001))
print(t(f"\ngiorni al proprio massimo storico: {len(dd) - sotto_zero} su {len(dd)} "
f"({1 - sotto_zero / len(dd):.1%} del tempo)",
f"\ndays at their all-time high: {len(dd) - sotto_zero} out of {len(dd)} "
f"({1 - sotto_zero / len(dd):.1%} of the time)"))3. Quanto ci si mette a tornare a galla
Non solo quanto si scende: quanto dura. È la statistica che manca in ogni scheda prodotto.
Output
episodi sotto il massimo: 50 durata mediana: 4 giorni durata media: 63 giorni il piu' lungo: 1073 giorni (2.9 anni)
Espandi lo script di questo passo
picchi = np.maximum.accumulate(curva)
in_calo = curva < picchi - 1e-12
durate, corrente = [], 0
for x in in_calo:
if x:
corrente += 1
elif corrente:
durate.append(corrente)
corrente = 0
if corrente:
durate.append(corrente) # ancora in corso alla fine della serie / still ongoing at the end of the series
durate = np.array(durate)
print(t(f"episodi sotto il massimo: {len(durate)}", f"episodes below the peak: {len(durate)}"))
print(t(f"durata mediana: {np.median(durate):6.0f} giorni", f"median duration: {np.median(durate):6.0f} days"))
print(t(f"durata media: {durate.mean():6.0f} giorni", f"mean duration: {durate.mean():6.0f} days"))
print(t(f"il piu' lungo: {durate.max():6.0f} giorni ({durate.max() / 365:.1f} anni)",
f"the longest: {durate.max():6.0f} days ({durate.max() / 365:.1f} years)"))4. Il conto da fare prima di aprire una posizione
Metti il tuo capitale e la perdita che davvero non vuoi superare. Il calcolo risponde: quanto potevi metterci, se il periodo peggiore già accaduto si ripetesse identico.
Output
calo massimo gia' accaduto su btcusdt: 83.2% perdita accettabile: 3,000 su 20,000 euro (15.0% del capitale) posizione compatibile: 18.0% del capitale, cioe' 3,606 euro E ricordati che il peggio gia' visto NON e' il peggio possibile: e' il peggio di una sola realizzazione. Con un margine del 20% la posizione scende a 3,005 euro.
Espandi lo script di questo passo
CAPITALE = 20_000.0 # ← il tuo capitale totale, in euro
# PROVA / TRY: il tuo capitale reale
PERDITA_ACCETTABILE = 3_000.0 # ← quanto sei disposto a vedere sparire, in euro
# PROVA / TRY: la tua soglia VERA, non quella
# che diresti a un amico (vedi esercizio 2)
peggiore = abs(drawdown_massimo(curva))
quota_massima = PERDITA_ACCETTABILE / (CAPITALE * peggiore)
print(t(f"calo massimo gia' accaduto su {SERIE}: {peggiore:.1%}",
f"max drawdown already seen on {SERIE}: {peggiore:.1%}"))
print(t(f"perdita accettabile: {PERDITA_ACCETTABILE:,.0f} su {CAPITALE:,.0f} euro "
f"({PERDITA_ACCETTABILE / CAPITALE:.1%} del capitale)\n",
f"acceptable loss: {PERDITA_ACCETTABILE:,.0f} out of {CAPITALE:,.0f} euros "
f"({PERDITA_ACCETTABILE / CAPITALE:.1%} of capital)\n"))
print(t(f"posizione compatibile: {min(quota_massima, 1.0):.1%} del capitale, "
f"cioe' {min(quota_massima, 1.0) * CAPITALE:,.0f} euro",
f"compatible position: {min(quota_massima, 1.0):.1%} of capital, "
f"i.e. {min(quota_massima, 1.0) * CAPITALE:,.0f} euros"))
print(t(f"\nE ricordati che il peggio gia' visto NON e' il peggio possibile: e' il "
f"peggio di una sola realizzazione. Con un margine del 20% la posizione "
f"scende a {min(quota_massima / 1.2, 1.0) * CAPITALE:,.0f} euro.",
f"\nAnd remember that the worst seen so far is NOT the worst possible: it's "
f"the worst of a single realization. With a 20% margin the position "
f"drops to {min(quota_massima / 1.2, 1.0) * CAPITALE:,.0f} euros."))Esercizi
- Cambia
SERIE. Il calo massimo cambia, e con esso la posizione compatibile: la stessa tolleranza produce dimensioni molto diverse su asset diversi. È così che si confrontano gli asset, non guardando quanto sono saliti. - Nella quarta cella metti la tua perdita accettabile vera — quella dopo la quale cambieresti comportamento, non quella che dichiareresti a un amico.
- Guarda l'episodio più lungo della terza cella e chiediti la domanda del capitolo: quanto tempo posso restare sotto senza cambiare comportamento? Se la risposta è più corta di quel numero, il problema non è l'asset: è l'accoppiamento fra lui e te.
Riproducibilità e download
Eseguito il 2026-08-27 dal quaderno del repository
Il quaderno
calc_03_stop_sizing.ipynb13,2 KB
sha256 3009a508ba5c98464ba4fb78154fbc4b2c4003988499b0ccc8753a605b425f49
calc_03_stop_sizing.py10,2 KB
sha256 234198563772be6199b1c2ca78be5e6704251c5985aa53c53bcf01d079a0fd92
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