Dashboard nach Avalonia portiert - LiveCharts2 laeuft

Drittes Fenster: KPI-Kacheln, drei Diagramme, Tradehistorie mit Suche/Filter und
Modul-Aktivierung. Aufbau nach docs/UI-SPEZIFIKATION-WinForms.md.

- Diagramme sind jetzt echte Steuerelemente (LiveCharts2) statt nach Bitmap gerenderter
  ScottPlot-Bilder: interaktiv (Tooltips/Zoom) und ohne System.Drawing. Die Auswertung
  kommt unveraendert aus TradeAnalytics - der Chart-Wechsel beruehrte keine Fachlogik.
  Das war der Zweck der bestehenden Trennung und hat sich hier ausgezahlt.
- ModuleActivationInfo vom UI-Typ in den Core verschoben (PolyTrader.Core.Modularity):
  Aussage ueber die Modularitaet, keine Darstellungsfrage - beide Shells brauchen sie.
- Modul-Tab listet auch NICHT geladene Module, sonst liessen sie sich nie reaktivieren.
  Blockierte Module haben eine deaktivierte Schaltflaeche statt eines Hinweisdialogs;
  der Grund steht ohnehin in der Spalte 'Hinweis'.

WICHTIG - Avalonia 12 -> 11.3.19 zurueckgenommen:
Der erste Wurf zog per Version='*' Avalonia 12.1.1. LiveCharts2 2.0.5 (die aktuellste
Version) ist gegen Avalonia 11 gebaut und bricht dort zur Laufzeit:
MissingFieldException 'Avalonia.Input.Gestures.PinchEvent'. Avalonia 12 ist dem
Chart-Oekosystem voraus. Jetzt 11.3.19 (DataGrid folgt eigener Reihe: 11.3.13).
Erst wieder anheben, wenn LiveCharts2 Avalonia 12 unterstuetzt.

