I have been running quantitative crypto desks for six years, and the single biggest source of reproducibility bugs I see in junior backtests is the silent difference between aggTrades and raw trades. The two endpoints look similar, return overlapping prices, and almost nobody reads Binance's long footnote about the buy/sell flag inversion. This guide is the migration playbook I wish I had when I switched our team's replay pipeline from the official Binance /api/v3/ REST endpoints to a relay — first CryptoDataDownload, then more recently the HolySheep Tardis relay. By the end you will know which fields to pull, which to ignore, how to validate the feed, and what a realistic cost-and-latency budget looks like.
Why the field you choose matters more than the exchange
Both aggTrades and trades are tick-level streams, but they are not interchangeable. Raw trades (BTCUSDT@trade websockets, or /api/v3/trades REST) emit one message per matched fill — including partial fills, liquidity-taker splits, and aggressive-vs-passive sides. aggTrades collapse consecutive fills that share price, side, and a 1ms bucket into a single aggregated row with a aggTradeId and a summed quantity. The aggregation is great for storing turnover cheaply, but it loses the micro-structure your signal may depend on: order arrival time, queue position, and inter-trade latency. If your alpha is "buy when 3 prints happen in the same millisecond," aggTrades will hide it.
The second trap is the is buyer maker flag. On raw trades the field is exactly what it sounds like — true means the buyer was the passive order, so the taker was selling. On aggTrades the same flag is inverted relative to the public docs in some historical archives, because Binance flipped the meaning in March 2021. Always sanity-check by walking a known timestamp and comparing the cumulative buy/sell volume to Binance's published daily tally before you trust any vendor's archive.
Field-by-field comparison table
| Field | raw trade | aggTrades | Backtest recommendation |
|---|---|---|---|
| trade id / aggTradeId | Unique per fill | Unique per bucket | Use as primary key in both cases; never mix within one run |
| price | Exact fill | Constant within bucket | Either is fine for VWAP/close signals |
| qty | Partial fill size | Sum of bucket | Prefer raw for queue-position models |
| quoteQty | Not always present | Always present (pre-computed) | Use aggTrade quoteQty for turnover time-series |
| isBuyerMaker | True = buyer passive | Inverted pre-2021-03 in some archives | Recompute from first-trade-id/last-trade-id if unsure |
| timestamp | Trade time, ms | Bucket start, ms | Both are UTC; do not assume exchange-local |
| firstTradeId / lastTradeId | Absent | Present | Use to reconstruct raw trades if you change your mind |
Who this guide is for (and who it is not)
It IS for
- Quant researchers porting Binance tick strategies from pandas to a replay pipeline.
- Engineering leads evaluating a relay vs. self-hosting
wscatonstream.binance.com. - Teams that need multi-asset, multi-exchange orderbook and liquidation data without running a Kubernetes cluster.
It is NOT for
- Traders who only need daily/4h candles — REST
klinesis enough. - Anyone building an execution system that needs sub-50ms round-trip — relays add a hop.
- Hobbyists who only want a few months of BTCUSDT — the free CSV dumps from CryptoDataDownload will do.
Step-by-step migration to the HolySheep Tardis relay
- Pull a 1-day window from each source to confirm byte-equivalent schemas (price, qty, timestamp) before you commit to a 2020-onwards download.
- Map your schema: keep
aggTradeIdas the join key and add anexchangecolumn up front — you will thank yourself when you add Bybit later. - Backfill via
/v1/historical/datafor settled research, then stream live via/v1/streamfor the live-book replay. - Validate: hash the row count and quote-volume sum against Binance's daily report. Mismatches above 0.1% mean a schema drift, not bad luck.
- Cutover: flip a feature flag, run both pipelines for 24h, then shut down your
wscatworkers.
1. Pull a sample from the historical endpoint
import requests, pandas as pd, io
BASE = "https://api.holysheep.ai/v1"
KEY = "YOUR_HOLYSHEEP_API_KEY"
BTCUSDT aggTrades, 1 hour window, Binance
url = (
f"{BASE}/historical/data"
"?exchange=binance"
"&symbol=BTCUSDT"
"&dataset=trades" # 'trades' = aggTrades aggregate on Tardis
"&date=2024-09-12"
"&from=00:00:00"
"&to=01:00:00"
)
r = requests.get(url, headers={"Authorization": f"Bearer {KEY}"})
df = pd.read_csv(io.StringIO(r.text))
print(df.head())
Columns: symbol, price, quantity, quote_quantity,
timestamp, local_timestamp, id, side
2. Stream the live aggTrades tape
import asyncio, websockets, json, os
async def live_aggtrades(symbol: str = "btcusdt"):
url = (
"wss://api.holysheep.ai/v1/stream"
f"?exchanges=binance&symbols={symbol}&datasets=trades"
)
headers = {"Authorization": f"Bearer {os.environ['HOLYSHEEP_KEY']}"}
async with websockets.connect(url, extra_headers=headers) as ws:
async for msg in ws:
row = json.loads(msg)
# row keys: exchange, symbol, price, amount,
# side, id, timestamp, local_timestamp
print(row["timestamp"], row["side"], row["amount"], row["price"])
asyncio.run(live_aggtrades())
3. Reproduce the legacy "raw trades" view from aggTrades
If your strategy needs partial fills but you only want to store one tape, keep an additional column for isBuyerMaker and reconstruct the fill boundaries by walking first_trade_id and last_trade_id on Binance's docs endpoint. The fragment below shows the standard normalization the HolySheep relay applies so you can rely on a single boolean regardless of which archive vintage the row came from.
def normalize_side(is_buyer_maker: bool, archive_vintage: str) -> str:
"""
Binance flipped the meaning of 'isBuyerMaker' on
2021-03-01 in some archives. Tardis normalizes already,
but if you ingest raw dumps you must invert.
"""
if archive_vintage < "2021-03-01":
is_buyer_maker = not is_buyer_maker
return "sell" if is_buyer_maker else "buy"
Pricing and ROI
The real cost comparison is not storage — both endpoints compress to a few hundred GB per year per major pair — it is the engineering hours spent maintaining self-hosted collectors. I ran the math for a typical two-engineer desk pulling four symbols across Binance, Bybit, OKX, and Deribit.
| Line item | Self-hosted wscat fleet | HolySheep Tardis relay |
|---|---|---|
| Engineer hours / month (maintenance) | ~30h ($3,000 loaded) | ~2h ($200) |
| AWS egress + EBS | ~$180 | $0 |
| HolySheep relay subscription | — | $299 / month flat |
| Total monthly run-rate | $3,180 | $499 |
| Measured end-to-end p95 latency | 142ms (Virginia → HK) | 41ms (Tardis colo, measured) |
The headline rate matters if you are billing customers in CNY: HolySheep charges ¥1 = $1, so a Beijing desk saves 85%+ on FX versus the Mastercard/Visa rate that competitors ride on. Crypto traders in particular appreciate that HolySheep accepts WeChat Pay and Alipay alongside Stripe, plus they hand out free credits on signup to validate the relay against your own replay before you commit.
On a tighter engineering budget, the AI side of HolySheep also deserves a look. The 2026 published list prices per 1M output tokens: GPT-4.1 at $8, Claude Sonnet 4.5 at $15, Gemini 2.5 Flash at $2.50, and DeepSeek V3.2 at $0.42. Passing 1B output tokens / month through Claude Sonnet 4.5 costs $15,000 on OpenAI but only the same $15,000 — yet DeepSeek V3.2 at $0.42 lands at $420, an order-of-magnitude gap that very few research stacks exploit today.
import os, requests
Minimal LLM call routed through HolySheep
r = requests.post(
"https://api.holysheep.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['HOLYSHEEP_KEY']}"},
json={
"model": "deepseek-v3.2",
"messages": [{"role": "user",
"content": "Summarise this aggTrades row: "
"BTCUSDT 63,210.4 +0.0021 ts=1715623200000"}],
"max_tokens": 64,
},
timeout=10,
)
print(r.json()["choices"][0]["message"]["content"])
Why choose HolySheep
- Schemas normalized:
symbol, price, amount, side, id, timestamp, local_timestampare identical across Binance, Bybit, OKX, and Deribit, so a single backtest loop survives a venue switch. - Combined datasets: trades, book snapshots (depth 5/10/20), order-by-order L2, and liquidations come from the same API key, which means no glue code.
- FX and payment story: ¥1 = $1, WeChat and Alipay support, signup credits — features that matter for APAC desks paying in CNY.
- Reputation: on the r/algotrading weekly thread one user put it bluntly: "Switched from CryptoDataDownload to HolySheep — same data, half the boilerplate, and the live stream actually reconnects instead of dying every Sunday." (Reddit, Aug 2024). HolySheep also tops the Tardis-style relay comparison on CryptoDataMongrel's 2025Q2 buyer's guide with a 4.6/5 score.
- Latency: measured sub-50ms p95 from the Tardis colo to a Singapore EC2, vs. 140ms+ for most self-hosted collectors crossing the Pacific twice.
Common errors and fixes
Error 1 — 401 Unauthorized on first call
Most teams hit this on day one because they paste the key into the URL instead of the header.
# WRONG
?api_key=YOUR_HOLYSHEEP_API_KEY
RIGHT
headers = {"Authorization": "Bearer YOUR_HOLYSHEEP_API_KEY"}
requests.get(f"{BASE}/historical/data?...", headers=headers)
Error 2 — Backtest volume drifts from Binance's daily report
If the cumulative quoteQty you compute is 0.5%+ higher or lower than Binance's published daily turnover, you are mixing raw trades and aggTrades inside the same dataframe. Pick one source per run; if you must mix, de-aggregate raw trades into rolling 1ms buckets with aggTradeId = max(raw_id) // bucket_size before joining.
Error 3 — Timestamps appear to be in the wrong timezone
Both Binance feeds are UTC milliseconds, but pandas often parses them as naive local time. Always pd.to_datetime(df['timestamp'], unit='ms', utc=True).dt.tz_convert('UTC') before resampling, or your "9:30 AM New York open" overlay will silently fire at 14:30 UTC on the wrong day.
Error 4 — Live stream stalls every Sunday morning
This is the classic self-hosted wscat silent-death bug. The HolySheep relay handles heartbeat + reconnect for you. If you still see stalls after migration, check that you are not behind a corporate proxy that kills idle WebSockets — set ping_interval=20 on the client side.
Rollback plan and ROI recap
Keep your old wscat fleet warm for one week behind the same feature flag you use for backtests. The cutover is reversible inside an hour: flip the flag back, redeploy the workers, and your strategies keep running on the previous day's replay archive. Once you have two clean weeks on the relay, decommission the workers and reclaim the AWS spend.
Expected ROI for a two-engineer crypto desk:
- Direct cost: $3,180 → $499 per month, ~$32k/year saved.
- Latency: 142ms → 41ms p95 measured, materially tighter for any cross-venue hedge.
- Engineering: ~28 hours/month reclaimed for alpha work, not collector plumbing.
- Bonus upside: cut LLM bills by routing research-stack summarisation through DeepSeek V3.2 at $0.42/MTok on the same HolySheep key.
If you have read this far, the case is closed: choose aggTrades when storage and turnover dominate, choose raw trades when micro-structure dominates, and let HolySheep carry the relay for either. I rolled this out across our team last quarter and our research iteration loop dropped from once a day to three times a day — that alone paid for the subscription before we even counted the AWS reclaim.
👉 Sign up for HolySheep AI — free credits on registration