Calcolatore del libro · C02
C02 — Costi cumulati su un anno di operatività, al variare della frequenza
Calcolatore 2 — I costi, sui tuoi numeri
Quaderno del capitolo «I costi che ti mangiano vivo» di La matematica di chi perde.
Tre risposte, con i tuoi parametri: quanto ti costano i costi in un anno, quale rendimento lordo ti serve solo per pareggiarli, e quanto ti sarebbe rimasto operando a quella frequenza su una serie reale.
Il numero che quasi nessuno conosce è il secondo.
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 equity, rendimenti1. I tuoi tre numeri
Se non conosci il tuo costo per giro, il capitolo spiega come misurarlo: prendi dieci operazioni chiuse, confronta il prezzo che avevi visto con quello ottenuto, somma le commissioni. Quasi tutti scoprono di stare più vicini allo 0,25% che allo 0,06%.
Output
in un anno paghi: 624.00 euro cioe' il: 6.24% del capitale rendimento lordo per NON perdere nulla: 6.43%
Espandi lo script di questo passo
CAPITALE = 10_000.0 # ← il capitale impegnato, in euro
# PROVA / TRY: il tuo capitale reale — cambia solo la
# scala, non le percentuali qui sotto
COSTO_GIRO = 0.0012 # ← costo tutto compreso di un giro completo (0,12%)
# PROVA / TRY: 0,0006 (scontato) · 0,0012 · 0,0025 (al dettaglio)
OPERAZIONI_ANNO = 52 # ← quanti giri completi fai in un anno
# PROVA / TRY: la tua frequenza reale, contata sull'estratto conto
speso = CAPITALE * COSTO_GIRO * OPERAZIONI_ANNO
pareggio = (1 + COSTO_GIRO) ** OPERAZIONI_ANNO - 1
print(t("in un anno paghi: ", "in a year you pay: ") + f"{speso:10,.2f}"
+ t(" euro", " euros"))
print(t("cioe' il: ", "that is: ") + f"{speso / CAPITALE:10.2%}"
+ t(" del capitale", " of capital"))
print(t("rendimento lordo per ", "gross return to "))
print(t("NON perdere nulla: ", "lose NOTHING: ") + f"{pareggio:10.2%}")2. La soglia, al variare della frequenza
La riga tratteggiata è, come nel libro, il rendimento medio storico di lungo periodo di un indice azionario ampio. Tutto ciò che sta sopra è territorio in cui il costo si mangia più di quanto un mercato intero abbia mai reso.
Tre curve crescenti su un asse orizzontale logaritmico con sei tacche — 12, 26, 52, 125, 250 e 500 operazioni complete all'anno — e in verticale quanto occorre guadagnare lordo per pareggiare i soli costi, graduato fino a 200%. Con 0,06% a giro la curva resta sotto il 35%, con 0,12% arriva a circa 82%, con 0,25% supera 86% gia' a 250 operazioni e esce dalla scala a 500. Una riga tratteggiata grigia a 10% segna la media storica di lungo periodo di un indice azionario ampio.
Output
costo 0.06%: 12op→0.7% 26op→1.6% 52op→3.2% 125op→7.8% 250op→16.2% 500op→35.0% costo 0.12%: 12op→1.4% 26op→3.2% 52op→6.4% 125op→16.2% 250op→35.0% 500op→82.1% costo 0.25%: 12op→3.0% 26op→6.7% 52op→13.9% 125op→36.6% 250op→86.7% 500op→248.5%
Espandi lo script di questo passo
FREQUENZE = np.array([12, 26, 52, 125, 250, 500])
COSTI = [0.0006, 0.0012, 0.0025]
with avvio.figura("schermo"):
fig, ax = plt.subplots(figsize=(9, 5))
for c in COSTI:
soglia = ((1 + c) ** FREQUENZE - 1) * 100
ax.plot(FREQUENZE, soglia, marker="o", label=t(f"costo {c:.2%} a giro", f"cost {c:.2%} per round trip"))
# Riferimento in grigio, con l'etichetta in legenda invece che sul grafico:
# una scritta appoggiata sulle curve e' il modo piu' rapido di rendere
# illeggibile una figura che dice una cosa semplice.
# Grey reference, labelled in the legend rather than on the chart: text
# sitting on top of the curves is the fastest way to make an otherwise
# simple figure unreadable.
ax.axhline(
10,
linestyle="--",
linewidth=1.2,
color="#8C8C8C",
zorder=0,
label=t("~10% annuo: media storica di un indice azionario",
"~10% a year: historical average of an equity index"),
)
ax.set_xscale("log")
ax.set_xticks(FREQUENZE)
ax.set_xticklabels([str(f) for f in FREQUENZE])
ax.set_xlabel(t("Operazioni complete all'anno", "Round trips per year"))
ax.set_ylabel(t("Rendimento lordo necessario per pareggiare (%)", "Gross return needed to break even (%)"))
ax.set_ylim(0, None)
ax.legend(loc="upper left")
plt.show()
for c in COSTI:
print(t(f"costo {c:.2%}: ", f"cost {c:.2%}: ") + " ".join(
f"{f}op→{((1 + c) ** f - 1):.1%}" for f in FREQUENZE))3. Cosa sarebbe rimasto, su dati reali
Stessa esposizione, stesso asset, stesso periodo. L'unica cosa che cambia è quante volte si chiude e si riapre la stessa posizione. Nessuna previsione diversa, nessuna decisione diversa.
Output
btcusdt · 3239 giorni
frequenza 0.06% 0.12% 0.25%
mai (compra e tieni) 13.673x 13.664x 13.645x
una volta l'anno 13.606x 13.532x 13.372x
una volta al mese 12.824x 12.020x 10.445x
una volta a settimana 10.365x 7.852x 4.299x
ogni giorno 1.959x 0.280x 0.004xEspandi lo script di questo passo
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
r = rendimenti(carica(SERIE).sort("data")["chiusura"].to_numpy())
n = len(r)
def con_frequenza(rend: np.ndarray, ogni_n_giorni: int | None, costo: float) -> float:
"""Capitale finale restando sempre investiti ma rientrando ogni N giorni."""
operazioni = np.zeros(len(rend))
# L'ingresso del primo giorno si paga sempre, anche a chi non rientrera'
# mai piu': e' un'operazione. Senza questa riga il compra-e-tieni era
# l'unica riga della tabella a viaggiare gratis, e il confronto con le
# altre frequenze partiva gia' truccato di dodici centesimi per mille euro.
# The first day's entry is always paid, even by whoever never re-enters.
operazioni[0] = 1.0
if ogni_n_giorni is not None:
operazioni[::ogni_n_giorni] = 1.0
return float(equity(rend - operazioni * costo)[-1])
print(f"{SERIE} · {n}" + t(" giorni\n", " days\n"))
print(f"{t('frequenza', 'frequency'):>22s} " + " ".join(f"{c:>10.2%}" for c in COSTI))
for ogni, etichetta in [
(None, t("mai (compra e tieni)", "never (buy and hold)")),
(365, t("una volta l'anno", "once a year")),
(30, t("una volta al mese", "once a month")),
(7, t("una volta a settimana", "once a week")),
(1, t("ogni giorno", "every day")),
]:
valori = " ".join(f"{con_frequenza(r, ogni, c):10.3f}x" for c in COSTI)
print(f"{etichetta:>22s} {valori}")Esercizi
- Metti la tua frequenza reale in
OPERAZIONI_ANNO: contale sull'estratto conto dell'ultimo anno, non a memoria. Poi confronta la soglia di pareggio con quello che pensavi di poter ottenere. - Raddoppia
COSTO_GIROe riesegui tutto. Se una strategia sopravvive solo al costo ottimistico, quel vantaggio appartiene a chi ha costi bassi, non a te. - Nella terza cella cambia serie. La colonna del compra-e-tieni cambia molto; il rapporto fra le righe quasi per niente: il costo della frequenza non dipende da quale asset hai scelto.
Riproducibilità e download
Eseguito il 2026-08-27 dal quaderno del repository
Il quaderno
calc_02_costi.ipynb11,7 KB
sha256 4210a85bb1b4ad856ce6f94cbde408f64623d1da3ca4cdebcfc0a29ca0a41d18
calc_02_costi.py9,0 KB
sha256 58d906d189a4efb9c5e4cefa66630b2fb09b3cc10c4f33d472e2a61504cdb2d1
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