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)). Unlike a precomputed slice list (TwapScheduler, VwapScheduler, ImplementationShortfallScheduler), it 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 and 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, 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.

In-flight children

Report each child with onChildSent(long) and, when the venue is done with it, the unfilled remainder with onChildDone(long). Sent but unfilled quantity counts against the schedule, so a child that rests unfilled is not sent again next interval; without this bookkeeping the deficit would be re-sent every interval while the first child still works. Fills reported through onFill(long) move quantity from open to executed.

Completion

Over the last 1 - rampStart of the horizon the depth cap is relaxed linearly from maxDepthFraction to all of the displayed depth, and at scheduleFraction >= 1 every curve's target is the full parent, so the whole unworked remainder is due, limited only by what is displayed and by the maximum child. A residual left at the horizon is then one the book could not absorb, visible as remaining(). Optional child-size bounds (withChildBounds(long, long)) suppress dribble orders and cap signalling.

Participation

The POV rate p is a share of total traded volume, including ours (the industry definition). With V the volume printed by others, executing x gives a share x / (x + V); setting this equal to p gives the target x = p / (1 - p) * V. Using p * V instead would realise only p / (1 + p) (9.1 % for a 10 % target).

  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static enum 
    The benchmark this parent is measured against.
    static final record 
    A snapshot of the real-time inputs a benchmark algo evaluates.
  • Field Summary

    Fields
    Modifier and Type
    Field
    Description
    static final double
    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.
  • Constructor Summary

    Constructors
    Constructor
    Description
    BenchmarkExecutor(Side side, long parentQty, BenchmarkExecutor.Benchmark benchmark, double participationRate, double alphaUrgency, double maxDepthFraction)
     
  • Method Summary

    Modifier and Type
    Method
    Description
     
    boolean
     
    long
    dueQuantity(double scheduleFraction, BenchmarkExecutor.MarketState m)
    Shares to send now, given the current market and how far through the schedule we are.
    long
     
    long
     
    of(Side side, long parentQty, BenchmarkExecutor.Benchmark benchmark)
    Sensible defaults: alpha urgency 1 (a full-scale normalized alpha of ±1 doubles/halves the pace, smoothly and never rail-pinned), child capped at 25% of displayed depth.
    void
    onChildDone(long unfilledQty)
    A child finished (cancelled, expired or IOC remainder) with unfilledQty never executed; that quantity stops counting as open.
    void
    onChildSent(long qty)
    A child order of qty was sent and is now working.
    void
    onFill(long qty)
    Our own child fill; reduces the open quantity by the same amount.
    void
    onMarketVolume(long qty)
    A market print that was not our fill (drives VWAP/POV realized volume).
    long
    Sent child quantity not yet filled or reported done.
    long
     
    pov(Side side, long parentQty, double participationRate)
    POV convenience.
    double
    Realised share of total volume, executed / (executed + others); NaN before any volume.
    long
     
    double
    scheduleDrift(double scheduleFraction, BenchmarkExecutor.MarketState m)
    Schedule drift: executed fraction minus the benchmark's target fraction at scheduleFraction.
     
    withChildBounds(long min, long max)
    Child-size bounds: below min nothing is sent (except to finish the parent), above max the child is capped.
    withCompletionRamp(double start)
    Schedule fraction in [0, 1) from which the depth cap is relaxed linearly towards the full remainder at the horizon (default 0.9).
    withShortfallUrgency(double kappaT)
    Urgency kT of the IMPLEMENTATION_SHORTFALL trajectory (Almgren-Chriss kappa times horizon, kappa = sqrt(lambda sigma^2 / eta)); must be finite and positive.

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • 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 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, smoothly and never rail-pinned), child capped at 25% of displayed depth. Participation must state its rate; use pov(Side, long, double).
    • pov

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

      public BenchmarkExecutor withShortfallUrgency(double kappaT)
      Urgency kT of the IMPLEMENTATION_SHORTFALL trajectory (Almgren-Chriss kappa times horizon, kappa = sqrt(lambda sigma^2 / eta)); must be finite and positive. Small values approach TWAP, large values front-load.
    • withChildBounds

      public BenchmarkExecutor withChildBounds(long min, long max)
      Child-size bounds: below min nothing is sent (except to finish the parent), above max the child is capped.
    • withCompletionRamp

      public BenchmarkExecutor withCompletionRamp(double start)
      Schedule fraction in [0, 1) from which the depth cap is relaxed linearly towards the full remainder at the horizon (default 0.9).
    • onChildSent

      public void onChildSent(long qty)
      A child order of qty was sent and is now working.
    • onChildDone

      public void onChildDone(long unfilledQty)
      A child finished (cancelled, expired or IOC remainder) with unfilledQty never executed; that quantity stops counting as open. Fills must already have been reported with onFill(long).
    • 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; reduces the open quantity by the same amount.
    • 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()
    • openQuantity

      public long openQuantity()
      Sent child quantity not yet filled or reported done.
    • parentQty

      public long parentQty()
    • marketVolume

      public long marketVolume()
    • done

      public boolean done()
    • realizedParticipation

      public double realizedParticipation()
      Realised share of total volume, executed / (executed + others); NaN before any volume.
    • 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()