Umsetzung des Fable-Plans, Start mit dem risikoarmen Settings-Fundament (kein Hot-Path/CLOB-Verhalten geändert): - Alle Copytrading-Settings (alt + neu) haben jetzt [Description]/[DisplayName] -> im PropertyGrid als Erklärung sichtbar (Richards Vorgabe). Test erzwingt, dass jede einstellbare Option eine Beschreibung trägt. - ProfitTarget-Default -> 9999 (praktisch deaktiviert); Migration setzt zusätzlich bestehende Zeilen per SQL auf 9999 (Entscheidung 2). Take-Profit-LOGIK folgt. - Neue Account-Settings (noch nicht verdrahtet, für Phase 0.1/1.3/2): SellFloorPct (15), MaxSpreadPct (5), MinSellRatioPct (10). - Neue Master-Trader-Settings: AutoPauseEnabled (Default AN, per Master abschaltbar, Entscheidung 3) + MakerEntry (Default aus, Entscheidung 4). - EF-Migration AddCopytradingBehaviorSettings (neue Spalten + sinnvolle Defaults fuer Bestandszeilen) erstellt und auf MySQL angewendet. - Erklärung dokumentiert einen gefundenen Skalen-Verdacht: PreRedeemLimit wird gegen den 0-1-Preis verglichen; Alt-Werte 99.5 triggern nie (-> ggf. 0.995). Tests: 86 gruen. Build/Smoke gruen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
107 lines
5.1 KiB
C#
107 lines
5.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
using PolyTraderSharp.Models;
|
|
|
|
namespace PolyTrader.Modules.CopyTrading.Persistence.Ef
|
|
{
|
|
/// <summary>
|
|
/// EF-Core-Kontext für die Entitäten des Copytrading-Moduls (gleiche MySQL-DB wie der
|
|
/// Core, eigene Tabellen mit Präfix mod_copytrading_).
|
|
/// </summary>
|
|
public class CopyTradingDbContext : DbContext
|
|
{
|
|
public CopyTradingDbContext(DbContextOptions<CopyTradingDbContext> options) : base(options) { }
|
|
|
|
public DbSet<ClosedTrade> ClosedTrades => Set<ClosedTrade>();
|
|
public DbSet<TrackedTrader> Traders => Set<TrackedTrader>();
|
|
public DbSet<CopyTradingAccountSettings> AccountSettings => Set<CopyTradingAccountSettings>();
|
|
public DbSet<MasterTraderHistoryRecord> History => Set<MasterTraderHistoryRecord>();
|
|
|
|
protected override void OnModelCreating(ModelBuilder b)
|
|
{
|
|
b.Ignore<ClosedTradeRow>(); // reine UI-Anzeige-Klasse
|
|
|
|
b.Entity<ClosedTrade>(e =>
|
|
{
|
|
e.ToTable("mod_copytrading_closed_trades");
|
|
e.HasKey(x => x.TradeId);
|
|
e.Property(x => x.TradeId).ValueGeneratedNever();
|
|
e.Property(x => x.TokenId).HasMaxLength(120);
|
|
e.Property(x => x.MarketSlug).HasMaxLength(300);
|
|
e.Property(x => x.MarketQuestion).HasMaxLength(1000);
|
|
e.Property(x => x.Outcome).HasMaxLength(200);
|
|
e.Property(x => x.Side).HasMaxLength(10);
|
|
e.Property(x => x.ExitReason).HasMaxLength(200);
|
|
e.Property(x => x.EntryPrice).HasPrecision(18, 6);
|
|
e.Property(x => x.ExitPrice).HasPrecision(18, 6);
|
|
e.Property(x => x.Size).HasPrecision(18, 6);
|
|
e.Property(x => x.RealizedPnl).HasPrecision(18, 6);
|
|
e.Property(x => x.PnlPercent).HasPrecision(18, 6);
|
|
e.Property(x => x.TotalFees).HasPrecision(18, 6);
|
|
e.HasIndex(x => x.AccountId);
|
|
e.HasIndex(x => x.TokenId);
|
|
e.HasIndex(x => x.SourceTraderId);
|
|
});
|
|
|
|
b.Entity<TrackedTrader>(e =>
|
|
{
|
|
e.ToTable("mod_copytrading_traders");
|
|
e.HasKey(x => x.Id);
|
|
e.Property(x => x.Id).ValueGeneratedNever();
|
|
e.Property(x => x.WalletAddress).HasMaxLength(128);
|
|
e.Property(x => x.DisplayName).HasMaxLength(200);
|
|
e.Property(x => x.Category).HasMaxLength(64);
|
|
e.Property(x => x.Description).HasMaxLength(1000);
|
|
e.Property(x => x.Reasoning).HasMaxLength(1000);
|
|
|
|
var comparer = new ValueComparer<HashSet<int>>(
|
|
(a, c) => (a == null && c == null) || (a != null && c != null && a.SetEquals(c)),
|
|
v => v.Aggregate(0, (h, i) => HashCode.Combine(h, i)),
|
|
v => new HashSet<int>(v));
|
|
|
|
e.Property(x => x.AssignedAccountIds)
|
|
.HasConversion(
|
|
v => JsonSerializer.Serialize(v, (JsonSerializerOptions?)null),
|
|
v => string.IsNullOrEmpty(v) ? new HashSet<int>() : (JsonSerializer.Deserialize<HashSet<int>>(v, (JsonSerializerOptions?)null) ?? new HashSet<int>()))
|
|
.HasColumnType("text");
|
|
e.Property(x => x.AssignedAccountIds).Metadata.SetValueComparer(comparer);
|
|
});
|
|
|
|
b.Entity<CopyTradingAccountSettings>(e =>
|
|
{
|
|
e.ToTable("mod_copytrading_account_settings");
|
|
e.HasKey(x => x.AccountId);
|
|
e.Property(x => x.AccountId).ValueGeneratedNever();
|
|
e.Property(x => x.PerMarketLimit).HasPrecision(18, 6);
|
|
e.Property(x => x.MaxPriceDifference).HasPrecision(18, 6);
|
|
e.Property(x => x.MaxBuyPrice).HasPrecision(18, 6);
|
|
e.Property(x => x.ProfitTarget).HasPrecision(18, 6);
|
|
e.Property(x => x.PreRedeemLimit).HasPrecision(18, 6);
|
|
e.Property(x => x.PerMasterLimit).HasPrecision(18, 6);
|
|
e.Property(x => x.perMaxTime6h).HasPrecision(18, 6);
|
|
e.Property(x => x.perMaxTime24h).HasPrecision(18, 6);
|
|
e.Property(x => x.perMaxTime72h).HasPrecision(18, 6);
|
|
e.Property(x => x.perMaxTimeNone).HasPrecision(18, 6);
|
|
e.Property(x => x.SellFloorPct).HasPrecision(18, 6);
|
|
e.Property(x => x.MaxSpreadPct).HasPrecision(18, 6);
|
|
e.Property(x => x.MinSellRatioPct).HasPrecision(18, 6);
|
|
});
|
|
|
|
b.Entity<MasterTraderHistoryRecord>(e =>
|
|
{
|
|
e.ToTable("mod_copytrading_mt_history");
|
|
e.HasKey(x => x.Id);
|
|
e.Property(x => x.Id).HasMaxLength(64);
|
|
e.Property(x => x.TokenId).HasMaxLength(120);
|
|
e.Property(x => x.RealizedPnl).HasPrecision(18, 6);
|
|
e.HasIndex(x => x.TraderId);
|
|
e.HasIndex(x => x.ClosedAt);
|
|
});
|
|
}
|
|
}
|
|
}
|