{ "cells": [ { "cell_type": "markdown", "id": "45643b87", "metadata": {}, "source": [ "# Lab 1 — Chi perde davvero, e quanto\n", "\n", "*Quaderno del capitolo «Chi perde davvero, e quanto» di **La matematica di chi perde**.*\n", "\n", "Il capitolo mostra tre coordinate senza le quali un risultato non significa\n", "niente: **su cosa**, **su quale periodo**, **con quali costi**. Qui le vedi\n", "muoversi.\n", "\n", "La figura del libro dice che, su questo asset e in questo periodo, il momento\n", "in cui si è cominciato conta più di qualunque altra cosa. Il quaderno serve a\n", "rifare quel conto e — soprattutto — a **smontarlo**, esattamente come fa il\n", "capitolo: il campione è un solo mercato in un periodo fortunato, e questo va\n", "guardato in faccia invece che nascosto.\n", "\n", "Esegui le celle dall'alto verso il basso. La prima richiede una ventina di\n", "secondi, le altre sono immediate.\n", "\n", "---\n", "\n", "> **EN** — *Lab 1 — Who really loses, and how much.* Notebook for the\n", "> chapter \"Who really loses, and how much\". The chapter shows three\n", "> coordinates without which a result means nothing: **on what**, **over\n", "> which period**, **at what cost**. Here you watch them move. The book's\n", "> figure says that, on this asset and in this period, the moment you\n", "> started matters more than anything else. The notebook redoes that\n", "> calculation and — above all — **takes it apart**, exactly as the chapter\n", "> does: the sample is a single market in a lucky period, and that has to be\n", "> looked at directly, not hidden. Run the cells top to bottom. The first\n", "> takes about twenty seconds, the rest are immediate." ] }, { "cell_type": "markdown", "id": "c4070946", "metadata": {}, "source": [ "Le righe marcate **PROVA** sono quelle da cambiare: cambiale e riesegui per\n", "vedere l'effetto. Il resto — comprese le righe marcate **NON TOCCARE** —\n", "serve a mantenere il risultato confrontabile con quello stampato nel libro.\n", "\n", "The lines marked **TRY** are the ones to change: edit them and rerun to see\n", "the effect. Everything else — including lines marked **DO NOT CHANGE** —\n", "exists to keep the result comparable with the one printed in the book." ] }, { "cell_type": "code", "execution_count": null, "id": "c5e2a5c3", "metadata": {}, "outputs": [], "source": [ "# Setup — esegui questa cella per prima.\n", "%pip install -q \"polars>=1.0\"\n", "try:\n", " import avvio # gia' presente: quaderno rieseguito, o aperto dalla repository\n", "except ModuleNotFoundError:\n", " import urllib.request\n", "\n", " urllib.request.urlretrieve(\n", " \"https://raw.githubusercontent.com/cryptoverso-lab/quaderni/main/matematica/avvio.py\",\n", " \"avvio.py\",\n", " )\n", " import avvio\n", "\n", "avvio.prepara([\"btcusdt\"])\n", "# PROVA / TRY: per usare un'altra serie aggiungila qui — le 11 disponibili\n", "# sono in codice/dati/registro.json: btcusdt · ethusdt · solusdt · lunausdt ·\n", "# fttusdt · ftsemib · eni · enel · intesa · generali · eurusd" ] }, { "cell_type": "code", "execution_count": null, "id": "3cafd746", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "from cvbook.dati import carica, citazione\n", "from cvbook.metriche import drawdown_massimo\n", "\n", "df = carica(\"btcusdt\").sort(\"data\") # ← PROVA / TRY: \"ethusdt\" · \"solusdt\" (vedi esercizio 3)\n", "prezzi = df[\"chiusura\"].to_numpy()\n", "date = df[\"data\"].to_list()\n", "fonte, estratto = citazione(\"btcusdt\")\n", "\n", "print(f\"serie: {fonte}, estratta il {estratto}\")\n", "print(f\"periodo: {date[0]} → {date[-1]} ({len(prezzi)} giorni)\")" ] }, { "cell_type": "markdown", "id": "2ffc40db", "metadata": {}, "source": [ "## 1. Quanti ingressi finiscono in perdita, per orizzonte\n", "\n", "Per ogni giorno della storia ci chiediamo: chi fosse entrato **quel giorno** e\n", "uscito dopo N giorni, come sarebbe andata? Poi contiamo la quota di ingressi\n", "chiusi in perdita.\n", "\n", "È il conto che quasi nessuno fa, perché richiede di guardare *tutti* i giorni\n", "d'ingresso e non solo quello comodo.\n", "\n", "---\n", "\n", "> **EN** — *1. How many entries end at a loss, by horizon.* For every day\n", "> in the history we ask: if someone had entered **that day** and exited\n", "> after N days, how would it have gone? Then we count the share of entries\n", "> that closed at a loss. It's the calculation almost nobody makes, because\n", "> it requires looking at *every* entry day, not just the convenient one." ] }, { "cell_type": "code", "execution_count": null, "id": "4a0d8bc7", "metadata": {}, "outputs": [], "source": [ "ORIZZONTI = [(30, \"1 mese\"), (90, \"3 mesi\"), (365, \"1 anno\"), (730, \"2 anni\"), (1460, \"4 anni\")]\n", "# PROVA / TRY: aggiungi o cambia un orizzonte, es. (180, \"6 mesi\")\n", "\n", "\n", "def quota_in_perdita(p: np.ndarray, giorni: int) -> float:\n", " \"\"\"Frazione di giorni d'ingresso che, dopo `giorni`, si trova sotto zero.\"\"\"\n", " if giorni >= len(p):\n", " return float(\"nan\")\n", " esiti = p[giorni:] / p[:-giorni] - 1.0\n", " return float((esiti < 0).mean())\n", "\n", "\n", "for giorni, etichetta in ORIZZONTI:\n", " q = quota_in_perdita(prezzi, giorni)\n", " print(f\"{etichetta:>8s}: {q:6.1%} degli ingressi in perdita ({len(prezzi) - giorni} ingressi)\")" ] }, { "cell_type": "markdown", "id": "76ae21b4", "metadata": {}, "source": [ "## 2. Il primo limite: le osservazioni si sovrappongono\n", "\n", "I numeri appena stampati sembrano basati su migliaia di casi. Non lo sono.\n", "\n", "2.875 ingressi a dodici mesi su nove anni di storia **non sono 2.875\n", "esperimenti indipendenti**: sono nove anni guardati da 2.875 angolazioni\n", "che si accavallano quasi tutte fra loro.\n", "\n", "Il conto onesto delle occasioni indipendenti è più vicino a questo.\n", "\n", "---\n", "\n", "> **EN** — *2. The first limit: observations overlap.* The numbers just\n", "> printed look like they're based on thousands of cases. They're not.\n", "> 2,875 twelve-month entries over nine years of history **are not 2,875\n", "> independent experiments**: they're nine years seen from 2,875 angles that\n", "> almost all overlap with each other. The honest count of independent\n", "> occasions is closer to this." ] }, { "cell_type": "code", "execution_count": null, "id": "9e3835a0", "metadata": {}, "outputs": [], "source": [ "for giorni, etichetta in ORIZZONTI:\n", " sovrapposti = len(prezzi) - giorni\n", " indipendenti = len(prezzi) // giorni\n", " print(\n", " f\"{etichetta:>8s}: {sovrapposti:5d} righe nel file, \"\n", " f\"ma circa {indipendenti:3d} periodi davvero distinti\"\n", " )" ] }, { "cell_type": "markdown", "id": "5f48dff7", "metadata": {}, "source": [ "Guarda la colonna di destra. A quattro anni le osservazioni indipendenti sono\n", "**due**. Un numero su cui non si costruisce nessuna conclusione.\n", "\n", "---\n", "\n", "> **EN** — Look at the right-hand column. At four years the independent\n", "> observations are **two**. Not a number to build any conclusion on." ] }, { "cell_type": "markdown", "id": "f549f9ea", "metadata": {}, "source": [ "## 3. Il secondo limite: il periodo è fortunato\n", "\n", "Cambia la finestra e guarda cosa succede ai numeri. Il capitolo lo dice\n", "esplicitamente: la colonna dei quattro anni non dimostra che a quattro anni\n", "non si perde — dimostra che *in questa finestra* non è successo.\n", "\n", "---\n", "\n", "> **EN** — *3. The second limit: the period is lucky.* Change the window\n", "> and watch what happens to the numbers. The chapter says it explicitly:\n", "> the four-year column doesn't prove that you don't lose at four years — it\n", "> proves that *in this window* it didn't happen." ] }, { "cell_type": "code", "execution_count": null, "id": "280d2e19", "metadata": {}, "outputs": [], "source": [ "INIZIO, FINE = \"2017-08-17\", \"2026-06-30\"\n", "# PROVA / TRY: FINE = \"2022-12-31\" · INIZIO = \"2021-01-01\" (vedi esercizi 1 e 2)\n", "\n", "import datetime as dt\n", "\n", "maschera = [\n", " dt.date.fromisoformat(INIZIO) <= d <= dt.date.fromisoformat(FINE) for d in date\n", "]\n", "sotto = prezzi[np.array(maschera)]\n", "\n", "print(f\"finestra scelta: {INIZIO} → {FINE} ({len(sotto)} giorni)\")\n", "print(f\"risultato del compra-e-tieni: {sotto[-1] / sotto[0]:.2f}x\")\n", "print(f\"calo massimo attraversato: {drawdown_massimo(sotto):.1%}\\n\")\n", "for giorni, etichetta in ORIZZONTI:\n", " q = quota_in_perdita(sotto, giorni)\n", " print(f\"{etichetta:>8s}: {q:6.1%} in perdita\")" ] }, { "cell_type": "markdown", "id": "703d8da5", "metadata": {}, "source": [ "### Esercizi\n", "\n", "1. **Metti `FINE = \"2022-12-31\"`** ed esegui di nuovo la cella. La colonna dei\n", " quattro anni smette di essere zero. Non è cambiato nessun dato: è cambiata\n", " la finestra.\n", "2. **Metti `INIZIO = \"2021-01-01\"`.** Il compra-e-tieni scende parecchio. È il\n", " capitolo che dice che dieci mesi di differenza sull'ingresso valevano\n", " 129 punti percentuali.\n", "3. Cambia `\"btcusdt\"` in `\"ethusdt\"` o `\"solusdt\"` nella cella del setup e in\n", " quella del caricamento. La conclusione regge? Su quali orizzonti?\n", "\n", "### Cosa portarsi via\n", "\n", "Un risultato non è un numero: è un numero **con** il suo periodo, il suo\n", "campione e la sua dimensione effettiva. Se una di queste tre cose manca, la\n", "risposta corretta a «quindi funziona?» è: *non lo so, e non lo sai nemmeno tu.*\n", "\n", "---\n", "\n", "> **EN** — *Exercises.*\n", "> 1. **Set `FINE = \"2022-12-31\"`** and rerun the cell. The four-year column\n", "> stops being zero. No data changed: the window did.\n", "> 2. **Set `INIZIO = \"2021-01-01\"`.** Buy-and-hold drops a lot. It's the\n", "> chapter that says ten months of difference on entry were worth 129\n", "> percentage points.\n", "> 3. Change `\"btcusdt\"` to `\"ethusdt\"` or `\"solusdt\"` in the setup cell and\n", "> in the loading one. Does the conclusion hold? On which horizons?\n", ">\n", "> *Takeaway.* A result is not a number: it's a number **with** its period,\n", "> its sample, and its effective size. If any of these three is missing, the\n", "> correct answer to \"so does it work?\" is: *I don't know, and neither do\n", "> you.*" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }