Files
Mimante/Mimante/Controls/LearningControl.xaml
T
Alby96andClaude Fable 5.1 8b7c39c9e3 Sostituisci le euristiche a soglia con regime e anticipo adattivi; un solo archivio per asta
Le strategie a numero fisso (calore, ritiro morbido, anti-bot, avversari aggressivi,
puntata probabilistica, velocità del prezzo, esaurimento, concorrenza) e le impostazioni
morte (cadenze di polling, finestra critica, database, log avanzati, suggerimenti
sull'anticipo, schede dettagliate) non ci sono più: rigiocate sui dossier non fermavano
una puntata sbagliata senza fermarne anche di giuste, e i loro numeri andavano tarati a
mano. Restano i paletti dell'utente (tetti, budget, fascia oraria) e il duello.

Al loro posto due componenti che imparano dalla sessione in corso, dentro i paletti:
- CompetitionRegime per asta (Calmo / Sfogo / Sondaggio) guidato dal valore atteso
  appreso: tre negativi di fila e si lascia sfogare gli altri, si rientra con una
  puntata di prova dopo abbastanza cicli buoni, e se viene coperta subito la pazienza
  raddoppia. È il "capire da soli quando tornare a puntare".
- LatencyModel per l'anticipo: margine + p99 della latenza misurata adesso, tenuto fra
  LeadMinMs e LeadMaxMs; una puntata tardiva alza il margine subito, venti in tempo lo
  abbassano piano. Un anticipo scritto a mano su un'asta vince sempre (BidLeadIsManual).

Archiviazione: l'archivio mensile aste-AAAA-MM.jsonl (95 MB) duplicava il riepilogo che
ogni dossier ha già in coda. AuctionDetailStore ora legge testa e coda dei dossier, con
cache: 6349 file in 6 s la prima volta. Pulsanti per svuotare le esportazioni e per
azzerare le statistiche voce per voce (lo storico passa dalla copia di sicurezza).

Scheda Apprendimento: sezione "Autonomia sul momento" con il modello di latenza e il
regime di ogni asta seguita. Ml/LEGGIMI.md descrive l'algoritmo per intero.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 00:11:49 +02:00

286 lines
22 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<UserControl x:Class="AutoBidder.Controls.LearningControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ctl="clr-namespace:AutoBidder.Controls"
mc:Ignorable="d"
d:DesignHeight="800" d:DesignWidth="1200"
Background="{DynamicResource Brush.Bg}">
<UserControl.Resources>
<Style x:Key="Glyph" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
<Style x:Key="Stat" TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource Brush.Text}"/>
<Setter Property="FontSize" Value="{StaticResource Font.Size.Lg}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</Style>
<Style x:Key="StatLabel" TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource Brush.TextMuted}"/>
<Setter Property="FontSize" Value="{StaticResource Font.Size.Sm}"/>
<Setter Property="Margin" Value="0,2,0,0"/>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- ═══ Barra strumenti ═══ -->
<Border Grid.Row="0" Background="{DynamicResource Brush.Surface}" Padding="10,7"
BorderBrush="{DynamicResource Brush.Border}" BorderThickness="0,0,0,1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
<Button x:Name="RefreshButton" Style="{StaticResource IconButton}"
ToolTip="Aggiorna i numeri" Click="RefreshButton_Click">
<TextBlock Style="{StaticResource Glyph}" Text="&#xE72C;"/>
</Button>
<TextBlock Text="Apprendimento" FontWeight="SemiBold" Margin="8,0,0,0"
FontSize="{StaticResource Font.Size.Lg}"
Foreground="{DynamicResource Brush.Text}" VerticalAlignment="Center"/>
<Border Width="1" Background="{DynamicResource Brush.Border}" Margin="10,4"/>
<Border Style="{StaticResource Pill}" ToolTip="Aste da cui il modello ha imparato">
<TextBlock x:Name="PillAuctions" Text="—" FontSize="{StaticResource Font.Size.Sm}"
Foreground="{DynamicResource Brush.Text}"/>
</Border>
<Border Style="{StaticResource Pill}" ToolTip="Esempi (puntate) che hanno aggiornato i pesi">
<TextBlock x:Name="PillUpdates" Text="—" FontSize="{StaticResource Font.Size.Sm}"
Foreground="{DynamicResource Brush.TextMuted}"/>
</Border>
<Border Style="{StaticResource Pill}" ToolTip="Fattore di calibrazione: osservato/previsto sugli ultimi esempi. 1 = scala giusta">
<TextBlock x:Name="PillCalibration" Text="—" FontSize="{StaticResource Font.Size.Sm}"
Foreground="{DynamicResource Brush.TextMuted}"/>
</Border>
<Border x:Name="PillReadyBorder" Style="{StaticResource Pill}"
ToolTip="Pronto = ha appreso abbastanza aste per fermare una puntata. Prima parla soltanto.">
<TextBlock x:Name="PillReady" Text="—" FontSize="{StaticResource Font.Size.Sm}"
Foreground="{DynamicResource Brush.Warning}"/>
</Border>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center">
<Button x:Name="EvaluateButton" Style="{StaticResource SmallRoundedButton}"
Background="{DynamicResource Brush.Accent}"
Foreground="{DynamicResource Brush.TextOnAccent}"
Content="Valuta ora" Margin="0,0,8,0"
ToolTip="Addestra sui dossier più vecchi di questa macchina e giudica sui più recenti, mai visti. Poi la prova prequenziale: ogni asta prima prevista e poi appresa, come fa il motore. Qualche minuto in sottofondo."
Click="EvaluateButton_Click"/>
<Button x:Name="OpenFolderButton" Style="{StaticResource SmallRoundedButton}"
Background="{DynamicResource Brush.SurfaceAlt}"
Foreground="{DynamicResource Brush.Text}"
Content="Apri cartella" Margin="0,0,8,0"
ToolTip="La cartella con modello, profilo, elenco dei dossier letti e ultima valutazione"
Click="OpenFolderButton_Click"/>
<Button x:Name="RetrainButton" Style="{StaticResource SmallRoundedButton}"
Background="{DynamicResource Brush.SurfaceAlt}"
Foreground="{DynamicResource Brush.Danger}"
Content="Ricomincia da capo"
ToolTip="Butta modello e profilo e ristudia tutti i dossier. Non tocca i dossier né lo storico."
Click="RetrainButton_Click"/>
</StackPanel>
</Grid>
</Border>
<!-- ═══ Corpo ═══ -->
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto">
<Grid Margin="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Stato -->
<Border Grid.Row="0" Grid.Column="0" Style="{StaticResource CardBlock}">
<StackPanel>
<ctl:HelpHeader Title="Stato" Margin="0,0,0,10"
Help="Il modello impara da ogni asta che chiude: per ogni puntata, è rimasta senza risposta? È l'unica etichetta abbondante — milioni di esempi — mentre le vittorie proprie sono poche decine. Il comportamento avversario non dipende da chi ha puntato, e questo rende utilizzabili le puntate altrui.&#10;&#10;Decide col valore atteso: probabilità appresa × margine residuo costo della puntata. Sotto zero non punta, e lo scrive nel registro con i numeri. Decide solo dopo la soglia di aste apprese: prima parla soltanto."/>
<UniformGrid Columns="3" Rows="2">
<StackPanel Margin="0,0,8,10">
<TextBlock x:Name="StatAuctions" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="aste apprese"/>
</StackPanel>
<StackPanel Margin="0,0,8,10">
<TextBlock x:Name="StatUpdates" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="esempi visti"/>
</StackPanel>
<StackPanel Margin="0,0,8,10">
<TextBlock x:Name="StatProfile" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="aste nel profilo"/>
</StackPanel>
<StackPanel Margin="0,0,8,0">
<TextBlock x:Name="StatCalibration" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="calibrazione (1 = giusta)"/>
</StackPanel>
<StackPanel Margin="0,0,8,0">
<TextBlock x:Name="StatThreshold" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="soglia per decidere"/>
</StackPanel>
<StackPanel Margin="0,0,8,0">
<TextBlock x:Name="StatBootstrap" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="studio dei dossier"/>
</StackPanel>
</UniformGrid>
<TextBlock x:Name="StatDecisions" Style="{StaticResource Hint}" Margin="0,10,0,0" Text=""/>
</StackPanel>
</Border>
<!-- Cosa ha imparato -->
<Border Grid.Row="0" Grid.Column="2" Style="{StaticResource CardBlock}">
<StackPanel>
<ctl:HelpHeader Title="Cosa ha imparato" Margin="0,0,0,10"
Help="I pesi del modello, dal più forte. Positivo = quella condizione rende più probabile che una puntata resti senza risposta; negativo = meno probabile. Le variabili sono a fasce e il modello è lineare proprio per poterli leggere così.&#10;&#10;Il prodotto è ridotto a 32 cassetti con una funzione di hash: due prodotti possono condividere un cassetto, ed è accettato."/>
<DataGrid x:Name="WeightsGrid" MaxHeight="260" IsReadOnly="True" HeadersVisibility="Column"
AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Condizione" Binding="{Binding Nome}" Width="*"/>
<DataGridTextColumn Header="Peso" Binding="{Binding PesoDisplay}" Width="Auto" MinWidth="70"/>
<DataGridTextColumn Header="Effetto" Binding="{Binding Effetto}" Width="Auto" MinWidth="110"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Border>
<!-- Ultime decisioni -->
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CardBlock}">
<StackPanel>
<ctl:HelpHeader Title="Ultime decisioni" Margin="0,0,0,10"
Help="Ogni volta che il motore era pronto a puntare ha chiesto al modello. P è la probabilità che la puntata resti senza risposta; il valore atteso è P × margine residuo costo. «Fermata» = il cancello ha detto no. «Parere» = il modello non aveva ancora appreso abbastanza per decidere, e si è limitato a dirlo."/>
<DataGrid x:Name="DecisionsGrid" MaxHeight="280" IsReadOnly="True" HeadersVisibility="Column"
AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Ora" Binding="{Binding Ora}" Width="Auto" MinWidth="80"/>
<DataGridTextColumn Header="Asta" Binding="{Binding Asta}" Width="*"/>
<DataGridTextColumn Header="Prezzo" Binding="{Binding Prezzo}" Width="Auto" MinWidth="70"/>
<DataGridTextColumn Header="P senza risposta" Binding="{Binding Prob}" Width="Auto" MinWidth="110"/>
<DataGridTextColumn Header="Valore atteso" Binding="{Binding Ev}" Width="Auto" MinWidth="100"/>
<DataGridTextColumn Header="Esito" Binding="{Binding Esito}" Width="Auto" MinWidth="90"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Border>
<!-- Profilo per prodotto -->
<Border Grid.Row="2" Grid.Column="0" Style="{StaticResource CardBlock}">
<StackPanel>
<ctl:HelpHeader Title="Profilo per prodotto e fascia oraria" Margin="0,0,0,10"
Help="Cosa aspettarsi prima che un'asta cominci: puntate del vincitore e prezzo di chiusura in percentuale del valore, per prodotto, fascia oraria e tipo di giorno. Solo le combinazioni con aste osservate; quando i dati sono pochi il motore restringe verso il prodotto e poi verso la media generale.&#10;&#10;Fasce: 0-8 notte e ore sospese · 9 · 10-12 · 13-17 · 18-20 · 21-23."/>
<DataGrid x:Name="ProfileGrid" MaxHeight="320" IsReadOnly="True" HeadersVisibility="Column"
AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Prodotto" Binding="{Binding Prodotto}" Width="*"/>
<DataGridTextColumn Header="Fascia" Binding="{Binding Fascia}" Width="Auto" MinWidth="60"/>
<DataGridTextColumn Header="Giorno" Binding="{Binding Giorno}" Width="Auto" MinWidth="60"/>
<DataGridTextColumn Header="Aste" Binding="{Binding Aste}" Width="Auto" MinWidth="50"/>
<DataGridTextColumn Header="Punt. vincitore" Binding="{Binding Puntate}" Width="Auto" MinWidth="100"/>
<DataGridTextColumn Header="Chiusura" Binding="{Binding Chiusura}" Width="Auto" MinWidth="80"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Border>
<!-- Valutazione -->
<Border Grid.Row="2" Grid.Column="2" Style="{StaticResource CardBlock}">
<StackPanel>
<ctl:HelpHeader Title="Ultima valutazione" Margin="0,0,0,10"
Help="Il rapporto dell'ultima valutazione sui dossier di questa macchina. Le misure che contano: il sollevamento (quanto più spesso le puntate a probabilità alta erano davvero finali, rispetto al caso), la calibrazione per fasce (la probabilità prevista deve somigliare a quella osservata), e cosa il cancello avrebbe fatto sulle tue puntate vere."/>
<TextBox x:Name="EvaluationBox" IsReadOnly="True" TextWrapping="NoWrap"
MinHeight="200" MaxHeight="320"
FontFamily="Consolas" FontSize="11.5"
VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto"
Background="{DynamicResource Brush.SurfaceAlt}"
Foreground="{DynamicResource Brush.Text}"
BorderBrush="{DynamicResource Brush.Border}"
Text="Nessuna valutazione ancora. Premi «Valuta ora»."/>
</StackPanel>
</Border>
<!-- Autonomia sul momento -->
<Border Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="3" Style="{StaticResource CardBlock}">
<StackPanel>
<ctl:HelpHeader Title="Autonomia sul momento" Margin="0,0,0,10"
Help="Due cose che il sistema decide da solo sulla sessione in corso, dentro i paletti delle impostazioni.&#10;&#10;ANTICIPO: margine di sicurezza più la coda alta (p99) della latenza misurata adesso. Una puntata arrivata tardi alza il margine di 150 ms subito; venti puntate in tempo lo abbassano di 25. Il risultato resta fra il minimo e il massimo impostati; un anticipo scritto a mano su una singola asta vince sempre.&#10;&#10;REGIME: per ogni asta seguita. Calmo = punta se il valore atteso è positivo. Sfogo = gli altri si stanno battendo, si aspetta che si sfoghino: si rientra solo dopo «pazienza» cicli buoni di fila. Sondaggio = una puntata di prova; se viene coperta entro 9 s si torna a Sfogo e la pazienza raddoppia (fino a 24)."/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock Text="Anticipo adattivo" FontWeight="SemiBold" Margin="0,0,0,8"
Foreground="{DynamicResource Brush.Text}"/>
<UniformGrid Columns="3" Rows="2">
<StackPanel Margin="0,0,8,10">
<TextBlock x:Name="StatLeadNow" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="anticipo adesso"/>
</StackPanel>
<StackPanel Margin="0,0,8,10">
<TextBlock x:Name="StatLatP99" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="coda del ping (p99)"/>
</StackPanel>
<StackPanel Margin="0,0,8,10">
<TextBlock x:Name="StatLatMargin" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="margine"/>
</StackPanel>
<StackPanel Margin="0,0,8,0">
<TextBlock x:Name="StatLatP50" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="ping mediano"/>
</StackPanel>
<StackPanel Margin="0,0,8,0">
<TextBlock x:Name="StatLatSamples" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="campioni"/>
</StackPanel>
<StackPanel Margin="0,0,8,0">
<TextBlock x:Name="StatLatLate" Style="{StaticResource Stat}" Text="—"/>
<TextBlock Style="{StaticResource StatLabel}" Text="puntate tardive / totali"/>
</StackPanel>
</UniformGrid>
<TextBlock x:Name="StatLeadHint" Style="{StaticResource Hint}" Margin="0,8,0,0" Text=""/>
</StackPanel>
<StackPanel Grid.Column="2">
<TextBlock Text="Regime per asta" FontWeight="SemiBold" Margin="0,0,0,8"
Foreground="{DynamicResource Brush.Text}"/>
<DataGrid x:Name="RegimeGrid" MaxHeight="240" IsReadOnly="True" HeadersVisibility="Column"
AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Asta" Binding="{Binding Asta}" Width="*"/>
<DataGridTextColumn Header="Stato" Binding="{Binding StatoAsta}" Width="Auto" MinWidth="60"/>
<DataGridTextColumn Header="Regime" Binding="{Binding Regime}" Width="Auto" MinWidth="80"/>
<DataGridTextColumn Header="Pazienza" Binding="{Binding Pazienza}" Width="Auto" MinWidth="70"/>
<DataGridTextColumn Header="Sondaggi coperti" Binding="{Binding Sondaggi}" Width="Auto" MinWidth="110"/>
<DataGridTextColumn Header="P senza risposta" Binding="{Binding Prob}" Width="Auto" MinWidth="110"/>
<DataGridTextColumn Header="Valore atteso" Binding="{Binding Ev}" Width="Auto" MinWidth="100"/>
<DataGridTextColumn Header="Duello" Binding="{Binding Duello}" Width="Auto" MinWidth="60"/>
</DataGrid.Columns>
</DataGrid>
<TextBlock x:Name="RegimeHint" Style="{StaticResource Hint}" Margin="0,8,0,0" Text=""/>
</StackPanel>
</Grid>
</StackPanel>
</Border>
</Grid>
</ScrollViewer>
</Grid>
</UserControl>