"""Test della configurazione di esecuzione di GTAA01 su un broker a costo fisso (scripts/research/r0727_gtaa_broker.py). Il finding da bloccare e' UNO: una banda in dollari ASSOLUTI non scala col capitale, e a capitale piccolo congela lo sleeve invece di renderlo frugale. Lo Sharpe non lo dice — resta accettabile — lo dice la QUOTA DI TEMPO A MERCATO. E' la stessa firma del null de-levering che il progetto ha gia' incontrato quattro volte, in una veste nuova. """ from __future__ import annotations import re import sys from pathlib import Path import numpy as np import pytest ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "scripts" / "research")) import r0726_gtaa_ucits as U # noqa: E402 import r0727_gtaa_broker as B # noqa: E402 RAW = ROOT / "data" / "raw" @pytest.fixture(scope="module") def ctx(): for s in list(U.US) + list(U.SET_DEPLOY.values()): pre = "eq" if s in U.US else "eqx" if not (RAW / f"{pre}_{s.lower()}_1d.parquet").exists(): pytest.skip("data/raw incompleto (gitignored)") us = {s: U.close_us(s) for s in U.US} dep = {s: U.close_uc(u, us[s])[0] for s, u in U.SET_DEPLOY.items()} cal = U.common_cal(list(us.values()) + list(dep.values())) start = max(U._norm(x).index[0] for x in dep.values()) return dep, cal[cal >= start + np.timedelta64(380, "D")] def _esposta(dep, cal, cap, band, every=5) -> float: _, P = U.sleeve(dep, dep, cal, cap, band, every, U.cost_form(fixed=1.0), want_pos=True) return float((P > 0).mean().mean()) # =========================================================================== # il finding # =========================================================================== def test_banda_assoluta_larga_congela_lo_sleeve_a_capitale_piccolo(ctx): """IL test. A $3.000 una banda da $400 e' l'80% della gamba: lo sleeve smette di seguire il target. Lo Sharpe resta accettabile, quindi il controllo NON puo' essere sullo Sharpe.""" dep, cal = ctx assert _esposta(dep, cal, 3_000.0, 400.0) < B.ESPOSTA_SANA assert B.ordini_anno(dep, cal, 3_000.0, 400.0, 5) < 10 def test_la_stessa_banda_a_capitale_grande_e_sana(ctx): """Il confronto che rende il test precedente informativo: non e' la banda a essere sbagliata, e' il fatto che sia espressa in dollari assoluti.""" dep, cal = ctx assert _esposta(dep, cal, 10_000.0, 400.0) >= B.ESPOSTA_SANA def test_la_banda_proporzionale_resta_sana_a_ogni_capitale(ctx): dep, cal = ctx for cap in (3_000.0, 10_000.0, 50_000.0): assert _esposta(dep, cal, cap, B.BAND_FRAC * cap / 6.0) >= B.ESPOSTA_SANA, \ f"congelato a ${cap:,.0f}" def test_la_banda_proporzionale_rende_gli_ordini_invarianti_al_capitale(ctx): """E' la proprieta' che serve: il costo annuo scala col capitale invece che restare fisso.""" dep, cal = ctx n = [B.ordini_anno(dep, cal, c, B.BAND_FRAC * c / 6.0, 5) for c in (3_000., 10_000., 50_000.)] assert max(n) - min(n) <= 2, f"ordini/anno non invarianti: {n}" def test_una_banda_piu_larga_non_puo_generare_piu_ordini(ctx): dep, cal = ctx n = [B.ordini_anno(dep, cal, 10_000.0, b, 5) for b in (25.0, 100.0, 400.0)] assert n == sorted(n, reverse=True), f"monotonia violata: {n}" # =========================================================================== # la soglia per il broker # =========================================================================== def test_la_soglia_per_ordine_cresce_col_capitale(ctx): """Con banda proporzionale gli ordini sono costanti ma il capitale cresce: la tariffa tollerabile deve crescere in proporzione.""" dep, cal = ctx v = [] for cap in (3_000.0, 10_000.0): s = B.soglia_per_ordine(dep, cal, cap, B.BAND_FRAC * cap / 6.0, 5) v.append(60.0 if s.startswith(">") else float(s[1:])) assert v[1] > 2 * v[0], f"la soglia non scala col capitale: {v}" def test_la_banda_proporzionale_alza_la_soglia_rispetto_al_canonico(ctx): """La ragione operativa di tutto il filone: a $3.000 si passa da ~$2 a ~$5 per ordine.""" dep, cal = ctx can = B.soglia_per_ordine(dep, cal, 3_000.0, 50.0, 5) prop = B.soglia_per_ordine(dep, cal, 3_000.0, B.BAND_FRAC * 3_000.0 / 6.0, 5) assert float(prop[1:]) > 2 * float(can[1:]) # =========================================================================== # cio' che l'operatore copia e incolla # =========================================================================== def test_le_isin_sono_ben_formate_e_coprono_le_sei_gambe(): """Le ISIN finiscono in un campo di ricerca su un conto reale: un refuso qui compra un'altra cosa. Formato ISO 6166 e domicilio irlandese (e' il motivo per cui sono acquistabili nell'UE).""" assert set(B.ISIN) == set(U.SET_DEPLOY.values()) for t, (isin, desc) in B.ISIN.items(): assert re.fullmatch(r"IE00[A-Z0-9]{8}", isin), f"{t}: ISIN malformata {isin}" assert desc.strip() def test_le_soglie_di_lettura_sono_dichiarate(): assert B.BAND_FRAC == 0.25 assert B.ESPOSTA_SANA == 0.60