Class AdaptiveSor
SmartOrderRouter
ranks venues purely on fee-adjusted displayed price and HftSor
does the same at tick-path speed, this router prices in everything a
production SOR actually weighs:
- Displayed liquidity and fees/rebates — the all-in price, as before;
- Probability of fill / venue reliability — a venue's
VenueScorecardfill rate discounts its quote: expected cost adds(1 − fillRate) × missPenalty(the spread-ish cost of re-routing a faded child), and venues below a reliability floor are vetoed outright; - Latency — slower venues pay
latency × urgency: in a moving market, microseconds of delay are adverse selection. The scorecard's measured latency overrides the advertisedVenueQuote.latencyNanosonce observed; - Adverse selection — a venue whose fills are followed by
reversion (
VenueScorecard.postFillMarkout(int)negative) charges that reversion as a per-share cost: two identical quotes are not equal when one venue's fills systematically fade; - Hidden liquidity — dark pools are probed with sizes learned
from realized probe fills (
VenueScorecard.onDarkProbe(int, long)), seeded by a configurable default when a pool is still unknown; - Queue position — for the passive leg of a child (posting
rather than taking),
passiveFillProbability(long, long, double)delegates toQueueModelso placement decisions can weigh the queue they would join.
The plan for the worked example (Exchange A: 10,000 shares at 120µs;
Exchange B: 8,000 at 80µs, same price; Dark Pool: unknown) is exactly
the classic answer: B's 8,000 first (equal price, lower latency), the
remaining 2,000 to A, and a simultaneous dark probe. Probe legs are
additive and contingent (AdaptiveSor.RoutingDecision.probes): the
executor sends them alongside the lit legs and, on dark fills, cancels
lit remainder. The router plans; the executor manages overfill.
Cross-asset: prices are raw doubles, so the same router serves
equities (exchanges + dark pools) and FX (ECN/matching venues + mid-match
pools) — an FX "dark" venue is a mid-match session, and "fill rate" on an
FX venue is one minus its last-look reject rate. For routing over
per-LP tiered quote streams (the bank-stream side of FX), use
fx.LpRouter, which prices last-look rejects and hold time
natively from fx.LpScorecard.
Research/decision lane (allocates the plan); the decision runs once per parent slice, not per tick (timings in docs/BENCHMARKS.md). Venue identity is by name, mapped once to dense scorecard ids at registration.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordTunable penalties;AdaptiveSor.Config.defaults()is a sane starting point.static final recordThe routed plan:litlegs cover up to the requested quantity (best-expected-cost first);probesare additive contingent dark legs sent alongside;unroutedis the shortfall no eligible lit venue could absorb (0 on a fully routed order). -
Constructor Summary
ConstructorsConstructorDescriptionAdaptiveSor(VenueScorecard scorecard) AdaptiveSor(VenueScorecard scorecard, AdaptiveSor.Config config) -
Method Summary
Modifier and TypeMethodDescriptionmaxLatencyPenaltyBps(double bps) Caps the latency term of the expected cost atbpsof price (default: no cap).static doublepassiveFillProbability(long qtyAhead, long orderQty, double expectedTradedQty) Fill probability for a passive child joining a queue withqtyAheadahead of it — the queue-position leg of the routing checklist, delegated toQueueModel.voidMaps a venue name to its scorecard index.route(Side side, long quantity, List<VenueQuote> venues) Routes a marketable parent ofquantity.The scorecard this router learns from (feed fills/misses/probes to it).
-
Constructor Details
-
AdaptiveSor
-
AdaptiveSor
-
-
Method Details
-
register
Maps a venue name to its scorecard index. Register every venue once at setup; quotes from unregistered venues are routed on quote data alone (prior fill rate, advertised latency). -
maxLatencyPenaltyBps
Caps the latency term of the expected cost atbpsof price (default: no cap). Without a cap a venue with a pathological measured latency is pushed arbitrarily far down the ranking; with one it is charged at most this much and other terms still order it. -
scorecard
The scorecard this router learns from (feed fills/misses/probes to it). -
route
Routes a marketable parent ofquantity. Lit venues are ranked by expected cost per share, every term in price units:allIn + s × [px × ((1−fillRate)×missPenaltyBps + latencyBps) / 1e4 + adverseSelection]withs = +1for buys and-1for sells (a sell ranks by the highest expected proceeds),allIn = px (1 + s fee/1e4),latencyBps = min(latencyMs × urgencyBpsPerMs, maxLatencyPenaltyBps)and adverseSelection the venue's negative post-fill markout in price units (a venue with favorable or unknown markout pays nothing extra). Venues are swept best-first at displayed size. Every quoting dark venue gets a contingent probe leg at its midpoint, sized by learned hidden liquidity (or the configured default while unknown); the probes of all dark venues together are capped atmaxDarkFraction × quantity, filled in quote order. -
passiveFillProbability
public static double passiveFillProbability(long qtyAhead, long orderQty, double expectedTradedQty) Fill probability for a passive child joining a queue withqtyAheadahead of it — the queue-position leg of the routing checklist, delegated toQueueModel. FeedqtyAheadfrommarketdata.L3BookBuilder.sharesAheadwhen you track the order, or the displayed level size before you join.
-