The paper trading system simulates realistic order fills using PaperVenue which wraps MockVenue. Fills are triggered when real-time Polymarket book updates cross resting order prices.
When your order is immediately marketable:
- Buy fills when:
order.price >= best_ask * (1 + slippage) - Sell fills when:
order.price <= best_bid * (1 - slippage)
Fill price calculation (adverse selection):
// Buys get the WORSE of limit price or slipped ask
fill_price = min(order_price, ask * (1 + slip))
// Sells get the WORSE of limit price or slipped bid
fill_price = max(order_price, bid * (1 - slip))This models the reality that aggressive orders typically get worse execution than their limit.
For orders that improve the book (set new best bid/ask):
- Triggers if:
random() < passive_fill_probAND order improves book - Fill price: Your limit price (favorable execution)
This models the case where a market taker hits your resting order.
Both fill types wait latency_ms before executing, then re-check if the order is still marketable at the current book state. This prevents fills that would have been cancelled in real trading.
| Parameter | Default | Description |
|---|---|---|
slippage_bps |
10 | Slippage in basis points for aggressive fills |
passive_fill_prob |
0.1 | Probability (0-1) of passive fills when improving book |
latency_ms |
100 | Simulated execution delay in milliseconds |
- Main matching loop:
src/venue/paper.rs#L51-L126 - Aggressive fill price calc:
src/venue/paper.rs#L159-L170 - Passive fill execution:
src/venue/paper.rs#L175-L190 - MockVenue state management:
src/venue/mock.rs
BookUpdate (from Polymarket WS)
│
▼
PaperVenue::on_book_update()
│
├─► Check each resting order
│ │
│ ├─► Is marketable? (price crosses spread + slippage)
│ │ │
│ │ └─► Yes: Schedule aggressive fill after latency
│ │
│ └─► Is improving book AND random < passive_fill_prob?
│ │
│ └─► Yes: Schedule passive fill after latency
│
▼
After latency_ms sleep:
│
├─► Re-check if still marketable (book may have moved)
│
└─► Execute fill at calculated price
│
▼
Emit Event::Fill to engine