{ "cells": [ { "cell_type": "markdown", "id": "795cb67e", "metadata": {}, "source": [ "# Lab 2 — Il caso vestito da bravura\n", "\n", "*Quaderno del capitolo «Il caso vestito da bravura» di **La matematica di chi perde**.*\n", "\n", "Questo quaderno genera curve di capitale che **non contengono nulla**: nessuna\n", "decisione, nessun segnale, vantaggio atteso esattamente zero. Poi fa quello che\n", "fa chiunque mostri i propri risultati: tiene le migliori e butta il resto.\n", "\n", "L'esercizio finale è il più utile del quaderno: ci metti dentro un risultato\n", "che hai visto in giro e ti dice quale percentuale di curve casuali fa meglio.\n", "\n", "---\n", "\n", "> **EN** — *Lab 2 — Chance dressed up as skill.* Notebook for the chapter\n", "> \"Chance dressed up as skill\". This notebook generates capital curves that\n", "> **contain nothing**: no decisions, no signal, expected edge exactly zero.\n", "> Then it does what everyone who shows off their results does: keeps the\n", "> best and throws away the rest. The final exercise is the most useful one\n", "> in the notebook: plug in a result you've seen around and it tells you\n", "> what percentage of random curves do better." ] }, { "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": "5f17f7c0", "metadata": {}, "outputs": [], "source": [ "# Setup — esegui questa cella per prima.\n", "%pip install -q \"polars>=1.0\"\n", "try:\n", " import avvio\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([])" ] }, { "cell_type": "code", "execution_count": null, "id": "04b6dbc1", "metadata": {}, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "\n", "from cvbook import seed_for\n", "from cvbook.metriche import drawdown_massimo\n", "from cvbook.simulazioni import equity_casuali, migliori_per_caso" ] }, { "cell_type": "markdown", "id": "114c6a47", "metadata": {}, "source": [ "## 1. Dodici curve. Indovina quali sono quelle brave\n", "\n", "Guardale prima di scorrere. Alcune ti sembreranno convincenti — e va bene così,\n", "è esattamente il punto del capitolo.\n", "\n", "---\n", "\n", "> **EN** — *1. Twelve curves. Guess which ones are the skilled ones.* Look\n", "> at them before scrolling. Some will look convincing to you — and that's\n", "> fine, it's exactly the chapter's point." ] }, { "cell_type": "code", "execution_count": null, "id": "2e76dc37", "metadata": {}, "outputs": [], "source": [ "GIORNI = 500 # ← due anni circa di operatività\n", " # PROVA / TRY: 125 (sei mesi) · 500 · 1000\n", "VOLATILITA = 0.018 # ← oscillazione tipica per operazione\n", " # PROVA / TRY: 0,01 · 0,018 · 0,04\n", "VANTAGGIO = 0.0 # ← lascialo a zero: è il punto\n", "\n", "rng = np.random.default_rng(seed_for(\"lab-equity-casuali\"))\n", "# PROVA / TRY: cambia il nome dentro seed_for(...) per vedere altre dodici\n", "# curve — nessuna delle due versioni contiene più abilità dell'altra\n", "curve = equity_casuali(12, GIORNI, rendimento_atteso=VANTAGGIO,\n", " volatilita_periodo=VOLATILITA, rng=rng)\n", "\n", "with avvio.figura(\"schermo\"):\n", " fig, assi = plt.subplots(3, 4, figsize=(11, 6), sharex=True)\n", " for k, ax in enumerate(assi.flat):\n", " ax.plot(curve[k] * 100, linewidth=1.2)\n", " ax.axhline(100, linestyle=\":\", linewidth=0.8)\n", " ax.set_title(f\"{chr(65 + k)} {curve[k][-1]:.2f}x\", fontsize=9)\n", " ax.set_xticks([])\n", " fig.suptitle(\"Dodici curve senza alcuna abilità dentro\")\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "c910e607", "metadata": {}, "source": [ "Non ce ne sono. Sono dodici estrazioni dallo stesso generatore, con vantaggio\n", "atteso zero. Le due che ti sono piaciute hanno avuto una buona settimana.\n", "\n", "---\n", "\n", "> **EN** — There aren't any. They are twelve draws from the same generator,\n", "> with zero expected edge. The two you liked just had a good week." ] }, { "cell_type": "markdown", "id": "26baaf59", "metadata": {}, "source": [ "## 2. Le migliori cinque su mille\n", "\n", "Adesso l'operazione che compie, senza dirlo, chiunque presenti i propri\n", "risultati: si generano mille tentativi e si mostrano i cinque migliori.\n", "\n", "---\n", "\n", "> **EN** — *2. The five best out of a thousand.* Now the operation that\n", "> everyone who presents their own results performs, without saying so:\n", "> generate a thousand attempts and show the five best." ] }, { "cell_type": "code", "execution_count": null, "id": "bdfeee31", "metadata": {}, "outputs": [], "source": [ "N = 1000 # PROVA / TRY: 200 (veloce) · 1000 · 10000 (il numero del capitolo si stabilizza)\n", "rng = np.random.default_rng(seed_for(\"migliori-per-caso\"))\n", "# NON TOCCARE / DO NOT CHANGE: questa cella mostra le 5 migliori su 1000\n", "# tentativi senza vantaggio — è la messa in scena del p-hacking, non\n", "# l'occasione per cercare un seme che renda le 5 migliori ancora più belle.\n", "# This cell shows the 5 best out of 1000 attempts with no edge — it's the\n", "# staging of p-hacking, not a chance to hunt for a seed that makes the 5\n", "# best look even more impressive.\n", "tutte = equity_casuali(N, GIORNI, rendimento_atteso=VANTAGGIO,\n", " volatilita_periodo=VOLATILITA, rng=rng)\n", "migliori = migliori_per_caso(tutte, 5)\n", "\n", "with avvio.figura(\"schermo\"):\n", " fig, (sx, dx) = plt.subplots(1, 2, figsize=(11, 4))\n", " for curva in migliori:\n", " sx.plot(curva * 100, linewidth=1.4)\n", " sx.axhline(100, linestyle=\":\", linewidth=0.9)\n", " sx.set_title(f\"Le 5 migliori su {N}\")\n", " sx.set_ylabel(\"Capitale (base 100)\")\n", "\n", " dx.hist(tutte[:, -1] * 100, bins=60)\n", " dx.axvline(100, linestyle=\":\", linewidth=1.2)\n", " dx.set_title(\"Da dove sono state estratte\")\n", " dx.set_xlabel(\"Capitale finale (base 100)\")\n", " plt.show()\n", "\n", "finali = tutte[:, -1]\n", "print(f\"mediana dei {N} risultati: {np.median(finali):.3f}x\")\n", "print(f\"la migliore: {finali.max():.3f}x \"\n", " f\"(calo massimo {drawdown_massimo(migliori[0]):.1%})\")\n", "print(f\"quota sopra 1,5x: {(finali > 1.5).mean():.2%}\")\n", "print(f\"quota sopra 2x: {(finali > 2.0).mean():.2%}\")\n", "print(f\"quota sopra 2x CON calo massimo sotto il 20%: \"\n", " f\"{np.mean((finali > 2) & (np.array([drawdown_massimo(c) for c in tutte]) > -0.20)):.2%}\")" ] }, { "cell_type": "markdown", "id": "218cd5fd", "metadata": {}, "source": [ "L'ultima riga è il numero del capitolo: **circa uno su cento**. Su diecimila\n", "persone che ci provano, cento producono un biennio da fuoriclasse senza avere\n", "assolutamente nulla dentro.\n", "\n", "---\n", "\n", "> **EN** — The last line is the chapter's number: **about one in a\n", "> hundred**. Out of ten thousand people who try, a hundred produce a\n", "> standout two-year run with absolutely nothing inside." ] }, { "cell_type": "markdown", "id": "b9ddf21b", "metadata": {}, "source": [ "## 3. E adesso il tuo caso\n", "\n", "Prendi un risultato che ti ha colpito — un grafico, una pubblicità, una curva\n", "di un canale — e mettici i tre numeri qui sotto.\n", "\n", "---\n", "\n", "> **EN** — *3. And now your own case.* Take a result that struck you — a\n", "> chart, an ad, a curve from some channel — and plug in the three numbers\n", "> below." ] }, { "cell_type": "code", "execution_count": null, "id": "9e187345", "metadata": {}, "outputs": [], "source": [ "RISULTATO_DICHIARATO = 2.5 # ← capitale finale dichiarato, in volte (2,5 = +150%)\n", " # PROVA / TRY: il risultato che ti ha colpito davvero\n", "DURATA_GIORNI = 500 # ← su quanti giorni di operatività\n", " # PROVA / TRY: 125 (vedi esercizio 1) · 500\n", "VOLATILITA_TIPICA = 0.018 # ← oscillazione per operazione, se la conosci\n", " # PROVA / TRY: 0,018 · 0,04 (vedi esercizio 2)\n", "\n", "rng = np.random.default_rng(seed_for(\"confronto-personale\"))\n", "prova = equity_casuali(5000, DURATA_GIORNI, rendimento_atteso=0.0,\n", " volatilita_periodo=VOLATILITA_TIPICA, rng=rng)[:, -1]\n", "meglio = float((prova >= RISULTATO_DICHIARATO).mean())\n", "\n", "print(f\"risultato dichiarato: {RISULTATO_DICHIARATO:.2f}x su {DURATA_GIORNI} giorni\")\n", "print(f\"curve casuali che fanno altrettanto o meglio: {meglio:.2%}\")\n", "print(f\"su 10.000 persone senza alcuna abilita', ne otterrebbero altrettanto: \"\n", " f\"{meglio * 10_000:.0f}\")" ] }, { "cell_type": "markdown", "id": "8e9b0c4d", "metadata": {}, "source": [ "### Esercizi\n", "\n", "1. Riduci `DURATA_GIORNI` a 125 (sei mesi) tenendo lo stesso risultato: la\n", " percentuale crolla. Un risultato spettacolare su un periodo **corto** è più\n", " difficile da ottenere per caso di uno spettacolare su un periodo lungo.\n", "2. Alza `VOLATILITA_TIPICA` a 0,04. La stessa cifra diventa molto più facile da\n", " ottenere per caso: **quanto oscilla** cambia il significato di quanto rende.\n", "3. Metti `VANTAGGIO = 0.0005` nella prima cella. Ora un vantaggio c'è davvero.\n", " Riesci a distinguerlo a occhio dalle curve senza vantaggio? Quasi nessuno ci\n", " riesce, ed è il motivo per cui esiste il capitolo sulla potenza statistica.\n", "\n", "---\n", "\n", "> **EN** — *Exercises.*\n", "> 1. Reduce `DURATA_GIORNI` to 125 (six months) keeping the same result:\n", "> the percentage collapses. A spectacular result over a **short**\n", "> period is harder to get by chance than a spectacular one over a long\n", "> period.\n", "> 2. Raise `VOLATILITA_TIPICA` to 0.04. The same figure becomes much easier\n", "> to get by chance: **how much it swings** changes the meaning of how\n", "> much it returns.\n", "> 3. Set `VANTAGGIO = 0.0005` in the first cell. Now there really is an\n", "> edge. Can you tell it apart by eye from the curves with no edge?\n", "> Almost nobody can, and that's why the chapter on statistical power\n", "> exists." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } }, "nbformat": 4, "nbformat_minor": 5 }