Install
pip install "aperiodic[polars]" pandas backtraderThe code
# pip install "aperiodic[polars]" pandas backtrader
from datetime import date
import aperiodic
import backtrader as bt
window = dict(
timestamp="exchange", interval="5m", exchange="binance-futures",
symbol="perpetual-BTC-USDT:USDT", start_date=date(2025, 5, 1),
end_date=date(2025, 5, 31), preview=True, output="pandas",
show_progress=False,
)
bars = aperiodic.get_ohlcv(**window).set_index("time")
flow = aperiodic.get_metrics(metric="flow", **window).set_index("time")
bars["buy_share"] = flow["taker_buy_volume"] / (
flow["taker_buy_volume"] + flow["taker_sell_volume"]
)
class AperiodicData(bt.feeds.PandasData):
"""OHLCV plus one Aperiodic metric as an extra line."""
lines = ("buy_share",)
params = (("buy_share", -1), ("openinterest", None))
class BuyShareStrategy(bt.Strategy):
def __init__(self):
self.held = 0
def next(self):
if not self.position and self.data.buy_share[0] > 0.7:
self.buy(size=0.01)
self.held = 0
elif self.position:
self.held += 1
if self.held >= 6: # 30 minutes
self.close()
cerebro = bt.Cerebro()
cerebro.adddata(AperiodicData(dataname=bars))
cerebro.addstrategy(BuyShareStrategy)
cerebro.broker.setcash(10_000)
cerebro.broker.setcommission(commission=0.0005)
cerebro.run()
print(len(bars), "bars,", round(cerebro.broker.getvalue(), 2), "final value")What it prints: 8928 bars, then the broker value at the end of the run.
How the data fits Backtrader
One line per metric
Add a column to the DataFrame, name it in lines and point its parameter at the column (-1 means "find it by name"). Funding, open interest or slippage work the same way as buy_share does here.
Where 0.7 comes from
In May 2025, 450 of the 8,928 bars had more than 70% of taker volume buying; 70.05% was the month’s 95th percentile. Pick your threshold from your own data.
Bars are stamped at their open
The time column is the start of the bar: the first 5-minute bar of May 2025 is 2025-05-01 00:00:00 UTC, and its metrics describe what happened until 00:05. A signal built from a bar is known at its close, so trade on it from the next bar.
Exchange time or arrival time
timestamp="exchange" builds every bar on the venue’s clock. timestamp="true" builds it on when each event reached our collectors closest to the exchange, so a bar holds only what a live strategy could have seen by its close. That is the clock to backtest on if you plan to trade live. The preview is exchange time only; local time comes with a plan.
Metrics join on time
Every dataset shares the same time column for a given symbol, interval and timestamp choice, so OHLCV, flow, funding or open interest join on the index without resampling.
The preview is one month of one coin
Without a key, every dataset previews the Binance BTCUSDT perpetual at 5 minutes for May 2025. With a free account’s key, drop preview=True and pass api_key= for every metric over 2025 on BTC, ETH, SOL and 10 RWA perps, on any venue, from 1 minute to 1 day. A plan opens every perp and every year.
Delisted coins are in the data
On a plan, the same call works for every perpetual on Binance, OKX and Hyperliquid, including the ones since delisted, so a cross-sectional backtest has no survivorship bias. Published bars are never recalculated, so it reads the same history next year.
Pull years at once
Set start_date and end_date years apart and the SDK downloads the parquet files in parallel, 10 at a time by default (max_concurrent). Prime and above have no API rate limits; Pro has high ones.
Beyond the preview
The snippet uses ohlcv and flow. Swap in any of the 220 metrics across 19 datasets (catalog). A free account gets all of them for 2025 on BTC, ETH, SOL and 10 RWA perps. For every perp and every year, funding, open interest, basis, slippage and price impact are on Pro, from $199 a month billed yearly, and L1 and L2 order book imbalance and liquidity are on Prime (pricing). The Backtrader documentation covers everything on its side.