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
+16
View File
@@ -1120,6 +1120,22 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis
**LEZIONE: una raccomandazione poggiata su un fatto non verificato sul conto reale vale quanto
quel fatto** — due volte in due giorni un'assunzione sul conto ha cambiato la conclusione (prima
la negoziabilita' PRIIPs, poi quale conto e' davvero operativo).
(e) **"degiro ha api?" — NO, e costa 0.02 di Sharpe.** DEGIRO non espone una API di trading
ufficiale al retail; Revolut nemmeno (la sua e' per pagamenti); IB si'. I wrapper NON ufficiali
degli endpoint interni girano su credenziali + seed 2FA e **si rompono in silenzio** a ogni cambio
di front-end — e il progetto ha gia' pagato quel prezzo (`fresh_5m`, 26/07): **un esecutore
automatico che puo' rompersi senza dirlo e' peggio dell'esecuzione manuale.** Misurato invece il
costo di NON automatizzare: carico operativo **15 settimane/anno con ≥1 ordine** (29% dei
controlli, 1.4 ordini per volta); costo del ritardo su 10 anni **1g 0.02 (peggiora in 6/11 anni
= moneta), 3g 0.13, 10g 0.27**. ⚠️ Sulla finestra UCITS di 3.2 anni la curva NON e' monotona
(5g 0.26, 10g 0.13) → il campione corto non risolve differenze di questa taglia, si legge la
finestra lunga. **Il costo non e' il ritardo tipico ma la CODA: il rischio e' la dimenticanza, e
si copre con un ALLARME non con una API** (`gtaa_rebalance_plan` esiste gia' in produzione, nato
per un esecutore; l'allerta Telegram c'e' gia' sul book live). ⚠️ Lato opposto, vero: il resto del
book e' automatico, una gamba manuale aggiunge un modo di fallire che oggi non c'e' — argomento
reale a favore di IB, che pero' richiede un secondo venue e quindi cade sotto la decisione venue
($20k). **REGOLA: una capacita' mancante si valuta sul costo di non averla, non sulla sua
assenza.**
- ⚠️ **IL FEED EQUITY NON AVEVA UN CROSS-CHECK — buco trovato e chiuso (2026-07-26).**
`src/data/eq_crosscheck.py`. Nel crypto la certificazione incrocia sempre piu' venue
(`certify_feed.py` vs Coinbase USD); il feed equity aveva **solo controlli locali** (integrita',
+64
View File
@@ -394,3 +394,67 @@ guardando i due conti** — non sono cose che posso misurare da questo lato.
Ieri ho consigliato IB in parte perché "il conto esiste già": era un'assunzione, e per la seconda
volta in due giorni un'assunzione sul conto reale ha cambiato la conclusione. La prima volta era la
negoziabilità (PRIIPs), questa volta quale conto è davvero operativo.
---
# Addendum 2026-07-27 (2) — "degiro ha api?"
**Fatto:** DEGIRO **non** espone una API di trading ufficiale per il retail. Revolut nemmeno (la sua
API è per pagamenti, non per il portafoglio titoli). IB sì, documentata e supportata — è quella che
il progetto già usa per i dati.
Esistono wrapper **non ufficiali** degli endpoint interni del web trader DEGIRO. Girano su
credenziali + seed 2FA depositati sulla macchina, sono fuori dai termini di servizio, e si rompono
**in silenzio** a ogni cambio di front-end. Questo progetto ha già pagato una volta il prezzo di un
guasto silenzioso (`fresh_5m`, 26/07: fallimento muto → latenza d'uscita di SKH01 da ~1h a ~1 giorno
senza che nulla lo segnalasse). **Un esecutore automatico che può rompersi senza dirlo è peggio
dell'esecuzione manuale.**
Quindi la domanda utile non è "si può automatizzare" ma **"quanto costa non farlo"**.
## Carico operativo (banda 25%, controllo settimanale, $10.000)
| | |
|---|---|
| controlli/anno | 50 |
| **settimane con ≥1 ordine** | **15** (29% dei controlli) |
| ordini per settimana attiva | 1.4 (max 5) |
Nel 71% dei controlli non c'è nulla da fare.
## Costo del ritardo di esecuzione
Veicoli USA, 10 anni. ⚠️ Sulla finestra UCITS di 3.2 anni la curva **non risulta monotona** (5
giorni 0.26, 10 giorni 0.13): il campione corto non risolve differenze di questa taglia, quindi
il numero si legge sulla finestra lunga.
| ritardo | Sharpe | ΔSharpe | stabilità |
|---|---|---|---|
| 0 giorni | 0.90 | — | — |
| **1 giorno** | 0.89 | **0.02** | peggiora in 6/11 anni = **moneta** |
| 2 giorni | 0.83 | 0.07 | 7/11 |
| 3 giorni | 0.78 | 0.13 | 7/11 |
| 5 giorni | 0.73 | 0.18 | 7/11 |
| **10 giorni** | 0.63 | **0.27** | 6/11 |
**Il costo non è il ritardo tipico, è la coda.** Eseguire il giorno stesso o il giorno dopo costa
nulla; dieci giorni valgono quasi un terzo dello sleeve.
## Conseguenza
Il rischio dell'operatività manuale non è la lentezza, è la **dimenticanza** — e quella si compra
con un **allarme**, non con una API. `gtaa_rebalance_plan(held, capital)` esiste già in produzione
ed è nato esattamente per un esecutore; il progetto ha già l'allerta Telegram sul book live. Un cron
settimanale che calcola il piano e notifica quando c'è da agire copre il caso 15 volte l'anno.
⚠️ **L'altro lato, che resta vero:** il resto del book è automatico (TP01 e SKH01 mandano ordini
veri su cron). Aggiungere una gamba manuale introduce un modo di fallire che il sistema oggi non ha.
È un argomento reale a favore di IB — che l'API ce l'ha — ma richiede finanziare un secondo venue, e
la decisione venue del 26/07 tiene tutto su Deribit fino a $20k. **La scelta è quindi rimandata al
punto in cui la si riaprirebbe comunque.**
## Lezione
**Una capacità mancante si valuta sul costo di non averla, non sulla sua assenza.** Qui l'assenza di
API sembra squalificante e vale 0.02 di Sharpe se si esegue entro un giorno; ciò che vale davvero
(0.27) è un rischio operativo che si copre con una notifica.
+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")