Phase 0.1: SELL-Eskalationsleiter statt Market-Dump (CLOB-kritisch)
Behebt die April-Verlustquelle: SELLs wurden als Market-Order mit 0.01-Limit ins
oft leergeraeumte Orderbuch geworfen -> wir wurden zur Exit-Liquidity. Jetzt:
GTC-Limit nahe am Master-Exit, stufenweises Nachpreisen bis zum Floor.
- Position.ExitPending (runtime-only, EF-ignoriert): Position wird bei SELL NICHT
mehr optimistisch entfernt, sondern als ExitPending zurueckgestellt (kein
Doppel-SELL, Limits rechnen korrekt; Sync schliesst nach bestaetigtem Fill).
- CopyTradingState.ExitLadders + ExitLadderState (transienter Leiter-Zustand).
- SellLogic (pure, getestet): FirstLimit (HF-fest/prozentual), Floor (SellFloorPct),
NextPrice (relative Stufe, auf Floor geclamped), IsAtFloor, LadderStepPct (3%),
LadderIntervalSeconds (HF 20s / sonst 120s).
- SellLadderService (BackgroundService): senkt offene Exit-Limits stufenweise
(cancel via CancelConflictingOrdersAsync -> tiefer neu platzieren), am Floor ohne
Fill Position halten + Threema-Benachrichtigung. Fills erkennt der bestehende Sync.
- CopyTradingEngine SELL-Live-Pfad ruft die Leiter; Demo-Pfad unveraendert.
Doppel-SELL-Guard ueber ExitPending. Umfangreiches Logging (kein Live-Test moeglich).
163 Tests gruen. Build/Smoke gruen. Backup-Rollback: Commit 1dffc9e.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
1dffc9e253
commit
e22a6e3091
@@ -85,5 +85,33 @@ namespace PolyTrader.Tests
|
||||
Assert.True(IsAtFloor(0.79m, 0.80m));
|
||||
Assert.False(IsAtFloor(0.81m, 0.80m));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true, 20)] // HF-Trader: schnelles Nachpreisen
|
||||
[InlineData(false, 120)] // Standard
|
||||
public void LadderIntervalSeconds_hf_is_faster(bool isHf, int expected)
|
||||
{
|
||||
Assert.Equal(expected, LadderIntervalSeconds(isHf));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ladder_walks_down_in_steps_until_floor()
|
||||
{
|
||||
// Simuliert den Service: von Startlimit in 3%-Schritten bis zum Floor, dann Stopp.
|
||||
decimal reference = 0.99m;
|
||||
decimal price = FirstLimit(reference, isHf: false, maxPriceDifferencePct: 2m); // 0.9702
|
||||
decimal floor = Floor(reference, 15m); // 0.8415
|
||||
|
||||
int steps = 0;
|
||||
while (!IsAtFloor(price, floor) && steps < 100)
|
||||
{
|
||||
price = NextPrice(price, LadderStepPct, floor);
|
||||
steps++;
|
||||
}
|
||||
|
||||
Assert.True(IsAtFloor(price, floor));
|
||||
Assert.Equal(floor, price); // endet exakt auf dem Floor (geclamped)
|
||||
Assert.InRange(steps, 1, 20); // terminiert in wenigen Schritten
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user