Complete strategy and build spec for the Gaming & Esports trading desk. Covers match markets, tournament markets, and meta/awards/Twitch markets on Kalshi. An engineer should be able to read this and know exactly what to build, what data to collect, and how to find edges.
Same as all sports desks. We trade on Kalshi prediction markets. We find mispriced lines by comparing Kalshi prices to sharp reference fair values. For esports, our anchor is a Sharp Composite Line (SCL) built from multiple sharp books, with Pinnacle as the heaviest weight.
$56.5M+ in settled Kalshi esports volume. This is real money:
| Title | Settled Volume | Avg/Market | Max Single Market |
|---|---|---|---|
| FIFA | $17.4M | $87K | $775K |
| CoD | $8.2M | $41K | $146K |
| LoL | $8.2M+ | $41-70K | $626K |
| CS2 | $6.1M | $31K | $339K |
| Valorant | $5.5M | $27K | $208K |
| R6 | $779K | $4K | $74K |
| Others | <$100K each | — | — |
Pinnacle is the sharpest generalist book for esports but WEAKER than for traditional sports — wider vig (2.5-4% vs 1.5-2%), lower limits, slower line movement on tier-2/3 events. Their esports traders are generalists covering dozens of titles.
We do NOT rely on Pinnacle alone. We build a weighted no-vig composite across multiple sharp books:
SCL = w_pinnacle * pinnacle_novig + w_bet365 * bet365_novig + w_ggbet * ggbet_novig + ...
Weights based on historical closing line accuracy and volume/limit size per book, per title. Pinnacle gets ~50% weight, others split the rest.
Anchor hierarchy by market type:
| Market Type | Primary Anchor | Secondary |
|---|---|---|
| CS2 match winner | Pinnacle + bet365 | GG.bet, Betway |
| LoL match winner | Pinnacle | Betway, GG.bet |
| Valorant match winner | Pinnacle | GG.bet |
| Dota 2 match winner | Pinnacle + bet365 | — |
| CoD match winner | Pinnacle | BetOnline |
| FIFA match winner | Pinnacle (thin) | Model-only for some formats |
| Map winner / totals | Pinnacle (when available) | Model-only |
| Tournament outrights | Pinnacle | Model + bracket simulation |
| Game Awards / Steam / Twitch | No book anchor | Pure model + sentiment |
Data access:
Structural reasons edges exist — larger and more persistent than traditional sports:
Roster instability — Esports rosters change mid-season constantly. A CS2 team benching their AWPer is equivalent to losing a starting QB, but books are slow to reprice (12-24 hours). Stand-ins for online matches often not repriced at all.
Patch meta shifts — Game patches every 2-3 weeks fundamentally change agent/champion/weapon viability. Books price off historical data that is immediately stale post-patch. The 2-6 hour window after patch notes publish is a recurring edge.
Map pool asymmetry — CS2 and Valorant matches are best-of-N across maps. Teams have wildly different win rates per map (85% on Mirage, 40% on Ancient). Map veto is predictable, and the actual maps played dramatically shift true probabilities vs. the aggregate line.
Online vs. LAN gap — Some teams are "onliners" who overperform online and underperform at LAN (and vice versa). Documented but rarely modeled quantitatively by books.
Regional strength miscalibration — Cross-regional matches are systematically mispriced. Books anchor on regional form without adjusting for meta differences and style matchups.
Low-information markets — CoD, R6, Overwatch, Rocket League have minimal sharp action. Fattest edges but also lowest Kalshi volume.
Information speed — Scrim results, roster leaks, player health/motivation issues travel through Discord, Twitter, and Reddit hours before books react.
Casual bettor dominance on Kalshi — Favorites systematically overpriced, underdogs underpriced. Verify this with our $56.5M settled data.
CS2 (highest priority after FIFA)
LoL ($8.2M+ volume)
Valorant ($5.5M)
CoD ($8.2M — surprisingly high volume)
FIFA ($17.4M — volume leader)
Dota 2
R6 / Overwatch / Rocket League
No book anchor exists — we ARE the model.
Game Awards:
Steam Rankings:
Twitch Sub Counts / Streamer Bans:
| # | Source | What | Access | Priority |
|---|---|---|---|---|
| 1 | Pinnacle esports | ML, spreads, totals — sharpest esports lines | Stealth Chromium scraper (PRIMARY) | P0 |
| 2 | The Odds API | Multi-book odds (Pinnacle + bet365 + Betway) | API key (BACKUP when scraper fails) | P0 |
| 3 | GG.bet | CS2/LoL alternative anchor, early line mover | Scrape | P1 |
| 4 | Kalshi esports prices | Our target market — 109 series across all titles | API key (have it), every 30min | P0 |
| # | Source | What | Access | Frequency |
|---|---|---|---|---|
| 5 | HLTV.org | Team/player stats, rankings, map win rates, H2H, roster changes, event info | Scrape (no API) | Real-time |
| 6 | HLTV Rankings | World team rankings (updated every Monday) | Scrape | Weekly |
| 7 | HLTV Transfers | Roster changes feed — CRITICAL, check hourly | Scrape /transfers page | Hourly |
| 8 | FACEIT API | CS2 Elo, match history, hub stats for pro players | Free API (key required) | Real-time |
| # | Source | What | Access | Frequency |
|---|---|---|---|---|
| 9 | Oracle's Elixir | All pro match data — GD@15, vision, objectives, player stats | Free CSV downloads | Weekly CSV, real-time site |
| 10 | Riot Games LoL API | Official match data, player stats, live game data | Free API (developer account) | Real-time |
| 11 | gol.gg | Champion stats, team stats by patch | Scrape | Per patch |
| 12 | LoL Patch Notes | Champion buffs/nerfs, meta shifts | Scrape official site | Every ~2 weeks |
| # | Source | What | Access | Frequency |
|---|---|---|---|---|
| 13 | VLR.gg | All pro results, player stats, agent comps, map win rates, H2H | Scrape | Real-time |
| 14 | Riot Valorant API | Official match data | Free API | Real-time during events |
| 15 | Valorant Patch Notes | Agent/map changes | Scrape official site | Every ~2 weeks |
| # | Source | What | Access | Frequency |
|---|---|---|---|---|
| 16 | OpenDota API | All pro match data, hero stats, player stats | Free REST API (no auth) | Real-time |
| 17 | Dotabuff | Hero win rates, player stats, draft analysis | Scrape | Real-time |
| # | Source | What | Access | Frequency |
|---|---|---|---|---|
| 18 | Breaking Point / calstats.gg | CoD CDL player/team stats per mode | Scrape | Per match |
| 19 | CDL Official | CoD standings, schedule, scores | Scrape | Per match |
| 20 | Octane.gg | Rocket League stats, match results | Free REST API | Real-time |
| 21 | Siege.gg | R6 pro league stats, operator usage | Scrape | Per match |
| # | Source | What | Access | Frequency |
|---|---|---|---|---|
| 22 | Liquipedia | All esports: rosters, results, brackets, prize pools, transfers | MediaWiki API / scrape | Real-time (wiki) |
| 23 | SBR multi-book odds | Consensus lines from 10+ books | Scrape (already built) | 10AM/2PM/6PM |
| # | Source | What | Access | Frequency |
|---|---|---|---|---|
| 24 | SteamDB | Player counts, update history, depot changes | Scrape | Hourly |
| 25 | Steam Charts | Historical player counts, trends | Scrape | Hourly |
| 26 | TwitchTracker | Channel stats, sub counts, viewer history | Scrape | Daily |
| 27 | SullyGnome | Twitch channel analytics, game viewership | Scrape | Daily |
| 28 | Metacritic / OpenCritic | Aggregated critic scores | Scrape | Per release |
| 29 | Game Awards Archive | Historical nominees/winners, voting patterns | Scrape | Annual |
| # | Source | What | Access | Frequency |
|---|---|---|---|---|
| 30 | Twitter/X | Roster announcements, scrim rumors, player sentiment | Grok API (have key) for analysis | Real-time |
| 31 | r/GlobalOffensive, r/ValorantCompetitive, r/leagueoflegends — scrim leaks, community sentiment | Reddit API | Real-time | |
| 32 | Patch note RSS | Automated detection of new patches for all titles | RSS/webhook on official sites | Per patch |
| 33 | SteamDB Depot Monitor | Detect incoming patches before official announcement | Scrape depot pages | Hourly |
Predicts which maps will be played in a best-of-N series based on historical veto patterns.
Inputs:
Algorithm (Monte Carlo, 10,000 runs):
For each simulation:
1. Simulate Team A ban: weighted random from their ban frequency distribution
2. Simulate Team B ban: weighted random from their ban frequency distribution
3. Team A picks best remaining map (by opponent-adjusted win rate)
4. Team B picks best remaining map
5. Decider is the remaining map
Output:
P(map_i played) for each map
P(A wins match) = avg across sims of product of map-specific win probs
Edge: Generic match odds treat all maps equally. A team that is 90% to win Mirage and 30% to win Nuke has a VERY different Bo3 probability depending on which maps are played. Expected edge: 3-8% on map-dependent markets.
Quantifies expected impact of a roster change before books reprice.
RCIS = (player_out_rating - player_in_rating) * role_weight * synergy_penalty
Role weights (CS2):
IGL (in-game leader): 1.5
AWPer: 1.3
Entry: 1.0
Lurk: 0.9
Support: 0.8
Role weights (LoL):
Mid: 1.3, Jungle: 1.2, ADC: 1.1, Top: 0.9, Support: 0.8
Role weights (Valorant):
IGL: 1.5, Duelist: 1.2, Sentinel: 1.0, Controller: 1.0, Initiator: 0.9
synergy_penalty = max(0.5, 1 - (days_since_roster_change / 90))
Trading signal: New rosters are overbet by the public (name recognition). The synergy penalty means teams underperform their "on paper" rating for ~6-8 weeks. Fade new rosters in their first 15-20 matches.
Measures how much a patch changes a team's expected performance.
PSS(team, patch) = SUM over agents/champions used by team:
usage_rate * nerf_magnitude * team_dependency
team_dependency = (team_winrate_with_agent - team_winrate_without) / team_overall_winrate
nerf_magnitude: 0.0 (minor tweak), 0.5 (significant), 1.0 (major rework)
LoL-specific: Use solo queue win rate delta in first 7 days post-patch as a leading indicator of pro meta shift. Solo queue reacts faster than pro play.
Trading window: 2-6 hours post-patch before sharp bettors force books to reprice.
OLAF(team) = team_LAN_winrate / team_online_winrate
Apply as multiplier when match is LAN:
adjusted_prob = base_prob * OLAF(A) / (base_prob * OLAF(A) + (1-base_prob) * OLAF(B))
Additional modifiers:
Travel distance > 8 hours: -0.02
First international event of split: -0.015
Timezone difference * hours_since_arrival factor:
>72h: 0.2 impact, 24-72h: 0.6 impact, <24h: 1.0 impact
Typical OLAF range: 0.80 (onliner) to 1.25 (LAN specialist). HLTV tracks LAN vs online results separately.
Foundation layer for all predictions.
K_factor adjustments:
LAN matches: K * 1.3 (more informative)
Online matches: K * 1.0
Tier-1 events: K * 1.2
Tier-3 events: K * 0.7
Within 14 days of roster change: K * 0.5
First 7 days of new patch: K * 0.7
Elo decay:
No matches in >21 days: regress toward mean by 5% per week of inactivity
Map-level Elo:
Maintain SEPARATE Elo per map per team (CS2: 7 maps * N teams)
Match Elo is weighted average of map Elos based on predicted maps played
pistol_adj = (team_pistol_winrate - 0.50) * 0.12
(each 1% pistol WR above 50% = approx 0.12% match WR — empirically derived)
eco_rating = (team_eco_round_winrate - league_avg) / league_std
Teams with elite pistol rounds (>55%) are systematically underpriced. Pistol wins create compounding economic advantage through the entire half.
early_game_score = w1 * first_blood_rate + w2 * first_tower_rate
+ w3 * first_dragon_rate + w4 * avg_gold_diff_15min / norm
Weights (LoL): w1=0.15, w2=0.25, w3=0.20, w4=0.40
Weights (Dota): w1=0.10, w2=0.20, w3=0.15, w4=0.55
For "tournament winner" Kalshi outright markets.
sim_tournament(teams[], format, n=100000):
For each simulation:
Simulate each match using Elo-derived win_prob(A, B, format)
Account for: upper/lower bracket, fatigue tax (-0.5% per extra match), rest days
Return: P(X wins tournament) for each team
Compare output to Kalshi outright prices -> find mispriced teams
Measures a team's historical ability to adapt to patches.
PMF(team) = Avg(WinRate_post_patch - WinRate_pre_patch) across last N major patches
Teams with high positive PMF: undervalued in early days of new meta
Teams with negative PMF: overvalued when meta shifts
Pro teams practice in private scrimmages. Results leak on Twitter, Reddit, Discord. Build keyword-monitoring bot:
Patches drop at known times (Riot: every other Tuesday). The 2-6 hour window between publication and book adjustment is a recurring edge.
Before patches go live, game files update in Steam depots. SteamDB tracks this in real-time.
Many pros have public Steam profiles and FACEIT accounts.
Monitor Liquipedia roster pages and HLTV match pages for lineup changes within 24 hours of a match. Stand-in for a star player often not repriced at all for online tier-2 matches.
Analyze our $56.5M in settled Kalshi esports volume:
When maps are added/removed from competitive pool:
CS2: KXCS2GAME, KXCS2MAP, KXCS2TOTALMAPS, KXCS2GAMES, KXCS2MAPWINNER Valorant: KXVALORANTGAME, KXVALORANTMAP, KXVALORANTGAMETEAMVSMIBR LoL: KXLOLGAME, KXLOLGAMES, KXLOLMAP, KXLOLTOTAL, KXLOLTOTALMAPS Dota 2: KXDOTA2GAME, KXDOTA2MAP CoD: KXCODGAME Overwatch: KXOWGAME Rocket League: KXRLGAME R6: KXR6GAME, KXR6MAP FIFA: KXFIFAGAME, KXFIFASPREAD, KXFIFATOTAL
CS2: KXCS2, KXCS2QUALIFY, KXCS2QUALIFIER, KXCS2QUALIFIERS, KXCS2IEMCOLOGNE Valorant: KXVALORANT, KXVALORANTMASTERSFINALS LoL: KXLOL1STTIMEWIN Dota 2: KXDOTA2, KXTORONTOULTRACHAMPIONSHIP CoD: KXCOD PUBG: KXPUBG, KXPUBGGC Overwatch: KXOVERWATCH R6: KXR6 FIFA: KXFIFAADVANCE, KXFIFAUSPULL, KXFIFAUSPULLGAME Fortnite: KXFORTNITEPROAM
KXEWCCS2, KXEWCVALORANT, KXEWCDOTA2, KXEWCLEAGUEOFLEGENDS, KXEWCCHESS, KXEWCCHESS2025, KXEWCAPEXLEGENDS, KXEWCPUBGBATTLEG, KXEWCFREEFIRE, KXEWCMLBB, KXEWCMOBILELEGENDSBBWOMENS, KXEWCHONOROFKINGS, KXEWCFATALFURY, KXEWCCALLOFDUTYBLOPS6, KXEWCEASPORTSFC, KXEWCRAINBOW6SEIGE, KXEWCRB6, KXEWCRSS, KXEWCTEAMFIGHTTACTICS, KXEWCSTARCRAFTII, KXEWCRENNSPORT
KXGAMEAWARDS, KXGAMEAWARDSBET, KXGAMEAWARDSBEA, KXGAMEAWARDSBGD, KXGAMEAWARDSBAD, KXGAMEAWARDSBP, KXGAMEAWARDSBSM, KXGAMEAWARDSBSRG, KXGAMEAWARDSBIG, KXGAMEAWARDSBN, KXGAMEAWARDSBA, KXGAMERANK, KXRANKLISTIGN
KXSTEAMGOTY, KXSTEAMBGOSD, KXSTEAMLOL, KXSTEAMBS, KXSTEAMMIG, KXSTEAMBWF, KXSTEAMBGYSA, KXSTEAMVRGOTY, KXSTEAMOVS, KXSTEAMOSRG, KXSTEAMPRICE, KXTOPSELLERS
KXTWITCHSUBSKAICENAT, KXTWITCHSUBSSPEED, KXTWITCHSUBSJYNXZI, KXTWITCHSUBSPLAQUEBOYMAX, KXTWITCHSUBSFAZELACY, KXTWITCHSUBSFAZEJASON, KXTWITCHSUBSFAZEADAPT, KXTWITCHSUBSNINJA, KXTWITCHSUBSK4PACK, KXTWITCHSUBSYOURRAGEGAMING, KXCEOTWITCH, KXBANCLAVICULAR, KXBANDANTES, KXBANXNUBCAT, KXBAN2XRAKAI
KXESPORTSTEST, KXAUSTINMAJOR, KXHALOPS, KXXAIGAME, KXNBA2KCOVER, GAMERANK, KXFORTNITEPROAM
| Data Type | Frequency | Source |
|---|---|---|
| Pinnacle esports odds | Adaptive (same cadence as sports: 2hr -> 15min -> 5min) | Stealth Chromium scraper |
| Kalshi esports prices | Every 30min (5min pre-match) | Kalshi API |
| The Odds API (backup) | On Pinnacle scraper failure only | API |
| HLTV stats + roster changes | Hourly (transfers), daily (stats) | Scrape |
| VLR.gg stats | Daily | Scrape |
| Oracle's Elixir | Weekly CSV download | Free |
| OpenDota | Per match | Free API |
| Liquipedia rosters | Every 30min (roster change detection) | MediaWiki API |
| Patch notes | Check every 5min on known patch days | RSS/scrape |
| SteamDB player counts | Hourly | Scrape |
| Twitch analytics | Daily | Scrape |
| Reddit/Twitter sentiment | Real-time keyword monitoring | API |
| SBR multi-book odds | 10AM/2PM/6PM | Scrape (already built) |
| Dimension | Sports (NHL/NBA/MLB) | Esports |
|---|---|---|
| Anchor reliability | Pinnacle very sharp | Pinnacle softer — use multi-book SCL |
| Anchor access | Pinnacle scraper (PRIMARY) | Same scraper, add esports URLs |
| Roster stability | Season-long (mostly) | Changes weekly/monthly |
| "Venue" impact | Home ice/court | Online vs LAN + timezone |
| Game rules changes | Rare (annual) | Every 2 weeks (patches) |
| Data availability | Mature (official APIs) | Fragmented, mostly scrape-dependent |
| Market efficiency | Moderate-high | Low-moderate (our advantage) |
| Information flow | Controlled (team PR) | Leaky (Discord, Twitter, streams) |
| Season structure | Fixed schedule | Irregular, overlapping tournaments |