research(rendita): curva capitale->book eseguibile, muro della rendita, asimmetria funded
Ondata "rendita passiva" (2° filone del 2026-07-25). Book live, pesi, config e cron INVARIATI: questa sessione non tocca nulla del path di esecuzione. 1) DIFETTO DI ESEGUIBILITA' su GTAA01 (5° sleeve del book attivo). gtaa.py modella 2bps proporzionali e lo sleeve e' documentato "switch mensile/basso turnover": entrambe false. Il vol-target ribilancia OGNI GIORNO su 6 gambe e IB ha un pavimento FISSO per ordine (min(max($0.35, $0.0035/az), 1% valore)) = ~$530/anno indipendenti dal capitale. A $600: CAGR -3.5% / Sharpe -0.55 (vs +5.5%/0.77 modellato); negativo fino a ~$3k. Con banda $50 + cadenza settimanale (plateau, non argmax) torna a Sh 0.52 / CAGR 3.0%. -> Sharpe onesto di GTAA01 ~0.55-0.64, non 0.77. REGOLA NUOVA: il costo di un venue va modellato nella sua FORMA (fisso vs proporzionale), non solo nel livello. Controprova su Deribit (proporzionale): TP01 realistico = modellato da ~$500 in su. 2) LA RENDITA NON E' capitale x CAGR. Il muro del 24/07 (EUR 122k) ignorava il rischio di sequenza. Prelievo perpetuo (P(cap a 20a >= cap iniziale) >= 90%) sul path live de-luckato: 5.98% -> $497k; banda onesta $232k-$497k (il de-luck x0.6 sopra il path live rischia di contare due volte la degradazione SKH01). Invariante di lente: la rendita perpetua vale ~45-55% del CAGR -> ogni muro calcolato come target/CAGR sbaglia di ~2x. 3) DIVERSIFICARE NON CREA REDDITO a pari nozionale (5.98% -> 6.35%); libera BUDGET DI RISCHIO: a iso-rischio (vol 15%) 7.51%/$395k -> 9.25%/$321k. L'ipotesi "piu' capitale -> piu' sleeve -> CAGR super-lineare" e' REFUTATA nella forma forte (curva Sharpe piatta da $600 a $200k). 4) ASIMMETRIA CAPITALE-PROPRIO vs FUNDED. Su conto funded il capitale e' $100k -> gli sleeve STAT-MODE per taglia (XS01) diventano eseguibili, e le regole prop passano sul DRAWDOWN, non sul CAGR. Aggiungendo XS01: P(pass) HYRO 44.4->54.7%, funded P(vivo 1a) 18.9->57.6%. 5) LA VIA: i 600 euro come BIGLIETTO. Scala di conti funded, 36 mesi. Problema mai studiato: N conti sullo STESSO book bustano INSIEME -> serve sleeve diversi su conti diversi. Vince il MISTO, non gli estremi; il book live attuale e' la politica PEGGIORE. Verdetto in banda: P(arrivare a 50 EUR/g in 3 anni) 2-21%, P(bruciare i 600) 33-78% -> scommessa a coda destra, non una rendita. 6) ADDENDUM "10k su IB": allocazione fra venue. 94% su IB piu' che dimezza il reddito (EUR 0.93/g vs 2.16/g). La leva che convertirebbe lo Sharpe in reddito e' chiusa dai costi (Reg-T 2x + margine 5.5%) -> vince tutto-Deribit a 1.33x. GTAA01 sui 10k rende EUR 0.50/g sopra la liquidita' ferma, pagati con un maxDD del 10%. Bug catturato in sessione: base di prelievo funded aggiornata giornalmente come HWM mobile -> payout $230/anno invece di ~$7.000; regole vere = max-loss STATICO dal saldo iniziale. Test di regressione cablato. Test: 214 pass (200 preesistenti + 14 nuovi in tests/test_capcurve.py). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,217 @@
|
||||
"""r0725_ib10k — E SE METTO 10K SU IB? Lo split ottimale Deribit/IB (2026-07-25).
|
||||
|
||||
Domanda dell'operatore, posta dopo l'ondata rendita: "se metto 10K in IB?".
|
||||
Non e' una domanda su GTAA01: e' una domanda di ALLOCAZIONE FRA DUE VENUE, e le due
|
||||
gambe hanno profili opposti:
|
||||
|
||||
* DERIBIT (TP01+SKH01): Sharpe ~0.98 de-luck, CAGR ~11%, vol 11.4% — e' il MOTORE di reddito.
|
||||
* IB (GTAA01): Sharpe ~0.6, CAGR ~3.5-5%, vol ~7% — e' un DIVERSIFICATORE (corr ~+0.10),
|
||||
e a $600 era negativo per il pavimento di commissione; a $10k il pavimento diventa
|
||||
trascurabile (0.02% per ordine invece dell'1-3%) -> **il deposito SBLOCCA lo sleeve**.
|
||||
|
||||
Tre effetti che si sommano e che vanno contati INSIEME:
|
||||
1. sblocco di GTAA01 (il pavimento IB smette di mordere);
|
||||
2. spostamento del baricentro su un venue a CAGR basso (se ci va TUTTO, il reddito CALA);
|
||||
3. **fisco diverso**: plusvalenze ETF 26% vs crypto 33% (L.199/2025) -> un euro lordo su IB
|
||||
vale il 10% in piu' netto di un euro lordo su Deribit.
|
||||
(+ pavimento alternativo: la liquidita' IB non investita rende interesse ~2% EUR / ~4% USD.)
|
||||
|
||||
Output: la curva dello split, giudicata su TRE metriche diverse perche' danno risposte diverse —
|
||||
reddito atteso netto, rendita perpetua (preservazione del capitale), e reddito a ISO-RISCHIO.
|
||||
|
||||
ONESTA': (a) SKH01 sul path live, de-luck x0.6 (lente di decisione del progetto);
|
||||
(b) GTAA01 col costo IB REALE (pavimento fisso) e banda/cadenza scelte sul plateau;
|
||||
(c) le aliquote sono l'assunzione dichiarata 26%/33% — da verificare col commercialista,
|
||||
NON un parere fiscale; (d) i numeri di GTAA01 sono su ~30 anni, quelli crypto su ~7.
|
||||
|
||||
Uso: `uv run python scripts/research/r0725_ib10k.py`
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
sys.path.insert(0, str(ROOT / "scripts" / "research"))
|
||||
|
||||
from src.portfolio.portfolio import combine_outer, metrics # noqa: E402
|
||||
|
||||
import r0724_skh_live_weight as skl # noqa: E402
|
||||
from r0725_capcurve import (EURUSD, gtaa_banded, metrics as _m, survival, # noqa: E402
|
||||
tp01_realistic)
|
||||
|
||||
TAX_CRYPTO = 0.33 # L.199/2025 dal 1/1/2026
|
||||
TAX_EQUITY = 0.26 # plusvalenze su ETF (assunzione dichiarata)
|
||||
IB_CASH_RATE = 0.02 # interesse su liquidita EUR non investita (IBKR, sopra $10k)
|
||||
IB_MARGIN_RATE = 0.055 # costo del margine IB in USD (benchmark + spread) — assunzione dichiarata
|
||||
DERIBIT_FUNDING = 0.02 # funding medio dei perp Deribit (ricerca 24/07: ciclo 0-4%/anno)
|
||||
DELUCK = 0.6
|
||||
DERIBIT_NOW = 600.0 # cio' che c'e' oggi sul conto Deribit
|
||||
BOOK_START = pd.Timestamp("2019-03-14", tz="UTC")
|
||||
|
||||
|
||||
def _d(s: pd.Series) -> pd.Series:
|
||||
s = s.dropna().sort_index()
|
||||
if s.index.tz is None:
|
||||
s.index = s.index.tz_localize("UTC")
|
||||
return s
|
||||
|
||||
|
||||
def _deluck(s: pd.Series) -> pd.Series:
|
||||
return s - (1.0 - DELUCK) * float(s.mean())
|
||||
|
||||
|
||||
def gtaa_best_band(alloc: float) -> tuple[float, int, dict]:
|
||||
"""Config di costo per GTAA al capitale dato, scelta sul PLATEAU (weekly, banda 25-100):
|
||||
non e' un parametro di segnale, si sceglie sul costo e si dichiara."""
|
||||
best = None
|
||||
for every in (5, 21):
|
||||
for band in (25.0, 50.0, 100.0):
|
||||
if band > alloc / 6:
|
||||
continue
|
||||
m = metrics(gtaa_banded(alloc, band, every))
|
||||
if best is None or m["sharpe"] > best[2]["sharpe"]:
|
||||
best = (band, every, m)
|
||||
if best is None: # capitale troppo piccolo per una banda
|
||||
best = (1.0, 21, metrics(gtaa_banded(alloc, 1.0, 21)))
|
||||
return best
|
||||
|
||||
|
||||
def build(total: float, f_ib: float) -> dict:
|
||||
"""Book con frazione f_ib del capitale su IB (GTAA01) e il resto su Deribit (TP01/SKH01)."""
|
||||
a_ib, a_der = total * f_ib, total * (1.0 - f_ib)
|
||||
skh = _d(skl.skh_book(0, "hourly"))
|
||||
cols, w = {}, {}
|
||||
if a_der > 1.0:
|
||||
cols["TP01"] = _deluck(_d(tp01_realistic(a_der * 0.75)))
|
||||
cols["SKH01"] = _deluck(skh)
|
||||
w["TP01"] = (1.0 - f_ib) * 0.75
|
||||
w["SKH01"] = (1.0 - f_ib) * 0.25
|
||||
band = every = None
|
||||
if a_ib > 1.0:
|
||||
band, every, _ = gtaa_best_band(a_ib)
|
||||
cols["GTAA01"] = _deluck(_d(gtaa_banded(a_ib, band, every)))
|
||||
w["GTAA01"] = f_ib
|
||||
combo = combine_outer(cols, w, lo=BOOK_START)
|
||||
m = metrics(combo)
|
||||
vol = float(combo.std() * np.sqrt(365))
|
||||
# fisco misto: la quota IB e' tassata al 26%, la quota Deribit al 33%
|
||||
tax_blend = f_ib * TAX_EQUITY + (1.0 - f_ib) * TAX_CRYPTO
|
||||
eur_day = total * m["cagr"] * (1.0 - tax_blend) / EURUSD / 365.0
|
||||
return dict(metrics=m, vol=vol, combo=combo, tax=tax_blend, eur_day=eur_day,
|
||||
band=band, every=every, a_ib=a_ib, a_der=a_der)
|
||||
|
||||
|
||||
def perp_rate(combo: pd.Series, k: float = 1.0, n: int = 1500) -> float:
|
||||
"""Prelievo perpetuo (P(cap a 20a >= cap iniziale) >= 90%)."""
|
||||
r = combo.values.astype(float)
|
||||
lo, hi = 0.0, 0.35
|
||||
for _ in range(12):
|
||||
mid = (lo + hi) / 2
|
||||
if survival(r, 1e6, 1e6 * mid, 20, k, n_paths=n)["p10_end"] >= 1e6:
|
||||
lo = mid
|
||||
else:
|
||||
hi = mid
|
||||
return lo
|
||||
|
||||
|
||||
def main() -> None:
|
||||
total = DERIBIT_NOW + 10_000.0 * EURUSD # $600 + EUR 10k
|
||||
print("=" * 100)
|
||||
print(f" SE METTO 10K SU IB — capitale totale ${total:,.0f} "
|
||||
f"(= $600 Deribit + EUR 10.000)")
|
||||
print("=" * 100)
|
||||
print(f" fisco assunto: ETF {TAX_EQUITY:.0%} / crypto {TAX_CRYPTO:.0%} | de-luck x{DELUCK} | "
|
||||
f"SKH01 path live")
|
||||
print(f" pavimento alternativo: liquidita' IB non investita ~{IB_CASH_RATE:.0%}/anno EUR "
|
||||
f"= EUR {total/EURUSD*IB_CASH_RATE*(1-TAX_EQUITY)/365:.2f}/g\n")
|
||||
|
||||
print(f" {'quota su IB':>12} {'GTAA banda':>12} {'Sharpe':>7} {'CAGR':>7} {'vol':>6} "
|
||||
f"{'maxDD':>7} {'fisco':>6} {'EUR/g':>7} {'rendita':>8} {'EUR/g rend.':>12}")
|
||||
rows = []
|
||||
for f_ib in (0.0, 0.25, 0.50, 0.75, 0.90, 0.94, 1.0):
|
||||
b = build(total, f_ib)
|
||||
pr = perp_rate(b["combo"])
|
||||
eur_perp = total * pr * (1.0 - b["tax"]) / EURUSD / 365.0
|
||||
lbl = "—" if b["band"] is None else f"${b['band']:.0f}/{ 'sett' if b['every']==5 else 'mens'}"
|
||||
print(f" {f_ib:>11.0%} {lbl:>12} {b['metrics']['sharpe']:>7.2f} "
|
||||
f"{b['metrics']['cagr']:>7.1%} {b['vol']:>6.1%} {b['metrics']['maxdd']:>7.1%} "
|
||||
f"{b['tax']:>6.0%} {b['eur_day']:>7.2f} {pr:>8.2%} {eur_perp:>12.2f}")
|
||||
rows.append((f_ib, b, pr))
|
||||
print("\n ('quota su IB' 94% = mettere i 10k su IB e lasciare i $600 dove sono;")
|
||||
print(" 'rendita' = prelievo perpetuo al 90% di preservazione del capitale.)")
|
||||
|
||||
# --- ISO-RISCHIO: e' qui che un diversificatore a basso CAGR si giudica
|
||||
print("\n" + "-" * 100)
|
||||
print(" A ISO-RISCHIO (ogni split portato a vol 15% con la leva) — il confronto corretto")
|
||||
print("-" * 100)
|
||||
print(f" {'quota su IB':>12} {'leva':>6} {'CAGR iso':>9} {'EUR/g iso':>10} "
|
||||
f"{'rendita iso':>12} {'EUR/g rend.iso':>15}")
|
||||
best = None
|
||||
for f_ib, b, _ in rows:
|
||||
k = min(0.15 / b["vol"], 3.0)
|
||||
pr = perp_rate(b["combo"], k=k)
|
||||
eur = total * b["metrics"]["cagr"] * k * (1.0 - b["tax"]) / EURUSD / 365.0
|
||||
eur_p = total * pr * (1.0 - b["tax"]) / EURUSD / 365.0
|
||||
print(f" {f_ib:>11.0%} {k:>6.2f}x {b['metrics']['cagr']*k:>9.1%} {eur:>10.2f} "
|
||||
f"{pr:>12.2%} {eur_p:>15.2f}")
|
||||
if best is None or eur_p > best[1]:
|
||||
best = (f_ib, eur_p, k, pr)
|
||||
print(f"\n -> split che massimizza la RENDITA a iso-rischio: {best[0]:.0%} su IB "
|
||||
f"(leva {best[2]:.2f}x, rendita {best[3]:.2%} = EUR {best[1]:.2f}/g)")
|
||||
|
||||
# --- ISO-RISCHIO CON VINCOLI DI LEVA REALI -------------------------------
|
||||
# La leva che l'iso-rischio chiede non e' sempre ottenibile: su IB un conto da EUR 10k ha
|
||||
# Reg-T (2x max sugli ETF); il portfolio margin parte da ~$110k. Su Deribit i perp danno
|
||||
# leva alta. La leva ATTUABILE del book misto e' quindi limitata dalla gamba peggiore.
|
||||
print("\n" + "-" * 100)
|
||||
print(" ISO-RISCHIO CON LEVA ATTUABILE (IB Reg-T 2.0x su ETF; Deribit perp 3.0x)")
|
||||
print("-" * 100)
|
||||
print(f" {'quota su IB':>12} {'leva max':>9} {'leva usata':>11} {'CAGR':>7} "
|
||||
f"{'EUR/g':>7} {'rendita':>9} {'EUR/g rend.':>12}")
|
||||
print(" (COSTO DEL MARGINE INCLUSO: la parte presa a prestito si paga — IB ~5.5%/anno sul")
|
||||
print(" margine USD, Deribit funding perp ~2%/anno medio. Ignorarlo gonfia ogni riga levata.)")
|
||||
print(f" {'quota su IB':>12} {'leva max':>9} {'leva usata':>11} {'CAGR lordo':>11} "
|
||||
f"{'costo leva':>11} {'CAGR netto':>11} {'EUR/g':>7} {'rendita':>9} {'EUR/g rend.':>12}")
|
||||
best2 = None
|
||||
for f_ib, b, _ in rows:
|
||||
k_want = min(0.15 / b["vol"], 3.0)
|
||||
k_max = f_ib * 2.0 + (1.0 - f_ib) * 3.0 # vincolo pesato per venue
|
||||
k = min(k_want, k_max)
|
||||
borrow_rate = f_ib * IB_MARGIN_RATE + (1.0 - f_ib) * DERIBIT_FUNDING
|
||||
fin = max(k - 1.0, 0.0) * borrow_rate # costo sulla quota presa a prestito
|
||||
cagr_gross = b["metrics"]["cagr"] * k
|
||||
cagr_net = cagr_gross - fin
|
||||
pr = max(perp_rate(b["combo"], k=k) - fin, 0.0) # il costo grava anche sul prelievo
|
||||
eur_p = total * pr * (1.0 - b["tax"]) / EURUSD / 365.0
|
||||
eur = total * cagr_net * (1.0 - b["tax"]) / EURUSD / 365.0
|
||||
flag = " (leva TAGLIATA)" if k < k_want - 1e-9 else ""
|
||||
print(f" {f_ib:>11.0%} {k_max:>8.2f}x {k:>10.2f}x {cagr_gross:>11.1%} "
|
||||
f"{fin:>11.1%} {cagr_net:>11.1%} {eur:>7.2f} {pr:>9.2%} {eur_p:>12.2f}{flag}")
|
||||
if best2 is None or eur_p > best2[1]:
|
||||
best2 = (f_ib, eur_p, k, pr)
|
||||
print(f"\n -> con leva ATTUABILE E costo del margine il migliore e' {best2[0]:.0%} su IB "
|
||||
f"(leva {best2[2]:.2f}x, rendita {best2[3]:.2%} = EUR {best2[1]:.2f}/g)")
|
||||
|
||||
# --- il confronto che conta: GTAA vs LIQUIDITA' FERMA ---------------------
|
||||
print("\n" + "-" * 100)
|
||||
print(" IL CONFRONTO ONESTO: GTAA01 sui 10k vs la stessa liquidita' FERMA che prende interesse")
|
||||
print("-" * 100)
|
||||
b94 = [b for f, b, _ in rows if abs(f - 0.94) < 1e-9][0]
|
||||
cash_eur_day = total / EURUSD * IB_CASH_RATE * (1 - TAX_EQUITY) / 365
|
||||
print(f" 10k investiti in GTAA01 (+ $600 su Deribit) : EUR {b94['eur_day']:.2f}/g "
|
||||
f"maxDD {b94['metrics']['maxdd']:.1%}")
|
||||
print(f" 10k FERMI a interesse ~{IB_CASH_RATE:.0%} (+ $600 Deribit): EUR "
|
||||
f"{cash_eur_day + rows[0][1]['eur_day']*DERIBIT_NOW/total:.2f}/g maxDD ~0%")
|
||||
print(f" -> il premio di GTAA01 sopra la liquidita' e' ~EUR "
|
||||
f"{b94['eur_day'] - cash_eur_day:.2f}/g, pagato con un maxDD del "
|
||||
f"{b94['metrics']['maxdd']:.0%}.")
|
||||
print(" (tasso liquidita' = assunzione dichiarata; un BTP/monetario EUR renderebbe di piu'.)")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user