Reading Polymarket backtest results
Both the execution and alpha engines return the same result object. This guide covers what is in it: the metrics, the DataFrames underneath them, the dashboard, and how to compare and archive runs.
The summary metrics
result.summary() prints every metric at once; each is also a plain attribute:
print(result.summary()) # all metrics, formatted
result.total_pnl, result.total_return
result.win_rate, result.profit_factor, result.expectancy
result.max_drawdown, result.sharpe_ratio, result.sortino_ratio
result.total_fees, result.fee_drag_bps, result.turnoverTwo deserve special attention on Polymarket. fee_drag_bps tells you how much of the edge fees consumed, which decides whether a high frequency idea survives contact with the fee schedule. max_drawdown comes with max_drawdown_duration_ms, since a small but months long drawdown is a different problem from a deep fast one.
DataFrames for anything deeper
The metrics are aggregates over data you can get at directly:
result.settlements_df() # one row per settled market: P&L, fees
result.by_series() # the same, rolled up per series
result.trades_df() # every fill
result.orders_df() # full order lifecycle
result.equity_df() # equity curve time seriessettlements_df() is usually where the story is: a strategy that makes all its money in two markets out of fifty is telling you something. orders_df() answers execution questions, like how many limit orders never filled.
The dashboard
result.show() opens an interactive dashboard in the browser: equity curve, drawdown, per market settlements, and the trade tape. It works identically for execution and alpha runs.
Racing strategies
Pass a list of strategies and labels= to backtest them over the same window in one run:
result = client.backtest(
[Tight(), Loose()], "btc-up-or-down-5m",
labels=["tight", "loose"],
initial_cash=10_000, after=..., before=...,
)
result.show() # overlaid equity curves
result["tight"].summary() # each run individuallySaving and reloading runs
Runs archive to disk and restore later, dashboard included, so a good run survives your terminal session:
result.save("runs/tight-v3")
from marketlens.backtest import BacktestResult
result = BacktestResult.load("runs/tight-v3")FAQ
Common questions
What metrics does a Polymarket backtest report?
The summary covers P&L and return, win rate, profit factor, expectancy, average win and loss, payoff ratio, max drawdown and its duration, Sharpe and Sortino ratios, volatility, turnover, capital utilization, average holding time, trade and market counts, total fees, and fee drag. Each is also available as a plain attribute on the result.
How do I see per market P&L in a Polymarket backtest?
The settlements DataFrame has one row per settled market with its P&L and fees, and a per series rollup is available too. Separate DataFrames cover fills, the order lifecycle, and the equity curve.
How do I compare two Polymarket strategies?
Pass a list of strategies with labels to a single backtest call. The equity curves overlay in one dashboard, and each labelled run is accessible on its own.
Can I save a backtest run and reload it later?
Yes. A result saves to a directory on disk and loads back later, dashboard included, so runs can be archived, diffed, and shared without re-running.
Try it
Run one and read it yourself
The free tier includes 5M events per day with full API and full archive access, no card required.
$ pip install marketlens