Phase 0.4: Konsistenz-Fixes (Spam-Blockade, GlobalPnl, Demo-TokenId)
- SELL-Spam-Blockade greift nur noch bei kürzlichem SELL: PendingOrderTimestamps
bekommt eine Side ("BUY"/"SELL"); ein vorheriger BUY blockiert einen legitimen
schnellen SELL des Masters nicht mehr (alle Schreibstellen aktualisiert).
- GlobalPnl: PollClosedAccountsAsync addiert den realisierten PnL jetzt auch
(wie PollLiveAccountsAsync) -> Anzeige driftet nicht mehr.
- Demo-ClosedTrade erhält TokenId (der _processedClosures-Preload filtert darauf;
fehlte -> Duplikat-/Dedup-Risiko nach Neustart).
163 Tests gruen. Build/Smoke gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e22a6e3091
commit
b378bc3499
@@ -555,7 +555,7 @@ namespace PolyTraderSharp.Services
|
||||
|
||||
// Track order placement time for stale order cleanup
|
||||
string orderKey = $"{account.AccountId}_{signal.TokenId}";
|
||||
_copyState.PendingOrderTimestamps[orderKey] = (DateTime.UtcNow, signal.TraderId);
|
||||
_copyState.PendingOrderTimestamps[orderKey] = (DateTime.UtcNow, signal.TraderId, "BUY");
|
||||
|
||||
// Initialize master position tracking with signal size if not yet tracked
|
||||
// The background sync will update with the real value within 30 seconds
|
||||
@@ -570,9 +570,11 @@ namespace PolyTraderSharp.Services
|
||||
string orderKey = $"{account.AccountId}_{signal.TokenId}";
|
||||
if (_copyState.PendingOrderTimestamps.TryGetValue(orderKey, out var pendingInfo))
|
||||
{
|
||||
if ((DateTime.UtcNow - pendingInfo.PlacedAt).TotalSeconds < 20)
|
||||
// Nur ein KÜRZLICHER SELL blockiert (Doppel-SELL/Retry-Spam); ein vorheriger
|
||||
// BUY blockiert einen legitimen schnellen SELL des Masters NICHT mehr.
|
||||
if (pendingInfo.Side == "SELL" && (DateTime.UtcNow - pendingInfo.PlacedAt).TotalSeconds < 20)
|
||||
{
|
||||
return; // Spam-Blockade: Die Order wurde in den letzten 20 Sekunden bereits versendet
|
||||
return; // Spam-Blockade: SELL wurde in den letzten 20 Sekunden bereits versendet
|
||||
}
|
||||
}
|
||||
|
||||
@@ -633,6 +635,7 @@ namespace PolyTraderSharp.Services
|
||||
AccountId = account.AccountId,
|
||||
SourceTraderId = signal.TraderId,
|
||||
IsDemo = account.IsDemo,
|
||||
TokenId = signal.TokenId,
|
||||
MarketSlug = signal.MarketSlug,
|
||||
MarketQuestion = signal.MarketQuestion,
|
||||
Outcome = signal.Outcome,
|
||||
@@ -705,14 +708,14 @@ namespace PolyTraderSharp.Services
|
||||
Attempt = 1,
|
||||
LastActionAt = DateTime.UtcNow
|
||||
};
|
||||
_copyState.PendingOrderTimestamps[orderKey] = (DateTime.UtcNow, signal.TraderId);
|
||||
_copyState.PendingOrderTimestamps[orderKey] = (DateTime.UtcNow, signal.TraderId, "SELL");
|
||||
_logger.Trade($"✅ [LIVE SELL-LEITER platziert] {account.Name} | GTC-Limit {firstLimit:F3} für {openPos.Size:F2} Shares.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Startorder fehlgeschlagen: Position bleibt (ExitPending zurücksetzen), Cooldown.
|
||||
openPos.ExitPending = false;
|
||||
_copyState.PendingOrderTimestamps[orderKey] = (DateTime.UtcNow.AddSeconds(-15), signal.TraderId);
|
||||
_copyState.PendingOrderTimestamps[orderKey] = (DateTime.UtcNow.AddSeconds(-15), signal.TraderId, "SELL");
|
||||
_logger.TradeReasoning($"❌ [LIVE SELL-LEITER] Startorder fehlgeschlagen: {result}. Position bleibt im Portfolio; neuer Versuch beim nächsten Signal/Sync.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace PolyTraderSharp.Services
|
||||
ladder.CurrentLimit = next;
|
||||
ladder.Attempt++;
|
||||
ladder.LastActionAt = DateTime.UtcNow;
|
||||
_copyState.PendingOrderTimestamps[key] = (DateTime.UtcNow, ladder.SourceTraderId);
|
||||
_copyState.PendingOrderTimestamps[key] = (DateTime.UtcNow, ladder.SourceTraderId, "SELL");
|
||||
_logger.Trade($"✅ [SELL-LEITER Stufe {ladder.Attempt}] {account.Name} | neues GTC-Limit {next:F3} für {pos.Size:F2} Shares.");
|
||||
}
|
||||
else
|
||||
|
||||
@@ -639,9 +639,10 @@ namespace PolyTraderSharp.Services
|
||||
ExitReason = exitReason
|
||||
};
|
||||
|
||||
_state.GlobalPnl += realizedPnl; // Konsistenz: wie in PollLiveAccountsAsync (Anzeige driftete sonst)
|
||||
_processedClosures.TryAdd(duplicateKey, true);
|
||||
_closedTradeWriter.TryWrite(ctRecord);
|
||||
|
||||
|
||||
_logger.Info($"🏆 Trade {ctRecord.MarketQuestion} synchronisiert (Hintergrund)! PnL: ${realizedPnl:F2}");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user