brackt/app/routes.ts
Chris Parsons 79ec477a98 feat: implement Expected Value System with ICM probability calculator
Implements Phase 5.2 of the EV system with Harville-Malmuth Independent Chip Model
for calculating participant placement probabilities from futures odds.

## Key Features

### ICM Probability Calculator
- Implements Harville-Malmuth method for distributing probabilities
- Converts American odds to championship probabilities
- Generates P(1st) through P(8th) for all participants
- Column-normalized: each placement sums to 100% across all teams
- Works with any number of participants (not limited to 8)

### Admin UI - Futures Odds Entry
- Enter American odds (e.g., +550, -200) for championship futures
- Live preview of ICM-calculated probability distributions
- Displays all 8 placement probabilities
- Persists odds for editing on subsequent visits
- Automatic probability normalization (removes bookmaker vig)

### Database Schema Updates
- Renamed participant_expected_values.season_id → sports_season_id
- Updated foreign key to reference sports_seasons instead of seasons
- Added source_odds field to store original futures odds
- Migration 0025: Column rename and FK update
- Migration 0026: Add source_odds field

### Model Layer
- participant-expected-value: CRUD operations for probability distributions
- Supports multiple probability sources (manual, futures_odds, elo_simulation)
- Automatic EV calculation based on league scoring rules
- Probability validation and normalization

### Service Layer
- icm-calculator: Harville-Malmuth probability distribution
- probability-engine: Odds conversion and Elo utilities (for future use)
- bracket-simulator: Monte Carlo simulation (for future hybrid approach)
- ev-calculator: Expected value computation from probabilities

## Technical Details

- Uses exponential decay favoring top positions for strong teams
- Preserves championship probability ordering in final distributions
- Row sums vary (strong teams ~100%, weak teams lower)
- All probabilities between 0-1, mathematically valid
- Comprehensive test suite: 97 tests passing

## Future Enhancements

- Hybrid approach: ICM pre-playoffs, bracket simulation during playoffs
- Integration with league-specific scoring rules
- Historical probability tracking for accuracy analysis

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 22:19:46 -08:00

90 lines
3.5 KiB
TypeScript

import { type RouteConfig, index, route } from "@react-router/dev/routes";
export default [
index("routes/home.tsx"),
route("i/:inviteCode", "routes/i.$inviteCode.tsx"),
route("leagues/new", "routes/leagues/new.tsx"),
route("leagues/:leagueId", "routes/leagues/$leagueId.tsx"),
route("leagues/:leagueId/settings", "routes/leagues/$leagueId.settings.tsx"),
route(
"leagues/:leagueId/sports-seasons/:sportsSeasonId",
"routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.tsx"
),
route(
"leagues/:leagueId/draft/:seasonId",
"routes/leagues/$leagueId.draft.$seasonId.tsx"
),
route(
"leagues/:leagueId/draft-board/:seasonId",
"routes/leagues/$leagueId.draft-board.$seasonId.tsx"
),
route(
"leagues/:leagueId/standings/:seasonId",
"routes/leagues/$leagueId.standings.$seasonId.tsx"
),
route(
"leagues/:leagueId/standings/:seasonId/teams/:teamId",
"routes/leagues/$leagueId.standings.$seasonId.teams.$teamId.tsx"
),
route("teams/:teamId/settings", "routes/teams/$teamId.settings.tsx"),
route("api/webhooks/clerk", "routes/api/webhooks/clerk.ts"),
route("api/queue/add", "routes/api/queue.add.ts"),
route("api/queue/remove", "routes/api/queue.remove.ts"),
route("api/queue/clear", "routes/api/queue.clear.ts"),
route("api/queue/reorder", "routes/api/queue.reorder.ts"),
route("api/draft/make-pick", "routes/api/draft.make-pick.ts"),
route("api/draft/start", "routes/api/draft.start.ts"),
route("api/draft/pause", "routes/api/draft.pause.ts"),
route("api/draft/resume", "routes/api/draft.resume.ts"),
route("api/draft/force-autopick", "routes/api/draft.force-autopick.ts"),
route("api/draft/force-manual-pick", "routes/api/draft.force-manual-pick.ts"),
route("api/autodraft/update", "routes/api/autodraft.update.ts"),
route("user-profile", "routes/user-profile.tsx"),
route("how-to-play", "routes/how-to-play.tsx"),
route("test-socket", "routes/test-socket.tsx"),
// Admin routes
route("admin", "routes/admin.tsx", [
index("routes/admin._index.tsx"),
route("sports", "routes/admin.sports.tsx"),
route("sports/new", "routes/admin.sports.new.tsx"),
route("sports/:id", "routes/admin.sports.$id.tsx"),
route("sports-seasons", "routes/admin.sports-seasons.tsx"),
route("sports-seasons/new", "routes/admin.sports-seasons.new.tsx"),
route("sports-seasons/:id", "routes/admin.sports-seasons.$id.tsx"),
route(
"sports-seasons/:id/participants",
"routes/admin.sports-seasons.$id.participants.tsx"
),
route(
"sports-seasons/:id/events",
"routes/admin.sports-seasons.$id.events.tsx"
),
route(
"sports-seasons/:id/events/:eventId",
"routes/admin.sports-seasons.$id.events.$eventId.tsx"
),
route(
"sports-seasons/:id/events/:eventId/bracket",
"routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx"
),
route(
"sports-seasons/:id/expected-values",
"routes/admin.sports-seasons.$id.expected-values.tsx"
),
route(
"sports-seasons/:id/futures-odds",
"routes/admin.sports-seasons.$id.futures-odds.tsx"
),
route("participants", "routes/admin.participants.tsx"),
route("templates", "routes/admin.templates.tsx"),
route("templates/new", "routes/admin.templates.new.tsx"),
route("templates/:id", "routes/admin.templates.$id.tsx"),
route("data-sync", "routes/admin.data-sync.tsx"),
route("standings-snapshots", "routes/admin.standings-snapshots.tsx"),
]),
route(
"api/admin/export-sports-data",
"routes/api.admin.export-sports-data.ts"
),
] satisfies RouteConfig;