d597fc64e8
S5.1 RIPARATO E RIGENERATO. Filtro condiviso src/live/paper_guard.py (barra open-labeled chiusa = ts + cadenza <= adesso) importato da tutti e 6 i monitor; serie rigenerate dallo stesso start_ts con scripts/live/paper_regen.py (evidenza in *.pre_regen_20260826.*): statarb +1,95 -> -1,61 (il ribaltamento del gate 27/09 previsto dall'audit), dvolspread -14,73 -> -4,41, xsr -4,98 -> -2,72, prevday invariato. Nessuna data di gate si sposta. Guardia cablata in monitor_health: stato PREMATURO (ultima barra che chiude dopo l'mtime, grazia 5 min, open_labeled=False per collect_chain) — sul dato vivo segnala i 5 rotti e tace sui 2 sani; dopo la rigenerazione 7/7 OK. paper_portfolio non rigenerato (GTAA su ADJUSTED_LAST: replay != serie registrata, P12), tolta la coda non chiusa. D6 pagata di nuovo nel fix: asi8 in pandas 3 e' in us, non ns — blindata con test su tre risoluzioni. S5.12 ESTESO: conftest devia anche trades.db (wrapper su connect: il default e' catturato alla definizione) e docs/journal/; book_executions.jsonl sorvegliato con impronta inizio/fine suite. S5.5 FATTO: test_leva_massima cancellato con nota (misurava frac*n_asset: con una chiave di scala avrebbe continuato a passare smettendo di controllare). S5.9 INDAGATO E RIPARATO (r0826_skh_band_drift): il dato regge (taglio 02/07 riproduce l'audit 1,6376, in-sample identico su ogni taglio); la deriva era la finestra hold-out — e la sola settimana 15-22/08 vale +0,35 di Sharpe hold-out. Il test ora taglia il feed al 02/07 e verifica la riproduzione stretta. Suite: 751 passati, 0 falliti. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B9UyJLHzR7EJzxR3iQ3RN1
101 lines
4.5 KiB
Python
101 lines
4.5 KiB
Python
"""Test per r0726_fee_sensitivity.py — sensibilita' del book live al cambio fee Deribit.
|
|
|
|
Due categorie:
|
|
* le repliche parametrizzate sulla fee devono riprodurre BIT-EXACT gli sleeve di produzione
|
|
alla fee canonica (5 bps/lato), altrimenti la curva descrive un'altra strategia;
|
|
* il test sul CAP e' una guardia di DECISIONE: la conclusione "liquidation fee 1% irrilevante"
|
|
poggia sulla leva <= 1x che discende da `live.json`. Se qualcuno alza il cap, quella
|
|
conclusione decade e il test deve dirlo invece di lasciarla in giro valida per inerzia.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
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"))
|
|
sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt"))
|
|
|
|
import r0726_fee_sensitivity as FS # noqa: E402
|
|
|
|
|
|
# ===========================================================================
|
|
# le repliche alla fee canonica == produzione
|
|
# ===========================================================================
|
|
def test_tp01_alla_fee_canonica_riproduce_lo_sleeve():
|
|
from src.portfolio.portfolio import to_daily
|
|
from src.portfolio.sleeves import _tp01_returns
|
|
from src.strategies.trend_portfolio import CANONICAL
|
|
|
|
mine = FS.tp01_at_fee(CANONICAL["fee_side"])
|
|
ref = to_daily(_tp01_returns())
|
|
assert len(mine) == len(ref)
|
|
assert float(np.max(np.abs(mine.values - ref.values))) == 0.0
|
|
|
|
|
|
def test_skh01_alla_fee_canonica_riproduce_lo_sleeve():
|
|
from src.portfolio.portfolio import to_daily
|
|
from src.portfolio.sleeves import _skyhook_returns
|
|
|
|
mine = FS.skh01_at_fee(0.001) # fee_rt canonico dello sleeve
|
|
ref = to_daily(_skyhook_returns())
|
|
assert len(mine) == len(ref)
|
|
assert float(np.max(np.abs(mine.values - ref.values))) == 0.0
|
|
|
|
|
|
def test_la_fee_canonica_del_progetto_e_5bps_per_lato():
|
|
"""Se qualcuno cambia il default, la curva va ri-ancorata: 'oggi' non sarebbe piu' 5 bps."""
|
|
from src.strategies.trend_portfolio import CANONICAL
|
|
|
|
assert CANONICAL["fee_side"] == pytest.approx(0.0005)
|
|
assert 5.0 in FS.BPS_GRID
|
|
|
|
|
|
# ===========================================================================
|
|
# monotonicita': piu' fee, meno rendimento
|
|
# ===========================================================================
|
|
def test_piu_fee_meno_sharpe_e_meno_cagr():
|
|
from src.portfolio.portfolio import metrics
|
|
|
|
lo = metrics(FS.tp01_at_fee(0.0))
|
|
hi = metrics(FS.tp01_at_fee(0.0015))
|
|
assert lo["sharpe"] > hi["sharpe"]
|
|
assert lo["cagr"] > hi["cagr"]
|
|
|
|
|
|
def test_skh01_e_piu_sensibile_alla_fee_di_tp01():
|
|
"""Il fatto che guida la raccomandazione (se il taker salisse, rivedere il peso di SKH01):
|
|
SKH01 fa round-trip discreti, TP01 e' una posizione lenta vol-targeted."""
|
|
from src.portfolio.portfolio import metrics
|
|
|
|
d_tp = metrics(FS.tp01_at_fee(0.0))["cagr"] - metrics(FS.tp01_at_fee(0.001))["cagr"]
|
|
d_sk = metrics(FS.skh01_at_fee(0.0))["cagr"] - metrics(FS.skh01_at_fee(0.002))["cagr"]
|
|
assert d_sk > d_tp
|
|
|
|
|
|
# ===========================================================================
|
|
# guardia di decisione RIMOSSA (2026-08-26) — e il perche' va letto, non saltato
|
|
# ===========================================================================
|
|
# Qui viveva `test_leva_massima_da_config_resta_sotto_o_uguale_a_1x`: verificava che
|
|
# `max_notional_per_asset_frac x n_asset <= 1.0`, a presidio della conclusione
|
|
# "liquidation fee 1% irrilevante" (r0726_fee_sensitivity).
|
|
#
|
|
# E' stato CANCELLATO, non aggiustato, per la ragione registrata in CLAUDE.md §5.5:
|
|
# misurava `frac x n_asset`, ma se il progetto introducesse la chiave di scala di
|
|
# SPEC-scale-key la grandezza vera diventerebbe `frac x n_asset x scala` — e questo
|
|
# test avrebbe CONTINUATO A PASSARE smettendo di controllare (P1: un sorvegliante che
|
|
# non deriva il bersaglio dal codice sorvegliato passa sempre). Una guardia che passa
|
|
# a bersaglio cambiato e' peggio di nessuna guardia: certifica una proprieta' che non
|
|
# vale piu'.
|
|
#
|
|
# La guardia GIUSTA e' gia' scritta nella spec: GATE SCALA-01 impone il tetto sul
|
|
# PRODOTTO `n_asset · frac · scala · disaster_sl_pct <= 0,50` — va cablata QUANDO (se)
|
|
# la chiave di scala entrera' in config, dentro il codice che la legge, non qui.
|
|
# Fino ad allora: la leva resta 1,0x per costruzione (nessuna chiave di scala esiste,
|
|
# verificato in §3 di CLAUDE.md) e il disaster-SL e' presidiato dai test del book.
|