Class AdaptiveSor

java.lang.Object
com.quantfinlib.execution.AdaptiveSor

public final class AdaptiveSor extends Object
The full-checklist smart order router: where 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 VenueScorecard fill 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 advertised VenueQuote.latencyNanos once 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 to QueueModel so 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.

  • Constructor Details

  • Method Details

    • register

      public void register(String venue, int scorecardId)
      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

      public VenueScorecard scorecard()
      The scorecard this router learns from (feed fills/misses/probes to it).
    • route

      public AdaptiveSor.RoutingDecision route(Side side, long quantity, List<VenueQuote> venues)
      Routes a marketable parent of quantity. 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 at maxDarkFraction × quantity.
    • passiveFillProbability

      public static double passiveFillProbability(long qtyAhead, long orderQty, double expectedTradedQty)
      Fill probability for a PASSIVE child joining a queue with qtyAhead ahead of it — the queue-position leg of the routing checklist, delegated to QueueModel. Feed qtyAhead from marketdata.L3BookBuilder.sharesAhead when you track the order, or the displayed level size before you join.