RF-Slice 0: FeeModel nach Core (geteilte Markt-Infrastruktur)

Vorbereitung fuer das ResolutionFarming-Modul: FeeModel von
PolyTrader.Modules.CopyTrading.Logic nach PolyTrader.Core.Trading verschoben, damit
mehrere Strategiemodule dieselbe Fee-Logik nutzen koennen (Module duerfen nicht
voneinander abhaengen -> geteiltes gehoert in den Core). CopyTrading-Referenzen +
FeeModelTests auf den Core-Namespace umgestellt. Rein mechanisch, verhaltensneutral.

Build 0 Fehler, 244 Tests gruen, --smoke-ui ok.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-09 21:35:11 +02:00
co-authored by Claude Opus 4.8
parent c091e79869
commit 2f5270a2f3
7 changed files with 13 additions and 7 deletions
@@ -1,4 +1,5 @@
using System;
using PolyTrader.Core.Trading;
namespace PolyTrader.Modules.CopyTrading.Logic
{
@@ -1,49 +0,0 @@
namespace PolyTrader.Modules.CopyTrading.Logic
{
/// <summary>
/// Reine Fee-Logik (Phase 0.2). Polymarket erhebt seit März 2026 Taker-Fees (Maker zahlen 0).
/// Der reale Satz kommt idealerweise vom Markt-Objekt der CLOB-/Gamma-API (Feldname im Zielland
/// anhand https://docs.polymarket.com/trading/fees verifizieren); bis dahin greift die
/// Kategorie-Fallback-Tabelle. Werte in Basispunkten (bps): 100 bps = 1,0 %.
/// </summary>
public static class FeeModel
{
public const int MakerBps = 0;
// Fallback-Sätze je Kategorie (Taker, bps) Näherung laut Plan, im Zielland verifizieren.
public const int SportsBps = 75; // 0,75 %
public const int PoliticsFinanceBps = 100; // 1,0 %
public const int CryptoBps = 180; // 1,8 %
public const int GeopoliticsBps = 0; // 0 %
public const int DefaultBps = 100; // konservativer Default
/// <summary>
/// Fallback-Taker-Fee (bps) anhand der Markt-Kategorie, wenn die API keinen Satz liefert.
/// Robust gegen unbekannte/leere Kategorien (→ konservativer Default).
/// </summary>
public static int FallbackBps(string? category)
{
string c = (category ?? string.Empty).ToLowerInvariant();
if (c.Contains("sport")) return SportsBps;
if (c.Contains("crypto")) return CryptoBps;
if (c.Contains("geopolit")) return GeopoliticsBps;
if (c.Contains("politic") || c.Contains("finance") || c.Contains("econom")) return PoliticsFinanceBps;
return DefaultBps;
}
/// <summary>
/// Effektiver Taker-Satz (bps) für eine Order (M6): den realen Satz aus dem Markt-Objekt
/// (<paramref name="takerFeeBps"/> &gt; 0) nutzen, sonst den Kategorie-Fallback. Wird in die
/// signierte Order geschrieben; ein zu niedriger Satz kann auf Fee-Märkten abgelehnt werden.
/// </summary>
public static int ResolveBps(int takerFeeBps, string? category) =>
takerFeeBps > 0 ? takerFeeBps : FallbackBps(category);
/// <summary>Taker-Fee in USDC für ein Notional (Preis × Shares) bei gegebenem Satz.</summary>
public static decimal FeeUsd(decimal notionalUsd, int feeBps)
{
if (feeBps <= 0 || notionalUsd <= 0m) return 0m;
return notionalUsd * feeBps / 10000m;
}
}
}
@@ -1,5 +1,6 @@
using System;
using PolyTrader.Core.Persistence;
using PolyTrader.Core.Trading;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;
@@ -1,5 +1,6 @@
using System;
using PolyTrader.Core.Persistence;
using PolyTrader.Core.Trading;
using PolyTrader.Modules.CopyTrading.Logic;
using PolyTrader.Modules.CopyTrading.Persistence;
using System.Collections.Concurrent;
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Threading;
using PolyTrader.Core.Trading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using PolyTrader.Core.Persistence;
@@ -1,5 +1,6 @@
using System;
using PolyTrader.Core.Persistence;
using PolyTrader.Core.Trading;
using PolyTrader.Modules.CopyTrading.Logic;
using PolyTrader.Modules.CopyTrading.Persistence;
using System.Collections.Concurrent;