Documentation Index
Fetch the complete documentation index at: https://docs.probalytics.io/llms.txt
Use this file to discover all available pages before exploring further.
Production-ready queries for common prediction market analysis tasks. All queries include platform and date filters for optimal performance.
Market Discovery
Search Markets by Keyword
SELECT
id,
platform,
title,
status,
created_at
FROM markets
WHERE platform = 'POLYMARKET'
AND created_at >= now() - INTERVAL 90 DAY
AND (
title ILIKE '%bitcoin%'
OR title ILIKE '%btc%'
)
ORDER BY created_at DESC
LIMIT 50;
Markets Closing Soon
Find active markets closing in the next 7 days—useful for time-sensitive analysis.
SELECT
id,
platform,
title,
closes_at,
dateDiff('hour', now(), closes_at) as hours_until_close
FROM markets
WHERE platform = 'POLYMARKET'
AND status = 'ACTIVE'
AND closes_at BETWEEN now() AND now() + INTERVAL 7 DAY
ORDER BY closes_at ASC
LIMIT 100;
Recently Created Markets
SELECT
id,
platform,
title,
category,
created_at
FROM markets
WHERE platform = 'POLYMARKET'
AND created_at >= now() - INTERVAL 24 HOUR
ORDER BY created_at DESC
LIMIT 50;
Most Traded Markets (Last 7 Days)
SELECT
f.market_id,
m.title,
m.platform,
count() as fill_count,
sum(f.size) as total_size,
uniq(f.taker_id) as unique_takers
FROM fills f
JOIN markets m ON f.market_id = m.id
WHERE f.platform = 'POLYMARKET'
AND f.timestamp >= now() - INTERVAL 7 DAY
GROUP BY f.market_id, m.title, m.platform
ORDER BY total_size DESC
LIMIT 25;
Price Analysis
Price History for a Market
Get all fills for a specific market to plot price over time. Use price for per-outcome charts or normalized_price for a single Outcome 0 chart.
SELECT
timestamp,
outcome.name as outcome,
price,
size,
taker_side
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 30 DAY
ORDER BY timestamp ASC;
OHLC Candles (Hourly)
Build candlestick data for charting. Uses price (the per-outcome execution price) so each outcome gets correct candles.
SELECT
toStartOfHour(timestamp) as hour,
outcome.name as outcome,
argMin(price, timestamp) as open,
max(price) as high,
min(price) as low,
argMax(price, timestamp) as close,
sum(size) as volume,
count() as fill_count
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY hour, outcome
ORDER BY hour ASC, outcome;
OHLC Candles (Daily)
SELECT
toDate(timestamp) as date,
outcome.name as outcome,
argMin(price, timestamp) as open,
max(price) as high,
min(price) as low,
argMax(price, timestamp) as close,
sum(size) as volume
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 90 DAY
GROUP BY date, outcome
ORDER BY date ASC, outcome;
Volume-Weighted Average Price (VWAP)
SELECT
outcome.name as outcome,
sum(price * size) / sum(size) as vwap,
sum(size) as total_size
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 24 HOUR
GROUP BY outcome;
Latest Price per Outcome
Get the most recent fill price for each outcome in a market.
SELECT
outcome.name as outcome,
argMax(price, timestamp) as latest_price,
max(timestamp) as last_fill_time
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 30 DAY
GROUP BY outcome;
Trading Activity
Recent Fills on a Market
SELECT
timestamp,
outcome.name as outcome,
taker_side,
price,
size,
taker_id,
maker_id
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
ORDER BY timestamp DESC
LIMIT 100;
Hourly Volume Over Time
SELECT
toStartOfHour(timestamp) as hour,
count() as fills,
sum(size) as total_size,
uniq(taker_id) as unique_takers
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY hour
ORDER BY hour ASC;
Large Fills (Whale Watching)
Find fills above a size threshold.
SELECT
f.timestamp,
m.title,
f.outcome.name as outcome,
f.taker_side,
f.price,
f.size,
f.taker_id
FROM fills f
JOIN markets m ON f.market_id = m.id
WHERE f.platform = 'POLYMARKET'
AND f.timestamp >= now() - INTERVAL 24 HOUR
AND f.size > 1000 -- adjust threshold
ORDER BY f.size DESC
LIMIT 100;
Buy vs Sell Pressure
SELECT
toStartOfHour(timestamp) as hour,
outcome.name as outcome,
sumIf(size, taker_side = 'BUY') as buy_size,
sumIf(size, taker_side = 'SELL') as sell_size,
sumIf(size, taker_side = 'BUY') - sumIf(size, taker_side = 'SELL') as net_flow
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 7 DAY
GROUP BY hour, outcome
ORDER BY hour ASC;
Cash Flow Analysis
SELECT
outcome.name as outcome,
taker_side,
count() as fill_count,
sum(taker_cash_flow) as total_taker_cash_flow,
sum(maker_cash_flow) as total_maker_cash_flow,
sum(fee) as total_fees
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 30 DAY
GROUP BY outcome, taker_side;
Trader Analysis
Top Takers by Volume
SELECT
taker_id,
count() as fill_count,
sum(size) as total_size,
uniq(market_id) as markets_traded,
min(timestamp) as first_fill,
max(timestamp) as last_fill
FROM fills
WHERE platform = 'POLYMARKET'
AND timestamp >= now() - INTERVAL 30 DAY
AND taker_id IS NOT NULL
GROUP BY taker_id
ORDER BY total_size DESC
LIMIT 100;
Trader Activity on a Specific Market
SELECT
taker_id,
count() as fills,
sum(size) as total_size,
sumIf(size, taker_side = 'BUY') as buy_size,
sumIf(size, taker_side = 'SELL') as sell_size,
avg(price) as avg_price
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND taker_id IS NOT NULL
GROUP BY taker_id
ORDER BY total_size DESC
LIMIT 50;
Trader’s Position (Net Contracts per Outcome)
Estimate a trader’s current position by summing buys minus sells.
SELECT
outcome.name as outcome,
sumIf(size, taker_side = 'BUY') - sumIf(size, taker_side = 'SELL') as net_contracts,
sum(taker_cash_flow) as total_cash_flow
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND taker_id = 'trader-id-here'
GROUP BY outcome;
Trader’s Recent Activity Across Markets
SELECT
f.timestamp,
m.title,
f.outcome.name as outcome,
f.taker_side,
f.price,
f.size
FROM fills f
JOIN markets m ON f.market_id = m.id
WHERE f.platform = 'POLYMARKET'
AND f.taker_id = 'trader-id-here'
AND f.timestamp >= now() - INTERVAL 7 DAY
ORDER BY f.timestamp DESC
LIMIT 100;
Orderbook Analysis
Current Best Bid/Ask
SELECT
outcome.name as outcome,
bids[1].price as best_bid,
bids[1].size as bid_size,
asks[1].price as best_ask,
asks[1].size as ask_size,
asks[1].price - bids[1].price as spread,
timestamp
FROM orderbook_snapshots
WHERE market_id = 'your-market-uuid-here'
AND length(bids) > 0
AND length(asks) > 0
ORDER BY timestamp DESC
LIMIT 1 BY outcome.name;
Spread Over Time
SELECT
toStartOfMinute(timestamp) as minute,
outcome.name as outcome,
avg(asks[1].price - bids[1].price) as avg_spread,
min(asks[1].price - bids[1].price) as min_spread,
max(asks[1].price - bids[1].price) as max_spread
FROM orderbook_snapshots
WHERE market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 1 HOUR
AND length(bids) > 0
AND length(asks) > 0
GROUP BY minute, outcome
ORDER BY minute ASC;
Orderbook at a Specific Time
Get the book state at an exact point in time (e.g., to correlate with a fill event). Uses the most recent snapshot at or before the target time.
SELECT
outcome.name as outcome,
bids[1].price as best_bid,
asks[1].price as best_ask,
(bids[1].price + asks[1].price) / 2 as mid_price,
bids,
asks,
timestamp
FROM orderbook_snapshots
WHERE market_id = 'your-market-uuid-here'
AND outcome.id = 'your-outcome-uuid-here'
AND timestamp <= '2026-03-20T14:30:00.000Z'
ORDER BY timestamp DESC
LIMIT 1;
Top-N Depth Levels
Extract only the top N price levels without transferring the full book.
SELECT
outcome.name as outcome,
arraySlice(bids, 1, 10) as top_bids,
arraySlice(asks, 1, 10) as top_asks,
timestamp
FROM orderbook_snapshots
WHERE market_id = 'your-market-uuid-here'
ORDER BY timestamp DESC
LIMIT 1 BY outcome.name;
Orderbook Depth
Total size available across all levels.
SELECT
outcome.name as outcome,
sumArray(arrayMap(b -> b.2, bids)) as total_bid_size,
sumArray(arrayMap(a -> a.2, asks)) as total_ask_size,
timestamp
FROM orderbook_snapshots
WHERE market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 1 DAY
ORDER BY timestamp DESC
LIMIT 1 BY outcome.name;
Tick-Level Reconstruction
Reconstruct millisecond-resolution orderbook data from sparse snapshots using ClickHouse WITH FILL and INTERPOLATE. Each gap is filled with the last known book state (LOCF).
SELECT timestamp, bids, asks
FROM orderbook_snapshots
WHERE market_id = 'your-market-uuid-here'
AND outcome.id = 'your-outcome-uuid-here'
ORDER BY timestamp ASC
WITH FILL
FROM toDateTime64('2026-03-20T14:00:00.000', 3)
TO toDateTime64('2026-03-20T14:01:00.000', 3)
STEP toIntervalMillisecond(1)
INTERPOLATE (bids, asks);
Tick-level reconstruction generates one row per millisecond. Keep the time range small — 1 minute produces 60,000 rows. For longer ranges, aggregate to a coarser resolution instead.
Market Resolution
Recently Resolved Markets
SELECT
id,
platform,
title,
resolution_type,
resolution_resolved_at,
resolution_winning_outcome_id
FROM markets
WHERE platform = 'POLYMARKET'
AND status = 'RESOLVED'
AND resolution_resolved_at >= now() - INTERVAL 7 DAY
ORDER BY resolution_resolved_at DESC
LIMIT 50;
Resolution with Winning Outcome Name
SELECT
m.id,
m.title,
m.resolution_resolved_at,
m.resolution_type,
winning.name as winning_outcome
FROM markets m
ARRAY JOIN m.outcomes as winning
WHERE m.platform = 'POLYMARKET'
AND m.status = 'RESOLVED'
AND m.resolution_resolved_at >= now() - INTERVAL 30 DAY
AND winning.id = m.resolution_winning_outcome_id
ORDER BY m.resolution_resolved_at DESC
LIMIT 50;
Aggregated Statistics
SELECT
toDate(timestamp) as date,
platform,
count() as fills,
sum(size) as total_size,
uniq(taker_id) as unique_takers,
uniq(market_id) as active_markets
FROM fills
WHERE timestamp >= now() - INTERVAL 30 DAY
GROUP BY date, platform
ORDER BY date DESC, platform;
Markets by Category
SELECT
category,
count() as market_count,
countIf(status = 'ACTIVE') as active_markets,
countIf(status = 'RESOLVED') as resolved_markets
FROM markets
WHERE platform = 'POLYMARKET'
AND created_at >= now() - INTERVAL 90 DAY
GROUP BY category
ORDER BY market_count DESC;
Outcome Count Distribution
How many markets have 2 outcomes vs 3+ outcomes?
SELECT
length(outcomes) as outcome_count,
count() as market_count
FROM markets
WHERE platform = 'POLYMARKET'
AND created_at >= now() - INTERVAL 90 DAY
GROUP BY outcome_count
ORDER BY outcome_count;
Advanced Patterns
Price Movement Detection
Find markets where price moved significantly in the last hour.
WITH recent_prices AS (
SELECT
market_id,
outcome.name as outcome,
argMin(price, timestamp) as price_1h_ago,
argMax(price, timestamp) as current_price
FROM fills
WHERE platform = 'POLYMARKET'
AND timestamp >= now() - INTERVAL 1 HOUR
GROUP BY market_id, outcome
)
SELECT
m.title,
r.outcome,
r.price_1h_ago,
r.current_price,
r.current_price - r.price_1h_ago as price_change,
(r.current_price - r.price_1h_ago) / r.price_1h_ago * 100 as pct_change
FROM recent_prices r
JOIN markets m ON r.market_id = m.id
WHERE abs(r.current_price - r.price_1h_ago) > 0.05 -- 5+ point move
ORDER BY abs(pct_change) DESC
LIMIT 25;
Markets with Unusual Volume
Find markets trading above their 7-day average.
WITH volume_stats AS (
SELECT
market_id,
sum(size) / 7 as avg_daily_size
FROM fills
WHERE platform = 'POLYMARKET'
AND timestamp BETWEEN now() - INTERVAL 8 DAY AND now() - INTERVAL 1 DAY
GROUP BY market_id
),
today_volume AS (
SELECT
market_id,
sum(size) as today_size
FROM fills
WHERE platform = 'POLYMARKET'
AND timestamp >= now() - INTERVAL 1 DAY
GROUP BY market_id
)
SELECT
m.title,
t.today_size,
v.avg_daily_size,
t.today_size / v.avg_daily_size as volume_ratio
FROM today_volume t
JOIN volume_stats v ON t.market_id = v.market_id
JOIN markets m ON t.market_id = m.id
WHERE v.avg_daily_size > 100 -- filter low-volume markets
ORDER BY volume_ratio DESC
LIMIT 25;
Spread Between Outcomes
For binary markets, check if outcome prices sum to ~1. A large deviation indicates an arbitrage opportunity or stale pricing.
SELECT
m.title,
sumIf(latest_price, outcome_index = 0) as yes_price,
sumIf(latest_price, outcome_index = 1) as no_price,
sumIf(latest_price, outcome_index = 0) + sumIf(latest_price, outcome_index = 1) as total,
abs(1 - (sumIf(latest_price, outcome_index = 0) + sumIf(latest_price, outcome_index = 1))) as spread
FROM (
SELECT
market_id,
outcome.index as outcome_index,
argMax(price, timestamp) as latest_price
FROM fills
WHERE platform = 'POLYMARKET'
AND timestamp >= now() - INTERVAL 1 DAY
GROUP BY market_id, outcome_index
) t
JOIN markets m ON t.market_id = m.id
WHERE m.market_type = 'BINARY'
GROUP BY m.id, m.title
HAVING spread > 0.02 -- markets with >2% spread
ORDER BY spread DESC
LIMIT 25;
Time-Weighted Average Price (TWAP)
Weight prices by time between fills, not volume.
WITH fill_intervals AS (
SELECT
timestamp,
price,
leadInFrame(timestamp) OVER (
ORDER BY timestamp
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
) as next_fill_time,
dateDiff('second', timestamp,
leadInFrame(timestamp) OVER (
ORDER BY timestamp
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
)
) as seconds_until_next
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND outcome.name = 'Yes'
AND timestamp >= now() - INTERVAL 24 HOUR
)
SELECT
sum(price * seconds_until_next) / sum(seconds_until_next) as twap
FROM fill_intervals
WHERE seconds_until_next > 0;
Exporting Data
Add FORMAT CSV or FORMAT CSVWithNames to any query:
SELECT
timestamp,
normalized_price,
size,
taker_side
FROM fills
WHERE platform = 'POLYMARKET'
AND market_id = 'your-market-uuid-here'
AND timestamp >= now() - INTERVAL 30 DAY
ORDER BY timestamp
FORMAT CSVWithNames;
Export to JSON
SELECT
id,
title,
status,
created_at
FROM markets
WHERE platform = 'POLYMARKET'
AND created_at >= now() - INTERVAL 7 DAY
FORMAT JSONEachRow;
When using programmatic clients, the format is usually handled by the client library. These FORMAT clauses are useful for CLI exports or direct HTTP queries.