chore: initial repo setup (.gitignore, README, CLAUDE.md)
This commit is contained in:
+83
@@ -0,0 +1,83 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# .NET
|
||||||
|
# =============================================================================
|
||||||
|
bin/
|
||||||
|
obj/
|
||||||
|
out/
|
||||||
|
*.user
|
||||||
|
*.suo
|
||||||
|
*.userprefs
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
*.lock.json
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
TestResults/
|
||||||
|
coverage/
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
*.opencover.xml
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Visual Studio / Rider / VS Code
|
||||||
|
# =============================================================================
|
||||||
|
.vs/
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OS
|
||||||
|
# =============================================================================
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
.DS_Store
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Build / Tooling artifacts
|
||||||
|
# =============================================================================
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.tmp
|
||||||
|
*.cache
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.pdb
|
||||||
|
node_modules/
|
||||||
|
.playwright/
|
||||||
|
playwright-report/
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Project-specific
|
||||||
|
# =============================================================================
|
||||||
|
# Local SQLite databases (never commit data)
|
||||||
|
*.db
|
||||||
|
*.db-shm
|
||||||
|
*.db-wal
|
||||||
|
data/
|
||||||
|
exports/
|
||||||
|
|
||||||
|
# Local secrets / runtime config overrides
|
||||||
|
appsettings.Local.json
|
||||||
|
appsettings.*.local.json
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# Scraping fixtures captured during Phase 0 spike (kept locally, not in repo)
|
||||||
|
spike/captures/
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
# CLAUDE.md — maraphon-app
|
||||||
|
|
||||||
|
> Project memory for Claude Code sessions on this repository. Keep entries concise.
|
||||||
|
> Per-feature learnings are appended below by the feature-planner workflow.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
**maraphon-app** is a sports betting odds analyzer for marathonbet.by. It scrapes
|
||||||
|
pre-match (`/su`) and live (`/su/live`) events, persists odds snapshots over time, and
|
||||||
|
detects anomalies — especially the **odds-flip** pattern (bookmaker freezes bets then
|
||||||
|
inverts underdog/favorite coefficients).
|
||||||
|
|
||||||
|
## Architecture (Clean Architecture, 5 projects + tests)
|
||||||
|
|
||||||
|
```
|
||||||
|
Marathon.Domain ← entities, value objects, no external deps
|
||||||
|
↑
|
||||||
|
Marathon.Application ← use cases + abstractions (IOddsScraper, IRepository, ...)
|
||||||
|
↑
|
||||||
|
Marathon.Infrastructure ← EF Core (SQLite), scraping (AngleSharp/Playwright), Excel, Polly
|
||||||
|
Marathon.UI ← Razor Class Library (all Blazor components — host-agnostic)
|
||||||
|
↑
|
||||||
|
Marathon.Hosts.WpfBlazor ← WPF + BlazorWebView host (replaceable for ASP.NET Core later)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key portability invariant:** All UI lives in `Marathon.UI` (Razor Class Library). The
|
||||||
|
host project (`Marathon.Hosts.WpfBlazor`) is the *only* thing that changes when migrating
|
||||||
|
to a web app — drop in an ASP.NET Core Blazor Server host that references the same RCL.
|
||||||
|
|
||||||
|
## Tech stack
|
||||||
|
|
||||||
|
- **.NET 8 LTS**, C# 12
|
||||||
|
- **EF Core 8** + SQLite (WAL mode)
|
||||||
|
- **AngleSharp** (HTML), **Playwright for .NET** (SPA fallback)
|
||||||
|
- **Polly v8** (`Microsoft.Extensions.Http.Resilience`)
|
||||||
|
- **MudBlazor** components, **Plotly.Blazor** charts
|
||||||
|
- **Serilog** logging (rolling file + console)
|
||||||
|
- **xUnit + FluentAssertions + NSubstitute**, in-memory SQLite for repo tests
|
||||||
|
|
||||||
|
## Build & test
|
||||||
|
|
||||||
|
| Command | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `dotnet build Marathon.sln` | Build all projects |
|
||||||
|
| `dotnet test Marathon.sln` | Run all tests |
|
||||||
|
| `dotnet format Marathon.sln --verify-no-changes` | Lint |
|
||||||
|
| `dotnet run --project src/Marathon.Hosts.WpfBlazor` | Run desktop app |
|
||||||
|
|
||||||
|
## Coding conventions
|
||||||
|
|
||||||
|
- Nullable reference types: **enabled** (`<Nullable>enable</Nullable>`)
|
||||||
|
- Implicit usings: enabled
|
||||||
|
- Treat warnings as errors in `Release` builds
|
||||||
|
- File-scoped namespaces
|
||||||
|
- One public type per file (except small DTOs/records grouped in a feature folder)
|
||||||
|
- Domain entities: prefer `record` for immutable data; class with private setters when
|
||||||
|
identity matters
|
||||||
|
- No mutation of domain objects after construction — return new instances
|
||||||
|
- Repositories return `IReadOnlyList<T>`, not `List<T>` or `IEnumerable<T>` (clarity on
|
||||||
|
enumeration cost)
|
||||||
|
- Tests follow `Given_When_Then` or `Should_<expected>_When_<condition>` naming
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Every variable parameter is configurable via `appsettings.json` and overridable via
|
||||||
|
`appsettings.Local.json` (gitignored) or environment variables:
|
||||||
|
|
||||||
|
- `Scraping:PollingIntervalSeconds` (default 30)
|
||||||
|
- `Scraping:MaxConcurrentRequests` (default 4)
|
||||||
|
- `Scraping:UserAgents[]` (rotated per request)
|
||||||
|
- `Scraping:RetryPolicy:*` (Polly settings)
|
||||||
|
- `Scraping:RateLimit:RequestsPerSecond` (default 1)
|
||||||
|
- `Storage:DatabasePath` (default `./data/marathon.db`)
|
||||||
|
- `Storage:ExportDirectory` (default `./exports`)
|
||||||
|
- `Storage:SnapshotRetentionDays` (default 90)
|
||||||
|
- `Anomaly:SuspensionGapSeconds` (default 60)
|
||||||
|
- `Anomaly:OddsFlipThreshold` (default 0.30 — implied probability delta)
|
||||||
|
- `Localization:DefaultCulture` (default `ru-RU`)
|
||||||
|
|
||||||
|
A future Settings page in the UI binds to these.
|
||||||
|
|
||||||
|
## Domain model summary
|
||||||
|
|
||||||
|
- `Sport(Code, Name)` — e.g., `(6, "Баскетбол")`
|
||||||
|
- `Event(Id, SportCode, CountryCode, LeagueId, CategoryId, ScheduledAt, EventCodeFromBookmaker)`
|
||||||
|
- `OddsSnapshot(EventId, CapturedAt, Source: Pre|Live, Bets: List<Bet>)`
|
||||||
|
- `Bet(Scope: Match|Period[N], Type: Win|Draw|WinFora|Total, Side: 1|2|Less|More, Value?, Rate)`
|
||||||
|
- `EventResult(EventId, FinalScore, WinnerSide)`
|
||||||
|
- `Anomaly(EventId, DetectedAt, Kind: SuspensionFlip, Score, EvidenceTimeline)`
|
||||||
|
|
||||||
|
## Excel export schema (compliance with customer spec)
|
||||||
|
|
||||||
|
Customer TZ requires wide-table layout with columns like `Bet_Match_Win_1`,
|
||||||
|
`Bet_Period-1_Win_Fora_2_Value`, etc.
|
||||||
|
|
||||||
|
**Internal storage is normalized** (one row per Bet in `OddsSnapshots`). The Excel
|
||||||
|
exporter denormalizes to the wide format on demand. Filename pattern:
|
||||||
|
|
||||||
|
```
|
||||||
|
Marathon_<YYYY-MM-DD>_to_<YYYY-MM-DD>.xlsx
|
||||||
|
```
|
||||||
|
|
||||||
|
## Recurring Issues & Patterns
|
||||||
|
|
||||||
|
(Populated as we work — leave empty until something repeats.)
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
# maraphon-app
|
||||||
|
|
||||||
|
Sports betting odds analyzer for [marathonbet.by](https://www.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
|
||||||
|
|
||||||
|
```pwsh
|
||||||
|
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`](plans/initial-implementation/PLAN.md)
|
||||||
|
for the current phase plan and progress.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Private — customer project.
|
||||||
Reference in New Issue
Block a user