revisione 02/09: il rotolante si CONTROLLA ogni ora (non "si ri-ancora"), TWR con limiti dichiarati e tre stati
Quattro segnalazioni della revisione sui commit di oggi, tutte verificate e riparate. 1. book_execute.py (+ CLAUDE.md §5.7, memoria 40): «il disaster-SL rotolante si ri-ancora ogni ora» era falso — ensure_disaster_sl lascia il bracket finche' lo stop e' entro il 5% e la taglia entro il 10%; il giro orario CONTROLLA, ri-ancora oltre la tolleranza (mark +5,263%/-4,762%). Lo stop siede fra -26,3% e -33,3% dal mark corrente. 2. journal.rendimento_twr: limite dichiarato (D5) — l'intervallo che contiene un movimento certo esce intero dal rendimento, il suo P&L di mercato va in `certi`; errore massimo meta' del movimento per costruzione (25/08: ~$0,5). Non si stima (P12). Segmenti a lunghezza zero non prodotti; nessun tempo a mercato -> 0,0 dichiarato numero. 3. base di equity zero: `twr` E `trading` None con motivo (il salto 0->X e' invisibile al classificatore, `trading` valeva l'intero conto); il report stampa n/d. 4. test_book_cadenza: la guardia estrae ogni prescrizione «ogni/every N unita'» e pretende 60 minuti, con controllo positivo (M15); limite P13 dichiarato. Test +5 (847 verdi). Diari 02/09 e 02/09b con la sezione "Revisione". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RziUCB336YPUUyDJ29x4Ke
This commit is contained in:
@@ -286,8 +286,10 @@ d'ancora in modo diverso (nella differenza si cancella in parte, nel livello per
|
||||
mentre il cron gira **ogni ora** (`47 * * * *`): era il docstring a essere sbagliato, e chi lo
|
||||
avesse "corretto" nel verso del cron avrebbe spostato il libro sulla riga 4h, dove BTC
|
||||
**raddoppia** gli scatti del disaster-SL (`r0823_sl_anchor.py`). Ora il docstring dichiara
|
||||
**CADENZA: ORARIA** con la ragione (giro idempotente: latenza SKH01 ≤1h, ri-ancoraggio del
|
||||
rotolante ogni ora) e il divieto esplicito. **`tests/test_book_cadenza.py` (10 test)** tiene
|
||||
**CADENZA: ORARIA** con la ragione (giro idempotente: latenza SKH01 ≤1h; il rotolante e'
|
||||
**controllato** ogni ora e ri-ancorato solo oltre la tolleranza di `ensure_disaster_sl`, mark
|
||||
+5,263%/−4,762% o taglia >10% — «si ri-ancora ogni ora» era una frase sbagliata, corretta in
|
||||
revisione il 02/09) e il divieto esplicito. **`tests/test_book_cadenza.py` (10 test)** tiene
|
||||
d'accordo le tre dichiarazioni — docstring, riga in `cron_book.sh`, **crontab installata**
|
||||
(letta con `crontab -l`; dove non e' leggibile il test si dichiara SALTATO, non verde) — e
|
||||
verifica che la cadenza dichiarata sia 60 min, sotto la griglia 230m e lontana dai 240 della
|
||||
@@ -346,7 +348,7 @@ d'ancora in modo diverso (nella differenza si cancella in parte, nel livello per
|
||||
elencati, e il delta $ grezzo etichettato «movimenti INCLUSI» — il `%` grezzo **non compare piu'**.
|
||||
Test: 5 in `test_journal.py` (fra cui la riproduzione del +10,80% del diario 01/09, M23) + 2 in
|
||||
`test_trades_report.py` sul testo stampato, con `connect()` deviato in tmp. Diario
|
||||
`2026-09-02-debito-14-report-twr.md`. ⚠️ **Limite ereditato e dichiarato (D5):** un +10% di
|
||||
`2026-09-02-debito-14-report-twr.md`. ⚠️ **Due limiti dichiarati dalla revisione del 02/09** (D5): (a) **l'intervallo che contiene un movimento certo esce INTERO dal rendimento** — il suo P&L di mercato finisce in `certi` e fuori da `trading`, con errore massimo pari a META' del movimento riconosciuto per costruzione del classificatore (il 25/08: ~$0,5 su $1.399); non si stima, si dichiara (P12); (b) **a base di equity zero `twr` E `trading` sono None**: il salto 0→X del primo versamento e' invisibile al classificatore e `trading` varrebbe l'intero conto. ⚠️ **Limite ereditato (D5):** un +10% di
|
||||
trading fra due letture consecutive tocca la soglia del rilevatore e, a mercato fermo, verrebbe
|
||||
classificato «movimento» — e' lo stesso limite del rilevatore live, non un difetto nuovo.
|
||||
|
||||
@@ -567,7 +569,7 @@ uv run python scripts/live/trades_db.py --report # stato libro d
|
||||
uv run python scripts/live/trades_db.py --reconcile # incrocio delle 3 fonti sui fill
|
||||
uv run python scripts/live/journal.py # voce del giorno (numeri + lettura)
|
||||
uv run python scripts/live/analista.py --secco # analisi del giorno, senza salvare
|
||||
uv run pytest # test (842, tutti verdi al 02/09)
|
||||
uv run pytest # test (847, tutti verdi al 02/09)
|
||||
```
|
||||
|
||||
```python
|
||||
|
||||
@@ -51,3 +51,13 @@ nel test, nel CLAUDE.md e qui.
|
||||
dollari e la sua pagina ha una forma testata. Aggiungerlo è una riga, ma non era il debito.
|
||||
- Non ho scomposto lo scarto (~$25) fra round-trip + non realizzato e il trading al netto: era già
|
||||
fuori scope il 01/09 e lo resta.
|
||||
|
||||
## Revisione del codice (15:20Z) — tre cose trovate, tutte riparate
|
||||
|
||||
| trovato | vero? | riparazione |
|
||||
|---|---|---|
|
||||
| l'intervallo che contiene un movimento certo esce intero dal rendimento: il suo P&L di mercato finisce in `certi` e fuori da `trading`, e nessun documento lo diceva | sì, per costruzione. Errore massimo = metà del movimento riconosciuto (margine del classificatore). Il 25/08: ~$263 lordi a ±0,2% → ~$0,5 | dichiarato nel docstring, nel report, in CLAUDE.md §5.14 e in memoria 40. Non si stima (P12) |
|
||||
| a base di equity zero `trading` era un numero mentre `twr` era None, e sbagliato: il salto 0→X è invisibile al classificatore, `trading` valeva l'intero conto sotto l'etichetta "al netto dei versamenti" | sì. Non raggiungibile dal cron (non scrive mai 0), ma raggiungibile da un backfill | tre stati veri: `twr` e `trading` entrambi None con motivo; il report stampa n/d. Test in `test_journal.py` e `test_trades_report.py` |
|
||||
| movimento nel primo intervallo o due consecutivi → segmento `+0.00% [x -> x]` | sì, cosmetico | i segmenti a lunghezza zero non si producono; nessun tempo a mercato → TWR 0,0 dichiarato numero. Test |
|
||||
|
||||
Il numero pubblicato non cambia: +10,61% alle 14:02Z, +10,56% alle 14:47Z per la marcatura.
|
||||
|
||||
@@ -43,3 +43,10 @@ minuti) BTC **raddoppia** gli scatti del disaster-SL rotolante (2 contro 1; a 24
|
||||
- Non ho toccato il cron né `cron_book.sh`: la configurazione che gira era quella giusta.
|
||||
- Non ho aggiunto un sorvegliante a runtime sulla cadenza (contare i giri per ora nel log): il
|
||||
giornale già stampa «giri mancanti» ogni giorno, e il test copre la deriva di configurazione.
|
||||
|
||||
## Revisione del codice (15:20Z) — due cose trovate, tutte riparate
|
||||
|
||||
| trovato | vero? | riparazione |
|
||||
|---|---|---|
|
||||
| il docstring nuovo diceva «il disaster-SL rotolante si ri-ancora ogni ora»: falso. `ensure_disaster_sl` lascia il bracket com'è finché lo stop voluto è entro il 5% da quello piazzato e la taglia entro il 10%; il giro orario **controlla**, ri-ancora oltre la tolleranza (mark +5,263% / −4,762%). I «BTC 0 / ETH 1» di `r0823` sono misurati **con** questa isteresi | sì, verificato in `execution.py:233-235`. Propagato anche a CLAUDE.md §5.7 e memoria 40 | riscritto in tutti e tre i posti: «controllato ogni ora, ri-ancorato solo oltre la tolleranza; lo stop siede fra −26,3% e −33,3% dal mark corrente». Frase sbagliata su un meccanismo di sicurezza vivo: era la segnalazione più importante |
|
||||
| la guardia del test cercava due letterali («ogni ~230»): «ogni quattro ore» sarebbe passato | sì | la guardia estrae **ogni** prescrizione «ogni/every N unità» e pretende 60 minuti; controllo positivo (M15) su cinque frasi. Limite dichiarato (P13): una prosa che prescrive senza «ogni» passa |
|
||||
|
||||
@@ -703,7 +703,7 @@ e i vincoli di deploy (PRIIPs/UCITS/broker).
|
||||
movimento certo e moltiplica i segmenti (gli ambigui non spezzano: restano nel rendimento,
|
||||
dichiarati accanto — P12). Il report stampa TWR, segmenti con le date, movimenti elencati,
|
||||
trading al netto, e il delta $ grezzo solo etichettato «movimenti INCLUSI». Verifica M23: la
|
||||
macchina riproduce il +10,80% del diario 01/09 sui suoi quattro punti. **Limite ereditato:** un
|
||||
macchina riproduce il +10,80% del diario 01/09 sui suoi quattro punti. **Due limiti trovati in revisione (02/09):** l'intervallo che contiene un movimento certo esce INTERO dal rendimento (il suo P&L di mercato va in `certi`; errore massimo META' del movimento, per costruzione; il 25/08 ~$0,5) e a base di equity zero `twr` e `trading` sono entrambi None, perche' il salto 0→X del primo versamento e' invisibile al classificatore. I segmenti a lunghezza zero (movimento nel primo intervallo, o due consecutivi) non si stampano. **Limite ereditato:** un
|
||||
+10% di trading fra due letture orarie consecutive, a mercato fermo, sarebbe classificato
|
||||
movimento — e' il limite dichiarato del rilevatore live (D5), non uno nuovo.
|
||||
**Le tre fonti si INCROCIANO e non si sovrascrivono:** log (ora vera) x jsonl (i fill) x venue
|
||||
@@ -923,7 +923,7 @@ rotolante è funzione della **cadenza del cron** (1h: BTC 0 / ETH 1 in 7-8 anni
|
||||
con 2 in 30 giorni), quindi chi avesse "corretto" il cron verso il docstring avrebbe spostato il libro
|
||||
sulla riga peggiore. La riparazione è il docstring stesso — cadenza ORARIA, la ragione (giro
|
||||
idempotente: girare più fitto della griglia non costa ordini, e compra latenza ≤1h per gli
|
||||
ingressi/uscite software di SKH01 e un ri-ancoraggio orario del rotolante) e il divieto scritto —
|
||||
ingressi/uscite software di SKH01 e un **controllo** orario del rotolante — che si ri-ancora solo oltre la tolleranza di `ensure_disaster_sl`, mark +5,263%/−4,762% o taglia >10%: la prima stesura diceva «si ri-ancora ogni ora» ed era sbagliata, corretta in revisione lo stesso giorno) e il divieto scritto —
|
||||
più `tests/test_book_cadenza.py`, che **deriva** (P1) le tre dichiarazioni e le confronta: la riga
|
||||
citata nel docstring, quella dichiarata nell'intestazione di `cron_book.sh` (`47 * * * *`) e la
|
||||
crontab **installata** (`crontab -l`; se non è leggibile il test è SALTATO, non verde — P5). Il
|
||||
|
||||
@@ -14,8 +14,11 @@ data/live/book_executions.jsonl.
|
||||
decide su griglia 230m, ma il giro e' IDEMPOTENTE (riconcilia al target netto corrente: se non
|
||||
cambia nulla -> HOLD), quindi girare piu' fitto della griglia non costa ordini e compra due cose
|
||||
misurate: (1) gli ingressi/uscite SOFTWARE di SKH01 arrivano con latenza <=1h invece che fino a
|
||||
230m; (2) il disaster-SL rotolante (-30%, l'unico on-book) si ri-ancora ogni ora — a cadenza 1h in
|
||||
7-8 anni BTC 0 scatti / ETH 1, sulla riga 4h BTC ne fa 2 (`r0823_sl_anchor.py`).
|
||||
230m; (2) il disaster-SL rotolante (-30%, l'unico on-book) viene CONTROLLATO ogni ora e
|
||||
ri-ancorato solo oltre la tolleranza di `ensure_disaster_sl` (stop a >5% da quello piazzato, cioe'
|
||||
mark +5,263%/-4,762%, o taglia >10%) — con questa isteresi a cadenza 1h in 7-8 anni BTC 0 scatti /
|
||||
ETH 1, sulla riga 4h BTC ne fa 2 (`r0823_sl_anchor.py`). Lo stop siede quindi fra -26,3% e -33,3%
|
||||
dal mark corrente, non a -30% dall'ultima ora: e' rotolante, non un massimo di perdita (CLAUDE.md §1).
|
||||
🚨 NON "correggere" la cadenza a ~230 minuti: fino al 2026-09-02 questo docstring lo prescriveva,
|
||||
ed era il docstring a essere sbagliato, non il cron (debito §5.7). `tests/test_book_cadenza.py`
|
||||
tiene d'accordo docstring, `cron_book.sh` e crontab installata. NON il cron giornaliero: le entrate
|
||||
|
||||
@@ -92,7 +92,11 @@ def report() -> None:
|
||||
f" (restano nel P&L, dichiarati)")
|
||||
for ev in r["eventi"]:
|
||||
print(f" {ev['ts_dopo'][:16]} {ev['delta']:+,.2f} [{ev['classe']}]")
|
||||
print(f" trading da arming : {r['trading']:+,.2f} (equity al netto dei movimenti certi)")
|
||||
if r["trading"] is None:
|
||||
print(f" trading da arming : n/d ({r['motivo']})")
|
||||
else:
|
||||
print(f" trading da arming : {r['trading']:+,.2f} (equity al netto dei movimenti "
|
||||
f"certi; l'ora del movimento esce intera, vedi journal.rendimento_twr)")
|
||||
if r["twr"] is None:
|
||||
print(f" TWR : n/d ({r['motivo']})")
|
||||
else:
|
||||
|
||||
+22
-9
@@ -191,7 +191,19 @@ def rendimento_twr(con, fino_ts: str) -> dict:
|
||||
apre uno all'equity DOPO; il TWR e' il prodotto dei rendimenti di segmento, meno uno. Gli
|
||||
eventi `ambiguo` NON spezzano niente (P12: restano nel rendimento, dichiarati accanto).
|
||||
|
||||
TRE STATI: `twr` e' un numero, oppure None con `motivo` (serie vuota, base zero).
|
||||
⚠️ LIMITE DICHIARATO (D5). L'intervallo che CONTIENE un movimento certo esce INTERO dal
|
||||
rendimento: il suo P&L di mercato (marcatura delle posizioni aperte, funding; in un buco di
|
||||
lettura — fino a 10h nella serie vera — l'intero buco) finisce in `certi`, quindi anche fuori
|
||||
da `trading`. E' limitato per costruzione dal margine del classificatore (|salto| >
|
||||
2·max_mkt·leva·equity), percio' l'errore massimo vale max_mkt·leva·equity: fino a META' del
|
||||
movimento riconosciuto. Il 25/08: libro a ~$263 lordi, mercato a ±0,2% -> ~$0,5 su $1.399,39.
|
||||
Non si stima e non si corregge (P12): si dichiara. Un intervallo di sola marcatura fra due
|
||||
movimenti consecutivi, o prima del primo, ha lunghezza zero e NON produce un segmento.
|
||||
|
||||
TRE STATI: `twr` e `trading` sono numeri, oppure None con `motivo`. Base di equity zero =>
|
||||
entrambi None: il salto 0 -> X del primo versamento e' INVISIBILE a `movimenti_capitale`
|
||||
(una base zero non ha percentuale), quindi `certi` lo perderebbe e `trading` varrebbe
|
||||
l'intero conto — un numero etichettato "al netto dei versamenti" che li contiene tutti.
|
||||
"""
|
||||
rows = con.execute("SELECT ts_utc, equity FROM equity WHERE ts_utc <= ? ORDER BY ts_utc",
|
||||
(fino_ts,)).fetchall()
|
||||
@@ -199,23 +211,24 @@ def rendimento_twr(con, fino_ts: str) -> dict:
|
||||
return dict(twr=None, motivo="nessuna lettura di equity", segmenti=[], eventi=[],
|
||||
certi=0.0, ambigui=0.0, delta_equity=None, trading=None, letture=0)
|
||||
mv = movimenti_capitale(con, fino_ts)
|
||||
certi = [e for e in mv["eventi"] if e["classe"] == "movimento"]
|
||||
e0, e1 = float(rows[0]["equity"]), float(rows[-1]["equity"])
|
||||
if e0 <= 0:
|
||||
return dict(twr=None, trading=None,
|
||||
motivo="base di equity nulla: il primo versamento non e' classificabile",
|
||||
segmenti=[], eventi=mv["eventi"], certi=mv["certi"], ambigui=mv["ambigui"],
|
||||
delta_equity=e1 - e0, letture=len(rows))
|
||||
certi = [e for e in mv["eventi"] if e["classe"] == "movimento"]
|
||||
segmenti, da_ts, da_eq = [], rows[0]["ts_utc"], e0
|
||||
for ev in certi: # gia' in ordine di tempo
|
||||
if da_eq:
|
||||
if da_eq and ev["ts_prima"] != da_ts: # lunghezza zero = niente segmento
|
||||
segmenti.append(dict(da=da_ts, a=ev["ts_prima"], e_da=da_eq, e_a=ev["prima"],
|
||||
ret=ev["prima"] / da_eq - 1.0))
|
||||
da_ts, da_eq = ev["ts_dopo"], ev["dopo"]
|
||||
if da_eq:
|
||||
if da_eq and rows[-1]["ts_utc"] != da_ts:
|
||||
segmenti.append(dict(da=da_ts, a=rows[-1]["ts_utc"], e_da=da_eq, e_a=e1,
|
||||
ret=e1 / da_eq - 1.0))
|
||||
if not segmenti or e0 <= 0:
|
||||
return dict(twr=None, motivo="base di equity nulla", segmenti=segmenti, eventi=mv["eventi"],
|
||||
certi=mv["certi"], ambigui=mv["ambigui"], delta_equity=e1 - e0,
|
||||
trading=e1 - e0 - mv["certi"], letture=len(rows))
|
||||
twr = 1.0
|
||||
for sg in segmenti:
|
||||
for sg in segmenti: # nessun segmento = nessun tempo a mercato = 0
|
||||
twr *= 1.0 + sg["ret"]
|
||||
return dict(twr=twr - 1.0, motivo=None, segmenti=segmenti, eventi=mv["eventi"],
|
||||
certi=mv["certi"], ambigui=mv["ambigui"], delta_equity=e1 - e0,
|
||||
|
||||
@@ -68,10 +68,41 @@ def _cron_installato() -> str | None:
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- regressione sul docstring
|
||||
# La guardia non cerca due letterali («ogni ~230»): estrae OGNI prescrizione di cadenza scritta
|
||||
# come «ogni/every N unita'» e pretende che valga 60 minuti. ⚠️ Limite dichiarato (P13): una
|
||||
# guardia sui numeri non copre il ragionamento — una prosa che prescrive una cadenza senza la
|
||||
# parola «ogni» passa. Il controllo positivo qui sotto dice cosa la guardia VEDE.
|
||||
|
||||
def test_il_docstring_non_prescrive_piu_i_230_minuti():
|
||||
_NUMERI = {"un": 1, "una": 1, "due": 2, "tre": 3, "quattro": 4, "sei": 6, "otto": 8, "dodici": 12}
|
||||
_RX_CADENZA = re.compile(
|
||||
r"\b(?:ogni|every)\s*~?\s*(\d+|un'?|una|due|tre|quattro|sei|otto|dodici)?\s*'?\s*"
|
||||
r"(minuti|minuto|minutes?|min|m|ore|ora|hours?|h)\b", re.I)
|
||||
|
||||
|
||||
def prescrizioni_minuti(testo: str) -> list[int]:
|
||||
"""Ogni «ogni N unita'» del testo, in minuti. «ogni ora» -> 60, «ogni ~230 minuti» -> 230."""
|
||||
out = []
|
||||
for m in _RX_CADENZA.finditer(testo):
|
||||
n_raw, unita = m.group(1), m.group(2).lower()
|
||||
n = 1 if n_raw is None else (int(n_raw) if n_raw.isdigit() else _NUMERI[n_raw.rstrip("'")])
|
||||
out.append(n * (60 if unita.startswith(("or", "h")) else 1))
|
||||
return out
|
||||
|
||||
|
||||
def test_la_guardia_vede_le_prescrizioni_che_deve_vedere():
|
||||
# controllo positivo (M15): i modi in cui il docstring potrebbe ri-prescrivere la riga 4h
|
||||
assert prescrizioni_minuti("va lanciato ogni ~230 minuti con la feed") == [230]
|
||||
assert prescrizioni_minuti("il giro va lanciato ogni quattro ore") == [240]
|
||||
assert prescrizioni_minuti("every 4 hours, then every 230 min") == [240, 230]
|
||||
assert prescrizioni_minuti("controllato ogni ora; ogni 60 minuti") == [60, 60]
|
||||
assert prescrizioni_minuti("fino a 230m di latenza") == [] # non e' una prescrizione
|
||||
|
||||
|
||||
def test_il_docstring_prescrive_solo_la_cadenza_oraria():
|
||||
d = _docstring()
|
||||
assert "ogni ~230" not in d and "ogni 230" not in d
|
||||
p = prescrizioni_minuti(d)
|
||||
assert p, "il docstring deve contenere almeno una prescrizione di cadenza"
|
||||
assert all(x == 60 for x in p), f"prescrizioni non orarie nel docstring: {p}"
|
||||
assert re.search(r"CADENZA:\s*ORARIA", d), "il docstring deve dichiarare la cadenza ORARIA"
|
||||
|
||||
|
||||
|
||||
@@ -568,3 +568,36 @@ def test_twr_riproduce_il_numero_del_diario_01_09(db, monkeypatch):
|
||||
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)
|
||||
|
||||
|
||||
def test_twr_a_base_zero_e_None_anche_sul_trading(db, monkeypatch):
|
||||
# revisione 02/09: a base zero `movimenti_capitale` non vede il salto 0 -> X del primo
|
||||
# versamento, quindi `trading = e1-e0-certi` varrebbe l'intero conto. Tre stati, non due.
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: _px_piatto())
|
||||
_serie_equity(db, [("2026-08-24T09:47:01+00:00", 0.0),
|
||||
("2026-08-24T10:47:01+00:00", 598.06),
|
||||
("2026-08-25T10:47:01+00:00", 610.0)])
|
||||
r = J.rendimento_twr(db, "2026-08-25T23:59:59+00:00")
|
||||
assert r["twr"] is None and r["trading"] is None and "base" in r["motivo"]
|
||||
|
||||
|
||||
def test_twr_due_movimenti_consecutivi_non_producono_segmenti_a_lunghezza_zero(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-24T11:47:01+00:00", 2000.0), # movimento nel PRIMO intervallo
|
||||
("2026-08-24T12:47:01+00:00", 3400.0), # movimento consecutivo
|
||||
("2026-08-25T10:47:01+00:00", 3434.0)]) # +1% di trading
|
||||
r = J.rendimento_twr(db, "2026-08-25T23:59:59+00:00")
|
||||
assert len(r["segmenti"]) == 1 and r["segmenti"][0]["ret"] == pytest.approx(0.01)
|
||||
assert r["twr"] == pytest.approx(0.01)
|
||||
assert all(s["da"] != s["a"] for s in r["segmenti"])
|
||||
assert r["certi"] == pytest.approx(1400.0 + 1400.0)
|
||||
|
||||
|
||||
def test_twr_senza_tempo_a_mercato_e_zero_non_None(db, monkeypatch):
|
||||
# due letture, la seconda e' un versamento: nessun segmento, rendimento 0, dichiarato numero
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: _px_piatto())
|
||||
_serie_equity(db, [("2026-08-24T10:47:01+00:00", 600.0),
|
||||
("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)
|
||||
|
||||
@@ -66,3 +66,17 @@ def test_il_report_senza_movimenti_dice_lo_stesso_numero_di_prima(monkeypatch, c
|
||||
_carica_script().report()
|
||||
out = capsys.readouterr().out
|
||||
assert "TWR : +5.00%" in out and "movimenti capitale" not in out
|
||||
|
||||
|
||||
def test_il_report_a_base_zero_dice_n_d_e_non_l_intero_conto(monkeypatch, capsys):
|
||||
monkeypatch.setattr(J, "_px_1h", lambda a: _px_piatto())
|
||||
con = T.connect()
|
||||
_prepara(con, [("2026-08-24T09:47:01+00:00", 0.0),
|
||||
("2026-08-24T10:47:01+00:00", 598.06),
|
||||
("2026-08-25T10:47:01+00:00", 610.0)])
|
||||
con.close()
|
||||
_carica_script().report()
|
||||
out = capsys.readouterr().out
|
||||
assert "trading da arming : n/d" in out and "TWR : n/d" in out
|
||||
assert "trading da arming : +610.00" not in out # l'intero conto NON e' "trading"
|
||||
assert "(+610.00 di equity, movimenti di capitale INCLUSI)" in out # il fatto resta, etichettato
|
||||
|
||||
Reference in New Issue
Block a user