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.

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)