brackt/app/models/participant-expected-value.ts

436 lines
13 KiB
TypeScript
Raw Normal View History

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
/**
* Model for Participant Expected Values
*
* Manages probability distributions and calculated EVs for participants
* in sports seasons.
*/
import { database } from "~/database/context";
import { participantExpectedValues, participants } from "~/database/schema";
Add Zod validation and expand export/import for EV and results data (#14) * Include participant EV/futures data in data sync export/import The export now includes the full participantExpectedValues records (placement probabilities, source, sourceOdds) alongside participants. On import, EV records are upserted in merge mode and created fresh in replace mode (cascade handles cleanup). Backward-compatible with older exports that lack the participantExpectedValues field. https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B * Address all data-sync code review findings - Wrap importSportsDataFromJSON in a db.transaction() so any mid-import failure rolls back cleanly instead of leaving partial data - Add zod validation of imported JSON before any DB writes, giving a clear error on malformed/incompatible files - Fix N+1 queries: build participantIdMap during participant import so the EV and results sections resolve IDs from memory, not extra queries - Fix merge mode silently skipping existing participants — now updates shortName, externalId, and expectedValue - Add participantResults to export/import (was deleted in replace mode but never exported, so results were permanently lost) - Restore calculatedAt timestamp on EV import instead of defaulting to now(); stored as ISO string in the export for portability - Document that onDelete:cascade covers participantExpectedValues and participantResults when participants is deleted; remove the now- redundant explicit participantResults delete from replace mode - Bump export version to 1.1; old v1.0 files still import cleanly since participantExpectedValues, participantResults, and calculatedAt are all optional in the zod schema - Collapse all six DB fetches in exportSportsDataToJSON into one Promise.all for parallel execution - Add countAllParticipants() and countAllParticipantEVs() model funcs - Show Participants and EV Records counts on the dashboard stat cards - Replace raw DOM form creation/submit in handleImport with useFetcher, giving proper loading state, no full-page reload, and type-safe data - Remove dead export intent handler from the action function https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
import { eq, and, count } from "drizzle-orm";
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
import type { ProbabilityDistribution, ScoringRules } from "~/services/ev-calculator";
import { calculateEV, normalizeProbabilities } from "~/services/ev-calculator";
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
export type ProbabilitySource = "manual" | "futures_odds" | "elo_simulation" | "performance_model";
export interface ParticipantEV {
id: string;
participantId: string;
sportsSeasonId: string;
probFirst: string;
probSecond: string;
probThird: string;
probFourth: string;
probFifth: string;
probSixth: string;
probSeventh: string;
probEighth: string;
expectedValue: string;
source: ProbabilitySource | null;
sourceOdds: number | null;
calculatedAt: Date;
updatedAt: Date;
}
export interface CreateProbabilityInput {
participantId: string;
sportsSeasonId: string;
probabilities: ProbabilityDistribution;
scoringRules: ScoringRules;
source?: ProbabilitySource;
sourceOdds?: number; // American odds if source is futures_odds
}
export interface UpdateProbabilityInput {
probabilities: ProbabilityDistribution;
scoringRules: ScoringRules;
source?: ProbabilitySource;
}
/**
* Create or update participant probabilities and calculate EV
*
* @param input - Probabilities, scoring rules, and metadata
* @returns Created/updated participant EV record
* @throws Error if probabilities don't sum to 1.0 (within tolerance)
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
*/
export async function upsertParticipantEV(
input: CreateProbabilityInput
): Promise<ParticipantEV> {
const { participantId, sportsSeasonId, probabilities, scoringRules, source = "manual", sourceOdds } = input;
// Always validate probabilities don't exceed 1.0
const sum = Object.values(probabilities).reduce((a, b) => a + b, 0);
if (sum > 1.01) {
throw new Error(
`Probabilities cannot sum to more than 1.0. Current sum: ${sum.toFixed(4)} (${(sum * 100).toFixed(1)}%)`
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
);
}
// Calculate EV
const expectedValue = calculateEV(probabilities, scoringRules);
const db = database();
// Check if record exists
const existing = await db
.select()
.from(participantExpectedValues)
.where(
and(
eq(participantExpectedValues.participantId, participantId),
eq(participantExpectedValues.sportsSeasonId, sportsSeasonId)
)
)
.limit(1);
const now = new Date();
let result: ParticipantEV;
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
if (existing.length > 0) {
// Update existing
const updated = await db
.update(participantExpectedValues)
.set({
probFirst: probabilities.probFirst.toString(),
probSecond: probabilities.probSecond.toString(),
probThird: probabilities.probThird.toString(),
probFourth: probabilities.probFourth.toString(),
probFifth: probabilities.probFifth.toString(),
probSixth: probabilities.probSixth.toString(),
probSeventh: probabilities.probSeventh.toString(),
probEighth: probabilities.probEighth.toString(),
expectedValue: expectedValue.toString(),
source,
User/chris/ev f1 framework (#93) * feat: EV simulation framework with F1 Monte Carlo simulator - Add EV snapshot tables (participant_ev_snapshots, team_ev_snapshots) and simulation_status column on sports seasons - Add ev-snapshot model with upsert and history query functions - Add simulator framework: types, bracket/F1/golf simulators, registry - F1 simulator: vig-removed ICM weighted draw (pre-season) + race-by-race Monte Carlo from current standings (in-season); per-position column normalization to prevent floating-point EV drift - Add admin simulate route and Run Simulation button on sports season page - Rework futures-odds admin page to save odds then run simulation in one action - Remove recalculate-probabilities route (superseded by simulate route) - Remove EV trend chart panel and associated DB queries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: map simulators to sports via simulatorType field Adds a `simulator_type` enum column to the `sports` table so each sport can be assigned a specific simulation algorithm rather than deriving it from the sports season's scoring pattern. - Add `simulatorTypeEnum` (f1_standings, indycar_standings, golf_qualifying_points, playoff_bracket) + `simulatorType` nullable column on `sports` table; migration 0037 - Rewrite simulator registry to key off `SimulatorType` instead of `ScoringPattern`; indycar_standings shares F1Simulator for now - `findSportsSeasonById` now returns `SportsSeasonWithSport` so callers have typed access to `sport.simulatorType` - Simulate and futures-odds actions read `sport.simulatorType`; guard fires before setting `simulationStatus: running` - Admin sport edit page gains a Simulator Type dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 15:34:31 -07:00
...(sourceOdds !== undefined ? { sourceOdds } : {}),
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
calculatedAt: now,
updatedAt: now,
})
.where(eq(participantExpectedValues.id, existing[0].id))
.returning();
result = updated[0];
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
} else {
// Create new
const created = await db
.insert(participantExpectedValues)
.values({
participantId,
sportsSeasonId,
probFirst: probabilities.probFirst.toString(),
probSecond: probabilities.probSecond.toString(),
probThird: probabilities.probThird.toString(),
probFourth: probabilities.probFourth.toString(),
probFifth: probabilities.probFifth.toString(),
probSixth: probabilities.probSixth.toString(),
probSeventh: probabilities.probSeventh.toString(),
probEighth: probabilities.probEighth.toString(),
expectedValue: expectedValue.toString(),
source,
sourceOdds: sourceOdds ?? null,
calculatedAt: now,
updatedAt: now,
})
.returning();
result = created[0];
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
}
// Sync calculated EV to participants table for draft room ranking
await db
.update(participants)
.set({ expectedValue: expectedValue.toString(), updatedAt: now })
.where(eq(participants.id, participantId));
return result;
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
}
/**
* Create or update with auto-normalization
* Automatically normalizes probabilities if they don't sum to 100%
*/
export async function upsertParticipantEVWithNormalization(
input: CreateProbabilityInput
): Promise<ParticipantEV> {
const normalized = normalizeProbabilities(input.probabilities);
return upsertParticipantEV({
...input,
probabilities: normalized,
});
}
Add Zod validation and expand export/import for EV and results data (#14) * Include participant EV/futures data in data sync export/import The export now includes the full participantExpectedValues records (placement probabilities, source, sourceOdds) alongside participants. On import, EV records are upserted in merge mode and created fresh in replace mode (cascade handles cleanup). Backward-compatible with older exports that lack the participantExpectedValues field. https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B * Address all data-sync code review findings - Wrap importSportsDataFromJSON in a db.transaction() so any mid-import failure rolls back cleanly instead of leaving partial data - Add zod validation of imported JSON before any DB writes, giving a clear error on malformed/incompatible files - Fix N+1 queries: build participantIdMap during participant import so the EV and results sections resolve IDs from memory, not extra queries - Fix merge mode silently skipping existing participants — now updates shortName, externalId, and expectedValue - Add participantResults to export/import (was deleted in replace mode but never exported, so results were permanently lost) - Restore calculatedAt timestamp on EV import instead of defaulting to now(); stored as ISO string in the export for portability - Document that onDelete:cascade covers participantExpectedValues and participantResults when participants is deleted; remove the now- redundant explicit participantResults delete from replace mode - Bump export version to 1.1; old v1.0 files still import cleanly since participantExpectedValues, participantResults, and calculatedAt are all optional in the zod schema - Collapse all six DB fetches in exportSportsDataToJSON into one Promise.all for parallel execution - Add countAllParticipants() and countAllParticipantEVs() model funcs - Show Participants and EV Records counts on the dashboard stat cards - Replace raw DOM form creation/submit in handleImport with useFetcher, giving proper loading state, no full-page reload, and type-safe data - Remove dead export intent handler from the action function https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B --------- Co-authored-by: Claude <noreply@anthropic.com>
2026-02-20 20:25:25 -08:00
export async function countAllParticipantEVs(): Promise<number> {
const db = database();
const result = await db.select({ value: count() }).from(participantExpectedValues);
return result[0].value;
}
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
/**
* Get participant EV for a specific sports season
*/
export async function getParticipantEV(
participantId: string,
sportsSeasonId: string
): Promise<ParticipantEV | null> {
const db = database();
const result = await db
.select()
.from(participantExpectedValues)
.where(
and(
eq(participantExpectedValues.participantId, participantId),
eq(participantExpectedValues.sportsSeasonId, sportsSeasonId)
)
)
.limit(1);
return result[0] || null;
}
/**
* Get all participant EVs for a sports season
*/
export async function getAllParticipantEVsForSeason(
sportsSeasonId: string
): Promise<ParticipantEV[]> {
const db = database();
return db
.select()
.from(participantExpectedValues)
.where(eq(participantExpectedValues.sportsSeasonId, sportsSeasonId));
}
/**
* Delete participant EV
*/
export async function deleteParticipantEV(
participantId: string,
sportsSeasonId: string
): Promise<void> {
const db = database();
await db
.delete(participantExpectedValues)
.where(
and(
eq(participantExpectedValues.participantId, participantId),
eq(participantExpectedValues.sportsSeasonId, sportsSeasonId)
)
);
// Reset participant EV to 0
await db
.update(participants)
.set({ expectedValue: "0", updatedAt: new Date() })
.where(eq(participants.id, participantId));
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
}
/**
User/chris/ev f1 framework (#93) * feat: EV simulation framework with F1 Monte Carlo simulator - Add EV snapshot tables (participant_ev_snapshots, team_ev_snapshots) and simulation_status column on sports seasons - Add ev-snapshot model with upsert and history query functions - Add simulator framework: types, bracket/F1/golf simulators, registry - F1 simulator: vig-removed ICM weighted draw (pre-season) + race-by-race Monte Carlo from current standings (in-season); per-position column normalization to prevent floating-point EV drift - Add admin simulate route and Run Simulation button on sports season page - Rework futures-odds admin page to save odds then run simulation in one action - Remove recalculate-probabilities route (superseded by simulate route) - Remove EV trend chart panel and associated DB queries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: map simulators to sports via simulatorType field Adds a `simulator_type` enum column to the `sports` table so each sport can be assigned a specific simulation algorithm rather than deriving it from the sports season's scoring pattern. - Add `simulatorTypeEnum` (f1_standings, indycar_standings, golf_qualifying_points, playoff_bracket) + `simulatorType` nullable column on `sports` table; migration 0037 - Rewrite simulator registry to key off `SimulatorType` instead of `ScoringPattern`; indycar_standings shares F1Simulator for now - `findSportsSeasonById` now returns `SportsSeasonWithSport` so callers have typed access to `sport.simulatorType` - Simulate and futures-odds actions read `sport.simulatorType`; guard fires before setting `simulationStatus: running` - Admin sport edit page gains a Simulator Type dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 15:34:31 -07:00
* Batch upsert multiple participant EVs within a single transaction.
* All records succeed or all fail together.
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
*/
export async function batchUpsertParticipantEVs(
inputs: CreateProbabilityInput[]
): Promise<ParticipantEV[]> {
User/chris/ev f1 framework (#93) * feat: EV simulation framework with F1 Monte Carlo simulator - Add EV snapshot tables (participant_ev_snapshots, team_ev_snapshots) and simulation_status column on sports seasons - Add ev-snapshot model with upsert and history query functions - Add simulator framework: types, bracket/F1/golf simulators, registry - F1 simulator: vig-removed ICM weighted draw (pre-season) + race-by-race Monte Carlo from current standings (in-season); per-position column normalization to prevent floating-point EV drift - Add admin simulate route and Run Simulation button on sports season page - Rework futures-odds admin page to save odds then run simulation in one action - Remove recalculate-probabilities route (superseded by simulate route) - Remove EV trend chart panel and associated DB queries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: map simulators to sports via simulatorType field Adds a `simulator_type` enum column to the `sports` table so each sport can be assigned a specific simulation algorithm rather than deriving it from the sports season's scoring pattern. - Add `simulatorTypeEnum` (f1_standings, indycar_standings, golf_qualifying_points, playoff_bracket) + `simulatorType` nullable column on `sports` table; migration 0037 - Rewrite simulator registry to key off `SimulatorType` instead of `ScoringPattern`; indycar_standings shares F1Simulator for now - `findSportsSeasonById` now returns `SportsSeasonWithSport` so callers have typed access to `sport.simulatorType` - Simulate and futures-odds actions read `sport.simulatorType`; guard fires before setting `simulationStatus: running` - Admin sport edit page gains a Simulator Type dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 15:34:31 -07:00
const db = database();
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
const results: ParticipantEV[] = [];
User/chris/ev f1 framework (#93) * feat: EV simulation framework with F1 Monte Carlo simulator - Add EV snapshot tables (participant_ev_snapshots, team_ev_snapshots) and simulation_status column on sports seasons - Add ev-snapshot model with upsert and history query functions - Add simulator framework: types, bracket/F1/golf simulators, registry - F1 simulator: vig-removed ICM weighted draw (pre-season) + race-by-race Monte Carlo from current standings (in-season); per-position column normalization to prevent floating-point EV drift - Add admin simulate route and Run Simulation button on sports season page - Rework futures-odds admin page to save odds then run simulation in one action - Remove recalculate-probabilities route (superseded by simulate route) - Remove EV trend chart panel and associated DB queries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: map simulators to sports via simulatorType field Adds a `simulator_type` enum column to the `sports` table so each sport can be assigned a specific simulation algorithm rather than deriving it from the sports season's scoring pattern. - Add `simulatorTypeEnum` (f1_standings, indycar_standings, golf_qualifying_points, playoff_bracket) + `simulatorType` nullable column on `sports` table; migration 0037 - Rewrite simulator registry to key off `SimulatorType` instead of `ScoringPattern`; indycar_standings shares F1Simulator for now - `findSportsSeasonById` now returns `SportsSeasonWithSport` so callers have typed access to `sport.simulatorType` - Simulate and futures-odds actions read `sport.simulatorType`; guard fires before setting `simulationStatus: running` - Admin sport edit page gains a Simulator Type dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 15:34:31 -07:00
await db.transaction(async (tx) => {
const now = new Date();
for (const input of inputs) {
const { participantId, sportsSeasonId, probabilities, scoringRules, source = "manual", sourceOdds } = input;
const sum = Object.values(probabilities).reduce((a, b) => a + b, 0);
if (sum > 1.01) {
throw new Error(
`Probabilities for participant ${participantId} cannot sum to more than 1.0. Current sum: ${sum.toFixed(4)}`
);
}
const expectedValue = calculateEV(probabilities, scoringRules);
const existing = await tx
.select({ id: participantExpectedValues.id })
.from(participantExpectedValues)
.where(
and(
eq(participantExpectedValues.participantId, participantId),
eq(participantExpectedValues.sportsSeasonId, sportsSeasonId)
)
)
.limit(1);
const baseValues = {
probFirst: probabilities.probFirst.toString(),
probSecond: probabilities.probSecond.toString(),
probThird: probabilities.probThird.toString(),
probFourth: probabilities.probFourth.toString(),
probFifth: probabilities.probFifth.toString(),
probSixth: probabilities.probSixth.toString(),
probSeventh: probabilities.probSeventh.toString(),
probEighth: probabilities.probEighth.toString(),
expectedValue: expectedValue.toString(),
source,
calculatedAt: now,
updatedAt: now,
};
let result: ParticipantEV;
if (existing.length > 0) {
const [updated] = await tx
.update(participantExpectedValues)
// Only overwrite sourceOdds if explicitly provided; preserve existing value otherwise
.set(sourceOdds !== undefined ? { ...baseValues, sourceOdds } : baseValues)
.where(eq(participantExpectedValues.id, existing[0].id))
.returning();
result = updated;
} else {
const [created] = await tx
.insert(participantExpectedValues)
.values({ participantId, sportsSeasonId, ...baseValues, sourceOdds: sourceOdds ?? null })
.returning();
result = created;
}
await tx
.update(participants)
.set({ expectedValue: expectedValue.toString(), updatedAt: now })
.where(eq(participants.id, participantId));
results.push(result);
}
});
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
return results;
}
User/chris/ev f1 framework (#93) * feat: EV simulation framework with F1 Monte Carlo simulator - Add EV snapshot tables (participant_ev_snapshots, team_ev_snapshots) and simulation_status column on sports seasons - Add ev-snapshot model with upsert and history query functions - Add simulator framework: types, bracket/F1/golf simulators, registry - F1 simulator: vig-removed ICM weighted draw (pre-season) + race-by-race Monte Carlo from current standings (in-season); per-position column normalization to prevent floating-point EV drift - Add admin simulate route and Run Simulation button on sports season page - Rework futures-odds admin page to save odds then run simulation in one action - Remove recalculate-probabilities route (superseded by simulate route) - Remove EV trend chart panel and associated DB queries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: map simulators to sports via simulatorType field Adds a `simulator_type` enum column to the `sports` table so each sport can be assigned a specific simulation algorithm rather than deriving it from the sports season's scoring pattern. - Add `simulatorTypeEnum` (f1_standings, indycar_standings, golf_qualifying_points, playoff_bracket) + `simulatorType` nullable column on `sports` table; migration 0037 - Rewrite simulator registry to key off `SimulatorType` instead of `ScoringPattern`; indycar_standings shares F1Simulator for now - `findSportsSeasonById` now returns `SportsSeasonWithSport` so callers have typed access to `sport.simulatorType` - Simulate and futures-odds actions read `sport.simulatorType`; guard fires before setting `simulationStatus: running` - Admin sport edit page gains a Simulator Type dropdown Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 15:34:31 -07:00
/**
* Save American odds for a batch of participants without touching probabilities or EV.
* Used by the futures-odds admin page to persist odds before running the full simulation.
*/
export async function batchSaveSourceOdds(
inputs: Array<{ participantId: string; sportsSeasonId: string; sourceOdds: number }>
): Promise<void> {
const db = database();
await db.transaction(async (tx) => {
const now = new Date();
for (const { participantId, sportsSeasonId, sourceOdds } of inputs) {
const existing = await tx
.select({ id: participantExpectedValues.id })
.from(participantExpectedValues)
.where(
and(
eq(participantExpectedValues.participantId, participantId),
eq(participantExpectedValues.sportsSeasonId, sportsSeasonId)
)
)
.limit(1);
if (existing.length > 0) {
await tx
.update(participantExpectedValues)
.set({ sourceOdds, updatedAt: now })
.where(eq(participantExpectedValues.id, existing[0].id));
} else {
// Insert a stub record — probabilities/EV will be filled in by the simulator
await tx.insert(participantExpectedValues).values({
participantId,
sportsSeasonId,
probFirst: "0",
probSecond: "0",
probThird: "0",
probFourth: "0",
probFifth: "0",
probSixth: "0",
probSeventh: "0",
probEighth: "0",
expectedValue: "0",
source: "futures_odds",
sourceOdds,
calculatedAt: now,
updatedAt: now,
});
}
}
});
}
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
/**
* Convert database record to ProbabilityDistribution
*/
export function toProbabilityDistribution(ev: ParticipantEV): ProbabilityDistribution {
return {
probFirst: parseFloat(ev.probFirst),
probSecond: parseFloat(ev.probSecond),
probThird: parseFloat(ev.probThird),
probFourth: parseFloat(ev.probFourth),
probFifth: parseFloat(ev.probFifth),
probSixth: parseFloat(ev.probSixth),
probSeventh: parseFloat(ev.probSeventh),
probEighth: parseFloat(ev.probEighth),
};
}
/**
* Recalculate EV for a participant with new scoring rules
* Keeps probabilities the same, only updates EV based on new scoring
*/
export async function recalculateEV(
participantId: string,
sportsSeasonId: string,
newScoringRules: ScoringRules
): Promise<ParticipantEV | null> {
const existing = await getParticipantEV(participantId, sportsSeasonId);
if (!existing) {
return null;
}
const probabilities = toProbabilityDistribution(existing);
const newEV = calculateEV(probabilities, newScoringRules);
const db = database();
const now = new Date();
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
const updated = await db
.update(participantExpectedValues)
.set({
expectedValue: newEV.toString(),
calculatedAt: now,
updatedAt: now,
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
})
.where(eq(participantExpectedValues.id, existing.id))
.returning();
// Sync recalculated EV to participants table
await db
.update(participants)
.set({ expectedValue: newEV.toString(), updatedAt: now })
.where(eq(participants.id, participantId));
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
return updated[0];
}
/**
* Recalculate EVs for all participants in a sports season
* Used when scoring rules change
*/
export async function recalculateAllEVsForSeason(
sportsSeasonId: string,
newScoringRules: ScoringRules
): Promise<number> {
const allEVs = await getAllParticipantEVsForSeason(sportsSeasonId);
await Promise.all(
allEVs.map((ev) =>
recalculateEV(ev.participantId, sportsSeasonId, newScoringRules)
)
);
return allEVs.length;
}