Class BenchmarkExecutor

java.lang.Object
com.quantfinlib.execution.BenchmarkExecutor

public final class BenchmarkExecutor extends Object
The dynamic benchmark execution algorithm: one stateful executor that works a parent order toward any of the standard benchmarks — VWAP, TWAP, Arrival Price, Implementation Shortfall, Closing Price, Opening Price, and Participation (POV) — and, unlike a precomputed slice list (TwapScheduler, VwapScheduler, ImplementationShortfallScheduler), re-decides every interval from live market state. Cross-asset: prices/sizes are doubles, so it serves equities and FX identically.

Two layers

  1. The benchmark curve — each BenchmarkExecutor.Benchmark defines the fraction of the parent that should be complete by now. Time-driven benchmarks (TWAP linear, Arrival/IS front-loaded, Close back-loaded, Open aggressively front-loaded) use elapsed schedule fraction; volume-driven benchmarks (VWAP, POV) use the realized volume curve — VWAP against the expected cumulative profile, POV against a fixed share of actual volume.
  2. The dynamic adjustment — the raw "behind schedule" quantity is then shaped by the real-time inputs a production algo watches:
    • Alpha — expected short-term move: trade faster when the price is about to move against you (alpha in your trading direction), slower when it favors waiting;
    • Spread & volatility — the cost/urgency trade-off: a wide spread or (for passive benchmarks) high volatility damps aggression; for Arrival/IS, volatility raises urgency (timing risk);
    • Liquidity — the interval child is capped at a participation fraction of displayed depth, so the algo never asks for more than the book can give;
    • Fill rate / schedule drift — feed realized fills back via onFill(long); falling behind pulls the next child up, running ahead lets it ease off.

Usage: construct with the parent, benchmark and horizon; each interval call dueQuantity(double, com.quantfinlib.execution.BenchmarkExecutor.MarketState) with the current BenchmarkExecutor.MarketState and the elapsed schedule fraction (and, for volume benchmarks, feed market prints via onMarketVolume(long)); send the returned child; report fills via onFill(long). The executor is single-parent, single-writer.

  • Field Details

    • SPREAD_SENSITIVITY

      public static final double SPREAD_SENSITIVITY
      How hard a 1% relative trading cost (spread fraction of mid plus impact as a fraction) damps aggression: the term is 1/(1 + cost × SPREAD_SENSITIVITY), so a 1% cost halves the pace and a 2-pip FX spread (~0.002%) barely registers. A deliberate calibration constant, not a unit conversion.
      See Also:
  • Constructor Details

    • BenchmarkExecutor

      public BenchmarkExecutor(Side side, long parentQty, BenchmarkExecutor.Benchmark benchmark, double participationRate, double alphaUrgency, double maxDepthFraction)
      Parameters:
      side - buy or sell (sets the alpha sign convention)
      parentQty - total quantity to execute
      benchmark - the benchmark to track
      participationRate - POV target in (0,1] (ignored unless PARTICIPATION)
      alphaUrgency - how hard alpha shifts the pace (0 disables; ~5–20 typical)
      maxDepthFraction - cap each child at this fraction of displayed depth (0,1]
  • Method Details

    • of

      public static BenchmarkExecutor of(Side side, long parentQty, BenchmarkExecutor.Benchmark benchmark)
      Sensible defaults: alpha urgency 1 (a full-scale normalized alpha of ±1 doubles/halves the pace — smooth, never rail-pinned), child capped at 25% of displayed depth. PARTICIPATION must state its rate — use pov(com.quantfinlib.orderbook.Side, long, double).
    • pov

      public static BenchmarkExecutor pov(Side side, long parentQty, double participationRate)
      POV convenience.
    • onMarketVolume

      public void onMarketVolume(long qty)
      A market print that was NOT our fill (drives VWAP/POV realized volume).
    • onFill

      public void onFill(long qty)
      Our own child fill.
    • dueQuantity

      public long dueQuantity(double scheduleFraction, BenchmarkExecutor.MarketState m)
      Shares to send now, given the current market and how far through the schedule we are. Returns 0 when on/ahead of schedule or done; caps at the parent remainder and at maxDepthFraction × displayedDepth.
      Parameters:
      scheduleFraction - elapsed fraction of the execution horizon in [0, 1] (wall-clock progress); ignored by PARTICIPATION, which is volume-driven
      m - the live market snapshot
    • executed

      public long executed()
    • remaining

      public long remaining()
    • parentQty

      public long parentQty()
    • marketVolume

      public long marketVolume()
    • done

      public boolean done()
    • realizedParticipation

      public double realizedParticipation()
      Realized participation vs other-flow volume (NaN before any market print).
    • scheduleDrift

      public double scheduleDrift(double scheduleFraction, BenchmarkExecutor.MarketState m)
      Schedule drift: executed fraction minus the benchmark's target fraction at scheduleFraction. Positive = ahead, negative = behind. For PARTICIPATION this compares against the participation target instead of the time curve.
    • side

      public Side side()
    • benchmark

      public BenchmarkExecutor.Benchmark benchmark()