From 4c26fd3209518689ecee6f70ba2a2860d459b867 Mon Sep 17 00:00:00 2001 From: Alberto Balbo Date: Wed, 16 Sep 2026 15:45:55 +0200 Subject: [PATCH] Passa ai Correlation Baskets su eToro e rimuove i motori precedenti (4.0.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Perché: l'utente ha chiesto un bot che operi cinque basket di coppie forex correlate su eToro, autonomo, con ledger, feed gratuiti e apprendimento costruito da zero, e ha deciso di eliminare tutto ciò che restava delle gestioni precedenti (Binance, cTrader/proba, ricerca con SQLite, GBDT, RL, TA-Lib) e di non avere approvazioni manuali sui singoli ordini. Cosa cambia: - nuovo Core dei basket (cross sintetici, decisore, cost gate, sizing, esecutore leg-risk, backtest con PSR/DSR/PBO, livelli 0-3 di apprendimento), adattatore eToro Public API, motore autonomo con equity stop, kill-switch, riconciliazione, ledger append-only, calendario e notizie con sentiment; - modalità Paper / Demo / Live (Live con flag e frase CONFERMO LIVE); - interfaccia rifatta: barra in alto con tre schede, dashboard con i soli numeri principali, fuso orario selezionabile, test di rendering in PNG; - corretto il parser dei costi eToro (campo "value"): markup e overnight non venivano letti; - strumento di ricerca ridotto a ticks / baskets / falsify con due scenari di costo; risultati in results/ e reports/: nessuna configurazione è profittevole al netto dei costi (docs/STRATEGY.md lo dice con i numeri); - documentazione completa (STRATEGY, ML_AND_LEARNING, RUNBOOK, GLOSSARY, KNOWN_ISSUES, ADR-0004, ADR-0005) e catena di rilascio aggiornata. Co-Authored-By: Claude Fable 5.1 --- Encelado/.gitignore | 3 + Encelado/.vscode/tasks.json | 10 +- Encelado/CHANGELOG.md | 22 + Encelado/CLAUDE.md | 67 + Encelado/Directory.Build.props | 2 +- Encelado/Encelado.slnx | 3 +- Encelado/Modifiche.txt | 315 +-- Encelado/build/Encelado.iss | 6 +- Encelado/build/README.md | 32 +- Encelado/build/Release.proj | 36 +- Encelado/config/encelado.json | 239 +- Encelado/config/strategy.json | 80 + Encelado/docs/ARCHITECTURE.md | 148 ++ Encelado/docs/DATA_SOURCES.md | 69 + Encelado/docs/GLOSSARY.md | 32 + Encelado/docs/KNOWN_ISSUES.md | 36 + Encelado/docs/LEDGER_SCHEMA.md | 83 + Encelado/docs/ML_AND_LEARNING.md | 94 + Encelado/docs/QUESTIONS.md | 43 + Encelado/docs/RISK_RULES.md | 36 + Encelado/docs/RUNBOOK.md | 92 + Encelado/docs/STATE.md | 34 + Encelado/docs/STRATEGY.md | 130 + Encelado/docs/adr/ADR-0001-broker-etoro.md | 31 + Encelado/docs/adr/ADR-0002-storage-su-file.md | 31 + .../ADR-0003-motore-ctrader-selezionabile.md | 15 + .../ADR-0004-rimozione-motori-precedenti.md | 19 + .../ADR-0005-nessuna-approvazione-manuale.md | 19 + Encelado/reports/falsificazione.csv | 14 + Encelado/reports/falsificazione_costi_api.csv | 14 + .../results/baskets_baseline_moderate.csv | 2225 +++++++++++++++++ .../baskets_baseline_moderate_costi_api.csv | 2077 +++++++++++++++ Encelado/results/riepilogo_baskets.csv | 8 + .../results/riepilogo_baskets_costi_api.csv | 8 + Encelado/results/trials.csv | 58 + Encelado/results/trials_costi_api.csv | 58 + .../src/Encelado.Binance/BinanceOptions.cs | 158 -- .../Encelado.Binance/Encelado.Binance.csproj | 16 - .../src/Encelado.Binance/Internal/JsonRead.cs | 155 -- .../Rest/BinanceFuturesClient.cs | 489 ---- .../src/Encelado.Binance/Rest/BinanceHttp.cs | 346 --- .../Encelado.Binance/Rest/BinanceModels.cs | 396 --- .../Streaming/MarketDataStream.cs | 495 ---- .../Encelado.Binance/Streaming/SymbolTable.cs | 62 - .../Streaming/UserDataStream.cs | 232 -- .../Streaming/WebSocketChannel.cs | 402 --- Encelado/src/Encelado.Bot/App.xaml.cs | 59 +- .../src/Encelado.Bot/Baskets/BasketEngine.cs | 1747 +++++++++++++ .../Encelado.Bot/Baskets/ContextProvider.cs | 52 + Encelado/src/Encelado.Bot/Baskets/Feeds.cs | 539 ++++ .../Encelado.Bot/Baskets/HeadlessRunner.cs | 242 ++ .../src/Encelado.Bot/Baskets/LearningState.cs | 537 ++++ Encelado/src/Encelado.Bot/Baskets/Ledger.cs | 388 +++ .../Encelado.Bot/Configuration/BotConfig.cs | 518 +--- .../Configuration/ConfigDefaults.cs | 287 +-- .../Configuration/ConfigLoader.cs | 323 +-- .../Configuration/CredentialResolver.cs | 174 -- .../Configuration/CredentialStore.cs | 283 --- .../Configuration/EtoroKeyStore.cs | 253 ++ .../Encelado.Bot/Diagnostics/AnalyticsLog.cs | 487 ---- .../src/Encelado.Bot/Diagnostics/Metrics.cs | 158 -- Encelado/src/Encelado.Bot/Encelado.Bot.csproj | 3 +- .../src/Encelado.Bot/Engine/AccountState.cs | 84 - .../src/Encelado.Bot/Engine/BotSnapshot.cs | 353 +-- .../src/Encelado.Bot/Engine/BotSupervisor.cs | 451 +--- Encelado/src/Encelado.Bot/Engine/IEngine.cs | 38 + Encelado/src/Encelado.Bot/Engine/LegState.cs | 120 - Encelado/src/Encelado.Bot/Engine/MetaGate.cs | 305 --- .../src/Encelado.Bot/Engine/PairPipeline.cs | 216 -- .../src/Encelado.Bot/Engine/PairRouter.cs | 666 ----- .../src/Encelado.Bot/Engine/TradeJournal.cs | 120 - .../src/Encelado.Bot/Engine/TradingEngine.cs | 1118 --------- Encelado/src/Encelado.Bot/MainWindow.xaml | 117 +- Encelado/src/Encelado.Bot/MainWindow.xaml.cs | 356 ++- Encelado/src/Encelado.Bot/Ui/Converters.cs | 157 +- ...LoginWindow.xaml => EtoroLoginWindow.xaml} | 31 +- .../Encelado.Bot/Ui/EtoroLoginWindow.xaml.cs | 143 ++ Encelado/src/Encelado.Bot/Ui/IUiActions.cs | 44 + .../src/Encelado.Bot/Ui/LoginWindow.xaml.cs | 110 - Encelado/src/Encelado.Bot/Ui/MainViewModel.cs | 275 +- Encelado/src/Encelado.Bot/Ui/Navigation.cs | 77 - .../Encelado.Bot/Ui/Pages/DashboardPage.xaml | 298 +++ .../Ui/Pages/DashboardPage.xaml.cs | 84 + .../src/Encelado.Bot/Ui/Pages/LogPage.xaml | 4 +- .../Encelado.Bot/Ui/Pages/PositionsPage.xaml | 144 -- .../Ui/Pages/PositionsPage.xaml.cs | 18 - .../Encelado.Bot/Ui/Pages/SettingsPage.xaml | 22 +- .../Ui/Pages/SettingsPage.xaml.cs | 39 +- .../src/Encelado.Bot/Ui/Pages/StatusPage.xaml | 301 --- .../Encelado.Bot/Ui/Pages/StatusPage.xaml.cs | 38 - .../src/Encelado.Bot/Ui/PromptWindow.xaml | 24 + .../src/Encelado.Bot/Ui/PromptWindow.xaml.cs | 44 + .../src/Encelado.Bot/Ui/SettingsCatalogue.cs | 846 +------ Encelado/src/Encelado.Bot/Ui/SparkChart.cs | 168 -- Encelado/src/Encelado.Bot/Ui/Theme.xaml | 1074 ++++---- Encelado/src/Encelado.Bot/Ui/UiClock.cs | 72 + .../src/Encelado.Core/Backtest/Baseline.cs | 188 -- .../Encelado.Core/Backtest/CsvBarSource.cs | 386 --- .../Encelado.Core/Backtest/PairBacktest.cs | 667 ----- .../Encelado.Core/Backtest/PrimaryReplay.cs | 143 -- .../Baskets/Backtest/BacktestBroker.cs | 247 ++ .../Baskets/Backtest/BasketBacktest.cs | 508 ++++ .../Baskets/Backtest/BasketTrials.cs | 301 +++ .../Encelado.Core/Baskets/BasketDecider.cs | 743 ++++++ .../Encelado.Core/Baskets/BasketExecutor.cs | 359 +++ .../src/Encelado.Core/Baskets/BasketMath.cs | 297 +++ .../Encelado.Core/Baskets/BasketPosition.cs | 150 ++ .../Baskets/BasketStrategyConfig.cs | 593 +++++ .../src/Encelado.Core/Baskets/CostGate.cs | 91 + .../Encelado.Core/Baskets/Data/BidAskBar.cs | 234 ++ .../Encelado.Core/Baskets/Data/TickToBars.cs | 264 ++ .../Encelado.Core/Baskets/ExecutionMode.cs | 54 + .../Baskets/Learning/LearningFeatures.cs | 85 + .../Baskets/Learning/ModelEvaluator.cs | 429 ++++ .../Baskets/Learning/OnlineLogistic.cs | 263 ++ .../Baskets/Learning/SmallMlp.cs | 263 ++ .../Baskets/Learning/ThompsonBandit.cs | 232 ++ .../Baskets/Learning/VolForecast.cs | 246 ++ Encelado/src/Encelado.Core/Baskets/PipMath.cs | 89 + .../src/Encelado.Core/Baskets/SymbolSeries.cs | 250 ++ .../Encelado.Core/Baskets/SyntheticCross.cs | 82 + .../Encelado.Core/Baskets/VolParitySizing.cs | 150 ++ Encelado/src/Encelado.Core/Broker/Broker.cs | 198 ++ .../src/Encelado.Core/Broker/PaperBroker.cs | 549 ++++ .../src/Encelado.Core/Broker/RateLimiter.cs | 95 + .../Indicators/AdvancedIndicators.cs | 225 -- .../Encelado.Core/Indicators/Indicators.cs | 561 ----- .../Indicators/OrderFlowIndicators.cs | 145 -- .../Encelado.Core/Indicators/RollingWindow.cs | 84 - .../src/Encelado.Core/Journal/IJournalSink.cs | 95 - .../src/Encelado.Core/Market/MarketTypes.cs | 191 -- .../src/Encelado.Core/Ml/AlternativeBars.cs | 138 - .../src/Encelado.Core/Ml/Classification.cs | 156 ++ Encelado/src/Encelado.Core/Ml/DriftMonitor.cs | 146 -- .../src/Encelado.Core/Ml/FeatureRegistry.cs | 372 --- .../Ml/FractionalDifferentiation.cs | 123 - Encelado/src/Encelado.Core/Ml/Gbdt.cs | 492 ---- Encelado/src/Encelado.Core/Ml/MetaLabeling.cs | 280 --- .../src/Encelado.Core/Ml/PairFeatureBuffer.cs | 147 -- Encelado/src/Encelado.Core/Ml/PairSeries.cs | 153 -- Encelado/src/Encelado.Core/Ml/Pbo.cs | 180 ++ .../src/Encelado.Core/Ml/SampleWeights.cs | 113 - .../src/Encelado.Core/Ml/TripleBarrier.cs | 164 -- Encelado/src/Encelado.Core/Ml/Validation.cs | 403 --- .../src/Encelado.Core/News/CalendarParser.cs | 207 ++ Encelado/src/Encelado.Core/News/RssParser.cs | 156 ++ .../src/Encelado.Core/News/SentimentEngine.cs | 309 +++ .../Encelado.Core/News/SentimentLexicon.cs | 107 + .../Encelado.Core/Portfolio/PortfolioBook.cs | 257 -- .../src/Encelado.Core/Portfolio/Position.cs | 98 - Encelado/src/Encelado.Core/Risk/RiskEngine.cs | 473 ---- Encelado/src/Encelado.Core/Risk/RiskLimits.cs | 170 -- Encelado/src/Encelado.Core/Rl/DqnAgent.cs | 255 -- Encelado/src/Encelado.Core/Rl/Mlp.cs | 261 -- .../src/Encelado.Core/Rl/PairEnvironment.cs | 226 -- .../Encelado.Core/Statistics/Cointegration.cs | 215 -- .../Encelado.Core/Statistics/DickeyFuller.cs | 304 --- .../src/Encelado.Core/Statistics/Johansen.cs | 276 -- .../Encelado.Core/Statistics/KalmanHedge.cs | 120 - .../src/Encelado.Core/Statistics/Linear.cs | 238 -- .../Strategies/Pairs/PairTypes.cs | 105 - .../Strategies/Pairs/StatArbStrategy.cs | 383 --- .../Strategies/StrategyParameters.cs | 38 - .../src/Encelado.Etoro/Encelado.Etoro.csproj | 21 + Encelado/src/Encelado.Etoro/EtoroBroker.cs | 740 ++++++ Encelado/src/Encelado.Etoro/EtoroHttp.cs | 169 ++ Encelado/src/Encelado.Etoro/EtoroOptions.cs | 59 + Encelado/src/Encelado.Etoro/Json.cs | 113 + .../Encelado.Storage/Encelado.Storage.csproj | 29 - Encelado/src/Encelado.Storage/StorageDb.cs | 760 ------ .../Encelado.Tests/AdvancedIndicatorTests.cs | 182 -- Encelado/tests/Encelado.Tests/BasketTests.cs | 567 +++++ Encelado/tests/Encelado.Tests/BinanceTests.cs | 285 --- .../Encelado.Tests/ConfigurationTests.cs | 346 ++- .../Encelado.Tests/CredentialStoreTests.cs | 143 +- .../Encelado.Tests/Encelado.Tests.csproj | 18 +- .../tests/Encelado.Tests/IndicatorTests.cs | 277 -- .../tests/Encelado.Tests/LearningTests.cs | 241 ++ Encelado/tests/Encelado.Tests/MlTests.cs | 490 ---- Encelado/tests/Encelado.Tests/NewsTests.cs | 143 ++ .../tests/Encelado.Tests/OrderFlowTests.cs | 246 -- .../tests/Encelado.Tests/PairEngineTests.cs | 175 -- .../tests/Encelado.Tests/PortfolioTests.cs | 165 -- Encelado/tests/Encelado.Tests/QuantTests.cs | 250 -- .../tests/Encelado.Tests/ReconnectTests.cs | 154 -- .../tests/Encelado.Tests/ResearchTests.cs | 382 --- Encelado/tests/Encelado.Tests/RiskTests.cs | 288 --- Encelado/tests/Encelado.Tests/StatArbTests.cs | 275 -- .../tests/Encelado.Tests/StatisticsTests.cs | 386 +-- Encelado/tests/Encelado.Tests/StorageTests.cs | 168 -- .../Encelado.Tests/StreamDecodingTests.cs | 148 -- .../tests/Encelado.Tests/TestSnapshots.cs | 178 +- .../tests/Encelado.Tests/UiBindingTests.cs | 40 +- .../tests/Encelado.Tests/UiRenderTests.cs | 122 + Encelado/tests/fixtures/boc_press.xml | 153 ++ Encelado/tests/fixtures/ecb_press.xml | 7 + Encelado/tests/fixtures/fed_press_all.xml | 169 ++ .../tests/fixtures/ff_calendar_thisweek.json | 1 + .../tests/fixtures/ff_calendar_thisweek.xml | 1053 ++++++++ Encelado/tests/fixtures/fxstreet_news.xml | 251 ++ Encelado/tools/Encelado.Backtest/Baskets.cs | 260 ++ .../Encelado.Backtest.csproj | 13 +- Encelado/tools/Encelado.Backtest/Options.cs | 307 +-- Encelado/tools/Encelado.Backtest/Program.cs | 680 +---- Encelado/tools/Encelado.Backtest/Research.cs | 907 ------- Encelado/tools/Encelado.Backtest/Ticks.cs | 88 + 206 files changed, 24320 insertions(+), 26598 deletions(-) create mode 100644 Encelado/CHANGELOG.md create mode 100644 Encelado/CLAUDE.md create mode 100644 Encelado/config/strategy.json create mode 100644 Encelado/docs/ARCHITECTURE.md create mode 100644 Encelado/docs/DATA_SOURCES.md create mode 100644 Encelado/docs/GLOSSARY.md create mode 100644 Encelado/docs/KNOWN_ISSUES.md create mode 100644 Encelado/docs/LEDGER_SCHEMA.md create mode 100644 Encelado/docs/ML_AND_LEARNING.md create mode 100644 Encelado/docs/QUESTIONS.md create mode 100644 Encelado/docs/RISK_RULES.md create mode 100644 Encelado/docs/RUNBOOK.md create mode 100644 Encelado/docs/STATE.md create mode 100644 Encelado/docs/STRATEGY.md create mode 100644 Encelado/docs/adr/ADR-0001-broker-etoro.md create mode 100644 Encelado/docs/adr/ADR-0002-storage-su-file.md create mode 100644 Encelado/docs/adr/ADR-0003-motore-ctrader-selezionabile.md create mode 100644 Encelado/docs/adr/ADR-0004-rimozione-motori-precedenti.md create mode 100644 Encelado/docs/adr/ADR-0005-nessuna-approvazione-manuale.md create mode 100644 Encelado/reports/falsificazione.csv create mode 100644 Encelado/reports/falsificazione_costi_api.csv create mode 100644 Encelado/results/baskets_baseline_moderate.csv create mode 100644 Encelado/results/baskets_baseline_moderate_costi_api.csv create mode 100644 Encelado/results/riepilogo_baskets.csv create mode 100644 Encelado/results/riepilogo_baskets_costi_api.csv create mode 100644 Encelado/results/trials.csv create mode 100644 Encelado/results/trials_costi_api.csv delete mode 100644 Encelado/src/Encelado.Binance/BinanceOptions.cs delete mode 100644 Encelado/src/Encelado.Binance/Encelado.Binance.csproj delete mode 100644 Encelado/src/Encelado.Binance/Internal/JsonRead.cs delete mode 100644 Encelado/src/Encelado.Binance/Rest/BinanceFuturesClient.cs delete mode 100644 Encelado/src/Encelado.Binance/Rest/BinanceHttp.cs delete mode 100644 Encelado/src/Encelado.Binance/Rest/BinanceModels.cs delete mode 100644 Encelado/src/Encelado.Binance/Streaming/MarketDataStream.cs delete mode 100644 Encelado/src/Encelado.Binance/Streaming/SymbolTable.cs delete mode 100644 Encelado/src/Encelado.Binance/Streaming/UserDataStream.cs delete mode 100644 Encelado/src/Encelado.Binance/Streaming/WebSocketChannel.cs create mode 100644 Encelado/src/Encelado.Bot/Baskets/BasketEngine.cs create mode 100644 Encelado/src/Encelado.Bot/Baskets/ContextProvider.cs create mode 100644 Encelado/src/Encelado.Bot/Baskets/Feeds.cs create mode 100644 Encelado/src/Encelado.Bot/Baskets/HeadlessRunner.cs create mode 100644 Encelado/src/Encelado.Bot/Baskets/LearningState.cs create mode 100644 Encelado/src/Encelado.Bot/Baskets/Ledger.cs delete mode 100644 Encelado/src/Encelado.Bot/Configuration/CredentialResolver.cs delete mode 100644 Encelado/src/Encelado.Bot/Configuration/CredentialStore.cs create mode 100644 Encelado/src/Encelado.Bot/Configuration/EtoroKeyStore.cs delete mode 100644 Encelado/src/Encelado.Bot/Diagnostics/AnalyticsLog.cs delete mode 100644 Encelado/src/Encelado.Bot/Diagnostics/Metrics.cs delete mode 100644 Encelado/src/Encelado.Bot/Engine/AccountState.cs create mode 100644 Encelado/src/Encelado.Bot/Engine/IEngine.cs delete mode 100644 Encelado/src/Encelado.Bot/Engine/LegState.cs delete mode 100644 Encelado/src/Encelado.Bot/Engine/MetaGate.cs delete mode 100644 Encelado/src/Encelado.Bot/Engine/PairPipeline.cs delete mode 100644 Encelado/src/Encelado.Bot/Engine/PairRouter.cs delete mode 100644 Encelado/src/Encelado.Bot/Engine/TradeJournal.cs delete mode 100644 Encelado/src/Encelado.Bot/Engine/TradingEngine.cs rename Encelado/src/Encelado.Bot/Ui/{LoginWindow.xaml => EtoroLoginWindow.xaml} (54%) create mode 100644 Encelado/src/Encelado.Bot/Ui/EtoroLoginWindow.xaml.cs create mode 100644 Encelado/src/Encelado.Bot/Ui/IUiActions.cs delete mode 100644 Encelado/src/Encelado.Bot/Ui/LoginWindow.xaml.cs delete mode 100644 Encelado/src/Encelado.Bot/Ui/Navigation.cs create mode 100644 Encelado/src/Encelado.Bot/Ui/Pages/DashboardPage.xaml create mode 100644 Encelado/src/Encelado.Bot/Ui/Pages/DashboardPage.xaml.cs delete mode 100644 Encelado/src/Encelado.Bot/Ui/Pages/PositionsPage.xaml delete mode 100644 Encelado/src/Encelado.Bot/Ui/Pages/PositionsPage.xaml.cs delete mode 100644 Encelado/src/Encelado.Bot/Ui/Pages/StatusPage.xaml delete mode 100644 Encelado/src/Encelado.Bot/Ui/Pages/StatusPage.xaml.cs create mode 100644 Encelado/src/Encelado.Bot/Ui/PromptWindow.xaml create mode 100644 Encelado/src/Encelado.Bot/Ui/PromptWindow.xaml.cs delete mode 100644 Encelado/src/Encelado.Bot/Ui/SparkChart.cs create mode 100644 Encelado/src/Encelado.Bot/Ui/UiClock.cs delete mode 100644 Encelado/src/Encelado.Core/Backtest/Baseline.cs delete mode 100644 Encelado/src/Encelado.Core/Backtest/CsvBarSource.cs delete mode 100644 Encelado/src/Encelado.Core/Backtest/PairBacktest.cs delete mode 100644 Encelado/src/Encelado.Core/Backtest/PrimaryReplay.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Backtest/BacktestBroker.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Backtest/BasketBacktest.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Backtest/BasketTrials.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/BasketDecider.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/BasketExecutor.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/BasketMath.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/BasketPosition.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/BasketStrategyConfig.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/CostGate.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Data/BidAskBar.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Data/TickToBars.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/ExecutionMode.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Learning/LearningFeatures.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Learning/ModelEvaluator.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Learning/OnlineLogistic.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Learning/SmallMlp.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Learning/ThompsonBandit.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/Learning/VolForecast.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/PipMath.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/SymbolSeries.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/SyntheticCross.cs create mode 100644 Encelado/src/Encelado.Core/Baskets/VolParitySizing.cs create mode 100644 Encelado/src/Encelado.Core/Broker/Broker.cs create mode 100644 Encelado/src/Encelado.Core/Broker/PaperBroker.cs create mode 100644 Encelado/src/Encelado.Core/Broker/RateLimiter.cs delete mode 100644 Encelado/src/Encelado.Core/Indicators/AdvancedIndicators.cs delete mode 100644 Encelado/src/Encelado.Core/Indicators/Indicators.cs delete mode 100644 Encelado/src/Encelado.Core/Indicators/OrderFlowIndicators.cs delete mode 100644 Encelado/src/Encelado.Core/Indicators/RollingWindow.cs delete mode 100644 Encelado/src/Encelado.Core/Journal/IJournalSink.cs delete mode 100644 Encelado/src/Encelado.Core/Market/MarketTypes.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/AlternativeBars.cs create mode 100644 Encelado/src/Encelado.Core/Ml/Classification.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/DriftMonitor.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/FeatureRegistry.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/FractionalDifferentiation.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/Gbdt.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/MetaLabeling.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/PairFeatureBuffer.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/PairSeries.cs create mode 100644 Encelado/src/Encelado.Core/Ml/Pbo.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/SampleWeights.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/TripleBarrier.cs delete mode 100644 Encelado/src/Encelado.Core/Ml/Validation.cs create mode 100644 Encelado/src/Encelado.Core/News/CalendarParser.cs create mode 100644 Encelado/src/Encelado.Core/News/RssParser.cs create mode 100644 Encelado/src/Encelado.Core/News/SentimentEngine.cs create mode 100644 Encelado/src/Encelado.Core/News/SentimentLexicon.cs delete mode 100644 Encelado/src/Encelado.Core/Portfolio/PortfolioBook.cs delete mode 100644 Encelado/src/Encelado.Core/Portfolio/Position.cs delete mode 100644 Encelado/src/Encelado.Core/Risk/RiskEngine.cs delete mode 100644 Encelado/src/Encelado.Core/Risk/RiskLimits.cs delete mode 100644 Encelado/src/Encelado.Core/Rl/DqnAgent.cs delete mode 100644 Encelado/src/Encelado.Core/Rl/Mlp.cs delete mode 100644 Encelado/src/Encelado.Core/Rl/PairEnvironment.cs delete mode 100644 Encelado/src/Encelado.Core/Statistics/Cointegration.cs delete mode 100644 Encelado/src/Encelado.Core/Statistics/DickeyFuller.cs delete mode 100644 Encelado/src/Encelado.Core/Statistics/Johansen.cs delete mode 100644 Encelado/src/Encelado.Core/Statistics/KalmanHedge.cs delete mode 100644 Encelado/src/Encelado.Core/Statistics/Linear.cs delete mode 100644 Encelado/src/Encelado.Core/Strategies/Pairs/PairTypes.cs delete mode 100644 Encelado/src/Encelado.Core/Strategies/Pairs/StatArbStrategy.cs delete mode 100644 Encelado/src/Encelado.Core/Strategies/StrategyParameters.cs create mode 100644 Encelado/src/Encelado.Etoro/Encelado.Etoro.csproj create mode 100644 Encelado/src/Encelado.Etoro/EtoroBroker.cs create mode 100644 Encelado/src/Encelado.Etoro/EtoroHttp.cs create mode 100644 Encelado/src/Encelado.Etoro/EtoroOptions.cs create mode 100644 Encelado/src/Encelado.Etoro/Json.cs delete mode 100644 Encelado/src/Encelado.Storage/Encelado.Storage.csproj delete mode 100644 Encelado/src/Encelado.Storage/StorageDb.cs delete mode 100644 Encelado/tests/Encelado.Tests/AdvancedIndicatorTests.cs create mode 100644 Encelado/tests/Encelado.Tests/BasketTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/BinanceTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/IndicatorTests.cs create mode 100644 Encelado/tests/Encelado.Tests/LearningTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/MlTests.cs create mode 100644 Encelado/tests/Encelado.Tests/NewsTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/OrderFlowTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/PairEngineTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/PortfolioTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/QuantTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/ReconnectTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/ResearchTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/RiskTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/StatArbTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/StorageTests.cs delete mode 100644 Encelado/tests/Encelado.Tests/StreamDecodingTests.cs create mode 100644 Encelado/tests/Encelado.Tests/UiRenderTests.cs create mode 100644 Encelado/tests/fixtures/boc_press.xml create mode 100644 Encelado/tests/fixtures/ecb_press.xml create mode 100644 Encelado/tests/fixtures/fed_press_all.xml create mode 100644 Encelado/tests/fixtures/ff_calendar_thisweek.json create mode 100644 Encelado/tests/fixtures/ff_calendar_thisweek.xml create mode 100644 Encelado/tests/fixtures/fxstreet_news.xml create mode 100644 Encelado/tools/Encelado.Backtest/Baskets.cs delete mode 100644 Encelado/tools/Encelado.Backtest/Research.cs create mode 100644 Encelado/tools/Encelado.Backtest/Ticks.cs diff --git a/Encelado/.gitignore b/Encelado/.gitignore index 7bea176..314ef13 100644 --- a/Encelado/.gitignore +++ b/Encelado/.gitignore @@ -16,3 +16,6 @@ logs/ # build/gitea.example.json; questo file contiene una credenziale e non entra # mai nel repository. build/gitea.json + +# Output dei test (coverlet) +TestResults/ diff --git a/Encelado/.vscode/tasks.json b/Encelado/.vscode/tasks.json index a94bfaa..c5a3d7f 100644 --- a/Encelado/.vscode/tasks.json +++ b/Encelado/.vscode/tasks.json @@ -39,7 +39,7 @@ }, { "label": "backtest", - "detail": "Rigioca una serie storica di prezzi: taratura contro verifica, con il confronto sul comprare e tenere.", + "detail": "Ricerca sui basket: ticks (tick MT5 → barre), baskets (griglia, PSR/DSR, PBO, walk-forward), falsify (test di falsificazione).", "type": "process", "command": "dotnet", "args": [ @@ -128,15 +128,15 @@ { "id": "dati", "type": "promptString", - "description": "File CSV con la serie storica dei prezzi", - "default": "C:\\Users\\alber\\Downloads\\BTC\\btcusd_bitstamp_1min_2012-2025.csv" + "description": "Cartella dei dati: data/market (barre M15) per baskets e falsify, la cartella dei tick MT5 per ticks", + "default": "C:\\Users\\alber\\Documents\\Encelado\\data\\market" }, { "id": "comando", "type": "pickString", "description": "Cosa misurare", - "options": ["split", "walk", "frequency", "costs", "sweep", "explore"], - "default": "split" + "options": ["baskets", "falsify", "ticks"], + "default": "baskets" } ] } diff --git a/Encelado/CHANGELOG.md b/Encelado/CHANGELOG.md new file mode 100644 index 0000000..f59cb71 --- /dev/null +++ b/Encelado/CHANGELOG.md @@ -0,0 +1,22 @@ +# Cronologia + +Formato: una voce per sessione di lavoro, con data. Le voci più recenti in alto. + +## 2026-09-16 (pomeriggio) — 4.0.0: solo Correlation Baskets su eToro, bot autonomo, interfaccia nuova + +- **Rimossi** i motori precedenti: Binance, Alpaca, cTrader/proba, SQLite, GBDT, RL, TA-Lib, indicatori, backtest a coppie, pagine e test relativi (ADR-0004). Nessun pacchetto NuGet nell'applicazione. +- **Modalità** ridotte a `Paper`, `Demo` (default), `Live`: nessuna approvazione manuale dei singoli ordini (decisione dell'utente, ADR-0005). I nomi precedenti vengono letti con un avviso. +- **Interfaccia** rifatta: barra in alto con tre schede, stato, ambiente, ora e AVVIA; dashboard con equity, P&L di oggi, P&L aperto, drawdown, basket aperti, tabella dei basket, contesto e attività. Tema nuovo. Test di rendering in PNG. +- **Fuso orario** della finestra selezionabile (`ui.timeZone`); il log porta l'offset, il ledger resta UTC. +- **Corretto** il parser dei costi di eToro (campo `value`): markup e overnight non erano letti. +- **Apprendimento** collegato al motore: modello in ombra, challenger, bandit, previsione di volatilità, ciclo settimanale, `knowledge/`. Standardizzatore dell'MLP adattato all'insieme di addestramento. +- **Backtest**: test di falsificazione 5 (segnale invertito), scenario di costi `api`, `docs/STRATEGY.md` con il verdetto negativo e i numeri. +- Feed: dopo due errori consecutivi una fonte logga solo a debug e ritenta con attese crescenti. +- Documenti nuovi: `STRATEGY.md`, `ML_AND_LEARNING.md`, `RUNBOOK.md`, `GLOSSARY.md`, `KNOWN_ISSUES.md`, ADR-0004, ADR-0005. Catena di rilascio aggiornata ai tre comandi dello strumento. +- Versione 4.0.0. + +## 2026-09-16 (mattina) — Correlation Baskets su eToro, Fasi 0-7 + +- Ricognizione del repository; verifica dell'API eToro (rotte, quote, schemi, limiti), degli strumenti, della valuta del conto, dei feed, del formato dei tick. +- Broker eToro (`Encelado.Etoro`), `PaperBroker`, chiavi DPAPI, `--headless`; cross sintetici, indicatori, decisore, cost gate, sizing, esecutore con protocollo leg-risk, backtest event-driven e griglia con PSR/DSR/PBO/walk-forward; calendario, RSS, sentiment, ledger; livelli di apprendimento 0-3 nel Core. +- Documenti: `CLAUDE.md`, `docs/ARCHITECTURE.md`, `docs/QUESTIONS.md`, `docs/STATE.md`, `DATA_SOURCES.md`, `LEDGER_SCHEMA.md`, `RISK_RULES.md`, ADR-0001 (eToro), ADR-0002 (storage su file), ADR-0003 (motore cTrader mantenuto selezionabile; superata da ADR-0004). diff --git a/Encelado/CLAUDE.md b/Encelado/CLAUDE.md new file mode 100644 index 0000000..5701059 --- /dev/null +++ b/Encelado/CLAUDE.md @@ -0,0 +1,67 @@ +# Encelado — guida per chi lavora sul repository (umano o AI) + +**Leggi prima `docs/STATE.md`.** È la memoria di lavoro fra una sessione e l'altra: dice a che fase siamo, cosa è stato fatto per ultimo e cosa manca. + +## Scopo + +Bot di trading in C# (.NET 10, WPF) su **eToro** con la strategia "Correlation Baskets": cinque basket di due coppie forex correlate, ingresso quando il cross sintetico diverge (z-score), uscita quando converge o al take-profit di basket, stop di basket obbligatorio, cost gate sullo spread reale, ledger completo, feed gratuiti di calendario e notizie, livelli di apprendimento 0-3 costruiti da zero. È l'unica strategia del repository: i motori precedenti (Binance, cTrader/proba, ricerca) sono stati rimossi il 2026-09-16 (ADR-0004) e vivono solo nella storia git. Il bot opera da solo in ogni modalità (ADR-0005): `Paper`, `Demo` (default), `Live`. + +## Mappa dei documenti + +| File | Contenuto | +|---|---| +| `docs/STATE.md` | stato corrente, fase, ultima sessione, prossimi passi, problemi aperti | +| `docs/ARCHITECTURE.md` | progetti, flusso dati, macchine a stati, interfacce | +| `docs/STRATEGY.md` | logica dei basket, cross sintetici, formule, preset, aspettative oneste, numeri | +| `docs/ML_AND_LEARNING.md` | livelli 0-3, feature, label, addestramento, attivazione, esclusioni | +| `docs/DATA_SOURCES.md` | ogni fonte (URL, formato, limiti), schema dei file in `data/` | +| `docs/LEDGER_SCHEMA.md` | schema di `decisions.jsonl`, `baskets.csv`, `trials.csv`, `calibration.csv`, `preregistrazione.csv`, `proposals.csv` | +| `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/QUESTIONS.md` | domande poste per fase, risposte o default applicati, con data | +| `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 | + +## Convenzioni + +- **C#**, `Nullable` e `TreatWarningsAsErrors` attivi. Identificatori e commenti tecnici in inglese; documentazione, report e colonna `motivazione` in italiano. +- **Nessun pacchetto NuGet.** Solo BCL e WPF nei progetti dell'applicazione; xunit nei test. +- **Tabelle**: CSV con separatore `;`, header, ultima colonna `motivazione`; JSONL append-only per ledger e notizie; JSON per modelli e stato. Scritture atomiche (`.tmp` + `File.Move`), rotazione mensile. **Nessuna riga del ledger viene mai modificata**: le correzioni sono righe nuove con `evento = correzione`. +- **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` 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. +- 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`. +- **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= dotnet test tests/Encelado.Tests --filter UiRenderTests` scrive `dashboard.png`, `log.png`, `settings.png`, `window.png`. + +## Comandi + +```powershell +dotnet build Encelado.slnx # compilazione +dotnet test tests/Encelado.Tests --no-restore # test (xunit) +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 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 -- falsify --data "%USERPROFILE%\Documents\Encelado\data\market" --out reports [--costs api] +dotnet msbuild build/Release.proj -t:Rilascia -p:Versione=4.0.0 # installatore + zip + tag + release su Gitea (dopo il commit e il push del ramo) +``` + +## Regole + +1. **Chiedi se hai un dubbio.** Le domande vanno in `docs/QUESTIONS.md`, numerate, con il default che applicheresti; in assenza di risposta applica il default più prudente e annotalo. +2. **Mai un ordine reale senza flag e conferma.** `run.executionMode` predefinito `Demo`; `Live` richiede `run.allowLive = true` **e** la frase `CONFERMO LIVE` all'avvio. Nessuna approvazione per singolo ordine (D-20): il bot opera da solo. +3. **Mai una riga del ledger modificata.** +4. **Mai un risultato abbellito.** Se la strategia non regge i costi di eToro, il report lo dice con i numeri. "Nessuna configurazione profittevole" è un esito ammesso. +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. +7. Aggiorna `docs/STATE.md` e `CHANGELOG.md` a fine sessione; un ADR per ogni scelta non ovvia. + +## Cose da non fare + +- Non scrivere chiavi in chat, nel log, nel repo o in `Documenti`. +- Non usare spread fissi nel cost gate: sempre lo spread reale letto dall'API in quel momento più il markup dell'endpoint dei costi. +- Non ricostruire feature a posteriori: il dataset di addestramento è il ledger scritto al momento della decisione. +- Non cambiare parametri live in automatico: le proposte passano da `knowledge/proposals.csv` e dal forward test. +- Non spostare l'installazione in Program Files (vedi `docs/adr/`): l'app scrive accanto alla configurazione. +- Non usare heredoc lunghi o con backslash nel Bash tool: usare `Write`/`Edit` (vedi memoria `strumenti-heredoc-backslash`). diff --git a/Encelado/Directory.Build.props b/Encelado/Directory.Build.props index 70c0ab5..240668b 100644 --- a/Encelado/Directory.Build.props +++ b/Encelado/Directory.Build.props @@ -18,7 +18,7 @@ build/Release.proj — e questo serve solo da seme quando non esiste ancora nessun tag. Tenerlo allineato all'ultimo rilascio evita di leggere in finestra un numero che non corrisponde a niente. --> - 3.5.0 + 4.0.0 + - pairs - 15m + baskets $(Radice)\tools\Encelado.Backtest\bin\Release\net10.0\backtest.exe + Text="Serve la cartella dei dati: -p:Dati="%USERPROFILE%\Documents\Encelado\data\market" (barre candles_SYMBOL_M15.csv) per baskets e falsify, oppure la cartella dei tick MT5 per ticks.%0AComandi disponibili in -p:Comando= : ticks, baskets, falsify (altre opzioni in -p:Extra=, es. --costs api --quick)." /> - + - + + Command=""$(BacktestExe)" $(Comando) --data "$(Dati)" $(Extra)" /> diff --git a/Encelado/config/encelado.json b/Encelado/config/encelado.json index c23a15e..307a50b 100644 --- a/Encelado/config/encelado.json +++ b/Encelado/config/encelado.json @@ -1,225 +1,52 @@ { - "_comment": "Encelado — arbitraggio statistico su coppie cointegrate, Binance Futures USDⓈ-M. Il bot non prende posizione sulla direzione del mercato: è long una gamba e short l'altra nel rapporto che il test di cointegrazione produce, e scommette solo sul fatto che la distanza fra le due si richiuda.", + "_commento": "Configurazione di Encelado — Correlation Baskets su eToro (CFD forex). Le chiavi con il prefisso _ sono documentazione e vengono ignorate. I parametri della strategia (basket, preset, soglie, rischio) stanno in strategy.json accanto a questo file. Le chiavi API non stanno qui: si inseriscono dalla finestra e vivono cifrate in %LOCALAPPDATA%\\Encelado\\etoro.dat, oppure nelle variabili d'ambiente ETORO_API_KEY e ETORO_USER_KEY.", - "_misura": "IMPORTANTE — che cosa dice il backtest, e perché dryRun parte ATTIVO. Misurato su 6,6 anni di barre da un minuto (2020-2026) di ETHUSDT, BTCUSDT, SOLUSDT e AVAXUSDT, ripiegate su 5m, 15m, 30m, 1h e 4h, con commissione taker 4 bps più 1 bp di slippage per lato. Il risultato: su 5m nessuna combinazione di soglie supera nemmeno i filtri di taratura; su 15m e 1h la ricerca a griglia trova combinazioni che rendono in taratura e in verifica, ma NESSUNA delle prime dieci resta positiva sulla terza fetta, quella che non entra mai nella scelta. Con i valori qui sotto, sulle quattro coppie misurabili, la conferma dà tre risultati negativi e uno positivo del +0,40% su una sola operazione. La ragione di fondo è che il test di cointegrazione passa solo l'8-13% del tempo, quindi in sei anni una coppia produce qualche decina di operazioni: troppo poche per distinguere un margine reale da una serie fortunata. Questi valori sono i meglio supportati fra quelli provati, non una strategia dimostrata: per questo la configurazione di fabbrica punta alla TESTNET, dove gli ordini partono davvero ma i soldi sono finti. Lo strumento è in tools/Encelado.Backtest: 'backtest basket --data ' rifà la scelta da capo sui tuoi dati.", - - "binance": { - "_testnet": "true = testnet (denaro finto, stesse API). Metterlo a false opera con denaro reale. Le chiavi dei due ambienti sono diverse e vengono salvate separatamente.", - "testnet": true, - - "_leverage": "Leva applicata a ogni simbolo all'avvio. La guida indica 2x-3x: un libro delta-neutral si liquida comunque se una sola gamba fa un salto isolato abbastanza grande. Alzarla non aumenta il margine della strategia, moltiplica soltanto guadagno e perdita.", - "leverage": 2, - - "_marginType": "CROSSED mette le due gambe nello stesso pool di margine, così si compensano invece di avere ciascuna il proprio prezzo di liquidazione.", - "marginType": "CROSSED", - - "recvWindowMs": 5000, - "requestsPerMinute": 1200, - "httpTimeoutSeconds": 10, - "maxRetries": 3 + "etoro": { + "_note": "eToro Public API. environment = demo oppure real: chiavi e rotte sono diverse, e l'ambiente attivo è sempre visibile nella finestra.", + "environment": "demo", + "baseUrl": "https://public-api.etoro.com", + "requestTimeoutSeconds": 20, + "_fillTimeoutSeconds": "Quanto attendere l'esito di un ordine (eToro lo lavora in modo asincrono) prima di trattarlo come non confermato e riconciliare. È anche il timeout della seconda gamba (leg-risk).", + "fillTimeoutSeconds": 5 }, - "engine": { - "_timeFrame": "15m. La guida indica 5m o 15m; il backtest ha bocciato 5m senza appello — nessuna combinazione di soglie supera nemmeno i filtri di taratura, perché a cinque minuti la commissione vale circa una deviazione standard dello spread. A 15m il numero di operazioni è almeno misurabile. A 1h e 4h le operazioni scendono a poche decine in sei anni: troppo poche per dire alcunché. '1m' è rifiutato dal validatore.", - "timeFrame": "15m", - - "_warmupBars": "Barre storiche scaricate all'avvio per riempire la finestra dello z-score prima della prima decisione. Deve coprire zWindow.", - "warmupBars": 1500, - - "_calibrationBars": "Su quante barre gira la regressione di cointegrazione che produce beta e p-value. 500 è il valore della guida ed è anche il migliore misurato: a 1000 lo stesso paniere peggiora nettamente su tutte e tre le fette. Questa finestra è indipendente da zWindow, che è molto più lunga: la prima stabilisce il rapporto di copertura, la seconda dice quanto è insolito lo scostamento di adesso.", - "calibrationBars": 500, - - "_recalibrateHours": "Ogni quanto l'intero paniere viene rifittato e ritestato. La guida dice 24 ore; provate anche una settimana, senza differenze sostanziali.", - "recalibrateHours": 24, - - "_entryOrderType": "'limit' invia un limite marcabile, prezzato oltre il touch di limitOffsetBps: si comporta come un ordine a mercato ma non può eseguire a un prezzo assurdo. 'market' attraversa e basta.", - "entryOrderType": "limit", - "limitOffsetBps": 2, - - "_postOnly": "Solo maker (GTX). Fa risparmiare la commissione ma un ordine che non esegue lascia una gamba scoperta, che costa molto di più. Lasciare false se non hai misurato il tuo tasso di riempimento.", - "postOnlyEntries": false, - - "_dryRun": "Calcola e registra tutto, non invia nessun ordine. Spento di fabbrica: in testnet i soldi sono finti e la prova più vicina alla produzione è quella con gli ordini veri sul conto di prova — eseguiti, rifiutati, parzialmente riempiti, con le stesse regole di lotto e di margine. Accendilo per osservare una configurazione nuova senza toccare nemmeno il conto di prova. Sul conto reale resta comunque la conferma esplicita all'avvio.", - "dryRun": false, - - "_reconcile": "Ogni quanto il bot ricontrolla conto, posizioni e ordini contro Binance. È anche quando recupera le barre che lo stream non ha consegnato e chiude le coppie rimaste con una gamba sola.", - "reconcileSeconds": 20, + "run": { + "_executionMode": "Paper = simulatore locale sopra le quotazioni reali (nessun ordine sul conto). Demo = conto demo di eToro: ordini veri, denaro virtuale, il bot apre e chiude da solo. Live = conto reale: richiede allowLive = true e la frase CONFERMO LIVE a ogni avvio. Nessuna modalità chiede l'approvazione dei singoli ordini (decisione D-20).", + "executionMode": "Demo", + "allowLive": false, + "_pollSeconds": "Secondi fra due letture delle quotazioni (una richiesta per tutti gli strumenti). 3 s = 20 richieste al minuto su una quota di 120: resta spazio per candele e costi.", + "pollSeconds": 3, + "_statusSeconds": "Ogni quanti secondi il bot scrive una riga di stato nel log (e sulla console in headless).", "statusSeconds": 60, - - "_explain": "Ogni quanto il bot rilegge cosa farebbe adesso e lo scrive nel log, se è cambiato. È la riga che distingue un bot che aspetta da uno bloccato.", - "explainSeconds": 5, - - "_maxQuoteAge": "Rifiuta un ingresso se il book di una gamba è più vecchio di così. 0 disattiva il controllo.", - "maxQuoteAgeSeconds": 15, - + "_closeOnShutdown": "true = fermare il bot chiude i basket aperti a mercato. false = restano sul conto con gli stop nativi sul server, senza nessuno che applichi il take-profit o lo stop di basket finché il bot non riparte.", "closeOnShutdown": false, - "logEveryBar": true + "strategyFile": "strategy.json", + "_cartelle": "Relative alla cartella di questo file: data (mercato, calendario, notizie, ledger, modelli), knowledge (calibrazione, proposte, registri), reports.", + "dataDirectory": "data", + "knowledgeDirectory": "knowledge", + "reportsDirectory": "reports", + "_paper": "Solo per executionMode = Paper: saldo iniziale del simulatore e slippage per gamba oltre lo spread reale del momento.", + "paperStartingBalance": 10000, + "paperSlippagePips": 0.3 }, - "risk": { - "_stake": "Frazione dell'equity impegnata come MARGINE su una coppia. Con leva 2 il controvalore effettivamente al lavoro è il doppio, diviso fra le due gambe secondo β. Con 4 coppie al 15% il margine totale impegnato arriva al 60% dell'equity.", - "stakePct": 0.15, - "stakeAmount": 0, - - "_funding": "Una coppia delta-neutral incassa il funding su una gamba e lo paga sull'altra. Quando il netto è a favore la posizione rende per il solo fatto di esistere. 0.12 è il centro dell'intervallo 10-15% indicato dalla guida. 0 disattiva.", - "fundingTiltPct": 0.12, - "fundingTiltThreshold": 0.0001, - - "_exposure": "Controvalore lordo totale come multiplo dell'equity. Con leva 2 e 4 coppie al 15% si arriva a 1.2×: 2.0 lascia margine senza permettere il raddoppio.", - "maxGrossExposurePct": 2.0, - "maxOpenPairs": 4, - - "_frequency": "0 = nessun limite. Sono reti contro un difetto, non contro la strategia: un ciclo che riapre la stessa coppia cento volte costa cento volte le commissioni.", - "maxTradesPerDay": 40, - "maxTradesPerPairPerDay": 8, - "minSecondsBetweenEntries": 60, - - "_dailyLoss": "Kill switch giornaliero, non disattivabile. Su futures con leva è l'ultima fermata prima di una liquidazione.", - "maxDailyLossPct": 0.06, - "maxDailyProfitPct": 0, - - "_spread": "Il book più largo che una gamba può mostrare ed essere comunque entrata. Conta molto più che su un modello direzionale: un giro completo attraversa lo spread QUATTRO volte, quindi un book da 10 bps costa 40 bps contro un rientro che spesso vale meno.", - "maxRelativeSpread": 0.0006, - - "_notional": "Controvalore minimo e massimo per gamba, in USDT. Binance ha anche i suoi minimi per simbolo, più stringenti su BTCUSDT.", - "minOrderNotional": 25, - "maxOrderNotional": 0, - - "_margin": "Rapporto massimo fra margine di mantenimento ed equity oltre il quale non si aprono nuove coppie.", - "maxMarginRatio": 0.5, - - "_hedge": "β fuori da [1/5, 5] viene rifiutato: non è una copertura, è una scommessa sulla seconda gamba travestita da copertura.", - "maxHedgeRatio": 5.0 + "ui": { + "_timeZone": "Fuso orario con cui la finestra mostra gli orari. 'computer' = quello di Windows; 'UTC'; oppure un id di Windows (es. 'W. Europe Standard Time') o IANA (es. 'Europe/Rome'). Il file di log porta l'offset, il ledger è in UTC: cambiare questo valore non tocca nessun file.", + "timeZone": "computer" }, "logging": { - "_level": "trace | debug | info | warn | error | none. Ogni rifiuto che ferma un ordine viene scritto a 'info' o sopra, quindi 'debug' serve per il flusso dati, non per capire perché il bot non ha operato.", + "_level": "trace, debug, info, warn, error, none. 'info' basta: ogni rifiuto che impedisce un ordine viene scritto a questo livello o sopra, con il basket e il motivo esatto.", "level": "info", - - "_directory": "Dove salvare tutti gli output. Relativa alla cartella della configurazione (Documenti\\Encelado), oppure un percorso assoluto. Si cambia anche da Impostazioni.", - "directory": "logs", - "console": false, + "_directory": "Cartella dei log, relativa a questo file se non è assoluta.", + "directory": "logs", "file": "encelado.log", + "_rotazione": "Superata maxFileSizeMb il file viene ruotato (encelado.1.log, encelado.2.log…) e ne restano maxFiles.", "maxFileSizeMb": 32, "maxFiles": 10, - - "_analysis": "Tutte le tabelle usano il separatore ; e hanno una colonna motivazione che spiega in italiano il perché della riga. decisions.csv ha una riga per ogni barra valutata con spread, z-score e calibrazione; executions.csv una riga per ogni segnale arrivato agli ordini, con il verdetto del risk engine; trades.csv una riga per ogni evento d'ordine. decisions ed executions si uniscono su decisionId.", - "tradeJournal": "trades.csv", - "decisionLog": "decisions.csv", - "executionLog": "executions.csv", - - "logMarketData": false, + "_righe": "statusLines = righe della striscia di attività nella dashboard; bufferedLines = righe tenute in memoria dalla pagina Log (il file su disco resta completo).", "statusLines": 200, "bufferedLines": 5000 - }, - - "storage": { - "_note": "Il database locale (SQLite): barre canoniche, dataset, modelli, validazioni, journal e drift. Vuoto = %ProgramData%\\Encelado\\encelado.db, una cartella di sistema separata sia da Documenti (dove sta la configurazione) sia dai dati utente (dove stanno le chiavi).", - "enabled": true, - "databasePath": "" - }, - - "ml": { - "_note": "Il meta-modello (meta-labeling): un classificatore addestrato sugli esiti dei segnali passati che dice se un ingresso proposto dalla regola statistica ha probabilità di ripagare i costi. Non propone mai operazioni: può solo rifiutarne una o ridurne la size. Si addestra con lo strumento tools/Encelado.Backtest (comandi dataset, train) e vive nel database locale come 'campione' della coppia; senza campione queste voci non fanno nulla.", - "enabled": true, - "_minProbability": "Sotto questa probabilità predetta l'ingresso viene rifiutato. 0.55 significa: opero solo quando il modello vede un vantaggio, anche piccolo, rispetto a lanciare una moneta.", - "minProbability": 0.55, - "_sizeByProbability": "Con true la size è proporzionale alla convinzione del modello (2·Φ(z)−1, López de Prado), fra minSizeFraction e 1; con false è tutto o niente.", - "sizeByProbability": true, - "minSizeFraction": 0.25, - "_drift": "Ogni driftCheckBars segnali valutati le feature live vengono confrontate con quelle di addestramento (PSI e Kolmogorov-Smirnov). Una feature con PSI ≥ driftPsiAlert è 'in deriva'; con driftAlertsToSuspend feature in deriva il modello viene sospeso fino alla ricalibrazione successiva e il bot torna alla sola regola statistica. 0 = mai sospendere, solo registrare.", - "driftCheckBars": 50, - "driftPsiAlert": 0.25, - "driftAlertsToSuspend": 3 - }, - - "_pairs": "Il paniere della guida. Ogni coppia si legge come ln(A) − β·ln(B): A è la gamba su cui si misura lo spread, B quella di copertura. Il test di cointegrazione decide da solo, a ogni ricalibrazione, se una coppia è operabile: quelle che non passano restano in elenco e non vengono aperte. Sui dati disponibili passano il test solo l'8-17% del tempo, quindi il bot resta fermo a lungo — è il comportamento previsto.", - - "_parametri": "I valori qui sotto vengono dalla ricerca a griglia (tools/Encelado.Backtest, comando 'basket'), non dalla guida. Le differenze rispetto alla guida sono tre e sono tutte misurate: zWindow 1500 invece di 100, entryZ 2.5 invece di 2.0, stopZ 6.0 invece di 3.5. La prima è la più importante: con una finestra vicina all'emivita del rientro (30-40 barre) la media mobile insegue lo scostamento e lo assorbe, e il margine sparisce prima ancora delle commissioni.", - - "pairs": [ - { - "_note": "Benchmark. È l'unica coppia del paniere risultata positiva sull'intero periodo, ma con sole 35-50 operazioni in 6,6 anni: troppe poche perché il risultato significhi qualcosa.", - "symbolA": "ETHUSDT", - "symbolB": "BTCUSDT", - "enabled": true, - "parameters": { - "_zWindow": "Finestra mobile su cui si normalizza lo spread. 1500 barre da 15 minuti sono circa 15 giorni. Deve essere molte volte l'emivita del rientro (qui 30-40 barre), altrimenti la media insegue lo scostamento e lo cancella.", - "zWindow": 1500, - - "_entryZ": "Quante deviazioni standard di scostamento servono per aprire. 2.5-3.0 ha battuto costantemente 2.0.", - "entryZ": 2.5, - - "_exitZ": "Sotto questo valore lo spread è rientrato: si chiude in guadagno.", - "exitZ": 0.5, - - "_stopZ": "Stop statistico: non è uno stop di prezzo, è l'affermazione che la relazione ha smesso di valere. 6.0 ha battuto 3.5 e 4.0 — uno stop stretto su uno spread che rientra lentamente realizza perdite che sarebbero rientrate.", - "stopZ": 6.0, - - "_maxPValue": "Soglia di cointegrazione. È il filtro che tiene in piedi tutto: senza, ogni combinazione provata passa da leggermente positiva a −73%/−87%.", - "maxPValue": 0.05, - - "_halfLife": "Emivita del rientro, in barre. Sotto il minimo lo spread rientra troppo in fretta per ripagare le commissioni; sopra il massimo il capitale resta impegnato più a lungo di quanto duri la relazione.", - "minHalfLife": 3, - "maxHalfLife": 200, - - "_maxBarsInTrade": "Stop temporale in barre. 0 disattiva: nei test non ha migliorato nulla, ma è la rete contro il caso peggiore — uno spread che si ferma a metà strada e ci resta per giorni.", - "maxBarsInTrade": 0, - - "requireCointegration": 1 - } - }, - { - "_note": "Layer-1. Verificata sul backtest: negativa sull'intero periodo con questi parametri.", - "symbolA": "SOLUSDT", - "symbolB": "AVAXUSDT", - "enabled": true, - "parameters": { - "zWindow": 1500, - "entryZ": 2.5, - "exitZ": 0.5, - "stopZ": 6.0, - "maxPValue": 0.05, - "minHalfLife": 3, - "maxHalfLife": 200, - "maxBarsInTrade": 0, - "requireCointegration": 1 - } - }, - { - "_note": "Layer-2 su Ethereum. NON verificata: non avevo dati storici di OP e ARB. È la coppia con le premesse migliori — stesso ecosistema, stessa età, stessi flussi — ma finché non la misuri resta un'ipotesi.", - "symbolA": "OPUSDT", - "symbolB": "ARBUSDT", - "enabled": true, - "parameters": { - "zWindow": 1500, - "entryZ": 2.5, - "exitZ": 0.5, - "stopZ": 6.0, - "maxPValue": 0.05, - "minHalfLife": 3, - "maxHalfLife": 200, - "maxBarsInTrade": 0, - "requireCointegration": 1 - } - }, - { - "_note": "DeFi blue-chip. NON verificata: non avevo dati storici di LINK e UNI.", - "symbolA": "LINKUSDT", - "symbolB": "UNIUSDT", - "enabled": true, - "parameters": { - "zWindow": 1500, - "entryZ": 2.5, - "exitZ": 0.5, - "stopZ": 6.0, - "maxPValue": 0.05, - "minHalfLife": 3, - "maxHalfLife": 200, - "maxBarsInTrade": 0, - "requireCointegration": 1 - } - } - ] + } } diff --git a/Encelado/config/strategy.json b/Encelado/config/strategy.json new file mode 100644 index 0000000..96ba187 --- /dev/null +++ b/Encelado/config/strategy.json @@ -0,0 +1,80 @@ +{ + "_comment": "Encelado — strategia Correlation Baskets su eToro. Cinque basket di due coppie forex correlate: si entra quando il cross sintetico diverge (z-score), si esce quando converge o al take-profit di basket in pip; lo stop di basket è obbligatorio. Ogni chiave con '_' davanti è documentazione.", + + "_preset": "Conservative | Moderate | Aggressive. Fissa zIn, riskPerBasketPct, maxBaskets, tpPips, maxAdds, zStop; si cambia a caldo dalla finestra senza toccare i basket aperti. Le chiavi omonime qui sotto, se presenti, sovrascrivono il preset.", + "preset": "Moderate", + + "_signalMode": "ZScoreSynthetic (default, |z| >= zIn sul cross sintetico) oppure PipDivergence (fedele all'interfaccia Titany: divergenza in pip dall'ancora, dIn).", + "signalMode": "ZScoreSynthetic", + "_exitMode": "First = la prima fra TP in pip e rientro dello z; FixedPips = solo TP in pip lordi; ZReturn = solo |z| <= zOut.", + "exitMode": "First", + "_averagingMode": "Off | AddOnce | Grid. Off in live; AddOnce in paper. Moltiplicatore di lotto sempre 1,0 (niente martingala).", + "averagingMode": "Off", + "tpMode": "Pips", + "_sameCrossPolicy": "I basket 4 e 5 sono entrambi EURCAD: Exclusive = uno solo aperto per volta; Half = entrambi a metà size.", + "sameCrossPolicy": "Exclusive", + "preferDirectCross": false, + + "_indicatori": "Correlazione di Pearson rolling dei rendimenti M15 su window (ρ_W) e windowShort (ρ_20); z-score del cross sintetico su window; half-life OLS ricalcolata ogni halfLifeRecalcHours.", + "window": 100, + "windowShort": 20, + "rhoMin": 0.60, + "rhoShortMin": 0.40, + "halfLifeMinBars": 4, + "halfLifeMaxBars": 96, + "halfLifeRecalcHours": 4, + "atrPeriod": 14, + "ewmaSpan": 100, + "trendPeriod": 14, + + "zOut": 0.25, + "dIn": 15, + "anchorBars": 32, + "gridStepZ": 0.75, + "lotMultiplier": 1.0, + + "_uscite": "Stop di basket: |z| >= zStop, oppure perdita netta >= maxLossPerBasketPct dell'equity, oppure |ρ_20| < rhoBreak per rhoBreakBars barre, oppure maxHoldingBars barre (96 = 24 h).", + "maxLossPerBasketPct": 1.5, + "rhoBreak": 0.20, + "rhoBreakBars": 8, + "maxHoldingBars": 96, + "tpAtrMultiple": 1.0, + + "_costGate": "Costo = spread_A + spread_B (in pip-equivalenti di A) + markup e commissioni dell'API + overnight stimato per maxHoldingBars. Entrata solo se TP >= costMultiple × costo e ogni spread <= spreadMedianMultiple × la sua mediana delle ultime 24 h; spread oltre spreadAnomalyMultiple × mediana = chiusura forzata.", + "costMultiple": 3, + "spreadMedianMultiple": 2, + "spreadAnomalyMultiple": 3, + "slippagePipsPerLeg": 0.3, + "overnightPipsPerDay": 0.3, + + "_calendario": "Nessuna entrata nei blackoutBeforeMin minuti prima e blackoutAfterMin dopo un evento ad alto impatto sulle valute del basket; niente entrate dal venerdì fridayCutoffUtcHour UTC alla riapertura né nei primi openDelayMinutes dopo l'apertura settimanale; sessions = fasce orarie UTC ammesse (vuoto = sempre).", + "blackoutBeforeMin": 45, + "blackoutAfterMin": 30, + "fridayCutoffUtcHour": 20, + "openDelayMinutes": 30, + "sessions": [], + + "_sizing": "Lotto B = lotto A × (ATR_A × pipValue_A) / (ATR_B × pipValue_B); lotto A tale che la perdita allo stop valga riskPerBasketPct dell'equity; leva effettiva <= maxEffectiveLeverage sul nozionale complessivo; orderLeverage è la leva dichiarata a eToro per ogni gamba (1, 2, 5, 10, 20, 30).", + "maxEffectiveLeverage": 10, + "orderLeverage": 10, + "_volScale": "zIn effettivo = zIn × clamp(σ_prevista / σ_media_30g, volScaleMin, volScaleMax).", + "volScaleMin": 0.8, + "volScaleMax": 1.5, + "volAverageDays": 30, + "mlMinProbability": 0.55, + + "_sicurezza": "equityStopPct: perdita dal picco di equity oltre la quale il bot chiude tutto e si blocca (reset manuale con motivazione). dailyLossPct: perdita giornaliera oltre la quale niente nuove entrate fino al giorno dopo.", + "equityStopPct": 9, + "dailyLossPct": 3, + "legTimeoutSec": 5, + "clockSkewMaxSeconds": 5, + + "_baskets": "I cinque basket della specifica. Il cross sintetico e il verso delle gambe sono derivati dai codici delle valute, non configurati.", + "baskets": [ + { "a": "EURUSD", "b": "USDCHF", "enabled": true, "note": "cross sintetico EURCHF" }, + { "a": "AUDUSD", "b": "USDCAD", "enabled": true, "note": "cross sintetico AUDCAD" }, + { "a": "NZDUSD", "b": "EURNZD", "enabled": true, "note": "cross sintetico EURUSD: replica EURUSD pagando due spread" }, + { "a": "USDCAD", "b": "EURUSD", "enabled": true, "note": "cross sintetico EURCAD (stessa esposizione del basket 5)" }, + { "a": "EURAUD", "b": "AUDCAD", "enabled": true, "note": "cross sintetico EURCAD (stessa esposizione del basket 4)" } + ] +} diff --git a/Encelado/docs/ARCHITECTURE.md b/Encelado/docs/ARCHITECTURE.md new file mode 100644 index 0000000..d21359b --- /dev/null +++ b/Encelado/docs/ARCHITECTURE.md @@ -0,0 +1,148 @@ +# Architettura di Encelado + +Aggiornato: 2026-09-16 (Fase 0 della modifica "Correlation Baskets" su eToro). + +## 1. Che cosa c'era prima della modifica (ricognizione) + +### 1.1 Albero dei progetti + +``` +Encelado.slnx +├── src/Encelado.Core libreria portabile (net10.0), zero NuGet, AOT/trim-compatibile +│ ├── Backtest/ replay su coppie cointegrate (era Binance), CsvBarSource, CrossSectional +│ ├── Indicators/ SMA, EMA, RSI, MACD, ATR, RollingStdDev, Bollinger, Donchian, RollingWindow +│ ├── Journal/ IJournalSink e record dei journal (DecisionRow, TradeRow, ProbaDecisionRow…) +│ ├── Market/ Bar, Quote, Tick, Side, TimeFrame +│ ├── Ml/ GBDT nativo, meta-labeling, triple barrier, PurgedCv/CPCV, Pbo (CSCV), Classification (AUC, Brier, log-loss, calibrazione), DriftMonitor (PSI/KS) +│ ├── Research/ pipeline ProbaBot: AssetFrame, eventi CUSUM, bracci di feature B/C, EventBacktest, Trials, Gates +│ ├── Risk/ RiskEngine e RiskLimits (kill-switch giornaliero, esposizione, spread) +│ ├── Rl/ Mlp a due strati (Adam), DqnAgent, PairEnvironment +│ ├── Statistics/ Ols, DickeyFuller, Cointegration (+HalfLife), Johansen, Kalman, Pca, Performance (Sharpe, PSR, DSR, Kelly), Normal +│ └── Strategies/ StatArbStrategy (coppie), StrategyParameters +├── src/Encelado.Storage SQLite (Microsoft.Data.Sqlite) — l'unica dipendenza NuGet a runtime; journal in doppia scrittura, dataset, modelli, campioni +├── src/Encelado.CTrader adattatore cTrader Open API (NuGet cTrader.OpenAPI.Net). NON referenziato dal Bot: non è mai stato collegato +├── src/Encelado.Bot WPF (net10.0-windows), WinExe "Encelado.exe", zero NuGet +│ ├── Configuration/ BotConfig, ConfigLoader (JsonDocument a mano, chiavi sconosciute segnalate), ConfigDefaults (JSON di fabbrica incorporato), ConfigWriter (modifica per percorso puntato, scrittura atomica), CredentialStore (DPAPI in %LOCALAPPDATA%\Encelado), CredentialResolver +│ ├── Engine/ BotSupervisor (ciclo di vita, snapshot), ProbaEngine (motore cTrader, incompleto), AccountState, BotSnapshot, TradeJournal, DecisionLog +│ ├── Diagnostics/ CsvTable (tabelle ';' con header e spostamento in .old), Metrics +│ ├── Logging/ Log statico non bloccante su Channel, file ';' con rotazione, Sink per la UI +│ ├── Ui/ MainViewModel (INotifyPropertyChanged a mano), Theme.xaml (tema scuro proprio), pagine Status/Log/Settings, LoginWindow (OAuth cTrader), SettingsCatalogue, SettingField, Converters +│ └── MainWindow.xaml(.cs) shell con navigazione laterale, timer 1 s che applica lo snapshot +├── tests/Encelado.Tests xunit 2.9 (framework GIÀ presente: si usa quello, niente mini-runner) +├── tools/Encelado.Backtest strumento console di ricerca ("backtest "), unico progetto con TA-Lib +└── build/ Release.proj (verifica, pacchetto, rilascio su Gitea), Encelado.iss (Inno Setup) +``` + +- **Target framework**: `net10.0` (Bot e test `net10.0-windows`), `Nullable` e `TreatWarningsAsErrors` attivi per tutti i progetti via `Directory.Build.props`. SDK installato: 10.0.301. +- **Pattern**: nessun contenitore DI; oggetti costruiti a mano nel supervisore; async/await con `ConfigureAwait(false)` nel motore; `Channel` per il log; `Lock` per lo stato condiviso; snapshot immutabili verso la UI; ogni tabella è CSV `;` con colonna finale `motivazione`; log strutturato `timestamp;level;source;subject;event;message;exception;stack`. +- **Client broker esistente**: nessun client eToro. Esisteva un adattatore Binance (cancellato, non committato) e un adattatore cTrader (mai collegato al Bot). Il motore `ProbaEngine` usa i tipi cTrader direttamente. +- **Storage**: SQLite in `%ProgramData%\Encelado\encelado.db` (barre, dataset, modelli, journal) più CSV nella cartella dei log. Configurazione in `Documenti\Encelado\encelado.json`; credenziali cifrate DPAPI in `%LOCALAPPDATA%\Encelado`. +- **UI**: WPF, tema scuro proprio (`Ui/Theme.xaml`: palette, `Card`, `Chip`, `Kpi`, `Label`, `Value`, `Sub`, `Head`, pulsanti `Primary`/`Danger`, `PowerButton`, `ModeBadge`), font tabulare `Cascadia Mono`. Nessuna libreria MVVM: `MainViewModel` implementa `INotifyPropertyChanged` a mano. +- **Test**: xunit con test di binding WPF (`UiBindingTests` ascolta la trace source dei binding e fallisce su ogni binding irrisolto), test di configurazione, statistica, ML, rischio. +- **Build ed esecuzione**: `dotnet build Encelado.slnx`; verifica completa `dotnet msbuild build/Release.proj -t:Verifica`; l'app legge `Documenti\Encelado\encelado.json` (creato dal JSON di fabbrica al primo avvio); la versione rilasciata viene dal tag git (`build/Release.proj`). + +### 1.2 Stato dell'albero di lavoro trovato il 2026-09-16 + +L'albero **non compilava**: la sessione precedente (rework verso cTrader, 2026-09-09) era rimasta a metà e non committata. + +| Problema | Dove | +|---|---| +| `Encelado.Bot` non referenzia `Encelado.CTrader`, quindi `ProbaEngine`, `BotConfig`, `CredentialResolver`, `LoginWindow`, `AccountState` non risolvono i tipi cTrader | `src/Encelado.Bot/Encelado.Bot.csproj` | +| `MainWindow.xaml.cs` referenzia `PositionsPage` (cancellata), `_config.Binance`, `ClosePairAsync`, `EnabledPairs` (era Binance) | `src/Encelado.Bot/MainWindow.xaml.cs` | +| `CsvTable.cs` usa `Side` senza `using Encelado.Core.Market` | `src/Encelado.Bot/Diagnostics/CsvTable.cs` | +| `TestSnapshots.cs` costruisce lo snapshot dell'era Binance (`PairRow`, `EquityCurve`, `OrderRow`…) | `tests/Encelado.Tests/TestSnapshots.cs` | +| `Documenti\Encelado\encelado.json` dell'utente è nel formato Binance (sezioni `binance`, `pairs`) | file dell'utente, non nel repo | + +Decisione presa (vedi `docs/QUESTIONS.md`, D-09): il motore cTrader resta nel repository come modulo selezionabile (`engine.strategy = "proba"`) e viene rimesso in compilazione; il motore nuovo (`"baskets"`) è il predefinito. + +### 1.3 Punti di estensione usati dalla modifica + +| Cosa | Dove si aggancia | +|---|---| +| Nuova strategia | `BotSupervisor` costruisce il motore in base a `engine.strategy`; il motore espone `IEngine` (`RunAsync`, `Snapshot`, comandi) | +| Flusso dati di mercato | il motore basket interroga `IBroker.GetQuotesAsync` a polling (2-5 s) e costruisce le barre M15 in locale; le candele ufficiali servono per il riscaldamento e la riconciliazione | +| Esecuzione ordini | `IBroker.OpenAsync/CloseAsync/UpdateStopsAsync`, tre implementazioni (`EtoroBroker`, `PaperBroker`, `BacktestBroker`) | +| Log delle operazioni | `Log` (file `;`), più il ledger nuovo (`data/ledger/decisions.jsonl`, `baskets.csv`) | +| UI | pagine nuove (`BasketsPage`) selezionate dalla shell in base al motore; `Theme.xaml` riusato | +| Configurazione | `encelado.json` (sezioni `engine`, `etoro`, `logging`) + `strategy.json` (parametri e preset dei basket) letti con `JsonDocument`, modificati con `ConfigWriter` | +| Test | xunit esistente; nuove suite in `tests/Encelado.Tests/Baskets*.cs` | + +## 2. Architettura della modifica (obiettivo) + +### 2.1 Progetti (stato del 2026-09-16 sera, dopo la rimozione dei motori precedenti — ADR-0004) + +``` +src/Encelado.Core/Broker/ IBroker, modelli (Instrument, QuoteSnapshot, AccountSnapshot, BrokerPosition, OrderRequest, OrderOutcome), PaperBroker (simulatore sopra un feed reale), RateLimiter +src/Encelado.Core/Baskets/ matematica e logica pura, senza I/O: + SyntheticCross (derivazione automatica del cross e dei segni), PipMath, BasketMath (rendimenti log, ATR, EWMA vol, ρ_W/ρ_20, z-score, semiperiodo OLS, forza di trend), + SymbolSeries (barre + quote + qualità dati), BasketDecider (entrate/uscite/averaging di §5), CostGate, VolParitySizing, BasketExecutor (protocollo leg-risk), + BasketPosition (macchina a stati), BasketStrategyConfig (strategy.json, preset), ExecutionMode (Paper | Demo | Live) +src/Encelado.Core/Baskets/Data/ BidAskBar + CSV, TickToBars (tick MT5 → M15) +src/Encelado.Core/Baskets/Learning/ livelli 0-3: CalibrationTables, OnlineLogistic (SGD+L2, standardizzazione rolling), SmallMlp (16 ReLU, Adam, early stopping, gradient check), + ThompsonBandit (Beta per preset × terzile di vol), VolForecast (EWMA vs HAR-RV, PSI), LearningFeatures (28 feature del ledger), ModelEvaluator (walk-forward, fold purgati, bootstrap, attivazione) +src/Encelado.Core/Baskets/Backtest/ BasketBacktest (event-driven su barre M15 bid/ask), BacktestBroker, BasketTrials (griglia, PSR/DSR, PBO, walk-forward 6m/1m) +src/Encelado.Core/News/ parser puri: CalendarParser (JSON/XML FairEconomy), RssParser (XmlReader), SentimentLexicon, SentimentEngine (finestre 1h/4h/24h con decadimento) +src/Encelado.Core/Ml/, Statistics/ la statistica condivisa rimasta: Classification (AUC, Brier, log-loss, calibrazione), Pbo (CSCV), Performance (Sharpe, PSR, DSR, drawdown, momenti), Ols, Normal +src/Encelado.Etoro/ EtoroOptions, EtoroHttp (HttpClient, x-api-key/x-user-key/x-request-id, limitatore per classe di quota, 429 con Retry-After, scarto orologio dall'header Date), EtoroBroker : IBroker +src/Encelado.Bot/Baskets/ BasketEngine (ciclo di decisione a thread singolo, polling quote, barre locali, esecuzione diretta, equity stop, kill-switch, file STOP, riconciliazione), + Ledger (decisions.jsonl append-only, baskets.csv, rotazione mensile, scritture atomiche), Feeds (calendario + RSS con cache su disco, robots.txt, backoff), + LearningState (modello in ombra, bandit, ciclo settimanale, knowledge/), HeadlessRunner (--headless) +src/Encelado.Bot/Configuration/ BotConfig (etoro, run, ui, logging), ConfigLoader (JsonDocument, avvisi sulle sezioni di versioni precedenti), ConfigDefaults, ConfigWriter, EtoroKeyStore (DPAPI) +src/Encelado.Bot/Engine/ IEngine, BotSupervisor (ciclo di vita, snapshot, feed di attività), BotSnapshot +src/Encelado.Bot/Ui/ Theme.xaml, MainWindow (barra in alto con le tre schede), Pages/DashboardPage (i cinque numeri, la tabella dei basket, il contesto, l'attività), LogPage, SettingsPage (SettingsCatalogue, fuso orario), + EtoroLoginWindow, PromptWindow (CONFERMO LIVE, motivazione del reset), UiClock (fuso orario della finestra), MainViewModel +tools/Encelado.Backtest `ticks` (tick MT5 → barre M15 bid/ask), `baskets` (baseline, griglia, trials, PBO, walk-forward), `falsify` (i cinque test di falsificazione); `--costs etoro|api` +``` + +Progetti rimossi il 2026-09-16 (ADR-0004): `Encelado.CTrader`, `Encelado.Storage`, `Core/Backtest`, `Indicators`, `Journal`, `Market`, `Portfolio`, `Research`, `Risk`, `Rl`, `Strategies`, il grosso di `Ml` e `Statistics`, `ProbaEngine`, le pagine `StatusPage`/`LoginWindow`, `ApprovalQueue`. + +### 2.2 Flusso dati (live) + +```mermaid +flowchart LR + E[eToro Public API] -->|rates ogni 3 s| Q[Quote poller] + Q --> B[Bar builder M15] + E -->|candles| B + B --> S[Strategy loop
un solo thread] + C[Calendario + RSS] --> F[Feature contesto] + F --> S + M[Meta-modello in ombra
vol forecast] --> S + S -->|decisione| X[Executor
leg-risk protocol] + X --> E + S --> L[(Ledger jsonl/csv)] + X --> L + S --> U[Snapshot → UI / headless] + L --> K[Ciclo settimanale: L0-L3] + K --> M +``` + +Le decisioni avvengono su un solo thread; l'I/O è asincrono; l'unico gate umano per ordine è sparito (ADR-0005): restano avvio del reale, kill-switch, reset e cambio di preset. + +### 2.3 Macchina a stati del basket + +``` +Idle ──(segnale + cancelli)──► Entering ──(A e B eseguite)──► Open ──(add)──► Adding ──► Open + ▲ │ (B rifiutata/timeout → chiudi A, leg_risk_unwind, basket disattivato 1 h) + │ ▼ + └────────── Closed ◄──── Exiting ◄──(TP | z_out | stop | time-stop | manuale | forzata)── Open + │ (una gamba non chiude dopo 3 tentativi) + ▼ + Error (blocco nuove entrate finché non risolto) +``` + +### 2.4 Interfacce + +- `IBroker`: `Environment`, `GetInstrumentsAsync`, `GetQuotesAsync(ids)`, `GetCandlesAsync(id, interval, count)`, `GetAccountAsync`, `GetPositionsAsync`, `OpenAsync(OrderRequest)`, `LookupOrderAsync`, `CloseAsync(positionId, instrumentId)`, `UpdateStopsAsync(positionId, sl, tp)`, `GetCostAsync(OrderRequest)`, `GetClosedTradesAsync`, `ClockSkew`. +- `IContextProvider` (Bot): calendario, notizie e sentiment per basket (`FeedContextProvider`; `EmptyContextProvider` nei test). +- `IModel`: `Predict(features)`, `Update(features, label)`, JSON, implementato da `OnlineLogistic` e `SmallMlp`. +- `IEngine` (Bot): `RunAsync`, `CloseAllAsync`, `ExecuteAsync(EngineCommand)` con `Close`, `KillSwitch`, `SetPreset`, `ResetEquityStop(motivazione)`, `Snapshot()`. +- `IUiActions` (Bot): ciò che le pagine possono chiedere alla finestra (chiudi basket, kill-switch, preset, reset, chiavi, file). + +### 2.5 Vincoli e limiti scoperti in Fase 0 + +- L'endpoint candele di eToro accetta solo `count ≤ 1000` senza data di partenza: dà al massimo ~10 giorni di M15. Lo storico per il backtest viene dai tick MT5 forniti dall'utente (`A:\Download\Trading`, 2018-12 → 2026-09, UTC), convertiti in M15 bid/ask dallo strumento `backtest ticks`. +- Quote di mercato (`/api/v2/market-data/rates`) in batch fino a 1000 strumenti per chiamata: un polling ogni 3 s costa 20 richieste/min sulla quota condivisa di 120/min. +- Quota ordini: 20 richieste/min (demo e reale separate). Un basket costa 2 aperture + 2 chiusure. +- Le quote di `rates` sono senza markup; il costo effettivo (markup + spread di mercato + overnight) arriva da `POST /trading/info/{demo/}costs` (20/min dedicate). Il cost gate somma i due. +- Ordini: `POST /api/v2/trading/execution/{demo/}orders` (asincrono: esito con `orders:lookup` per `referenceId` = `x-request-id`); `sellShort` e leva > 1 richiedono `stopLossRate`. Chiusura: `POST /api/v1/trading/execution/{demo/}market-close-orders/positions/{id}`. +- Esposizione minima per posizione: 1000 USD (`minPositionExposure`); leva ammessa 1-30 (majors) e 1-20 (minors). Il conto reale dell'utente vale 193,18 USD: con i limiti di rischio della strategia il reale non è praticabile oggi (vedi QUESTIONS D-05). diff --git a/Encelado/docs/DATA_SOURCES.md b/Encelado/docs/DATA_SOURCES.md new file mode 100644 index 0000000..f3a31d7 --- /dev/null +++ b/Encelado/docs/DATA_SOURCES.md @@ -0,0 +1,69 @@ +# Fonti dei dati + +Aggiornato: 2026-09-16. Ogni fonte è stata verificata alla data indicata; se un feed cambia o sparisce, la voce va aggiornata e la decisione annotata in `docs/QUESTIONS.md`. + +## 1. Mercato + +| Fonte | Cosa | Formato | Frequenza | Limiti | Fallback | +|---|---|---|---|---|---| +| **eToro Public API** — `GET /api/v2/market-data/rates?instrumentIds=…` | bid/ask di tutti gli strumenti in una chiamata | JSON `{results:[{instrumentId,bid,ask,date,quoteType}]}` | polling ogni `run.pollSeconds` (3 s) | quota condivisa 120/min con le altre rotte di market data; il bot ne usa ~20/min | nessuno: senza quote il bot non decide | +| **eToro Public API** — `GET /api/v1/market-data/instruments/{id}/history/candles/asc/FifteenMinutes/1000` | ultime 1000 candele M15 (mid, senza spread) | JSON | all'avvio, per il riscaldamento e il delta | **non pagina**: niente data di partenza, al massimo ~10 giorni | le barre locali salvate a ogni chiusura | +| **eToro Public API** — `GET /api/v2/market-data/instruments?symbols=…`, `POST /trading/info/{demo/}eligibility` | id, nome, esposizione minima, leve, limiti di stop | JSON | all'avvio, scritti in `instruments.json` | 120/min e 20/min dedicate | valori prudenti incorporati (esposizione minima 1000 USD, leve 1-20) | +| **eToro Public API** — `POST /trading/info/{demo/}costs` | markup, spread di mercato, commissione, overnight e weekend per un ordine ipotetico | JSON `{costs:[{costType, currency, value}]}` — il campo è **`value`** (verificato il 2026-09-16: EURUSD 10 000 unità → markup 0, marketSpread 0,1 USD, overnightFee 0,91 USD/giorno) | ogni 15 minuti per strumento | 20/min dedicate | markup 0 e overnight da `strategy.json` | +| **Tick MetaTrader 5** — `A:\Download\Trading\__.csv` | tick bid/ask 2018-12-12 → 2026-09-15, **UTC** (verificato sui fine settimana: chiusura venerdì 20:53 estate / 21:57 inverno, riapertura domenica 21:05 / 22:05) | tab-separato `