Half The Volume Was Trades Of Exactly $5,500: Building Surveillance And Probability Charts For Event Contracts Before Cboe's KPI Binaries Land
On 30 September CNBC reported that nearly half the dollar volume in Kalshi's ether perpetual futures on 20 September came from trades sized between $5,495 and $5,505, with 24-hour volume far above resting liquidity; the CFTC is examining the trades and Kalshi has ended a trader incentive programme. Polymarket's international venue shows persistent activity in the least-likely outcomes of multi-contract markets. Both deny wash trading. The same day Cboe said it will launch binary contracts on company KPIs in October on its SEC-regulated exchange, with Robinhood first in line. Event contracts are becoming a regulated asset class whose product is a probability, which means every platform that lists, routes or plots them needs two things it has not needed before: surveillance tuned to the way these markets fail, and charts that show a probability with an expiry, a settlement source and a volume-quality signal rather than just a price. Here is both, in code.
AlchmAI Engineering16 min read
~50%
Of dollar volume in Kalshi's ether perpetuals on 20 September from trades sized $5,495-$5,505, per CNBC's analysis
80%
Kalshi's share of US prediction-market volume; combined monthly volume with Polymarket reached $45bn in August
Oct 2026
Target launch of Cboe's KPI-linked binary contracts on its SEC-regulated exchange, Robinhood first, Cboe Clear US seeking covered clearing agency status
4
Signals a surveillance layer for event contracts needs: size clustering, volume-versus-depth, longshot concentration and incentive correlation
The CNBC analysis is a gift to anyone building market-surveillance systems, because it documents the failure mode so precisely. On 20 September a user noticed that trades on Kalshi's ether perpetual futures clustered around $5,500; CNBC's reconstruction found nearly half the day's dollar volume came from tickets between $5,495 and $5,505, and that 24-hour volume was abnormally high relative to the liquidity actually resting in the book. The CFTC is examining the trades. Kalshi has terminated a trader incentive programme. On Polymarket's international exchange, observers have flagged a different pattern: in multi-outcome markets on elections, sport and central-bank decisions, the least likely contracts often see more activity than the favourites. Both companies deny wash trading.
The timing matters because event contracts are about to become mainstream, regulated instruments. Cboe announced on 30 September that it will launch binary contracts tied to companies' key performance indicators and corporate events in October, pending approval, on its SEC-regulated US securities exchange, with Robinhood as the first retail broker and a temporary covered-clearing-agency registration for Cboe Clear US. Kalshi holds about 80% of US prediction-market share and is reportedly discussing a raise at a $40bn valuation. For brokers routing to these venues, platforms displaying them and agents acting on their probabilities, volume quality is no longer an academic question.
Part 1: Surveillance Signals
Four detectors cover the patterns reported this month. They run on trade and order-book snapshots per contract, per day, and emit scored alerts rather than verdicts - a human analyst decides, as with any surveillance system.
from collections import Counter
from dataclasses import dataclass
from statistics import median
@dataclass(frozen=True)
class Trade:
ts: float; contract: str; price: float; qty: float; side: str; taker: str; maker: str
def size_clustering(trades, band=0.001, flag_share=0.30):
"""Share of notional at the single most common ticket size (within +/- band)."""
notional = [(t.price * t.qty, t) for t in trades]
total = sum(n for n, _ in notional) or 1.0
buckets = Counter(round(n / (n * band if n else 1)) for n, _ in notional) # coarse bucketing
top_bucket, _ = buckets.most_common(1)[0]
clustered = sum(n for n, _ in notional if round(n / (n * band if n else 1)) == top_bucket)
share = clustered / total
return {"signal": "size_clustering", "share": share, "flag": share >= flag_share}
def volume_vs_depth(day_notional, avg_resting_depth, max_ratio=25.0):
"""Turnover far above resting liquidity means trades match without the book absorbing risk."""
ratio = day_notional / max(avg_resting_depth, 1.0)
return {"signal": "volume_vs_depth", "ratio": ratio, "flag": ratio >= max_ratio}
def longshot_concentration(outcomes, max_share=0.50):
"""outcomes: list of (probability, notional). Flags when low-probability legs carry most volume."""
total = sum(n for _, n in outcomes) or 1.0
longshot = sum(n for p, n in outcomes if p < 0.15)
share = longshot / total
return {"signal": "longshot_concentration", "share": share, "flag": share >= max_share}
def self_matching(trades, max_share=0.05):
"""Share of notional where taker and maker resolve to the same beneficial owner."""
total = sum(t.price * t.qty for t in trades) or 1.0
same = sum(t.price * t.qty for t in trades if t.taker == t.maker)
return {"signal": "self_matching", "share": same / total, "flag": same / total >= max_share}
def incentive_correlation(daily_volume, programme_days, lift=2.0):
"""Volume on incentive-programme days versus the median of other days."""
inside = [v for d, v in daily_volume.items() if d in programme_days]
outside = [v for d, v in daily_volume.items() if d not in programme_days]
if not inside or not outside:
return {"signal": "incentive_correlation", "lift": None, "flag": False}
ratio = median(inside) / max(median(outside), 1.0)
return {"signal": "incentive_correlation", "lift": ratio, "flag": ratio >= lift}The size-clustering detector is the one the CNBC case would have fired: half of notional in a single ticket size is far above any organic market's concentration. The volume-versus-depth detector is the second half of the same finding. The thresholds above are starting points; calibrate them per venue and contract type on a clean month and review the alert rate weekly.
WEIGHTS = {"size_clustering": 0.35, "volume_vs_depth": 0.30, "longshot_concentration": 0.15,
"self_matching": 0.15, "incentive_correlation": 0.05}
def volume_quality(signals: list) -> dict:
"""0..1 where 1 = no concerns. Shown on the chart and used to weight the probability as a signal."""
penalty = sum(WEIGHTS[s["signal"]] for s in signals if s["flag"])
score = max(0.0, 1.0 - penalty)
return {"quality": round(score, 2), "flags": [s["signal"] for s in signals if s["flag"]]}Part 2: A Probability Is Not A Price - Chart It Properly
An event contract has properties a price chart does not: a hard expiry, a settlement source and rule, a probability bounded between 0 and 1, and - after this month - a volume-quality signal that tells the viewer how much to trust it. For Cboe's KPI binaries, a trader will also want the related equity on the same screen. Lightweight Charts v5 handles this with a line series on a fixed 0-100 scale, price lines for key levels, markers for incentive-programme windows and anomaly flags, and a second pane for the underlying.
import {
createChart, LineSeries, HistogramSeries, CandlestickSeries,
createSeriesMarkers, LineStyle, UTCTimestamp, SeriesMarker,
} from "lightweight-charts";
interface ProbPoint { time: UTCTimestamp; value: number } // 0..100
interface QualityPoint { time: UTCTimestamp; value: number; color: string }
interface Flag { time: UTCTimestamp; text: string }
export function mountEventContractChart(el: HTMLElement, cfg: {
probability: ProbPoint[]; quality: QualityPoint[]; flags: Flag[];
expiry: UTCTimestamp; settlementRule: string; underlying?: any[];
}) {
const chart = createChart(el, {
layout: { background: { color: "#0b0b12" }, textColor: "#cbd5e1", panes: { separatorColor: "#1f2937" } },
rightPriceScale: { scaleMargins: { top: 0.05, bottom: 0.05 } },
});
// Pane 0: probability on a fixed 0-100 axis. Never autoscale a probability.
const prob = chart.addSeries(LineSeries, {
color: "#a855f7", lineWidth: 2, title: "P(yes) %",
autoscaleInfoProvider: () => ({ priceRange: { minValue: 0, maxValue: 100 } }),
});
prob.setData(cfg.probability);
for (const level of [25, 50, 75]) {
prob.createPriceLine({ price: level, color: "#334155", lineWidth: 1, lineStyle: LineStyle.Dotted, axisLabelVisible: false, title: "" });
}
// Pane 1: volume-quality score as a histogram, coloured by the surveillance layer.
const quality = chart.addSeries(HistogramSeries, { title: "volume quality", priceFormat: { type: "percent" } }, 1);
quality.setData(cfg.quality);
// Pane 2 (optional): the related equity for KPI-linked binaries.
if (cfg.underlying) chart.addSeries(CandlestickSeries, {}, 2).setData(cfg.underlying);
// Anomaly flags and the expiry as markers on the probability series.
const markers: SeriesMarker<UTCTimestamp>[] = [
...cfg.flags.map((f) => ({ time: f.time, position: "aboveBar" as const, color: "#f59e0b", shape: "square" as const, text: f.text })),
{ time: cfg.expiry, position: "inBar" as const, color: "#ef4444", shape: "circle" as const, text: "EXPIRY - settles on: " + cfg.settlementRule },
].sort((a, b) => a.time - b.time);
createSeriesMarkers(prob, markers);
return chart;
}- Fixed 0-100 axis: an autoscaled probability chart turns a 2-point move into a cliff. Pin it.
- Expiry and settlement rule on the chart itself, because a binary at 60% two hours before expiry is a different object from one at 60% with three months left.
- Volume quality in its own pane, fed by the surveillance score, so a trader - or an agent - sees how much to trust the probability.
- For KPI binaries, the related equity in a third pane: the hedging relationship is the whole point of the product.
Part 3: Weight The Signal, Not Just The Order
Retail brokers now sell agents that trade when a probability crosses a threshold. Any such agent should treat the probability as a signal whose weight depends on volume quality, and the gateway that places the order should refuse when quality is below a floor. This is a two-line change to the debounced threshold we published for prediction-market signals, and it is the difference between acting on a market and acting on a bot.
class QualityGatedThreshold(DebouncedThreshold):
def __init__(self, level, min_quality=0.7, **kw):
super().__init__(level, **kw)
self.min_quality = min_quality
def update(self, prob: float, volume: int, quality: float, ts: float = None) -> bool:
if quality < self.min_quality:
self.window.clear() # do not accumulate evidence from a market we distrust
return False
return super().update(prob, volume, ts)“A price chart tells you what people paid. An event-contract chart has to tell you what the probability is, when it stops mattering, what decides it, and whether the volume behind it is real.”
What To Build Before October
- 01Run the five detectors on every event-contract venue you route to, daily, and keep the alert history - it is evidence of due diligence when a regulator asks why you trusted a venue's prices.
- 02Add volume quality to every probability you display or consume, and plot it. The surveillance layer and the chart share the same score.
- 03Pin probability axes, show expiry and settlement rule on-chart, and pair KPI binaries with their underlying.
- 04Gate agent signals on quality. A probability from a market with clustered tickets and thin depth is not a signal; it is noise with a confident face.
The Bottom Line
CNBC's finding that half of a Kalshi market's volume came from $5,500 tickets, the CFTC's examination, Kalshi's ended incentive programme and Polymarket's longshot activity arrive weeks before Cboe lists KPI-linked binaries on a regulated securities exchange with Robinhood. Event contracts sell probabilities, so the engineering has to protect the probability: size-clustering, volume-versus-depth, longshot-concentration, self-matching and incentive-correlation detectors producing a volume-quality score; charts with a pinned 0-100 axis, expiry and settlement rule on-screen, the quality score in its own pane and the underlying alongside; and agent signals gated on quality before any order is proposed. That is the trading-charts and surveillance work we build in London, and this month wrote the test cases.
References & Further Reading
- CNBC - Kalshi, Polymarket trading volumes on some products raise questions amid massive growth (30 September 2026). cnbc.com/2026/09/30/kalshi-polymarket-trading-volume-scrutiny.html
- Blockonomi - Kalshi terminates trader incentive program amid CFTC ethereum futures probe. blockonomi.com/kalshi-terminates-trader-incentive-program-amid-cftc-ethereum-futures-probe
- GuruFocus - Cboe Global Markets plans launch of KPI-linked binary contracts in October 2026. gurufocus.com/news/9103443/cboe-global-markets-cboe-plans-launch-of-kpilinked-binary-contracts-in-october-2026
- DefiRate - Prediction market volume: Kalshi and Polymarket aggregated data. defirate.com/prediction-markets/volume
- TradingView - Lightweight Charts: how to add series markers. tradingview.github.io/lightweight-charts/tutorials/how_to/series-markers
- TradingView - Lightweight Charts documentation: panes. tradingview.github.io/lightweight-charts/docs/panes
- CFTC - Prediction markets and event contracts. cftc.gov/PressRoom/PressReleases
AlchmAI Engineering
Engineering, London
Written by the AlchmAI engineering team in Mayfair, London. We build trading platforms, real-time charts, market data pipelines and AI features for brokers, prop firms and fintech teams. The Playbook is where we explain how we approach these systems, with code you can run and sources you can check.
Code in this guide is illustrative and supplied without warranty. Review and test it before production use. Nothing here is investment advice. Important information