Class BenchmarkExecutor
java.lang.Object
com.quantfinlib.execution.BenchmarkExecutor
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
- The benchmark curve — each
BenchmarkExecutor.Benchmarkdefines 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. - 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.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic enumThe benchmark this parent is measured against.static final recordA snapshot of the real-time inputs a benchmark algo evaluates. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final doubleHow hard a 1% relative trading cost (spread fraction of mid plus impact as a fraction) damps aggression: the term is1/(1 + cost × SPREAD_SENSITIVITY), so a 1% cost halves the pace and a 2-pip FX spread (~0.002%) barely registers. -
Constructor Summary
ConstructorsConstructorDescriptionBenchmarkExecutor(Side side, long parentQty, BenchmarkExecutor.Benchmark benchmark, double participationRate, double alphaUrgency, double maxDepthFraction) -
Method Summary
Modifier and TypeMethodDescriptionbooleandone()longdueQuantity(double scheduleFraction, BenchmarkExecutor.MarketState m) Shares to send now, given the current market and how far through the schedule we are.longexecuted()longstatic BenchmarkExecutorof(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.voidonFill(long qty) Our own child fill.voidonMarketVolume(long qty) A market print that was NOT our fill (drives VWAP/POV realized volume).longstatic BenchmarkExecutorPOV convenience.doubleRealized participation vs other-flow volume (NaN before any market print).longdoublescheduleDrift(double scheduleFraction, BenchmarkExecutor.MarketState m) Schedule drift: executed fraction minus the benchmark's target fraction atscheduleFraction.side()
-
Field Details
-
SPREAD_SENSITIVITY
public static final double SPREAD_SENSITIVITYHow hard a 1% relative trading cost (spread fraction of mid plus impact as a fraction) damps aggression: the term is1/(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 executebenchmark- the benchmark to trackparticipationRate- 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 — usepov(com.quantfinlib.orderbook.Side, long, double). -
pov
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
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 atmaxDepthFraction × displayedDepth.- Parameters:
scheduleFraction- elapsed fraction of the execution horizon in [0, 1] (wall-clock progress); ignored by PARTICIPATION, which is volume-drivenm- 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
Schedule drift: executed fraction minus the benchmark's target fraction atscheduleFraction. Positive = ahead, negative = behind. For PARTICIPATION this compares against the participation target instead of the time curve. -
side
-
benchmark
-