Methodology

How F1 Finantics predicts fantasy points and optimizes lineups.

Pipeline Overview

Polymarket Odds+Practice Results+Custom SignalsExpected PositionsGridRival ScoringMIP Optimizer

We combine real-money betting market probabilities with live practice session performance and custom signals (track affinity, weather, DNF risk) to estimate each driver's expected finishing position, then convert those positions into predicted GridRival fantasy points and feed them into a lineup optimizer.

1. Polymarket Odds

We fetch live odds from Polymarket for three markets per race: Race Winner, Pole Position, and Constructor Winner. These are real-money prediction markets, meaning they reflect the aggregate wisdom of thousands of bettors with skin in the game.

Normalization

Raw market prices include overround (they sum to >100%). We normalize by dividing each probability by the total sum, so all probabilities add to exactly 100%.

P(driver) = raw_price(driver) / sum(all_raw_prices)

2. Bradley-Terry Expected Position

Win probabilities tell us who is likely to win, but we need expected finishing positions for scoring. We use the Bradley-Terry pairwise comparison model to convert win probabilities into a full position distribution.

Formula

For each pair of drivers (i, j), the probability that j finishes ahead of i is:

P(j beats i) = P_win(j) / (P_win(i) + P_win(j))

Then the expected position for driver i is:

E[Position_i] = 1 + Σ(j≠i) P(j beats i)

Intuitively: your expected position equals 1 plus the number of drivers expected to finish ahead of you. This is applied separately for race and qualifying markets to produce E[Race] and E[Qual].

3. Practice Session Adjustment

Live practice results from FP1, FP2, and FP3 (via the OpenF1 API) are blended into the expected race position to capture weekend-specific form that the betting markets may not have fully absorbed.

Practice Weighting

Each practice session is weighted by recency and importance:

25%

FP1

25%

FP2

50%

FP3

If FP3 hasn't happened yet, FP1 and FP2 are normalized to 50/50.

Blending Formula

The practice-weighted position is blended with the odds-based expected position:

E[Race]_final = 0.75 × E[Race]_odds + 0.25 × FP_weighted_pos

75% weight on market odds, 25% on practice performance. Markets are generally more predictive, but practice data captures setup changes, conditions, and car upgrades.

3b. Custom Signal Adjustments

Three additional signals are computed and blended into the expected race position to capture factors that betting markets may underweight. Each signal produces a position adjustment that shifts the driver's expected finishing position up or down.

Track Affinity (15%)

Compares a driver's average finish on the current circuit type (street / permanent / hybrid) versus their overall average across all circuit types.

affinity = overall_avg - type_avg

Positive = driver performs better on this track type. Uses 2024–2025 historical data, minimum 3 races on the circuit type.

Wet Edge (10%)

Combines a driver's rain performance with the forecast rain probability for race day. “Wet performance” is proxied by how much a driver overperforms in chaotic races (where >25% of the field retired).

wet_edge = wet_delta × rain_prob

Rain probability from Open-Meteo 16-day forecast, targeted to the actual race date. Only activates when rain is forecast.

DNF Risk (10%)

Constructor mechanical retirement rate from the last 16 results (8 races × 2 drivers). Only “Retired” and “Did not start” count as DNFs — lapped/disqualified cars are treated as finishers.

impact = dnf_rate × (field_size / 2)

Pushes the expected position back proportionally to how often the team's cars fail to finish.

Upgrade Boost (10%)

Counts upgrade-categorized news articles per constructor from the last 14 days. Teams bringing new parts tend to see step improvements that markets may not have priced in yet.

boost = min(upgrade_articles, 3)

Each article = 1 position of raw signal, capped at 3. Articles are auto-categorized from Autosport and Formula1.com RSS feeds using keyword matching (floor, wing, diffuser, etc).

Combined Adjustment

adjustment = 0.15 × track + 0.10 × wet_edge + 0.10 × dnf + 0.10 × upgradeE[Race]_final = E[Race]_blended + adjustment

Negative adjustment = driver moves up (better). The adjustment is applied after practice blending.

Correlation Monitor

Every signal computation is logged per-driver with the Polymarket baseline and (after the race) the actual result. This lets us track whether signals improve or hurt prediction accuracy over time.

No correlation data yet — needs actuals backfilled after races complete.

4. GridRival Fantasy Scoring

Expected positions are converted to predicted fantasy points using the GridRival scoring rules. Points are awarded for:

Driver Points

  • Race Position — lookup table by rounded E[Race]
  • Qualifying Position — lookup table by rounded E[Qual]
  • Completion Bonus — assumed 90%+ completion
  • Overtake Points — (E[Qual] - E[Race]) × pts/position gained
  • Beat Teammate — bonus based on position gap to teammate

Constructor Points

  • Race Points — sum of both drivers' constructor race scoring
  • Qualifying Points — sum of both drivers' constructor quali scoring

Driver Total

Total = Race_pts + Qual_pts + Completion_pts + Overtake_pts + Beat_Teammate_pts

Talent Multiplier

One eligible driver can be designated as a "Talent" pick, which multiplies their points by the talent multiplier (configured per season in GridRival scoring rules).

5. Lineup Optimizer (MIP)

We use a Mixed-Integer Programming (MIP) solver (PuLP/CBC) to find the optimal lineup of 5 drivers + 1 constructor within a $100M budget cap.

Objective Functions

  • Max Points — maximize total predicted points
  • Max Value — maximize points per dollar spent
  • Balanced — weighted combination of points and value

Constraints

Exactly 5 drivers selectedExactly 1 constructor selectedTotal cost ≤ $100M budget capAt most N drivers per constructor (optional)Locked drivers: x[id] = 1Excluded drivers: x[id] = 0

The solver tries every possible talent designation (one per eligible driver) and returns the top N lineups ranked by the chosen objective.

6. Pricing Assessment

Each driver's price is evaluated against the average points-per-dollar across all drivers. This tells you if a driver is overpriced, underpriced, or fairly priced relative to their predicted output.

Pts/$ = predicted_points / priceDeviation = (driver_pts_per_$ - avg_pts_per_$) / avg_pts_per_$ × 100%
  • Under — deviation > +10% (good value)
  • Over — deviation < -10% (overpriced)
  • Fair — within ±10%

Data Sources

Polymarket

Real-money betting odds for race winner, pole, constructor

OpenF1 API

Live practice session lap times and positions

Jolpica API

Historical race results, qualifying, and schedule data