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>
286 lines
22 KiB
XML
286 lines
22 KiB
XML
<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=""/>
|
||
</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. 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ì. 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. 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. 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. 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>
|