Slippage and Fill Quality: Market Orders vs Limit Orders
The backtest curve slopes up and to the right, but the live account goes nowhere. Anyone who has run an automated strategy knows the feeling. Most of the time the culprit isn't the strategy—it's execution. The gap between the price on your screen and the price your order actually filled at: that's slippage.
⚠️ This article is for educational and informational purposes. It does not guarantee returns from any particular technique, and you alone are responsible for any losses.
1. Where Slippage Comes From
Backtests generally assume "filled at the close." In live trading, a market order eats through the order book from the top down. If the size resting at the best ask is smaller than your order, the remainder walks up to the second and third levels and your average fill price slips.
Send a market buy for 500 shares:
Ask level 1 10,000 × 300 shares → 300 filled
Ask level 2 10,005 × 200 shares → 200 filled
Average fill = (10,000×300 + 10,005×200) / 500 = 10,002
→ 2 (0.02%) of slippage versus the 10,000 on screen
The wider the spread, the thinner the book, and the larger the order, the bigger this loss. On a round trip (entry plus exit) you pay slippage twice.
2. Market vs Limit — What Are You Giving Up?
Brokerage APIs usually distinguish order types by code. Real bot code handles it like this.
OrderType string // "00" limit, "01" market
- Market ("01"): the fill is all but guaranteed, but you surrender the price. Right for urgent exits and stop-losses—at the cost of accepting slippage.
- Limit ("00"): you control the price but may surrender the fill. If the market never comes to your number, the order just sits there unfilled.
The question is which hurts more. For strategies where the entry price is the edge—arbitrage, pairs—limit orders are the rule. Conversely, when a trend has broken and you must get out, a market order is the safe choice.
3. Order Size Against Book Depth
Subscribe to real-time depth and you can calculate in advance how many levels your order will consume before you send it. A bot holds quote data like this.
type RealtimeQuote struct {
AskPrice float64 // best ask
BidPrice float64 // best bid
AskQty int64 // size resting at the best ask
BidQty int64 // size resting at the best bid
}
// Warn about slippage if the order exceeds the size at the best level
func checkDepth(orderQty, askQty int64) bool {
if orderQty > askQty {
// Can't be absorbed at level 1 → consider splitting or a limit order
return false
}
return true
}
If your order fits inside the size at the best level, slippage is effectively zero. If it doesn't, you're better off splitting the order into several tranches, or switching to a limit order and letting the market come to you.
4. Unfilled Orders — Sending Isn't the End
Use limit orders and managing unfilled orders comes with them, unavoidably. A resting order left unattended can fill long after the market has moved on, handing you a position you no longer want.
- Timeout cancel: if it isn't filled within N seconds, cancel and re-evaluate. A mechanism for admitting "this price is no longer valid."
- Chase: cancel, then re-post one tick along with the current quote. Limit the number of attempts—chasing forever invites the very slippage you were avoiding.
- Partial fills: track precisely that 300 of 500 shares filled. Leave the remaining 200 out of your math and your position accounting drifts.
if !filled && elapsed > orderTimeout {
cancelOrder(orderID) // cancel the unfilled order
if retries < maxChase {
placeLimit(currentBid) // re-post at the current quote
retries++
} else {
placeMarket() // give up on price, take the certain fill
}
}
Summary
- The gap between backtest and reality is mostly slippage — "filled at the close" is a lie
- Market orders buy the fill; limit orders buy the price. If your edge is the price, use limits
- Compare resting size against order size before sending, to predict and split around slippage
- Limit orders come as a set with timeouts, chasing, and partial-fill handling
What separates live returns isn't a flashy entry signal—it's the craft of turning that signal into a fill without leaking. Slippage of 0.1% looks trivial, but for a bot doing dozens of round trips a day it's enough to flip an entire strategy into the red.
댓글
댓글 쓰기