UI: Launcher-Account-Uebersicht + pure TradeAnalytics-Fundament
- TradeAnalytics (Core, pur/testbar): KPIs (Netto-PnL/Winrate/Ø/Profit-Faktor), Equity-Kurve, PnL je Modul/Account/Tag, Window-Summary. Speist Dashboard + Launcher. 7 Tests. - Launcher dgv_accountlist: Spalten via Designer (Account, Module, Polymarket-Button, Wallet-USDC, 3T-PnL, 3T-Winrate, Overall P/L). Daten je Account aus dem Core-Trade-Log via TradeAnalytics; Auto-Refresh alle 30 s; Polymarket-Button oeffnet das Wallet-Profil. DB-Abfragen fehlertolerant. Build 0 Fehler, 331 Tests gruen, --smoke-ui ok (Launcher laedt Uebersicht). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0aedddabe8
commit
7d63791e38
@@ -1,9 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using PolyTrader.Core.Analytics;
|
||||
using PolyTrader.Core.Modularity;
|
||||
using PolyTrader.Core.Persistence;
|
||||
using PolyTraderSharp.Models;
|
||||
|
||||
namespace PolyTraderSharp.Ui
|
||||
{
|
||||
@@ -21,6 +25,7 @@ namespace PolyTraderSharp.Ui
|
||||
private readonly TradingState _state;
|
||||
private readonly System.Windows.Forms.Timer _statusTimer = new() { Interval = 1000 };
|
||||
private readonly Dictionary<string, ToolStripButton> _viewButtons;
|
||||
private int _statusTicks;
|
||||
|
||||
public LauncherForm(ShellUiHost uiHost, IServiceProvider services)
|
||||
{
|
||||
@@ -74,6 +79,13 @@ namespace PolyTraderSharp.Ui
|
||||
// Offen-Status der Fenster spiegeln (Button „checked", wenn Fenster offen).
|
||||
_uiHost.OpenStateChanged += UpdateWindowButtonStates;
|
||||
|
||||
// Account-Übersicht (dgv_accountlist): Zahlenformate + Polymarket-Button.
|
||||
colAccBalance.DefaultCellStyle.Format = "N2";
|
||||
colAccPnl3d.DefaultCellStyle.Format = "N2";
|
||||
colAccWin3d.DefaultCellStyle.Format = "N1";
|
||||
colAccOverall.DefaultCellStyle.Format = "N2";
|
||||
dgv_accountlist.CellContentClick += AccountList_CellContentClick;
|
||||
|
||||
_statusTimer.Tick += (_, _) => UpdateStatus();
|
||||
_statusTimer.Start();
|
||||
UpdateStatus();
|
||||
@@ -149,6 +161,94 @@ namespace PolyTraderSharp.Ui
|
||||
|
||||
UpdateTradingToggles();
|
||||
UpdateWindowButtonStates();
|
||||
|
||||
// Account-Übersicht alle 30 s aktualisieren (DB-Abfrage je Account – nicht jede Sekunde).
|
||||
if (_statusTicks++ % 30 == 0) LoadAccountOverview();
|
||||
}
|
||||
|
||||
// ===== Account-Übersicht (dgv_accountlist) =====
|
||||
|
||||
private void LoadAccountOverview()
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
var tradeLog = _services.GetService<ITradeLogRepository>();
|
||||
if (tradeLog == null) return;
|
||||
|
||||
DateTime since3d = DateTime.UtcNow.AddDays(-3);
|
||||
var rows = new List<AccountOverviewRow>();
|
||||
|
||||
foreach (var acc in _state.Accounts.Values.OrderBy(a => a.AccountId))
|
||||
{
|
||||
List<TradeRecord> trades;
|
||||
try { trades = tradeLog.Find(t => t.AccountId == acc.AccountId); }
|
||||
catch { trades = new List<TradeRecord>(); } // DB nicht bereit -> leer statt Absturz
|
||||
|
||||
var (pnl3d, win3d, _) = TradeAnalytics.WindowSummary(trades.Where(t => t.ClosedAt >= since3d));
|
||||
string modules = trades
|
||||
.Select(t => t.ModuleName)
|
||||
.Where(m => !string.IsNullOrEmpty(m))
|
||||
.Distinct().OrderBy(m => m)
|
||||
.DefaultIfEmpty("—")
|
||||
.Aggregate((a, b) => a + ", " + b);
|
||||
|
||||
rows.Add(new AccountOverviewRow
|
||||
{
|
||||
AccountId = acc.AccountId,
|
||||
Name = (string.IsNullOrEmpty(acc.Name) ? $"#{acc.AccountId}" : acc.Name) + (acc.IsDemo ? " (Demo)" : ""),
|
||||
Modules = modules,
|
||||
WalletAddress = acc.WalletAddress,
|
||||
Balance = acc.TotalBalance,
|
||||
Pnl3d = pnl3d,
|
||||
WinRate3d = win3d,
|
||||
OverallPnl = trades.Sum(t => t.RealizedPnl)
|
||||
});
|
||||
}
|
||||
|
||||
dgv_accountlist.DataSource = rows;
|
||||
}
|
||||
|
||||
private void AccountList_CellContentClick(object? sender, DataGridViewCellEventArgs e)
|
||||
{
|
||||
if (e.RowIndex < 0 || e.ColumnIndex < 0) return;
|
||||
if (dgv_accountlist.Columns[e.ColumnIndex].Name != "colAccPoly") return;
|
||||
if (dgv_accountlist.Rows[e.RowIndex].DataBoundItem is AccountOverviewRow row)
|
||||
OpenPolymarketProfile(row.WalletAddress);
|
||||
}
|
||||
|
||||
private void OpenPolymarketProfile(string walletAddress)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(walletAddress))
|
||||
{
|
||||
MessageBox.Show("Für diesen Account ist keine Wallet-Adresse hinterlegt.", "Polymarket",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = $"https://polymarket.com/profile/{walletAddress}",
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Konnte Polymarket nicht öffnen: {ex.Message}", "Fehler",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Anzeige-Zeile der Account-Übersicht (Bindung an dgv_accountlist über DataPropertyName).</summary>
|
||||
private sealed class AccountOverviewRow
|
||||
{
|
||||
public int AccountId { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Modules { get; set; } = string.Empty;
|
||||
public string WalletAddress { get; set; } = string.Empty;
|
||||
public decimal Balance { get; set; }
|
||||
public decimal Pnl3d { get; set; }
|
||||
public decimal WinRate3d { get; set; }
|
||||
public decimal OverallPnl { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user