alexei.dolgolyov 0d52b7beff feat(backtest): historical strategy backtester
Adds an interactive backtester that replays the SuspensionFlip detector over
all flagged anomalies under a chosen score threshold and staking rule
(flat / percent-of-bankroll / Kelly), and reports the headline numbers a
user needs to judge edge: final bankroll, ROI, max drawdown (peak-to-trough),
win/loss streaks, plus per-bet equity curve.

Domain (pure):
- StakeRule enum + BacktestStrategy params (with validation).
- BacktestSimulator: deterministic function taking strategy + chronological
  candidates → BacktestResult. Implements Kelly with post-flip implied prob
  as p (skipping negative-edge bets), peak-to-trough drawdown tracking, and
  win/loss streak rollups. Mirrors AnomalyOutcomeEvaluator on the 2-way Draw
  guard so tennis data inconsistencies are refused rather than miss-counted.
- Skipped counter split into SkippedByThreshold / SkippedByDataQuality /
  SkippedByBankroll so the UI can distinguish "strategy choice" from
  "data-quality" from "bankroll empty".

Application:
- RunBacktestUseCase: loads anomalies + events + results, parses evidence,
  builds candidates, hands event titles into the simulator so the UI does
  zero repository round-trips of its own.

UI:
- Pages/Anomalies/Backtest.razor: hero, strategy form (MudBlazor — conditional
  sub-field per staking rule), 4-card KPI strip (final bankroll / net profit
  / ROI / max drawdown), counters row, inline-SVG equity curve, trade-trace
  table with per-bet outcome pills and link-back to the source anomaly.
- Nav entry under Analysis. RU + EN i18n.

Tests: +20 (16 simulator math — flat / percent compounding / Kelly +/-
edge / quarter-Kelly / bankroll-exceeded / out-of-order chronology / Draw
favourite / multi-window drawdown / event-title pass-through + 4 use-case
join). All 399 tests pass.

Money rounding switched to MidpointRounding.AwayFromZero throughout the
simulator output for accounting convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 18:34:42 +03:00

maraphon-app

Sports betting odds analyzer for marathonbet.by.

Scrapes pre-match (/su) and live (/su/live) sports events, tracks coefficient changes over time, and detects anomalies — in particular the "odds-flip" pattern where the bookmaker freezes betting and then inverts underdog/favorite odds.

Tech stack

  • .NET 8 + C# 12
  • Blazor Hybrid — WPF shell hosting BlazorWebView (designed to migrate to ASP.NET Core Blazor Server with no UI rewrite)
  • EF Core + SQLite (WAL mode) for local storage
  • ClosedXML for Excel export
  • AngleSharp for HTML scraping (with Playwright fallback for JS-rendered pages)
  • Polly v8 for retry / circuit breaker / rate limiting
  • MudBlazor UI components, Plotly.Blazor for charts
  • Serilog structured logging
  • xUnit / FluentAssertions / NSubstitute for tests

Project layout

src/
  Marathon.Domain/         entities, value objects, no dependencies
  Marathon.Application/    use cases, abstractions (IOddsScraper, IRepository, ...)
  Marathon.Infrastructure/ EF Core, scraping, Polly, Excel, Playwright
  Marathon.UI/             Razor Class Library — all Blazor components live here
  Marathon.Hosts.WpfBlazor/ WPF + BlazorWebView host (replaceable for web)
tests/
  Marathon.*.Tests/        unit + integration tests per layer

Build & run

dotnet build Marathon.sln
dotnet test  Marathon.sln
dotnet run   --project src/Marathon.Hosts.WpfBlazor

Configuration

All variable parameters (polling intervals, concurrency, user-agents, retry policy, snapshot retention, locale) are exposed via appsettings.json and live-editable via the in-app Settings page.

Status

🟡 In active development. See plans/initial-implementation/PLAN.md for the current phase plan and progress.

License

Private — customer project.

S
Description
No description provided
Readme 1.8 MiB
Languages
HTML 54.3%
C# 45%
CSS 0.7%