Database SQLite al posto dei dossier, gestore del rischio, decisioni in shadow, rete neurale, bandit e simulatore

Le aste non si salvano più in un file JSON per asta più tre archivi da tenere allineati:
un solo database SQLite (winsqlite3.dll di Windows via P/Invoke, nessun pacchetto) con
prodotti, aste, puntate, interrogazioni coalescenti, reset con profondità del ciclo,
nostre puntate, decisioni del motore, misure di rete, puntatori, sessioni e contabilità.
Scrittura in coda su un thread dedicato, letture in WAL. Per scelta dell'utente si parte
da zero: il database si riempie man mano che si osservano le aste; i vecchi dossier
restano leggibili per un'importazione futura ma non si scrivono più. Storico, schede,
apprendimento, rigiocata ed esportazioni leggono tutti da lì.

Il documento di progetto (Modifiche.txt) tradotto in C# senza librerie:
- RiskManager: kill-switch (file KILL_SWITCH, anche dalla barra), HALT persistente per
  stop-loss e drawdown, tetto del giorno, aste in gioco insieme, contabilità in euro.
- Theory: valore atteso con fee, spedizione, valore reale per prodotto (nuova colonna
  nella scheda Prodotti) e copertura «Compralo Ora»; null-model; sopravvivenza empirica e
  Kaplan-Meier dai reset; arrivi di Poisson.
- Shadow per asta: in Osserva il cecchino arriva allo stesso istante, registra cosa
  avrebbe fatto e non punta; alla chiusura ogni decisione riceve l'esito, e ShadowReport
  confronta le policy sugli stessi istanti con intervallo di confidenza sul ROI.
- NeuralNet (MLP, Adam) come sfidante del logistico, scelto dal Brier prequenziale;
  ThompsonBandit come seconda policy che impara dagli esiti delle decisioni;
  PennyAuctionEnv con avversari tarati sul database e QLearningAgent conservativo;
  SimulationLab per il confronto fra policy; esportazione CSV delle decisioni con
  reason_detail. Tutto nella scheda Apprendimento.

Ml/LEGGIMI.md riscritto con le avvertenze su termini d'uso, quadro legale e realtà
economica, lo schema del database e lo stato delle milestone.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-08 11:00:49 +02:00
co-authored by Claude Fable 5.1
parent 8b7c39c9e3
commit 7993ca44a0
65 changed files with 6645 additions and 2123 deletions
@@ -117,6 +117,18 @@ namespace AutoBidder
Settings.WriteDossiersCheckBox.IsChecked = settings.WriteAuctionDossiers;
Settings.RawPollsCheckBox.IsChecked = settings.DossierIncludeRawPolls;
Settings.LogRetentionTextBox.Text = settings.LogRetentionDays.ToString();
Settings.DatabaseFileTextBox.Text = AppPaths.DatabaseFile;
Settings.BankrollEnabledCheckBox.IsChecked = settings.BankrollManagerEnabled;
Settings.MaxBidsPerAuctionTextBox.Text = settings.MaxBidsPerAuction.ToString();
Settings.MaxBidsPerSessionTextBox.Text = settings.MaxBidsPerSession.ToString();
Settings.DailyBudgetTextBox.Text = settings.DailyBudgetEuro.ToString("F2", System.Globalization.CultureInfo.CurrentCulture);
Settings.DailyStopLossTextBox.Text = settings.DailyStopLossEuro.ToString("F2", System.Globalization.CultureInfo.CurrentCulture);
Settings.MaxDrawdownTextBox.Text = settings.MaxDrawdownEuro.ToString("F2", System.Globalization.CultureInfo.CurrentCulture);
Settings.MaxConcurrentTextBox.Text = settings.MaxConcurrentActiveAuctions.ToString();
Settings.TransactionFeeTextBox.Text = settings.TransactionFeeEuro.ToString("F2", System.Globalization.CultureInfo.CurrentCulture);
Settings.HedgeCheckBox.IsChecked = settings.BuyNowHedgeEnabled;
Settings.RefreshRiskStatus();
RefreshBidLeadSummary();
@@ -440,6 +452,21 @@ namespace AutoBidder
if (int.TryParse(Settings.LogRetentionTextBox.Text?.Trim(), out var retention) && retention >= 0)
settings.LogRetentionDays = retention;
var previousDb = AppPaths.DatabaseFile;
settings.DatabasePath = NormalizeFolderChoice(Settings.DatabaseFileTextBox.Text, AppPaths.DatabaseFile, settings.DatabasePath);
if (!string.Equals(previousDb, settings.DatabasePath.Length > 0 ? settings.DatabasePath : AppPaths.DatabaseFile, StringComparison.OrdinalIgnoreCase))
Log("[DATI] Percorso del database cambiato: vale dal prossimo avvio", LogLevel.Warning);
settings.BankrollManagerEnabled = Settings.BankrollEnabledCheckBox.IsChecked ?? true;
settings.MaxBidsPerAuction = ReadBounded(Settings.MaxBidsPerAuctionTextBox.Text, settings.MaxBidsPerAuction, 0, 100000, "puntate massime per asta", "");
settings.MaxBidsPerSession = ReadBounded(Settings.MaxBidsPerSessionTextBox.Text, settings.MaxBidsPerSession, 0, 1000000, "puntate massime per sessione", "");
settings.DailyBudgetEuro = ReadEuro(Settings.DailyBudgetTextBox.Text, settings.DailyBudgetEuro, "tetto di spesa del giorno");
settings.DailyStopLossEuro = ReadEuro(Settings.DailyStopLossTextBox.Text, settings.DailyStopLossEuro, "stop-loss del giorno");
settings.MaxDrawdownEuro = ReadEuro(Settings.MaxDrawdownTextBox.Text, settings.MaxDrawdownEuro, "drawdown massimo");
settings.MaxConcurrentActiveAuctions = ReadBounded(Settings.MaxConcurrentTextBox.Text, settings.MaxConcurrentActiveAuctions, 0, 1000, "aste in gioco insieme", "");
settings.TransactionFeeEuro = ReadEuro(Settings.TransactionFeeTextBox.Text, settings.TransactionFeeEuro, "fee di transazione");
settings.BuyNowHedgeEnabled = Settings.HedgeCheckBox.IsChecked ?? true;
Utilities.SettingsManager.Save(settings);
ApplyDataFolderSettings(settings, previousData, previousStats);
@@ -488,6 +515,17 @@ namespace AutoBidder
return current;
}
/// <summary>Un importo in euro, con virgola o punto; se non valido si tiene il valore attuale.</summary>
private double ReadEuro(string? text, double current, string label)
{
var t = (text ?? "").Trim().Replace(',', '.');
if (double.TryParse(t, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var v) && v >= 0 && v < 1_000_000)
return Math.Round(v, 2);
Log($"[ERRORE] Valore {label} non valido: mantenuto {current:F2} €", LogLevel.Error);
return current;
}
/// <summary>
/// Mostra lo stato del motore: aggancio all'orologio del server e traffico prodotto.
/// </summary>