revisione 02/09, seconda tornata: il classificatore dei movimenti era cieco ~23 ore al giorno
Il feed 1h certificato si ferma alle 00:00: per le letture successive `asof` dava la stessa barra a t0 e t1, mercato "fermo" = 0, e qualunque calo >=10% del giorno sarebbe stato un "movimento" scorporato come prelievo (giornale dal 25/08, report da oggi). Con feed assente tutto era "ambiguo" e il report stampava e1/e0-1 sotto l'etichetta TWR. - journal.movimenti_capitale: stato "mercato non misurabile" (feed fermo/assente/barra mancante) => ambiguo con la ragione; leva_tetto = min(frac x n x scala, LEVA_LORDA_MAX) (P1). - journal.rendimento_twr: salto non classificabile => twr E trading None con motivo. - journal.pnl_giorno CHIAMA rendimento_twr (prima rifaceva cum - certi: "entrambi la chiamano" era falso); la pagina stampa il TWR; analista qualifica il cumulato "di cui versati". - trades_db --report: la classe di ogni salto con la sua misura (mercato max a leva piena). - book_execute docstring: banda dello stop -33,5%/-26,5% (era invertita), "non costa ordini" -> micro-ordini di ri-taglia (22/48, C2), latenza <=1h solo con feed fresca, conteggi di r0823 al posto di "raddoppia" (2 contro 1 e' rotolante vs pavimento sulla riga 4h; la peggiore misurata e' 24h). - test_book_cadenza: LTF_MIN importato da skyhook; tutte le righe attive della crontab (una sola); tre stati dello skip (col progetto ma senza cron_book => ROSSO); slot di release testato con venue_probe (il :07 come controllo positivo); parser regolare, passo > 0. - r0823_sl_anchor: guardia sul :47 (era sul vecchio :07), prosa al passato; cron_chain.sh idem. - CLAUDE.md: §5.7 frase invertita corretta, §5.14 limite vero (non l'artefatto della fixture), §2 ora della LETTURA (13:47Z), nuovo debito §5.15 (USDE fuori dal bound di mercato). - diari: tempi "scritto" corretti coi commit; sezione "Seconda tornata". Test +11 (858 verdi). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDJsH3iDSaBns3ccpPBwiu
This commit is contained in:
@@ -601,3 +601,61 @@ def test_twr_senza_tempo_a_mercato_e_zero_non_None(db, monkeypatch):
|
||||
("2026-08-24T11:47:01+00:00", 2000.0)])
|
||||
r = J.rendimento_twr(db, "2026-08-25T23:59:59+00:00")
|
||||
assert r["twr"] == 0.0 and r["segmenti"] == [] and r["trading"] == pytest.approx(0.0)
|
||||
|
||||
|
||||
# ------------------------------------------------- mercato NON misurabile (revisione 02/09)
|
||||
# Il feed 1h certificato si ferma alle 00:00 (rebuild alle 00:30): per il resto del giorno
|
||||
# `asof` restituiva la STESSA barra per le due letture, max_mkt 0, e qualunque calo >=10%
|
||||
# diventava un "movimento" — un crash stampato come prelievo. E con il feed assente tutto
|
||||
# era "ambiguo", certi=0, e il TWR tornava a essere e1/e0-1 sotto l'etichetta sbagliata.
|
||||
|
||||
def test_un_salto_oltre_la_fine_del_feed_e_ambiguo_con_la_ragione(db, monkeypatch):
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: _px_piatto()) # copre fino al 27/08
|
||||
_serie_equity(db, [("2026-08-28T10:47:01+00:00", 2000.0),
|
||||
("2026-08-28T11:47:01+00:00", 1760.0)]) # -12% dopo la fine
|
||||
p = J.pnl_giorno(db, date(2026, 8, 28))
|
||||
ev = p["movimenti_giorno"]
|
||||
assert ev[0]["classe"] == "ambiguo" and ev[0]["mercato_max_pct"] is None
|
||||
assert "fermo a 2026-08-27" in ev[0]["mercato_nota"]
|
||||
assert p["mov_giorno"] == 0.0 # NON scorporato
|
||||
assert p["trading_da_arming"] is None and p["twr_da_arming"] is None
|
||||
assert "non classificabile" in p["twr_motivo"]
|
||||
|
||||
|
||||
def test_con_il_feed_assente_il_twr_e_None_non_il_grezzo(db, monkeypatch):
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: None)
|
||||
_serie_equity(db, [("2026-08-25T10:47:01+00:00", 667.0),
|
||||
("2026-08-25T11:47:01+00:00", 2066.0)])
|
||||
r = J.rendimento_twr(db, "2026-08-25T23:59:59+00:00")
|
||||
assert r["twr"] is None and r["trading"] is None
|
||||
assert "non leggibile" in r["motivo"]
|
||||
assert r["eventi"][0]["classe"] == "ambiguo"
|
||||
|
||||
|
||||
def test_il_giornale_e_il_report_usano_la_stessa_funzione(db, monkeypatch):
|
||||
# P1, revisione 02/09: pnl_giorno non rifa' piu' `cum - certi`; prende trading e TWR da
|
||||
# rendimento_twr, e la pagina lo stampa accanto al cumulato
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: _px_piatto())
|
||||
_serie_equity(db, [("2026-08-24T10:47:01+00:00", 600.0),
|
||||
("2026-08-25T10:47:01+00:00", 630.0),
|
||||
("2026-08-25T11:47:01+00:00", 2030.0),
|
||||
("2026-08-25T12:47:01+00:00", 2009.7)])
|
||||
p = J.pnl_giorno(db, date(2026, 8, 25))
|
||||
r = J.rendimento_twr(db, "2026-08-25T23:59:59+00:00")
|
||||
assert p["trading_da_arming"] == pytest.approx(r["trading"])
|
||||
assert p["twr_da_arming"] == pytest.approx(r["twr"]) == pytest.approx(1.05 * 0.99 - 1)
|
||||
md = J.rendi_markdown(db, _voce(pnl=p, giorno="2026-08-25"))
|
||||
assert "TWR **+3.95%**" in md
|
||||
|
||||
|
||||
def test_la_leva_del_classificatore_include_la_scala_e_il_tetto_di_codice(db, monkeypatch, tmp_path):
|
||||
# P1: la chiave di scala del 01/09 entra nel bound da sola; e il fallback e' il tetto di CODICE
|
||||
from src.live import book as B
|
||||
cfg = tmp_path / "live.json"
|
||||
cfg.write_text(json.dumps({"max_notional_per_asset_frac": 0.5, "book_scale_k": 1.25}))
|
||||
monkeypatch.setattr(J, "PROJECT_ROOT", tmp_path); (tmp_path / "config").mkdir()
|
||||
cfg.rename(tmp_path / "config" / "live.json")
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: _px_piatto())
|
||||
_serie_equity(db, [("2026-08-25T10:47:01+00:00", 600.0), ("2026-08-25T11:47:01+00:00", 2000.0)])
|
||||
mv = J.movimenti_capitale(db, "2026-08-25T23:59:59+00:00")
|
||||
assert mv["leva_tetto"] == pytest.approx(min(0.5 * 2 * 1.25, B.LEVA_LORDA_MAX))
|
||||
|
||||
Reference in New Issue
Block a user