Fase 0 della 5.0: piano, post-mortem degli ordini pendenti, domande D-26…D-37

Verifica della diagnosi sul codice e sul conto demo via API: il lookup per
referenceId fallisce perché il server registra un riferimento nullo per gli
ordini v2, e dal terzo ordine eToro ha ridotto ogni esecuzione a 2 000 USD di
margine. Il piano fissa l'ordine delle fasi e le decisioni vincolanti
(orderId come chiave, registro persistente degli ordini, margine come vincolo
di primo livello, picco al netto dei movimenti di cassa).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 10:24:54 +02:00
co-authored by Claude Fable 5.1
parent b8c3c75647
commit f97f1e1fc3
3 changed files with 175 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
# Piano 5.0 — valutazione, ordine e stima
Scritto: 2026-09-23 (Fase 0 del prompt "Encelado 5.0"). Ogni punto del prompt è valutato come **fattibile**, **da chiarire** (con la domanda in `docs/QUESTIONS.md`) o **da rifiutare** (con il motivo). Le stime sono in sessioni di lavoro; una sessione è mezza giornata con verifica verde e commit.
## 0. Verifica della diagnosi preliminare
Verificata il 2026-09-23 sul codice, sul conto demo via API (sola lettura) e sui file di questa macchina.
| Affermazione del prompt | Esito |
|---|---|
| `EtoroBroker.OpenAsync` legge `orderId` e non lo usa più; il polling passa solo da `orders:lookup?referenceId=` | **Confermato** (`src/Encelado.Etoro/EtoroBroker.cs`, metodo `OpenAsync` e `LookupOrderAsync`). |
| Il lookup per `referenceId` risponde 404 | **Confermato e spiegato**: `GET api/v1/trading/info/demo/orders/381739181` (uno degli ordini del bot) risponde `referenceID: 00000000-0000-0000-0000-000000000000`. Per gli ordini v2 il server **non ha registrato** l'`x-request-id` come riferimento, quindi la ricerca per riferimento non può trovarli. La ricerca per `orderId` funziona (verificata su due ordini, v1 e `orders:lookup?orderId=`). |
| `BasketExecutor.SendAsync` ripete il lookup e dichiara la gamba «non eseguita»; il basket è rifiutato e l'ordine resta sul server | **Confermato** nel codice. Dallo storico del conto demo: **21 posizioni forex del bot** fra il 16/9 19:00 e il 21/9 10:15 UTC (EURUSD 1, USDCAD 1, AUDUSD 4, NZDUSD 3, EURAUD 12), tutte gambe singole, tutte chiuse il **21/9 alle 13:26 UTC** in blocco (chiusura manuale, non del bot: il kill-switch non avrebbe trovato slot da chiudere). P&L netto complessivo di quelle 21 gambe: **+289,05 USD**, per fortuna e non per merito. |
| `ReconcileAsync` segnala le posizioni sconosciute una volta e non le tocca; il contatore conta gli slot | **Confermato**. |
| `KillAsync → CloseAllAsync` chiude solo gli slot con `Position` | **Confermato**. |
| Il ledger 16-22/9 ha 65 `segnale_ingresso` e 65 `rifiuto` | **Non verificabile qui**: su questa macchina `Documenti\Encelado\data\ledger\decisions.jsonl` ha 112 righe del solo 16/9 (mattina e pomeriggio, 110 `skip` e 3 `correzione`). Le sessioni dal 16/9 sera in poi hanno girato altrove (installazione 4.0.0 su un'altra macchina o un altro profilo). Chiesto in D-36. |
| «Depositi» = movimenti di cassa del conto virtuale, non operazioni del bot | **Confermato per esclusione**: nessuna rotta di deposito o trasferimento è chiamata dal codice; le rotte di trasferimento dell'API (`api/v1/money/transfers`) non compaiono nel client. La cifra tonda (+30 000) è coerente con un accredito demo. |
| Margine: un basket Aggressive ha impegnato tutto il margine | **Confermato e precisato**: i primi due ordini eseguiti (16/9 19:00:48 EURUSD short 547 214,68 unità, 62 792 USD di margine; 19:00:58 USDCAD long 464 358,5 unità, 46 436 USD) sommano 109 228 USD di margine = l'intera equity, cioè 1 092 281 USD di nozionale a leva 10. Sono due gambe A di due basket diversi (EURUSD/USDCHF e USDCAD/EURUSD), ciascuna sizata al tetto `maxEffectiveLeverage = 10` sul nozionale del basket. **Fatto nuovo**: dal terzo ordine in poi eToro ha eseguito ogni ordine a **esattamente 2 000 USD di margine** (`requestedAmount 2000`, `frozenAmount 2000`, unità con sei decimali ricalcolate dal server, mentre il bot manda unità a due decimali): il server ha ridotto gli ordini a un importo fisso perché il margine era esaurito. Conseguenze: (1) le unità eseguite possono differire molto da quelle richieste, quindi la riconciliazione per «unità ± 1 %» è solo un indizio, non la chiave: la chiave è l'`orderId`; (2) i limiti di margine di §10 sono indispensabili. |
## 1. Valutazione punto per punto
| § | Contenuto | Valutazione | Note |
|---|---|---|---|
| 1 | Scheda Storico (ordini, posizioni, profitti per periodo, export CSV) | **fattibile** | Modulo puro `Core/Baskets/History` + endpoint web. La vista dipende da §11 (web UI). Fonte del realizzato: `api/v1/trading/info/trade/{demo/}history` (già usato), da riconciliare con `baskets.csv` e `orders.jsonl`. |
| 2 | Navigation rail M3 a sinistra | **fattibile** in web (§11); in WPF sarebbe lavoro perso | Da fare dopo D-28. |
| 3 | Versione fuori dalla dashboard, Informazioni e Diagnostica | **fattibile** | Banale in web; in WPF vale la pena solo se D-28 è «no». |
| 4 | Valuta di visualizzazione | **fattibile** | Tassi dalle quote già in polling: EURUSD (1), USDCHF (6), AUDUSD (7), USDCAD (4), NZDUSD (3) ci sono; **GBP e JPY no**: servono GBPUSD e USDJPY nel polling (2 strumenti in più, stessa richiesta). Conversione solo in presentazione. |
| 5 | OrderTracker, lookup per orderId, stati Pending, orfane, contatori, picco netto dei movimenti di cassa, bonifica | **fattibile, prioritario** | Endpoint verificati (D-26, D-27). Il matching per posizioni usa strumento + verso + finestra temporale; le unità solo come conferma (vedi §0). Movimenti di cassa: il conto demo non compare in `api/v1/balances` (solo i conti reali), quindi si riconoscono dal salto del saldo non spiegato dalle chiusure (`CashFlowDetector`), in Demo e in Live allo stesso modo. |
| 6 | Recupero dopo inattività, heartbeat | **fattibile** | Riusa riconciliazione, riscaldamento e decisore già esistenti. |
| 7 | Telegram | **fattibile** | Solo `HttpClient`; token e chatId da ambiente (D-31). Un solo task di long polling. |
| 8 | Apprendimento: disattivare MLP, bandit, ciclo settimanale; spostarli nello strumento | **fattibile**, decisione D-30 | Il bandit oggi **applica** il preset in Demo (`ObserveOutcome`): va spento comunque, è un cambio di parametro automatico che contraddice la regola «nessun parametro cambiato dal bot». |
| 9 | Kill-switch reale e ripristino guidato | **fattibile, prioritario** | Endpoint di cancellazione esiste: `DELETE api/v2/trading/execution/{demo/}orders/{orderId}` (200 = richiesta accettata, esito da confermare con lookup: 7 o 9 annullato, 6 in corso). |
| 10 | Margine: soglie, buffer, sizing = min(rischio, margine), ordine per \|z\|, margin guard | **fattibile, prioritario** | In Demo `available` viene da `pnl` (`credit + bonusCredit`), in Live idem; `api/v1/balances` aggiunge `totalUsedMargin`/`currentPNL` solo per il reale. |
| 11 | UI web M3 servita dal bot, ritiro di WPF | **da chiarire (D-28)** | Tecnicamente fattibile senza NuGet (`Microsoft.AspNetCore.App` è un framework reference). È la scelta più impattante del prompt: cambia progetti, installer, test. Si fa dopo la conferma. |
| 12 | Engine/Server, Docker, IKeyStore | **da chiarire (D-28, D-29)** | Dipende da §11. Il `Dockerfile` multi-stage e l'`entrypoint` con PUID/PGID sono standard. |
| 13 | Template Unraid | **da chiarire (D-29)** | Serve il registry e l'URL dell'icona. |
| 14 | Documentazione, ADR, skill di progetto | **fattibile** | Le skill in `.claude/skills/` vanno bene; `encelado-diagnose` legge i file nuovi. |
| 15 | Domande | poste in `docs/QUESTIONS.md` (D-26…D-37) | D-26 e D-27 già risposte via API. |
### Punti da rifiutare o ridimensionare (con motivo)
- **§5.2 «polling per orderId e in parallelo per referenceId»**: il riferimento non è registrato dal server per gli ordini v2 (vedi §0). Si interroga **per `orderId`**, e per riferimento solo come ripiego quando la risposta al `POST` è andata persa (timeout di rete) — che è l'unico caso in cui l'`orderId` non c'è. Non «in parallelo»: la quota dei lookup è 60/min condivisa con l'esito delle chiusure, e due lookup ogni 400 ms per gamba la esaurirebbero in un minuto con tre basket.
- **§5.4 «unità ± 1 %» come criterio di adozione**: eToro può ridurre l'ordine (vedi §0), quindi le unità non sono affidabili. Criterio applicato: `orderId`/`positionId` nel registro (certo) → strumento + verso + orario ± 90 s coerenti con un ordine del registro o con una riga `segnale_ingresso`/`rifiuto`/`pending` del ledger (probabile; le unità ± 1 % alzano la confidenza ma non sono richieste). Le posizioni «probabili» sono comunque orfane-bot: sul conto demo non c'è altro che operi sul forex a leva 10 con quegli orari di chiusura di barra.
- **§8 bandit in Demo**: già oggi cambia il preset da solo (`ObserveOutcome``SetPreset`). Non è «da tenere in ombra»: è da spegnere del tutto a runtime, perché è un cambio automatico di parametro.
- **§11 test (aa) «UiRenderTests sostituiti»**: i test di rendering WPF restano finché esiste WPF; vengono rimossi con il progetto (Fase 6-7), non prima.
## 2. Ordine di esecuzione e criteri
Invariato rispetto a §16 del prompt, con una precisazione: le Fasi 1-3 non dipendono da nessuna domanda aperta (D-26/27 sono verificate, D-32/34/35 hanno default che sono parametri) e vengono eseguite subito; le Fasi 4-5 nemmeno; le Fasi 6-9 dipendono da D-28/D-29 e aspettano la risposta.
| Fase | Contenuto | Stima | Dipendenze |
|---|---|---|---|
| 0 | questo piano, post-mortem, domande | 0,5 | — |
| 1 | §5: `OrderTracker` + `pending_orders.json` + `orders.jsonl`; lookup per `orderId` con matching sulle posizioni; stati `PendingA/PendingB`; classificazione `basket / orfana-bot / esterna` e chiusura delle orfane; contatori e P&L del conto nello snapshot; picco al netto dei movimenti di cassa; comando `bonifica`; spezzare `BasketEngine.cs` in file parziali (riconciliazione, stato, comandi) | 2 | — |
| 2 | §10: sezione `risk` in `strategy.json`, sizing a margine, ricontrollo di `available` prima di B, ordine per \|z\|, margin guard, `sizing_bound` nel ledger | 1 | Fase 1 |
| 3 | §9: kill-switch con cancellazione, chiusura di basket + orfane, esterne opzionali, verifica di piattezza, `Halted-Residuo`; procedura di ripristino in cinque passi nel motore (la finestra WPF la usa come oggi, la web UI la mostrerà passo per passo) | 1 | Fasi 1-2 |
| 4 | §6: heartbeat, `recovery` in `strategy.json`, procedura di recupero, lock di istanza, rapporto `recupero_<run_id>.csv` | 1 | Fasi 1-3 |
| 5 | §7: `INotifier`, `TelegramNotifier`, stato orario, eventi, riepilogo, comandi | 1 | Fase 4 |
| 6 | §12: `Encelado.Engine`, `Encelado.Server`, Kestrel, SSE, API JSON, `IKeyStore` | 2 | **D-28** |
| 7 | §11 + §1-4 + §8: UI web M3, Storico, valuta, navigazione, Ricerca | 3 | Fase 6 |
| 8 | §12-13: Dockerfile, compose, template Unraid, docs | 1 | Fase 6, **D-29** |
| 9 | §14: documenti, ADR, skill, CHANGELOG, 5.0.0 | 0,5 | tutto |
Totale: circa 13 sessioni. Ogni fase termina con `dotnet build`, `dotnet test`, documenti aggiornati e un commit.
## 3. Decisioni di progetto prese in Fase 0 (vincolanti per le fasi seguenti)
1. **Un ordine non si abbandona mai.** Ogni invio viene registrato in `data/state/pending_orders.json` prima della chiamata HTTP; all'avvio i pendenti si risolvono prima di qualsiasi decisione; una posizione che porta la firma del bot è del bot.
2. **La chiave è l'`orderId`.** Il riferimento cliente resta nell'intestazione (idempotenza) e nel registro, ma non è il mezzo con cui si chiede l'esito.
3. **Nessun esito sintetico.** `OrderOutcome.Status` riporta ciò che il server ha detto; quando non ha detto niente lo stato è `Unknown` e l'ordine resta pendente nel registro.
4. **Le unità eseguite le dice il server.** Il sizing propone, l'esecuzione registra ciò che l'API riporta (`positionExecutions[].openingData.units`), e la gamba B si dimensiona sulle unità **eseguite** di A, non su quelle richieste.
5. **Il margine è un vincolo di primo livello**, non una conseguenza: nessun ordine parte se `available` non copre il margine con il buffer.
6. **Il picco di equity è al netto dei movimenti di cassa**: depositi e prelievi spostano il riferimento, non il drawdown.
7. **Il codice nuovo di logica pura va nel Core** (`OrderTracker`, `PositionClassifier`, `CashFlowDetector`, `History`, sizing a margine) così la migrazione a `Encelado.Engine` della Fase 6 sposta file, non riscrive niente.
@@ -0,0 +1,82 @@
# Post-mortem: ordini dall'esito ignoto e gambe orfane (16-21 settembre 2026)
Scritto: 2026-09-23. Riguarda il forward test in Demo della versione 4.0.0.
## Che cosa è successo
Dal 16 settembre (sera) al 21 settembre il bot ha inviato ordini di apertura della gamba A di vari basket, ha dichiarato ogni gamba «non eseguita» perché non riusciva a leggerne l'esito, ha rifiutato il basket e non ha inviato la gamba B. Gli ordini però erano stati eseguiti dal server: sul conto demo si sono accumulate **posizioni singole senza copertura**, con lo stop nativo ma senza take-profit né stop di basket, che il bot vedeva come «posizioni sconosciute» e non toccava. Nessun basket è mai passato in `Open`; il contatore in dashboard diceva `0/5` mentre il conto aveva fino a venti posizioni aperte.
## Evidenze
Dallo storico del conto demo (`api/v1/trading/info/trade/demo/history`, letto il 2026-09-23):
| Apertura (UTC) | Strumento | Verso | Unità | Margine USD | P&L netto |
|---|---|---|---|---|---|
| 16/9 19:00:48 | EURUSD | short | 547 214,68 | 62 792,33 | 355,69 |
| 16/9 19:00:58 | USDCAD | long | 464 358,50 | 46 435,84 | +414,56 |
| 16/9 20:00:14 | AUDUSD | long | 28 207,949 | 2 000,00 | +122,14 |
| 16/9 22:15:01 | AUDUSD | long | 28 226,660 | 1 999,99 | +135,49 |
| 16/9 23:01:22 | AUDUSD | long | 28 221,482 | 2 000,00 | +131,79 |
| 17/9 00:45:04 | EURAUD | long | 17 444,888 | 1 999,99 | 84,13 |
| 17/9 01:15:03 | EURAUD | long | 17 448,661 | 1 999,99 | 76,67 |
| 17/9 02:00:23 | EURAUD | long | 17 452,394 | 1 999,99 | 72,96 |
| 17/9 03:15:01 | EURAUD | long | 17 450,257 | 1 999,99 | 51,91 |
| 17/9 12:30:02 | AUDUSD | short | 28 073,722 | 2 000,00 | 26,39 |
| 18/9 04:00:03 | NZDUSD | short | 34 883,315 | 1 999,99 | +17,79 |
| 18/9 04:15:04 | NZDUSD | short | 34 843,813 | 2 000,00 | +40,42 |
| 18/9 07:00:32 | EURAUD | short | 17 418,146 | 1 999,99 | +18,89 |
| 18/9 08:15:04 | NZDUSD | short | 34 950,981 | 1 999,99 | 20,97 |
| 18/9 09:15:03 | EURAUD | short | 17 419,919 | 1 999,99 | +26,97 |
| 18/9 10:15:01 | EURAUD | short | 17 415,984 | 2 000,00 | +27,83 |
| 21/9 06:45:02 | EURAUD | short | 17 427,466 | 1 999,99 | +12,68 |
| 21/9 07:15:03 | EURAUD | short | 17 432,862 | 2 000,00 | +9,57 |
| 21/9 08:15:02 | EURAUD | short | 17 425,358 | 1 999,99 | 0,62 |
| 21/9 09:00:44 | EURAUD | short | 17 421,555 | 1 999,99 | +7,95 |
| 21/9 10:15:03 | EURAUD | short | 17 420,945 | 1 999,99 | +12,31 |
Tutte chiuse il 21/9 alle 13:26 UTC in blocco (chiusura manuale). Totale: 21 gambe, **+289,05 USD** netti, 0 commissioni. Il segno positivo è casuale: gambe singole senza copertura, nessuna regola di uscita applicata per cinque giorni.
Osservazioni:
1. Gli orari sono chiusure di barra M15 (`:00`, `:15`, `:30`, `:45` più pochi secondi): sono ordini del bot. Ogni volta che il segnale restava oltre `zIn` alla barra successiva, il basket — tornato `Idle` dopo il «rifiuto» — inviava **un'altra gamba A**. Le dodici EURAUD sono lo stesso segnale ripetuto per dodici barre.
2. `GET api/v1/trading/info/demo/orders/381739181` (l'ordine EURUSD) risponde `referenceID: 00000000-0000-0000-0000-000000000000`: il server **non ha associato** l'`x-request-id` all'ordine. La ricerca `orders:lookup?referenceId=<x-request-id>` non poteva che rispondere 404. La ricerca per `orderId` risponde 200 con `status.id = 3 (Filled)` e la posizione.
3. I primi due ordini hanno impegnato **109 228 USD di margine, cioè tutta l'equity** (1 092 281 USD di nozionale a leva 10): erano le gambe A di due basket diversi, ciascuna sizata fino al tetto `maxEffectiveLeverage = 10` calcolato sul nozionale del *basket* senza guardare il margine disponibile.
4. Dal terzo ordine in poi ogni esecuzione vale **esattamente 2 000 USD di margine**: `orders:lookup?orderId=382724150` mostra `requestedAmount 2000`, `frozenAmount 2000`, `requestType byUnits`, `requestedUnits 17420.945125`. Il bot manda unità arrotondate a due decimali; sei decimali significano che il server ha **ricalcolato le unità da un importo**: con il margine esaurito eToro ha ridotto l'ordine a un importo fisso invece di rifiutarlo. Le unità eseguite non erano quelle richieste.
5. Il saldo è passato da 109 228 a 139 228 USD (+30 000) il 18/9: un accredito di fondi virtuali, non un'operazione (nessuna rotta di deposito o trasferimento nel client). In dashboard l'accredito ha alzato il picco di equity e falsato drawdown e P&L del giorno.
## Causa
Tre difetti concatenati, nessuno dei quali da solo avrebbe prodotto il danno:
1. **Esito cercato con la chiave sbagliata** (`EtoroBroker.OpenAsync`): l'`orderId` restituito dal `POST` veniva letto e ignorato; l'esito era chiesto solo per `referenceId`, che il server non registra per gli ordini v2. Dopo `fillTimeoutSeconds` il metodo restituiva un esito **costruito a mano** (`"Received"`, «esito non ancora noto») invece di dire «non so».
2. **Ordine abbandonato** (`BasketExecutor.SendAsync`, `BasketEngine.ExecuteEntryAsync`): allo scadere di `legTimeoutSec` la gamba era «non eseguita», il basket rifiutato e riportato a `Idle`, e nessuno seguiva più l'ordine. Alla barra dopo si ripartiva da zero.
3. **Posizione sconosciuta = posizione intoccabile** (`BasketEngine.ReconcileAsync`): la regola di sicurezza «non toccare ciò che non è tuo» era corretta per le posizioni esterne, ma il bot non aveva alcun modo per riconoscere le proprie. Il contatore contava gli slot, non le posizioni; il kill-switch chiudeva gli slot, non le posizioni.
Aggravante: il **sizing non guardava il margine**. Con `orderLeverage = maxEffectiveLeverage = 10` un solo basket al tetto impegna il 100 % dell'equity.
## Correzione (Fase 1-3 del piano 5.0)
| Difetto | Correzione | Dove |
|---|---|---|
| esito per `referenceId` | esito per **`orderId`** (`orders:lookup?orderId=`, ripiego `api/v1/trading/info/{demo/}orders/{id}`); il riferimento solo se la risposta al `POST` è andata persa; matching sulle posizioni (strumento + verso + orario ± 90 s) come ultima risorsa | `EtoroBroker.OpenAsync`, `LookupOrderByIdAsync` |
| esito sintetico | `OrderOutcome` con stato `Unknown` e `Pending = true`; mai un nome di stato che il server non ha detto | `EtoroBroker`, `OrderOutcome` |
| ordine abbandonato | **`OrderTracker`** con registro persistente `data/state/pending_orders.json` scritto **prima** dell'HTTP; stati `PendingA` / `PendingB`; risoluzione a ogni ciclo e all'avvio; `orders.jsonl` append-only | `Core/Baskets/OrderTracker.cs`, `BasketExecutor`, `BasketEngine` |
| posizioni proprie non riconosciute | **`PositionClassifier`**: `basket` / `orfana-bot` / `esterna`; le orfane-bot vengono adottate e chiuse; contatori separati in dashboard; banner se equity saldo non torna con le posizioni | `Core/Baskets/PositionClassifier.cs`, `BasketEngine.Reconcile.cs` |
| kill-switch che chiude gli slot | kill-switch che chiude **le posizioni** (basket + orfane), cancella i pendenti, verifica la piattezza, stato `Halted-Residuo` | `BasketEngine.Kill.cs` (Fase 3) |
| sizing senza margine | `risk.maxMarginUsePct`, `maxMarginPerBasketPct`, `marginBufferPct`, sizing = min(rischio, margine), ricontrollo di `available` prima di B, margin guard | `VolParitySizing`, `BasketDecider`, `BasketExecutor` (Fase 2) |
| accredito che gonfia il picco | `CashFlowDetector`: salto di saldo non spiegato dalle chiusure = movimento di cassa; picco e equity di inizio giornata al netto | `Core/Baskets/CashFlowDetector.cs`, `BasketEngine.State.cs` |
## Come si verifica che non si ripeta
- Test (m): lookup 404 persistente + posizione presente → `Filled` per matching (`EtoroBrokerTests`).
- Test (n): A eseguita, B rifiutata → A richiusa entro 5 s (`LegRiskTests`).
- Test (o): A pendente oltre il timeout poi eseguita → `PendingA → Open` con B, oppure unwind se il segnale è decaduto (`OrderTrackerTests`).
- Test (p): posizione con la firma del bot → `orfana-bot` e chiusa (`PositionClassifierTests`).
- Test (q): un deposito non muove il picco né il drawdown (`CashFlowTests`).
- In Demo: 24 ore con ordini eseguiti riconosciuti e **zero** orfane (contatore «Gambe orfane» a 0 per tutta la sessione; `orders.jsonl` senza stati `Unknown` non risolti).
- Skill `encelado-diagnose`: rapporto di riconciliazione fra `orders.jsonl`, `pending_orders.json` e le posizioni del conto.
## Che cosa resta aperto
- Il ledger delle sessioni 16-21/9 non è su questa macchina: le righe `rifiuto` con `unitsA` direbbero quante unità il bot aveva chiesto per gli ordini che il server ha ridotto a 2 000 USD (D-36).
- La regola con cui eToro riduce un ordine a margine esaurito non è documentata nell'OpenAPI; il bot non deve più trovarsi in quella condizione (Fase 2), ma la registra se accade (`units_eseguite ≠ units_richieste` in `orders.jsonl`).
+19
View File
@@ -41,3 +41,22 @@ Ogni domanda è numerata per fase. Quando l'utente non ha risposto, è stato app
| D-23 | Fuso orario della finestra: quello del computer o selezionabile? | computer, selezionabile | **Applicato**: `ui.timeZone` = `computer` di fabbrica; elenco dei fusi di Windows in Impostazioni; `ENCELADO_TIME_ZONE` da ambiente. Solo la finestra cambia: il log porta l'offset, il ledger è UTC. |
| D-24 | L'endpoint dei costi restituiva markup e overnight a zero: era davvero zero? | verificare | **Verificato via API il 2026-09-16 12:45 UTC**: il campo si chiama `value`, non `amount`. EURUSD 10 000 unità leva 10: markup 0,0, spread di mercato 0,1 USD (0,1 pip), overnight 0,91 USD/giorno (≈ 0,9 pip/gamba/giorno). Parser corretto; aggiunto lo scenario di costi `api` al backtest. |
| D-25 | Google News vieta `/rss/search` nel robots.txt: forzare, sostituire o omettere? | omettere | **Default applicato: omettere** (il bot rispetta il robots.txt). SNB e RBNZ restano senza fonte; documentato in `KNOWN_ISSUES.md`. |
## Fase 0 della 5.0 — 2026-09-23
Domande del prompt «Encelado 5.0» (§15) più quelle emerse dalla verifica. D-26 e D-27 sono state verificate via API (sola lettura, collegamento MCP dell'utente) e non aspettano risposta. Le Fasi 1-3 usano i default senza attendere; le Fasi 6-9 aspettano D-28 e D-29.
| # | Domanda | Default proposto | Stato / risposta |
|---|---|---|---|
| D-26 | Endpoint per leggere un ordine per `orderId` e mappa degli stati? | verifica sul portale | **Verificato via API il 2026-09-23**: `GET api/v2/trading/info/{demo/}orders:lookup?orderId=<id>` (stessa risposta del lookup per riferimento) e `GET api/v1/trading/info/{demo/}orders/{orderId}` (risposta v1: `statusID`, `positions[]`). Stati: 1 Received, 2 Placed, 3 Filled, 4 Rejected, 5 PartiallyFilled, 6 PendingCancel, 7 Canceled, 8 Expired, 9 CanceledPartiallyFilled, 10 RejectedPartiallyFilled, 11 WaitingForMarket, 12 PendingTriggeredRate. Eseguito = 3 o 5; rifiutato = 4, 7, 8, 9, 10; in corso = 1, 2, 6, 11, 12. **Il lookup per `referenceId` non funziona per gli ordini v2**: il server ha registrato `referenceID = 00000000-…` per gli ordini del bot (verificato su 381739181). Scritto in `docs/DATA_SOURCES.md`. |
| D-27 | Esiste un endpoint di cancellazione ordini? | no | **Sì, verificato**: `DELETE api/v2/trading/execution/{demo/}orders/{orderId}` (quota 20/min condivisa con gli ordini). Il 200 conferma solo la richiesta; l'esito si legge con il lookup: 7 o 9 = annullato, 6 = in corso. Il kill-switch lo usa e poi attende la risoluzione (≤ 30 s). |
| D-28 | Confermi il ritiro del progetto WPF a favore della web UI servita dal bot? | sì (ADR-0007) | **In attesa.** Le Fasi 6-9 non partono senza risposta. |
| D-29 | Registry per l'immagine e URL dell'icona del template Unraid? | Gitea (`build/gitea.example.json`), icona nel repo | **In attesa.** |
| D-30 | Confermi la disattivazione a runtime di MLP, bandit e ciclo settimanale? | sì (ADR-0006) | **Default applicato in Fase 1 per il solo bandit**: oggi il bandit cambia il preset da solo in Demo (`ObserveOutcome`), cioè un parametro cambiato dal bot; viene spento subito. MLP e ciclo settimanale seguono in Fase 7 con ADR-0006. |
| D-31 | Token e chat ID Telegram via variabili d'ambiente? Comandi in ingresso o solo notifiche? | env; comandi attivi | **Default applicato** (Fase 5). |
| D-32 | Le posizioni esterne vanno chiuse dal kill-switch di default? | no | **Default applicato**: `risk.closeForeignOnKill = false`; la conferma del kill-switch ha la spunta «chiudi anche le esterne», deselezionata. |
| D-33 | Valute del selettore oltre USD/EUR? | USD, EUR, GBP, CHF, JPY, AUD, CAD, NZD | **Default applicato** (Fase 7). Per GBP e JPY il polling aggiunge GBPUSD e USDJPY. |
| D-34 | Soglie di margine (40 % totale, 12 % per basket, buffer 25 %) e soglia di inattività (10 min)? | sì | **Default applicato** (Fasi 2 e 4), tutte in `strategy.json` e cambiabili dall'operatore. |
| D-35 | Le orfane sul demo: bonifica con conferma o chiusura manuale? | bonifica con conferma | **Superata dai fatti**: il 2026-09-23 il conto demo ha 0 posizioni e 0 ordini; le 21 gambe del bot sono state chiuse in blocco il 21/9 alle 13:26 UTC (chiusura manuale, non del bot). La bonifica resta come comando (`--bonifica`, e pulsante in Diagnostica) per il futuro e non ha niente da chiudere oggi. |
| D-36 | Il ledger delle sessioni 16-21/9 (65 `segnale_ingresso`/`rifiuto`) non è su questa macchina: `Documenti\Encelado\data\ledger\decisions.jsonl` ha solo il 16/9. Su quale macchina o profilo ha girato il bot? Puoi copiare qui `decisions.jsonl`, `baskets_state.json` e `learning_state.json` di quella sessione? | procedere con le evidenze dell'API | **In attesa.** Servono per leggere `unitsA` nelle righe `rifiuto` e chiudere il punto 4 del post-mortem (ordini ridotti dal server a 2 000 USD di margine). |
| D-37 | Confermi che la chiusura in blocco del 21/9 alle 13:26 UTC l'hai fatta tu dalla piattaforma? | sì | **In attesa.** Se non sei stato tu, va capito chi ha chiuso (la 4.0.0 non poteva: contava gli slot). |