Phase 3: Copy-Score + Auto-Pause-Kill-Switch (Trader-Intelligence)

Aus UNSEREN geschlossenen Trades (nicht der externen Data-API) berechnet -> jetzt machbar.

- TraderScore (pure, getestet): Compute (CopyPnl/ProfitFactor/AvgPnlPerTrade/Count
  aus realisierten PnLs) + ShouldAutoPause (enabled & count>=minTrades & pnl<=-threshold).
- TrackedTrader: CopyPnl30d/CopyProfitFactor/CopyAvgPnlPerTrade/CopyTradeCount30d
  (mit Erklärungen) + Migration AddTraderCopyScore (auf MySQL angewendet).
- CopyTradingState: globale Auto-Pause-Config (AutoPauseMinTrades 10,
  AutoPauseDrawdownUsd 10). Per-Master-Schalter TrackedTrader.AutoPauseEnabled.
- MasterTraderAnalyticsJob.UpdateCopyScoresAndAutoPauseAsync (entkoppelt von der
  flakigen Master-History-API): Copy-Score je Master (30T), harte Auto-Pause bei
  Verlust über Schwelle (IsActive=false, Reasoning+Zeitstempel, Threema; Reaktivierung
  nur manuell). Injiziert ICopyTradeLogRepository + ThreemaService.
- MasterTradersView: 4 Copy-Score-Spalten.

