research(gtaa): ZPRR e' R2US (stessa ISIN) + correzione sul verso del costo FX
L'operatore ha verificato su Revolut: R2US assente, ZPRR e SPY4 presenti. (1) ZPRR (Xetra EUR) == R2US (Londra USD): stessa ISIN IE00BJ38QD84, stessa classe di quote, stesso NAV. La gamba small cap e' risolta. Regola: si cerca per ISIN, non per ticker — lo stesso fondo ha ticker diversi su borse diverse. (2) SPY4 IE00B4YBJ215 NON e' small cap ne' S&P 500: e' SPDR S&P 400 MID cap (l'S&P 500 e' SPY5). Misurato come ripiego su 6.5 anni: Sharpe 0.86 vs 0.86, corr fra i due sleeve 0.991, peggiore in 3/7 anni = moneta. Sulla finestra corta di 3.2 anni sembrava -0.15: era rumore. Ripiego accettabile per ragione meccanica (small e mid USA correlano ~0.95 giornaliero), NON validato — e' proprio perche' non si distinguono che la scelta non conta. (3) CORREZIONE a un'indicazione data ieri. Avevo scritto "una linea in EUR aggiunge la conversione per ordine". Vero solo per un conto in USD: per un conto in EURO vale l'opposto. L'esposizione economica e' identica (il fondo detiene attivi USD, nessuna delle due linee e' coperta) — la valuta di quotazione non copre nulla, decide solo se serve una conversione. Regola giusta: prendere la linea nella valuta del proprio saldo. Misurato: turnover lordo $13.655/anno su $10k -> 25bps = -0.07 Sharpe = $34/anno = ~$1.6 per ordine, contro una soglia di $18.90. Regola generale: un costo di conversione non e' una proprieta' dello strumento ma della coppia strumento-CONTO. Book, pesi, cron, config INVARIATI. 447 test verdi (+3). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBbYmiXqbUNuGpK9sfbsGp
This commit is contained in:
@@ -1113,10 +1113,29 @@ Prima ondata di ricerca onesta su BTC/ETH certificati (5 track, harness condivis
|
||||
(d) **Cosa cercare sul proprio conto — per ISIN, non per ticker** (da contract details IB; tutti
|
||||
domiciliati IE, quotati a **Londra in USD**): **VUAA** `IE00BFMXXD54` ($143.80) · **XNAS**
|
||||
`IE00BMFKG444` ($65.74) · **R2US** `IE00BJ38QD84` ($86.35) · **IDTL** `IE00BSKRJZ44` ($3.09) ·
|
||||
**IGLN** `IE00B4ND3602` ($79.06) · **IHYU** `IE00B4PY7Y77` ($94.12). ⚠️ Le stesse ISIN esistono
|
||||
in EUR su altre borse: la conversione per ordine a 25bps costa **~0.15 di Sharpe**, piu' del
|
||||
costo per ordine di qualunque broker a questa cadenza → **la valuta della linea conta piu' del
|
||||
broker**. Nessuna azione oggi (decisione venue: 100% Deribit fino a $20k).
|
||||
**IGLN** `IE00B4ND3602` ($79.06) · **IHYU** `IE00B4PY7Y77` ($94.12).
|
||||
Nessuna azione oggi (decisione venue: 100% Deribit fino a $20k).
|
||||
⚠️ **CORREZIONE 27/07 A QUESTO PUNTO.** Avevo scritto "una linea in EUR aggiunge la conversione
|
||||
per ordine → prendere quella in USD". E' vero **solo per un conto in USD**. Per un conto in EURO
|
||||
(retail italiano su Revolut/Degiro) vale l'**OPPOSTO**: la linea USD costringe a convertire a ogni
|
||||
ordine, quella EUR no. L'esposizione economica e' identica (il fondo detiene attivi USD e
|
||||
**nessuna delle due linee e' coperta**): la valuta di quotazione non copre nulla, decide solo se
|
||||
serve una conversione. **REGOLA GIUSTA: prendere la linea nella valuta del proprio saldo.**
|
||||
Misurato (turnover lordo **$13.655/anno** su $10k, 21 ordini): 15bps −0.04 Sh/$20 a., 25bps
|
||||
**−0.07 Sh/$34 a.**, 50bps −0.14/$68 → a 25bps equivale a **~$1.6 in piu' per ordine**, piccolo
|
||||
rispetto alla soglia di $18.90. **REGOLA: un costo di conversione non e' una proprieta' dello
|
||||
strumento ma della coppia strumento-CONTO.**
|
||||
(f) **Equivalenze e ripieghi, verificati su contract details IB (27/07).**
|
||||
**ZPRR (Xetra EUR) == R2US (Londra USD): stessa ISIN `IE00BJ38QD84`, stesso fondo, stesso NAV** →
|
||||
se un broker non quota la linea di Londra, quella di Xetra e' identica (e su conto in euro,
|
||||
migliore). **REGOLA: si cerca per ISIN, non per ticker** — lo stesso fondo ha ticker diversi su
|
||||
borse diverse. ⚠️ **SPY4 `IE00B4YBJ215` NON e' small cap ne' S&P 500**: e' SPDR **S&P 400 MID
|
||||
cap** (l'S&P 500 e' SPY5 `IE00B6YX5C33`). Misurato come ripiego della gamba small: su 6.5 anni
|
||||
Sharpe **0.86 vs 0.86**, CAGR 4.26% vs 4.20%, **corr fra i due sleeve 0.991**, peggiore in 3/7
|
||||
anni = moneta (sulla finestra corta di 3.2a sembrava −0.15: rumore) → **ripiego accettabile**, per
|
||||
ragione meccanica (small e mid USA correlano ~0.95 giornaliero, un TSMOM si accende negli stessi
|
||||
giorni). Non e' "validato": 6.5 anni non distinguono due gambe cosi' simili, ed e' esattamente
|
||||
perche' la scelta non conta. Se c'e' una linea Russell 2000, si usa quella.
|
||||
**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).
|
||||
|
||||
@@ -458,3 +458,70 @@ punto in cui la si riaprirebbe comunque.**
|
||||
**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.
|
||||
|
||||
---
|
||||
|
||||
# Addendum 2026-07-27 (3) — "r2us in revolut non c'è, ma ho zprr e spy4"
|
||||
|
||||
## ZPRR **è** R2US
|
||||
|
||||
| ticker | borsa | valuta | ISIN |
|
||||
|---|---|---|---|
|
||||
| R2US | Londra | USD | `IE00BJ38QD84` |
|
||||
| **ZPRR** | **Xetra** | **EUR** | **`IE00BJ38QD84`** |
|
||||
|
||||
Stessa ISIN = stessa classe di quote = **stesso fondo, stesso NAV**. Cambia solo dove e in che
|
||||
valuta si negozia. La gamba small cap è risolta: **compra ZPRR**, non si perde niente.
|
||||
|
||||
> **Regola: si cerca per ISIN, non per ticker.** Lo stesso fondo ha ticker diversi su borse diverse,
|
||||
> e un ticker copiato da una lista può essere un altro prodotto.
|
||||
|
||||
## ⚠️ SPY4 non è quello che sembra
|
||||
|
||||
`IE00B4YBJ215` = SPDR **S&P 400 US Mid Cap**. Non è small cap, e non è l'S&P 500 (quello è SPY5,
|
||||
`IE00B6YX5C33`). Non serve, dato che ZPRR c'è — ma misurato come ripiego, su 6.5 anni:
|
||||
|
||||
| gamba small cap | Sharpe | CAGR | maxDD |
|
||||
|---|---|---|---|
|
||||
| R2US (small) | 0.86 | 4.20% | −8.2% |
|
||||
| SPY4 (mid) | 0.86 | 4.26% | −8.3% |
|
||||
|
||||
Correlazione dei due sleeve **0.991**, SPY4 peggiore in **3/7 anni** = moneta. *(Sulla finestra
|
||||
corta di 3.2 anni sembrava −0.15 di Sharpe: era rumore.)*
|
||||
|
||||
**Ripiego accettabile**, e il motivo è meccanico: small e mid cap USA hanno correlazione giornaliera
|
||||
~0.95, e un TSMOM su due serie così vicine si accende quasi negli stessi giorni. ⚠️ Non è
|
||||
"validato" — 6.5 anni non bastano a distinguere due gambe così simili, ed è **esattamente per
|
||||
questo** che la scelta fra le due non conta.
|
||||
|
||||
## ⚠️ Correzione a un'indicazione data ieri: sulla valuta avevo ragionato al contrario
|
||||
|
||||
Ieri ho scritto che *"una linea in EUR aggiunge la conversione per ordine"*. È vero **solo per un
|
||||
conto in USD**. Per un conto in **euro** — cioè un retail italiano su Revolut o Degiro — vale
|
||||
l'opposto: comprare la linea in USD costringe a convertire a ogni ordine, la linea in EUR no.
|
||||
|
||||
L'esposizione economica è identica nei due casi: il fondo detiene attivi in USD e **nessuna delle
|
||||
due linee è coperta**. La valuta di quotazione non copre niente — decide solo se serve una
|
||||
conversione.
|
||||
|
||||
> **Regola giusta: prendere la linea nella valuta del proprio saldo.**
|
||||
|
||||
Quanto vale, misurato (turnover lordo **$13.655/anno** su $10.000, 21 ordini):
|
||||
|
||||
| bps FX per ordine | ΔSharpe | costo/anno |
|
||||
|---|---|---|
|
||||
| 15 bps | −0.04 | $20 |
|
||||
| 25 bps | −0.07 | $34 |
|
||||
| 50 bps | −0.14 | $68 |
|
||||
|
||||
A 25bps la conversione equivale a ~**$1.6 in più su ciascuno dei 21 ordini**: reale, ma piccolo
|
||||
rispetto alla soglia di $18.90/ordine a $10.000. Quindi **ZPRR in EUR su un conto in euro è la
|
||||
scelta migliore**, non un compromesso.
|
||||
|
||||
## Lezione
|
||||
|
||||
**Un costo di conversione non è una proprietà dello strumento ma della coppia strumento-conto.**
|
||||
Avevo dedotto "linea in EUR = costo in più" da un caso (conto in USD) e l'avevo scritta come regola
|
||||
generale; per il conto che l'operatore usa davvero il segno è invertito. È la terza volta in due
|
||||
giorni che un'assunzione non verificata sul conto reale cambia la conclusione — dopo la
|
||||
negoziabilità PRIIPs e quale conto è operativo.
|
||||
|
||||
@@ -54,7 +54,10 @@ EXCH, CUR = "LSEETF", "USD"
|
||||
CANDIDATI: dict[str, tuple[str, ...]] = {
|
||||
"SPY": ("CSPX", "VUAA"), # S&P 500: CSPX $802 (storia) · VUAA $144 (deploy)
|
||||
"QQQ": ("CNDX", "EQQQ", "XNAS"), # Nasdaq-100: EQQQ $693 (storia) · XNAS $66 (deploy)
|
||||
"IWM": ("XRSU", "R2US", "IDP6"), # Russell 2000: XRSU $438 · R2US $86 (piu' economico E piu' lungo)
|
||||
"IWM": ("XRSU", "R2US", "IDP6", "SPY4"), # Russell 2000: XRSU $438 · R2US $86 (piu' economico E lungo)
|
||||
# SPY4 = S&P 400 MID cap: indice DIVERSO, tenuto solo
|
||||
# per misurare il costo di una sostituzione forzata
|
||||
# (e' cio' che alcuni broker offrono al posto dello small cap)
|
||||
"TLT": ("IDTL", "DTLA"), # US Treasury 20+ — indice identico, IDTL $3.09
|
||||
"GLD": ("IGLN",), # oro fisico (ETC, non UCITS: ha comunque il KID)
|
||||
"HYG": ("IHYU",), # USD High Yield corp — indice equivalente
|
||||
|
||||
@@ -46,6 +46,15 @@ ISIN = {"VUAA": ("IE00BFMXXD54", "Vanguard S&P 500 UCITS (Acc, USD)"),
|
||||
"IGLN": ("IE00B4ND3602", "iShares Physical Gold ETC"),
|
||||
"IHYU": ("IE00B4PY7Y77", "iShares $ High Yield Corp Bond UCITS")}
|
||||
|
||||
# Stesso fondo, ticker diverso su borse diverse. Verificato su contract details IB: se le ISIN
|
||||
# coincidono e' la STESSA classe di quote, quindi lo stesso NAV — cambia solo dove si negozia e in
|
||||
# che valuta. Serve perche' un broker puo' non quotare la linea di Londra e quotare quella di Xetra.
|
||||
EQUIVALENZE = [("R2US", "ZPRR", "IE00BJ38QD84", "Xetra EUR")]
|
||||
|
||||
# Ripiego misurato: se un broker non ha NESSUNA linea Russell 2000, l'S&P 400 mid cap e' l'unica
|
||||
# alternativa comune. NON e' lo stesso indice (mid, non small) — vedi sezione (6) per il costo.
|
||||
RIPIEGO_SMALLCAP = ("SPY4", "IE00B4YBJ215", "SPDR S&P 400 US Mid Cap UCITS")
|
||||
|
||||
|
||||
def ordini_anno(veic, cal, capital, band, every) -> float:
|
||||
n = 0
|
||||
@@ -238,9 +247,72 @@ def main() -> None:
|
||||
t = U.SET_DEPLOY[s]
|
||||
isin, d = ISIN[t]
|
||||
print(f" {s:7s}{t:8s}{isin:16s}{float(U._norm(dep[s]).iloc[-1]):>9.2f} {d}")
|
||||
print("\n Tutti su LONDRA in USD. Le stesse ISIN esistono su altre borse e in altre valute:")
|
||||
print(" una linea in EUR aggiunge la conversione per ordine, che a 25bps costa ~0.15 di")
|
||||
print(" Sharpe — piu' del costo per ordine di qualunque broker a questa cadenza.")
|
||||
print("\n ⚠️ SI CERCA PER ISIN, NON PER TICKER — e il motivo non e' la pignoleria: lo STESSO")
|
||||
print(" fondo ha ticker diversi su borse diverse. Verificato su contract details IB:")
|
||||
for a, b, isin, nota in EQUIVALENZE:
|
||||
print(f" {a:6s} (Londra USD) == {b:6s} ({nota:12s}) ISIN {isin}")
|
||||
print(" Sono la stessa classe di quote: NAV identico, cambia solo dove e in che valuta si")
|
||||
print(" negozia. Cercando per ISIN si trova qualunque linea il proprio broker offra.")
|
||||
print("\n ⚠️ CORREZIONE (2026-07-27) A UN'INDICAZIONE DATA IERI. Avevo scritto che 'una linea in")
|
||||
print(" EUR aggiunge la conversione per ordine'. E' vero solo per un conto in USD. Per un conto")
|
||||
print(" in EURO — che e' il caso di un retail italiano su Revolut/Degiro — vale l'OPPOSTO:")
|
||||
print(" comprare la linea in USD costringe a convertire a ogni ordine, la linea in EUR no.")
|
||||
print(" L'esposizione economica e' identica in entrambi i casi (il fondo detiene attivi in USD")
|
||||
print(" e nessuna delle due linee e' coperta): la valuta di quotazione non copre nulla, decide")
|
||||
print(" solo se serve una conversione. **La regola giusta e': prendere la linea nella valuta")
|
||||
print(" del proprio saldo.**")
|
||||
turnover = 0.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))
|
||||
turnover += float(pos.diff().abs().sum()) * CAP / 6.0
|
||||
turnover /= (len(cal) / 252.0)
|
||||
print(f"\n Quanto vale la conversione, misurato: turnover lordo ${turnover:,.0f}/anno su "
|
||||
f"${CAP:,.0f}")
|
||||
print(f" {'bps FX/ordine':>15s}{'Sharpe':>9s}{'dSharpe':>10s}{'costo/anno':>13s}")
|
||||
b0 = None
|
||||
for p in (0.0, 15.0, 25.0, 50.0):
|
||||
m = U.met(U.sleeve(dep, dep, cal, CAP, band_c, can_every, U.cost_form(prop_bps=p)))
|
||||
b0 = m["sharpe"] if b0 is None else b0
|
||||
print(f" {format(p,'.0f')+' bps':>15s}{m['sharpe']:>9.2f}{m['sharpe']-b0:>+10.2f}"
|
||||
f"{'$'+format(turnover*p/1e4,',.0f'):>13s}")
|
||||
print(" Cioe' a 25bps la conversione equivale a ~$1.6 in piu' su ciascuno dei 21 ordini:")
|
||||
print(" reale, ma piccolo rispetto alla soglia (${:.2f}/ordine a ${:,.0f}).".format(
|
||||
float(soglia_per_ordine(dep, cal, CAP, band_c, can_every)[1:]), int(CAP)))
|
||||
|
||||
# ---------- (6) ripiego sulla gamba small cap ----------
|
||||
print("\n" + "=" * 100)
|
||||
print(" (6) SE IL BROKER NON HA NESSUNA LINEA RUSSELL 2000")
|
||||
print("=" * 100)
|
||||
tic, isin_r, desc_r = RIPIEGO_SMALLCAP
|
||||
print(f" Ripiego comune: {tic} {isin_r} — {desc_r}. NON e' lo stesso indice (MID cap, non")
|
||||
print(" small): va misurato, non assunto equivalente.")
|
||||
alt = dict(dep)
|
||||
alt["IWM"], _ = U.close_uc(tic, us["IWM"])
|
||||
# finestra lunga: veicoli STORIA sulle altre 5 gambe, cosi' il campione non e' limitato da XNAS
|
||||
st_set = {s: U.close_uc(u, us[s])[0] for s, u in U.SET_STORIA.items()}
|
||||
small = dict(st_set)
|
||||
small["IWM"], _ = U.close_uc("R2US", us["IWM"])
|
||||
mid = dict(st_set)
|
||||
mid["IWM"] = alt["IWM"]
|
||||
cl = U.common_cal(list(small.values()) + [mid["IWM"]])
|
||||
st = max(U._norm(x).index[0] for x in list(small.values()) + [mid["IWM"]])
|
||||
cl = cl[cl >= st + np.timedelta64(380, "D")]
|
||||
print(f"\n finestra {cl[0].date()} -> {cl[-1].date()} ({len(cl)/252:.1f} anni)")
|
||||
s1 = U.sleeve(small, small, cl, CAP, BAND_FRAC * CAP / 6.0, can_every, U.cost_form(fixed=1.0))
|
||||
s2 = U.sleeve(mid, mid, cl, CAP, BAND_FRAC * CAP / 6.0, can_every, U.cost_form(fixed=1.0))
|
||||
for nome, s in [("R2US small cap", s1), ("SPY4 mid cap ", s2)]:
|
||||
m = U.met(s)
|
||||
print(f" {nome} Sharpe {m['sharpe']:.2f} CAGR {m['cagr']:.2%} maxDD {m['dd']:.1%}")
|
||||
peggio = sum(1 for y, g in s2.groupby(s2.index.year)
|
||||
if len(g) > 100 and U.met(g)["sharpe"] < U.met(s1[s1.index.year == y])["sharpe"])
|
||||
tot = sum(1 for y, g in s2.groupby(s2.index.year) if len(g) > 100)
|
||||
print(f" correlazione dei due sleeve {s1.corr(s2):.4f} · SPY4 peggiore in {peggio}/{tot} anni")
|
||||
print("\n -> RIPIEGO ACCETTABILE, e il motivo e' meccanico: small e mid cap USA hanno")
|
||||
print(" correlazione giornaliera ~0.95, e un segnale TSMOM su due serie cosi' vicine si")
|
||||
print(" accende quasi negli stessi giorni. ⚠️ NON e' 'validato': 6.5 anni non bastano a")
|
||||
print(" distinguere due gambe cosi' simili, ed e' esattamente per questo che la scelta fra")
|
||||
print(" le due non e' importante. Se c'e' una linea Russell 2000, si usa quella.")
|
||||
print("\n" + "=" * 100)
|
||||
|
||||
|
||||
|
||||
@@ -118,3 +118,42 @@ def test_le_isin_sono_ben_formate_e_coprono_le_sei_gambe():
|
||||
def test_le_soglie_di_lettura_sono_dichiarate():
|
||||
assert B.BAND_FRAC == 0.25
|
||||
assert B.ESPOSTA_SANA == 0.60
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# equivalenze fra listini — la stessa ISIN su borse diverse e' lo STESSO fondo
|
||||
# ===========================================================================
|
||||
def test_le_equivalenze_dichiarate_condividono_la_isin_del_veicolo_scelto():
|
||||
"""ZPRR (Xetra EUR) e R2US (Londra USD) sono la stessa classe di quote: se le ISIN non
|
||||
coincidono l'equivalenza e' falsa e l'operatore comprerebbe un altro fondo."""
|
||||
for a, b, isin, _ in B.EQUIVALENZE:
|
||||
assert a in B.ISIN, f"{a} non e' un veicolo del set DEPLOY"
|
||||
assert B.ISIN[a][0] == isin, f"{a}: la ISIN dell'equivalenza non e' quella del veicolo"
|
||||
assert re.fullmatch(r"IE00[A-Z0-9]{8}", isin)
|
||||
assert b not in B.ISIN, f"{b} e' un ALIAS di {a}, non un veicolo distinto"
|
||||
|
||||
|
||||
def test_il_ripiego_smallcap_e_dichiarato_come_indice_DIVERSO():
|
||||
"""SPY4 e' S&P 400 MID cap: e' un ripiego misurato, non un equivalente. Il test esiste
|
||||
perche' la differenza fra 'equivalente' e 'ripiego accettabile' e' proprio cio' che si
|
||||
perde quando si copiano ticker da una lista."""
|
||||
tic, isin, desc = B.RIPIEGO_SMALLCAP
|
||||
assert re.fullmatch(r"IE00[A-Z0-9]{8}", isin)
|
||||
assert isin != B.ISIN["R2US"][0], "un ripiego con la stessa ISIN sarebbe un'equivalenza"
|
||||
assert "Mid" in desc
|
||||
|
||||
|
||||
def test_il_ripiego_smallcap_e_indistinguibile_dalla_gamba_scelta(ctx):
|
||||
"""Il finding della sezione (6): sostituire mid a small lascia lo sleeve al 99% uguale.
|
||||
Se un giorno smettesse di essere vero, la raccomandazione 'usa SPY4 se non trovi R2US'
|
||||
andrebbe ritirata."""
|
||||
dep, cal = ctx
|
||||
us = {s: U.close_us(s) for s in U.US}
|
||||
mid = dict(dep)
|
||||
mid["IWM"], _ = U.close_uc(B.RIPIEGO_SMALLCAP[0], us["IWM"])
|
||||
cl = U.common_cal(list(dep.values()) + [mid["IWM"]])
|
||||
cl = cl[cl >= cal[0]]
|
||||
band = B.BAND_FRAC * 10_000.0 / 6.0
|
||||
a = U.sleeve(dep, dep, cl, 10_000.0, band, 5, U.cost_form(fixed=1.0))
|
||||
b = U.sleeve(mid, mid, cl, 10_000.0, band, 5, U.cost_form(fixed=1.0))
|
||||
assert a.corr(b) > 0.95, f"il ripiego non e' piu' equivalente: corr {a.corr(b):.3f}"
|
||||
|
||||
Reference in New Issue
Block a user