merge: piano versamenti — interruzione, crescita, calendario, domanda inversa
This commit is contained in:
@@ -1024,6 +1024,57 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis
|
|||||||
(d) quando le leve hanno ordini di grandezza diversi **dirlo**, o si lavora molto senza cambiare
|
(d) quando le leve hanno ordini di grandezza diversi **dirlo**, o si lavora molto senza cambiare
|
||||||
niente; (e) rivalutare **non** vuol dire anticipare i gate pre-registrati — anticiparli e'
|
niente; (e) rivalutare **non** vuol dire anticipare i gate pre-registrati — anticiparli e'
|
||||||
selezione sull'hold-out.
|
selezione sull'hold-out.
|
||||||
|
- 💰 **I VERSAMENTI — le 4 ipotesi che il piano non aveva mai fatto (2026-07-26, ultimo filone).**
|
||||||
|
Tutte le traiettorie del 25-26/07 assumevano versamento **piatto, ininterrotto, per sempre** =
|
||||||
|
l'ipotesi meno realistica del piano. Script `r0726_deposits.py`, test `tests/test_deposits.py`
|
||||||
|
(12), diario `2026-07-26-versamenti.md`. **Book/pesi/cron/config INVARIATI** (non tocca la
|
||||||
|
produzione). Block bootstrap sui ritorni reali del book live, fattore ×0.89 misurato.
|
||||||
|
(1) **SMETTERE — il costo non e' proporzionale ai soldi mancanti.** €250/m per K anni poi stop,
|
||||||
|
orizzonte 20a: 3a ($10.410) → $202.771 / P(muro) 32.7%; 5a ($16.950) → $287.081 / **53.3%**;
|
||||||
|
10a ($33.572) → $410.220 / 77.9%; 20a ($66.818) → $496.778 / 90.0%. **I primi 5 anni sono il 25%
|
||||||
|
dei soldi e il 58% del risultato.** → un'interruzione al 12° anno costa poco, una al 3° quasi
|
||||||
|
tutto: argomento per partire con un importo **sostenibile**, non ambizioso.
|
||||||
|
(2) **CRESCENTE E' PEGGIO DI PIATTO a pari soldi.** €150/m +5%/anno versa €67.998 → $401.889;
|
||||||
|
piatto €250 versa €66.818 → **$496.778** = **+24% con gli stessi soldi**, solo perche' entrano
|
||||||
|
prima. Metrica giusta per confrontare piani di taglia diversa = **`$ finale / $ versato`**
|
||||||
|
(piatti 7.4x, crescenti 5.1-5.9x). Contro-intuitivo: "i versamenti crescono col reddito" e'
|
||||||
|
prudente per il bilancio, **non** per il capitale.
|
||||||
|
(3) **STESSO TOTALE, CALENDARIO DIVERSO = fattore 6.** €60.000 distribuiti: ultimi 10 anni
|
||||||
|
$178.494 (P(muro) 11%) / piatto 20a $496.778 (90%) / primi 10a $806.285 (98.3%) / primi 5a
|
||||||
|
**$1.104.587 (99.4%)**. ⚠️ NON significa "versa tutto subito": un piano che non si sostiene non
|
||||||
|
e' un piano — serve a scegliere fra calendari **sostenibili**.
|
||||||
|
✅ **Verificato col rischio di venue dentro** (il vantaggio front-load mette piu' capitale
|
||||||
|
sull'exchange prima = proprio il rischio del giorno): **regge**, 2.22x → 2.09x a p=2%, perche' il
|
||||||
|
rischio colpisce il **tempo**, non il calendario. ⚠️ **MA a p=5% il capitale mediano e' $0 per
|
||||||
|
OGNI calendario** (64% di rovina su 20a) → **formulazione piu' netta del rischio di venue trovata
|
||||||
|
finora: non erode il piano, lo CANCELLA.**
|
||||||
|
(4) **LA DOMANDA INVERSA — rendita netta €/g mediana** (riformula l'obiettivo: €50/g e' UN punto,
|
||||||
|
non l'unico risultato):
|
||||||
|
| €/mese | 5a | 10a | 15a | 20a | P(€50/g a 20a) |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| 100 | 2.08 | 6.54 | 16.40 | 38.07 | 31.0% |
|
||||||
|
| 150 | 3.00 | 9.55 | 23.96 | 55.80 | 58.7% |
|
||||||
|
| **250** | 4.83 | 15.53 | 39.11 | **91.30** | **90.0%** |
|
||||||
|
| 400 | 7.59 | 24.52 | 61.85 | 144.23 | 98.9% |
|
||||||
|
| 600 | 11.26 | 36.52 | 92.17 | 215.07 | 100.0% |
|
||||||
|
Non-linearita': **da 15 a 20 anni la rendita piu' che raddoppia a ogni livello** (gli ultimi anni
|
||||||
|
contano piu' in *rendita*, i primi piu' in *versamenti*).
|
||||||
|
(5) **FREQUENZA = la decisione meno importante.** Mensile fino a ~$2 di costo per trasferimento,
|
||||||
|
bimestrale sopra, trimestrale oltre $25 — ma le differenze sono **1-3%** del capitale finale.
|
||||||
|
Verificato che un deposito **non resta strozzato**: col cap dinamico attivo cap = equity/2 =
|
||||||
|
esattamente il nozionale massimo richiedibile per asset.
|
||||||
|
📌 **ORDINE DI IMPORTANZA (da citare quando si parla del piano):** versare o no (**da mai a 16
|
||||||
|
anni**) > quando (**6x**) > quanto presto si smette (5 anni = 58% del risultato) > piatto vs
|
||||||
|
crescente (24%) > frequenza (1-3%). E sopra tutte, fuori scala: **a p=5% di rischio venue il
|
||||||
|
risultato mediano e' zero comunque.**
|
||||||
|
**REGOLE:** (a) un piano di accumulo si giudica sulle sue **deviazioni**, non sul caso nominale —
|
||||||
|
piatto/ininterrotto/per sempre e' l'unico scenario che non succede; (b) piani di taglia diversa si
|
||||||
|
confrontano con una metrica **normalizzata** (`$ finale / $ versato`), altrimenti "versa di piu'"
|
||||||
|
vince sempre; (c) un vantaggio calcolato **ignorando un rischio noto** va ri-misurato con quel
|
||||||
|
rischio dentro anche quando ci si aspetta che regga (qui reggeva, ma la colonna p=5% ha prodotto
|
||||||
|
il risultato piu' importante del filone); (d) quando l'obiettivo dichiarato non e' raggiungibile,
|
||||||
|
la tabella utile e' quella **inversa** — non "quando arrivo a X" ma "cosa compro con quello che
|
||||||
|
ho".
|
||||||
- 📅 **NUOVO SCHEMA FEE DERIBIT dal 2026-08-01 — misurata la CURVA, nessuna azione oggi
|
- 📅 **NUOVO SCHEMA FEE DERIBIT dal 2026-08-01 — misurata la CURVA, nessuna azione oggi
|
||||||
(2026-07-26).** Script `r0726_fee_sensitivity.py`, test `tests/test_fee_sensitivity.py` (6),
|
(2026-07-26).** Script `r0726_fee_sensitivity.py`, test `tests/test_fee_sensitivity.py` (6),
|
||||||
diario `2026-07-26-fee-deribit.md`. **Book/pesi/cron/config INVARIATI.**
|
diario `2026-07-26-fee-deribit.md`. **Book/pesi/cron/config INVARIATI.**
|
||||||
|
|||||||
@@ -0,0 +1,154 @@
|
|||||||
|
# 2026-07-26 — I versamenti: le quattro ipotesi che il piano non aveva mai fatto
|
||||||
|
|
||||||
|
**Richiesta:** *"allora concentriamoci sui versamenti"* — dopo la rivalutazione che ha misurato il
|
||||||
|
book dentro il suo plateau (ottimizzare il peso = +0.030 Sharpe, gate fallito) mentre i versamenti
|
||||||
|
valgono la differenza fra **mai** e **16 anni**.
|
||||||
|
|
||||||
|
**Script:** `r0726_deposits.py`. **Test:** `tests/test_deposits.py` (12).
|
||||||
|
**Book, pesi, cron, config: INVARIATI.** (Questo filone non tocca il codice di produzione.)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 0. Cosa mancava
|
||||||
|
|
||||||
|
Tutte le traiettorie calcolate finora (25/07 e 26/07) assumono la stessa cosa: **versamento
|
||||||
|
piatto, ininterrotto, per sempre**. E' l'ipotesi meno realistica dell'intero piano. Qui si misurano
|
||||||
|
le deviazioni che succedono davvero, con la stessa macchineria (block bootstrap sui ritorni reali
|
||||||
|
del book live, fattore d'ancora ×0.89 **misurato**).
|
||||||
|
|
||||||
|
## 1. Smettere di versare — il costo non e' proporzionale ai soldi mancanti
|
||||||
|
|
||||||
|
€250/mese per K anni, poi stop, orizzonte 20 anni:
|
||||||
|
|
||||||
|
```
|
||||||
|
versi per totale versato cap. mediano a 20a P(muro entro 20a) rendita mediana
|
||||||
|
3a $10,410 $202,771 32.7% 37.27 €/g
|
||||||
|
5a $16,950 $287,081 53.3% 52.76 €/g
|
||||||
|
10a $33,572 $410,220 77.9% 75.39 €/g
|
||||||
|
20a $66,818 $496,778 90.0% 91.30 €/g
|
||||||
|
```
|
||||||
|
|
||||||
|
**I primi 5 anni sono il 25% dei soldi e il 58% del risultato.** Chi versa 5 anni e poi smette
|
||||||
|
arriva comunque alla rendita bersaglio nel 53% dei casi; chi versa gli ultimi 10 anni (piu' soldi)
|
||||||
|
arriva nell'11% (§3). **La variabile non e' quanto versi: e' quanto presto.**
|
||||||
|
|
||||||
|
Corollario pratico: un'interruzione al 12° anno costa poco; una al 3° costa quasi tutto. Se il
|
||||||
|
piano deve rompersi, e' meglio che si rompa tardi — il che e' un argomento per **partire con un
|
||||||
|
importo sostenibile** invece che con uno ambizioso.
|
||||||
|
|
||||||
|
## 2. Un piano crescente e' peggiore di uno piatto, a pari soldi
|
||||||
|
|
||||||
|
```
|
||||||
|
piano totale versato cap. mediano P(muro) $ finale / $ versato
|
||||||
|
piatto €250 $66,818 $496,778 90.0% 7.43x
|
||||||
|
€150 +5%/anno $67,998 $401,889 80.8% 5.91x
|
||||||
|
```
|
||||||
|
|
||||||
|
**Stessi soldi (€67-68k), 24% di risultato in meno.** Il piano crescente versa gli stessi euro
|
||||||
|
piu' **tardi**, e quegli euro compongono meno. L'ultima colonna e' la metrica giusta per
|
||||||
|
confrontare piani di taglia diversa: quanto capitale finale compra ogni dollaro versato — e i
|
||||||
|
piani crescenti stanno tutti a 5.1-5.9x contro 7.4x dei piatti.
|
||||||
|
|
||||||
|
E' contro-intuitivo perche' "i versamenti crescono col reddito" suona prudente. Lo e' dal punto di
|
||||||
|
vista del bilancio familiare; **non** lo e' dal punto di vista del capitale.
|
||||||
|
|
||||||
|
## 3. Stesso totale, calendario diverso — il tempo vale 2.2x
|
||||||
|
|
||||||
|
€60.000 in totale (= €250/mese per 20 anni), distribuiti diversamente:
|
||||||
|
|
||||||
|
```
|
||||||
|
calendario cap. mediano P(muro) vs piatto
|
||||||
|
piatto su 20 anni $496,778 90.0% 1.00x
|
||||||
|
tutto nei primi 10 anni $806,285 98.3% 1.62x
|
||||||
|
tutto nei primi 5 anni $1,104,587 99.4% 2.22x
|
||||||
|
solo negli ultimi 10 anni $178,494 11.0% 0.36x
|
||||||
|
```
|
||||||
|
|
||||||
|
**Gli stessi €60.000 valgono da $178k a $1.104k — un fattore 6 — a seconda di QUANDO entrano.**
|
||||||
|
|
||||||
|
⚠️ Questo **non** dice "versa tutto subito": dice quanto vale il tempo. Un piano che non si
|
||||||
|
sostiene non e' un piano, e il confronto serve a scegliere fra calendari **sostenibili**.
|
||||||
|
|
||||||
|
### E regge al rischio di venue?
|
||||||
|
|
||||||
|
Il vantaggio del front-loading e' calcolato mettendo **piu' capitale sull'exchange prima** — cioe'
|
||||||
|
proprio il rischio misurato stamattina. Andava verificato, non assunto:
|
||||||
|
|
||||||
|
```
|
||||||
|
calendario p=0.5% p=2.0% p=5.0%
|
||||||
|
piatto su 20 anni $463,413 $359,251 $0
|
||||||
|
tutto nei primi 10 anni $765,178 $555,904 $0
|
||||||
|
tutto nei primi 5 anni $1,015,491 $752,367 $0
|
||||||
|
solo negli ultimi 10 anni $172,228 $147,203 $0
|
||||||
|
```
|
||||||
|
|
||||||
|
Il vantaggio **regge** (2.22x → 2.09x a p=2%): il rischio di venue colpisce il **tempo**, non il
|
||||||
|
calendario dei versamenti. Anticipare non aumenta la probabilita' di essere colpiti, aumenta solo
|
||||||
|
quanto c'e' dentro quando succede — e nel frattempo ha composto di piu'.
|
||||||
|
|
||||||
|
⚠️ **Ma guardare la colonna p=5%: il capitale mediano e' $0 per OGNI calendario.** A quel tasso di
|
||||||
|
rischio la meta' dei percorsi finisce a zero (64% di rovina su 20 anni, come misurato stamattina) e
|
||||||
|
**come versi diventa irrilevante**. E' la formulazione piu' netta del rischio di venue trovata
|
||||||
|
finora: non erode il piano, lo **cancella**.
|
||||||
|
|
||||||
|
## 4. La domanda inversa — che rendita compra quello che posso permettermi
|
||||||
|
|
||||||
|
Rendita **netta** mediana in €/giorno:
|
||||||
|
|
||||||
|
```
|
||||||
|
€/mese 5a 10a 15a 20a P(€50/g a 20a)
|
||||||
|
100 2.08€ 6.54€ 16.40€ 38.07€ 31.0%
|
||||||
|
150 3.00€ 9.55€ 23.96€ 55.80€ 58.7%
|
||||||
|
250 4.83€ 15.53€ 39.11€ 91.30€ 90.0%
|
||||||
|
400 7.59€ 24.52€ 61.85€ 144.23€ 98.9%
|
||||||
|
600 11.26€ 36.52€ 92.17€ 215.07€ 100.0%
|
||||||
|
```
|
||||||
|
|
||||||
|
E' la tabella che **riformula l'obiettivo**. €50/g e' un punto su questa griglia, non l'unico
|
||||||
|
risultato che conta: €150/mese porta a ~€24/g in 15 anni, che non e' un fallimento del piano da
|
||||||
|
€50 — e' un risultato diverso, e ora si sa quanto costa il salto.
|
||||||
|
|
||||||
|
Da notare la **non-linearita' temporale**: da 15 a 20 anni la rendita piu' che raddoppia a ogni
|
||||||
|
livello. Gli ultimi anni del piano contano piu' dei primi *in rendita*, esattamente come i primi
|
||||||
|
contano piu' degli ultimi *in versamenti*.
|
||||||
|
|
||||||
|
## 5. Ogni quanto versare — la decisione meno importante
|
||||||
|
|
||||||
|
```
|
||||||
|
costo/tras. mensile bimestrale trimestrale semestrale
|
||||||
|
$0.00 *$490,149 $486,725 $482,587 $473,908
|
||||||
|
$2.00 *$486,597 $484,964 $481,446 $473,342
|
||||||
|
$5.00 $481,270 *$482,323 $479,727 $472,495
|
||||||
|
$25.00 $445,984 $464,692 *$468,226 $466,817
|
||||||
|
```
|
||||||
|
|
||||||
|
**Mensile fino a ~$2 di costo per trasferimento, bimestrale sopra.** Ma le differenze sono
|
||||||
|
**1-3%** del capitale finale: e' la meno importante delle cinque decisioni di questo diario, e non
|
||||||
|
merita ottimizzazione oltre la regola in una riga.
|
||||||
|
|
||||||
|
Verificato che un deposito **non resta strozzato**: col cap dinamico attivo (`_cap`), cap =
|
||||||
|
equity/2 = esattamente il nozionale massimo che il book puo' chiedere per asset.
|
||||||
|
|
||||||
|
## 6. Le cinque decisioni, in ordine di quanto pesano
|
||||||
|
|
||||||
|
| decisione | effetto misurato |
|
||||||
|
|---|---|
|
||||||
|
| **versare o no** | da **mai** a 16 anni |
|
||||||
|
| **quando** (front vs back, stesso totale) | **6x** sul capitale finale |
|
||||||
|
| **quanto presto smettere** | 5 anni = 25% dei soldi, 58% del risultato |
|
||||||
|
| piatto vs crescente | 24% a pari soldi |
|
||||||
|
| frequenza | 1-3% |
|
||||||
|
|
||||||
|
E sopra tutte, fuori scala: **a p=5% di rischio venue il risultato mediano e' zero comunque.**
|
||||||
|
|
||||||
|
## 7. Regole trasferibili
|
||||||
|
|
||||||
|
1. **Un piano di accumulo si giudica sulle sue deviazioni, non sul caso nominale.** Piatto,
|
||||||
|
ininterrotto e per sempre e' l'unico scenario che non succede.
|
||||||
|
2. **Confrontare piani di taglia diversa richiede una metrica normalizzata** (`$ finale / $
|
||||||
|
versato`), altrimenti "versa di piu'" vince sempre e non si impara niente.
|
||||||
|
3. **Un vantaggio calcolato ignorando un rischio noto va ri-misurato con quel rischio dentro**,
|
||||||
|
anche quando ci si aspetta che regga — qui reggeva, ma la colonna p=5% ha prodotto il risultato
|
||||||
|
piu' importante del filone.
|
||||||
|
4. **Quando l'obiettivo dichiarato non e' raggiungibile, la tabella utile e' quella inversa**:
|
||||||
|
non "quando arrivo a X" ma "cosa compro con quello che ho".
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""r0726_deposits.py — il piano dei VERSAMENTI, sotto le ipotesi che il piano non ha mai fatto.
|
||||||
|
|
||||||
|
CONTESTO. La rivalutazione di fine giornata ha misurato che il book e' dentro il suo plateau su
|
||||||
|
ogni asse (ottimizzare il peso = +0.030 Sharpe, gate fallito) mentre i versamenti valgono la
|
||||||
|
differenza fra **mai** e **16 anni**. Quindi il vincolo binding non e' piu' la strategia.
|
||||||
|
|
||||||
|
Ma tutte le traiettorie calcolate finora (25/07 e 26/07) assumono la stessa cosa:
|
||||||
|
**versamento PIATTO, ININTERROTTO, PER SEMPRE**. E' l'ipotesi meno realistica del piano intero.
|
||||||
|
Qui si misurano le quattro deviazioni che contano davvero, tutte con la stessa macchineria
|
||||||
|
(block bootstrap sui ritorni REALI del book live, fattore d'ancora ×0.89 misurato).
|
||||||
|
|
||||||
|
(A) INTERRUZIONE — si versa per K anni e poi si smette. E' la deviazione piu' probabile di
|
||||||
|
tutte (lavoro, spese, vita). Quanto costa smettere, e quando smettere fa meno male?
|
||||||
|
(B) CRESCITA — €150/mese che crescono del 5%/anno contro €250/mese piatti. Piu' realistico
|
||||||
|
di un importo fisso per vent'anni, e la domanda e' se conviene A PARI TOTALE VERSATO.
|
||||||
|
(C) FRONT vs BACK LOADING — stesso totale, calendario diverso. Dice se vale la pena
|
||||||
|
sforzarsi ADESSO invece che dopo, e di quanto.
|
||||||
|
(D) LA DOMANDA INVERSA — invece di "quando arrivo a €50/g", **"che rendita compro con quello
|
||||||
|
che posso permettermi?"**. Se €50/g non e' raggiungibile, il numero utile e' l'altro.
|
||||||
|
|
||||||
|
uv run python scripts/research/r0726_deposits.py
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||||
|
|
||||||
|
import r0725_capcurve as CC # noqa: E402
|
||||||
|
import r0726_capwall_refresh as WR # noqa: E402
|
||||||
|
|
||||||
|
DELUCK = 0.89 # fattore d'ancora MISURATO (r0726_deluck_factor), non scelto a occhio
|
||||||
|
START = 600.0
|
||||||
|
N_PATHS = 4000
|
||||||
|
BLOCK = 20
|
||||||
|
SEED = 20260726
|
||||||
|
|
||||||
|
|
||||||
|
def deluck_returns() -> np.ndarray:
|
||||||
|
"""Ritorni giornalieri del book live con il drift ridotto del fattore d'ancora misurato."""
|
||||||
|
base = CC.book_series("hourly")
|
||||||
|
mu = float(base.values.mean())
|
||||||
|
return (base - (1.0 - DELUCK) * mu).values.astype(float)
|
||||||
|
|
||||||
|
|
||||||
|
def simulate(paths: np.ndarray, schedule: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
|
||||||
|
"""Capitale finale e totale versato, dato un CALENDARIO di versamenti giornaliero (USD/giorno).
|
||||||
|
|
||||||
|
`schedule[t]` = quanto entra il giorno t. Vettoriale su tutti i path insieme.
|
||||||
|
Ritorna (capitale finale per path, totale versato — uguale per tutti, ma ritornato come
|
||||||
|
scalare replicato per rendere impossibile confrontare per sbaglio grandezze diverse).
|
||||||
|
"""
|
||||||
|
n_paths, n_days = paths.shape
|
||||||
|
cap = np.full(n_paths, START)
|
||||||
|
for t in range(n_days):
|
||||||
|
cap = cap * (1.0 + paths[:, t])
|
||||||
|
if schedule[t]:
|
||||||
|
cap += schedule[t]
|
||||||
|
return cap, np.full(n_paths, START + float(schedule.sum()))
|
||||||
|
|
||||||
|
|
||||||
|
def monthly_schedule(n_days: int, eur_month: float, stop_year: float | None = None,
|
||||||
|
growth: float = 0.0) -> np.ndarray:
|
||||||
|
"""Calendario: versamento ogni 30 giorni, opzionalmente crescente e/o interrotto."""
|
||||||
|
s = np.zeros(n_days)
|
||||||
|
for t in range(30, n_days, 30):
|
||||||
|
if stop_year is not None and t >= stop_year * 365:
|
||||||
|
break
|
||||||
|
yrs = t / 365.0
|
||||||
|
s[t] = eur_month * CC.EURUSD * ((1.0 + growth) ** yrs)
|
||||||
|
return s
|
||||||
|
|
||||||
|
|
||||||
|
def rent_from_capital(cap: np.ndarray, perp_rate: float) -> np.ndarray:
|
||||||
|
"""Rendita NETTA in €/giorno da un capitale, al tasso di prelievo perpetuo misurato."""
|
||||||
|
return cap * perp_rate * (1 - CC.TAX_RATE) / 365.0 / CC.EURUSD
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
print("=" * 100)
|
||||||
|
print(" r0726 — I VERSAMENTI: interruzione, crescita, calendario, e la domanda inversa")
|
||||||
|
print("=" * 100)
|
||||||
|
|
||||||
|
r = deluck_returns()
|
||||||
|
gross = CC.TARGET_EUR_DAY * 365 * CC.EURUSD / (1 - CC.TAX_RATE)
|
||||||
|
_, perp, wall = WR.perp_and_wall(r, 1.0, gross)
|
||||||
|
print(f" book live de-luckato ×{DELUCK:.2f} | rendita perpetua {perp:.2%} | "
|
||||||
|
f"muro €{CC.TARGET_EUR_DAY:.0f}/g = ${wall:,.0f}")
|
||||||
|
print(f" block bootstrap, {N_PATHS} path, blocchi da {BLOCK} giorni, partenza ${START:.0f}")
|
||||||
|
|
||||||
|
rng = np.random.default_rng(SEED)
|
||||||
|
YEARS = 20
|
||||||
|
n_days = 365 * YEARS
|
||||||
|
paths = CC._boot_paths(r, N_PATHS, n_days, BLOCK, rng)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------ (A) interruzione
|
||||||
|
print("\n" + "=" * 100)
|
||||||
|
print(" (A) SMETTERE DI VERSARE — €250/mese per K anni, poi stop. Orizzonte 20 anni.")
|
||||||
|
print("=" * 100)
|
||||||
|
print(f"\n {'versi per':>10}{'totale versato':>16}{'cap. mediano a 20a':>21}"
|
||||||
|
f"{'P(muro entro 20a)':>19}{'rendita mediana':>17}")
|
||||||
|
ref = None
|
||||||
|
for k in (3, 5, 8, 10, 15, 20):
|
||||||
|
sch = monthly_schedule(n_days, 250.0, stop_year=k)
|
||||||
|
cap, paid = simulate(paths, sch)
|
||||||
|
p_wall = float((cap >= wall).mean())
|
||||||
|
rent = float(np.median(rent_from_capital(cap, perp)))
|
||||||
|
if k == 20:
|
||||||
|
ref = (float(np.median(cap)), p_wall)
|
||||||
|
print(f" {k:>8}a {paid[0]:>15,.0f}${np.median(cap):>20,.0f}"
|
||||||
|
f"{p_wall:>18.1%}{rent:>15.2f} €/g")
|
||||||
|
print("\n Lettura: la curva NON e' proporzionale al totale versato — gli anni iniziali")
|
||||||
|
print(" valgono di piu' perche' compongono piu' a lungo. Smettere presto costa il doppio")
|
||||||
|
print(" di quanto suggerisca il totale mancante.")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------ (B) crescita
|
||||||
|
print("\n" + "=" * 100)
|
||||||
|
print(" (B) VERSAMENTO CRESCENTE vs PIATTO — a confronto anche A PARI TOTALE VERSATO")
|
||||||
|
print("=" * 100)
|
||||||
|
plans = [("piatto €250", 250.0, 0.0), ("piatto €300", 300.0, 0.0),
|
||||||
|
("€150 +5%/anno", 150.0, 0.05), ("€200 +5%/anno", 200.0, 0.05),
|
||||||
|
("€150 +8%/anno", 150.0, 0.08)]
|
||||||
|
print(f"\n {'piano':<16}{'totale versato':>16}{'cap. mediano':>15}"
|
||||||
|
f"{'P(muro)':>10}{'$ finale / $ versato':>22}")
|
||||||
|
for lab, eur, g in plans:
|
||||||
|
sch = monthly_schedule(n_days, eur, growth=g)
|
||||||
|
cap, paid = simulate(paths, sch)
|
||||||
|
print(f" {lab:<16}{paid[0]:>15,.0f}${np.median(cap):>14,.0f}"
|
||||||
|
f"{float((cap >= wall).mean()):>10.1%}{np.median(cap)/paid[0]:>21.2f}x")
|
||||||
|
print("\n L'ultima colonna e' la METRICA GIUSTA per confrontare piani di taglia diversa:")
|
||||||
|
print(" quanto capitale finale compra ogni dollaro versato. Un piano crescente versa")
|
||||||
|
print(" di piu' TARDI, quindi quei dollari compongono meno.")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------ (C) calendario
|
||||||
|
print("\n" + "=" * 100)
|
||||||
|
print(" (C) STESSO TOTALE, CALENDARIO DIVERSO — conviene sforzarsi adesso?")
|
||||||
|
print("=" * 100)
|
||||||
|
TOT = 250.0 * 12 * 20 # totale del piano di riferimento, in EUR
|
||||||
|
print(f"\n Totale fissato: €{TOT:,.0f} (= €250/mese per 20 anni), distribuito diversamente:")
|
||||||
|
print(f"\n {'calendario':<28}{'cap. mediano':>15}{'P(muro)':>10}{'vs piatto':>12}")
|
||||||
|
layouts = [("piatto su 20 anni", 20, 0), ("tutto nei primi 10 anni", 10, 0),
|
||||||
|
("tutto nei primi 5 anni", 5, 0), ("solo negli ultimi 10 anni", 10, 10)]
|
||||||
|
base_med = None
|
||||||
|
scheds = {}
|
||||||
|
for lab, span, delay in layouts:
|
||||||
|
eur_m = TOT / (span * 12)
|
||||||
|
s = np.zeros(n_days)
|
||||||
|
for t in range(30, n_days, 30):
|
||||||
|
if delay * 365 <= t < (delay + span) * 365:
|
||||||
|
s[t] = eur_m * CC.EURUSD
|
||||||
|
scheds[lab] = s
|
||||||
|
cap, paid = simulate(paths, s)
|
||||||
|
med = float(np.median(cap))
|
||||||
|
if base_med is None:
|
||||||
|
base_med = med
|
||||||
|
print(f" {lab:<28}${med:>14,.0f}{float((cap >= wall).mean()):>10.1%}"
|
||||||
|
f"{med/base_med:>11.2f}x")
|
||||||
|
print("\n ⚠️ Questo NON dice 'versa tutto subito': dice quanto vale il tempo. Un piano")
|
||||||
|
print(" che non riesci a sostenere non e' un piano — il confronto serve a scegliere fra")
|
||||||
|
print(" calendari SOSTENIBILI, non a giustificare uno sforzo che si rompe al terzo anno.")
|
||||||
|
|
||||||
|
# --- e il vantaggio del front-loading regge al RISCHIO DI VENUE? ---
|
||||||
|
# Anticipare i versamenti mette PIU' capitale sull'exchange PRIMA: il numero sopra e'
|
||||||
|
# calcolato senza il rischio che il conto sparisca, che e' proprio il rischio che cresce con
|
||||||
|
# quanto tieni li' e per quanto tempo (r0726_venue_risk). Va misurato, non assunto.
|
||||||
|
print(f"\n Lo stesso confronto CON il rischio di venue (salto a zero a probabilita' annua p,")
|
||||||
|
print(f" 100% Deribit come da decisione del 26/07):")
|
||||||
|
print(f"\n {'calendario':<28}" + "".join(f"{'p=' + f'{p:.1%}':>16}" for p in (0.005, 0.02, 0.05)))
|
||||||
|
rng_v = np.random.default_rng(SEED + 1)
|
||||||
|
for lab, span, delay in layouts:
|
||||||
|
cells = []
|
||||||
|
for p in (0.005, 0.02, 0.05):
|
||||||
|
haz = 1.0 - (1.0 - p) ** (1.0 / 365.0) # hazard giornaliero -> p annua
|
||||||
|
alive = rng_v.random((N_PATHS, n_days)) > haz
|
||||||
|
surv = np.cumprod(alive, axis=1) # 1 finche' vivo, 0 dopo il fallimento
|
||||||
|
cap = np.full(N_PATHS, START)
|
||||||
|
s = scheds[lab]
|
||||||
|
for t in range(n_days):
|
||||||
|
cap = cap * (1.0 + paths[:, t])
|
||||||
|
if s[t]:
|
||||||
|
cap += s[t]
|
||||||
|
cap = cap * surv[:, t] # il conto sparisce col saldo dentro
|
||||||
|
cells.append(float(np.median(cap)))
|
||||||
|
print(f" {lab:<28}" + "".join(f"${c:>15,.0f}" for c in cells))
|
||||||
|
print("\n Il front-loading resta avanti perche' il rischio di venue colpisce il TEMPO, non")
|
||||||
|
print(" il calendario dei versamenti: anticipare non aumenta la probabilita' di essere")
|
||||||
|
print(" colpiti, aumenta solo quanto c'e' dentro quando succede — e nel frattempo ha")
|
||||||
|
print(" composto di piu'. La conclusione non si ribalta, ma il margine si assottiglia.")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------ (D) domanda inversa
|
||||||
|
print("\n" + "=" * 100)
|
||||||
|
print(" (D) LA DOMANDA INVERSA — che rendita compra quello che posso permettermi?")
|
||||||
|
print("=" * 100)
|
||||||
|
print("\n Rendita NETTA mediana in €/giorno, per versamento mensile e orizzonte:")
|
||||||
|
horizons = (5, 10, 15, 20)
|
||||||
|
print(f"\n {'€/mese':>8}" + "".join(f"{h:>10}a" for h in horizons) + f"{' (a 20a: P(€50/g))':>20}")
|
||||||
|
for eur in (100, 150, 250, 400, 600, 1000):
|
||||||
|
cells = []
|
||||||
|
p50g = None
|
||||||
|
for h in horizons:
|
||||||
|
nd = 365 * h
|
||||||
|
pp = paths[:, :nd]
|
||||||
|
sch = monthly_schedule(nd, eur)
|
||||||
|
cap, _ = simulate(pp, sch)
|
||||||
|
rent = rent_from_capital(cap, perp)
|
||||||
|
cells.append(f"{np.median(rent):>9.2f}€")
|
||||||
|
if h == 20:
|
||||||
|
p50g = float((rent >= CC.TARGET_EUR_DAY).mean())
|
||||||
|
print(f" {eur:>8}" + "".join(cells) + f"{p50g:>19.1%}")
|
||||||
|
print("\n E' la tabella che riformula l'obiettivo: €50/g e' UN punto su questa griglia,")
|
||||||
|
print(" non l'unico risultato che conta. Un piano che porta a €10-15/g in 15 anni non")
|
||||||
|
print(" e' un fallimento del piano da €50 — e' un risultato diverso, e va scelto sapendo")
|
||||||
|
print(" quanto costa il salto.")
|
||||||
|
|
||||||
|
# ------------------------------------------------------------ (E) frequenza
|
||||||
|
print("\n" + "=" * 100)
|
||||||
|
print(" (E) OGNI QUANTO VERSARE — l'unica di queste decisioni che si prende ogni mese")
|
||||||
|
print("=" * 100)
|
||||||
|
print("\n Compromesso: versare spesso = piu' tempo a mercato ma piu' costi fissi di")
|
||||||
|
print(" trasferimento; versare di rado = meno costi ma i soldi restano fermi.")
|
||||||
|
print(" ⚠️ Il costo per trasferimento NON e' stimato da me (dipende da rete e metodo):")
|
||||||
|
print(" e' una SENSIBILITA'. Cerchia la riga che corrisponde al tuo costo reale.")
|
||||||
|
EUR_YEAR = 250.0 * 12
|
||||||
|
cadences = [("mensile", 30), ("bimestrale", 60), ("trimestrale", 91), ("semestrale", 182)]
|
||||||
|
print(f"\n {'costo/tras.':>12}" + "".join(f"{lab:>16}" for lab, _ in cadences))
|
||||||
|
for fee in (0.0, 1.0, 2.0, 5.0, 10.0, 25.0):
|
||||||
|
cells = []
|
||||||
|
for lab, days in cadences:
|
||||||
|
per_dep = EUR_YEAR * CC.EURUSD * days / 365.0
|
||||||
|
s = np.zeros(n_days)
|
||||||
|
for t in range(days, n_days, days):
|
||||||
|
s[t] = per_dep - fee # il costo fisso si paga a OGNI trasferimento
|
||||||
|
cap, _ = simulate(paths, s)
|
||||||
|
cells.append(float(np.median(cap)))
|
||||||
|
best = max(range(len(cells)), key=lambda i: cells[i])
|
||||||
|
row = "".join(f"{('*' if i == best else ' ') + '$' + format(c, ',.0f'):>16}"
|
||||||
|
for i, c in enumerate(cells))
|
||||||
|
print(f" {'$' + format(fee, '.2f'):>12}" + row)
|
||||||
|
print("\n (* = migliore della riga). Il ribaltamento avviene dove il costo fisso supera")
|
||||||
|
print(" il valore del tempo a mercato del versamento medio.")
|
||||||
|
print(" ⚠️ Le differenze sono ~1-3% del capitale finale: e' la meno importante di queste")
|
||||||
|
print(" cinque decisioni. Non vale la pena ottimizzarla oltre 'mensile, salvo costi alti'.")
|
||||||
|
print("\n NB: a $600 il min-order del book e' $5 e il cap e' equity/2 — un deposito NON")
|
||||||
|
print(" resta strozzato (vedi la guardia in `_cap`: col cap dinamico attivo, cap =")
|
||||||
|
print(" equity/2 = esattamente il nozionale massimo che il book puo' chiedere).")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
"""Test del piano versamenti (scripts/research/r0726_deposits.py).
|
||||||
|
|
||||||
|
Il test piu' importante e' `test_la_rendita_al_muro_e_esattamente_il_bersaglio`: lega la tabella
|
||||||
|
della "domanda inversa" al muro calcolato altrove. Se le due macchinerie divergono, una delle due
|
||||||
|
tabelle sta mentendo e nessun altro controllo se ne accorgerebbe.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||||
|
|
||||||
|
import r0725_capcurve as CC # noqa: E402
|
||||||
|
import r0726_deposits as DP # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# calendario dei versamenti
|
||||||
|
# ===========================================================================
|
||||||
|
def test_versamento_mensile_conta_i_periodi_giusti():
|
||||||
|
s = DP.monthly_schedule(365, 100.0)
|
||||||
|
assert (s > 0).sum() == 12, "12 versamenti in un anno di 365 giorni a passo 30"
|
||||||
|
assert s[0] == 0.0, "il primo versamento non e' al giorno 0 (li' c'e' il capitale iniziale)"
|
||||||
|
|
||||||
|
|
||||||
|
def test_stop_year_interrompe_davvero():
|
||||||
|
s = DP.monthly_schedule(365 * 10, 100.0, stop_year=3)
|
||||||
|
ultimo = np.nonzero(s)[0].max()
|
||||||
|
assert ultimo < 3 * 365
|
||||||
|
assert (s > 0).sum() == pytest.approx(36, abs=1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_la_crescita_aumenta_gli_importi_nel_tempo():
|
||||||
|
s = DP.monthly_schedule(365 * 10, 100.0, growth=0.05)
|
||||||
|
nz = s[s > 0]
|
||||||
|
assert nz[-1] > nz[0] * 1.5, "dopo ~10 anni al 5% l'importo deve essere >1.5x"
|
||||||
|
piatto = DP.monthly_schedule(365 * 10, 100.0)
|
||||||
|
assert s.sum() > piatto.sum()
|
||||||
|
|
||||||
|
|
||||||
|
def test_crescita_zero_equivale_al_piatto():
|
||||||
|
a = DP.monthly_schedule(365 * 5, 200.0, growth=0.0)
|
||||||
|
b = DP.monthly_schedule(365 * 5, 200.0)
|
||||||
|
assert np.allclose(a, b)
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# simulazione
|
||||||
|
# ===========================================================================
|
||||||
|
def test_senza_versamenti_e_solo_capitalizzazione():
|
||||||
|
paths = np.full((3, 100), 0.001)
|
||||||
|
cap, paid = DP.simulate(paths, np.zeros(100))
|
||||||
|
atteso = DP.START * (1.001 ** 100)
|
||||||
|
assert cap[0] == pytest.approx(atteso, rel=1e-9)
|
||||||
|
assert paid[0] == pytest.approx(DP.START)
|
||||||
|
|
||||||
|
|
||||||
|
def test_a_rendimento_zero_il_capitale_e_la_somma_dei_versamenti():
|
||||||
|
paths = np.zeros((2, 365))
|
||||||
|
s = DP.monthly_schedule(365, 100.0)
|
||||||
|
cap, paid = DP.simulate(paths, s)
|
||||||
|
assert cap[0] == pytest.approx(DP.START + s.sum())
|
||||||
|
assert paid[0] == pytest.approx(DP.START + s.sum())
|
||||||
|
|
||||||
|
|
||||||
|
def test_il_totale_versato_include_il_capitale_iniziale():
|
||||||
|
"""Confrontare un capitale finale con un 'versato' che dimentica i $600 iniziali gonfia il
|
||||||
|
rapporto capitale/versato — errore gia' fatto una volta oggi nel contatore `paid`."""
|
||||||
|
s = DP.monthly_schedule(365, 50.0)
|
||||||
|
_, paid = DP.simulate(np.zeros((2, 365)), s)
|
||||||
|
assert paid[0] == pytest.approx(DP.START + s.sum())
|
||||||
|
assert paid[0] > s.sum()
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# il legame con il muro — il test che lega le due macchinerie
|
||||||
|
# ===========================================================================
|
||||||
|
def test_la_rendita_al_muro_e_esattamente_il_bersaglio():
|
||||||
|
"""Un capitale pari al muro deve dare, per costruzione, esattamente TARGET_EUR_DAY.
|
||||||
|
Se questa identita' si rompe, la tabella (D) e la tabella dei muri non parlano piu' della
|
||||||
|
stessa cosa."""
|
||||||
|
import r0726_capwall_refresh as WR
|
||||||
|
|
||||||
|
r = DP.deluck_returns()
|
||||||
|
gross = CC.TARGET_EUR_DAY * 365 * CC.EURUSD / (1 - CC.TAX_RATE)
|
||||||
|
_, perp, wall = WR.perp_and_wall(r, 1.0, gross)
|
||||||
|
rent = DP.rent_from_capital(np.array([wall]), perp)[0]
|
||||||
|
assert rent == pytest.approx(CC.TARGET_EUR_DAY, rel=1e-6)
|
||||||
|
|
||||||
|
|
||||||
|
def test_la_rendita_e_lineare_nel_capitale():
|
||||||
|
r = DP.rent_from_capital(np.array([100_000.0, 200_000.0]), 0.10)
|
||||||
|
assert r[1] == pytest.approx(2 * r[0])
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# i FINDING: se cambiano, e' una notizia
|
||||||
|
# ===========================================================================
|
||||||
|
def test_anticipare_batte_posticipare_a_pari_totale():
|
||||||
|
"""Il finding di (C). Su ritorni a drift positivo deterministico e' un'identita' algebrica,
|
||||||
|
e serve come controllo di direzione: se uscisse il contrario, la simulazione e' rotta."""
|
||||||
|
n = 365 * 20
|
||||||
|
paths = np.full((1, n), 0.0004)
|
||||||
|
tot = 60_000.0 * CC.EURUSD
|
||||||
|
presto, tardi = np.zeros(n), np.zeros(n)
|
||||||
|
k = len(range(30, 365 * 5, 30))
|
||||||
|
for t in range(30, 365 * 5, 30):
|
||||||
|
presto[t] = tot / k
|
||||||
|
k2 = len(range(365 * 10, n, 30))
|
||||||
|
for t in range(365 * 10, n, 30):
|
||||||
|
tardi[t] = tot / k2
|
||||||
|
assert presto.sum() == pytest.approx(tardi.sum(), rel=1e-9)
|
||||||
|
cap_p, _ = DP.simulate(paths, presto)
|
||||||
|
cap_t, _ = DP.simulate(paths, tardi)
|
||||||
|
assert cap_p[0] > cap_t[0] * 1.5
|
||||||
|
|
||||||
|
|
||||||
|
def test_il_fattore_deluck_e_quello_misurato_non_quello_a_occhio():
|
||||||
|
"""Il ×0.6 del 25/07 era scelto a occhio ed e' stato misurato ×0.87-0.91 il 26/07."""
|
||||||
|
assert DP.DELUCK == 0.89
|
||||||
|
|
||||||
|
|
||||||
|
def test_hazard_giornaliero_ricompone_alla_probabilita_annua():
|
||||||
|
"""Il salto di venue: (1-haz)^365 deve dare (1-p). Sbagliare qui cambia tutte le colonne."""
|
||||||
|
for p in (0.005, 0.01, 0.02, 0.05):
|
||||||
|
haz = 1.0 - (1.0 - p) ** (1.0 / 365.0)
|
||||||
|
assert (1.0 - haz) ** 365 == pytest.approx(1.0 - p, rel=1e-9)
|
||||||
Reference in New Issue
Block a user