Wer darf die ruhende Leiter-Order anfassen? Nur die Leiter selbst. - H1 (Race): StartLadderAsync macht ZUERST einen atomaren Claim (ExitLadders.TryAdd). Master-SELL (Engine) und Profit-Target (Sync) sind damit idempotent – kein Doppel-GTC / keine Fehlerkaskade. Verliert ein Aufrufer den Claim, wird die (evtl. von der Engine entfernte) Position als ExitPending zurueckgestellt – kein Waise. Order-Fehler gibt den Claim zurueck. - K1a (Floor-Deadlock): CleanupStaleOpenOrdersAsync ueberspringt Keys mit aktiver Leiter (ExitLadders.ContainsKey) – cancelt die Floor-Order nicht mehr. - K1b (Floor-Robustheit): ProcessLadderAsync prueft am Floor via GetOpenOrders, ob die SELL-Order noch ruht; falls nicht (Cleanup/extern/Teil-Fill), neu platzieren statt bis zum Neustart unverkaeuflich zu haengen. - H2 (Engine-Cancel): Pre-Signal-CancelConflictingOrders wird uebersprungen, wenn fuer den Markt eine Leiter aktiv ist – zerschiesst die Leiter-Order nicht. Tests: 8 neue Integrationstests (SellLadderService ueber gemockten IClobClient): atomarer/paralleler Claim, Waisen-Schutz, Claim-Rueckgabe, Floor-Neuplatzierung, Floor-Halten bei ruhender Order, Leiter-Ende bei gefuellter Position. Verifikation: Build 0 Fehler, 215 Tests gruen, --smoke-ui ok. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
26 lines
1.1 KiB
C#
26 lines
1.1 KiB
C#
using System.Collections.Generic;
|
|
using PolyTrader.Core.Persistence;
|
|
using PolyTraderSharp.Models;
|
|
|
|
namespace PolyTrader.Tests.Fakes
|
|
{
|
|
/// <summary>
|
|
/// Minimaler In-Memory-Stub für <see cref="IPositionRepository"/>. Die Leiter-Tests brauchen nur,
|
|
/// dass UpsertLive nicht crasht; die eigentliche Persistenz ist hier irrelevant.
|
|
/// </summary>
|
|
public sealed class FakePositionRepository : IPositionRepository
|
|
{
|
|
public List<(int AccountId, string TokenId)> UpsertedLive { get; } = new();
|
|
|
|
public List<Position> GetLive(int accountId) => new();
|
|
public List<Position> GetDemo(int accountId) => new();
|
|
public Position? FindLive(int accountId, string tokenId) => null;
|
|
public Position? FindDemo(int accountId, string tokenId) => null;
|
|
public void UpsertLive(int accountId, Position position) => UpsertedLive.Add((accountId, position.TokenId));
|
|
public void UpsertDemo(int accountId, Position position) { }
|
|
public void DeleteLive(int accountId, string tokenId) { }
|
|
public void DeleteDemo(int accountId, string tokenId) { }
|
|
public void DropDemo(int accountId) { }
|
|
}
|
|
}
|