Testnetz: Micro-Order-Minimum + SELL-Proportionalität extrahiert; SELL-Leiter (Phase 0.1) vorbereitet

Weiter im Muster extrahieren->testen (verhaltensneutral, clob.md):
- CopyTradingRisk.IsBelowPolymarketMinimum (Shares < 5.5 || USDC < 0.10) –
  BUY-Micro-Order-Filter verdrahtet + Tests.
- SellLogic.SellProportion (signalSize/(remaining+signalSize), Denominator<=0 -> 0)
  + Konstante MinSignificantSellRatio (0.30); SELL-Proportionalitätscheck der
  Engine ruft sie jetzt (Charakterisierung, sichert Phase-2-Umbau ab).
- SellLogic-Eskalationsleiter (Phase 0.1, NOCH NICHT verdrahtet): FirstLimit
  (HF-fest/prozentual), Floor (SellFloorPct), NextPrice (relative Stufe, auf Floor
  geclamped), IsAtFloor. Reine Grundlage fuer das spaetere SELL-Wiring.
- Tests: CopyTradingRisk-Minimum (5 Faelle) + SellLogicTests (Proportion +
  Leiter). Gesamt 154 gruen.

Build/Smoke gruen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-07 11:03:44 +02:00
co-authored by Claude Opus 4.8
parent 6a8ecbc80a
commit 581755029a
5 changed files with 175 additions and 3 deletions
@@ -18,6 +18,20 @@ namespace PolyTrader.Modules.CopyTrading.Logic
/// <summary>Fester Limit-Aufschlag für HF-Trader (0,5 ¢).</summary>
public const decimal HfLimitOffset = 0.005m;
/// <summary>Polymarket-Mindestgröße: wir filtern schon knapp über 5 Shares, um API-Fehler
/// ("Size lower than the minimum: 5") zu vermeiden.</summary>
public const decimal MinShares = 5.5m;
/// <summary>Absolute USDC-Untergrenze pro Order.</summary>
public const decimal MinUsdc = 0.10m;
/// <summary>
/// Liegt eine berechnete Order unter dem Polymarket-Minimum (Shares ODER USDC)?
/// Solche Mikro-Orders werden vor dem API-Call verworfen.
/// </summary>
public static bool IsBelowPolymarketMinimum(decimal shares, decimal usdc) =>
shares < MinShares || usdc < MinUsdc;
/// <summary>
/// Limit-Preis einer BUY-Order: HF-Trader bekommen einen festen Aufschlag von 0.005,
/// sonst einen prozentualen Aufschlag (<paramref name="maxPriceDifferencePct"/>) über dem
@@ -0,0 +1,55 @@
using System;
namespace PolyTrader.Modules.CopyTrading.Logic
{
/// <summary>
/// Reine SELL-Entscheidungs-/Preislogik. Teil davon (Proportionalität) ist 1:1 aus der
/// aktuellen Engine übernommen (Charakterisierung, damit der Phase-2-Umbau abgesichert ist);
/// die Eskalationsleiter ist die Grundlage für Phase 0.1 (noch NICHT in der Engine verdrahtet).
/// </summary>
public static class SellLogic
{
// ----- Proportionalität (aktuelles Verhalten, Schwelle hart 30 %) -----
/// <summary>Ab dieser Verkaufsquote des Masters kopieren wir den SELL (aktuell hart 30 %).</summary>
public const decimal MinSignificantSellRatio = 0.30m;
/// <summary>
/// Anteil, den dieser SELL am Master-Bestand VOR dem Verkauf ausmacht:
/// <c>signalSize / (masterSharesRemaining + signalSize)</c>. Denominator ≤ 0 → 0.
/// </summary>
public static decimal SellProportion(decimal masterSharesRemaining, decimal signalSize)
{
decimal total = masterSharesRemaining + signalSize;
if (total <= 0m) return 0m;
return signalSize / total;
}
// ----- Eskalationsleiter (Phase 0.1, noch nicht verdrahtet) -----
/// <summary>
/// Erster SELL-Limit-Preis (GTD): HF-Trader fester Abschlag von 0.005 unter dem Master-Exit,
/// sonst prozentualer Abschlag (<paramref name="maxPriceDifferencePct"/>).
/// </summary>
public static decimal FirstLimit(decimal referencePrice, bool isHf, decimal maxPriceDifferencePct) =>
isHf ? referencePrice - CopyTradingRisk.HfLimitOffset
: referencePrice * (1.0m - maxPriceDifferencePct / 100.0m);
/// <summary>
/// Absolute Untergrenze der Leiter: <c>referencePrice × (1 SellFloorPct/100)</c>.
/// Darunter wird nicht mehr verkauft (Position halten statt Exit-Liquidity zu werden).
/// </summary>
public static decimal Floor(decimal referencePrice, decimal sellFloorPct) =>
referencePrice * (1.0m - sellFloorPct / 100.0m);
/// <summary>
/// Nächste Stufe nach ausbleibendem Fill: aktuellen Preis um <paramref name="stepPct"/> %
/// relativ senken, aber nie unter den <paramref name="floor"/>.
/// </summary>
public static decimal NextPrice(decimal currentPrice, decimal stepPct, decimal floor) =>
Math.Max(currentPrice * (1.0m - stepPct / 100.0m), floor);
/// <summary>Leiter erschöpft: aktueller Preis hat den Floor erreicht/unterschritten.</summary>
public static bool IsAtFloor(decimal currentPrice, decimal floor) => currentPrice <= floor;
}
}
@@ -383,7 +383,7 @@ namespace PolyTraderSharp.Services
// ===== MICRO-ORDER FILTER: Polymarket Minimum Size Enforcement =====
// Polymarket lehnt Orders mit < 5 Shares ab ("Size lower than the minimum: 5").
// Statt die API zu belasten und Fehler-Logs zu erzeugen, filtern wir hier sofort.
if (exact.shares < 5.5m || exact.usdc < 0.10m)
if (CopyTradingRisk.IsBelowPolymarketMinimum(exact.shares, exact.usdc))
{
_logger.TradeReasoning($"❌ Trade BUY [{signal.MarketQuestion}] [{shareType}] gestoppt:\n" +
$" Konto: {account.Name}\n" +
@@ -431,8 +431,9 @@ namespace PolyTraderSharp.Services
if (masterShares > 0 && signal.Size > 0)
{
// Calculate what percentage of the master's known position this SELL represents
decimal sellRatio = signal.Size / (masterShares + signal.Size); // +signal.Size because the position was already reduced
if (sellRatio < 0.30m)
// (Proportionalität: SellLogic, unit-getestet)
decimal sellRatio = SellLogic.SellProportion(masterShares, signal.Size);
if (sellRatio < SellLogic.MinSignificantSellRatio)
{
_logger.TradeReasoning($"📊 Trade SELL [{signal.MarketQuestion}] [{shareType}] ignoriert:\n" +
$" Konto: {account.Name}\n" +