Phase 3d (2/2): CopyTradingEngine auf Repositories umgestellt

- MarketCache-Preload + Market-Hydration -> IMarketRepository
  (GetActive/FindByTokenId/Upsert).
- Demo-/Live-Positionen -> IPositionRepository (UpsertDemo/UpsertLive/DeleteDemo).
- Account-Persistenz -> IAccountRepository.
- _db (IMongoDatabase) vollständig aus CopyTradingEngine entfernt.
- Verhalten unverändert; Build 0 Fehler.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
bergm
2026-07-01 16:42:18 +02:00
co-authored by Claude Opus 4.8
parent a0e367e645
commit 0c6fc6a2af
+21 -22
View File
@@ -1,5 +1,6 @@
using System; using System;
using MongoDB.Driver; using MongoDB.Driver;
using PolyTrader.Core.Persistence;
using PolyTraderSharp.Extensions; using PolyTraderSharp.Extensions;
using System.Threading; using System.Threading;
using System.Threading.Channels; using System.Threading.Channels;
@@ -20,7 +21,9 @@ namespace PolyTraderSharp.Services
private readonly TerminalLogger _logger; private readonly TerminalLogger _logger;
private readonly PolymarketClobClient _clob; private readonly PolymarketClobClient _clob;
private readonly PolymarketApiService _api; private readonly PolymarketApiService _api;
private readonly IMongoDatabase? _db; private readonly IPositionRepository _positionRepo;
private readonly IMarketRepository _marketRepo;
private readonly IAccountRepository _accountRepo;
private readonly ConcurrentDictionary<int, SemaphoreSlim> _accountSemaphores = new(); private readonly ConcurrentDictionary<int, SemaphoreSlim> _accountSemaphores = new();
private readonly ConcurrentDictionary<int, DateTime> _lastInactiveLogPerTrader = new(); private readonly ConcurrentDictionary<int, DateTime> _lastInactiveLogPerTrader = new();
@@ -31,7 +34,9 @@ namespace PolyTraderSharp.Services
TerminalLogger logger, TerminalLogger logger,
PolymarketClobClient clob, PolymarketClobClient clob,
PolymarketApiService api, PolymarketApiService api,
IMongoDatabase? db = null) IPositionRepository positionRepo,
IMarketRepository marketRepo,
IAccountRepository accountRepo)
{ {
_state = state; _state = state;
_signalReader = signalReader; _signalReader = signalReader;
@@ -39,18 +44,17 @@ namespace PolyTraderSharp.Services
_logger = logger; _logger = logger;
_clob = clob; _clob = clob;
_api = api; _api = api;
_db = db; _positionRepo = positionRepo;
_marketRepo = marketRepo;
_accountRepo = accountRepo;
} }
public override async Task StartAsync(CancellationToken cancellationToken) public override async Task StartAsync(CancellationToken cancellationToken)
{ {
_logger.Info("Starte Preload des MarketCache aus MongoDB um Flaschenhälse zu vermeiden..."); _logger.Info("Starte Preload des MarketCache aus MongoDB um Flaschenhälse zu vermeiden...");
if (_db != null)
{ {
var coll = _db.GetCollection<MarketData>("markets");
// Initialize cache for EVERYTHING in DB that is not closed! // Initialize cache for EVERYTHING in DB that is not closed!
var activeMarkets = coll.LiteFind(x => !x.Closed); var activeMarkets = _marketRepo.GetActive();
int loaded = 0; int loaded = 0;
foreach (var md in activeMarkets) foreach (var md in activeMarkets)
@@ -126,24 +130,23 @@ namespace PolyTraderSharp.Services
if (cachedData.EndDate.HasValue) signal.EndDate = cachedData.EndDate; if (cachedData.EndDate.HasValue) signal.EndDate = cachedData.EndDate;
isNegRisk = cachedData.NegRisk; isNegRisk = cachedData.NegRisk;
} }
else if (_db != null) else
{ {
try try
{ {
var marketColl = _db.GetCollection<MarketData>("markets"); var marketData = _marketRepo.FindByTokenId(signal.TokenId);
var marketData = marketColl.LiteFind(x => x.ClobTokenIds != null && x.ClobTokenIds.Contains(signal.TokenId)).FirstOrDefault();
if (marketData == null) if (marketData == null)
{ {
var fetchedMarket = await _api.GetMarketByTokenIdAsync(signal.TokenId); var fetchedMarket = await _api.GetMarketByTokenIdAsync(signal.TokenId);
if (fetchedMarket != null) { marketColl.Upsert(fetchedMarket); marketData = fetchedMarket; } if (fetchedMarket != null) { _marketRepo.Upsert(fetchedMarket); marketData = fetchedMarket; }
} }
if (marketData == null && !string.IsNullOrEmpty(signal.MarketSlug) && !signal.MarketSlug.StartsWith("0x")) if (marketData == null && !string.IsNullOrEmpty(signal.MarketSlug) && !signal.MarketSlug.StartsWith("0x"))
{ {
var fetchedMarkets = await _api.GetMarketsByEventSlugAsync(signal.MarketSlug); var fetchedMarkets = await _api.GetMarketsByEventSlugAsync(signal.MarketSlug);
foreach (var fetched in fetchedMarkets) { foreach (var fetched in fetchedMarkets) {
marketColl.Upsert(fetched); _marketRepo.Upsert(fetched);
if (fetched.ClobTokenIds != null && fetched.ClobTokenIds.Contains(signal.TokenId)) marketData = fetched; if (fetched.ClobTokenIds != null && fetched.ClobTokenIds.Contains(signal.TokenId)) marketData = fetched;
} }
} }
@@ -531,10 +534,10 @@ namespace PolyTraderSharp.Services
return old; return old;
}); });
if (_db != null) _db.GetCollection<Position>($"demo_positions_{account.AccountId}").Upsert(finalPos); _positionRepo.UpsertDemo(account.AccountId, finalPos);
account.UpdateBalance(account.AvailableBalance - exactUsdc); account.UpdateBalance(account.AvailableBalance - exactUsdc);
if (_db != null) _db.GetCollection<AccountState>("accounts").Upsert(account); _accountRepo.Upsert(account);
_logger.Trade($"✅ [DEMO AUSGEFÜHRT]\n" + _logger.Trade($"✅ [DEMO AUSGEFÜHRT]\n" +
$" Konto: {account.Name}\n" + $" Konto: {account.Name}\n" +
$" Markt: {signal.MarketQuestion}\n" + $" Markt: {signal.MarketQuestion}\n" +
@@ -576,15 +579,11 @@ namespace PolyTraderSharp.Services
}); });
account.UpdateBalance(account.AvailableBalance - exactUsdc); account.UpdateBalance(account.AvailableBalance - exactUsdc);
if (_db != null) _db.GetCollection<AccountState>("accounts").Upsert(account); _accountRepo.Upsert(account);
if (_db != null)
{
var liveCol = _db.GetCollection<Position>($"open_positions_{account.AccountId}");
if (account.OpenPositions.TryGetValue(signal.TokenId, out var savedPos)) if (account.OpenPositions.TryGetValue(signal.TokenId, out var savedPos))
{ {
liveCol.Upsert(savedPos); _positionRepo.UpsertLive(account.AccountId, savedPos);
}
} }
// Track order placement time for stale order cleanup // Track order placement time for stale order cleanup
@@ -644,14 +643,14 @@ namespace PolyTraderSharp.Services
{ {
if (account.IsDemo) if (account.IsDemo)
{ {
if (_db != null) _db.GetCollection<Position>($"demo_positions_{account.AccountId}").Delete(signal.TokenId); _positionRepo.DeleteDemo(account.AccountId, signal.TokenId);
decimal exitUsd = openPos.Size * signal.Price; decimal exitUsd = openPos.Size * signal.Price;
decimal realizedPnl = exitUsd - openPos.AmountUsd; decimal realizedPnl = exitUsd - openPos.AmountUsd;
_state.GlobalPnl += realizedPnl; _state.GlobalPnl += realizedPnl;
account.UpdateBalance(account.AvailableBalance + exitUsd); account.UpdateBalance(account.AvailableBalance + exitUsd);
if (_db != null) _db.GetCollection<AccountState>("accounts").Upsert(account); _accountRepo.Upsert(account);
var ct = new ClosedTrade var ct = new ClosedTrade
{ {