Back to Pulse
EurekaLabs Pulse

Venue Selection in Smart Order Routing: Beyond Price Priority

Haruto Yamane
Venue Selection in Smart Order Routing: Beyond Price Priority

Regulation NMS requires that orders be routed to the national best bid or offer before being executed at an inferior price. This creates a floor on routing logic: at minimum, your SOR needs to know the NBBO and route to the venue posting it. Most teams start here. The gap between "at minimum" and "genuinely good routing" is where execution quality differences emerge.

This post describes the additional factors that improve venue selection quality beyond price priority, how we incorporate them in the EurekaLabs routing model, and where the tradeoffs lie between scoring precision and routing latency budget.

Why Price Priority Alone Is Insufficient

Consider a scenario: AAPL is quoted at $186.42 bid / $186.43 ask across seven lit venues. Your SOR needs to buy 500 shares. All seven venues are posting the same $186.43 ask. Price priority gives you no discriminating information. You could route to any of them. The differences between venues in this moment include:

  • Quoted depth at $186.43: one venue has 2,000 shares available, another has 100
  • Historical fill rate for this symbol at this price level over the last five minutes
  • Current spread at this venue versus the composite (are they exactly at NBBO or inside it?)
  • Adverse selection rate at each venue over the last rolling window
  • Current queue position estimate if you have open passive orders at this venue

Each of these factors affects the expected execution outcome. Routing to the venue with 100 shares available when you need 500 virtually guarantees a partial fill, potentially followed by a marketable residual order that will pay spread cost. Routing to the venue with the highest adverse selection rate means a higher probability of trading against informed flow. A good scoring function captures all of these factors and combines them into a single comparative metric per venue.

The Venue Score Components

Our scoring model computes a composite score for each candidate venue on every routing decision. The score combines four sub-components:

Available depth score: The displayed quantity at the best available price at this venue as a fraction of the order size we are trying to fill. A venue with 5x the order size available scores higher than one with 0.5x. This is the most important factor for orders above a few hundred shares.

Spread-adjusted price score: For aggressive orders, we want to minimize the effective execution price. At times when a venue is trading inside the NBBO (perhaps via price improvement mechanisms), that is worth routing there even if the depth is lower. The score term for this is the difference between the venue's current best price and the composite NBBO, normalized by the average spread for the symbol over the session.

Fill rate score: A rolling window fill rate, updated continuously from our own execution history at each venue for the current symbol. We track fill rate over the last 50 orders at this symbol-venue pair, decayed toward the population mean for venues with thin history. This captures real-time liquidity quality without requiring a persistent statistical model.

Adverse selection signal: Post-fill price drift over a 1-second window, averaged over recent fills at this venue-symbol pair. A venue with consistently negative price drift after our fills (price moves away from us after we are filled) is penalized in the score. This is the most latency-intensive term to compute because it requires a lookback over recent fills.

Latency Budget Implications

Each scoring term has a computational cost. Available depth is a single integer comparison on data already in cache: negligible. Spread-adjusted price requires a subtraction and a division: under 10 ns with floating-point ops on modern CPUs. Fill rate requires a lookback over the rolling window: 50 to 100 ns depending on cache state. Adverse selection requires computing a mean over recent fills: 100 to 200 ns with SIMD.

The total scoring computation for 15 venues with all four terms runs approximately 400 to 600 ns in our testbed. This is a meaningful fraction of a sub-10-us routing budget. The tradeoff we make: for orders below a minimum notional size threshold, we skip the adverse selection term and use only the first three components. The adverse selection term has the smallest marginal benefit for small orders and the highest computational cost. Dropping it for small orders saves 150 to 200 ns per routing decision.

Dynamic Re-Scoring Under Queue Depletion

A common failure mode in simple routing implementations is sending a large order to the top venue and watching the depth disappear as your order arrives. If you are routing 1,000 shares to a venue that shows 800 shares at the ask and four other orders arrive at the same time, you will receive a partial fill for 200 shares and a partial cancel. The remaining 800 shares need to be re-routed.

Our routing logic handles this with a residual re-routing step. When a partial fill acknowledgment arrives from the exchange, the residual is immediately re-scored against current venue conditions (not the conditions at initial order entry) and re-routed. This adds one routing decision latency to the residual but avoids the failure mode of chasing a depleted book with the same venue selection.

The re-routing adds complexity: you need to track outstanding partial fills, recognize when a cancel-on-partial is returning quantity, and resubmit the residual before conditions change further. The feed handler needs to be monitoring the venues where your partial fills happened so that the residual re-score uses fresh data.

Venue Selection for Passive Orders

Everything above applies to aggressive (marketable) order routing. Passive order routing (limit orders resting in the book) uses a different scoring logic because the success metric is different. For passive orders, you care about fill probability and queue position, not immediate execution speed.

The key additional metric for passive routing is time-in-queue: how long does a passive order at this price level at this venue typically wait before being filled, given current depth? This is a per-venue, per-symbol statistic that requires historical execution data at depth resolution. A venue that shows 500 shares of depth but has had those 500 shares sitting unchanged for 30 seconds is a low-fill-probability venue for a new passive order. A venue that turns over its depth every 3 to 5 seconds is much better for passive execution.

Queue turnover rate is one of the statistics we track in the EurekaLabs feed analytics layer and make available to the routing scoring function as a pre-computed table entry, updated on a 30-second rolling window. Using it requires an order book that tracks not just current depth but the age of each price level's quantity.

Where the Model Has Limits

We are not claiming that this scoring model always outperforms price-priority routing. For small, liquid instruments with tight spreads and balanced depth across venues, the additional scoring terms often change the venue selection only marginally, and the routing cost savings are small relative to the complexity introduced. The model is most valuable for larger orders in less liquid symbols where depth imbalances are significant and adverse selection rates vary meaningfully across venues.

The model is also backward-looking by design: fill rates and adverse selection scores are computed from your own execution history, which means a newly added venue starts with priors that may not reflect its true liquidity profile. We initialize new venues with population-mean priors and decay toward actual observed stats over the first 200 fills at that venue-symbol pair.

EurekaLabs

See the Infrastructure Behind These Numbers

Request access to the EurekaLabs platform and run your own latency benchmark against your current infrastructure.