debito 14: il report stampa il TWR (+10,61%), non il bonifico (+243%)
`trades_db.py --report` calcolava e1/e0-1 sulla serie grezza di equity: il 96,3% del numero era il versamento di $1.399,39 del 25/08. La riparazione (journal.movimenti_capitale) esisteva e non aveva attraversato il confine fra i due lettori della stessa serie (P1). - src/live/journal.py: `rendimento_twr` — funzione unica, spezza la serie sui movimenti CERTI e moltiplica i segmenti; gli ambigui restano dentro, dichiarati (P12); tre stati. - scripts/live/trades_db.py: report() la chiama; stampa TWR con segmenti datati, movimenti elencati, trading al netto, delta $ etichettato "movimenti INCLUSI"; il % grezzo sparisce. - test: +5 in test_journal.py (incl. riproduzione del +10,80% del diario 01/09, M23), +2 in test_trades_report.py sul testo stampato con connect() deviato in tmp. 832 verdi. - docs: CLAUDE.md §5.14 chiuso, §2 e §13 aggiornati; memoria 40; diario 02/09. Limite ereditato e dichiarato (D5): +10% di trading fra due letture consecutive tocca la soglia del rilevatore e a mercato fermo verrebbe classificato movimento. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RziUCB336YPUUyDJ29x4Ke
This commit is contained in:
@@ -501,3 +501,70 @@ def test_ogni_soglia_dice_COSA_decidere_non_solo_che_e_arrivata():
|
||||
"""P4: un'allerta risponde a due domande. Una riga che dice solo «sei a $15k» non serve."""
|
||||
for _, testo in J.SOGLIE_CAPITALE:
|
||||
assert len(testo) > 80 and "**" in testo, "la soglia non dice quale decisione sblocca"
|
||||
|
||||
|
||||
# ------------------------------------------------- TWR: il rendimento che non e' un bonifico
|
||||
# Debito #14 (2026-09-01): `trades_db --report` stampava `e1/e0-1` sulla serie grezza, «+243%»
|
||||
# per il 96,3% un versamento. La riparazione (`movimenti_capitale`) viveva qui e non aveva
|
||||
# attraversato il confine fra i due lettori. `rendimento_twr` e' la funzione che ENTRAMBI chiamano.
|
||||
|
||||
def test_twr_spezza_sul_versamento_e_il_grezzo_no(db, monkeypatch):
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: _px_piatto())
|
||||
# +5% di trading: SOTTO la soglia del rilevatore (0,10) — un +10% con mercato piatto
|
||||
# sarebbe esso stesso un "movimento", ed e' il limite dichiarato (D5) del classificatore.
|
||||
_serie_equity(db, [("2026-08-24T10:47:01+00:00", 600.0),
|
||||
("2026-08-25T10:47:01+00:00", 630.0), # +5% di trading
|
||||
("2026-08-25T11:47:01+00:00", 2030.0), # +1400: versamento
|
||||
("2026-08-26T10:47:01+00:00", 2009.7)]) # -1% di trading
|
||||
r = J.rendimento_twr(db, "2026-08-26T23:59:59+00:00")
|
||||
assert r["twr"] == pytest.approx(1.05 * 0.99 - 1.0) # +3,95%
|
||||
assert r["certi"] == pytest.approx(1400.0) and r["ambigui"] == 0.0
|
||||
assert [round(s["ret"], 4) for s in r["segmenti"]] == [0.05, -0.01]
|
||||
assert r["trading"] == pytest.approx(2009.7 - 600.0 - 1400.0) # +9,7
|
||||
grezzo = 2009.7 / 600.0 - 1.0 # +235%: il numero sbagliato
|
||||
assert abs(r["twr"] - grezzo) > 2.0
|
||||
|
||||
|
||||
def test_twr_senza_movimenti_e_il_rendimento_semplice(db, monkeypatch):
|
||||
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)])
|
||||
r = J.rendimento_twr(db, "2026-08-25T23:59:59+00:00")
|
||||
assert r["twr"] == pytest.approx(0.05) and len(r["segmenti"]) == 1
|
||||
assert r["trading"] == pytest.approx(30.0) and r["eventi"] == []
|
||||
|
||||
|
||||
def test_twr_un_ambiguo_non_spezza_e_resta_dichiarato(db, monkeypatch):
|
||||
# stesso caso del test sul crash a tutta leva: -14% con il mercato a -15% -> ambiguo
|
||||
ix = pd.date_range("2026-08-20", "2026-08-27", freq="1h", tz="UTC")
|
||||
px = pd.Series(100.0, index=ix)
|
||||
px[ix >= "2026-08-25T11:00:00+00:00"] = 85.0
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: px)
|
||||
_serie_equity(db, [("2026-08-25T10:47:01+00:00", 600.0),
|
||||
("2026-08-25T11:47:01+00:00", 516.0)])
|
||||
r = J.rendimento_twr(db, "2026-08-25T23:59:59+00:00")
|
||||
assert r["twr"] == pytest.approx(-0.14) and len(r["segmenti"]) == 1
|
||||
assert r["certi"] == 0.0 and r["ambigui"] == pytest.approx(-84.0)
|
||||
assert r["trading"] == pytest.approx(-84.0) # niente scorporo
|
||||
|
||||
|
||||
def test_twr_serie_vuota_e_None_con_motivo_non_zero(db):
|
||||
r = J.rendimento_twr(db, "2026-08-25T23:59:59+00:00")
|
||||
assert r["twr"] is None and r["motivo"] and r["trading"] is None
|
||||
|
||||
|
||||
def test_twr_riproduce_il_numero_del_diario_01_09(db, monkeypatch):
|
||||
# M23: prima di pubblicare un numero nuovo, far riprodurre alla macchina quello vecchio.
|
||||
# Diario 2026-09-01-stato-trades: 598,06 -> 667,49 | +1.399,39 | 2.066,88 -> 2.051,84 = +10,80%
|
||||
ix = pd.date_range("2026-06-20", "2026-09-02", freq="1h", tz="UTC")
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: pd.Series(100.0, index=ix))
|
||||
# una lettura intermedia: nella serie vera ci sono ~1.500 letture orarie fra le due date,
|
||||
# e nessun salto di trading fra due consecutive tocca la soglia del rilevatore
|
||||
_serie_equity(db, [("2026-06-23T22:00:00+00:00", 598.06),
|
||||
("2026-07-20T10:47:01+00:00", 630.00),
|
||||
("2026-08-25T10:47:01+00:00", 667.49),
|
||||
("2026-08-25T11:47:01+00:00", 2066.88),
|
||||
("2026-09-01T16:47:01+00:00", 2051.84)])
|
||||
r = J.rendimento_twr(db, "2026-09-01T23:59:59+00:00")
|
||||
assert round(100 * r["twr"], 2) == 10.80
|
||||
assert r["certi"] == pytest.approx(1399.39)
|
||||
|
||||
Reference in New Issue
Block a user