5.0 Fase 1: registro persistente degli ordini, stati Pending, orfane adottate e chiuse

Un ordine dall'esito ignoto non viene più abbandonato: entra in
data/state/pending_orders.json prima della chiamata HTTP, l'esito si legge per
orderId (il server non registra il referenceId degli ordini v2) e in mancanza
si ricostruisce dalla posizione comparsa sul conto. Una gamba senza esito porta
il basket in PendingA/PendingB invece di rifiutarlo; alla risoluzione parte la
gamba B, ridimensionata sulle unità eseguite, oppure la gamba A viene richiusa.
Ogni posizione del conto è classificata basket / orfana-bot / esterna: le orfane
del bot vengono adottate e chiuse, le esterne contate e mai toccate. Il picco di
equity ignora i movimenti di cassa. Bonifica da headless, orders.jsonl, contatori
in dashboard, bandit che propone e non applica. ADR-0009, test (m)-(q).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 10:54:50 +02:00
co-authored by Claude Fable 5.1
parent f97f1e1fc3
commit 13d64d1830
36 changed files with 4337 additions and 855 deletions
+8 -3
View File
@@ -1,6 +1,6 @@
# Problemi noti e limiti
Aggiornato: 2026-09-16. Una voce per limite, con lo stato. Quando un limite viene rimosso, la voce si sposta nel `CHANGELOG.md`.
Aggiornato: 2026-09-23. Una voce per limite, con lo stato. Quando un limite viene rimosso, la voce si sposta nel `CHANGELOG.md`.
## Strategia
@@ -15,7 +15,9 @@ Aggiornato: 2026-09-16. Una voce per limite, con lo stato. Quando un limite vien
- L'endpoint delle candele non pagina: al massimo ~10 giorni di M15. Lo storico dipende dai tick forniti dall'utente.
- L'API demo mostra spread di mercato di 0,1-0,7 pip senza markup e un overnight di 0,91 USD/giorno per 10 000 EURUSD. Se l'esecuzione reale applica uno spread diverso, lo si vedrà dallo slippage scritto nel ledger a ogni ingresso.
- Il campo dei costi si chiama `value` (non `amount`, come si era scritto in prima battuta): corretto il 2026-09-16 pomeriggio; le righe del ledger della mattina hanno `markupA/B = 0` e `overnight` nullo per questo motivo.
- Il conto reale dell'utente vale 193,18 USD: con l'esposizione minima di 1000 USD per gamba il Live non è praticabile a prescindere dai cancelli.
- Il conto reale dell'utente vale 224,90 USD (2026-09-23): con l'esposizione minima di 1000 USD per gamba il Live non è praticabile a prescindere dai cancelli.
- **Il server non registra l'`x-request-id` come `referenceId`** degli ordini v2: il lookup per riferimento risponde 404 anche per ordini eseguiti. Dalla 5.0 l'esito si legge per `orderId`; il riferimento resta solo come ripiego (ADR-0009).
- **A margine esaurito il server riduce l'ordine** a un importo fisso (2 000 USD di margine osservati il 16-21/9) invece di rifiutarlo; la regola non è documentata nell'OpenAPI. Il bot registra `unita_richieste` e `unita_eseguite` in `orders.jsonl` e dimensiona la gamba B sulle unità eseguite di A; i limiti di margine della Fase 2 evitano di arrivarci.
## Feed
@@ -27,11 +29,14 @@ Aggiornato: 2026-09-16. Una voce per limite, con lo stato. Quando un limite vien
- **Una sola istanza** per cartella di lavoro: non c'è un lock; due bot sullo stesso conto si contendono le posizioni. Documentato nel runbook, non imposto dal codice.
- Le posizioni salvate in `baskets_state.json` da una modalità diversa non vengono riprese (si riparte dalla riconciliazione del conto).
- Il ledger delle sessioni Demo del 16-21/9 (le 65 righe `segnale_ingresso`/`rifiuto` del post-mortem) non è su questa macchina; l'analisi si basa sullo storico del conto letto via API (D-36).
- La finestra WPF mostra i contatori nuovi (in attesa, orfane, esterne, P&L del conto) nei riquadri esistenti; la scheda Storico, la bonifica con pulsante e la navigazione a sinistra arrivano con la web UI (Fasi 6-7, dopo D-28). In attesa, la bonifica si lancia da headless (`--bonifica`).
- Un movimento di cassa viene riconosciuto dal salto del saldo (oltre 10 USD e 0,25 %): un accredito piccolo sotto quella soglia passa per rumore; un prelievo che coincide con una chiusura viene distinto solo se lo storico del conto risponde.
- Il ciclo settimanale gira solo mentre il bot è acceso la domenica dopo le 10 UTC (o al primo avvio dopo sette giorni).
- La finestra e l'headless usano lo stesso log e lo stesso ledger: se si avviano insieme le righe si mescolano.
## Codice
- `BasketEngine.cs` è un file unico di ~1900 righe: funziona, ma un intervento vi costa più di quanto dovrebbe. Da spezzare (quote poller, riconciliazione, snapshot) in una sessione dedicata.
- `BasketEngine` è spezzato in sei file parziali dalla 5.0; il file principale resta di ~900 righe (loop, decisioni, esecuzione).
- I test dell'interfaccia rendono le pagine in memoria (`UiRenderTests`, con `ENCELADO_RENDER_DIR`), non il comportamento della finestra vera (dialoghi, timer).
- Il test (l) copre i blocchi nel decisore, non la simulazione completa dell'equity stop nel motore live; quella è coperta dal backtest (`EquityStops` in `BacktestResult`) e dal ledger.