193 Tests gruen. Build/Smoke gruen.
OFFEN (3.2, API-abhaengig, Zielland): Sniper-Metriken (MedianHold/SellWithin5Min)
aus der Data-API (Portierung analyze_snipers.py).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-07 18:19:41 +02:00
co-authored by Claude Opus 4.8
parent cda5b055aa
commit 4619edbb99
11 changed files with 626 additions and 3 deletions
@@ -18,6 +18,10 @@ namespace PolyTraderSharp
// Copytrading-Risk-Regel: mindestens 6 Shares pro Order erzwingen.
public bool SixSharesMinimum { get; set; } = true;
// Auto-Pause-Kill-Switch (Phase 3.3), global. Pro-Master-Schalter: TrackedTrader.AutoPauseEnabled.
public int AutoPauseMinTrades { get; set; } = 10; // Mindest-Stichprobe vor Pause
public decimal AutoPauseDrawdownUsd { get; set; } = 10m; // Copy-PnL (30T) darunter -> Pause
// Kopierte Master-Trader (TraderId -> TrackedTrader)
public ConcurrentDictionary<int, TrackedTrader> Traders { get; } = new();
@@ -0,0 +1,58 @@
using System.Collections.Generic;
namespace PolyTrader.Modules.CopyTrading.Logic
{
/// <summary>
/// Reine Trader-Intelligence-Logik (Phase 3): Kennzahlen dazu, was WIR mit einem Master
/// verdient haben (Copy-PnL aus unseren geschlossenen Trades), plus die Auto-Pause-Entscheidung.
/// Bewusst pur/testbar; die Datenbeschaffung (Filter SourceTraderId + Zeitfenster) macht der Job.
/// </summary>
public static class TraderScore
{
/// <summary>Sentinel für Profit-Faktor ohne jeden Verlust (sonst Division durch 0).</summary>
public const decimal NoLossProfitFactor = 999m;
public readonly record struct CopyMetrics(int TradeCount, decimal CopyPnl, decimal ProfitFactor, decimal AvgPnlPerTrade);
/// <summary>
/// Berechnet Copy-Kennzahlen aus den realisierten PnLs unserer Trades eines Masters
/// (bereits gefiltert auf Master + Zeitfenster).
/// </summary>
public static CopyMetrics Compute(IEnumerable<decimal> realizedPnls)
{
int count = 0;
decimal sum = 0m, grossProfit = 0m, grossLoss = 0m;
foreach (var pnl in realizedPnls)
{
count++;
sum += pnl;
if (pnl > 0m) grossProfit += pnl;
else if (pnl < 0m) grossLoss += -pnl;
}
decimal profitFactor =
grossLoss > 0m ? grossProfit / grossLoss :
grossProfit > 0m ? NoLossProfitFactor : 0m;
decimal avg = count > 0 ? sum / count : 0m;
return new CopyMetrics(count, sum, profitFactor, avg);
}
/// <summary>
/// Auto-Pause-Kill-Switch (Phase 3.3): pausiert einen Master hart, wenn er über genügend
/// Trades ins Minus läuft. Reaktivierung bewusst nur manuell (hier nicht abgebildet).
/// </summary>
/// <param name="autoPauseEnabled">Per-Master-Flag (Default an).</param>
/// <param name="tradeCount">Anzahl Copy-Trades im Fenster.</param>
/// <param name="copyPnl">Summierter Copy-PnL im Fenster.</param>
/// <param name="minTrades">Mindestanzahl Trades, bevor pausiert wird (Rausch-Schutz).</param>
/// <param name="drawdownThresholdUsd">Max. erlaubter Verlust (positiv); darunter → Pause.</param>
public static bool ShouldAutoPause(bool autoPauseEnabled, int tradeCount, decimal copyPnl, int minTrades, decimal drawdownThresholdUsd)
{
if (!autoPauseEnabled) return false;
if (tradeCount < minTrades) return false;
return copyPnl <= -drawdownThresholdUsd;
}
}
}
@@ -91,9 +91,35 @@ namespace PolyTraderSharp.Models
[Category("04. Statistics")]
[ReadOnly(true)]
[DisplayName("PnL (7 Tage)")]
[Description("Realisierter Gewinn/Verlust des MASTERS im Fenster (nicht unser Copy-Ergebnis). Copy-PnL folgt in Phase 3.")]
[Description("Realisierter Gewinn/Verlust des MASTERS im Fenster (nicht unser Copy-Ergebnis).")]
public double TotalPnl { get; set; } = 0.0;
// --- Copy-Score (Phase 3): was WIR mit diesem Master verdient haben, letzte 30 Tage ---
[Category("05. Copy-Score (30 Tage)")]
[ReadOnly(true)]
[DisplayName("Copy-PnL (30T)")]
[Description("Summierter realisierter Gewinn/Verlust UNSERER kopierten Trades dieses Masters der letzten 30 Tage (inkl. unserer Slippage/Fees). Primäre Rentabilitätskennzahl aussagekräftiger als die Master-eigene Winrate.")]
public decimal CopyPnl30d { get; set; }
[Category("05. Copy-Score (30 Tage)")]
[ReadOnly(true)]
[DisplayName("Profit-Faktor (30T)")]
[Description("Bruttogewinn / Bruttoverlust unserer Copy-Trades (30T). > 1 = profitabel, < 1 = Verlust. 999 = bisher kein Verlust. Robuster als die reine Winrate.")]
public decimal CopyProfitFactor { get; set; }
[Category("05. Copy-Score (30 Tage)")]
[ReadOnly(true)]
[DisplayName("Ø PnL/Trade (30T)")]
[Description("Durchschnittlicher Copy-PnL pro Trade (30T). Negativ = dieser Master kostet uns im Schnitt Geld.")]
public decimal CopyAvgPnlPerTrade { get; set; }
[Category("05. Copy-Score (30 Tage)")]
[ReadOnly(true)]
[DisplayName("Copy-Trades (30T)")]
[Description("Anzahl unserer geschlossenen Copy-Trades dieses Masters in den letzten 30 Tagen (Stichprobengröße für die obigen Kennzahlen).")]
public int CopyTradeCount30d { get; set; }
[Browsable(false)]
public HashSet<int> AssignedAccountIds { get; set; } = new();
}
@@ -57,6 +57,9 @@ namespace PolyTrader.Modules.CopyTrading.Persistence.Ef
e.Property(x => x.Category).HasMaxLength(64);
e.Property(x => x.Description).HasMaxLength(1000);
e.Property(x => x.Reasoning).HasMaxLength(1000);
e.Property(x => x.CopyPnl30d).HasPrecision(18, 6);
e.Property(x => x.CopyProfitFactor).HasPrecision(18, 6);
e.Property(x => x.CopyAvgPnlPerTrade).HasPrecision(18, 6);
var comparer = new ValueComparer<HashSet<int>>(
(a, c) => (a == null && c == null) || (a != null && c != null && a.SetEquals(c)),
@@ -0,0 +1,285 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using PolyTrader.Modules.CopyTrading.Persistence.Ef;
#nullable disable
namespace PolyTrader.Modules.CopyTrading.Persistence.Ef.Migrations
{
[DbContext(typeof(CopyTradingDbContext))]
[Migration("20260707160622_AddTraderCopyScore")]
partial class AddTraderCopyScore
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.13")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
modelBuilder.Entity("PolyTraderSharp.Models.ClosedTrade", b =>
{
b.Property<int>("TradeId")
.HasColumnType("int");
b.Property<int>("AccountId")
.HasColumnType("int");
b.Property<DateTime>("ClosedAt")
.HasColumnType("datetime(6)");
b.Property<decimal>("EntryPrice")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("ExitPrice")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<string>("ExitReason")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<bool>("IsDemo")
.HasColumnType("tinyint(1)");
b.Property<string>("MarketQuestion")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("varchar(1000)");
b.Property<string>("MarketSlug")
.IsRequired()
.HasMaxLength(300)
.HasColumnType("varchar(300)");
b.Property<DateTime>("OpenedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Outcome")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<decimal>("PnlPercent")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("RealizedPnl")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<string>("Side")
.IsRequired()
.HasMaxLength(10)
.HasColumnType("varchar(10)");
b.Property<decimal>("Size")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<int>("SourceTraderId")
.HasColumnType("int");
b.Property<string>("TokenId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("varchar(120)");
b.Property<decimal>("TotalFees")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.HasKey("TradeId");
b.HasIndex("AccountId");
b.HasIndex("SourceTraderId");
b.HasIndex("TokenId");
b.ToTable("mod_copytrading_closed_trades", (string)null);
});
modelBuilder.Entity("PolyTraderSharp.Models.CopyTradingAccountSettings", b =>
{
b.Property<int>("AccountId")
.HasColumnType("int");
b.Property<decimal>("MaxBuyPrice")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("MaxPriceDifference")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("MaxSpreadPct")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("MinSellRatioPct")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("PerMarketLimit")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("PerMasterLimit")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("PreRedeemLimit")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("ProfitTarget")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("SellFloorPct")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("perMaxTime24h")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("perMaxTime6h")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("perMaxTime72h")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("perMaxTimeNone")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.HasKey("AccountId");
b.ToTable("mod_copytrading_account_settings", (string)null);
});
modelBuilder.Entity("PolyTraderSharp.Models.MasterTraderHistoryRecord", b =>
{
b.Property<string>("Id")
.HasMaxLength(64)
.HasColumnType("varchar(64)");
b.Property<DateTime>("ClosedAt")
.HasColumnType("datetime(6)");
b.Property<decimal>("RealizedPnl")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<string>("TokenId")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("varchar(120)");
b.Property<int>("TraderId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("ClosedAt");
b.HasIndex("TraderId");
b.ToTable("mod_copytrading_mt_history", (string)null);
});
modelBuilder.Entity("PolyTraderSharp.Models.TrackedTrader", b =>
{
b.Property<int>("Id")
.HasColumnType("int");
b.Property<string>("AssignedAccountIds")
.IsRequired()
.HasColumnType("text");
b.Property<bool>("AutoPauseEnabled")
.HasColumnType("tinyint(1)");
b.Property<string>("Category")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("varchar(64)");
b.Property<decimal>("CopyAvgPnlPerTrade")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("CopyPnl30d")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("CopyProfitFactor")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<int>("CopyTradeCount30d")
.HasColumnType("int");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("varchar(1000)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("varchar(200)");
b.Property<bool>("IsActive")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsHidden")
.HasColumnType("tinyint(1)");
b.Property<bool>("MakerEntry")
.HasColumnType("tinyint(1)");
b.Property<string>("Reasoning")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("varchar(1000)");
b.Property<double>("TotalPnl")
.HasColumnType("double");
b.Property<int>("TotalTrades")
.HasColumnType("int");
b.Property<string>("WalletAddress")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<int>("WinningTrades")
.HasColumnType("int");
b.Property<double>("Winrate30t")
.HasColumnType("double");
b.HasKey("Id");
b.ToTable("mod_copytrading_traders", (string)null);
});
#pragma warning restore 612, 618
}
}
}
@@ -0,0 +1,68 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace PolyTrader.Modules.CopyTrading.Persistence.Ef.Migrations
{
/// <inheritdoc />
public partial class AddTraderCopyScore : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<decimal>(
name: "CopyAvgPnlPerTrade",
table: "mod_copytrading_traders",
type: "decimal(18,6)",
precision: 18,
scale: 6,
nullable: false,
defaultValue: 0m);
migrationBuilder.AddColumn<decimal>(
name: "CopyPnl30d",
table: "mod_copytrading_traders",
type: "decimal(18,6)",
precision: 18,
scale: 6,
nullable: false,
defaultValue: 0m);
migrationBuilder.AddColumn<decimal>(
name: "CopyProfitFactor",
table: "mod_copytrading_traders",
type: "decimal(18,6)",
precision: 18,
scale: 6,
nullable: false,
defaultValue: 0m);
migrationBuilder.AddColumn<int>(
name: "CopyTradeCount30d",
table: "mod_copytrading_traders",
type: "int",
nullable: false,
defaultValue: 0);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CopyAvgPnlPerTrade",
table: "mod_copytrading_traders");
migrationBuilder.DropColumn(
name: "CopyPnl30d",
table: "mod_copytrading_traders");
migrationBuilder.DropColumn(
name: "CopyProfitFactor",
table: "mod_copytrading_traders");
migrationBuilder.DropColumn(
name: "CopyTradeCount30d",
table: "mod_copytrading_traders");
}
}
}
@@ -216,6 +216,21 @@ namespace PolyTrader.Modules.CopyTrading.Persistence.Ef.Migrations
.HasMaxLength(64)
.HasColumnType("varchar(64)");
b.Property<decimal>("CopyAvgPnlPerTrade")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("CopyPnl30d")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<decimal>("CopyProfitFactor")
.HasPrecision(18, 6)
.HasColumnType("decimal(18,6)");
b.Property<int>("CopyTradeCount30d")
.HasColumnType("int");
b.Property<string>("Description")
.IsRequired()
.HasMaxLength(1000)
@@ -1,4 +1,5 @@
using System;
using PolyTrader.Modules.CopyTrading.Logic;
using PolyTrader.Modules.CopyTrading.Persistence;
using System.Collections.Generic;
using System.Linq;
@@ -17,16 +18,20 @@ namespace PolyTraderSharp.Services
private readonly TerminalLogger _logger;
private readonly IMasterTraderHistoryRepository _historyRepo;
private readonly ITrackedTraderRepository _traderRepo;
private readonly ICopyTradeLogRepository _tradeLog;
private readonly ThreemaService _threema;
private readonly JobStatusRow _jobStatus;
private readonly PolymarketApiService _api;
public MasterTraderAnalyticsJob(TradingState state, CopyTradingState copyState, TerminalLogger logger, IMasterTraderHistoryRepository historyRepo, ITrackedTraderRepository traderRepo, JobManager jobManager, PolymarketApiService api)
public MasterTraderAnalyticsJob(TradingState state, CopyTradingState copyState, TerminalLogger logger, IMasterTraderHistoryRepository historyRepo, ITrackedTraderRepository traderRepo, ICopyTradeLogRepository tradeLog, ThreemaService threema, JobManager jobManager, PolymarketApiService api)
{
_state = state;
_copyState = copyState;
_logger = logger;
_historyRepo = historyRepo;
_traderRepo = traderRepo;
_tradeLog = tradeLog;
_threema = threema;
_api = api;
_jobStatus = new JobStatusRow
@@ -187,11 +192,61 @@ namespace PolyTraderSharp.Services
}
_logger.Info("✅ Master-Trader Historien-Analyse abgeschlossen.");
// Phase 3: Copy-Score + Auto-Pause entkoppelt von der (flakigen) Master-History-API,
// da aus UNSEREN geschlossenen Trades berechnet.
await UpdateCopyScoresAndAutoPauseAsync();
}
catch (Exception ex)
{
_logger.Error($"MasterTraderAnalyticsJob Exception: {ex}");
}
}
/// <summary>
/// Phase 3.1/3.3: Berechnet je Master den Copy-Score (letzte 30 Tage aus unseren
/// geschlossenen Copy-Trades) und pausiert Master hart, die über genügend Trades ins Minus
/// laufen (nur wenn deren AutoPauseEnabled gesetzt ist). Reaktivierung bewusst nur manuell.
/// </summary>
private async Task UpdateCopyScoresAndAutoPauseAsync()
{
var since = DateTime.UtcNow.AddDays(-30);
foreach (var trader in _copyState.Traders.Values.ToList())
{
try
{
var pnls = _tradeLog
.Find(t => t.SourceTraderId == trader.Id && t.ClosedAt >= since && !t.IsDemo)
.Select(t => t.RealizedPnl);
var m = TraderScore.Compute(pnls);
trader.CopyPnl30d = m.CopyPnl;
trader.CopyProfitFactor = m.ProfitFactor;
trader.CopyAvgPnlPerTrade = m.AvgPnlPerTrade;
trader.CopyTradeCount30d = m.TradeCount;
bool pause = trader.IsActive && TraderScore.ShouldAutoPause(
trader.AutoPauseEnabled, m.TradeCount, m.CopyPnl,
_copyState.AutoPauseMinTrades, _copyState.AutoPauseDrawdownUsd);
if (pause)
{
trader.IsActive = false;
trader.Reasoning = $"[Auto-Pause {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC] Copy-PnL {m.CopyPnl:F2} USDC über {m.TradeCount} Trades (30T) unter Schwelle (-{_copyState.AutoPauseDrawdownUsd:F0}). Reaktivierung manuell.";
_logger.Warning($"🛑 [AUTO-PAUSE] Master '{trader.DisplayName}' deaktiviert. Copy-PnL {m.CopyPnl:F2} / {m.TradeCount} Trades. Reaktivierung nur manuell.");
try { await _threema.SendMessageAsync($"🛑 Auto-Pause: Master '{trader.DisplayName}' deaktiviert.\nCopy-PnL 30T: {m.CopyPnl:F2} USDC über {m.TradeCount} Trades.\nReaktivierung manuell."); }
catch (Exception ex) { _logger.Error($"Threema Auto-Pause-Benachrichtigung fehlgeschlagen: {ex.Message}"); }
}
_copyState.Traders[trader.Id] = trader; // Hot-Path-State synchron halten
_traderRepo.Update(trader);
}
catch (Exception ex)
{
_logger.Error($"Copy-Score/Auto-Pause für '{trader.DisplayName}' fehlgeschlagen: {ex.Message}");
}
}
}
}
}
@@ -32,6 +32,10 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.colTrades = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colWinrate = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colPnl = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colCopyPnl = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colCopyPf = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colCopyAvg = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.colCopyCount = new System.Windows.Forms.DataGridViewTextBoxColumn();
this.splitter = new System.Windows.Forms.Splitter();
this.rightPanel = new System.Windows.Forms.Panel();
this.pgDetail = new System.Windows.Forms.PropertyGrid();
@@ -107,7 +111,11 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.colActive,
this.colTrades,
this.colWinrate,
this.colPnl});
this.colPnl,
this.colCopyPnl,
this.colCopyPf,
this.colCopyAvg,
this.colCopyCount});
this.grid.Dock = System.Windows.Forms.DockStyle.Fill;
this.grid.Location = new System.Drawing.Point(0, 25);
this.grid.MultiSelect = false;
@@ -182,6 +190,38 @@ namespace PolyTrader.Modules.CopyTrading.Ui
this.colPnl.ReadOnly = true;
this.colPnl.Width = 100;
//
// colCopyPnl
//
this.colCopyPnl.DataPropertyName = "CopyPnl30d";
this.colCopyPnl.HeaderText = "Copy-PnL (30T)";
this.colCopyPnl.Name = "colCopyPnl";
this.colCopyPnl.ReadOnly = true;
this.colCopyPnl.Width = 110;
//
// colCopyPf
//
this.colCopyPf.DataPropertyName = "CopyProfitFactor";
this.colCopyPf.HeaderText = "Profit-Faktor";
this.colCopyPf.Name = "colCopyPf";
this.colCopyPf.ReadOnly = true;
this.colCopyPf.Width = 100;
//
// colCopyAvg
//
this.colCopyAvg.DataPropertyName = "CopyAvgPnlPerTrade";
this.colCopyAvg.HeaderText = "Ø PnL/Trade";
this.colCopyAvg.Name = "colCopyAvg";
this.colCopyAvg.ReadOnly = true;
this.colCopyAvg.Width = 100;
//
// colCopyCount
//
this.colCopyCount.DataPropertyName = "CopyTradeCount30d";
this.colCopyCount.HeaderText = "Copy-Trades";
this.colCopyCount.Name = "colCopyCount";
this.colCopyCount.ReadOnly = true;
this.colCopyCount.Width = 90;
//
// splitter
//
this.splitter.Dock = System.Windows.Forms.DockStyle.Right;
@@ -292,6 +332,10 @@ namespace PolyTrader.Modules.CopyTrading.Ui
private System.Windows.Forms.DataGridViewTextBoxColumn colTrades;
private System.Windows.Forms.DataGridViewTextBoxColumn colWinrate;
private System.Windows.Forms.DataGridViewTextBoxColumn colPnl;
private System.Windows.Forms.DataGridViewTextBoxColumn colCopyPnl;
private System.Windows.Forms.DataGridViewTextBoxColumn colCopyPf;
private System.Windows.Forms.DataGridViewTextBoxColumn colCopyAvg;
private System.Windows.Forms.DataGridViewTextBoxColumn colCopyCount;
private System.Windows.Forms.Splitter splitter;
private System.Windows.Forms.Panel rightPanel;
private System.Windows.Forms.PropertyGrid pgDetail;
@@ -31,6 +31,9 @@ namespace PolyTrader.Modules.CopyTrading.Ui
colWinrate.DefaultCellStyle.Format = "F1";
colPnl.DefaultCellStyle.Format = "F2";
colCopyPnl.DefaultCellStyle.Format = "F2";
colCopyPf.DefaultCellStyle.Format = "F2";
colCopyAvg.DefaultCellStyle.Format = "F2";
tsNew.Click += (_, _) => AddNew();
tsSave.Click += (_, _) => SaveCurrent();