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 per-decision cost is microseconds, spent once per parent slice, not per tick. 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 TypeMethodDescriptionstatic 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). -
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:allIn × [1 + (1−fillRate)×missPenalty + latency×urgency + adverseSelection] (buys; the adjustments subtract for sells)where adverseSelection is the venue's negative post-fill markout as a fraction of price (a venue with favorable or unknown markout pays nothing extra). and 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), capped atmaxDarkFraction × quantity. -
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.
-