research(gtaa): l'assenza di API DEGIRO vale -0.02 di Sharpe, non e' squalificante

Domanda dell'operatore: "degiro ha api?". Fatto: no, non ufficiale; Revolut nemmeno
(la sua e' per pagamenti); IB si'. I wrapper non ufficiali girano su credenziali +
seed 2FA e si rompono IN SILENZIO a ogni cambio di front-end — e il progetto ha gia'
pagato quel prezzo con fresh_5m il 26/07. Un esecutore che puo' rompersi senza dirlo
e' peggio dell'esecuzione manuale.

Misurato il costo di NON automatizzare invece di discuterlo:
  carico operativo: 15 settimane/anno con >=1 ordine (29% dei controlli, 1.4 per volta)
  ritardo 1g  -0.02 Sharpe (peggiora in 6/11 anni = moneta)
  ritardo 3g  -0.13
  ritardo 10g -0.27

Il costo non e' il ritardo tipico ma la coda: il rischio dell'operativita' manuale e'
la dimenticanza, e si copre con un allarme, non con una API (gtaa_rebalance_plan esiste
gia' in produzione ed e' nato per un esecutore).

Nota di metodo: sulla finestra UCITS di 3.2 anni la curva del ritardo NON e' monotona
(5g -0.26, 10g -0.13) -> il campione corto non risolve differenze di questa taglia,
quindi il numero si legge sulla finestra lunga a 10 anni, dove lo e'.

Book, pesi, cron, config INVARIATI.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XBbYmiXqbUNuGpK9sfbsGp
This commit is contained in:
Adriano Dal Pastro
2026-07-27 11:05:33 +00:00
parent 842b0e865b
commit d0c804ebfc
3 changed files with 135 additions and 0 deletions
+55
View File
@@ -174,6 +174,61 @@ def main() -> None:
print(" controllo (2) su 10 anni e veicoli diversi, ma GTAA01 non e' deployabile prima dei")
print(" $20k (decisione venue 26/07): c'e' tempo per validarla come si deve.")
# ---------- (5-bis) l'esecuzione MANUALE e' sostenibile? ----------
print("\n" + "=" * 100)
print(" (5-bis) SE IL BROKER NON HA UNA API — quanto costa eseguire a mano")
print("=" * 100)
print(" DEGIRO non espone una API di trading ufficiale; Revolut nemmeno (la sua API e' per")
print(" pagamenti). Esistono wrapper non ufficiali degli endpoint interni, ma girano su")
print(" credenziali + seed 2FA e si rompono in SILENZIO a ogni cambio di front-end — e questo")
print(" progetto ha gia' pagato una volta il prezzo di un guasto silenzioso (fresh_5m, 26/07).")
print(" Quindi la domanda giusta non e' 'si puo' automatizzare' ma 'quanto costa NON farlo'.")
per_bar = None
band_c = BAND_FRAC * CAP / 6.0
for k in dep:
_, pos = U.leg_returns(dep[k], dep[k], cal, CAP / 6.0, band_c, can_every,
U.cost_form(fixed=1.0))
o = (pos.diff().abs() > 1e-12).astype(int)
per_bar = o if per_bar is None else per_bar + o
anni = len(cal) / 252.0
att = int((per_bar > 0).sum())
print(f"\n Carico operativo a ${CAP:,.0f} (banda {BAND_FRAC:.0%}, controllo settimanale):")
print(f" controlli/anno {len(cal)/5/anni:.0f}")
print(f" settimane con >=1 ordine/anno {att/anni:.0f} ({att/(len(cal)/5):.0%} dei controlli)")
print(f" ordini per settimana attiva {per_bar[per_bar > 0].mean():.1f} "
f"(max {int(per_bar.max())})")
print("\n Costo del RITARDO di esecuzione — veicoli USA, finestra lunga "
f"({len(cal_us)/252:.0f} anni), perche' su 3.2 anni la curva non risulta monotona")
print(" (cioe' il campione corto non risolve differenze di questa taglia):")
print(f"\n {'ritardo':>10s}{'Sharpe':>9s}{'CAGR':>9s}{'dSharpe':>10s} stabilita' per anno")
base_sh, base_s = None, None
for d in (0, 1, 2, 3, 5, 10):
cols = {}
for k in us:
_, pos = U.leg_returns(us[k], us[k], cal_us, CAP / 6.0, band_c, can_every,
U.cost_form(fixed=1.0))
px = U._norm(us[k]).reindex(cal_us).ffill().values.astype(float)
ret = np.zeros(len(px))
ret[1:] = px[1:] / px[:-1] - 1.0
p = pos.shift(d).fillna(0.0).values
c = (pos.diff().abs() > 1e-12).astype(float).shift(d).fillna(0.0).values / (CAP / 6.0)
cols[k] = __import__("pandas").Series(p * ret - c, index=cal_us)
s = __import__("pandas").concat(cols, axis=1).mean(axis=1)
m = U.met(s)
if base_sh is None:
base_sh, base_s = m["sharpe"], s.copy()
peggio = sum(1 for y, g in s.groupby(s.index.year)
if len(g) > 100 and U.met(g)["sharpe"] < U.met(base_s[base_s.index.year == y])["sharpe"])
tot = sum(1 for y, g in s.groupby(s.index.year) if len(g) > 100)
print(f" {str(d)+' giorni':>10s}{m['sharpe']:>9.2f}{m['cagr']:>8.2%}"
f"{m['sharpe']-base_sh:>+10.2f} peggiora in {peggio}/{tot} anni")
print("\n LETTURA: eseguire il giorno stesso o il giorno dopo costa ~nulla (-0.02, e peggiora")
print(" in 6/11 anni = moneta). Il costo NON e' il ritardo tipico ma la CODA: dieci giorni")
print(" valgono -0.27, cioe' quasi un terzo dello sleeve. Il rischio dell'operativita' manuale")
print(" non e' la lentezza, e' la DIMENTICANZA — e quella si compra con un ALLARME, non con")
print(" una API: `gtaa_rebalance_plan()` esiste gia' in produzione ed e' nato per un esecutore.")
# ---------- (5) cosa cercare sul proprio conto ----------
print("\n" + "=" * 100)
print(" (5) COSA CERCARE SUL PROPRIO BROKER — per ISIN, non per ticker")