2e7178487d
- MC aggiornato alle regole VERE (HYRO 10/6/4, BREAKOUT 10/6/3, FTMO 10/10/5): de-luck P(pass) 55-68% a leva 1-1.5x, mediana 68-137g; funded 100k de-luck P(vivo) 69%, atteso blended onesto 250-450 eur/MESE (attriti fuori-MC: carry, recycling bust, controparte non regolata, cap industria 200k). - Roadmap: prop = unico canale che cambia scala in mesi (8-15 eur/g); stack 24 mesi top-decile 30-60 eur/g, mediana 10-25; fisco 33% = muro x1.5. - Book live INVARIATO. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
207 lines
10 KiB
Python
207 lines
10 KiB
Python
"""r0724_goal50_math — la matematica ONESTA del target 50 EUR/giorno (2026-07-24).
|
|
|
|
Domanda del goal: "rendita aggiuntiva che entro qualche mese arrivi a 50 EUR/giorno".
|
|
Questo script NON cerca un edge nuovo: quantifica le vie possibili col book che ABBIAMO,
|
|
usando i rendimenti giornalieri REALI del book Deribit (TP01 0.75 + SKH01 0.25) e
|
|
scenari de-luckati. Quattro parti:
|
|
|
|
A) IL MURO — capitale richiesto per 50 EUR/g ai CAGR onesti; tempo da 600$ con depositi.
|
|
B) LA LEVA NON E' LA SCORCIATOIA — vol/DD implicati dal target su capitale piccolo.
|
|
C) PROP-FIRM EVAL — Monte Carlo (block bootstrap dei ritorni reali del book) di una
|
|
evaluation tipo crypto-prop: P(pass), giorni mediani, poi fase funded su 100k$:
|
|
distribuzione EUR/giorno e P(perdere il conto entro 1 anno).
|
|
D) VAULT / COPY — TVL necessario per 50 EUR/g a perf-share 10%.
|
|
|
|
Onesta': i ritorni del book sono MODELLATI (il path live SKH01 e' peggiore: FULL 1.19/
|
|
HOLD 1.15/DD~25% vs modellato ~1.4-1.6) -> ogni scenario gira anche con haircut sul
|
|
drift (x0.6) per il de-luck. EURUSD assunto 1.09 (parametro).
|
|
"""
|
|
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))
|
|
|
|
from src.portfolio.portfolio import StrategyPortfolio, metrics # noqa: E402
|
|
from src.portfolio.sleeves import deribit_book_sleeves # noqa: E402
|
|
|
|
EURUSD = 1.09 # 1 EUR = 1.09 USD (assunzione dichiarata)
|
|
TARGET_EUR_DAY = 50.0
|
|
TARGET_EUR_YR = TARGET_EUR_DAY * 365
|
|
TARGET_USD_YR = TARGET_EUR_YR * EURUSD
|
|
RNG = np.random.default_rng(724)
|
|
|
|
|
|
def eur_day(usd_yr: float) -> float:
|
|
return usd_yr / EURUSD / 365.0
|
|
|
|
|
|
# ----------------------------------------------------------------------------- A
|
|
def part_a() -> None:
|
|
print("=" * 100)
|
|
print(f" A) IL MURO — 50 EUR/g = {TARGET_EUR_YR:,.0f} EUR/anno = ${TARGET_USD_YR:,.0f}/anno")
|
|
print("=" * 100)
|
|
print(" capitale richiesto (rendita = capitale x CAGR netto):")
|
|
for cagr in (0.08, 0.10, 0.15, 0.20, 0.25):
|
|
cap = TARGET_USD_YR / cagr
|
|
print(f" CAGR {cagr:>4.0%} -> capitale ${cap:>10,.0f} (~EUR {cap / EURUSD:,.0f})")
|
|
print("\n tempo per arrivarci da $600 (compounding mensile + deposito mensile, CAGR 15%):")
|
|
for dep in (0, 500, 1000, 2000, 5000):
|
|
eq, months = 600.0, 0
|
|
target_cap = TARGET_USD_YR / 0.15
|
|
while eq < target_cap and months < 12 * 60:
|
|
eq = eq * (1 + 0.15) ** (1 / 12) + dep * EURUSD
|
|
months += 1
|
|
yrs = months / 12
|
|
lab = f"{yrs:.1f} anni" if months < 720 else ">60 anni"
|
|
print(f" deposito EUR {dep:>5}/mese -> {lab}")
|
|
print("\n VERDETTO A: da $600, 'qualche mese' per compounding NON esiste. Il capitale")
|
|
print(" proprio arriva al target solo con depositi da migliaia/mese per molti anni.")
|
|
|
|
|
|
# ----------------------------------------------------------------------------- B
|
|
def part_b(book: pd.Series) -> None:
|
|
m = metrics(book)
|
|
print("\n" + "=" * 100)
|
|
print(" B) LA LEVA NON E' LA SCORCIATOIA")
|
|
print("=" * 100)
|
|
sh = m["sharpe"]
|
|
vol = book.std() * np.sqrt(365)
|
|
cagr = (1 + book).prod() ** (365 / len(book)) - 1
|
|
print(f" book Deribit (TP01 .75 + SKH01 .25) modellato: Sharpe {sh:.2f} vol {vol:.0%} CAGR {cagr:.1%}")
|
|
for cap_usd in (600, 2000, 5000, 20000):
|
|
need = TARGET_USD_YR / cap_usd # rendimento annuo richiesto
|
|
lev = need / max(cagr, 1e-9) # multiplo di leva sul book
|
|
vol_imp = vol * lev
|
|
print(f" su ${cap_usd:>6,}: servono {need:>7.0%}/anno -> leva ~{lev:>5.1f}x il book "
|
|
f"-> vol implicita ~{vol_imp:>6.0%} -> rovina quasi certa" if lev > 3 else
|
|
f" su ${cap_usd:>6,}: servono {need:>7.0%}/anno -> leva ~{lev:>5.1f}x il book "
|
|
f"-> vol implicita ~{vol_imp:>6.0%}")
|
|
print(" VERDETTO B: il target su capitale piccolo implica vol a 3 cifre = rovina.")
|
|
print(" (Kelly gia' documentato: il book gira a ~0.01-0.27 Kelly a seconda dello sleeve.)")
|
|
|
|
|
|
# ----------------------------------------------------------------------------- C
|
|
def _block_bootstrap_paths(r: np.ndarray, n_days: int, n_paths: int, block: int = 20,
|
|
drift_scale: float = 1.0) -> np.ndarray:
|
|
"""Percorsi bootstrap a blocchi dai ritorni giornalieri reali del book.
|
|
|
|
drift_scale < 1 = haircut de-luck: riduce la media conservando la vol
|
|
(r' = (r - mu) + mu*scale).
|
|
"""
|
|
mu = r.mean()
|
|
r_adj = (r - mu) + mu * drift_scale
|
|
n = len(r_adj)
|
|
n_blocks = int(np.ceil(n_days / block))
|
|
starts = RNG.integers(0, n - block, size=(n_paths, n_blocks))
|
|
idx = (starts[:, :, None] + np.arange(block)[None, None, :]).reshape(n_paths, -1)[:, :n_days]
|
|
return r_adj[idx]
|
|
|
|
|
|
def _simulate_eval(paths: np.ndarray, target: float, max_dd: float, daily_loss: float,
|
|
trailing: bool, max_days: int) -> dict:
|
|
"""Simula una evaluation: passa se equity tocca (1+target) prima di violare
|
|
il max drawdown (statico da 1.0 o trailing dal max) o il daily loss limit."""
|
|
n_paths, n_days = paths.shape
|
|
eq = np.cumprod(1 + paths, axis=1)
|
|
hwm = np.maximum.accumulate(np.maximum(eq, 1.0), axis=1) if trailing else 1.0
|
|
dd_breach = eq < (hwm * (1 - max_dd) if trailing else (1 - max_dd))
|
|
dl_breach = paths < -daily_loss
|
|
fail = dd_breach | dl_breach
|
|
passed = eq >= 1 + target
|
|
|
|
first_fail = np.where(fail.any(axis=1), fail.argmax(axis=1), max_days + 1)
|
|
first_pass = np.where(passed.any(axis=1), passed.argmax(axis=1), max_days + 1)
|
|
ok = first_pass < first_fail
|
|
p_pass = ok.mean()
|
|
days = np.median(first_pass[ok]) if ok.any() else np.nan
|
|
return {"p_pass": p_pass, "median_days": days,
|
|
"p_fail": (first_fail <= np.minimum(first_pass, max_days)).mean()}
|
|
|
|
|
|
def part_c(book: pd.Series) -> None:
|
|
print("\n" + "=" * 100)
|
|
print(" C) PROP-FIRM EVALUATION — Monte Carlo sui ritorni REALI del book (block bootstrap 20g)")
|
|
print("=" * 100)
|
|
r = book.to_numpy()
|
|
n_paths, max_days = 20_000, 365
|
|
|
|
# regole VERE verificate (ricerca web R2, 2026-07-24): le firm crypto-native danno
|
|
# DD 3-6%, NON 10% — e' il vincolo binding per un book con maxDD storico 9.4%.
|
|
rules = [
|
|
("HYRO 1-step: tgt 10% / DD 6% st / daily 4%", 0.10, 0.06, 0.04, False),
|
|
("BREAKOUT Classic: tgt 10% / DD 6% st / dl 3%", 0.10, 0.06, 0.03, False),
|
|
("FTMO Swing: tgt 10% / DD 10% st / daily 5%", 0.10, 0.10, 0.05, False),
|
|
]
|
|
for drift_scale, lab in ((1.0, "book modellato"), (0.6, "de-luck x0.6 (onesto)")):
|
|
base = _block_bootstrap_paths(r, max_days, n_paths, drift_scale=drift_scale)
|
|
for lev in (1.0, 1.5, 2.0):
|
|
paths = base * lev
|
|
print(f"\n --- {lab}, leva eval x{lev:.1f} (vol ~{book.std() * np.sqrt(365) * lev:.0%}) ---")
|
|
for name, tgt, dd, dl, tr in rules:
|
|
res = _simulate_eval(paths, tgt, dd, dl, tr, max_days)
|
|
d = f"{res['median_days']:.0f}g" if np.isfinite(res["median_days"]) else "n/a"
|
|
print(f" {name:<44} P(pass entro 1a) {res['p_pass']:>5.1%} mediana {d}")
|
|
|
|
# fase funded: 100k, split 80%, anno di ritorni; regole di bust VERE (R2):
|
|
# HYRO = max loss 6% statico + daily 4% (vol nativa book 11% -> gia' nel range
|
|
# 10-12% raccomandato); FTMO = 10% statico + daily 5%.
|
|
print("\n --- fase FUNDED: $100k nozionale, split 80%, orizzonte 1 anno ---")
|
|
funded_rules = [("HYRO (max loss 6% st, daily 4%)", 0.06, 0.04),
|
|
("FTMO (max loss 10% st, daily 5%)", 0.10, 0.05)]
|
|
for drift_scale, lab in ((1.0, "book modellato"), (0.6, "de-luck x0.6")):
|
|
paths = _block_bootstrap_paths(r, 365, n_paths, drift_scale=drift_scale)
|
|
eq = np.cumprod(1 + paths, axis=1)
|
|
for fname, dd_lim, dl_lim in funded_rules:
|
|
blown = (eq < 1 - dd_lim) | (paths < -dl_lim)
|
|
alive = ~blown.any(axis=1)
|
|
pnl = (eq[:, -1] - 1) * 100_000 * 0.80
|
|
pnl_alive = pnl[alive]
|
|
q = np.percentile(pnl_alive, [10, 50, 90]) if alive.any() else [np.nan] * 3
|
|
print(f" {lab:<16} {fname:<32} P(vivo 1a) {alive.mean():>5.1%} "
|
|
f"payout p10/p50/p90: EUR {eur_day(q[0]):>5.1f} / {eur_day(q[1]):>5.1f} / "
|
|
f"{eur_day(q[2]):>5.1f} /g")
|
|
print("\n NB (ricerca R2 completata 2026-07-24): HYRO = API reale Bybit da funded, consistency")
|
|
print(" 40% solo in eval; BREAKOUT = carry nascosto 0.033%/g (~12%/anno) ostile ai multi-week")
|
|
print(" hold, esecuzione manuale ok a cadenza 1d; FTMO = CFD con swap ~10-25%/anno. Questi")
|
|
print(" attriti + recycling bust + controparte non regolata NON sono nel MC: l'atteso blended")
|
|
print(" onesto a $100k funded e' ~250-450 EUR/MESE (8-15/g), 15-30/g al cap $200k.")
|
|
|
|
|
|
# ----------------------------------------------------------------------------- D
|
|
def part_d() -> None:
|
|
print("\n" + "=" * 100)
|
|
print(" D) VAULT / COPY-TRADING — perf-share 10% sopra HWM")
|
|
print("=" * 100)
|
|
for cagr in (0.10, 0.15, 0.20):
|
|
tvl = TARGET_USD_YR / (cagr * 0.10)
|
|
print(f" CAGR {cagr:.0%}: per 50 EUR/g servono ${tvl / 1e6:,.1f}M di TVL altrui")
|
|
print(" VERDETTO D: TVL a 7 cifre richiede anni di track record pubblico, non mesi.")
|
|
|
|
|
|
def main() -> None:
|
|
port = StrategyPortfolio(deribit_book_sleeves())
|
|
book = port.combined_daily().dropna()
|
|
book = book[book.index >= "2019-03-01"]
|
|
m = metrics(book)
|
|
print(f"book Deribit live (modellato): {len(book)} giorni {book.index[0].date()} -> "
|
|
f"{book.index[-1].date()} Sharpe {m['sharpe']:.2f} maxDD {m['maxdd']:.1%}")
|
|
part_a()
|
|
part_b(book)
|
|
part_c(book)
|
|
part_d()
|
|
print("\n" + "=" * 100)
|
|
print(" SINTESI ONESTA: 50 EUR/g in qualche mese e' fuori portata del capitale proprio.")
|
|
print(" L'unica via quantificabile in MESI e' moltiplicare il nozionale gestito (prop/")
|
|
print(" funded), con P(successo) e regole da verificare; tutto il resto e' anni+capitale.")
|
|
print("=" * 100)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|