From 035bd086a2b51ed8caf1c6121f549987df9b16da Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Sun, 30 Aug 2026 17:38:35 +0000 Subject: [PATCH] =?UTF-8?q?usde:=20l'EV=20non=20puo'=20scegliere=20la=20qu?= =?UTF-8?q?ota=20=E2=80=94=20e=20aspettare=204=20settimane=20costa=20$1,33?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La decisione di quota e' dovuta lunedi' 31/08. Il risultato strutturale, dichiarato prima di guardare i numeri: l'EV e' LINEARE in q, quindi il suo argmax e' sempre un angolo (0% o 100%) e non puo' produrre una quota interna. Ogni quota intermedia nasce da un criterio sulla CODA, che va dichiarato e non ottimizzato (M8). Lo script quindi non sceglie: mette il prezzo accanto a ogni criterio candidato. Misure: - rendimento 3,26% annuo su n=4 finestre (3 pagate), banda bootstrap 95% [1,13% - 4,51%] su 200.000 ricampionamenti. Larga 3,4 punti su un livello di 3,2%: a n=4 il tasso non e' misurato, e' abbozzato. Il punto stimato si DERIVA da usde_watch.rendimento() invece di essere ridichiarato (P1), e la divergenza fra i due denominatori (0,06 punti) e' stampata, non appianata (P12). - l'hazard annuo di pareggio E' l'APR: la quota e' EV-positiva se e solo se si crede che Ethena stia sotto il 3,3%/anno di evento catastrofico. Numero ASSUNTO, non stimato, come il p di Deribit del 26/07 — il cui framework P=1-(1-p)^20 lo script riproduce prima di usarlo (10/18/33/64% contro 10/18/34/64% registrati). - il haircut 10% NON morde a nessuna quota testata, fino al 90%: il libro gira al 2,00% di margine ($11,37 su $568,64), tre ordini di grandezza sotto il collaterale. Il vincolo non e' il margine (D5: buco quantificato e innocuo). - il churn da depeg critico vale ~$52 di nozionale a quota 50%: secondo ordine. Il numero che decide (N10 — una data si giustifica col COSTO della misura): la conversione e' fee 0 e ~3 bps di spread, quindi la decisione e' reversibile a ~6 bps. Alzare a 50% fra quattro settimane invece che domani costa $1,33 di resa non incassata e porta il campione da 4 a 32 finestre, stringendo la banda da ~3,4 punti a ~1,2. Due letture del rischio che non si annullano (M28): l'USDE fa perdere soldi solo nel mondo in cui Ethena salta E Deribit sopravvive, quindi il rischio aggiunto e' di secondo ordine — ma e' un terzo strato sullo stesso conto, e N4 dice che un rischio di venue si compra con un secondo CONTO. La prima dice che costa poco, la seconda che non e' li' che si compra sicurezza. Verdetto a runtime: risoluzione insufficiente per alzare · nessun vincolo operativo · scala (la resa di oggi vale $16/anno, meno di UN mese di versamento: la leva binding resta il bonifico) · asimmetria (gia' a 24,2% l'evento emittente vale 3,1x il maxDD dell'intero libro) · reversibile e quasi gratis da rimandare. La scelta resta dell'operatore: lo script mette i prezzi, non ne sceglie uno (N4). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01E1gJmWPA4rEhmc6CQQuNZw --- docs/diary/2026-08-30-usde-quota.md | 128 +++++++++++ scripts/research/r0830_usde_quota.py | 303 +++++++++++++++++++++++++++ 2 files changed, 431 insertions(+) create mode 100644 docs/diary/2026-08-30-usde-quota.md create mode 100644 scripts/research/r0830_usde_quota.py diff --git a/docs/diary/2026-08-30-usde-quota.md b/docs/diary/2026-08-30-usde-quota.md new file mode 100644 index 0000000..46dff8a --- /dev/null +++ b/docs/diary/2026-08-30-usde-quota.md @@ -0,0 +1,128 @@ +# 2026-08-30 — La quota USDE: l'EV non puo' sceglierla, e aspettare costa $1,33 + +*Scritto 2026-08-30T17:32:57Z. Script: `scripts/research/r0830_usde_quota.py` (verdetto a runtime, N11). +Stato conto congelato nello script alla lettura del 2026-08-30T17:05Z.* + +## La domanda + +Il gate USDE-01 e' **chiuso IDONEO** dal 28/08. Resta la decisione di **quota**, rinviata +dall'operatore a **lunedi' 31/08**: quanto del conto tenere in USDE. + +## Il risultato strutturale — dichiarato prima di guardare i numeri + +Il valore atteso e' **lineare in q**. Un massimo lineare sta sempre in un **angolo** (0% o +100%). ⇒ **l'EV non puo' scegliere una quota interna**: puo' solo dirne il segno. Ogni quota +intermedia nasce da un criterio sulla **coda**, che va **dichiarato**, non ottimizzato (M8). + +Per questo lo script non sceglie: mette il **prezzo accanto a ogni criterio**. + +## (A) Il rendimento, col suo n + +| | | +|---|---| +| finestre misurabili | **4** (pagate 3) | +| per finestra | 0,00% · 3,80% · 4,51% · 4,51% | +| APR di `usde_watch` | **3,26%** annuo (tempo vero, 3,93 giorni) — *il numero che gira* | +| APR per-finestra | 3,21% (n finestre, 4,00 giorni) | +| banda bootstrap 95% | **[1,13% – 4,51%]** (200.000 ricampionamenti) | + +📌 La banda e' larga **3,4 punti** su un livello di 3,2%: a n=4 il tasso **non e' misurato, e' +abbozzato**. La divergenza fra i due denominatori (0,06 punti) e' **dichiarata, non appianata** +(P12), e il punto stimato si **deriva** da `usde_watch.rendimento()` invece di essere +ridichiarato (P1). + +## (B) L'hazard di pareggio — l'unico criterio che non richiede un criterio + +Perdita totale **non recuperabile**, resa 3,26%/anno ⇒ **hazard annuo di pareggio = APR**. + +- **3,26%/anno** (banda 1,13% – 4,51%) +- recupero di una perdita totale con la sola resa: **30,6 anni**, *indipendente dalla quota* +- ⇒ la quota e' EV-positiva **se e solo se** si crede che Ethena abbia meno del **3,3% annuo** + di probabilita' di evento catastrofico. Quel numero e' **assunto, non stimato** — esattamente + come il `p` di Deribit della decisione 26/07. + +## (C) Il rischio marginale, dato il 100%-Deribit gia' accettato + +Framework **ereditato** dalla decisione 26/07, non re-inventato: `P = 1-(1-p)^20`. Lo script lo +**riproduce** (M23: far riprodurre alla macchina il numero vecchio prima di pubblicarne uno nuovo): +10/18/33/64% contro i 10/18/34/64% registrati — **OK** a 1,5 punti. + +Due letture che **non si annullano**, e la contraddizione e' informazione (M28): + +- ✅ L'USDE fa perdere soldi **solo** nel mondo in cui Ethena salta **e Deribit sopravvive**. Nel + mondo in cui salta Deribit, USDC e USDE si perdono insieme e la quota e' irrilevante. Il rischio + **aggiunto** e' quindi di **secondo ordine** rispetto a quello gia' accettato. +- ⚠️ Ma e' un **terzo strato sullo stesso conto**, e **N4** dice che un rischio di venue si compra + con un **secondo CONTO**, non aumentando l'esposizione al primo. + +La prima dice che la quota costa poco; la seconda che **non e' li' che si compra sicurezza**. + +## (D) I vincoli operativi: nessuno morde + +Il libro gira al **2,00% di margine** ($11,37 su $568,64 di nozionale lordo). Al tetto di leva +1,00x servirebbero ~$41 su un collaterale di ~$2.000. + +| quota | margine utilizzabile | serve al tetto | morde? | +|---|---|---|---| +| 24,2% | $2.014 | $41 | no | +| 50,0% | $1.961 | $41 | no | +| 90,0% | $1.878 | $41 | no | + +⇒ **l'haircut 10% non morde a nessuna quota testata**, tre ordini di grandezza di margine. D5: +buco quantificato e innocuo. **Il vincolo non e' il margine.** + +Churn da depeg: a soglia critica 0,95 l'equity cala di 5%·q ⇒ a quota 50% sono **2,50%**, cioe' +~$52 di nozionale da girare. Il libro **de-risca mentre l'USDE e' a sconto e ri-carica al ritorno +del peg** — compra alto e vende basso — ma per importi di **secondo ordine**. Non e' il vincolo. + +## (E) Il menu dei criteri, ciascuno col suo prezzo + +| criterio | quota | USDE | resa $/a | drift libro | == €/mese | mesi di bonifico a rischio | +|---|---|---|---|---|---|---| +| **C0** status quo | 24,2% | $500 | $16 | 0,79% | 19 | **2,0** | +| **C1** ≤ maxDD del libro (7,94%) | 7,9% | $164 | $5 | 0,26% | 6 | 0,7 | +| **C2** ≤ 6 mesi di versamento | 72,7% | $1.500 | $49 | 2,37% | 58 | 6,0 | +| **C3** ≤ 12 mesi di versamento | 100% | $2.064 | $67 | 3,26% | 80 | 8,3 | +| **C4** tetto di ALLERTA di config | 50,0% | $1.032 | $34 | 1,63% | 40 | 4,1 | + +⚠️ **Due monete, non si sommano** (N8): *resa $/a* e' **cassa** vera; *== €/mese* e' l'equivalente +in **versamento** via l'equivalenza registrata (100 €/mese == +4,07%/anno di drift), che e' una +capitalizzazione a 10 anni, **non un flusso**. Semplificazione dichiarata: EUR e USD 1:1 (a ~1,08 +sposta i mesi di ~8%, non muove nessuna soglia). + +## (F) Cosa costa aspettare — il numero decisivo + +La conversione e' su spot `USDE_USDC`: **fee 0, spread ~3 bps** ⇒ andata+ritorno **~6 bps**. La +decisione e' **reversibile a costo quasi nullo** — non e' una porta a senso unico e va decisa come +tale. + +| attendere | finestre | andata/ritorno | resa persa | costo totale | +|---|---|---|---|---| +| 0 sett | 4 | $0,32 | $0,00 | $0,32 | +| 2 sett | 18 | $0,32 | $0,67 | $0,98 | +| **4 sett** | **32** | $0,32 | $1,33 | **$1,65** | +| 8 sett | 60 | $0,32 | $2,66 | $2,98 | + +📌 **Portare la quota a 50% fra quattro settimane invece che domani costa $1,33 di resa non +incassata e compra 8× le finestre** (n da 4 a 32), stringendo la banda di ~2,8× — da ~3,4 punti a +~1,2. **$1,33 per sapere se il tasso e' 1,1% o 4,5%.** E' l'applicazione diretta di N10: una data +si giustifica col **costo della misura**, non con la sua difficolta' percepita. + +## Verdetto (calcolato a runtime) + +- **RISOLUZIONE INSUFFICIENTE PER ALZARE** — n=4, banda larga 3,4 punti. Alzare ora e' comprare un + tasso che non e' ancora misurato. +- **NESSUN VINCOLO OPERATIVO** — l'haircut non morde a nessuna quota; il vincolo non e' il margine. +- **SCALA** — la resa alla quota di oggi vale **$16/anno**, meno di **un mese** di versamento. La + leva binding resta il **bonifico** (N1/N3), non la quota. +- **ASIMMETRIA** — gia' a 24,2% l'evento emittente vale **3,1× il maxDD dell'intero libro** (7,94%), + che e' il rischio che il progetto ha passato due anni a limare. +- **REVERSIBILE E QUASI GRATIS DA RIMANDARE** — $1,33 + 6 bps per 8× il campione. + +## Cosa NON dice questa analisi + +- Non stima `p` per Ethena. Nessuna fonte qui lo misura: e' **assunto**, e lo script lo tratta come + parametro, non come risultato. +- Non sceglie la quota. **La scelta e' dell'operatore** (N4). +- Il tasso osservato viene da un metodo **indiretto** (delta di equity al netto dei trade): il + gateway non espone il Transaction Log. Vale col suo limite gia' dichiarato in `usde_watch`. diff --git a/scripts/research/r0830_usde_quota.py b/scripts/research/r0830_usde_quota.py new file mode 100644 index 0000000..4622cd9 --- /dev/null +++ b/scripts/research/r0830_usde_quota.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python +"""r0830_usde_quota.py — la decisione di QUOTA USDE, dovuta lunedi' 31/08. SOLA LETTURA. + +COSA DECIDE E COSA NO. Il gate USDE-01 e' CHIUSO IDONEO (28/08): la pista e' aperta. Resta +da scegliere *quanto* del conto tenere in USDE. Questo script NON sceglie: mette accanto ai +criteri candidati la quota che ciascuno implica e il suo prezzo, perche' la scelta e' +dell'operatore (N4: il rischio emittente non e' diversificabile dagli sleeve). + +IL RISULTATO STRUTTURALE, DICHIARATO PRIMA DI GUARDARE I NUMERI (N6/P6). Il valore atteso +e' LINEARE in q: EV(q) = q*E*(apr - hazard*...). Un massimo lineare sta sempre in un ANGOLO +(0% o 100%). ⇒ **l'EV non puo' scegliere una quota interna**: puo' solo dire se il segno e' +positivo. La quota interna nasce SOLO da un criterio sulla CODA, e quel criterio va +dichiarato, non ottimizzato (M8: un argmax dentro un plateau non e' una decisione). + +Quindi la struttura e': + (A) il rendimento osservato, CON LA SUA BANDA e il suo n — un pagamento non e' un tasso; + (B) l'hazard di PAREGGIO: sopra quello ogni quota e' EV-negativa. Criterio senza criterio; + (C) il rischio MARGINALE, dato che il 100%-Deribit e' gia' stato accettato il 26/07; + (D) i vincoli operativi: il haircut morde? il depeg costa churn? + (E) il menu dei criteri di coda, ciascuno con la quota che implica e cosa costa in resa. + +LENTI (N8: quando le leve hanno ordini di grandezza diversi, dirlo). La resa si legge in DUE +monete e NON vanno sommate: (1) CASSA, gli euro veri all'anno; (2) DRIFT-EQUIVALENTE, via +l'equivalenza registrata in CLAUDE.md 100 EUR/mese == +4,07%/anno di drift, che e' una +capitalizzazione a 10 anni, non un flusso. + +SEMPLIFICAZIONE DICHIARATA: EUR e USD trattati 1:1. A ~1,08 sposta i "mesi di versamento" di +~8% (su 2,0 mesi = 0,16 mesi): non cambia nessuna soglia di questa tabella. + + uv run python scripts/research/r0830_usde_quota.py +""" +from __future__ import annotations + +import importlib.util +import json +import sys +from pathlib import Path + +import numpy as np + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) + +from src.live import usde as U # noqa: E402 + +# P1: il tasso puntuale si DERIVA dal sorvegliante che gira, non si ridichiara qui. Il bootstrap +# (banda) resta locale perche' usde_watch non lo fa — e i due denominatori NON coincidono: +# rendimento() divide per il TEMPO VERO fra le letture, questo per il NUMERO di finestre. +# La divergenza si stampa invece di essere appianata (P12). +_spec = importlib.util.spec_from_file_location( + "usde_watch", ROOT / "scripts" / "live" / "usde_watch.py") +_uw = importlib.util.module_from_spec(_spec) +sys.modules["usde_watch"] = _uw +_spec.loader.exec_module(_uw) + +WATCH = ROOT / "data" / "live" / "usde_watch.jsonl" + +# --- stato del conto, letto dal venue il 2026-08-30T17:05Z e CONGELATO qui per riproducibilita' --- +EQ_USDC = 1563.66151017 +AVAIL_USDC = 1552.28862113 # -> margine in uso = equity - available +EQ_USDE = 500.175691 +GROSS_NOTIONAL = 355.327065 + 213.317387 + +# --- framework di rischio EREDITATO dalla decisione 26/07, non re-inventato --- +# "100% Deribit fino a $20k: accettato P(perso tutto) 10/18/34/64% a p=0,5/1/2/5%" +# riprodotto esattamente da P = 1-(1-p)^T con T=20. Il test lo verifica. +ORIZZONTE_ANNI = 20 +P_DERIBIT = [0.005, 0.01, 0.02, 0.05] + +# --- piano di versamento: la leva CERTA (N1). Baseline dichiarata in CLAUDE.md --- +VERSAMENTO_BASE = 250.0 # EUR/mese, piano corrente +VERSAMENTO_ALT = 500.0 # EUR/mese, l'alternativa gia' misurata +EUR_PER_100_DRIFT = 4.07 / 100.0 # 100 EUR/mese == +4,07%/anno di drift + +# --- ancore del libro, dalla banda onesta di CLAUDE.md §2 --- +MAXDD_BOOK_HI = 0.0794 # maxDD FULL book, estremo alto della banda [5,99-7,94] +MARGINE_PERP = 0.02 # tasso di margine iniziale osservato (verificato sotto) + +SPREAD_BPS = 3.0 # spot USDE_USDC, osservato sul venue il 26/08 (fee 0) +N_BOOT = 200_000 # M23: la risoluzione del MC si dichiara +RNG = np.random.default_rng(20260830) + + +def finestre(records: list[dict]) -> list[float]: + """I reward per finestra, in USDE. Uno zero E' un'osservazione (P5), non un buco.""" + return [float(r["reward_stimato"]) for r in records + if r.get("reward_stimato") is not None and not r.get("con_trade")] + + +def apr_banda(rew: list[float], base: float) -> dict: + """APR puntuale e banda bootstrap sulle finestre. Con n piccolo la banda E' il risultato.""" + n = len(rew) + if n == 0: + return dict(apr=None, lo=None, hi=None, n=0) + a = np.array(rew, dtype=float) + punt = a.mean() / base * 365.0 + draws = RNG.choice(a, size=(N_BOOT, n), replace=True).mean(axis=1) / base * 365.0 + lo, hi = np.percentile(draws, [2.5, 97.5]) + return dict(apr=punt, lo=float(lo), hi=float(hi), n=n, + per_finestra=[x / base * 365.0 for x in rew]) + + +def p_rovina(p: float, anni: int = ORIZZONTE_ANNI) -> float: + return 1.0 - (1.0 - p) ** anni + + +def main() -> int: + recs = [json.loads(l) for l in WATCH.read_text().splitlines() if l.strip()] + c = U.cfg() + px = float(recs[-1]["px"]) + usde_usd, _ = U.valuta(EQ_USDE, px) + E = EQ_USDC + usde_usd + q0 = usde_usd / E + + rew = finestre(recs) + R = apr_banda(rew, EQ_USDE) + UFF = _uw.rendimento(recs) # il numero che gira, derivato + R["apr_uff"] = UFF["apr"] + R["giorni_uff"] = UFF["giorni"] + + print("=" * 79) + print(" ANALISI QUOTA USDE — decisione dovuta 2026-08-31") + print(f" config: {c['fonte']} · stato conto congelato al 2026-08-30T17:05Z") + print("=" * 79) + + # ---------------------------------------------------------------- (A) il rendimento + print("\n(A) IL RENDIMENTO, COL SUO n") + print(f" finestre misurabili : {R['n']} (pagate {sum(1 for x in rew if x > 0)})") + print(" per finestra : " + ", ".join(f"{x*100:.2f}%" for x in R["per_finestra"])) + print(f" APR di usde_watch : {R['apr_uff']*100:.2f}% annuo" + f" (denominatore = tempo vero, {R['giorni_uff']:.2f} giorni) <- IL NUMERO CHE GIRA") + print(f" APR per-finestra : {R['apr']*100:.2f}% annuo" + f" (denominatore = n finestre, {R['n']}.00 giorni)") + print(f" divergenza : {abs(R['apr_uff']-R['apr'])*100:.2f} punti — i due" + f" denominatori differiscono di {R['n']-R['giorni_uff']:.2f} giorni. Dichiarata (P12),") + print(" non appianata: sotto si usa il puntuale di usde_watch.") + print(f" banda bootstrap 95% : [{R['lo']*100:.2f}% - {R['hi']*100:.2f}%]" + f" (n={R['n']}, {N_BOOT:,} ricampionamenti)") + APR = R["apr_uff"] + larghezza = R["hi"] - R["lo"] + print(f" ⚠️ la banda e' larga {larghezza*100:.2f} punti su un livello di " + f"{R['apr']*100:.2f}% (per-finestra): a n={R['n']} il tasso NON e' misurato, e' abbozzato.") + + # ---------------------------------------------------------------- (B) hazard di pareggio + print("\n(B) L'HAZARD DI PAREGGIO — l'unico criterio che non richiede un criterio") + print(f" Perdita totale non recuperabile, resa {APR*100:.2f}%/anno.") + print(f" ⇒ hazard annuo di pareggio = APR = {APR*100:.2f}%/anno") + print(f" (banda: {R['lo']*100:.2f}% - {R['hi']*100:.2f}%)") + print(f" ⇒ recupero di una perdita totale con la sola resa: {1/APR:.1f} anni" + f" — INDIPENDENTE dalla quota.") + print(f" Letto sul verso della decisione: la quota e' EV-positiva SE E SOLO SE si crede") + print(f" che Ethena abbia meno del {APR*100:.1f}% annuo di probabilita' di evento") + print(f" catastrofico. Quel numero e' ASSUNTO, non stimato — come il p di Deribit (26/07).") + + # ---------------------------------------------------------------- (C) rischio marginale + print("\n(C) IL RISCHIO MARGINALE, dato il 100%-Deribit gia' accettato (26/07)") + print(f" Framework ereditato: P = 1-(1-p)^{ORIZZONTE_ANNI}. Riproduzione della riga 26/07:") + riga = " p annuo ".ljust(14) + "".join(f"{p*100:>7.1f}%" for p in P_DERIBIT) + print(riga) + print(" P(perso tutto)".ljust(14) + "".join(f"{p_rovina(p)*100:>7.0f}%" for p in P_DERIBIT)) + print(" atteso 26/07 " + "".join(f"{v:>7}" for v in ("10%", "18%", "34%", "64%"))) + ok_repro = all(abs(p_rovina(p) * 100 - v) < 1.5 + for p, v in zip(P_DERIBIT, (10, 18, 34, 64))) + print(f" riproduzione : {'OK' if ok_repro else 'DIVERGE'} (tolleranza 1,5 punti)") + print(" ⇒ L'USDE fa perdere soldi SOLO nel mondo in cui Ethena salta E Deribit sopravvive.") + print(" Nel mondo in cui salta Deribit, USDC e USDE si perdono insieme: la quota e'") + print(" irrilevante. Il rischio AGGIUNTO dalla quota e' quindi di SECONDO ordine") + print(" rispetto a quello gia' accettato — ma e' un TERZO strato sullo STESSO conto,") + print(" e N4 dice che un rischio di venue si compra con un secondo CONTO, non") + print(" aumentando l'esposizione al primo. Le due letture non si annullano: la prima") + print(" dice che la quota costa poco, la seconda che non e' li' che si compra sicurezza.") + + # ---------------------------------------------------------------- (D) vincoli operativi + print("\n(D) I VINCOLI OPERATIVI — il haircut morde? il depeg costa?") + margine_uso = EQ_USDC - AVAIL_USDC + tasso_margine = margine_uso / GROSS_NOTIONAL + print(f" nozionale lordo : ${GROSS_NOTIONAL:,.2f}") + print(f" margine in uso : ${margine_uso:,.2f} ⇒ tasso {tasso_margine*100:.2f}%") + print(f" tetto del libro : leva lorda 1,00x ⇒ nozionale max ${E:,.0f}" + f" ⇒ margine max ~${E*tasso_margine:,.0f}") + print(f" haircut USDE {c['haircut']:.0%} : il {1-c['haircut']:.0%} dell'USDE fa margine") + print("\n quota USDC USDE margine utilizzabile serve al tetto morde?") + binding = [] + for q in (q0, 0.25, 0.50, 0.75, 0.90): + usde_q, usdc_q = E * q, E * (1 - q) + util = usdc_q + usde_q * (1 - c["haircut"]) + serve = E * tasso_margine + morde = util < serve + binding.append(morde) + print(f" {q:5.1%} ${usdc_q:9,.0f} ${usde_q:9,.0f} ${util:14,.0f}" + f" ${serve:12,.0f} {'SI' if morde else 'no'}") + print(f" ⇒ il haircut NON morde a nessuna quota testata: il libro gira al" + f" {tasso_margine*100:.1f}% di margine,") + print(" tre ordini di grandezza sotto il collaterale. D5: buco quantificato e innocuo.") + depeg_eq = (1.0 - c["depeg_crit"]) + print(f"\n churn da depeg: a soglia critica {c['depeg_crit']} l'equity cala di" + f" {depeg_eq:.0%}*q,") + for q in (q0, 0.50): + print(f" a quota {q:.1%} ⇒ equity {depeg_eq*q*100:.2f}% ⇒ target di posizione" + f" -{depeg_eq*q*100:.2f}% (~${E*depeg_eq*q*1.0:,.0f} di nozionale da girare)") + print(" ⇒ il libro de-risca mentre l'USDE e' a sconto e ri-carica al ritorno del peg:") + print(" compra alto e vende basso, ma per importi di secondo ordine. Non e' il vincolo.") + + # ---------------------------------------------------------------- (E) il menu dei criteri + print("\n(E) IL MENU DEI CRITERI DI CODA — ciascuno con la quota che implica e il suo prezzo") + print(" (l'EV e' lineare in q: NON produce un ottimo interno. Questi si', ma sono") + print(" scelte di tolleranza, non misure.)") + criteri = [ + ("C0 status quo, nessuna decisione", + q0, "la quota di oggi, arrivata li' per la taglia del test di eligibilita'"), + ("C1 <= maxDD del libro (7,94%)", + MAXDD_BOOK_HI, "un evento emittente non deve superare cio' che la strategia gia' fa"), + ("C2 <= 6 mesi di versamento", + 6 * VERSAMENTO_BASE / E, f"a {VERSAMENTO_BASE:.0f} EUR/mese: il piano arretra <= mezzo anno"), + ("C3 <= 12 mesi di versamento", + 12 * VERSAMENTO_BASE / E, f"a {VERSAMENTO_BASE:.0f} EUR/mese: il piano arretra <= un anno"), + ("C4 tetto di ALLERTA di config", + c["quota_max_frac"], "non e' un criterio: e' la soglia oltre cui il watch grida"), + ] + print(f"\n {'criterio':38s} {'quota':>7s} {'USDE $':>9s} {'resa $/a':>9s}" + f" {'drift bk':>9s} {'== EUR/m':>9s} {'perdita':>9s} {'mesi vers':>9s}") + for nome, q, _ in criteri: + q = min(max(q, 0.0), 1.0) + usde_q = E * q + resa = usde_q * APR + drift = q * APR + eur_mese = drift / EUR_PER_100_DRIFT * 100.0 + mesi = usde_q / VERSAMENTO_BASE + print(f" {nome:38s} {q:6.1%} {usde_q:9,.0f} {resa:9,.2f}" + f" {drift*100:8.2f}% {eur_mese:9,.0f} {usde_q:9,.0f} {mesi:9.1f}") + print("\n note sulle colonne (N8: due monete, non si sommano):") + print(" · 'resa $/a' = CASSA vera all'anno, alla taglia di oggi") + print(" · '== EUR/m' = quanto varrebbe in VERSAMENTO l'equivalente di drift, lente 10 anni") + print(" · 'mesi vers' = quanti mesi di bonifico brucia una perdita TOTALE") + + # ---------------------------------------------------------------- (F) attesa e reversibilita' + print("\n(F) COSA COSTA ASPETTARE — N10: una data si giustifica col COSTO della misura,") + print(" non con la sua difficolta' percepita.") + print(f" La conversione e' su spot {U.SPOT}: fee 0, spread osservato ~{SPREAD_BPS:.0f} bps") + print(f" (26/08) ⇒ andata+ritorno ~{2*SPREAD_BPS:.0f} bps. La decisione e' REVERSIBILE" + " a costo quasi nullo:") + print(" non e' una porta a senso unico, e va decisa come tale.") + print(f"\n {'attendere':>10s} {'finestre':>9s} {'costo andata/ritorno':>21s}" + f" {'resa persa':>11s} {'costo totale':>13s}") + for sett, q_target in ((0, 0.50), (2, 0.50), (4, 0.50), (8, 0.50)): + incremento = E * q_target - usde_usd + giorni = sett * 7 + persa = incremento * APR * giorni / 365.0 + rt = incremento * 2 * SPREAD_BPS / 10_000.0 + n_fin = R["n"] + giorni + print(f" {sett:>7d} sett {n_fin:>9d} {rt:>20,.2f}$ {persa:>10,.2f}$" + f" {rt+persa:>12,.2f}$") + print(f"\n ⇒ portare la quota da {q0:.1%} a 50% fra QUATTRO settimane invece che domani") + inc = E * 0.50 - usde_usd + costo_4 = inc * APR * 28 / 365.0 + print(f" costa ${costo_4:,.2f} di resa non incassata, e compra" + f" {R['n']+28}/{R['n']} = {(R['n']+28)/R['n']:.0f}x le finestre.") + print(" A n=32 la banda si stringe di ~sqrt(8) ≈ 2,8x: da ~3,4 punti a ~1,2.") + print(f" ${costo_4:,.2f} per sapere se il tasso e' 1,1% o 4,5% e' il miglior rapporto") + print(" misura/costo che il progetto abbia incontrato (cfr. i due gate d'attesa da mesi") + print(" eliminati in una sera perche' nessuno aveva contato i secondi, N10).") + + # ---------------------------------------------------------------- verdetto a runtime (N11) + print("\n" + "=" * 79) + print(" VERDETTO calcolato a runtime") + print("=" * 79) + eur_mese_q0 = q0 * APR / EUR_PER_100_DRIFT * 100.0 + cassa_q0 = usde_usd * APR + mesi_q0 = usde_usd / VERSAMENTO_BASE + inc50 = E * 0.50 - usde_usd + costo_attesa_4s = inc50 * APR * 28 / 365.0 + verdetti = [] + if R["n"] < 8: + verdetti.append(f"RISOLUZIONE INSUFFICIENTE PER ALZARE: n={R['n']} finestre, banda" + f" [{R['lo']*100:.2f}%-{R['hi']*100:.2f}%] larga {larghezza*100:.1f} punti." + f" Alzare la quota ora e' comprare un tasso che non e' ancora misurato.") + if not any(binding): + verdetti.append("NESSUN VINCOLO OPERATIVO: il haircut non morde a nessuna quota" + " testata; il vincolo non e' il margine.") + if cassa_q0 < VERSAMENTO_BASE: + verdetti.append(f"SCALA: la resa alla quota di oggi vale ${cassa_q0:,.0f}/anno," + f" cioe' meno di UN mese di versamento ({VERSAMENTO_BASE:.0f})." + f" La leva binding resta il bonifico (N1/N3), non la quota.") + if MAXDD_BOOK_HI < q0: + verdetti.append(f"ASIMMETRIA: gia' alla quota di oggi ({q0:.1%}) l'evento emittente" + f" vale {q0/MAXDD_BOOK_HI:.1f}x il maxDD dell'intero libro" + f" ({MAXDD_BOOK_HI:.2%}), che e' il rischio che il progetto" + f" ha passato due anni a limare.") + verdetti.append( + f"REVERSIBILE E QUASI GRATIS DA RIMANDARE: alzare a 50% fra 4 settimane invece che" + f" domani costa ${costo_attesa_4s:,.2f} di resa e {2*SPREAD_BPS:.0f} bps di andata/ritorno," + f" e porta il campione da {R['n']} a {R['n']+28} finestre.") + for v in verdetti: + print(f" · {v}") + print(f"\n Quota di oggi {q0:.1%} = ${usde_usd:,.0f} · resa ${cassa_q0:,.0f}/anno" + f" · == {eur_mese_q0:,.0f} EUR/mese di drift · {mesi_q0:.1f} mesi di bonifico a rischio") + print(" La SCELTA resta dell'operatore: questo script mette il prezzo accanto a ogni") + print(" criterio, non ne sceglie uno (N4, e la colonna 'cosa la riapre' della §3).") + return 0 + + +if __name__ == "__main__": + sys.exit(main())