Files
Mimante/Mimante/Tests/MlModelBacktest.cs
T
Alby96andClaude Fable 5.1 7993ca44a0 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>
2026-09-08 11:00:49 +02:00

59 lines
2.2 KiB
C#

using System;
using System.IO;
using AutoBidder.Ml;
using Xunit;
using Xunit.Abstractions;
namespace AutoBidder.Tests;
/// <summary>
/// Valuta il modello sui dossier veri. La logica sta in <see cref="LearningEvaluator"/>,
/// condivisa con la scheda dell'applicazione: qui si aggiunge solo l'attivazione da
/// variabili d'ambiente e la condizione minima.
///
/// <list type="bullet">
/// <item><c>AUTOBIDDER_ML_DB</c> — file del database SQLite;</item>
/// <item><c>AUTOBIDDER_ML_MAX</c> — quante aste al massimo (0 = tutte);</item>
/// <item><c>AUTOBIDDER_ML_OUT</c> — dove scrivere il rapporto;</item>
/// <item><c>AUTOBIDDER_ML_ME</c> — il nostro nome utente, per i dossier vecchi.</item>
/// </list>
/// </summary>
public class MlModelBacktest
{
private readonly ITestOutputHelper _output;
public MlModelBacktest(ITestOutputHelper output) => _output = output;
[Fact]
public void Valuta_il_modello_sulle_aste_reali()
{
var dbPath = Environment.GetEnvironmentVariable("AUTOBIDDER_ML_DB");
if (string.IsNullOrWhiteSpace(dbPath) || !File.Exists(dbPath))
{
_output.WriteLine("AUTOBIDDER_ML_DB non impostata o inesistente: valutazione saltata.");
return;
}
var max = int.TryParse(Environment.GetEnvironmentVariable("AUTOBIDDER_ML_MAX"), out var m) ? m : 0;
var me = Environment.GetEnvironmentVariable("AUTOBIDDER_ML_ME");
using var db = AutoBidder.Data.AuctionDatabase.Open(dbPath);
var report = LearningEvaluator.Run(db, max, me, _output.WriteLine);
_output.WriteLine(report.Text);
var outFile = Environment.GetEnvironmentVariable("AUTOBIDDER_ML_OUT");
if (!string.IsNullOrWhiteSpace(outFile))
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(outFile))!);
File.WriteAllText(outFile, report.Text);
}
if (!report.Enough) return;
// La condizione minima perche' il modello serva a qualcosa: nella cima della
// classifica le puntate finali devono essere piu' frequenti che a caso.
Assert.True(report.Lift5 > 1.0, $"il modello non separa: sollevamento al 5% = {report.Lift5:N2}");
}
}