A comprehensive toolkit and dataset for Polymarket prediction markets. Fetch trading data directly from Polygon blockchain and Gamma API, process into multiple analysis-ready formats, and analyze with ease.
Zhengjie Wang1,2, Leiyu Chao1,3, Yu Bao1,4, Lian Cheng1,3, Jianhan Liao1,5, Yikang Li1,†
1Shanghai Innovation Institute 2Westlake University 3Shanghai Jiao Tong University
4Harbin Institute of Technology 5Fudan University
†Corresponding author
We provide 107GB of trading data from Polymarket containing 1.1 billion records across 268K+ markets, along with a complete toolkit to fetch, process, and analyze the data. Perfect for market research, behavioral studies, and quantitative analysis.
Get all historical data before 2026: Download the complete dataset from HuggingFace, or use this toolkit to fetch the latest data yourself.
| Field | Polymarket Data | Third-party |
|---|---|---|
| block_number | Yes | No |
| contract name | Yes | No |
| maker_fee / taker_fee / protocol_fee | Yes | No |
| order_hash | Yes | No |
| market_id (auto-linked) | Yes | Yes |
| Missing token auto-fill | Yes | Yes |
| File | Size | Records | Description |
|---|---|---|---|
orderfilled.parquet |
31GB | 293.3M | Raw blockchain events from OrderFilled logs |
trades.parquet |
32GB | 293.3M | Processed trades with market metadata linkage |
markets.parquet |
68MB | 268,706 | Market information and metadata |
quant.parquet |
21GB | 170.3M | Clean market data with unified YES perspective |
users.parquet |
23GB | 340.6M | User behavior data split by maker/taker roles |
Total: 107GB, 1.1 billion records
Download from HuggingFace: SII-WANGZJ/Polymarket_data
# Clone repository
git clone https://github.com/SII-WANGZJ/Polymarket_data.git
cd Polymarket_data
# Install dependencies
pip install -r requirements.txt
# Or install as package
pip install -e .
# Install HuggingFace CLI
pip install huggingface_hub
# Download specific file
hf download SII-WANGZJ/Polymarket_data quant.parquet --repo-type dataset
# Download all files
hf download SII-WANGZJ/Polymarket_data --repo-type dataset
Automatically fetch new blocks and keep running 24/7:
# Start continuous fetching
./scripts/continuous_start.sh
# View logs
tail -f logs/continuous_fetch.log
# Stop gracefully
./scripts/continuous_stop.sh
Features: - Batch mode: When behind by ≥100 blocks, fetch 100 blocks at once - Real-time mode: When caught up, fetch 1 block every 2 seconds - Auto data cleaning: Generate 4 parquet files in real-time - Graceful shutdown: Ensures all files are properly closed on exit
Fetch specific range of historical blocks:
# Fetch last 10,000 blocks
python -m polymarket.cli fetch-onchain --blocks 10000
# Resume from last checkpoint
python -m polymarket.cli fetch-onchain --continue
# Fetch specific block range
python -m polymarket.cli fetch-onchain --start 80000000 --end 80010000
Complete workflow: fetch markets → fetch on-chain → process data:
# Run full pipeline
./scripts/update_all.sh
# Or step by step
./scripts/fetch_markets.sh # Fetch market metadata
./scripts/fetch_onchain.sh 5000 # Fetch on-chain data
./scripts/clean_data.sh # Clean and process data
Use as a library in your Python code:
from polymarket import LogFetcher, EventDecoder, extract_trades
from polymarket import load_token_mapping
# 1. Fetch on-chain logs
fetcher = LogFetcher()
logs = fetcher.fetch_range_in_batches(start_block, end_block)
# 2. Decode events
decoder = EventDecoder()
decoded = decoder.decode_batch(logs)
events = decoder.format_batch(decoded)
# 3. Load token mapping and extract trades
token_mapping = load_token_mapping()
trades_df = extract_trades(events, token_mapping)
# 4. Save to parquet
trades_df.to_parquet('trades.parquet')
Polymarket_data/
├── polymarket/ # Core Python package
│ ├── cli/ # Command-line interface
│ ├── fetchers/ # Data fetchers (RPC, Gamma API)
│ ├── processors/ # Data processors (decoder, cleaner)
│ └── tools/ # Utility tools (merge, sort, etc.)
├── scripts/ # Shell scripts for common tasks
├── polymarket_data/ # Dataset documentation
├── data/ # Data storage (gitignored)
├── logs/ # Logs (gitignored)
├── README.md
├── LICENSE
└── requirements.txt
| Field | Description |
|---|---|
| timestamp | Unix timestamp |
| block_number | Block number |
| transaction_hash | Transaction hash |
| contract | Contract name (CTF_EXCHANGE or NEGRISK_CTF_EXCHANGE) |
| maker / taker | Trading parties' addresses |
| maker_asset_id / taker_asset_id | Asset IDs |
| maker_amount_filled / taker_amount_filled | Filled amounts |
| maker_fee / taker_fee / protocol_fee | Fees (in wei) |
| order_hash | Order hash |
| Field | Description |
|---|---|
| market_id | Market ID (auto-linked from token) |
| answer | Option name (YES/NO/etc.) |
| price | Trade price (0-1) |
| usd_amount / token_amount | USDC and token amounts |
| maker_direction / taker_direction | Buy/sell direction |
Filtered and normalized trade data with unified token perspective (YES token).
Key Features: - Unified perspective: All trades normalized to YES token (token1) - Clean data: Contract trades filtered out, only real user trades - Complete information: Maker/taker roles preserved - Best for: Market analysis, price studies, time-series forecasting
Schema:
{
'transaction_hash': str, # Blockchain transaction hash
'block_number': int, # Block number
'datetime': datetime, # Transaction timestamp
'market_id': str, # Market identifier
'maker': str, # Maker wallet address
'taker': str, # Taker wallet address
'token_amount': float, # Amount of tokens traded
'usd_amount': float, # USD value
'price': float, # Trade price (0-1)
}
Split maker/taker records with unified buy direction for user analysis.
Key Features: - Split records: Each trade becomes 2 records (one maker, one taker) - Unified direction: All converted to BUY (negative amounts = selling) - User sorted: Ordered by user for trajectory analysis - Best for: User profiling, PnL calculation, wallet analysis
Schema:
{
'transaction_hash': str, # Transaction hash
'block_number': int, # Block number
'datetime': datetime, # Timestamp
'market_id': str, # Market identifier
'user': str, # User wallet address
'role': str, # 'maker' or 'taker'
'token_amount': float, # Signed amount (+ buy, - sell)
'usd_amount': float, # USD value
'price': float, # Trade price
}
Market information and outcome token details.
Best for: Linking trades to market context, filtering by market attributes
See DATA_DESCRIPTION.md for complete schema documentation.
Polygon Blockchain (RPC) Gamma API
↓ ↓
orderfilled.parquet markets.parquet
↓
trades.parquet (+ Market linkage)
↓
├─→ quant.parquet (Unified YES perspective)
│ └─→ Filter contracts + Normalize tokens
│
└─→ users.parquet (Split maker/taker)
└─→ Split records + Unified BUY direction
Key Transformations:
Result: 170.3M records (from 293.3M)
users.parquet:
import pandas as pd
df = pd.read_parquet('quant.parquet')
# Market-level statistics
market_stats = df.groupby('market_id').agg({
'usd_amount': ['sum', 'mean'], # Total volume and average trade size
'price': ['mean', 'std', 'min', 'max'], # Price statistics
'transaction_hash': 'count' # Number of trades
}).round(4)
print(market_stats.head())
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_parquet('quant.parquet')
df['datetime'] = pd.to_datetime(df['datetime'])
# Select a specific market
market_id = 'your-market-id'
market_data = df[df['market_id'] == market_id].sort_values('datetime')
# Plot price over time
plt.figure(figsize=(12, 6))
plt.plot(market_data['datetime'], market_data['price'])
plt.title(f'Price Evolution - Market {market_id}')
plt.xlabel('Date')
plt.ylabel('Price')
plt.show()
import pandas as pd
df = pd.read_parquet('users.parquet')
# Calculate net position per user per market
user_positions = df.groupby(['user', 'market_id']).agg({
'token_amount': 'sum', # Net position (positive = long, negative = short)
'usd_amount': 'sum', # Total USD traded
'transaction_hash': 'count' # Number of trades
}).reset_index()
# Find most active users
active_users = user_positions.groupby('user').agg({
'market_id': 'count', # Number of markets traded
'usd_amount': 'sum' # Total volume
}).sort_values('usd_amount', ascending=False)
print(active_users.head(10))
import pandas as pd
df = pd.read_parquet('quant.parquet')
markets = pd.read_parquet('markets.parquet')
# Join with market metadata
df = df.merge(markets[['market_id', 'question']], on='market_id', how='left')
# Top markets by volume
top_markets = df.groupby(['market_id', 'question']).agg({
'usd_amount': 'sum'
}).sort_values('usd_amount', ascending=False).head(20)
print(top_markets)
Contracts Tracked:
- Exchange Contract 1: 0x4bFb41d5B3570DeFd03C39a9A4D8dE6Bd8B8982E
- Exchange Contract 2: 0xC5d563A36AE78145C45a50134d48A1215220f80a
```bash
python -m polymarket.cli fetch-markets
python -m polymarket.cli fetch-onchain --blocks 1000 python -m polymarket.cli fetch-onchain --continue
python -m polymarket.cli proc
$ claude mcp add Polymarket_data \
-- python -m otcore.mcp_server <graph>