> ## Documentation Index
> Fetch the complete documentation index at: https://polyquantlab.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Walk the book

> Realistic partial fills against L2 depth — the slippage model the backtest engine uses.

A naive backtest fills at mid. PolyQuantLab's engine **walks the book**
— consuming bids highest-first (when selling) or asks lowest-first
(when buying) until your dollar budget is filled.

## The algorithm

```python theme={null}
def walk_buy_book(orderbook, dollars_to_spend):
    """Walk asks lowest-first, spend up to dollars_to_spend.
    Returns (tokens_acquired, avg_fill_price, fully_filled)."""
    asks = orderbook.get("asks") or []
    remaining = dollars_to_spend
    tokens = 0.0
    for level in asks:
        price = float(level["price"])
        size = float(level["size"])
        cost_full = price * size
        if remaining >= cost_full:
            tokens += size
            remaining -= cost_full
        else:
            tokens += remaining / price
            remaining = 0.0
            break
    spent = dollars_to_spend - remaining
    avg = spent / tokens if tokens > 0 else 0.0
    return tokens, avg, remaining < 1e-4
```

A `walk_sell_book` mirror walks bids highest-first.

## Why it matters

Polymarket order books are **thin**. The 5m BTC market often shows
something like:

```
YES asks:  [0.41 × 200], [0.42 × 50], [0.45 × 30]
YES bids:  [0.39 × 180], [0.37 × 60], [0.35 × 25]
```

A $500 order at mid ($0.40) looks like it buys 1,250 tokens. But walking
the book it eats the $0.41 level (200 tokens = $82), then the $0.42
level (50 = $21), then the $0.45 level (30 = $13.50), then the next
levels that may not exist. Realistic avg fill: \~$0.43, ~$0.05 worse
than mid — and that's before the rest of the order can't fill.

The
[`/v1/backtest`](/docs/api-reference/backtest/run) engine models all of
this. There's also a `mid` fill mode for when you genuinely want the
naive comparison.

## Fees

On top of slippage, Polymarket charges a **taker fee** of `rate × p ×
(1 − p)` per share. For the 2026 rate of `0.072`:

* Trade at \$0.50 → fee = `0.072 × 0.50 × 0.50` = `0.018` (1.8 ¢ / share)
* Trade at \$0.95 → fee = `0.072 × 0.95 × 0.05` = `0.0034` (0.34 ¢ / share)

Fees collapse to zero at the extremes (which is why logical arbs work
clean) and peak at mid.

## In the backtest API

When you submit a strategy via [`/v1/backtest`](/docs/api-reference/backtest/run),
the `execution_params` block lets you control:

```json theme={null}
{
  "execution_params": {
    "fill_mode": "walk_book",
    "max_fill_price": 0.985,
    "size_usd": 7.5,
    "max_trades_per_market": 1
  }
}
```

* **`fill_mode`** — `"walk_book"` (default) or `"mid"` for the naive case.
* **`max_fill_price`** — refuse entries above this best-ask (avoid
  paying $0.99 for $0.99 of upside).
* **`size_usd`** — dollar size per trade.
* **`max_trades_per_market`** — cap.

The engine emits a per-trade slippage column in the response so you can
audit how much the book ate.
