RF-Slice 5: Demo-Execution + Resolution-Monitor (Phase RF-2)
Schliesst die Demo-Handelsschleife des ResolutionFarming: - FarmingExecutionPlanner (pur): waehlt aus akzeptierten Kandidaten die zu oeffnenden Positionen + Groesse, priorisiert nach Score, unter Markt-/Cluster-/Gesamt-Limits, Kill-Switch und Tages-Drossel; dedupliziert Token, schreibt Exposure im Lauf fort. - FarmingResolution (pur): baut aus Position + Ergebnis den RfClosedTrade (PnL/Fees/Redeem-Status). - FarmingExecutionService: Demo-Einstieg (Maker-Fill 0 Fee via FarmingFillModel) -> rf_positions. Live-Execution bewusst geloggt/uebersprungen (Zielland). Demo-Balance NICHT mutiert (kein Shared-Account-Konflikt; PnL fliesst ueber rf_closed_trades). - FarmingResolutionMonitorService: schliesst aufgeloeste Positionen, bucht GlobalPnl, Dual-Write ins Core-Trade-Log (Dashboard). Auflösungsstatus via IMarketResolutionSource. - NullMarketResolutionSource als Default (nichts loest auf), bis Live-Data-API verdrahtet ist. 15 neue Tests (Planner 8, Resolution 3, Execution-Service 2, Monitor 2). Build 0 Fehler, 311 Tests gruen, --smoke-ui ok. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
80e6ad9b2d
commit
1c3a364df2
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using PolyTrader.Modules.ResolutionFarming.Logic;
|
||||
using PolyTrader.Modules.ResolutionFarming.Models;
|
||||
using Xunit;
|
||||
|
||||
namespace PolyTrader.Tests
|
||||
{
|
||||
/// <summary>Sicherheitsnetz für den reinen Positions-Abschluss bei Auflösung (PnL, Fees, Redeem-Status).</summary>
|
||||
public class FarmingResolutionTests
|
||||
{
|
||||
private static RfPosition Pos(int feeBps = 0) => new()
|
||||
{
|
||||
AccountId = 1, TokenId = "a", MarketQuestion = "Q", Size = 100m, EntryPrice = 0.95m,
|
||||
AmountUsd = 95m, EntryFeeBps = feeBps, IsDemo = true, OpenedAt = DateTime.UtcNow.AddHours(-5)
|
||||
};
|
||||
|
||||
[Fact]
|
||||
public void Winner_maker_pays_out_one_per_share()
|
||||
{
|
||||
var t = FarmingResolution.BuildClosedTrade(Pos(), isWinner: true, DateTime.UtcNow);
|
||||
Assert.Equal(1.0m, t.ExitPrice);
|
||||
Assert.Equal(5m, t.RealizedPnl); // 100 - 95 - 0
|
||||
Assert.Equal(0m, t.TotalFees);
|
||||
Assert.Equal("Pending", t.RedeemStatus); // Gewinner müssen redeemt werden
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Loser_loses_entry_cost()
|
||||
{
|
||||
var t = FarmingResolution.BuildClosedTrade(Pos(), isWinner: false, DateTime.UtcNow);
|
||||
Assert.Equal(0.0m, t.ExitPrice);
|
||||
Assert.Equal(-95m, t.RealizedPnl);
|
||||
Assert.Equal("None", t.RedeemStatus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Taker_entry_fee_reduces_winner_pnl()
|
||||
{
|
||||
var t = FarmingResolution.BuildClosedTrade(Pos(feeBps: 100), isWinner: true, DateTime.UtcNow);
|
||||
Assert.Equal(4.05m, t.RealizedPnl); // 100 - 95 - 0.95
|
||||
Assert.Equal(0.95m, t.TotalFees);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user