N°05 / Build
Historical backtesting coming soon
The deterministic harness and in-memory tick source work today. File and venue historical sources still raise NotImplementedError, so this is not yet a complete historical-data product.
Why this design
We don’t want a separate “backtest framework” with its own interface — that creates two codebases for one strategy. Instead we keep the Strategy / Venue / Context contracts and swap the implementations beneath them:
- Hosted paper:
Polymarket()→ current market data →PaperContext - Backtest:
HistoricalVenue→ tick stream from aTickSource→BacktestContext
Same Strategy code, same risk envelope, same handlers. Only the plumbing differs.
Use the synthetic developer harness
from decimal import Decimal
from banger.backtest import Backtester, InMemoryTickSource
from my_strategy import MyStrategy
# Load ticks from wherever — in-memory list, parquet, Polymarket export, ...
source = InMemoryTickSource(ticks=[...])
result = Backtester(
MyStrategy,
source,
starting_capital=Decimal("10000"),
).run()
print(result)
# → BacktestResult(return=$5.67 (0.06%), max_dd=$107.49, trades=118, sharpe=0.022)Tick sources
A TickSource is anything that yields (Market, Tick) pairs in chronological order:
InMemoryTickSource(ticks)— pass a list. Useful for unit tests and small smoke runs.ParquetTickSource(path)— reads a parquet file with our standard schema. (Coming soon — stub raises NotImplementedError today.)PolymarketHistoricalSource(asset_ids, start, end)— pulls from Polymarket’s historical data API. (Coming soon.)
Roll your own
from banger.backtest import TickSource
class MyCsvSource(TickSource):
def __init__(self, path):
self.path = path
async def universe(self):
# Return all Markets present in your data.
...
async def stream(self):
# Async generator yielding (Market, Tick) pairs in time order.
...BacktestResult
Returned by Backtester.run():
result.summary # dict with total_return_usd, max_drawdown_usd, sharpe, win_rate, trades, ...
result.equity_curve # list of (timestamp, equity_usd) pairs
result.fills # list of every paper fill
result.save_csv("equity.csv") # equity curve as CSVDemo
See packages/sdk-python/examples/backtest_demo.py in the repo for a working end-to-end backtest against a synthetic random-walk source.
Caveats
- The harness uses a simplified fill model. It does not reproduce historical queue position, latency, or complete depth, and a non-crossing limit may be assumed filled.
- Position sizing via
ctx.kelly()assumeswin_prob = 0.5as a placeholder — pass your own edge calculation if precision matters. - Sharpe in the summary is a crude tick-bucketed approximation. For real analysis, dump the equity curve and compute properly downstream.