Phase 5-UI: Copytrading-Modul-Views (Master-Trader, Closed Trades, Account-Settings)
- MasterTradersView: Liste + PropertyGrid-Editor + Account-Zuweisung (CheckedListBox -> AssignedAccountIds), Neu/Speichern/Loeschen; persistiert via ITrackedTraderRepository + synct CopyTradingState.Traders. - ClosedTradesView: read-only Grid der geschlossenen Copytrades mit aufgeloesten Account-/Trader-Namen + Kurzauswertung (ICopyTradeLogRepository). - AccountSettingsView: Account-Auswahl + PropertyGrid fuer CopyTradingAccountSettings, Speichern via Repo + State-Sync. - IPolyTraderModule.RegisterUi erhaelt jetzt IServiceProvider (Module loesen ihre View-Abhaengigkeiten auf); Program uebergibt viewServices. - CopyTradingModule.RegisterUi registriert die 3 Views (Group "CopyTrading"). - LauncherForm haengt Modul-Views dynamisch als Buttons an toolstrip_windows (Core kennt keine Modulnamen; Bindung nur ueber View-IDs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b951b912f4
commit
05e0576965
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using PolyTrader.Modules.CopyTrading.Persistence;
|
||||
using PolyTraderSharp;
|
||||
using PolyTraderSharp.Models;
|
||||
|
||||
namespace PolyTrader.Modules.CopyTrading.Ui
|
||||
{
|
||||
/// <summary>
|
||||
/// Zeigt die geschlossenen Copytrades des Moduls (mod_copytrading_closed_trades) mit
|
||||
/// aufgelösten Account-/Master-Trader-Namen und einer Kurzauswertung.
|
||||
/// </summary>
|
||||
public class ClosedTradesView : Form
|
||||
{
|
||||
private readonly ICopyTradeLogRepository _tradeLog;
|
||||
private readonly TradingState _state;
|
||||
private readonly CopyTradingState _copyState;
|
||||
|
||||
private readonly DataGridView _grid = new();
|
||||
private readonly Label _summary = new();
|
||||
|
||||
public ClosedTradesView(ICopyTradeLogRepository tradeLog, TradingState state, CopyTradingState copyState)
|
||||
{
|
||||
_tradeLog = tradeLog;
|
||||
_state = state;
|
||||
_copyState = copyState;
|
||||
|
||||
Text = "Geschlossene Copytrades";
|
||||
StartPosition = FormStartPosition.CenterScreen;
|
||||
Size = new Size(1100, 620);
|
||||
MinimumSize = new Size(700, 400);
|
||||
|
||||
var top = new Panel { Dock = DockStyle.Top, Height = 40, Padding = new Padding(8, 6, 8, 6) };
|
||||
var btnRefresh = new Button { Text = "Aktualisieren", Dock = DockStyle.Left, Width = 120 };
|
||||
btnRefresh.Click += (_, _) => LoadData();
|
||||
_summary.Dock = DockStyle.Fill;
|
||||
_summary.TextAlign = ContentAlignment.MiddleLeft;
|
||||
_summary.Padding = new Padding(12, 0, 0, 0);
|
||||
top.Controls.Add(_summary);
|
||||
top.Controls.Add(btnRefresh);
|
||||
|
||||
_grid.Dock = DockStyle.Fill;
|
||||
_grid.ReadOnly = true;
|
||||
_grid.AllowUserToAddRows = false;
|
||||
_grid.AllowUserToDeleteRows = false;
|
||||
_grid.RowHeadersVisible = false;
|
||||
_grid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
|
||||
_grid.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
|
||||
_grid.AutoGenerateColumns = true;
|
||||
|
||||
Controls.Add(_grid);
|
||||
Controls.Add(top);
|
||||
|
||||
LoadData();
|
||||
}
|
||||
|
||||
private void LoadData()
|
||||
{
|
||||
var rows = _tradeLog.Find(_ => true)
|
||||
.OrderByDescending(t => t.ClosedAt)
|
||||
.Select(t => new ClosedTradeRow
|
||||
{
|
||||
TradeId = t.TradeId,
|
||||
AccountId = t.AccountId,
|
||||
SourceTraderId = t.SourceTraderId,
|
||||
IsDemo = t.IsDemo,
|
||||
TokenId = t.TokenId,
|
||||
MarketSlug = t.MarketSlug,
|
||||
MarketQuestion = t.MarketQuestion,
|
||||
Outcome = t.Outcome,
|
||||
Side = t.Side,
|
||||
EntryPrice = t.EntryPrice,
|
||||
ExitPrice = t.ExitPrice,
|
||||
Size = t.Size,
|
||||
RealizedPnl = t.RealizedPnl,
|
||||
PnlPercent = t.PnlPercent,
|
||||
TotalFees = t.TotalFees,
|
||||
OpenedAt = t.OpenedAt,
|
||||
ClosedAt = t.ClosedAt,
|
||||
ExitReason = t.ExitReason,
|
||||
AccountName = ResolveAccount(t.AccountId, t.IsDemo),
|
||||
SourceTraderName = ResolveTrader(t.SourceTraderId)
|
||||
})
|
||||
.ToList();
|
||||
|
||||
_grid.DataSource = new BindingList<ClosedTradeRow>(rows);
|
||||
FormatColumns();
|
||||
|
||||
decimal totalPnl = rows.Sum(x => x.RealizedPnl);
|
||||
int wins = rows.Count(x => x.RealizedPnl > 0);
|
||||
double winrate = rows.Count > 0 ? (double)wins / rows.Count * 100 : 0;
|
||||
_summary.Text = $"{rows.Count} Trades | PnL gesamt: {totalPnl:F2} USDC | Winrate: {winrate:F1}%";
|
||||
}
|
||||
|
||||
private void FormatColumns()
|
||||
{
|
||||
void Hide(string name) { if (_grid.Columns[name] is { } c) c.Visible = false; }
|
||||
void Fmt(string name, string f) { if (_grid.Columns[name] is { } c) c.DefaultCellStyle.Format = f; }
|
||||
void Head(string name, string h) { if (_grid.Columns[name] is { } c) c.HeaderText = h; }
|
||||
|
||||
Hide(nameof(ClosedTradeRow.TokenId));
|
||||
Hide(nameof(ClosedTradeRow.MarketSlug));
|
||||
Hide(nameof(ClosedTradeRow.AccountId));
|
||||
Hide(nameof(ClosedTradeRow.SourceTraderId));
|
||||
Hide(nameof(ClosedTradeRow.IsDemo));
|
||||
|
||||
Fmt(nameof(ClosedTradeRow.EntryPrice), "F3");
|
||||
Fmt(nameof(ClosedTradeRow.ExitPrice), "F3");
|
||||
Fmt(nameof(ClosedTradeRow.Size), "F2");
|
||||
Fmt(nameof(ClosedTradeRow.RealizedPnl), "F2");
|
||||
Fmt(nameof(ClosedTradeRow.PnlPercent), "F1");
|
||||
Fmt(nameof(ClosedTradeRow.TotalFees), "F2");
|
||||
Fmt(nameof(ClosedTradeRow.OpenedAt), "dd.MM.yyyy HH:mm");
|
||||
Fmt(nameof(ClosedTradeRow.ClosedAt), "dd.MM.yyyy HH:mm");
|
||||
|
||||
Head(nameof(ClosedTradeRow.TradeId), "#");
|
||||
Head(nameof(ClosedTradeRow.AccountName), "Account");
|
||||
Head(nameof(ClosedTradeRow.SourceTraderName), "Master-Trader");
|
||||
Head(nameof(ClosedTradeRow.MarketQuestion), "Markt");
|
||||
Head(nameof(ClosedTradeRow.RealizedPnl), "PnL");
|
||||
Head(nameof(ClosedTradeRow.PnlPercent), "PnL %");
|
||||
Head(nameof(ClosedTradeRow.ClosedAt), "Geschlossen");
|
||||
Head(nameof(ClosedTradeRow.OpenedAt), "Eröffnet");
|
||||
}
|
||||
|
||||
private string ResolveAccount(int accountId, bool isDemo)
|
||||
{
|
||||
string suffix = isDemo ? " (Demo)" : "";
|
||||
if (_state.Accounts.TryGetValue(accountId, out var acc) && !string.IsNullOrEmpty(acc.Name))
|
||||
return acc.Name + suffix;
|
||||
return $"#{accountId}{suffix}";
|
||||
}
|
||||
|
||||
private string ResolveTrader(int traderId)
|
||||
{
|
||||
if (_copyState.Traders.TryGetValue(traderId, out var t) && !string.IsNullOrEmpty(t.DisplayName))
|
||||
return t.DisplayName;
|
||||
return traderId > 0 ? $"#{traderId}" : "Unbekannt";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user