Rows run most recent ("Last") down to oldest — Home reads goals for then against, Away reads against then for, matching how away-side results are usually listed at the source
📊 Model Comparison
What PP predicts for the current fixture vs. what the Market expects, read from the de-vigged odds in the EV Finder card on the Verdict tab. The Market side needs odds entered there: all three 1X2 for the score picture and 1X2 tiles, Over/Under's own pair, BTTS's own pair.
1X2
Over / Under
Both Teams to Score
Top correct scores
1X2
Over / Under
Both Teams to Score
Top correct scores
Calculate probabilities on the Form tab to see your Match Verdict.
Match Verdict
💰 EV Finder
Positive-EV markets only, ranked highest first — compares the odds you enter in the Markets View card (Inputs tab) against PitchPulse Pro's own fair probability. EV% = (fair probability × odds) − 1.
🎯 Bookmaker De-Vig
Uses the same odds you typed in above — no separate entry needed. Strips the bookmaker's margin out of each group to reveal their true fair probabilities, then works out the exact scoreline picture those odds imply.
📋 Match Log
No matches logged yet. Calculate a fixture, then hit "Add to Match Log".
💡 How To Use PitchPulse Pro
Five recent matches per side, one league baseline, and PitchPulse Pro turns it into a full set of match probabilities in one tap.
Step 1 — League Baseline: Pick a Country and League to auto-fill the average home/away goals for that competition, or type your own. Defaults are Home 1.50 / Away 1.20 — a generic mid-table read. Switching Advanced back off restores these defaults. Playing at a neutral ground (cup final, playoff)? Check Neutral venue next to it to blend the two averages into one shared baseline instead of the usual home/away split.
Step 2 — Team names: Name your Home and Away side.
Step 3 — Recent form: Read off each team's last 5 matches (home matches only for the Home side, away matches only for the Away side) and enter each one's goals-for / goals-against in order, most recent match on top (row "Last") down to the oldest (row 5). No averaging or dropping needed — the model weights recent matches more heavily on its own.
Step 4 — Elo rating (optional): Each team-name row has an Elo pill. Leave both blank for a form-only read, or fill in both (e.g. from clubelo.com or eloratings.net) to nudge λ toward the stronger side — useful when the two teams aren't from the same league or division, so their form numbers alone don't capture the quality gap. It only takes effect once both sides have a value.
Step 5 — Calculate: Hit "⚡ Calculate Probabilities". The Model Comparison tab's PP column fills in with 1X2, Over/Under, BTTS, and the top correct scores; the stats strip up top shows Lambdas (H/A), PitchPulse's own top correct score (PP Exp. Score), the model's Best Bet — the market that wins the probability/excess hybrid ranking among markets that clear 50% raw probability, shimmering gold whenever all 3 of the top 3 most probable correct scores agree with it — and Top EV, the single positive-EV market ranked highest once odds are entered on the Inputs tab.
Step 6 — Track it: On the Verdict tab, hit "+ Add to Match Log" to save the pick, then fill in the real final score once the match is played. Signal Check and Calibration build up as your log grows. The log archives automatically each day — use 🗄 Archive or the ⬇ CSV/JSON buttons to export for analysis.
📊 Where To Get Your Team Stats
PitchPulse Pro doesn't pull live data — you feed it the numbers, which keeps it fast, offline-friendly, and usable for any league on earth.
FlashScore.com & WhoScored.com — filter a team's results by venue (home or away), then read off the score of each of the last 5, most recent first, straight into the 5 rows.
Official league sites — most top-flight league sites (e.g. premierleague.com, laliga.com) list a filterable fixture history per club.
FBref.com — free match logs for almost every senior league worldwide, filterable by venue.
Use the Country/League selector on the Form tab first — it auto-fills from a built-in database. If your league isn't listed, search "[league name] goals per game average" for the current season, or just leave the defaults (1.50 home / 1.20 away) as a generic baseline.
⚙️ Good To Know
Inputs, theme, and the Match Log are saved in your browser's local storage only. There's no backend and nothing is ever sent anywhere.
Served over HTTPS, PitchPulse Pro installs as a standalone app. A one-time "Install" toast appears on your first visit — if you dismissed it or missed it, use your browser's own install option (usually in its menu, or an icon in the address bar) any time. Once installed, it keeps working offline.
Toggle the sun/moon icon in the header any time — your choice is remembered on next visit.
❓ Methodology & FAQ
A recency-weighted Poisson model. Each team's last 5 matches (home-only for the Home side, away-only for the Away side) are combined with the most recent match weighted fully and each match further back weighted a bit less (a 0.85 decay per match back), so one outlier scoreline can't dominate the read on current form, and a swing 2-3 matches ago still registers, just less than last weekend's result. That weighted form becomes attack/defence strength relative to the league baseline, which converts into expected goals (λ) for each side — from there, independent Poisson distributions build the score matrix, with an adaptive Dixon-Coles correction applied to the low-scoring cells before it's renormalized. Every market on the Model Comparison tab's PP column is derived from that final matrix.
Independent Poisson treats home and away goals as statistically unrelated, but real match goals are mildly coupled by things like game state and tactics — and that shows up specifically in the low-scoring cells. The Dixon & Coles (1997) tau correction adjusts exactly those four cells (0–0, 1–0, 0–1, 1–1) before the matrix is renormalized. Rather than a single fixed literature constant, ρ here is adaptive: it scales with the league's own average goals, growing more negative for defensive/low-scoring leagues and lighter for high-scoring ones — anchored so a league at the app's default baselines (1.50/1.20) reproduces the original literature-standard ρ = -0.13 exactly. It's derived purely from the League Baseline inputs above (never from either team's own GF/GA form) and runs automatically in the background — there's nothing to configure.
A plain average treats a result from 5 matches ago the same as last weekend's. Recency weighting lets the model react faster to a team's current form — a hot or cold streak shows up sooner. Each match's goals are also capped before weighting (4 at this league's default scoring rate, tighter for low-scoring leagues and looser for high-scoring ones), so a freak scoreline (5-5, 6-1) is read as if it were 4-4 / 4-1 — this matters more than it did under the old trim, since recency weighting alone won't catch two blowouts that both happen to be recent.
HomeAttack = Home team's recency-weighted home goals-for ÷ league average home goals. HomeDefence = weighted home goals-against ÷ league average away goals. Away strengths mirror this using the team's away form. Before that division, each weighted sum is blended with a few "average" matches worth of the league baseline — so a genuine 0 across your last 5 doesn't collapse to a guaranteed clean sheet, while a sum already near the league average is barely affected. That blend strength is adaptive: goals follow a Poisson distribution, so a small sample is proportionally noisier in a low-scoring context than a high-scoring one — a defensive league (say 0.8 goals/game) gets pulled toward the baseline more firmly (around 46% weight) than a high-scoring one (say 2.5 goals/game, around 33%). At the app's own default baselines (1.50 / 1.20) it lands close to the original ~40% weight. λHome = HomeAttack × AwayDefence × league average home goals, and λAway = AwayAttack × HomeDefence × league average away goals. A strength of 1.00× means exactly league average; 1.25× means 25% above it.
1.50 home goals, 1.20 away goals per match — a generic mid-table baseline. Raw goal counts only mean something relative to context: 1.8 goals a game is modest in a high-scoring league and strong in a defensive one. Pick your league from the Country/League selector to auto-fill the real numbers, or enter them yourself for the sharpest read.
It's for a cup final, playoff, or any fixture played at a ground that belongs to neither team — check it and the model blends the home/away league averages into one shared baseline for both sides instead of the usual Home 1.50 / Away 1.20 asymmetry, since neither side actually gets a home-turf edge. It's a per-fixture flag: it's saved and restored with the rest of your inputs like team names and GF/GA, but ↺ Clear Fields resets it to unchecked, same as the Elo pills — the League Averages themselves are left alone by Clear Fields since those are a standing league default, not a per-match setting.
Best Bet scores all seven direct markets (Home/Draw/Away, Over/Under 2.5, BTTS Yes/No) by a hybrid score — 80% raw probability, 20% edge over the league baseline (what a coin-flip fixture in this league, using only the league's home/away goal averages and no team form, would show for that same market) — but only markets that clear 50% raw probability are eligible to win at all. Before the 80/20 blend, both figures are scaled onto the same 0–100 footing first — probability against its eligible range (50%→100%), edge against a fixed 30-point ceiling — so the 20% weight on edge is a real share of the score rather than getting swamped by probability's much larger raw range. That floor is a hard gate, not a tiebreaker: a market that's more likely to lose than win can never be Best Bet, no matter how large its edge over baseline is. Above 50%, every eligible market is ranked on that same flat hybrid score — there's no separate band or stricter bar for markets closer to 50% vs. markets deep into confident territory. When the winner beats the outright probability leader on a genuinely large excess edge (15+ points), it's flagged 🔥 Value Signal in the Verdict card — that's a badge, not a gate: it doesn't need to clear any extra bar to win, the flag just makes the pick's shape visible rather than letting a probability-defying pick look identical to a safe favorite win. If no market at all clears 50% — possible on a tight three-way 1X2 split, though Over/Under and BTTS are near-complementary pairs so one side of each almost always clears it — Best Bet falls back to the single highest-probability market instead, and the Verdict card says so explicitly (↩ Fallback). Draw is the market this floor hits hardest — it's genuinely rare for Draw to clear 50% outright in a 3-way split, so when Draw is still this fixture's single most likely result, just not eligible, the Verdict card says so explicitly too (⚖ Draw Watch) rather than Draw silently losing out. Separately, the top 3 most probable correct scores from the match's score matrix each imply a result, an Over/Under 2.5 read, and a BTTS read at once — e.g. a 2–1 implies Home Win + Over 2.5 + BTTS Yes — and cast a probability-weighted vote on those three axes. That vote never decides the winner; it's a corroborating signal only, sorted into a confirmation tier — 3/3 or 2/3 scorelines land on the pick ("AGREE", green), 1/3 is "NEUTRAL" (amber), 0/3 is "DIVERGE" (red, an explicit divergence warning) — shown as a badge next to the pick and referenced in the reason text below it. Separately from that scoreline-count tier, if another market is backed by meaningfully more vote-weight than the winner, the reason text flags that explicitly too rather than staying silent, and if the winner wasn't the outright probability leader (or the pick was a no-qualifiers fallback), a separate note says so and shows the margin. The "Why this pick?" breakdown under the Verdict card lists all seven markets with their probability, edge over baseline, and top-3 vote weight — markets at or below the 50% floor are dimmed and marked with a ⛔ (ineligible to win, shown for context), and markets whose edge barely clears (or doesn't clear) the baseline are separately marked with a ↓ (thin edge, informational only) — neither marker removes a row from the table.
It's a caution flag, separate from the divergence warning above. Best Bet's excess figure comes from a recency-weighted last-5 sample blended with the league-average baseline (see "How are λ calculated?" below) — a small excess built mostly on that baseline prior, rather than the form you actually entered, is weaker evidence than the same-sized excess would be with a less-shrunk sample. It only appears when both are true: the winning market's edge over baseline is small, and this league's shrink blend leans mostly on the prior rather than the entered matches (a defensive/low-scoring league shrinks harder than an attacking one — see the adaptive shrinkage note above). A large excess is trusted regardless of shrinkage, since it had to survive the pull toward baseline to still be large. This flag doesn't change the pick itself — it's purely informational, telling you the confidence behind this particular edge is thinner than usual.
It shimmers when the pick is unanimous — all 3 of the top 3 correct scores agree on it. The Verdict tab always shows a confirmation badge next to the pick — 3/3 or 2/3 scorelines backing it shows green "AGREE", 1/3 shows amber "NEUTRAL", and 0/3 shows red "DIVERGE" (an explicit warning that none of the model's own top-3 scorelines land on the pick) — so you can see the strength of that corroboration at a glance even when it isn't full 3/3. Only the exact 3/3 case shimmers; 2/3 is still shown as agreement, just not the unanimous version of it.
When a Best Bet is confirmed this way and you log it, the Match Log entry itself is marked with a gold "✨ CONFIRMED" badge and the same shimmer, so it stands out from the rest of your log.
The Home card lists each match as goals for, then goals against. The Away card lists the same pair the other way round — goals against, then goals for — to match how away-side results are commonly listed at the data source. It's a display-order swap only: the fields are still labeled and still feed the model correctly either way, so there's nothing to double-check beyond typing the numbers in as your source presents them.
Best Bet answers "which market does the model trust most" — it never looks at odds. The EV Finder answers a completely different question: "given the odds I'm actually being offered, is this a good bet?" You type in the decimal odds for each of the same seven markets, and it compares them against the model's own fair probability: EV% = (fair probability × odds) − 1. Positive EV means the price is better than the model's fair value; negative means it isn't — regardless of whether that market happens to be the current Best Bet. A market can be the model's top confidence pick and still be poor value if the odds are short, and a market the model rates lower can still be +EV if the price is generous. Odds you type in persist while you keep recalculating the same fixture, but aren't saved to local storage — a fresh page load starts blank.
Bookmaker De-Vig reads those same odds a different way, running quietly in the background rather than as its own visible card: instead of comparing them to the model, it strips the bookmaker's own margin (the "overround") out of each group of odds — 1X2, Over/Under 2.5, BTTS — to reveal the bookmaker's true fair probabilities. A group needs every one of its odds filled in before it can be de-vigged, since the margin can't be isolated from a partial set. Once the three 1X2 odds are in, it goes a step further and searches for the λHome/λAway pair whose own score matrix would reproduce those de-vigged prices, surfacing the bookies' own implied most-likely scorelines — the same kind of read the Verdict tab gives for the model's numbers, but built entirely from the market's, so the Model Comparison tab's "Market Implied" column can show where the model and the market disagree on the shape of the match, not just the price of one outcome.
A 🟢/🟡/🔴/⚪ tier for the specific bet category behind your current Best Bet (e.g. "Home Win"), computed from your own logged history using a Wilson score interval lower bound rather than a raw hit rate — so a 2-for-2 record doesn't get overrated. It needs 5+ resolved picks in that category before it rates anything; below that it shows ⚪ UNPROVEN.
The 📊 Calibration button opens a running Brier score (0–1 scale, lower is better) split by outcome — Home Win, Draw, Away Win, Over 2.5, Under 2.5, BTTS Yes, BTTS No — so you can see which specific side is improving or slipping instead of one blended number per market. Where a market has 5+ resolved picks on each side, it also splits Home Win / Draw / Away Win into an Elo ON vs. Elo OFF comparison — a higher Elo ON score means the Elo adjustment is hurting that market's calibration, a lower one means there's room to lean on it more. This is independent of Signal Check: it grades the model's raw probabilities directly, rather than just whether the top pick won.
Hit "+ Add to Match Log" on the Verdict tab after calculating to save a prediction. Once the match is played, enter the real final score — it's marked ✅ HIT or ❌ MISS against the Best Bet pick, and rolls into the Hit Rate shown in the stats bar. Best Bet, the Match Log, and the Model Comparison tab all grade against the same fixed Over/Under 2.5 line, so a logged "Over 2.5" always stays comparable.
It archives automatically. The first time the app checks after the calendar date changes — on load, or when you come back to an open tab — everything logged "today" moves into a dated archive bucket, and the Match Log starts fresh for the new day's fixtures. Nothing is deleted: open 🗄 Archive to browse every past day (with its HIT/MISS split) and export it as CSV or JSON. The ⬇ CSV / ⬇ JSON buttons next to "Clear all" export everything at once — today's log plus the full archive — as one file for spreadsheet or offline analysis.
No. Everything — inputs, theme, your Match Log, and its archive — lives in your browser's local storage. There's no backend and no data ever leaves your device unless you tap an export button yourself.
Yes — served over HTTPS, PitchPulse Pro is an installable PWA. A one-time "Install" toast appears on your first visit; if you dismissed it or it's not your first visit, use your browser's own install option (usually in its menu, or an icon in the address bar) instead. Either way, it'll keep working offline afterward.
PitchPulse Pro produces a probability estimate from recent form — not a guarantee. It doesn't account for injuries, motivation, or opponent quality beyond the last-5-match window (an Elo rating pair, entered on the Team Form card, can correct for a cross-league quality gap, but there's no read on things like a relegation six-pointer or a dead rubber), and Signal Check / Calibration are only as meaningful as how many matches you've logged and resolved. Please gamble responsibly. — by Victor Korir
Questions about the model, licensing, or custom builds? Reach out directly: