Phase 5.2: TradingState-Split (Core-State vs. CopyTradingState)
- Core TradingState (in Core): globale Schalter, Accounts, MarketCache, GlobalPnl. - Neuer CopyTradingState (im Modul): Traders, MasterTraderPositions, TraderAnalyticsCache, TotalCopyTrades/GetNextTradeId, PendingOrderTimestamps, SixSharesMinimum. - 10 Konsumenten umgestellt (Program, frm_main, CopyTradingEngine, TraderMonitor, Alchemy, WSS, Snapshot, StartupHydration, beide Analytics-Jobs): Modul-Felder von _state.* auf _copyState.* umgeleitet, CopyTradingState via DI. - Rein mechanische Feld-Umleitung, keine Logikänderung. Build 0 Fehler. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
55050a19e5
commit
f8d395b2a3
@@ -16,6 +16,7 @@ namespace PolyTraderSharp.Services
|
||||
public class TraderMonitorService : BackgroundService
|
||||
{
|
||||
private readonly TradingState _state;
|
||||
private readonly CopyTradingState _copyState;
|
||||
private readonly PolymarketApiService _api;
|
||||
private readonly PolymarketClobClient _clob;
|
||||
private readonly ChannelWriter<CopySignal> _signalWriter;
|
||||
@@ -39,8 +40,9 @@ namespace PolyTraderSharp.Services
|
||||
private static readonly ConcurrentDictionary<string, (int Count, DateTime LastAttempt)> _restRedeemAttempts = new();
|
||||
|
||||
public TraderMonitorService(
|
||||
TradingState state,
|
||||
PolymarketApiService api,
|
||||
TradingState state,
|
||||
CopyTradingState copyState,
|
||||
PolymarketApiService api,
|
||||
PolymarketClobClient clob,
|
||||
ChannelWriter<CopySignal> signalWriter,
|
||||
ChannelWriter<ClosedTrade> closedTradeWriter,
|
||||
@@ -50,6 +52,7 @@ namespace PolyTraderSharp.Services
|
||||
IMongoDatabase? db = null)
|
||||
{
|
||||
_state = state;
|
||||
_copyState = copyState;
|
||||
_api = api;
|
||||
_clob = clob;
|
||||
_signalWriter = signalWriter;
|
||||
@@ -102,7 +105,7 @@ namespace PolyTraderSharp.Services
|
||||
_lastLivePoll = DateTime.UtcNow;
|
||||
await SyncMasterTraderPositionsAsync(stoppingToken);
|
||||
_lastMasterPositionPoll = DateTime.UtcNow;
|
||||
_logger.Info($"Startup: MasterTraderPositions warmup abgeschlossen ({_state.MasterTraderPositions.Count} Einträge).");
|
||||
_logger.Info($"Startup: MasterTraderPositions warmup abgeschlossen ({_copyState.MasterTraderPositions.Count} Einträge).");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -157,7 +160,7 @@ namespace PolyTraderSharp.Services
|
||||
return;
|
||||
}
|
||||
|
||||
var activeTraders = _state.Traders.Values.Where(t => t.IsActive).ToList();
|
||||
var activeTraders = _copyState.Traders.Values.Where(t => t.IsActive).ToList();
|
||||
if (activeTraders.Count == 0) return;
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
@@ -240,7 +243,7 @@ namespace PolyTraderSharp.Services
|
||||
|
||||
public void TriggerFastBlockchainPoll(string txHash, string rpcUrl, string walletAddress)
|
||||
{
|
||||
var trader = _state.Traders.Values.FirstOrDefault(t => t.WalletAddress.Equals(walletAddress, StringComparison.OrdinalIgnoreCase));
|
||||
var trader = _copyState.Traders.Values.FirstOrDefault(t => t.WalletAddress.Equals(walletAddress, StringComparison.OrdinalIgnoreCase));
|
||||
if (trader == null || !trader.IsActive) return;
|
||||
|
||||
if (_state.EnableBlockchainParser)
|
||||
@@ -391,7 +394,7 @@ namespace PolyTraderSharp.Services
|
||||
/// </summary>
|
||||
public void TriggerManualPoll(string walletAddress)
|
||||
{
|
||||
var trader = _state.Traders.Values.FirstOrDefault(t => t.WalletAddress.Equals(walletAddress, StringComparison.OrdinalIgnoreCase));
|
||||
var trader = _copyState.Traders.Values.FirstOrDefault(t => t.WalletAddress.Equals(walletAddress, StringComparison.OrdinalIgnoreCase));
|
||||
if (trader != null && trader.IsActive)
|
||||
{
|
||||
if (!_activeWssPolls.TryAdd(trader.WalletAddress, true))
|
||||
@@ -625,12 +628,12 @@ namespace PolyTraderSharp.Services
|
||||
if (size > 0) exitPrice = (investment + realizedPnl) / size;
|
||||
|
||||
string orderKey = $"{acc.AccountId}_{asset}";
|
||||
bool soldByUs = _state.PendingOrderTimestamps.ContainsKey(orderKey);
|
||||
bool soldByUs = _copyState.PendingOrderTimestamps.ContainsKey(orderKey);
|
||||
string exitReason = soldByUs ? "Master Trader Sold" : "Manuell Geschlossen / System";
|
||||
|
||||
var ctRecord = new ClosedTrade
|
||||
{
|
||||
TradeId = _state.GetNextTradeId(),
|
||||
TradeId = _copyState.GetNextTradeId(),
|
||||
AccountId = acc.AccountId,
|
||||
SourceTraderId = resolvedSourceId,
|
||||
IsDemo = false,
|
||||
@@ -744,10 +747,10 @@ namespace PolyTraderSharp.Services
|
||||
|
||||
// 1. Prüfe PendingOrderTimestamps (CopyTradingEngine hat diese Order kürzlich platziert)
|
||||
string orderKey = $"{acc.AccountId}_{asset}";
|
||||
if (_state.PendingOrderTimestamps.TryGetValue(orderKey, out var pending) && pending.SourceTraderId > 0)
|
||||
if (_copyState.PendingOrderTimestamps.TryGetValue(orderKey, out var pending) && pending.SourceTraderId > 0)
|
||||
{
|
||||
resolvedTraderId = pending.SourceTraderId;
|
||||
if (_state.Traders.TryGetValue(resolvedTraderId, out var pendingTrader))
|
||||
if (_copyState.Traders.TryGetValue(resolvedTraderId, out var pendingTrader))
|
||||
{
|
||||
resolvedTraderName = pendingTrader.DisplayName;
|
||||
resolvedTraderAddress = pendingTrader.WalletAddress;
|
||||
@@ -920,12 +923,12 @@ namespace PolyTraderSharp.Services
|
||||
_logger.Info($"🏆 Live Market {removedPos.MarketQuestion} geschlossen! PnL: ${(realizedPnl):F2}");
|
||||
|
||||
string orderKey = $"{acc.AccountId}_{removedPos.TokenId}";
|
||||
bool soldByUs = _state.PendingOrderTimestamps.ContainsKey(orderKey);
|
||||
bool soldByUs = _copyState.PendingOrderTimestamps.ContainsKey(orderKey);
|
||||
string exitReason = soldByUs ? "Master Trader Sold" : "Market Resolved";
|
||||
|
||||
var ctRecord = new ClosedTrade
|
||||
{
|
||||
TradeId = _state.GetNextTradeId(),
|
||||
TradeId = _copyState.GetNextTradeId(),
|
||||
AccountId = acc.AccountId,
|
||||
SourceTraderId = removedPos.SourceTraderId,
|
||||
IsDemo = false,
|
||||
@@ -943,7 +946,7 @@ namespace PolyTraderSharp.Services
|
||||
ExitReason = exitReason
|
||||
};
|
||||
|
||||
if (soldByUs) _state.PendingOrderTimestamps.TryRemove(orderKey, out _);
|
||||
if (soldByUs) _copyState.PendingOrderTimestamps.TryRemove(orderKey, out _);
|
||||
|
||||
_processedClosures.TryAdd(duplicateKey, true);
|
||||
_closedTradeWriter.TryWrite(ctRecord);
|
||||
@@ -970,12 +973,12 @@ namespace PolyTraderSharp.Services
|
||||
_logger.Info($"🏆 Live Market {removedPos.MarketQuestion} aufgelöst (Fallback)! Auszahlung: ${(exitPrice * removedPos.Size):F2}");
|
||||
|
||||
string orderKey = $"{acc.AccountId}_{removedPos.TokenId}";
|
||||
bool soldByUs = _state.PendingOrderTimestamps.ContainsKey(orderKey);
|
||||
bool soldByUs = _copyState.PendingOrderTimestamps.ContainsKey(orderKey);
|
||||
string exitReason = soldByUs ? "Master Trader Sold" : "Market Resolved";
|
||||
|
||||
var ctRecord = new ClosedTrade
|
||||
{
|
||||
TradeId = _state.GetNextTradeId(),
|
||||
TradeId = _copyState.GetNextTradeId(),
|
||||
AccountId = acc.AccountId,
|
||||
SourceTraderId = removedPos.SourceTraderId,
|
||||
IsDemo = false,
|
||||
@@ -993,7 +996,7 @@ namespace PolyTraderSharp.Services
|
||||
ExitReason = exitReason
|
||||
};
|
||||
|
||||
if (soldByUs) _state.PendingOrderTimestamps.TryRemove(orderKey, out _);
|
||||
if (soldByUs) _copyState.PendingOrderTimestamps.TryRemove(orderKey, out _);
|
||||
|
||||
_processedClosures.TryAdd(duplicateKey, true);
|
||||
_closedTradeWriter.TryWrite(ctRecord);
|
||||
@@ -1079,7 +1082,7 @@ namespace PolyTraderSharp.Services
|
||||
{
|
||||
if (ct.IsCancellationRequested) break;
|
||||
|
||||
if (!_state.Traders.TryGetValue(traderId, out var trader) || string.IsNullOrEmpty(trader.WalletAddress))
|
||||
if (!_copyState.Traders.TryGetValue(traderId, out var trader) || string.IsNullOrEmpty(trader.WalletAddress))
|
||||
continue;
|
||||
|
||||
var positionSizes = await _api.GetTraderPositionSizesAsync(trader.WalletAddress, tokenIds);
|
||||
@@ -1089,7 +1092,7 @@ namespace PolyTraderSharp.Services
|
||||
{
|
||||
string key = $"{traderId}_{tokenId}";
|
||||
decimal shares = positionSizes.ContainsKey(tokenId) ? positionSizes[tokenId] : 0m;
|
||||
_state.MasterTraderPositions[key] = (shares, DateTime.UtcNow);
|
||||
_copyState.MasterTraderPositions[key] = (shares, DateTime.UtcNow);
|
||||
}
|
||||
|
||||
await Task.Delay(200, ct); // Brief delay between traders to avoid rate limits
|
||||
@@ -1104,7 +1107,7 @@ namespace PolyTraderSharp.Services
|
||||
|
||||
private async Task CleanupStaleOpenOrdersAsync(CancellationToken ct)
|
||||
{
|
||||
var keysToProcess = _state.PendingOrderTimestamps.ToArray();
|
||||
var keysToProcess = _copyState.PendingOrderTimestamps.ToArray();
|
||||
if (keysToProcess.Length == 0) return;
|
||||
|
||||
foreach (var kvp in keysToProcess)
|
||||
@@ -1119,7 +1122,7 @@ namespace PolyTraderSharp.Services
|
||||
|
||||
// Determine timeout based on trader category
|
||||
int timeoutMinutes = 30; // Default: 30 min
|
||||
if (_state.Traders.TryGetValue(kvp.Value.SourceTraderId, out var trader) && trader.Category == "HF")
|
||||
if (_copyState.Traders.TryGetValue(kvp.Value.SourceTraderId, out var trader) && trader.Category == "HF")
|
||||
{
|
||||
timeoutMinutes = 3; // HF Trader: 3 min
|
||||
}
|
||||
@@ -1146,7 +1149,7 @@ namespace PolyTraderSharp.Services
|
||||
}
|
||||
|
||||
// Remove from tracking regardless (even if cancel failed, we don't want to spam retries)
|
||||
_state.PendingOrderTimestamps.TryRemove(kvp.Key, out _);
|
||||
_copyState.PendingOrderTimestamps.TryRemove(kvp.Key, out _);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user