Via le due modalita' Addestramento/Gara, per scelta dell'utente: la misura aveva detto che il lavoro di registrazione costa mezzo punto di un core, e allora tanto vale registrare sempre. Resta la coalescenza verso l'interfaccia, che era la correzione vera. COSA IMPARA. Per ogni puntata di ogni asta chiusa: e' rimasta senza risposta? E' l'unica etichetta abbondante — 1,8 milioni di esempi sui dossier locali — contro le diciannove aste vinte dall'utente, che non farebbero un modello. Il comportamento avversario non dipende da chi ha puntato, e questo rende utilizzabili le puntate altrui. Regressione logistica online scritta a mano, senza librerie, in Ml/: un passo di gradiente per esempio, aggiornata a ogni chiusura, salvata subito. Variabili a fasce: durata del ciclo, quota di autopuntate recenti, puntatori distinti, prezzo sul valore, fascia oraria (le fasce ricalcano la curva misurata sulle chiusure), feriale/festivo, ora di aggancio, tipo di puntata, profondita' dell'asta, stesso puntatore di due fa, prodotto in 32 cassetti con hash stabile. In parallelo un profilo per prodotto x fascia oraria x tipo di giorno, con restringimento verso il prodotto e poi verso il globale dove i dati scarseggiano. COME DECIDE. In ShouldPlaceBid: probabilita' appresa per il margine residuo meno il costo della puntata; sotto zero non si punta, e il registro riporta i numeri. Il modello parla sempre ma decide solo dopo 300 aste apprese. Le previsioni sono calibrate in linea con il rapporto osservato/previsto sugli ultimi 20.000 esempi. QUANTO VALE, MISURATO. Valutazione prequenziale sui 3000 dossier piu' recenti (ogni asta prima prevista poi appresa, come nell'uso): sollevamento 10,8x nella cima dell'1%, 5,8x al 5%; calibrazione entro il 10-30% in ogni fascia. Sulle 116 puntate vere dell'utente di agosto: nessuna era finale, e il cancello ne avrebbe fermate 45 senza perderne una vincente. La valutazione congelata (addestra sui vecchi, giudica sui nuovi) sovrastima di 1,5-1,8x per deriva del mercato, non per passo o regolarizzazione: provati entrambi. Griglia e prequenziale stanno nel test MlModelBacktest, target Apprendimento, attivita' VS Code "valuta apprendimento". PRIMO ADDESTRAMENTO. In sottofondo, dal piu' recente, con un tempo massimo per avvio. Il lettore salta le righe di poll prima del JSON: 6289 dossier per 8,8 GB in 64 secondi, verificato dal vivo, archivio completo al primo avvio. ACQUISIZIONE. I poll nel dossier si scrivono solo quando cambia qualcosa di visibile, e comunque uno al secondo: -88,8% misurato, senza perdere la profondita' di nessun ciclo. Il reset porta il minimo del cronometro e la durata del ciclo chiuso. Via le righe di log che copiavano i reset parola per parola. Corretto un difetto vecchio: nessun dossier marcava le puntate nostre, perche' il parser lo faceva solo se riceveva il nome utente e nel percorso dei poll non gli arrivava. Ora si marcano in MergeBidHistory e l'intestazione porta il nome utente; per i dossier vecchi il lettore accetta il nome da fuori. Trovato dal vivo e corretto: salvataggi concorrenti all'avvio quando piu' aste chiudono insieme. Il modello salvato non rilegge piu' i parametri (passo, regolarizzazione): sono scelte del codice, e un cambio vale dal riavvio dopo. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
85 lines
3.8 KiB
C#
85 lines
3.8 KiB
C#
using System;
|
|
using System.Windows;
|
|
using AutoBidder.Utilities;
|
|
|
|
namespace AutoBidder
|
|
{
|
|
/// <summary>
|
|
/// Interaction logic for App.xaml
|
|
/// </summary>
|
|
public partial class App : Application
|
|
{
|
|
protected override void OnStartup(StartupEventArgs e)
|
|
{
|
|
base.OnStartup(e);
|
|
|
|
// Le descrizioni delle impostazioni vivono nei suggerimenti invece che stampate
|
|
// sotto ogni campo. Quello predefinito di Windows sparisce dopo cinque secondi:
|
|
// per un paragrafo di tre righe significa doverlo rincorrere.
|
|
System.Windows.Controls.ToolTipService.ShowDurationProperty.OverrideMetadata(
|
|
typeof(DependencyObject), new FrameworkPropertyMetadata(60000));
|
|
|
|
// I percorsi vanno decisi per primi: da qui in poi ogni archivio li usa.
|
|
// Le impostazioni stanno nella radice fissa, quindi si possono leggere anche
|
|
// prima di sapere dove finiranno gli altri dati.
|
|
var settings = SettingsManager.Load();
|
|
AppPaths.Configure(settings.DataFolder, settings.StatsFolder, settings.LogFolder);
|
|
AppPaths.EnsureFolders();
|
|
|
|
// Le versioni precedenti scrivevano parte dei dati in Roaming e parte in Local,
|
|
// senza sottocartelle: senza questo passaggio storico e aste sparirebbero.
|
|
AppPaths.MigrateLegacyFiles();
|
|
|
|
// I registri su file partono subito: quello che succede durante l'avvio è
|
|
// proprio ciò che non si riesce a leggere a video.
|
|
TextLogService.SessionStarted(AppInfo.Version);
|
|
TextLogService.PurgeOldLogs();
|
|
TextLogService.App("INFO", $"[PERCORSI] configurazione in {AppPaths.ConfigRoot}, dati in {AppPaths.DataFolder}");
|
|
|
|
// Le righe di registro delle aste confluiscono nei rispettivi dossier.
|
|
AuctionDossier.CaptureAuctionLogs();
|
|
|
|
// Lo storico e' cresciuto per mesi con versioni diverse dell'applicazione:
|
|
// alcuni record hanno campi che allora non esistevano. Quelli deducibili si
|
|
// ricalcolano qui, una volta, prima che qualcuno li legga. Quelli NON
|
|
// deducibili non si toccano: se ne occupa la pulizia, che chiede all'utente.
|
|
try
|
|
{
|
|
var repair = CompletedAuctionsStore.RepairOnStartup();
|
|
if (repair.AnythingDone)
|
|
{
|
|
TextLogService.App("INFO", $"[STORICO] Riparati {repair.Total} campi mancanti " +
|
|
$"(chiavi prodotto {repair.ProductKeys}, esiti {repair.Outcomes}, " +
|
|
$"nomi {repair.Names})");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
TextLogService.App("WARN", $"[STORICO] Riparazione non riuscita: {ex.Message}");
|
|
}
|
|
|
|
// L'apprendimento è sempre acceso: carica ciò che sa, ricostruisce il profilo
|
|
// dallo storico se manca, e in sottofondo legge i dossier non ancora appresi.
|
|
Ml.LearningService.OnLog += m => TextLogService.App("INFO", m);
|
|
Ml.LearningService.Start(settings);
|
|
|
|
// Applica il tema salvato (chiaro/scuro) prima di mostrare la finestra
|
|
ThemeManager.ApplyFromSettings();
|
|
}
|
|
|
|
protected override void OnExit(ExitEventArgs e)
|
|
{
|
|
// Quello che è ancora in coda va sul disco adesso: un registro che perde
|
|
// proprio le ultime righe è inutile esattamente quando serve di più.
|
|
TextLogService.SessionEnded();
|
|
FileLogWriter.DisposeAll();
|
|
|
|
// L'icona di notifica sopravvive al processo se non la si rimuove:
|
|
// resterebbe nell'area di notifica come icona fantasma.
|
|
WindowsNotifier.Dispose();
|
|
|
|
base.OnExit(e);
|
|
}
|
|
}
|
|
}
|