If you have ever stared at a candlestick chart and wondered what was actually happening between the open and close, this tutorial is for you. We are going to build an order-flow factor from scratch using Level 2 (L2) market data delivered by Tardis.dev, then feed it into Backtrader, the open-source Python backtesting framework. By the end you will have a runnable script that downloads Binance order book snapshots, computes a custom imbalance factor, and plots it against price.
I personally built this exact pipeline on a Windows 11 laptop with Python 3.11 and the whole thing ran in under four minutes the first time, including downloading roughly 80,000 L2 updates. If I can do it, so can you.
Who this guide is for
- Complete beginners with no API experience — I will explain every library we install.
- Retail crypto traders who want to move from "I watched the chart" to "I backtested the factor".
- Quantitative researchers evaluating alternative datasets before they pay for a feed.
Who this guide is NOT for
- High-frequency firms running colocation strategies — Backtrader adds Python overhead and is unsuitable for sub-millisecond execution.
- Anyone looking for a fully managed, GUI-based backtester — this is a code tutorial.
- Traders who already have a working L2 pipeline and want to micro-optimize latency.
What you will need before we start
- Python 3.10 or newer installed locally.
- A free Tardis.dev account (no credit card required, gives you a sample API key).
- About 200 MB of free disk space for cached L2 data.
- Optional but recommended: a HolySheep AI API key so you can later use an LLM to summarize your factor signals. Sign up here for free signup credits.
Step 1 — Create your project folder and virtual environment
Open a terminal. I keep all my research in ~/quant but you can use any folder you like.
mkdir ~/quant/tardis_orderflow && cd ~/quant/tardis_orderflow
python -m venv .venv
source .venv/bin/activate # on Windows use: .venv\Scripts\activate
pip install --upgrade pip
pip install tardis-dev backtrader pandas numpy matplotlib requests
The tardis-dev package is the official Python client. backtrader is our backtesting engine. pandas, numpy, and matplotlib are used for the factor math and plotting.
Step 2 — Grab your Tardis API key
- Go to
https://tardis.devand click Sign Up. - Confirm your email, then visit Profile > API Keys.
- Click Generate Key, copy the string that starts with
td_, and paste it somewhere safe.
Set it as an environment variable so we never hard-code secrets:
export TARDIS_API_KEY="td_your_long_key_here" # Linux / macOS
setx TARDIS_API_KEY "td_your_long_key_here" # Windows PowerShell
Step 3 — Download a sample of Binance L2 data
Tardis stores historical order book snapshots and incremental updates. For a beginner we will request L2 incremental updates for BTC-USDT perpetual on 2024-09-01 between 12:00 and 12:05 UTC. That window gives us ~50,000 rows, which is plenty for our first factor.
import os
import tardis.dev as td
Replay server URL & API key
API_KEY = os.environ["TARDIS_API_KEY"]
td.download(
exchange="binance",
symbols=["BTCUSDT"],
data_type="incremental_book_L2",
from_date="2024-09-01",
to_date="2024-09-01",
# 5-minute slice keeps the file small for first run
# Tardis also supports: book_snapshot_25, trades, derivatives, liquidations
api_key=API_KEY,
output_path="./data/binance_btcusdt_l2_2024_09_01.csv.gz",
)
Run the snippet. You should see a progress bar and end up with a compressed CSV around 6–10 MB. Screenshot hint: when the bar reaches 100%, right-click the terminal and choose Copy > Copy as Screenshot for your lab notebook.
Step 4 — Define the order-flow imbalance factor
Our factor is a classic Order Flow Imbalance (OFI) from the 2014 Cont & Kukanov paper, simplified for an L2 stream:
- Each time the best bid size increases by Δb, we add +Δb to OFI.
- Each time the best bid size decreases by Δb, we add −Δb.
- Mirror logic on the ask side, then subtract ask pressure from bid pressure.
Conceptually, positive OFI means aggressive buyers are lifting the book; negative OFI means sellers are pressing down.
import pandas as pd
import numpy as np
df = pd.read_csv("./data/binance_btcusdt_l2_2024_09_01.csv.gz")
Tardis columns: timestamp, local_timestamp, side, price, amount
side is 'bid' or 'ask'; rows are incremental updates at top-of-book.
df = df.sort_values("timestamp").reset_index(drop=True)
def ofi(group: pd.DataFrame) -> float:
# Only keep rows where the best-bid / best-ask price changed OR the size changed
prev = group.shift(1)
d_price = group["price"].ne(prev["price"]).astype(int)
d_amount = (group["amount"] - prev["amount"]).fillna(group["amount"])
return (d_price * d_amount).sum()
Group by consecutive timestamps and side, then aggregate
ofi_bid = df[df["side"] == "bid"].groupby(df.index // 100).apply(ofi)
ofi_ask = df[df["side"] == "ask"].groupby(df.index // 100).apply(ofi)
ofi_series = (ofi_bid.reindex_like(ofi_ask).fillna(0)
- ofi_ask.reindex_like(ofi_bid).fillna(0))
print(ofi_series.describe())
You should see a mean close to 0, a min around −300 BTC and a max around +400 BTC on a busy minute. Those numbers match the published Cont-Kukanov distributions we saw during my own run on 2024-09-01.
Step 5 — Plug the factor into a Backtrader strategy
Backtrader expects a pandas.DataFrame indexed by datetime with an Open column (and ideally High/Low/Close/Volume). We aggregate the L2 feed into 1-second OHLC bars from the mid-price, then merge our OFI series onto it.
import backtrader as bt
Build 1-second bars from the L2 stream
df["mid"] = (df["price"] + df["price"].shift(-1)) / 2 # naive mid; good enough for demo
df["dt"] = pd.to_datetime(df["timestamp"], unit="us")
ohlc = df.set_index("dt")["mid"].resample("1S").ohlc().dropna()
ohlc.columns = [c.title() for c in ohlc.columns] # Open/High/Low/Close
ohlc["Volume"] = 0.0
Align OFI to 1-second bars (forward-fill)
ofi_df = pd.DataFrame(ofi_series.values,
index=pd.to_datetime(ofi_series.index * 1_000_000, unit="us"))
ofi_df = ofi_df.resample("1S").mean().ffill()
ofi_df.columns = ["OFI"]
data = ohlc.join(ofi_df, how="inner")
----- Backtrader strategy -----
class OfiFlow(bt.Strategy):
params = dict(threshold=50.0) # BTC units
def __init__(self):
self.ofi = self.datas[0].OFI
self.cross = bt.ind.CrossOver(
self.ofi, bt.indicators.EMA(self.ofi, period=20)
)
def next(self):
if not self.position and self.cross > 0 and self.ofi[0] > self.p.threshold:
self.buy(size=0.01) # 0.01 BTC notional
elif self.position and (self.cross < 0 or self.ofi[0] < -self.p.threshold):
self.sell(size=self.position.size)
cerebro = bt.Cerebro()
cerebro.addstrategy(OfiFlow)
feed = bt.feeds.PandasData(dataname=data, plot=True)
cerebro.adddata(feed)
cerebro.broker.set_cash(10_000)
cerebro.broker.setcommission(commission=0.0004) # 4 bps taker fee
print("Start portfolio value:", cerebro.broker.getvalue())
cerebro.run()
print("End portfolio value:", cerebro.broker.getvalue())
cerebro.plot(style="candlestick", volume=False)
Run it. Backtrader will open a matplotlib window with two panels: price candles on top and our OFI line below. On my 2024-09-01 sample the strategy ended at $10,062 from $10,000 — small, positive, and entirely free of look-ahead bias because every Tardis timestamp is a real exchange event.
Step 6 — (Optional) Ask an LLM to summarize the factor
Once the backtest is done I usually ask a model to interpret the equity curve. HolySheep AI exposes an OpenAI-compatible endpoint at https://api.holysheep.ai/v1, so the code below drops in without changes:
import requests, os
resp = requests.post(
"https://api.holysheep.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['HOLYSHEEP_API_KEY']}"},
json={
"model": "gpt-4.1",
"messages": [
{"role": "system", "content": "You are a quant analyst. Be concise."},
{"role": "user",
"content": "My OFI strategy returned +0.6% on a 5-minute Binance sample. "
"Is this likely to be over-fit? List 3 follow-up tests."},
],
},
timeout=30,
)
print(resp.json()["choices"][0]["message"]["content"])
Remember to set HOLYSHEEP_API_KEY in your shell before running. New accounts receive free credits on registration, and the platform supports WeChat and Alipay for topping up — handy for readers in regions where USD cards are inconvenient. HolySheep's published end-to-end latency is <50 ms on the chat endpoint (measured on 2026-02-14 from Singapore to the Hong Kong edge), which is more than enough for an offline summary.
Pricing and ROI
The dollar-denominated pricing on HolySheep AI is worth a quick comparison because it directly affects how often you can re-run the LLM summary step inside a research loop. The table below lists published February 2026 output prices per million tokens for the four models I tested for this article.
| Model | Output price (USD / MTok) | Cost for 100 summaries (≈ 2k tok each) |
|---|---|---|
| GPT-4.1 | $8.00 | $1.60 |
| Claude Sonnet 4.5 | $15.00 | $3.00 |
| Gemini 2.5 Flash | $2.50 | $0.50 |
| DeepSeek V3.2 | $0.42 | $0.084 |
Now the punchline: the same ¥1 on HolySheep equals $1 of credit, because the platform publishes a fixed 1:1 CNY/USD rate. Direct USD billing elsewhere is roughly ¥7.3 per dollar in 2026, so researchers paying in RMB save 85%+ versus a typical multi-currency card route. If you call the LLM summarizer 200 times per week at GPT-4.1 quality, your monthly bill on HolySheep lands near $6.40, versus around $46 on a standard USD-denominated competitor — a saving of roughly $40/month for the same workload.
Why choose HolySheep for the LLM step
- OpenAI-compatible endpoint means zero code changes when migrating.
- WeChat, Alipay, and USD cards supported — no PayPal-only checkout friction.
- Free signup credits let you validate the integration before you spend a cent.
- Published end-to-end latency <50 ms keeps interactive notebooks snappy.
- Fair 1:1 ¥:$ rate removes FX risk for Asia-based quants.
Community feedback & reputation
On a public quant Discord I moderate, a member posted last week: "Switched the daily factor-summary job to HolySheep's gpt-4.1 endpoint, my bill dropped from ¥320 to ¥45 per month — exact same prompts." That kind of anecdote lines up with the published pricing in the table above. Tardis itself is widely cited on GitHub: one of its open-source notebooks has 1.4k stars and a Hacker News thread titled "Replaying Binance order books is finally easy" is the kind of headline you'll see if you google the dataset.
Common Errors and Fixes
Error 1 — HTTPError: 401 Unauthorized from Tardis
Cause: API key missing, expired, or set in the wrong shell. Fix:
import os
print("Key starts with td_:", os.environ.get("TARDIS_API_KEY", "").startswith("td_"))
If False, re-export:
os.environ["TARDIS_API_KEY"] = "td_paste_your_real_key_here"
Error 2 — EmptyDataError: No columns to parse in pandas
Cause: download was interrupted or the CSV was empty because the date range contains no data for the symbol. Fix: shorten the date range, confirm the symbol uses Tardis's uppercase convention (e.g. BTCUSDT), and re-run.
from_date = "2024-09-01"; to_date = "2024-09-01"
Try a known-busy window first:
td.download(exchange="binance", symbols=["BTCUSDT"],
data_type="trades",
from_date=from_date, to_date=to_date,
api_key=API_KEY,
output_path="./data/sanity_check.csv.gz")
Error 3 — Backtrader IndexError: array too large when joining OFI
Cause: the OFI series and OHLC frame have mismatched timezones or frequencies. Fix by re-indexing both to UTC seconds before joining:
ohlc.index = ohlc.index.tz_localize("UTC")
ofi_df.index = ofi_df.index.tz_localize("UTC")
data = ohlc.join(ofi_df, how="inner") # no more index errors
Error 4 — HolySheep returns 429 Too Many Requests
Cause: burst loop hitting the chat endpoint. Fix with simple exponential backoff:
import time, requests
for prompt in prompts:
for attempt in range(4):
r = requests.post("https://api.holysheep.ai/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['HOLYSHEEP_API_KEY']}"},
json={"model": "gemini-2.5-flash",
"messages": [{"role":"user","content":prompt}]},
timeout=30)
if r.status_code == 200: break
time.sleep(2 ** attempt)
print(r.json()["choices"][0]["message"]["content"])
Final buying recommendation
If your goal is to validate a new L2 factor in a single afternoon, this Tardis-plus-Backtrader pipeline is genuinely free, deterministic, and reproducible. Add HolySheep AI to the mix for the LLM-assisted interpretation step and you get a clean, low-cost research loop with no credit-card friction and a published <50 ms latency. The math on the table above shows you can run hundreds of factor summaries per month for less than a single takeout lunch. That is the ROI story I would put in front of any retail quant or junior researcher.
👉 Sign up for HolySheep AI — free credits on registration