5.0: apprendimento in ombra (ADR-0006), skill di progetto, documenti della sessione
Con il backtest negativo e zero basket chiusi nel forward test, un meta-modello attivo e un bandit che cambia il preset da solo sono rumore: learning.enabled = false di fabbrica lascia il ledger, la calibrazione, la previsione di volatilità e la logistica in ombra, e spegne ciclo settimanale e challenger finché non valgono 300 basket chiusi e un P&L forward non negativo. Le skill in .claude/skills dicono come si comincia, si verifica, si diagnostica, si chiude e si rilascia una sessione. CLAUDE.md, glossario e problemi noti aggiornati alla 5.0; catena Verifica verde con 219 test. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: encelado-diagnose
|
||||||
|
description: Riconcilia ledger, registro degli ordini, heartbeat e posizioni del conto eToro e produce un rapporto CSV con separatore ; e colonna motivazione.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Diagnosi di una sessione di Encelado
|
||||||
|
|
||||||
|
Cartella dati: `Documenti\Encelado\data` su Windows, `/data/data` nel container. Se l'utente indica un'altra cartella, usa quella.
|
||||||
|
|
||||||
|
1. **Ledger delle decisioni** `data/ledger/decisions.jsonl` (e i mensili `decisions_YYYYMM.jsonl`): conta gli `evento` (`segnale_ingresso`, `rifiuto`, `ingresso`, `pending`, `pending_risolto`, `leg_risk_unwind`, `uscita`, `orfana_adottata`, `orfana_chiusa`, `movimento_di_cassa`, `kill_switch_*`, `recupero_*`, `comando`, `correzione`). Per ogni `rifiuto` riporta `basket`, `ts`, `unitsA`, `unitsB`, `sizing_bound`, `motivazione`.
|
||||||
|
2. **Registro degli ordini** `data/ledger/orders.jsonl` e `data/state/pending_orders.json`: per ogni `client_ref` prendi l'ultima riga; elenca gli ordini con `esito = Pending` o `stato = Unknown` (sono le anomalie), quelli con `unita_eseguite ≠ unita_richieste` (il server ha ridotto l'ordine), gli slippage oltre 1 pip.
|
||||||
|
3. **Basket** `data/ledger/baskets.csv`: numero, win rate, P&L netto, `exit_reason` (attenzione a `orphan_closed`, `bonifica_orfana`, `leg_risk_unwind`, `margin_guard`, `kill_switch`).
|
||||||
|
4. **Heartbeat** `data/state/heartbeat.json` e **stato** `data/state/baskets_state.json`: ultimo battito, run, basket aperti e in attesa, `haltResidue`, `cumulativeCashFlow`, `peakNetEquity`.
|
||||||
|
5. **Conto eToro** (solo se le chiavi sono disponibili nell'ambiente `ETORO_API_KEY`/`ETORO_USER_KEY` o via collegamento MCP, e solo in lettura): posizioni aperte (`GET api/v1/trading/info/demo/pnl`), storico (`GET api/v1/trading/info/trade/demo/history?minDate=…`). Classifica ogni posizione come `basket` (id nello stato), `orfana-bot` (id nel registro, o strumento + verso + orario entro 90 s da una riga del ledger), `esterna`.
|
||||||
|
6. Scrivi `reports/diagnosi_<data>.csv` con separatore `;`, intestazione e colonna finale `motivazione`: una riga per anomalia (`tipo;riferimento;quando;dettaglio;motivazione`) e in fondo le righe di riepilogo. Nessuna riga del ledger va modificata: le correzioni sono righe nuove.
|
||||||
|
7. Riassumi all'utente: cosa non torna, cosa è già stato gestito dal bot, cosa richiede una mano (chiusure su eToro, reset).
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
name: encelado-docs
|
||||||
|
description: Chiude una sessione di lavoro su Encelado: aggiorna STATE, CHANGELOG, ADR e QUESTIONS, poi propone il messaggio di commit.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Chiusura della sessione: documenti e commit
|
||||||
|
|
||||||
|
1. `docs/STATE.md`: data, fase in corso, cosa è stato fatto in questa sessione (con i numeri: test, file, commit), prossimi passi numerati, problemi aperti, domande in attesa. È la memoria fra una sessione e l'altra: chi legge solo questo deve poter ripartire.
|
||||||
|
2. `CHANGELOG.md`: una voce in alto con la data e il titolo della sessione; punti brevi su cosa cambia e perché, non l'elenco dei file.
|
||||||
|
3. ADR: una scelta non ovvia = un file `docs/adr/ADR-NNNN-<slug>.md` con Contesto, Decisione, Alternative scartate, Conseguenze. Aggiorna la tabella in `CLAUDE.md` se compare un documento nuovo.
|
||||||
|
4. `docs/QUESTIONS.md`: ogni dubbio è una riga numerata (D-xx) con il default applicato; le risposte arrivate si segnano con la data.
|
||||||
|
5. Se sono cambiati schema del ledger, rotte dell'API, regole di rischio o file in `Documenti\Encelado`: `docs/LEDGER_SCHEMA.md`, `docs/DATA_SOURCES.md`, `docs/RISK_RULES.md`, `docs/RUNBOOK.md`, `docs/KNOWN_ISSUES.md` (togli ciò che si chiude, aggiungi ciò che resta).
|
||||||
|
6. Verifica (`/encelado-verify`), poi proponi il messaggio di commit: titolo di una riga che dice cosa cambia, corpo che dice perché; un commit per sessione, un commit per fase se la sessione ne copre più d'una. Il push lo decide l'utente.
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
name: encelado-release
|
||||||
|
description: Rilascio di Encelado: verifica, tag, installatore Windows, immagine Docker e template Unraid, in quest'ordine e solo dopo il commit e il push del ramo.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Rilascio di Encelado
|
||||||
|
|
||||||
|
Presupposti: albero pulito, ultima sessione committata e pushata, `/encelado-verify` verde, versione decisa dall'utente (semver; la 5.0.0 è la prima con web UI e Docker).
|
||||||
|
|
||||||
|
1. `dotnet msbuild build/Release.proj -t:Verifica`.
|
||||||
|
2. `dotnet msbuild build/Release.proj -t:Rilascia -p:Versione=<x.y.z>`: crea il tag git, l'installatore Inno Setup (`bin/installer/Encelado_<x.y.z>.exe`), lo zip e la release su Gitea (`build/gitea.json`, mai committato: vedi `build/gitea.example.json`). La versione viene dal tag, non da un file.
|
||||||
|
3. Docker (dalla Fase 8 della 5.0): `dotnet msbuild build/Release.proj -t:Docker -p:Versione=<x.y.z>` costruisce `encelado:<x.y.z>` e `encelado:latest`; poi `docker push <registry>/encelado:<x.y.z>` verso il registry deciso in D-29.
|
||||||
|
4. Template Unraid: aggiorna `deploy/unraid/encelado.xml` se sono cambiate variabili, porte o volumi; l'`Icon` punta al PNG raw del repository.
|
||||||
|
5. `CHANGELOG.md` e `docs/STATE.md` riportano la versione rilasciata; il tag e la release hanno lo stesso testo del CHANGELOG.
|
||||||
|
|
||||||
|
Non modificare `build/Release.proj` o `build/Encelado.iss` se non richiesto: la catena è condivisa con Mimante e AutoBidder.
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
name: encelado-start
|
||||||
|
description: Avvia una sessione di lavoro su Encelado: legge lo stato, compila, riassume fase e problemi aperti, chiede cosa fare.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Avvio di una sessione su Encelado
|
||||||
|
|
||||||
|
1. Leggi, in quest'ordine: `CLAUDE.md`, `docs/STATE.md`, `docs/PIANO_5.0.md` (se la 5.0 non è conclusa), `docs/QUESTIONS.md` (le domande in attesa), `docs/KNOWN_ISSUES.md`.
|
||||||
|
2. Controlla l'albero di lavoro: `git status --short` e `git log --oneline -5`. Un albero sporco va capito prima di toccare qualcosa.
|
||||||
|
3. Compila: `dotnet build Encelado.slnx`. Se non compila, la prima cosa da fare è farlo compilare, e va detto subito.
|
||||||
|
4. Riassumi in dieci righe: fase in corso, cosa è stato fatto nell'ultima sessione, prossimi passi, domande senza risposta, problemi aperti, esito della compilazione.
|
||||||
|
5. Chiedi all'utente cosa fare in questa sessione. Non iniziare una fase del piano senza che sia chiaro quale.
|
||||||
|
|
||||||
|
Regole che valgono sempre: niente pacchetti NuGet nell'applicazione, UTC ovunque, ledger append-only, mai un ordine reale senza flag e frase `CONFERMO LIVE`, un commit a fine sessione dopo la verifica.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: encelado-ui
|
||||||
|
description: Regole per toccare l'interfaccia di Encelado: i token Material 3 e le linee guida di docs/UI_GUIDELINES.md, nessuna libreria, stesse informazioni della dashboard.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Lavorare sull'interfaccia di Encelado
|
||||||
|
|
||||||
|
Finché esiste il progetto WPF (`src/Encelado.Bot`, fino alla Fase 7 della 5.0): tema in `Ui/Theme.xaml`, pagine in `Ui/Pages`, nessuna decisione nella UI (legge lo snapshot, manda comandi via `IUiActions`), orari via `UiClock`, test di binding (`UiBindingTests`) e di rendering (`ENCELADO_RENDER_DIR=<cartella> dotnet test tests/Encelado.Tests --filter UiRenderTests`).
|
||||||
|
|
||||||
|
Dalla web UI (Fase 7): leggi `docs/UI_GUIDELINES.md` prima di scrivere una riga. In sintesi:
|
||||||
|
|
||||||
|
- HTML + CSS + JavaScript vanilla incorporati come `EmbeddedResource` nel server; nessun framework, nessun font remoto, nessun `<script src=http…>`.
|
||||||
|
- Ruoli di colore Material 3 (`primary`, `on-primary`, `surface`, `surface-container-*`, `outline`, `error`), palette scura di default e chiara selezionabile, contrasto ≥ 4,5:1, una sola tinta d'accento, verde e rosso solo per P&L e stati.
|
||||||
|
- Tipografia M3 (`display`, `headline`, `title`, `body`, `label`), `Roboto, "Segoe UI", system-ui`, cifre tabulari per ogni numero.
|
||||||
|
- Angoli 12-16 px, elevazione tonale, state layer su hover/focus/pressed; componenti propri: navigation rail e drawer, top app bar, cards, data table compatta, chips, buttons, switch, select, dialog di conferma (kill-switch, chiusura, reset), snackbar, linear progress, tooltip con formula e fonte su ogni numero.
|
||||||
|
- Classi di finestra M3 (compact < 600, medium < 840, expanded ≥ 840); la tabella dei basket diventa cards sotto 600 px.
|
||||||
|
- Accessibilità: tastiera completa, `aria-label`, focus visibile, `prefers-reduced-motion`, `prefers-color-scheme`.
|
||||||
|
- Aggiornamenti via Server-Sent Events (`/api/stream`, uno snapshot al secondo), comandi via `POST /api/commands/...`, token locale `ENCELADO_WEB_TOKEN`.
|
||||||
|
- Ogni importo convertito nella valuta di visualizzazione porta nel tooltip il valore in USD e il tasso usato; il ledger resta in USD.
|
||||||
|
|
||||||
|
Prima di dichiarare finita una modifica: test dello snapshot JSON dell'API, test dell'HTML incorporato, revisione visiva con l'utente.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
---
|
||||||
|
name: encelado-verify
|
||||||
|
description: La verifica completa di Encelado prima di un commit: compilazione, test, controllo dei file di configurazione e catena di rilascio.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Verifica di Encelado
|
||||||
|
|
||||||
|
Esegui nell'ordine e riporta l'esito di ciascun passo con i numeri (errori, avvisi, test superati/non superati), senza abbellire:
|
||||||
|
|
||||||
|
1. `dotnet build Encelado.slnx` — deve chiudere con 0 errori e 0 avvisi (`TreatWarningsAsErrors` è attivo: un avviso è un errore).
|
||||||
|
2. `dotnet test tests/Encelado.Tests --no-build` — tutti verdi. Un test rosso si legge e si corregge; non si esclude.
|
||||||
|
3. Coerenza dei file di fabbrica: `config/strategy.json` deve essere identico a `BasketStrategyConfig.DefaultJson` e `config/encelado.json` a `ConfigDefaults.Json` (ci sono test che lo verificano: se falliscono, rigenera i file dalle costanti, non il contrario).
|
||||||
|
4. Interfaccia web (dalla Fase 7 della 5.0): il test che valida l'HTML incorporato (well-formed, nessun riferimento esterno, nessun `<script src=http…>`).
|
||||||
|
5. `dotnet msbuild build/Release.proj -t:Verifica` — la catena di rilascio compila e testa nella cartella di verifica.
|
||||||
|
6. Riepilogo: una tabella `passo;esito;dettaglio`. Se un passo fallisce, fermati lì e proponi la correzione.
|
||||||
|
|
||||||
|
Non toccare `build/` se non richiesto: è condiviso con Mimante e AutoBidder.
|
||||||
@@ -2,6 +2,13 @@
|
|||||||
|
|
||||||
Formato: una voce per sessione di lavoro, con data. Le voci più recenti in alto.
|
Formato: una voce per sessione di lavoro, con data. Le voci più recenti in alto.
|
||||||
|
|
||||||
|
## 2026-09-23 — 5.0, apprendimento in ombra (ADR-0006) e skill di progetto
|
||||||
|
|
||||||
|
- Sezione `learning` in `strategy.json` (`enabled`, `weeklyCycle`, `challenger`, tutti `false` di fabbrica): il ciclo settimanale e il challenger non girano nel bot, il cancello ML non può attivarsi, la logistica resta in ombra con `p_ML` nel ledger, il bandit propone e basta. Criterio di riattivazione in `docs/ML_AND_LEARNING.md`.
|
||||||
|
- Skill di progetto in `.claude/skills/`: `encelado-start`, `encelado-verify`, `encelado-diagnose`, `encelado-docs`, `encelado-release`, `encelado-ui`.
|
||||||
|
- `CLAUDE.md` aggiornato (esecuzione 5.0, `--bonifica`, variabili Telegram, skill); glossario e problemi noti aggiornati.
|
||||||
|
- Catena di verifica (`build/Release.proj -t:Verifica`) verde.
|
||||||
|
|
||||||
## 2026-09-23 — 5.0, Fase 5: notifiche e comandi Telegram
|
## 2026-09-23 — 5.0, Fase 5: notifiche e comandi Telegram
|
||||||
|
|
||||||
- `TelegramNotifier` nel Core (solo `HttpClient`): coda non bloccante, un messaggio al secondo, tre tentativi con backoff e `retry_after`, spezzatura a 4096 caratteri, long polling `getUpdates` da un solo task, comandi accettati solo dalla chat autorizzata.
|
- `TelegramNotifier` nel Core (solo `HttpClient`): coda non bloccante, un messaggio al secondo, tre tentativi con backoff e `retry_after`, spezzatura a 4096 caratteri, long polling `getUpdates` da un solo task, comandi accettati solo dalla chat autorizzata.
|
||||||
|
|||||||
+5
-2
@@ -19,6 +19,7 @@ Bot di trading in C# (.NET 10, WPF) su **eToro** con la strategia "Correlation B
|
|||||||
| `docs/RISK_RULES.md` | regole di sicurezza con i default e chi può cambiarle |
|
| `docs/RISK_RULES.md` | regole di sicurezza con i default e chi può cambiarle |
|
||||||
| `docs/RUNBOOK.md` | avvio, arresto, kill-switch, reset, riconciliazione, chiavi, errori API, checklist |
|
| `docs/RUNBOOK.md` | avvio, arresto, kill-switch, reset, riconciliazione, chiavi, errori API, checklist |
|
||||||
| `docs/QUESTIONS.md` | domande poste per fase, risposte o default applicati, con data |
|
| `docs/QUESTIONS.md` | domande poste per fase, risposte o default applicati, con data |
|
||||||
|
| `docs/PIANO_5.0.md`, `docs/POSTMORTEM_ordini_pendenti.md` | il piano della 5.0 (fasi, stime, decisioni vincolanti) e il post-mortem delle gambe orfane del 16-21/9/2026 |
|
||||||
| `docs/GLOSSARY.md`, `docs/KNOWN_ISSUES.md`, `CHANGELOG.md`, `docs/adr/` | glossario, problemi noti, cronologia, decisioni architetturali |
|
| `docs/GLOSSARY.md`, `docs/KNOWN_ISSUES.md`, `CHANGELOG.md`, `docs/adr/` | glossario, problemi noti, cronologia, decisioni architetturali |
|
||||||
| `build/README.md` | catena di verifica, pacchetto e rilascio |
|
| `build/README.md` | catena di verifica, pacchetto e rilascio |
|
||||||
|
|
||||||
@@ -30,7 +31,8 @@ Bot di trading in C# (.NET 10, WPF) su **eToro** con la strategia "Correlation B
|
|||||||
- **Tempo**: UTC ovunque; conversione solo in UI. `CultureInfo.InvariantCulture` per ogni parsing e formattazione su file.
|
- **Tempo**: UTC ovunque; conversione solo in UI. `CultureInfo.InvariantCulture` per ogni parsing e formattazione su file.
|
||||||
- **Concorrenza**: un solo thread di decisione; I/O asincrono; `Channel<T>` fra ingestion, strategia, esecuzione e UI.
|
- **Concorrenza**: un solo thread di decisione; I/O asincrono; `Channel<T>` fra ingestion, strategia, esecuzione e UI.
|
||||||
- **Riproducibilità**: seed fisso 42 per ogni componente stocastica; ogni run scrive `run_id`, hash della configurazione e versione del codice nel ledger.
|
- **Riproducibilità**: seed fisso 42 per ogni componente stocastica; ogni run scrive `run_id`, hash della configurazione e versione del codice nel ledger.
|
||||||
- Cartelle a runtime sotto `Documenti\Encelado\`: `data/`, `knowledge/`, `reports/`, `results/`, `logs/`. Credenziali solo in `%LOCALAPPDATA%\Encelado\etoro.dat` (DPAPI) o variabili d'ambiente `ETORO_API_KEY`, `ETORO_USER_KEY`.
|
- Cartelle a runtime sotto `Documenti\Encelado\`: `data/`, `knowledge/`, `reports/`, `results/`, `logs/`. Credenziali solo in `%LOCALAPPDATA%\Encelado\etoro.dat` (DPAPI) o variabili d'ambiente `ETORO_API_KEY`, `ETORO_USER_KEY`; token Telegram solo in `TELEGRAM_BOT_TOKEN` (chat in `TELEGRAM_CHAT_ID`).
|
||||||
|
- **Esecuzione (5.0)**: ogni ordine entra nel registro `data/state/pending_orders.json` prima dell'HTTP e l'esito si legge per `orderId` (il server non registra il `referenceId`); una gamba senza esito porta il basket in `PendingA`/`PendingB`, mai in un rifiuto; ogni posizione del conto è `basket`, `orfana-bot` (adottata e chiusa) o `esterna` (mai toccata); la size è il minimo fra rischio e margine (`strategy.json` → `risk`); il kill-switch verifica la piattezza sul conto e dichiara `Halted-Residuo` se resta qualcosa; heartbeat e recupero dopo inattività (`recovery`); un solo bot per cartella dati (`instance.lock`).
|
||||||
- **Interfaccia**: una barra in alto (schede Dashboard / Log / Impostazioni, stato, ambiente, ora nel fuso scelto, AVVIA), pagine sotto. Nella dashboard solo le informazioni principali; i dettagli nei tooltip e nel log. Gli orari a schermo passano da `UiClock` (`ui.timeZone`); il log porta l'offset, il ledger è UTC.
|
- **Interfaccia**: una barra in alto (schede Dashboard / Log / Impostazioni, stato, ambiente, ora nel fuso scelto, AVVIA), pagine sotto. Nella dashboard solo le informazioni principali; i dettagli nei tooltip e nel log. Gli orari a schermo passano da `UiClock` (`ui.timeZone`); il log porta l'offset, il ledger è UTC.
|
||||||
- **Verifica visiva**: `ENCELADO_RENDER_DIR=<cartella> dotnet test tests/Encelado.Tests --filter UiRenderTests` scrive `dashboard.png`, `log.png`, `settings.png`, `window.png`.
|
- **Verifica visiva**: `ENCELADO_RENDER_DIR=<cartella> dotnet test tests/Encelado.Tests --filter UiRenderTests` scrive `dashboard.png`, `log.png`, `settings.png`, `window.png`.
|
||||||
|
|
||||||
@@ -40,7 +42,7 @@ Bot di trading in C# (.NET 10, WPF) su **eToro** con la strategia "Correlation B
|
|||||||
dotnet build Encelado.slnx # compilazione
|
dotnet build Encelado.slnx # compilazione
|
||||||
dotnet test tests/Encelado.Tests --no-restore # test (xunit)
|
dotnet test tests/Encelado.Tests --no-restore # test (xunit)
|
||||||
dotnet msbuild build/Release.proj -t:Verifica # compilazione + test nella cartella di verifica
|
dotnet msbuild build/Release.proj -t:Verifica # compilazione + test nella cartella di verifica
|
||||||
dotnet run --project src/Encelado.Bot -- --headless [--minutes 240] # bot senza finestra (VPS, test lunghi)
|
dotnet run --project src/Encelado.Bot -- --headless [--minutes 240] [--bonifica] # bot senza finestra (VPS, test lunghi); --bonifica elenca e chiude le orfane con conferma
|
||||||
dotnet run --project tools/Encelado.Backtest -- ticks --data "A:\Download\Trading" --out "%USERPROFILE%\Documents\Encelado\data\market"
|
dotnet run --project tools/Encelado.Backtest -- ticks --data "A:\Download\Trading" --out "%USERPROFILE%\Documents\Encelado\data\market"
|
||||||
dotnet run --project tools/Encelado.Backtest -- baskets --data "%USERPROFILE%\Documents\Encelado\data\market" --out results
|
dotnet run --project tools/Encelado.Backtest -- baskets --data "%USERPROFILE%\Documents\Encelado\data\market" --out results
|
||||||
dotnet run --project tools/Encelado.Backtest -- falsify --data "%USERPROFILE%\Documents\Encelado\data\market" --out reports [--costs api]
|
dotnet run --project tools/Encelado.Backtest -- falsify --data "%USERPROFILE%\Documents\Encelado\data\market" --out reports [--costs api]
|
||||||
@@ -56,6 +58,7 @@ dotnet msbuild build/Release.proj -t:Rilascia -p:Versione=4.0.0 # installator
|
|||||||
5. **Non toccare la catena di rilascio** (`build/`) se non richiesto; è condivisa con Mimante/AutoBidder.
|
5. **Non toccare la catena di rilascio** (`build/`) se non richiesto; è condivisa con Mimante/AutoBidder.
|
||||||
6. **Un commit a fine sessione**, dopo che la verifica passa, con un messaggio che dice cosa cambia e perché. Il push lo decide l'utente.
|
6. **Un commit a fine sessione**, dopo che la verifica passa, con un messaggio che dice cosa cambia e perché. Il push lo decide l'utente.
|
||||||
7. Aggiorna `docs/STATE.md` e `CHANGELOG.md` a fine sessione; un ADR per ogni scelta non ovvia.
|
7. Aggiorna `docs/STATE.md` e `CHANGELOG.md` a fine sessione; un ADR per ogni scelta non ovvia.
|
||||||
|
8. Le skill di progetto in `.claude/skills/` (`encelado-start`, `encelado-verify`, `encelado-diagnose`, `encelado-docs`, `encelado-release`, `encelado-ui`) dicono come si comincia, si verifica, si diagnostica, si chiude e si rilascia: usale.
|
||||||
|
|
||||||
## Cose da non fare
|
## Cose da non fare
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,13 @@
|
|||||||
"heartbeatSeconds": 30
|
"heartbeatSeconds": 30
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"_learning": "Apprendimento (ADR-0006). Di fabbrica tutto spento: restano il ledger, le tabelle di calibrazione, la previsione di volatilità e la logistica in ombra (p_ML nel ledger, mai un cancello). enabled = true riaccende il ciclo settimanale (weeklyCycle) e il challenger MLP (challenger) solo dopo il criterio di riattivazione di docs/ML_AND_LEARNING.md: almeno 300 basket chiusi in Demo e P&L netto forward >= 0 sulla pre-registrazione. Il bandit propone soltanto, non applica mai il preset.",
|
||||||
|
"learning": {
|
||||||
|
"enabled": false,
|
||||||
|
"weeklyCycle": false,
|
||||||
|
"challenger": false
|
||||||
|
},
|
||||||
|
|
||||||
"_baskets": "I cinque basket della specifica. Il cross sintetico e il verso delle gambe sono derivati dai codici delle valute, non configurati.",
|
"_baskets": "I cinque basket della specifica. Il cross sintetico e il verso delle gambe sono derivati dai codici delle valute, non configurati.",
|
||||||
"baskets": [
|
"baskets": [
|
||||||
{ "a": "EURUSD", "b": "USDCHF", "enabled": true, "note": "cross sintetico EURCHF" },
|
{ "a": "EURUSD", "b": "USDCHF", "enabled": true, "note": "cross sintetico EURCHF" },
|
||||||
|
|||||||
@@ -20,6 +20,13 @@
|
|||||||
| **Esterna** | Una posizione sul conto senza la firma del bot: segnalata, contata, mai toccata. |
|
| **Esterna** | Una posizione sul conto senza la firma del bot: segnalata, contata, mai toccata. |
|
||||||
| **Movimento di cassa** | Deposito, prelievo o accredito virtuale: un salto del saldo che nessuna chiusura spiega. Escluso dal P&L, dal picco e dal drawdown. |
|
| **Movimento di cassa** | Deposito, prelievo o accredito virtuale: un salto del saldo che nessuna chiusura spiega. Escluso dal P&L, dal picco e dal drawdown. |
|
||||||
| **Bonifica** | La pulizia una tantum delle orfane con conferma per posizione (`--bonifica`). |
|
| **Bonifica** | La pulizia una tantum delle orfane con conferma per posizione (`--bonifica`). |
|
||||||
|
| **Halted-Residuo** | Lo stato del motore dopo un kill-switch che non è riuscito ad appiattire le posizioni del bot: banner con l'elenco, reset rifiutato finché restano. |
|
||||||
|
| **Verifica di piattezza** | La rilettura del conto dopo il kill-switch finché le posizioni del bot non sono sparite; niente è «chiuso» prima. |
|
||||||
|
| **Margin guard** | Sotto equity / margine usato = 1,5 niente entrate; sotto 1,2 si chiude il basket con il P&L peggiore. |
|
||||||
|
| **sizing_bound** | Quale vincolo ha deciso la size di un basket: `risk`, `margin`, `leverage`, `units`; `min_exposure` quando nessuna size è ammessa. |
|
||||||
|
| **Heartbeat** | `data/state/heartbeat.json`, scritto ogni 30 s: misura l'inattività che fa scattare il recupero. |
|
||||||
|
| **Recupero** | La procedura dopo un'inattività oltre la soglia: ordini pendenti, riconciliazione, barre perse, ogni basket aperto rivalutato (chiudi/tieni), rapporto, riscaldamento. |
|
||||||
|
| **Lock di istanza** | `data/state/instance.lock`, tenuto in esclusiva: un solo bot per cartella dati. |
|
||||||
| **Equity stop** | Chiusura di tutto e blocco a un drawdown del 9 % dal picco; riparte solo con un reset motivato. |
|
| **Equity stop** | Chiusura di tutto e blocco a un drawdown del 9 % dal picco; riparte solo con un reset motivato. |
|
||||||
| **Kill-switch** | Chiusura immediata di tutto e blocco delle nuove entrate: pulsante, comando o file `STOP`. |
|
| **Kill-switch** | Chiusura immediata di tutto e blocco delle nuove entrate: pulsante, comando o file `STOP`. |
|
||||||
| **Paper / Demo / Live** | Simulatore locale / conto demo eToro / conto reale. Il bot opera da solo in tutte e tre (D-20). |
|
| **Paper / Demo / Live** | Simulatore locale / conto demo eToro / conto reale. Il bot opera da solo in tutte e tre (D-20). |
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ Aggiornato: 2026-09-23. Una voce per limite, con lo stato. Quando un limite vien
|
|||||||
- 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).
|
- 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`).
|
- 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.
|
- 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.
|
||||||
|
- Telegram non avvisa ancora dei feed (calendario, RSS) in errore da più di un'ora: il provider dei feed non espone l'ora dell'ultimo successo; da fare con la Fase 6-7 insieme al riquadro «Telegram» della dashboard.
|
||||||
|
- Il comando `learn` dello strumento di ricerca (ciclo settimanale offline, ADR-0006) arriva con la Fase 6: `LearningState` dipende oggi dal log e dal ledger del Bot e non è ancora nel progetto portabile.
|
||||||
|
- Le sezioni `risk`, `recovery` e `learning` di `strategy.json` non compaiono nel form di Impostazioni della finestra WPF (si modificano nel file, come tutto il resto della strategia); la web UI le mostrerà in Impostazioni → Ricerca.
|
||||||
- Il ciclo settimanale gira solo mentre il bot è acceso la domenica dopo le 10 UTC (o al primo avvio dopo sette giorni).
|
- 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: il lock di istanza (5.0) impedisce che due motori girino sulla stessa cartella dati, ma la finestra aperta senza AVVIA scrive comunque nel log mentre gira l'headless.
|
- La finestra e l'headless usano lo stesso log: il lock di istanza (5.0) impedisce che due motori girino sulla stessa cartella dati, ma la finestra aperta senza AVVIA scrive comunque nel log mentre gira l'headless.
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,24 @@
|
|||||||
# Apprendimento: livelli 0-3
|
# Apprendimento: livelli 0-3
|
||||||
|
|
||||||
Aggiornato: 2026-09-16. Tutto è costruito da zero nel Core (`src/Encelado.Core/Baskets/Learning/`), senza pacchetti: regressione logistica online, un MLP a 16 unità ReLU con Adam, un bandit di Thompson, due previsori di volatilità e un indice di stabilità (PSI). Il codice del Bot che li usa a runtime è `src/Encelado.Bot/Baskets/LearningState.cs`.
|
Aggiornato: 2026-09-23 (ADR-0006). Tutto è costruito da zero nel Core (`src/Encelado.Core/Baskets/Learning/`), senza pacchetti: regressione logistica online, un MLP a 16 unità ReLU con Adam, un bandit di Thompson, due previsori di volatilità e un indice di stabilità (PSI). Il codice del Bot che li usa a runtime è `src/Encelado.Bot/Baskets/LearningState.cs`.
|
||||||
|
|
||||||
**Regola che governa tutto**: nessun livello cambia un parametro live da solo. Il meta-modello può soltanto *rifiutare* un ingresso quando è attivo; il bandit *propone* un preset e lo applica solo in Paper/Demo; tutto il resto finisce in `knowledge/proposals.csv` e passa dal forward test pre-registrato.
|
**Regola che governa tutto**: nessun livello cambia un parametro live da solo. Il meta-modello può soltanto *rifiutare* un ingresso quando è attivo; il bandit *propone* un preset e **non lo applica** (dalla 5.0; fino alla 4.0.0 lo applicava in Paper e Demo); tutto il resto finisce in `knowledge/proposals.csv` e passa dal forward test pre-registrato.
|
||||||
|
|
||||||
|
## Stato dalla 5.0 (ADR-0006): quasi tutto in ombra
|
||||||
|
|
||||||
|
`strategy.json` → `learning` è `{enabled: false, weeklyCycle: false, challenger: false}` di fabbrica. Con `enabled = false`:
|
||||||
|
|
||||||
|
| Componente | Runtime |
|
||||||
|
|---|---|
|
||||||
|
| Ledger, `orders.jsonl`, `baskets.csv` | scritti sempre: sono il dato |
|
||||||
|
| Livello 0 (calibrazione) | generato dal ciclo, quindi fermo finché il ciclo non gira (strumento `learn`, Fase 6) |
|
||||||
|
| Previsione di volatilità | attiva, deterministica, usata da `z_in` effettivo |
|
||||||
|
| Logistica (livello 1) | **in ombra**: `p_ML` a ogni chiusura di barra nel ledger, apprendimento a ogni chiusura di basket; mai un cancello (`mlActive = false`) |
|
||||||
|
| MLP challenger (livello 2) | non addestrato |
|
||||||
|
| Bandit (livello 3) | riceve i premi e **propone** nel log; non applica |
|
||||||
|
| Ciclo settimanale | non gira nel bot; passa a `tools/Encelado.Backtest learn` (Fase 6) |
|
||||||
|
|
||||||
|
**Criterio di riattivazione** (`learning.enabled = true`, scritto dall'operatore, mai dal bot): almeno **300 basket chiusi in Demo** *e* **P&L netto forward ≥ 0** sulla metrica pre-registrata in `knowledge/preregistrazione.csv`. Prima di allora qualunque «adattamento automatico» è rumore: il meta-modello può solo ridurre le perdite di una strategia che il backtest dice non reggere i costi, e a poche entrate al giorno i 300 basket sono mesi. Motivazione completa in `docs/adr/ADR-0006-apprendimento-in-ombra.md`.
|
||||||
|
|
||||||
## Il dataset
|
## Il dataset
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Domande del prompt «Encelado 5.0» (§15) più quelle emerse dalla verifica. D-
|
|||||||
| 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-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-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-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-30 | Confermi la disattivazione a runtime di MLP, bandit e ciclo settimanale? | sì (ADR-0006) | **Default applicato** (ADR-0006, 2026-09-23): il bandit propone e non applica (Fase 1); `learning.enabled = false` di fabbrica spegne ciclo settimanale e challenger e impedisce al cancello ML di attivarsi; la logistica resta in ombra. Il comando `learn` dello strumento arriva con la Fase 6. Resta aperta solo se vuoi il contrario. |
|
||||||
| 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-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-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-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. |
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Aggiornato: 2026-09-23 (sessione 5.0, Fasi 0-5 concluse).
|
|||||||
## Fatto nell'ultima sessione (2026-09-23)
|
## Fatto nell'ultima sessione (2026-09-23)
|
||||||
|
|
||||||
- **Fase 0**: diagnosi verificata sul codice e sul conto demo via API (sola lettura): `referenceID` nullo sugli ordini v2 (ecco il 404), 21 gambe orfane del 16-21/9 chiuse a mano il 21/9, primi due ordini a 100 % del margine, poi ordini ridotti dal server a 2 000 USD di margine. Endpoint per `orderId` e cancellazione verificati (D-26, D-27). `docs/PIANO_5.0.md`, `docs/POSTMORTEM_ordini_pendenti.md`, `docs/QUESTIONS.md` D-26…D-37.
|
- **Fase 0**: diagnosi verificata sul codice e sul conto demo via API (sola lettura): `referenceID` nullo sugli ordini v2 (ecco il 404), 21 gambe orfane del 16-21/9 chiuse a mano il 21/9, primi due ordini a 100 % del margine, poi ordini ridotti dal server a 2 000 USD di margine. Endpoint per `orderId` e cancellazione verificati (D-26, D-27). `docs/PIANO_5.0.md`, `docs/POSTMORTEM_ordini_pendenti.md`, `docs/QUESTIONS.md` D-26…D-37.
|
||||||
|
- **§8 e §14 (parte)**: ADR-0006 con `learning.enabled = false` di fabbrica; skill di progetto in `.claude/skills/`; `CLAUDE.md`, glossario, problemi noti; catena `Verifica` verde.
|
||||||
- **Fase 5**: `TelegramNotifier`, stato orario, eventi, riepilogo giornaliero, comandi dalla chat autorizzata, riga `comando` nel ledger; test (s)-(u); 219 verdi.
|
- **Fase 5**: `TelegramNotifier`, stato orario, eventi, riepilogo giornaliero, comandi dalla chat autorizzata, riga `comando` nel ledger; test (s)-(u); 219 verdi.
|
||||||
- **Fase 4**: heartbeat, lock di istanza, sezione `recovery`, procedura di recupero dopo inattività con rapporto `recupero_<run_id>.csv`; test (r); 211 verdi.
|
- **Fase 4**: heartbeat, lock di istanza, sezione `recovery`, procedura di recupero dopo inattività con rapporto `recupero_<run_id>.csv`; test (r); 211 verdi.
|
||||||
- **Fase 3**: `FlattenProcedure` (annulla, chiudi, verifica), kill-switch che chiude basket + gambe in attesa + orfane (esterne opzionali) e dichiara `Halted-Residuo` quando il conto non è piatto, comando `residuo`, reset in cinque passi rifiutato con residui, `INotifier`; test (v)-(x); 204 verdi.
|
- **Fase 3**: `FlattenProcedure` (annulla, chiudi, verifica), kill-switch che chiude basket + gambe in attesa + orfane (esterne opzionali) e dichiara `Halted-Residuo` quando il conto non è piatto, comando `residuo`, reset in cinque passi rifiutato con residui, `INotifier`; test (v)-(x); 204 verdi.
|
||||||
@@ -19,7 +20,7 @@ Aggiornato: 2026-09-23 (sessione 5.0, Fasi 0-5 concluse).
|
|||||||
|
|
||||||
1. **Rispondere a D-28 (ritiro WPF → web UI) e D-29 (registry, icona)**: senza risposta le Fasi 6-9 non partono (`docs/QUESTIONS.md`).
|
1. **Rispondere a D-28 (ritiro WPF → web UI) e D-29 (registry, icona)**: senza risposta le Fasi 6-9 non partono (`docs/QUESTIONS.md`).
|
||||||
2. Riaccendere il Demo per 24 ore di verifica: contatore «orfane» a 0, `orders.jsonl` senza `Unknown` irrisolti, `sizing_bound = margin` sui primi ingressi; provare il kill-switch a mano con la verifica di piattezza; fermare il bot un'ora con un basket aperto per vedere il recupero; ricevere i messaggi Telegram reali (serve `TELEGRAM_BOT_TOKEN`).
|
2. Riaccendere il Demo per 24 ore di verifica: contatore «orfane» a 0, `orders.jsonl` senza `Unknown` irrisolti, `sizing_bound = margin` sui primi ingressi; provare il kill-switch a mano con la verifica di piattezza; fermare il bot un'ora con un basket aperto per vedere il recupero; ricevere i messaggi Telegram reali (serve `TELEGRAM_BOT_TOKEN`).
|
||||||
3. Fasi 6-9 (Engine/Server, web UI M3 con Storico/valuta/navigazione/Ricerca, Docker, Unraid, skill, ADR-0006/0007/0008, 5.0.0).
|
3. Fasi 6-9 (Engine/Server, web UI M3 con Storico/valuta/navigazione/Ricerca, Docker, Unraid, ADR-0007/0008, comando `learn` dello strumento, avviso Telegram sui feed fermi, `docs/UI_GUIDELINES.md`, `docs/DOCKER.md`, 5.0.0).
|
||||||
4. Rifare il backtest (`backtest baskets`) con i limiti di margine e aggiornare §3 di `docs/STRATEGY.md`.
|
4. Rifare il backtest (`backtest baskets`) con i limiti di margine e aggiornare §3 di `docs/STRATEGY.md`.
|
||||||
|
|
||||||
## Problemi aperti
|
## Problemi aperti
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# ADR-0006 — Apprendimento: livello 0 e logistica in ombra restano, MLP, bandit e ciclo settimanale si spengono a runtime
|
||||||
|
|
||||||
|
Data: 2026-09-23. Stato: accettata con il default di D-30 (§8 del piano 5.0); revocabile con una risposta diversa.
|
||||||
|
|
||||||
|
## Contesto
|
||||||
|
|
||||||
|
`docs/STRATEGY.md` dà un verdetto negativo sul backtest (7,75 anni, nessuna configurazione con P&L netto positivo, segnale di circa 2 pip contro 3 pip di costo, walk-forward Sharpe −0,91). Il forward test in Demo ha prodotto **0 basket chiusi** in sei giorni per il difetto degli ordini pendenti (post-mortem), e il ciclo settimanale ha girato a vuoto («ledger senza basket chiusi: niente da addestrare»). Un meta-modello può soltanto filtrare i basket: con un segnale sotto i costi il massimo che può fare è ridurre le perdite, e per attivarsi gli servono almeno 300 basket chiusi, cioè mesi alla frequenza osservata. Nel frattempo il bandit **applicava** il preset da solo in Demo: un parametro cambiato dal bot, contro la regola di `RISK_RULES.md`.
|
||||||
|
|
||||||
|
## Decisione
|
||||||
|
|
||||||
|
- **Restano attivi**: il ledger (è il dato), il livello 0 (tabelle di calibrazione: quasi gratis, utili a leggere il forward test), la previsione di volatilità (`VolForecaster`, usata dal decisore in modo deterministico per `z_in` effettivo), la regressione logistica **in ombra** (predice `p_ML` nel ledger a ogni chiusura di barra e impara a ogni chiusura di basket; non fa mai da cancello).
|
||||||
|
- **Si spengono a runtime** (codice conservato nel Core): l'MLP challenger, il bandit di Thompson come attuatore (dalla Fase 1 propone soltanto), il ciclo settimanale. In `strategy.json` la sezione `learning` ha `enabled = false` di fabbrica: con `false` il motore non avvia il ciclo, non addestra il challenger e non può mai attivare il cancello `ml_gate`; il bandit registra i premi e propone nel log.
|
||||||
|
- **Il ciclo settimanale passa allo strumento** `tools/Encelado.Backtest` (comando `learn` su un ledger esportato), così i modelli si valutano quando ci saranno dati, fuori dal processo che opera. Il comando arriva con la Fase 6, quando `LearningState` si sposta nel progetto portabile `Encelado.Engine` (oggi dipende dal log e dal ledger del Bot).
|
||||||
|
- **Criterio di riattivazione** (`docs/ML_AND_LEARNING.md`): almeno **300 basket chiusi in Demo** *e* **P&L netto forward ≥ 0** sulla metrica pre-registrata (`knowledge/preregistrazione.csv`). Prima di allora ogni «adattamento automatico» è rumore, e `learning.enabled = true` è una decisione dell'operatore scritta nel file, non del bot.
|
||||||
|
- **Interfaccia**: il riquadro «Apprendimento» esce dalla dashboard; lo stato del modello in ombra (basket visti, AUC mobile, PSI) e il pulsante «esegui ciclo di apprendimento» vanno in Impostazioni → Ricerca (web UI, Fase 7). Nella finestra WPF resta la riga di stato del modello in ombra, che ora dice «disattivato (ADR-0006)».
|
||||||
|
|
||||||
|
## Conseguenze
|
||||||
|
|
||||||
|
- Nessun cambiamento nelle decisioni del bot: il cancello ML non era mai stato attivo (0 basket chiusi).
|
||||||
|
- `strategy.json` cresce della sezione `learning` (`enabled`, `weeklyCycle`, `challenger`); l'hash della configurazione la include.
|
||||||
|
- I file `data/models/*` e `knowledge/*` restano dove sono e vengono letti dallo strumento.
|
||||||
|
- Quando il criterio sarà soddisfatto, la riattivazione passa da una proposta in `knowledge/proposals.csv`, dalla pre-registrazione e da `learning.enabled = true`.
|
||||||
@@ -98,7 +98,8 @@ public sealed partial class BasketEngine
|
|||||||
ContextRow row = _context.Row(now);
|
ContextRow row = _context.Row(now);
|
||||||
BasketSlot? first = _slots.FirstOrDefault(static s => s.Enabled && s.Vol.Count > 0);
|
BasketSlot? first = _slots.FirstOrDefault(static s => s.Enabled && s.Vol.Count > 0);
|
||||||
string vol = first is null ? "in attesa di barre" : $"{first.Name}: {first.Vol.Describe()}";
|
string vol = first is null ? "in attesa di barre" : $"{first.Name}: {first.Vol.Describe()}";
|
||||||
return row with { VolForecast = vol, MlState = _learning.Describe(), BanditProposal = _learning.BanditText };
|
string ml = _strategy.Learning.Enabled ? _learning.Describe() : $"disattivato (ADR-0006): {_learning.Describe()}";
|
||||||
|
return row with { VolForecast = vol, MlState = ml, BanditProposal = _learning.BanditText + " (solo proposta)" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>The picture while the engine is not running.</summary>
|
/// <summary>The picture while the engine is not running.</summary>
|
||||||
|
|||||||
@@ -551,7 +551,8 @@ public sealed partial class BasketEngine : IEngine
|
|||||||
SaveState();
|
SaveState();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_learning.CycleDue(now) && now.DayOfWeek == DayOfWeek.Sunday && now.Hour >= 10)
|
// ADR-0006: the weekly cycle runs inside the bot only when the operator turned learning on.
|
||||||
|
if (_strategy.Learning.Enabled && _strategy.Learning.WeeklyCycle && _learning.CycleDue(now) && now.DayOfWeek == DayOfWeek.Sunday && now.Hour >= 10)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -870,7 +871,7 @@ public sealed partial class BasketEngine : IEngine
|
|||||||
double[] features = LearningFeatures.From(ctx, preview, preview.Z < 0);
|
double[] features = LearningFeatures.From(ctx, preview, preview.Z < 0);
|
||||||
slot.LastFeatures = features;
|
slot.LastFeatures = features;
|
||||||
slot.PMl = _learning.Predict(features);
|
slot.PMl = _learning.Predict(features);
|
||||||
ctx = ctx with { PMl = slot.PMl, MlActive = _learning.Active && double.IsFinite(slot.PMl) };
|
ctx = ctx with { PMl = slot.PMl, MlActive = _strategy.Learning.Enabled && _learning.Active && double.IsFinite(slot.PMl) };
|
||||||
}
|
}
|
||||||
|
|
||||||
d = _decider.Evaluate(ctx);
|
d = _decider.Evaluate(ctx);
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public static class BasketTrials
|
|||||||
MarginCallCloseRatio = c.Risk.MarginCallCloseRatio,
|
MarginCallCloseRatio = c.Risk.MarginCallCloseRatio,
|
||||||
};
|
};
|
||||||
copy.Recovery = new RecoveryOptions { ThresholdMinutes = c.Recovery.ThresholdMinutes, WarmupMinutes = c.Recovery.WarmupMinutes, HeartbeatSeconds = c.Recovery.HeartbeatSeconds };
|
copy.Recovery = new RecoveryOptions { ThresholdMinutes = c.Recovery.ThresholdMinutes, WarmupMinutes = c.Recovery.WarmupMinutes, HeartbeatSeconds = c.Recovery.HeartbeatSeconds };
|
||||||
|
copy.Learning = new LearningOptions { Enabled = c.Learning.Enabled, WeeklyCycle = c.Learning.WeeklyCycle, Challenger = c.Learning.Challenger };
|
||||||
copy.Preset = c.Preset;
|
copy.Preset = c.Preset;
|
||||||
copy.SignalMode = c.SignalMode;
|
copy.SignalMode = c.SignalMode;
|
||||||
copy.ExitMode = c.ExitMode;
|
copy.ExitMode = c.ExitMode;
|
||||||
|
|||||||
@@ -109,6 +109,24 @@ public sealed class RecoveryOptions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What of the learning stack runs at runtime (ADR-0006). Off by default: the shadow
|
||||||
|
/// logistic model keeps scoring and learning (its probability is a ledger column), the
|
||||||
|
/// weekly cycle, the MLP challenger and the bandit as an actuator do not run until the
|
||||||
|
/// operator turns them on after the reactivation criterion of <c>docs/ML_AND_LEARNING.md</c>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class LearningOptions
|
||||||
|
{
|
||||||
|
/// <summary>Master switch: false = shadow only, no weekly cycle, no challenger, the gate can never activate.</summary>
|
||||||
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Whether the weekly cycle runs inside the bot (Sunday after 10 UTC) when learning is enabled.</summary>
|
||||||
|
public bool WeeklyCycle { get; set; }
|
||||||
|
|
||||||
|
/// <summary>Whether the MLP challenger is trained by the cycle when learning is enabled.</summary>
|
||||||
|
public bool Challenger { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>One basket: two pairs. The synthetic cross and the leg signs are derived, never configured.</summary>
|
/// <summary>One basket: two pairs. The synthetic cross and the leg signs are derived, never configured.</summary>
|
||||||
public sealed class BasketDefinition
|
public sealed class BasketDefinition
|
||||||
{
|
{
|
||||||
@@ -244,6 +262,9 @@ public sealed class BasketStrategyConfig
|
|||||||
/// <summary>The recovery after an inactivity (§6 of the 5.0 plan).</summary>
|
/// <summary>The recovery after an inactivity (§6 of the 5.0 plan).</summary>
|
||||||
public RecoveryOptions Recovery { get; set; } = new();
|
public RecoveryOptions Recovery { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>What of the learning stack runs at runtime (ADR-0006).</summary>
|
||||||
|
public LearningOptions Learning { get; set; } = new();
|
||||||
|
|
||||||
// ---- overrides of the preset (NaN / 0 = take the preset's value) ----
|
// ---- overrides of the preset (NaN / 0 = take the preset's value) ----
|
||||||
public double ZInOverride { get; set; } = double.NaN;
|
public double ZInOverride { get; set; } = double.NaN;
|
||||||
|
|
||||||
@@ -370,7 +391,7 @@ public sealed class BasketStrategyConfig
|
|||||||
sb.Append(CultureInfo.InvariantCulture, $"cost={CostMultiple};spreadMed={SpreadMedianMultiple};slip={SlippagePipsPerLeg};on={OvernightPipsPerDay};blackout={BlackoutBeforeMin}/{BlackoutAfterMin};fri={FridayCutoffUtcHour};open={OpenDelayMinutes};");
|
sb.Append(CultureInfo.InvariantCulture, $"cost={CostMultiple};spreadMed={SpreadMedianMultiple};slip={SlippagePipsPerLeg};on={OvernightPipsPerDay};blackout={BlackoutBeforeMin}/{BlackoutAfterMin};fri={FridayCutoffUtcHour};open={OpenDelayMinutes};");
|
||||||
sb.Append(CultureInfo.InvariantCulture, $"lev={MaxEffectiveLeverage}/{OrderLeverage};vol={VolScaleMin}-{VolScaleMax}/{VolAverageDays};pMin={MlMinProbability};eqStop={EquityStopPct};daily={DailyLossPct};");
|
sb.Append(CultureInfo.InvariantCulture, $"lev={MaxEffectiveLeverage}/{OrderLeverage};vol={VolScaleMin}-{VolScaleMax}/{VolAverageDays};pMin={MlMinProbability};eqStop={EquityStopPct};daily={DailyLossPct};");
|
||||||
sb.Append(CultureInfo.InvariantCulture, $"margin={Risk.MaxMarginUsePct}/{Risk.MaxMarginPerBasketPct}/{Risk.MarginBufferPct};mcall={Risk.MarginCallBlockRatio}/{Risk.MarginCallCloseRatio};");
|
sb.Append(CultureInfo.InvariantCulture, $"margin={Risk.MaxMarginUsePct}/{Risk.MaxMarginPerBasketPct}/{Risk.MarginBufferPct};mcall={Risk.MarginCallBlockRatio}/{Risk.MarginCallCloseRatio};");
|
||||||
sb.Append(CultureInfo.InvariantCulture, $"recovery={Recovery.ThresholdMinutes}/{Recovery.WarmupMinutes};");
|
sb.Append(CultureInfo.InvariantCulture, $"recovery={Recovery.ThresholdMinutes}/{Recovery.WarmupMinutes};learning={(Learning.Enabled ? 1 : 0)}/{(Learning.WeeklyCycle ? 1 : 0)}/{(Learning.Challenger ? 1 : 0)};");
|
||||||
foreach (BasketDefinition b in Baskets)
|
foreach (BasketDefinition b in Baskets)
|
||||||
{
|
{
|
||||||
sb.Append(CultureInfo.InvariantCulture, $"{b.A}/{b.B}={(b.Enabled ? 1 : 0)};");
|
sb.Append(CultureInfo.InvariantCulture, $"{b.A}/{b.B}={(b.Enabled ? 1 : 0)};");
|
||||||
@@ -474,6 +495,7 @@ public sealed class BasketStrategyConfig
|
|||||||
case "baskets": c.Baskets = ReadBaskets(p.Value, warnings); break;
|
case "baskets": c.Baskets = ReadBaskets(p.Value, warnings); break;
|
||||||
case "risk": c.Risk = ReadRisk(p.Value, warnings); break;
|
case "risk": c.Risk = ReadRisk(p.Value, warnings); break;
|
||||||
case "recovery": c.Recovery = ReadRecovery(p.Value, warnings); break;
|
case "recovery": c.Recovery = ReadRecovery(p.Value, warnings); break;
|
||||||
|
case "learning": c.Learning = ReadLearning(p.Value, warnings); break;
|
||||||
default: warnings.Add($"chiave sconosciuta '{p.Name}' in strategy.json"); break;
|
default: warnings.Add($"chiave sconosciuta '{p.Name}' in strategy.json"); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,6 +579,34 @@ public sealed class BasketStrategyConfig
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static LearningOptions ReadLearning(JsonElement e, List<string> warnings)
|
||||||
|
{
|
||||||
|
LearningOptions l = new();
|
||||||
|
if (e.ValueKind != JsonValueKind.Object)
|
||||||
|
{
|
||||||
|
warnings.Add("'learning' deve essere un oggetto: uso i valori di fabbrica (tutto spento)");
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (JsonProperty p in e.EnumerateObject())
|
||||||
|
{
|
||||||
|
if (p.Name.StartsWith('_'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (p.Name.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "enabled": l.Enabled = Bool(p); break;
|
||||||
|
case "weeklycycle": l.WeeklyCycle = Bool(p); break;
|
||||||
|
case "challenger": l.Challenger = Bool(p); break;
|
||||||
|
default: warnings.Add($"chiave sconosciuta 'learning.{p.Name}' in strategy.json"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
private static RecoveryOptions ReadRecovery(JsonElement e, List<string> warnings)
|
private static RecoveryOptions ReadRecovery(JsonElement e, List<string> warnings)
|
||||||
{
|
{
|
||||||
RecoveryOptions r = new();
|
RecoveryOptions r = new();
|
||||||
@@ -721,6 +771,13 @@ public sealed class BasketStrategyConfig
|
|||||||
"heartbeatSeconds": 30
|
"heartbeatSeconds": 30
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"_learning": "Apprendimento (ADR-0006). Di fabbrica tutto spento: restano il ledger, le tabelle di calibrazione, la previsione di volatilità e la logistica in ombra (p_ML nel ledger, mai un cancello). enabled = true riaccende il ciclo settimanale (weeklyCycle) e il challenger MLP (challenger) solo dopo il criterio di riattivazione di docs/ML_AND_LEARNING.md: almeno 300 basket chiusi in Demo e P&L netto forward >= 0 sulla pre-registrazione. Il bandit propone soltanto, non applica mai il preset.",
|
||||||
|
"learning": {
|
||||||
|
"enabled": false,
|
||||||
|
"weeklyCycle": false,
|
||||||
|
"challenger": false
|
||||||
|
},
|
||||||
|
|
||||||
"_baskets": "I cinque basket della specifica. Il cross sintetico e il verso delle gambe sono derivati dai codici delle valute, non configurati.",
|
"_baskets": "I cinque basket della specifica. Il cross sintetico e il verso delle gambe sono derivati dai codici delle valute, non configurati.",
|
||||||
"baskets": [
|
"baskets": [
|
||||||
{ "a": "EURUSD", "b": "USDCHF", "enabled": true, "note": "cross sintetico EURCHF" },
|
{ "a": "EURUSD", "b": "USDCHF", "enabled": true, "note": "cross sintetico EURCHF" },
|
||||||
|
|||||||
Reference in New Issue
Block a user