Verifiziert: Solution baut, 442 Tests gruen, --smoke-ui gruen (alle 4 Fenster),
App laeuft real mit allen Trading-Diensten, publisht fuer linux-x64.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-08-06 20:45:07 +02:00
co-authored by Claude Opus 5
parent cd59e5c0a5
commit 0b8728b25f
9 changed files with 737 additions and 18 deletions
@@ -0,0 +1,171 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:PolyTrader.App.Avalonia.Controls"
xmlns:vm="using:PolyTrader.App.Avalonia.ViewModels"
xmlns:lvc="using:LiveChartsCore.SkiaSharpView.Avalonia"
x:Class="PolyTrader.App.Avalonia.Views.DashboardWindow"
x:DataType="vm:DashboardChartsModel"
Title="Dashboard"
Width="1714" Height="1083">
<!--
Modulübergreifendes Dashboard aus dem Core-Trade-Log.
Aufbau übernommen aus der WinForms-Fassung (docs/UI-SPEZIFIKATION-WinForms.md):
Tab „Dashboard" mit KPI-Kacheln und drei Diagrammen, Tab „Tradehistorie" mit
gefilterter Liste, Tab „Module" zum Aktivieren/Deaktivieren (wirkt nach Neustart).
Die Diagramme sind jetzt echte Steuerelemente (LiveCharts2) statt gerenderter Bitmaps —
damit interaktiv (Tooltips, Zoom) und ohne System.Drawing.
-->
<DockPanel>
<controls:WindowMenuBar Name="menuBar" DockPanel.Dock="Top" />
<Border Classes="toolbar" DockPanel.Dock="Top">
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Name="btnRefresh" Content="Aktualisieren" />
<TextBlock Text="Konto:" VerticalAlignment="Center" />
<ComboBox Name="cbAccount" MinWidth="180" />
<TextBlock Text="Modul:" VerticalAlignment="Center" />
<ComboBox Name="cbModule" MinWidth="150" />
<TextBlock Text="Modus:" VerticalAlignment="Center" />
<ComboBox Name="cbMode" MinWidth="100" />
<TextBlock Text="Zeitraum:" VerticalAlignment="Center" />
<ComboBox Name="cbRange" MinWidth="110" />
</StackPanel>
</Border>
<TabControl Name="tabs">
<TabItem Header="Dashboard">
<Grid RowDefinitions="Auto,*" Margin="10">
<!-- KPI-Kacheln: zweizeilig (Titel oben, Wert darunter) wie zuvor. -->
<WrapPanel Grid.Row="0" Orientation="Horizontal">
<Border Classes="kpi">
<StackPanel>
<TextBlock Classes="caption" Text="Netto-PnL" />
<TextBlock Name="kpiPnl" Classes="value" Text="—" />
</StackPanel>
</Border>
<Border Classes="kpi">
<StackPanel>
<TextBlock Classes="caption" Text="Winrate" />
<TextBlock Name="kpiWin" Classes="value" Text="—" />
</StackPanel>
</Border>
<Border Classes="kpi">
<StackPanel>
<TextBlock Classes="caption" Text="Trades" />
<TextBlock Name="kpiTrades" Classes="value" Text="—" />
</StackPanel>
</Border>
<Border Classes="kpi">
<StackPanel>
<TextBlock Classes="caption" Text="Ø PnL/Trade" />
<TextBlock Name="kpiAvg" Classes="value" Text="—" />
</StackPanel>
</Border>
<Border Classes="kpi">
<StackPanel>
<TextBlock Classes="caption" Text="Profit-Faktor" />
<TextBlock Name="kpiPf" Classes="value" Text="—" />
</StackPanel>
</Border>
</WrapPanel>
<Grid Grid.Row="1" RowDefinitions="*,*" ColumnDefinitions="*,*" Margin="0,12,0,0">
<Border Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
BorderBrush="#DDDDDD" BorderThickness="1" Margin="0,0,0,8">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Margin="8,6"
FontWeight="SemiBold" Text="Equity-Kurve (kumulierter PnL)" />
<lvc:CartesianChart Name="chartEquity"
Series="{Binding EquitySeries}"
XAxes="{Binding EquityXAxes}"
TooltipPosition="Top" />
</DockPanel>
</Border>
<Border Grid.Row="1" Grid.Column="0"
BorderBrush="#DDDDDD" BorderThickness="1" Margin="0,0,4,0">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Margin="8,6"
FontWeight="SemiBold" Text="PnL je Modul" />
<lvc:CartesianChart Name="chartModule"
Series="{Binding ModuleSeries}"
XAxes="{Binding ModuleXAxes}" />
</DockPanel>
</Border>
<Border Grid.Row="1" Grid.Column="1"
BorderBrush="#DDDDDD" BorderThickness="1" Margin="4,0,0,0">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Margin="8,6"
FontWeight="SemiBold" Text="PnL je Tag" />
<lvc:CartesianChart Name="chartDay"
Series="{Binding DaySeries}"
XAxes="{Binding DayXAxes}" />
</DockPanel>
</Border>
</Grid>
</Grid>
</TabItem>
<TabItem Header="Tradehistorie">
<DockPanel>
<Border Classes="toolbar" DockPanel.Dock="Top">
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="Suche:" VerticalAlignment="Center" />
<TextBox Name="tbSearch" MinWidth="260" Watermark="Markt oder Outcome …" />
<TextBlock Text="Ergebnis:" VerticalAlignment="Center" />
<ComboBox Name="cbWinLoss" MinWidth="130" />
</StackPanel>
</Border>
<DataGrid Name="gridTrades" x:DataType="vm:DashboardTradeRow">
<DataGrid.Columns>
<DataGridTextColumn Header="Modul" Width="110" Binding="{Binding Module}" />
<DataGridTextColumn Header="Konto" Width="130" Binding="{Binding Account}" />
<DataGridTextColumn Header="Markt" Width="*" Binding="{Binding Market}" />
<DataGridTextColumn Header="Outcome" Width="80" Binding="{Binding Outcome}" />
<DataGridTextColumn Header="Side" Width="60" Binding="{Binding Side}" />
<DataGridTextColumn Header="Entry" Width="70" Binding="{Binding EntryPrice, StringFormat=\{0:F3\}}" />
<DataGridTextColumn Header="Exit" Width="70" Binding="{Binding ExitPrice, StringFormat=\{0:F3\}}" />
<DataGridTextColumn Header="Size" Width="70" Binding="{Binding Size, StringFormat=\{0:F2\}}" />
<DataGridTextColumn Header="PnL" Width="80" Binding="{Binding RealizedPnl, StringFormat=\{0:F2\}}" />
<DataGridTextColumn Header="PnL %" Width="70" Binding="{Binding PnlPercent, StringFormat=\{0:F1\}}" />
<DataGridTextColumn Header="Geschlossen" Width="140" Binding="{Binding ClosedAt, StringFormat=\{0:dd.MM.yyyy HH:mm\}}" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</TabItem>
<TabItem Name="tabModules" Header="Module">
<DockPanel>
<TextBlock DockPanel.Dock="Top" Margin="10" TextWrapping="Wrap"
Text="Module aktivieren/deaktivieren. Änderungen greifen nach dem nächsten Neustart von PolyTrader." />
<DataGrid Name="gridModules" x:DataType="vm:ModuleRow">
<DataGrid.Columns>
<DataGridTextColumn Header="Modul" Width="220" Binding="{Binding Modul}" />
<DataGridTextColumn Header="Status" Width="260" Binding="{Binding Status}" />
<DataGridTextColumn Header="Hinweis" Width="*" Binding="{Binding Hinweis}" />
<DataGridTemplateColumn Header="Aktion" Width="160">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate x:DataType="vm:ModuleRow">
<Button Content="{Binding Aktion}"
Margin="2" Padding="8,2"
Click="OnModuleActionClick"
IsEnabled="{Binding CanToggle}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</TabItem>
</TabControl>
</DockPanel>
</Window>