Files
RichardandClaude Opus 4.8 c9eb11afe4 RF-Slice 1: ResolutionFarming-Modul-Skelett + geldkritische pure Logik + Tests
Neues Strategiemodul PolyTrader.Modules.ResolutionFarming (IPolyTraderModule,
Name=ResolutionFarming, DbPrefix=rf_), in Solution/App/Tests + beide Modul-Listen
in Program.cs eingebunden. Skelett laedt (RegisterServices/UI noch no-op).

Geldkritische Entscheidungslogik pur und vollstaendig unit-getestet:
- FarmingRiskEngine: Netto-Edge nach Fees (NetEdge/NetEdgePct/HasEdge), Positionsgroesse
  unter Markt-/Cluster-/Gesamt-Exposure-Limits (AllowedPositionUsd), Kill-Switch, Tages-Drossel.
- FarmingScanner: Preisband, Kategorie-Whitelist, Blacklist, Cluster-Key (korrelierte
  Favoriten teilen einen Cluster), Kandidaten-Score.
- FarmingFillModel: Shares fuer Budget (2-Dezimal-Floor), Resolve-PnL (Auszahlung - Kosten -
  Entry-Fee, Maker/Taker), Einstiegskosten inkl. Fee. Nutzt Core.FeeModel.
- RfSettings (rf_settings) mit konservativen Defaults + PropertyGrid-Attributen.

39 neue Tests. Build 0 Fehler, 283 Tests gruen, --smoke-ui ok.
Persistenz (DbContext/Migration/Repos), Scanner-/Monitor-Jobs, Execution und UI folgen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 21:43:34 +02:00

50 lines
1.7 KiB
C#

using PolyTrader.Modules.ResolutionFarming.Logic;
using Xunit;
using static PolyTrader.Modules.ResolutionFarming.Logic.FarmingFillModel;
namespace PolyTrader.Tests
{
/// <summary>Sicherheitsnetz für das Fill-/PnL-Modell (Demo-Realismus: Fee beim Einstieg, Auszahlung bei Auflösung).</summary>
public class FarmingFillModelTests
{
[Fact]
public void SharesForBudget_floors_to_two_decimals()
{
// 25 / 0.95 = 26.315... -> 26.31
Assert.Equal(26.31m, SharesForBudget(25m, 0.95m));
Assert.Equal(0m, SharesForBudget(0m, 0.95m));
Assert.Equal(0m, SharesForBudget(25m, 0m));
}
[Fact]
public void ResolvePnl_winner_maker_pays_no_fee()
{
// 100 Shares @ 0.95, Maker (0 Fee), Gewinner: 100 - 95 - 0 = 5
Assert.Equal(5m, ResolvePnl(100m, 0.95m, 0, isWinner: true));
}
[Fact]
public void ResolvePnl_winner_taker_subtracts_fee()
{
// 100 @ 0.95, Taker 100 bps: cost 95, fee 0.95, payout 100 -> 100 - 95 - 0.95 = 4.05
Assert.Equal(4.05m, ResolvePnl(100m, 0.95m, 100, isWinner: true));
}
[Fact]
public void ResolvePnl_loser_loses_cost_and_fee()
{
// Verlierer: payout 0 -> -(cost) - fee = -95 - 0.95 = -95.95
Assert.Equal(-95.95m, ResolvePnl(100m, 0.95m, 100, isWinner: false));
}
[Fact]
public void EntryCostWithFee_adds_taker_fee()
{
// 100 @ 0.95, 100 bps: 95 + 0.95 = 95.95
Assert.Equal(95.95m, EntryCostWithFee(100m, 0.95m, 100));
// Maker: nur cost
Assert.Equal(95m, EntryCostWithFee(100m, 0.95m, 0));
}
}
}