Execution

Replay historical L2 order books tick by tick. Orders fill against real book depth with queue priority, latency, and slippage. To research whether a signal is profitable over long periods, use Alpha. The SDK and backtest engine are open source on GitHub.

Quick Start

Orders fill against real historical liquidity with configurable latency, slippage, and platform-accurate fees.

Parameters
strategyStrategyrequired
Your Strategy subclass instance.
idstr | list[str]required
Market UUID, series slug, condition ID, or list thereof.
initial_cashfloatrequired
Starting capital in USD.
afterstr
Start of replay window (ISO 8601 or ms).
beforestr
End of replay window.
subtypestr | None= null
Backtest a single bet type of a series that bundles several, e.g. "moneyline". See Runs.
feesstr | None= "polymarket"
Trading fee model. Polymarket-accurate by default.
include_tradesbool= true
Include trade events (enables on_trade hook).
latency_msint= 50
Simulated order submission latency.
slippage_bpsint= 0
Additional slippage in basis points.
limit_fill_ratefloat= 0.1
Volume fraction that fills limit orders when queued.
queue_positionbool= false
CLOB queue position simulation for limit orders.
settlement_delay_msint= 5000
Delay before realized settlement cash becomes available (on-chain availability).
auto_mergebool= true
Net matched YES and NO shares back to cash; disable to keep them separate.
data_dirstr | PathLike | None= null
Local Parquet directory for offline replay. Downloads on first run when missing or empty.
progressbool= true
Show progress bars for fetching and backtesting. Auto-disables in non-TTY.
coalescebool | None= null
Trade-aligned compact data path (auto-detected when only on_trade is implemented).

Subtypes, results, saving, and the dashboard are shared across modes; see Runs.

python
from marketlens import MarketLens, Strategy class ValueBuyer(Strategy): def on_market_start(self, ctx, market, book): self._entered = False def on_trade(self, ctx, market, book, trade): if self._entered: return if book.midpoint < 0.35: ctx.buy_yes(size=100) self._entered = True client = MarketLens() result = client.backtest( strategy=ValueBuyer(), id="btc-up-or-down-5m", initial_cash=10_000, after="2026-04-15T01:45:00Z", before="2026-04-15T01:50:00Z", ) print(result.summary())

Strategies

Subclass Strategy and implement one or more hooks. The engine calls them on each replay tick.

Hooks
on_trade(ctx, market, book, trade)
Called when a trade executes. Primary hook for most strategies.
on_book(ctx, market, book)
Called on every order book update. Higher overhead, finer book granularity.
on_fill(ctx, market, fill)
Called when one of your orders fills.
on_reject(ctx, market, order)
Called when one of your orders is rejected or fails to submit.
on_market_start(ctx, market, book)
Called once when a market begins.
on_market_end(ctx, market)
Called when a market resolves or data ends.
python
class MyStrategy(Strategy): def on_trade(self, ctx, market, book, trade): if trade.size > 1000: ctx.buy_yes(size=100) def on_book(self, ctx, market, book): if book.imbalance(5) > 0.4: ctx.buy_yes(size=200) def on_fill(self, ctx, market, fill): ctx.cancel_all()

Context

Passed to every hook. Place orders, read positions, and query market state.

Orders
buy_yes(size, limit_price, cancel_after)Order
Buy YES outcome. Market order if no limit_price. cancel_after is absolute timestamp (ms).
buy_no(size, limit_price, cancel_after)Order
Buy NO outcome.
sell_yes(size, limit_price, cancel_after)Order
Sell YES position.
sell_no(size, limit_price, cancel_after)Order
Sell NO position.
split(size)None
Mint equal YES and NO shares for $1 per pair.
merge(size)None
Redeem matched YES and NO shares for $1 per pair.
cancel(order)None
Cancel a specific open order.
cancel_all()None
Cancel all open orders.
State
position()Position
Current position: side, shares, avg_entry_price, unrealized_pnl, realized_pnl.
yes_position()Position
YES shares only. position() returns the net of YES and NO.
no_position()Position
NO shares only.
cashfloat
Available cash balance.
equityfloat
Total equity (cash + positions marked to market).
open_orderslist[Order]
All open orders.
marketMarket
Current market object.
bookOrderBook
Current order book state.
timeint
Current simulation timestamp (ms).
booksdict
All active books by market_id (multi-market).
reference_price()float | None
Spot price of the underlying at current time.
python
# entry: market order or patient limit if book.spread < 0.03: ctx.buy_yes(size=500) else: ctx.buy_yes( size=500, limit_price=book.best_ask, cancel_after=ctx.time + 30000, )
python
# exit: take profit and clean up pos = ctx.position() if pos.shares > 0 and pos.unrealized_pnl > 100: ctx.sell_yes(size=pos.shares) ctx.cancel_all()

Order Book

L2 book state at the current replay tick. Properties: best_bid, best_ask, spread, midpoint, bid_depth, ask_depth.

Methods
impact(side, size)float | None
VWAP for a hypothetical order of the given size.
slippage(side, size)float | None
Price slippage from midpoint for the given size.
depth_within(spread)tuple[float, float]
Bid and ask liquidity within spread of mid.
microprice()float | None
Size-weighted midpoint using top-of-book.
weighted_midpoint(n)float | None
Size-weighted mid using top n levels.
spread_bps()float | None
Bid-ask spread in basis points.
imbalance(levels)float | None
Volume imbalance [-1, 1]. Positive = bid-heavy.
python
cost = book.impact("BUY", 500) if book.imbalance(5) > 0.3 and cost and cost < 0.65: ctx.buy_yes(size=500)