feat(paper-trading): forward-test ledger engine
Adds a background forward-test engine that records flat-stake "paper" bets for directional anomalies as they fire and settles them when results arrive, measuring the detector's live, out-of-sample edge — the antidote to backtest overfitting. The results UI is a follow-up. - Domain: PaperBet entity (Rate>1 / Stake>0 invariants, Open factory, SettleAgainst — Won pays stake x rate, else Lost) + AnomalyEvidenceSide.RateFor. - Application: OpenPaperBetsUseCase (directional + score gate, dedups by AnomalyId, picks the post-flip favourite and its locked-in rate) and SettlePaperBetsUseCase (Won when pick == winner else Lost; ungraded events stay open; batched result lookup). - Infrastructure: PaperBetEntity + config (TEXT decimals, unique AnomalyId index, Outcome index), repository, mapping, additive AddPaperBets migration, and PaperTradingWorker (config-gated, baseline since-marker, open+settle per cycle). - Config: PaperTradingOptions / appsettings PaperTrading (Enabled:false default). - 25 tests: domain settlement, both use cases, and a real-SQLite round-trip incl. the unique-AnomalyId double-open backstop.
This commit is contained in:
@@ -185,6 +185,53 @@ namespace Marathon.Infrastructure.Migrations
|
||||
b.ToTable("Leagues", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marathon.Infrastructure.Persistence.Entities.PaperBetEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("AnomalyId")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EventCode")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("OpenedAt")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Outcome")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal?>("Payout")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("PickedSide")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<decimal>("Rate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SettledAt")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<decimal>("Stake")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AnomalyId")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("IX_PaperBets_AnomalyId");
|
||||
|
||||
b.HasIndex("Outcome")
|
||||
.HasDatabaseName("IX_PaperBets_Outcome");
|
||||
|
||||
b.ToTable("PaperBets", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Marathon.Infrastructure.Persistence.Entities.PlacedBetEntity", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
|
||||
Reference in New Issue
Block a user