Strategy Guide

Replay strategies against historical L2 order book state with tick-by-tick execution simulation. Unlike price-series backtests, orders fill against actual book depth with real liquidity constraints.

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_cashstrrequired
Starting capital in USD.
afterstr
Start of replay window (ISO 8601 or ms).
beforestr
End of replay window.
feesstr | None= "polymarket"
"polymarket" (crypto 0.25% / sports 1.75%), ZeroFeeModel(), or FlatFeeModel(rate).
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.
python
from marketlens import MarketLens, Strategy class ValueBuyer(Strategy): def on_book(self, ctx, market, book): pos = ctx.position() if not pos.shares and book.midpoint < "0.35": ctx.buy_yes(size="100") elif pos.shares and book.midpoint > "0.50": ctx.sell_yes(size=pos.shares) client = MarketLens() result = client.backtest( strategy=ValueBuyer(), id="sol-up-or-down-hourly", initial_cash="10000", after="2024-03-10", before="2024-03-14", ) print(result.summary())

Strategies

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

Hooks
on_book(ctx, market, book)
Called on every order book update. Primary hook for most strategies.
on_trade(ctx, market, book, trade)
Called when a trade executes. Requires include_trades=True.
on_fill(ctx, market, fill)
Called when one of your orders fills.
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_book(self, ctx, market, book): if book.imbalance(5) > 0.4: ctx.buy_yes(size="200") def on_trade(self, ctx, market, book, trade): if float(trade.size) > 1000: ctx.buy_yes(size="100") 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.
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.
cashstr
Available cash balance.
equitystr
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()str | 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 and pos.unrealized_pnl > "100": ctx.sell_yes(size=pos.shares) ctx.cancel_all()

OrderBook

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

Methods
impact(side, size)str | None
VWAP for a hypothetical order of the given size.
slippage(side, size)str | None
Price slippage from midpoint for the given size.
depth_within(spread)tuple[str, str]
Bid and ask liquidity within spread of mid.
microprice()str | None
Size-weighted midpoint using top-of-book.
weighted_midpoint(n)str | 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 < "0.65": ctx.buy_yes(size="500")

Results

Access metrics directly as properties or as a dict via result.summary().

Summary Metrics
total_pnlstr
Net profit/loss in USD.
total_returnfloat
Return as a fraction (0.034 = 3.4%).
win_ratefloat
Fraction of winning trades.
sharpe_ratiofloat | None
Annualized Sharpe ratio.
sortino_ratiofloat | None
Annualized Sortino ratio.
max_drawdownfloat
Maximum peak-to-trough drawdown as fraction of capital.
profit_factorfloat
Gross profit / gross loss.
expectancystr
Average profit per trade.
avg_holding_msint
Average holding time (ms).
capital_utilizationfloat
Average fraction of capital deployed.
fee_drag_bpsfloat
Total fees as bps of traded volume.
jsonResponse
{ "total_pnl": "342.5000", "total_return": "3.43%", "win_rate": "68.00%", "sharpe_ratio": "2.14", "sortino_ratio": "3.01", "max_drawdown": "1.80%", "profit_factor": "2.35", "expectancy": "7.2872", "avg_holding_ms": "720000", "capital_utilization": "42.00%", "total_trades": "47", "total_fees": "38.4200", "fee_drag_bps": "8.20" }

Multi-Market

Pass a list of IDs to replay multiple markets with shared capital. Use ctx.books to access all active order books.

See Examples for complete strategy implementations.

python
result = client.backtest( strategy=MyStrategy(), id=[ "eth-up-or-down-5m", "sol-up-or-down-5m", ], initial_cash="50000", )