[{"data":1,"prerenderedAt":917},["ShallowReactive",2],{"search-sections":3,"blog-wc2026-fills-market-signals":137,"all-authors":868,"related-cat-/blog/wc2026-fills-market-signals":900,"surround-/blog/wc2026-fills-market-signals":906,"related-recent-/blog/wc2026-fills-market-signals":911},[4,12,18,23,28,33,38,44,49,54,59,64,69,74,79,85,90,95,100,105,112,117,122,127,132],{"category":5,"date":6,"id":7,"title":8,"titles":9,"content":10,"level":11},"research","2026-04-06","/blog/market-diversity-lockin","When Do Polymarket Markets Decide? Half Wait Until the Last 15 Minutes",[],"Across Polymarket markets, the number of market makers barely affects when a market reaches certainty. What matters is whether it's a sports bet or a crypto price target. TL;DR: 50% of Polymarket markets lock in within 15 minutes of resolution. The number of different liquidity providers explains ~1% of when markets decide. The type of market (sports, crypto, politics) explains 10x more. Half of all Polymarket binary markets reach certainty within 15 minutes of resolution. Out of 397,000 resolved markets since January 2025, the median time between the last uncertain trade and the final result is 14.6 minutes. We set out to test whether having more different liquidity providers — the people placing resting orders on each side — affects how quickly a market reaches its verdict. The hypothesis: too few providers means thin price discovery, too many means sustained disagreement, and somewhere in the middle should be optimal. The data has a different story. The number of liquidity providers barely matters. What does matter — 10x more — is what the market is about.",1,{"category":5,"date":6,"id":13,"title":14,"titles":15,"content":16,"level":17},"/blog/market-diversity-lockin#the-data","The Data",[8],"397,498 resolved Polymarket binary markets, each with at least 10 trades and at least one trade on the winning outcome below 90 cents. For each market, we measure lock-in timing: how long before resolution did the winning outcome's price last dip below 90 cents? A market that locked in 5 days before resolution \"decided\" early. One that locked in 2 minutes before resolution was uncertain until the end. Half of all markets lock in within the final 15 minutes. Another 20% take 1–6 hours — a second cluster of markets that \"know\" a few hours early. The remaining 30% spread across days to weeks, with 10% locking in more than 5 days before resolution.",2,{"category":5,"date":6,"id":19,"title":20,"titles":21,"content":22,"level":17},"/blog/market-diversity-lockin#more-providers-slightly-later-lock-in","More Providers, Slightly Later Lock-in",[8],"At first glance, markets with more liquidity providers lock in faster. But that's misleading — those markets also have more trades overall (correlation: 0.96), and more trades is the real driver. To separate the two, I held trading volume constant and asked: among markets with the same amount of trading, does having more different providers change anything? Yes, but barely. Markets with 8 unique providers lock in about 4 minutes before resolution. Markets with 500 unique providers lock in about 1.7 hours before resolution. The curve rises, but across the full range of provider diversity, the difference is small in absolute terms — and it explains only about 1% of the total variation.",{"category":5,"date":6,"id":24,"title":25,"titles":26,"content":27,"level":17},"/blog/market-diversity-lockin#what-actually-matters-the-type-of-market","What Actually Matters: The Type of Market",[8],"The type of market explains 10x more than the number of liquidity providers. Sports markets lock in at the final whistle. Crypto price targets lock in when the price hits. Political markets wait for official results. Adding market-type categories to the model explains 6.8% of additional variation beyond trade count alone. Provider diversity adds just 0.6%. The chart shows signed ΔR² — the additional variance explained by provider diversity, with the sign indicating direction. Positive means more providers predicts later lock-in; negative means faster. (Raw ΔR² is always positive; we sign it to show whether diversity delays or accelerates convergence.) In sports and crypto (87% of markets), more providers weakly predicts later lock-in — consistent with more disagreement. In politics, weather, tech, and entertainment, more providers weakly predicts faster lock-in. The pooled result is positive only because sports and crypto dominate the sample.",{"category":5,"date":6,"id":29,"title":30,"titles":31,"content":32,"level":17},"/blog/market-diversity-lockin#a-single-character-bug-that-changed-the-finding","A Single-Character Bug That Changed the Finding",[8],"The most useful discovery was not about liquidity at all. To classify markets by topic, I built a keyword matcher over market titles. The first version used  (space after \"vs\") to catch sports markets. Polymarket titles mostly use \"vs.\" (period). One character. This silently misclassified ~100,000 sports markets into the \"other\" bucket, inflating it from 5% to 35% of the sample and creating a fake signal — the misclassified sports markets mixed with genuinely unclassifiable markets produced an apparent provider-diversity effect of 2.7% within \"other\" that did not exist. If you do prediction market research that uses title-based topic classification — and most empirical PM work does — this is a reminder to check your classifier against sample titles. A one-character difference in a LIKE pattern changed the headline finding of a 400K-market analysis.",{"category":5,"date":6,"id":34,"title":35,"titles":36,"content":37,"level":17},"/blog/market-diversity-lockin#what-this-means","What This Means",[8],"For traders: counting liquidity providers won't help you predict when a market converges. Watch the market type and the event schedule instead. For researchers: when a market decides is a different question from whether it's right — and the answer depends almost entirely on the domain, not the market structure. For market designers: a sports market and a political market have fundamentally different convergence dynamics. One-size-fits-all parameters may be leaving efficiency on the table.",{"category":5,"date":39,"id":40,"title":41,"titles":42,"content":43,"level":11},"2026-04-14","/blog/sell-side-dominance","Why 87% of Polymarket Trades Are Sells",[],"Most of the SELL dominance is just crypto micro-markets taking over. The real story is what the makers are doing underneath. TL;DR: ~63% of the shift is crypto micro-markets taking over. The rest is newer, shorter markets replacing older ones. Three things are real: makers buying both sides for risk-free profit (96–100% consistency), a 20pp  gap between short and long-lived markets, and a two-week lifecycle every market goes through.",{"category":5,"date":39,"id":45,"title":46,"titles":47,"content":48,"level":17},"/blog/sell-side-dominance#the-top-makers-are-playing-a-different-game","The top makers are playing a different game",[41],"We looked at 600M+ Polymarket fills from October 2024 to March 2026. The headline: 87% of taker fills are sells. But the more interesting question is — who's on the other side? In crypto micro-markets (5-minute Up/Down bets that now make up 76% of all fills), 90%+ of trades are . Someone is buying all those tokens. We checked the top 10 makers: are they betting on a direction, or doing something else? MakerMarketsBought both sides%0x7164...53,92153,921100.0%0xd0D6...47,64246,67998.0%0x818F...32,24732,246100.0%0x63CE...31,99931,70599.1%0x6031...28,57828,578100.0%0x38e5...28,27328,14599.5%0xd44e...26,83925,90096.5%0xA45f...22,37822,378100.0%0x0489...20,69020,62199.7%0xB27B...14,41114,411100.0% They're not betting. In 96.5–100% of their markets, these makers are net buyers on both  and . They don't care which side wins.",{"category":5,"date":39,"id":50,"title":51,"titles":52,"content":53,"level":17},"/blog/sell-side-dominance#how-it-works-mint-and-sell","How it works: mint-and-sell",[41],"Polymarket uses a Conditional Token Framework (CTF) — traders can mint complete token sets from USDC and sell the side they don't want. This creates a two-sided flow where takers sell and makers collect: The maker posts resting bids on both outcomes. Different takers — each betting on a different side — sell their unwanted tokens into those bids. The maker ends up holding matched pairs (1  + 1 ) that are worth $1.00 at resolution regardless of the outcome. The spread between what they paid and $1.00 is pure profit. We can't directly observe the minting step — the data only records trades, not token creation. But the pattern fits: 90%+ , makers buying both sides at near-100% consistency, and a platform architecture that makes minting easy. This evidence covers June 2025 onward, when crypto micro-markets reached meaningful volume.",{"category":5,"date":39,"id":55,"title":56,"titles":57,"content":58,"level":17},"/blog/sell-side-dominance#where-does-the-87-come-from","Where does the 87% come from?",[41],"Now that we understand the mechanism — back to the headline number.  went from 59% to 87% in 18 months: The red line (SELL%) and the blue line (crypto micro-market share) track almost perfectly. December 2024 is the one exception:  surged when the US presidential election settled. Most of the trend is just market mix. We ran a shift-share decomposition — separating \"the mix of markets changed\" from \"people started trading differently\": What changedHow muchShare of totalMarket mix shifted (more crypto micro-markets)+7.6 pp29% rates rose within each market type+9.6 pp37%Both at once (crypto grew AND had high )+9.0 pp34% About 63% is mix-related. Here's what that looks like over time — the grey line is what  would have been if the market mix stayed frozen at January 2025 levels: The other 37%? It's 92% driven by traditional Yes/No markets, where  went from 59% to 71%. But that shift is itself explained by market turnover: each new batch of markets starts at a higher  rate, then decays toward 57–62%. No batch trends upward on its own. The reason newer batches start higher? They have shorter lifespans (median went from 37 days to 3.7 days). And shorter markets have more selling. It's mix effects all the way down.",{"category":5,"date":39,"id":60,"title":61,"titles":62,"content":63,"level":17},"/blog/sell-side-dominance#shorter-markets-more-selling","Shorter markets = more selling",[41],"Each dot is a market. Short-lived markets are heavily -dominated, long-lived ones much less so: LifespanCrypto microTraditionalOther\u003C1h92.2% (41K mkts)—92.9% (34)1–6h89.2% (73K)81.4% (179)85.3% (3K)6–24h87.7% (32K)79.7% (3.4K)86.1% (19K)1–7d88.0% (18K)76.1% (56K)83.3% (31K)7–30d77.1% (58)69.5% (25.5K)83.7% (3.3K)>30d—60.7% (20.3K)67.1% (157) For traditional markets,  drops from 81% to 61% — a 20pp gap. This isn't about token prices: in long-lived markets,  is flat at ~61% whether tokens trade at $0.20 or $0.80. It only shifts at the extremes (\u003C$0.10 and >$0.90). The likely reason: when a market resolves soon, makers can post aggressive bids without much risk of getting stuck holding tokens. The order book fills up with buy orders, and takers sell into them.",{"category":5,"date":39,"id":65,"title":66,"titles":67,"content":68,"level":17},"/blog/sell-side-dominance#every-market-goes-through-the-same-lifecycle","Every market goes through the same lifecycle",[41],"Here's the one finding that isn't explained by market mix. When you track  by how old a market is, long-lived traditional markets all follow the same curve: Week one: 66% . By week two: ~61%. Then it stays there for months. This holds regardless of how long the market ultimately lasts. What we're seeing is a shift from position-building (people minting and selling to establish their bets) to steady-state trading (people trading tokens that already exist). That transition takes about two weeks. This is the only finding in the whole analysis that reflects actual behavior change rather than market mix.",{"category":5,"date":39,"id":70,"title":71,"titles":72,"content":73,"level":17},"/blog/sell-side-dominance#what-to-watch-out-for","What to watch out for",[41],"We can't see the minting directly. We see the sells that result from it, and we see makers collecting both sides. But the minting step itself happens on-chain and isn't in the fills data.The maker data starts June 2025. Crypto micro-markets barely existed before that. The strategy is clear for the 10 months we can observe it.The 87% number is fragile. If crypto micro-markets disappeared tomorrow,  would drop back toward 70%. The underlying patterns would all still be there.Kalshi's 100%  is a data format thing, not a real behavioral difference. Their system just doesn't record sells.Fees are asymmetric. Same wallet pays 11x more when selling than buying (avg 0.56 vs 0.05 in traditional markets).  and  have different fee schedules regardless of who's trading.",{"category":5,"date":39,"id":75,"title":76,"titles":77,"content":78,"level":17},"/blog/sell-side-dominance#so-what","So what?",[41],"If you're comparing Polymarket to Kalshi,  doesn't mean the same thing on both platforms. Kalshi records 100%  — not because nobody sells, but because that's how their system works. The 87% vs 100% comparison is an architecture difference, not a behavior difference. If you're building models on Polymarket data, the 87% is not a stable number — it would drop to ~70% overnight if the crypto micro-markets went away. The lifespan gradient and the two-week lifecycle are the durable patterns. And if you're a maker: someone is already running the matched-pair strategy across 50,000+ markets with near-perfect consistency. The question isn't whether the strategy works — it's how thin the spread has gotten. Data: 600M+ Polymarket fills, Oct 2024 – Mar 2026. Dataset by Probalytics",{"category":5,"date":80,"id":81,"title":82,"titles":83,"content":84,"level":11},"2026-06-23","/blog/wc2026-fills-market-signals","What 200,000 Prediction Market Fills Reveal About the World Cup",[],"Analysis of Kalshi's KXMENWORLDCUP-26 outright winner markets reveals three patterns that end-of-day prices miss: hourly repricing, post-win spikes as artefacts, and France as the cleanest signal in the tournament. TL;DR: Analysis of Kalshi's KXMENWORLDCUP-26 winner markets across ten national teams — over 200,000 fills — reveals three patterns that end-of-day prices miss entirely: the USA's repricing from 9.2% to 27% is visible at hourly resolution before daily VWAP catches up; post-win spikes in illiquid markets inflate daily averages past the point of usefulness; and France's 63,000-fill sustained climb is the cleanest win-probability signal in the tournament. Data covers trades through June 22, 2026. VWAP computed at monthly granularity for the pre-tournament period and hourly granularity since June 11.",{"category":5,"date":80,"id":86,"title":87,"titles":88,"content":89,"level":17},"/blog/wc2026-fills-market-signals#the-usa-92-to-27-across-three-game-nights","The USA: 9.2% to 27% across three game nights",[82],"Before the tournament, Kalshi's markets gave the USA a 9.2% chance of winning — ninth out of ten teams tracked, in their own tournament. The move to 27% happened across three distinct sessions and is visible hour by hour. June 12–13 (first game): Game ended ~23:30 UTC June 12. Within one hour, 1,317 fills pushed price to 0.27. Sellers pushed back to 0.10–0.14 overnight — a contested result, not a blowout, and the fills show the disagreement.June 14 (second game): A second repricing ran to 0.55–0.62 across 394 fills, then settled as the result cleared.June 19 (third game): 3,374 fills in four hours — the highest-volume window for the USA market all tournament. Three games happened and each repriced the market. That is visible in the hourly VWAP before the daily number reflected it. The fills show the exact moments when the market changed its assessment and how much volume backed each move.",{"category":5,"date":80,"id":91,"title":92,"titles":93,"content":94,"level":17},"/blog/wc2026-fills-market-signals#post-win-spikes-what-argentinas-88-and-brazils-78-actually-mean","Post-win spikes: what Argentina's 88% and Brazil's 78% actually mean",[82],"At 00:00 UTC on June 13 — roughly 30 minutes after Argentina's first game — the outright market printed 0.884 on 74 fills. By 02:00 UTC it was back at 0.10. The daily VWAP for June 13 came out at 0.46. None of these figures represent Argentina's actual assessed win probability. The midnight spike is the structural result of buyers reacting before sellers arrive in an illiquid market. The 0.46 daily average is a mathematical artefact of the spike and the correction taken together. Argentina's settled level — once the market filled in — was 0.12–0.25, currently 0.252. Brazil followed the same pattern one night later: game ended ~23:00 UTC June 13, price reached 0.78 by 10:00 UTC June 14 on 26 fills, then fell to 0.11 by noon. Daily VWAPs of 0.328 and 0.318 for June 14–15 are artefacts of the same mechanism. This pattern is repeatable and predictable: every thin outright market shows it after a positive result. End-of-day prices capture the artefact. The fills show the correction.",{"category":5,"date":80,"id":96,"title":97,"titles":98,"content":99,"level":17},"/blog/wc2026-fills-market-signals#france-vs-spain-same-starting-price-opposite-trajectories","France vs Spain: same starting price, opposite trajectories",[82],"France entered May at 0.303, Spain at 0.263 — close enough to read as co-favourites. Twelve days in, the markets tell different stories. France's daily VWAP since tournament open: No single spike. 63,000 fills across 12 days. Consistent buying as results arrive. Spain peaked at 0.399 on June 16 — the day after a 0–0 draw with Cape Verde — then pulled back steadily to 0.270. Their current price is almost identical to the pre-tournament May VWAP of 0.263. The market is pricing Spain as a team meeting expectations. France at 35.5% across 63,000 fills is the most reliable win-probability estimate in this tournament so far.",{"category":5,"date":80,"id":101,"title":102,"titles":103,"content":104,"level":17},"/blog/wc2026-fills-market-signals#the-data","The data",[82],"Every fill is timestamped with price and size. VWAP is computable at any granularity — hourly resolution exposes spikes and corrections that daily resolution averages away. Matched against match results, the dataset identifies exactly when new information entered the market and how quickly it was absorbed. The three patterns above — the USA's session-by-session repricing, the Argentina and Brazil artefacts, the France/Spain divergence — are not visible from end-of-day price data. They require the fills. All data from the Probalytics ClickHouse database ( joined against ). Markets queried:  on Kalshi only. VWAP = sum(price × size) / sum(size) per period. Thin-market prints (under 50 fills/hour) noted where they drive daily figures.",{"category":106,"date":107,"id":108,"title":109,"titles":110,"content":111,"level":11},"market-analysis","2026-04-09","/blog/what-trades-dont-tell-you-orderbook","What Trades Don't Tell You But the Orderbook Will",[],"Polymarket fills are on-chain and complete. The orderbook isn't. If you're modeling slippage, market impact, or execution quality, you need the book — and there's no historical record of it unless someone captured it. TL;DR: Polymarket fills are on-chain and complete. The orderbook isn't — it lives on Polymarket's matching engine and there's no historical record of it anywhere unless someone captured it. If you're modeling execution quality, slippage, or market impact, you need the book, not just the fills. If you've done any serious work with Polymarket data, you've probably used the fills. They're on-chain, permanent, and complete — every matched trade is recorded on Polygon via the CTF and NegRisk exchange contracts. You can reconstruct the full trade history of any market going back to inception. What you can't reconstruct is what the book looked like between trades.",{"category":106,"date":107,"id":113,"title":114,"titles":115,"content":116,"level":17},"/blog/what-trades-dont-tell-you-orderbook#what-fills-dont-tell-you","What fills don't tell you",[109],"A fill tells you that at time T, X shares of outcome Y traded at price P. That's useful for a lot of things: price series, volume analysis, market convergence (we wrote about this here). It doesn't tell you what was sitting in the order book at T-1ms. It doesn't tell you what the best bid was before that fill came in, how deep the book was at each price level, or what the spread looked like. If the fill moved the price by 3 cents, you have no idea whether that was a 100-share book or a 10,000-share book. For researchers studying price discovery, this probably doesn't matter much. For anyone modeling execution — slippage, market impact, realistic fill simulation — it matters a lot.",{"category":106,"date":107,"id":118,"title":119,"titles":120,"content":121,"level":17},"/blog/what-trades-dont-tell-you-orderbook#why-theres-no-historical-orderbook","Why there's no historical orderbook",[109],"Polymarket's matching engine runs off-chain. The orderbook state at any moment exists on Polymarket's servers, not on the blockchain. Only the result of a match — the fill — gets settled on-chain. This means there is no canonical historical record of the orderbook. Not on Polymarket's API (no historical endpoint exists). Not on the blockchain. If no one captured it at the time, it's gone. We've been capturing continuously since November 2025. Full-resolution data is available from March 2026.",{"category":106,"date":107,"id":123,"title":124,"titles":125,"content":126,"level":17},"/blog/what-trades-dont-tell-you-orderbook#what-continuous-capture-gives-you","What continuous capture gives you",[109],"We snapshot the full orderbook across active Polymarket markets continuously and store every state. For any market, at any moment in its lifetime, you can reconstruct the full bid/ask book — every price level, every size. We interpolate to 1ms resolution using last-observation-carried-forward. On high-volume markets, the raw feed can hit 1,000 orderbook updates per second — storing and querying that at scale is non-trivial. The data ships as Parquet: , , , , each level as .",{"category":106,"date":107,"id":128,"title":129,"titles":130,"content":131,"level":17},"/blog/what-trades-dont-tell-you-orderbook#what-this-is-actually-useful-for","What this is actually useful for",[109],"Slippage modeling: Given a position size, what would your actual fill price have been? The mid-price from fills gives you a number. The orderbook gives you the right number. Market impact estimation: How much does a trade of size S move the book at different probability levels? This is only answerable if you know the book depth at the moment of entry — not after. Execution strategy research: Does it matter whether you're a maker or taker in the final hour of a contested market? With orderbook history, you can test it. Without it, you're guessing. These questions are answerable with orderbook history. They're not answerable from fills alone.",{"category":106,"date":107,"id":133,"title":134,"titles":135,"content":136,"level":17},"/blog/what-trades-dont-tell-you-orderbook#get-the-data","Get the data",[109],"Orderbook history is available on the Orderbook plan. If you're working on execution research or building a model that needs realistic fill simulation, reach out at arsenii@probalytics.io.",{"id":138,"title":82,"author":139,"body":140,"category":5,"date":80,"description":855,"draft":856,"extension":857,"featured":856,"image":858,"meta":859,"navigation":217,"path":81,"seo":860,"series":858,"seriesOrder":858,"stem":861,"tags":862,"__hash__":867},"blog/blog/wc2026-fills-market-signals.md","probalytics-team",{"type":141,"value":142,"toc":849},"minimark",[143,151,154,157,161,164,186,189,772,774,777,780,783,786,789,791,794,797,800,805,808,811,814,816,819,822,825,827,845],[144,145,146,150],"p",{},[147,148,149],"strong",{},"TL;DR:"," Analysis of Kalshi's KXMENWORLDCUP-26 winner markets across ten national teams — over 200,000 fills — reveals three patterns that end-of-day prices miss entirely: the USA's repricing from 9.2% to 27% is visible at hourly resolution before daily VWAP catches up; post-win spikes in illiquid markets inflate daily averages past the point of usefulness; and France's 63,000-fill sustained climb is the cleanest win-probability signal in the tournament.",[144,152,153],{},"Data covers trades through June 22, 2026. VWAP computed at monthly granularity for the pre-tournament period and hourly granularity since June 11.",[155,156],"hr",{},[158,159,87],"h2",{"id":160},"the-usa-92-to-27-across-three-game-nights",[144,162,163],{},"Before the tournament, Kalshi's markets gave the USA a 9.2% chance of winning — ninth out of ten teams tracked, in their own tournament. The move to 27% happened across three distinct sessions and is visible hour by hour.",[165,166,167,174,180],"ol",{},[168,169,170,173],"li",{},[147,171,172],{},"June 12–13 (first game):"," Game ended ~23:30 UTC June 12. Within one hour, 1,317 fills pushed price to 0.27. Sellers pushed back to 0.10–0.14 overnight — a contested result, not a blowout, and the fills show the disagreement.",[168,175,176,179],{},[147,177,178],{},"June 14 (second game):"," A second repricing ran to 0.55–0.62 across 394 fills, then settled as the result cleared.",[168,181,182,185],{},[147,183,184],{},"June 19 (third game):"," 3,374 fills in four hours — the highest-volume window for the USA market all tournament.",[144,187,188],{},"Three games happened and each repriced the market. That is visible in the hourly VWAP before the daily number reflected it. The fills show the exact moments when the market changed its assessment and how much volume backed each move.",[190,191,197],"pre",{"className":192,"code":193,"language":194,"meta":195,"style":196},"language-d3 shiki shiki-themes github-light github-dark-dimmed","const rawText = await (await fetch('/data/blog/wc2026-fills-market-signals/usa_wc2026_ohlc.csv')).text()\nconst rawAll = d3.csvParse(rawText)\n\nconst margin = { top: 36, right: 20, bottom: 48, left: 56 }\nconst GAP    = 10\nconst W  = width  - margin.left - margin.right\nconst H  = height - margin.top  - margin.bottom\nconst pH = Math.floor(H * 0.65)\nconst vH = H - pH - GAP\n\nconst g      = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)\nconst priceG = g.append('g')\nconst volG   = g.append('g').attr('transform', `translate(0,${pH + GAP})`)\n\nconst parseTime = d3.timeParse('%Y-%m-%d %H:%M:%S')\nconst t0 = new Date('2026-06-12T00:00:00Z')\nconst t1 = new Date('2026-06-21T00:00:00Z')\n\nconst data = rawAll\n  .map(d => ({ t: parseTime(d.hour), close: +d.close, fills: +d.fill_count }))\n  .filter(d => d.t >= t0 && d.t \u003C t1)\n\n// 4-hour volume-weighted trailing rolling mean; each raw close capped at 65%\n// so thin NO-dominated 15-min periods don't distort the signal.\nconst WIN = 4\nconst smoothed = data.map((_, i) => {\n  const slice = data.slice(Math.max(0, i - WIN + 1), i + 1)\n  const totalFills = d3.sum(slice, d => d.fills)\n  const vwap = totalFills > 0\n    ? d3.sum(slice, d => Math.min(d.close, 65) * d.fills) / totalFills\n    : d3.mean(slice, d => Math.min(d.close, 65))\n  return { t: data[i].t, vwap, fills: data[i].fills }\n})\n\nconst x  = d3.scaleTime().domain([t0, t1]).range([0, W])\nconst yMax = Math.min(d3.max(smoothed, d => d.vwap) * 1.15, 65)\nconst yP = d3.scaleLinear().domain([0, yMax]).range([pH, 0])\nconst yV = d3.scaleLinear().domain([0, d3.max(data, d => d.fills) * 1.05]).range([vH, 0])\n\n// Game event bands\nconst bands = [\n  { start: new Date('2026-06-12T21:00:00Z'), end: new Date('2026-06-13T06:00:00Z'), label: 'Game 1' },\n  { start: new Date('2026-06-14T01:00:00Z'), end: new Date('2026-06-14T09:00:00Z'), label: 'Game 2' },\n  { start: new Date('2026-06-19T17:00:00Z'), end: new Date('2026-06-20T02:00:00Z'), label: 'Game 3' },\n]\nbands.forEach(b => {\n  const bx = x(b.start), bw = x(b.end) - x(b.start)\n  g.append('rect').attr('x', bx).attr('y', 0).attr('width', bw).attr('height', pH + GAP + vH)\n    .attr('fill', '#78909c').attr('opacity', 0.10)\n  g.append('text').attr('x', bx + bw / 2).attr('y', 11)\n    .attr('text-anchor', 'middle').attr('font-size', '8.5px').attr('fill', colors.axisText).text(b.label)\n})\n\n// Smoothed probability area + line\npriceG.append('path').datum(smoothed)\n  .attr('d', d3.area().x(d => x(d.t)).y0(pH).y1(d => yP(d.vwap)).curve(d3.curveMonotoneX))\n  .attr('fill', '#26a69a').attr('opacity', 0.18)\npriceG.append('path').datum(smoothed)\n  .attr('d', d3.line().x(d => x(d.t)).y(d => yP(d.vwap)).curve(d3.curveMonotoneX))\n  .attr('fill', 'none').attr('stroke', '#00796b').attr('stroke-width', 2.2)\n\n// Pre-tournament reference line at 9.2% (drawn last so it sits on top)\nconst refColor = isDark ? '#e0e0e0' : '#555555'\npriceG.append('line').attr('x1', 0).attr('x2', W)\n  .attr('y1', yP(9.2)).attr('y2', yP(9.2))\n  .attr('stroke', refColor).attr('stroke-width', 1.5).attr('stroke-dasharray', '5,3')\npriceG.append('text').attr('x', 4).attr('y', yP(9.2) - 4)\n  .attr('font-size', '9px').attr('fill', refColor).text('9.2% pre-tournament')\n\n// Fill count bars\nconst maxFills = d3.max(data, d => d.fills)\nconst colorScale = d3.scaleSequential(d3.interpolate('#b2dfdb', '#00695c')).domain([0, maxFills])\nconst barW = Math.max(1.5, W / data.length * 0.75)\nvolG.selectAll('.bar').data(data).enter().append('rect')\n  .attr('x', d => x(d.t) - barW / 2).attr('y', d => yV(d.fills))\n  .attr('width', barW).attr('height', d => vH - yV(d.fills))\n  .attr('fill', d => colorScale(d.fills))\n\n// Axes\npriceG.append('g').call(d3.axisLeft(yP).ticks(5).tickFormat(d => `${d}%`))\n  .call(ax => ax.select('.domain').remove())\n  .call(ax => ax.selectAll('text').attr('fill', colors.axisText))\n  .call(ax => ax.selectAll('.tick line').attr('stroke', colors.grid))\npriceG.append('text').attr('transform', 'rotate(-90)').attr('x', -pH / 2).attr('y', -44)\n  .attr('text-anchor', 'middle').attr('font-size', '9px').attr('fill', colors.axisText).text('Win probability (%)')\n\nvolG.append('g').call(d3.axisLeft(yV).ticks(3).tickFormat(d => d >= 1e3 ? `${(d/1e3).toFixed(0)}k` : d))\n  .call(ax => ax.select('.domain').remove())\n  .call(ax => ax.selectAll('text').attr('fill', colors.axisText))\n  .call(ax => ax.selectAll('.tick line').attr('stroke', colors.grid))\nvolG.append('text').attr('transform', 'rotate(-90)').attr('x', -vH / 2).attr('y', -44)\n  .attr('text-anchor', 'middle').attr('font-size', '9px').attr('fill', colors.axisText).text('Fills / hour')\n\nconst xAxis = g.append('g').attr('transform', `translate(0,${pH + GAP + vH})`)\n  .call(d3.axisBottom(x).ticks(d3.timeDay.every(1)).tickFormat(d3.timeFormat('%d %b')))\nxAxis.select('.domain').remove()\nxAxis.selectAll('text').attr('fill', colors.axisText).attr('transform', 'rotate(-35)').style('text-anchor', 'end').attr('font-size', '9px')\nxAxis.selectAll('.tick line').attr('stroke', colors.grid)\n","d3","title=\"USA win probability — Jun 12–20\" subtitle=\"4-hour volume-weighted rolling mean, capped at 65%. Shaded bands = game nights.\" source=\"Probalytics / Kalshi KXMENWORLDCUP-26\" height=\"460\"","",[198,199,200,207,212,219,225,231,237,243,249,255,260,266,272,278,283,289,295,301,306,312,318,324,329,335,341,347,353,359,365,371,377,383,389,395,400,406,412,418,424,429,435,441,447,453,459,465,471,477,483,489,495,501,506,511,517,523,529,535,540,546,552,557,563,569,575,581,587,593,599,604,610,616,622,628,634,640,646,652,657,663,669,675,681,687,693,699,704,710,715,720,725,731,737,742,748,754,760,766],"code",{"__ignoreMap":196},[201,202,204],"span",{"class":203,"line":11},"line",[201,205,206],{},"const rawText = await (await fetch('/data/blog/wc2026-fills-market-signals/usa_wc2026_ohlc.csv')).text()\n",[201,208,209],{"class":203,"line":17},[201,210,211],{},"const rawAll = d3.csvParse(rawText)\n",[201,213,215],{"class":203,"line":214},3,[201,216,218],{"emptyLinePlaceholder":217},true,"\n",[201,220,222],{"class":203,"line":221},4,[201,223,224],{},"const margin = { top: 36, right: 20, bottom: 48, left: 56 }\n",[201,226,228],{"class":203,"line":227},5,[201,229,230],{},"const GAP    = 10\n",[201,232,234],{"class":203,"line":233},6,[201,235,236],{},"const W  = width  - margin.left - margin.right\n",[201,238,240],{"class":203,"line":239},7,[201,241,242],{},"const H  = height - margin.top  - margin.bottom\n",[201,244,246],{"class":203,"line":245},8,[201,247,248],{},"const pH = Math.floor(H * 0.65)\n",[201,250,252],{"class":203,"line":251},9,[201,253,254],{},"const vH = H - pH - GAP\n",[201,256,258],{"class":203,"line":257},10,[201,259,218],{"emptyLinePlaceholder":217},[201,261,263],{"class":203,"line":262},11,[201,264,265],{},"const g      = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)\n",[201,267,269],{"class":203,"line":268},12,[201,270,271],{},"const priceG = g.append('g')\n",[201,273,275],{"class":203,"line":274},13,[201,276,277],{},"const volG   = g.append('g').attr('transform', `translate(0,${pH + GAP})`)\n",[201,279,281],{"class":203,"line":280},14,[201,282,218],{"emptyLinePlaceholder":217},[201,284,286],{"class":203,"line":285},15,[201,287,288],{},"const parseTime = d3.timeParse('%Y-%m-%d %H:%M:%S')\n",[201,290,292],{"class":203,"line":291},16,[201,293,294],{},"const t0 = new Date('2026-06-12T00:00:00Z')\n",[201,296,298],{"class":203,"line":297},17,[201,299,300],{},"const t1 = new Date('2026-06-21T00:00:00Z')\n",[201,302,304],{"class":203,"line":303},18,[201,305,218],{"emptyLinePlaceholder":217},[201,307,309],{"class":203,"line":308},19,[201,310,311],{},"const data = rawAll\n",[201,313,315],{"class":203,"line":314},20,[201,316,317],{},"  .map(d => ({ t: parseTime(d.hour), close: +d.close, fills: +d.fill_count }))\n",[201,319,321],{"class":203,"line":320},21,[201,322,323],{},"  .filter(d => d.t >= t0 && d.t \u003C t1)\n",[201,325,327],{"class":203,"line":326},22,[201,328,218],{"emptyLinePlaceholder":217},[201,330,332],{"class":203,"line":331},23,[201,333,334],{},"// 4-hour volume-weighted trailing rolling mean; each raw close capped at 65%\n",[201,336,338],{"class":203,"line":337},24,[201,339,340],{},"// so thin NO-dominated 15-min periods don't distort the signal.\n",[201,342,344],{"class":203,"line":343},25,[201,345,346],{},"const WIN = 4\n",[201,348,350],{"class":203,"line":349},26,[201,351,352],{},"const smoothed = data.map((_, i) => {\n",[201,354,356],{"class":203,"line":355},27,[201,357,358],{},"  const slice = data.slice(Math.max(0, i - WIN + 1), i + 1)\n",[201,360,362],{"class":203,"line":361},28,[201,363,364],{},"  const totalFills = d3.sum(slice, d => d.fills)\n",[201,366,368],{"class":203,"line":367},29,[201,369,370],{},"  const vwap = totalFills > 0\n",[201,372,374],{"class":203,"line":373},30,[201,375,376],{},"    ? d3.sum(slice, d => Math.min(d.close, 65) * d.fills) / totalFills\n",[201,378,380],{"class":203,"line":379},31,[201,381,382],{},"    : d3.mean(slice, d => Math.min(d.close, 65))\n",[201,384,386],{"class":203,"line":385},32,[201,387,388],{},"  return { t: data[i].t, vwap, fills: data[i].fills }\n",[201,390,392],{"class":203,"line":391},33,[201,393,394],{},"})\n",[201,396,398],{"class":203,"line":397},34,[201,399,218],{"emptyLinePlaceholder":217},[201,401,403],{"class":203,"line":402},35,[201,404,405],{},"const x  = d3.scaleTime().domain([t0, t1]).range([0, W])\n",[201,407,409],{"class":203,"line":408},36,[201,410,411],{},"const yMax = Math.min(d3.max(smoothed, d => d.vwap) * 1.15, 65)\n",[201,413,415],{"class":203,"line":414},37,[201,416,417],{},"const yP = d3.scaleLinear().domain([0, yMax]).range([pH, 0])\n",[201,419,421],{"class":203,"line":420},38,[201,422,423],{},"const yV = d3.scaleLinear().domain([0, d3.max(data, d => d.fills) * 1.05]).range([vH, 0])\n",[201,425,427],{"class":203,"line":426},39,[201,428,218],{"emptyLinePlaceholder":217},[201,430,432],{"class":203,"line":431},40,[201,433,434],{},"// Game event bands\n",[201,436,438],{"class":203,"line":437},41,[201,439,440],{},"const bands = [\n",[201,442,444],{"class":203,"line":443},42,[201,445,446],{},"  { start: new Date('2026-06-12T21:00:00Z'), end: new Date('2026-06-13T06:00:00Z'), label: 'Game 1' },\n",[201,448,450],{"class":203,"line":449},43,[201,451,452],{},"  { start: new Date('2026-06-14T01:00:00Z'), end: new Date('2026-06-14T09:00:00Z'), label: 'Game 2' },\n",[201,454,456],{"class":203,"line":455},44,[201,457,458],{},"  { start: new Date('2026-06-19T17:00:00Z'), end: new Date('2026-06-20T02:00:00Z'), label: 'Game 3' },\n",[201,460,462],{"class":203,"line":461},45,[201,463,464],{},"]\n",[201,466,468],{"class":203,"line":467},46,[201,469,470],{},"bands.forEach(b => {\n",[201,472,474],{"class":203,"line":473},47,[201,475,476],{},"  const bx = x(b.start), bw = x(b.end) - x(b.start)\n",[201,478,480],{"class":203,"line":479},48,[201,481,482],{},"  g.append('rect').attr('x', bx).attr('y', 0).attr('width', bw).attr('height', pH + GAP + vH)\n",[201,484,486],{"class":203,"line":485},49,[201,487,488],{},"    .attr('fill', '#78909c').attr('opacity', 0.10)\n",[201,490,492],{"class":203,"line":491},50,[201,493,494],{},"  g.append('text').attr('x', bx + bw / 2).attr('y', 11)\n",[201,496,498],{"class":203,"line":497},51,[201,499,500],{},"    .attr('text-anchor', 'middle').attr('font-size', '8.5px').attr('fill', colors.axisText).text(b.label)\n",[201,502,504],{"class":203,"line":503},52,[201,505,394],{},[201,507,509],{"class":203,"line":508},53,[201,510,218],{"emptyLinePlaceholder":217},[201,512,514],{"class":203,"line":513},54,[201,515,516],{},"// Smoothed probability area + line\n",[201,518,520],{"class":203,"line":519},55,[201,521,522],{},"priceG.append('path').datum(smoothed)\n",[201,524,526],{"class":203,"line":525},56,[201,527,528],{},"  .attr('d', d3.area().x(d => x(d.t)).y0(pH).y1(d => yP(d.vwap)).curve(d3.curveMonotoneX))\n",[201,530,532],{"class":203,"line":531},57,[201,533,534],{},"  .attr('fill', '#26a69a').attr('opacity', 0.18)\n",[201,536,538],{"class":203,"line":537},58,[201,539,522],{},[201,541,543],{"class":203,"line":542},59,[201,544,545],{},"  .attr('d', d3.line().x(d => x(d.t)).y(d => yP(d.vwap)).curve(d3.curveMonotoneX))\n",[201,547,549],{"class":203,"line":548},60,[201,550,551],{},"  .attr('fill', 'none').attr('stroke', '#00796b').attr('stroke-width', 2.2)\n",[201,553,555],{"class":203,"line":554},61,[201,556,218],{"emptyLinePlaceholder":217},[201,558,560],{"class":203,"line":559},62,[201,561,562],{},"// Pre-tournament reference line at 9.2% (drawn last so it sits on top)\n",[201,564,566],{"class":203,"line":565},63,[201,567,568],{},"const refColor = isDark ? '#e0e0e0' : '#555555'\n",[201,570,572],{"class":203,"line":571},64,[201,573,574],{},"priceG.append('line').attr('x1', 0).attr('x2', W)\n",[201,576,578],{"class":203,"line":577},65,[201,579,580],{},"  .attr('y1', yP(9.2)).attr('y2', yP(9.2))\n",[201,582,584],{"class":203,"line":583},66,[201,585,586],{},"  .attr('stroke', refColor).attr('stroke-width', 1.5).attr('stroke-dasharray', '5,3')\n",[201,588,590],{"class":203,"line":589},67,[201,591,592],{},"priceG.append('text').attr('x', 4).attr('y', yP(9.2) - 4)\n",[201,594,596],{"class":203,"line":595},68,[201,597,598],{},"  .attr('font-size', '9px').attr('fill', refColor).text('9.2% pre-tournament')\n",[201,600,602],{"class":203,"line":601},69,[201,603,218],{"emptyLinePlaceholder":217},[201,605,607],{"class":203,"line":606},70,[201,608,609],{},"// Fill count bars\n",[201,611,613],{"class":203,"line":612},71,[201,614,615],{},"const maxFills = d3.max(data, d => d.fills)\n",[201,617,619],{"class":203,"line":618},72,[201,620,621],{},"const colorScale = d3.scaleSequential(d3.interpolate('#b2dfdb', '#00695c')).domain([0, maxFills])\n",[201,623,625],{"class":203,"line":624},73,[201,626,627],{},"const barW = Math.max(1.5, W / data.length * 0.75)\n",[201,629,631],{"class":203,"line":630},74,[201,632,633],{},"volG.selectAll('.bar').data(data).enter().append('rect')\n",[201,635,637],{"class":203,"line":636},75,[201,638,639],{},"  .attr('x', d => x(d.t) - barW / 2).attr('y', d => yV(d.fills))\n",[201,641,643],{"class":203,"line":642},76,[201,644,645],{},"  .attr('width', barW).attr('height', d => vH - yV(d.fills))\n",[201,647,649],{"class":203,"line":648},77,[201,650,651],{},"  .attr('fill', d => colorScale(d.fills))\n",[201,653,655],{"class":203,"line":654},78,[201,656,218],{"emptyLinePlaceholder":217},[201,658,660],{"class":203,"line":659},79,[201,661,662],{},"// Axes\n",[201,664,666],{"class":203,"line":665},80,[201,667,668],{},"priceG.append('g').call(d3.axisLeft(yP).ticks(5).tickFormat(d => `${d}%`))\n",[201,670,672],{"class":203,"line":671},81,[201,673,674],{},"  .call(ax => ax.select('.domain').remove())\n",[201,676,678],{"class":203,"line":677},82,[201,679,680],{},"  .call(ax => ax.selectAll('text').attr('fill', colors.axisText))\n",[201,682,684],{"class":203,"line":683},83,[201,685,686],{},"  .call(ax => ax.selectAll('.tick line').attr('stroke', colors.grid))\n",[201,688,690],{"class":203,"line":689},84,[201,691,692],{},"priceG.append('text').attr('transform', 'rotate(-90)').attr('x', -pH / 2).attr('y', -44)\n",[201,694,696],{"class":203,"line":695},85,[201,697,698],{},"  .attr('text-anchor', 'middle').attr('font-size', '9px').attr('fill', colors.axisText).text('Win probability (%)')\n",[201,700,702],{"class":203,"line":701},86,[201,703,218],{"emptyLinePlaceholder":217},[201,705,707],{"class":203,"line":706},87,[201,708,709],{},"volG.append('g').call(d3.axisLeft(yV).ticks(3).tickFormat(d => d >= 1e3 ? `${(d/1e3).toFixed(0)}k` : d))\n",[201,711,713],{"class":203,"line":712},88,[201,714,674],{},[201,716,718],{"class":203,"line":717},89,[201,719,680],{},[201,721,723],{"class":203,"line":722},90,[201,724,686],{},[201,726,728],{"class":203,"line":727},91,[201,729,730],{},"volG.append('text').attr('transform', 'rotate(-90)').attr('x', -vH / 2).attr('y', -44)\n",[201,732,734],{"class":203,"line":733},92,[201,735,736],{},"  .attr('text-anchor', 'middle').attr('font-size', '9px').attr('fill', colors.axisText).text('Fills / hour')\n",[201,738,740],{"class":203,"line":739},93,[201,741,218],{"emptyLinePlaceholder":217},[201,743,745],{"class":203,"line":744},94,[201,746,747],{},"const xAxis = g.append('g').attr('transform', `translate(0,${pH + GAP + vH})`)\n",[201,749,751],{"class":203,"line":750},95,[201,752,753],{},"  .call(d3.axisBottom(x).ticks(d3.timeDay.every(1)).tickFormat(d3.timeFormat('%d %b')))\n",[201,755,757],{"class":203,"line":756},96,[201,758,759],{},"xAxis.select('.domain').remove()\n",[201,761,763],{"class":203,"line":762},97,[201,764,765],{},"xAxis.selectAll('text').attr('fill', colors.axisText).attr('transform', 'rotate(-35)').style('text-anchor', 'end').attr('font-size', '9px')\n",[201,767,769],{"class":203,"line":768},98,[201,770,771],{},"xAxis.selectAll('.tick line').attr('stroke', colors.grid)\n",[155,773],{},[158,775,92],{"id":776},"post-win-spikes-what-argentinas-88-and-brazils-78-actually-mean",[144,778,779],{},"At 00:00 UTC on June 13 — roughly 30 minutes after Argentina's first game — the outright market printed 0.884 on 74 fills. By 02:00 UTC it was back at 0.10. The daily VWAP for June 13 came out at 0.46.",[144,781,782],{},"None of these figures represent Argentina's actual assessed win probability. The midnight spike is the structural result of buyers reacting before sellers arrive in an illiquid market. The 0.46 daily average is a mathematical artefact of the spike and the correction taken together. Argentina's settled level — once the market filled in — was 0.12–0.25, currently 0.252.",[144,784,785],{},"Brazil followed the same pattern one night later: game ended ~23:00 UTC June 13, price reached 0.78 by 10:00 UTC June 14 on 26 fills, then fell to 0.11 by noon. Daily VWAPs of 0.328 and 0.318 for June 14–15 are artefacts of the same mechanism.",[144,787,788],{},"This pattern is repeatable and predictable: every thin outright market shows it after a positive result. End-of-day prices capture the artefact. The fills show the correction.",[155,790],{},[158,792,97],{"id":793},"france-vs-spain-same-starting-price-opposite-trajectories",[144,795,796],{},"France entered May at 0.303, Spain at 0.263 — close enough to read as co-favourites. Twelve days in, the markets tell different stories.",[144,798,799],{},"France's daily VWAP since tournament open:",[144,801,802],{},[198,803,804],{},"0.188 → 0.204 → 0.234 → 0.290 → 0.300 → 0.295 → 0.302 → 0.353 → 0.363 → 0.355",[144,806,807],{},"No single spike. 63,000 fills across 12 days. Consistent buying as results arrive.",[144,809,810],{},"Spain peaked at 0.399 on June 16 — the day after a 0–0 draw with Cape Verde — then pulled back steadily to 0.270. Their current price is almost identical to the pre-tournament May VWAP of 0.263. The market is pricing Spain as a team meeting expectations.",[144,812,813],{},"France at 35.5% across 63,000 fills is the most reliable win-probability estimate in this tournament so far.",[155,815],{},[158,817,102],{"id":818},"the-data",[144,820,821],{},"Every fill is timestamped with price and size. VWAP is computable at any granularity — hourly resolution exposes spikes and corrections that daily resolution averages away. Matched against match results, the dataset identifies exactly when new information entered the market and how quickly it was absorbed.",[144,823,824],{},"The three patterns above — the USA's session-by-session repricing, the Argentina and Brazil artefacts, the France/Spain divergence — are not visible from end-of-day price data. They require the fills.",[155,826],{},[144,828,829],{},[830,831,832,833,836,837,840,841,844],"em",{},"All data from the Probalytics ClickHouse database (",[198,834,835],{},"probalytics.fills"," joined against ",[198,838,839],{},"probalytics.markets","). Markets queried: ",[198,842,843],{},"KXMENWORLDCUP-26-*"," on Kalshi only. VWAP = sum(price × size) / sum(size) per period. Thin-market prints (under 50 fills/hour) noted where they drive daily figures.",[846,847,848],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":196,"searchDepth":214,"depth":214,"links":850},[851,852,853,854],{"id":160,"depth":17,"text":87},{"id":776,"depth":17,"text":92},{"id":793,"depth":17,"text":97},{"id":818,"depth":17,"text":102},"Analysis of Kalshi's KXMENWORLDCUP-26 outright winner markets reveals three patterns that end-of-day prices miss: hourly repricing, post-win spikes as artefacts, and France as the cleanest signal in the tournament.",false,"md",null,{},{"title":82,"description":855},"blog/wc2026-fills-market-signals",[863,864,865,866],"prediction-markets","kalshi","world-cup","data-analysis","ob-xAGau3lB_9eEIiG4BeTmnLV1tkY_C8zyeyo8L1uc",[869,885],{"id":870,"avatar":871,"bio":872,"extension":857,"github":873,"icon":874,"linkedin":858,"meta":875,"name":880,"role":881,"stem":882,"twitter":883,"website":858,"__hash__":884},"authors/authors/arsenii-petrovich.md","/images/authors/arsenii-petrovich.jpg","Building Probalytics from the ground up. Focused on data pipelines, quantitative analysis, and making prediction market data accessible.","Singulariteer","ph:user-circle-duotone",{"body":876},{"type":141,"value":877,"toc":878},[],{"title":196,"searchDepth":214,"depth":214,"links":879},[],"Arsenii Petrovich","Founder & Lead Engineer","authors/arsenii-petrovich","probalytics_io","sorFeFDBF9341JmeyEJgl_P9b0xz5bRWmYS2g5sTYDQ",{"id":886,"avatar":887,"bio":888,"extension":857,"github":889,"icon":890,"linkedin":858,"meta":891,"name":896,"role":897,"stem":898,"twitter":883,"website":858,"__hash__":899},"authors/authors/probalytics-team.md","/images/authors/probalytics-team.svg","Building the data infrastructure for prediction markets. We cover quantitative methods, market analysis, and platform insights.","Probalytics","ph:users-three-duotone",{"body":892},{"type":141,"value":893,"toc":894},[],{"title":196,"searchDepth":214,"depth":214,"links":895},[],"Probalytics Team","Research & Analytics","authors/probalytics-team","-8l8v6Nan6hkqGsqOSmTe71zbEr9sxncNxrJ2GlYtqE",[901,902,904],{"title":82,"path":81,"description":855,"date":80,"category":5},{"title":41,"path":40,"description":903,"date":39,"category":5},"Most of the SELL dominance is just crypto micro-markets taking over. The real story is what the makers are doing underneath.",{"title":8,"path":7,"description":905,"date":6,"category":5},"Across Polymarket markets, the number of market makers barely affects when a market reaches certainty. What matters is whether it's a sports bet or a crypto price target.",[907,909],{"title":41,"path":40,"stem":908,"children":-1},"blog/sell-side-dominance",{"title":109,"path":108,"stem":910,"children":-1},"blog/what-trades-dont-tell-you-orderbook",[912,913,914,916],{"title":82,"path":81,"description":855,"date":80,"category":5},{"title":41,"path":40,"description":903,"date":39,"category":5},{"title":109,"path":108,"description":915,"date":107,"category":106},"Polymarket fills are on-chain and complete. The orderbook isn't. If you're modeling slippage, market impact, or execution quality, you need the book — and there's no historical record of it unless someone captured it.",{"title":8,"path":7,"description":905,"date":6,"category":5},1782843413606]