Class PortfolioExecutor
BenchmarkExecutor child — its benchmark, curve and per-symbol
shaping stay intact — and the portfolio layer applies the two overlays
that only exist at basket level:
- Leg balance — the defining constraint of a two-sided
transition: the buy leg and the sell leg must stay in step, or the
basket carries unintended net market exposure mid-flight. When the
projected net filled notional (buys − sells, plus this interval's
dues) would breach
maxNetNotional, the interval throttles the leg that is ahead. It never accelerates the lagging leg — pushing a child past its own schedule would break the benchmark it is measured against; - Capacity allocation —
maxIntervalNotionalcaps the basket's total demand per interval (participation budget, cash constraint). When it binds, capacity goes to the symbols carrying the most residual risk. By default that is the diagonal approximation of multi-asset Almgren-Chriss — weight ∝ (1 + volatility regime) × due notional. Plug in a streamingEwmaCovarianceviauseRiskModel(com.quantfinlib.microstructure.EwmaCovariance)and it becomes the real thing: weight ∝ (1 + marginal contribution to BASKET variance) × due notional, so two correlated legs are recognized as one concentrated risk and a natural hedge earns no urgency.
Both overlays only ever reduce a child's own due quantity, so per-symbol benchmark integrity holds by construction, and anything deferred reappears through each child's behind-schedule catch-up next interval. A binding cap can therefore leave a residual at the horizon — that is the constraint's honest meaning, not a bug.
Usage: add(com.quantfinlib.execution.BenchmarkExecutor) each child once (buys and sells mixed freely),
then each interval call decide(double, com.quantfinlib.execution.BenchmarkExecutor.MarketState[], long[]) with per-symbol
BenchmarkExecutor.MarketState snapshots and route the returned
dues; report fills via onFill(int, long, double) (which also maintains the net
ledger). Notional arithmetic needs a price: the layer remembers the
last finite mid per symbol (and fill prices); a symbol that has never
shown a price passes through unscaled — the caps cannot see what they
cannot price. Cross-asset (notional is just quantity × price), zero
allocation per decide, single writer.
-
Nested Class Summary
Nested Classes -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionintadd(BenchmarkExecutor child) Registers a child parent order; returns its handle for decide/onFill.child(int handle) The child executor behind a handle — for progress/drift reads and for feedingonMarketVolumeto VWAP/POV children.voiddecide(double scheduleFraction, BenchmarkExecutor.MarketState[] states, long[] dueOut) One portfolio interval: asks every child for its own due quantity, then applies the leg-balance band and the capacity allocation.booleandone()doubleSigned net filled notional: buys − sells.voidonFill(int handle, long qty, double price) A fill for one child: forwards to its executor and maintains the net ledger.intsize()voiduseRiskModel(EwmaCovariance model) Upgrades the capacity allocation from the diagonal approximation to true basket risk: with a covariance model, a bindingmaxIntervalNotionalflows to the symbols whose REMAINING position contributes most to portfolio variance (EwmaCovariance.marginalContribution(double[], double[])) — two correlated buys carry more joint timing risk than their individual vols admit, and a natural hedge carries less.
-
Constructor Details
-
PortfolioExecutor
-
-
Method Details
-
useRiskModel
Upgrades the capacity allocation from the diagonal approximation to true basket risk: with a covariance model, a bindingmaxIntervalNotionalflows to the symbols whose REMAINING position contributes most to portfolio variance (EwmaCovariance.marginalContribution(double[], double[])) — two correlated buys carry more joint timing risk than their individual vols admit, and a natural hedge carries less. Handleimaps to covariance symboli; feed the model one return vector per interval on your own clock. Without a model (or before it has learned), the weight falls back to the per-symbol volatility regime. -
add
Registers a child parent order; returns its handle for decide/onFill. -
decide
One portfolio interval: asks every child for its own due quantity, then applies the leg-balance band and the capacity allocation.dueOut[handle]receives the shares to send per symbol.- Parameters:
scheduleFraction- elapsed fraction of the execution horizonstates- per-handle market snapshots (index = handle)dueOut- per-handle output, length >=size()
-
onFill
public void onFill(int handle, long qty, double price) A fill for one child: forwards to its executor and maintains the net ledger. A non-positive or non-finite price still advances the child's schedule but cannot enter the notional ledger.Report every fill through THIS method, never through
child(h).onFill(...)— the child call advances that symbol's schedule but silently bypasses the buy/sell notional ledger the leg-balance band reads, leaving the basket's net exposure uncontrolled while every per-child number looks healthy. -
netNotional
public double netNotional()Signed net filled notional: buys − sells. The leg-balance ledger. -
done
public boolean done() -
size
public int size() -
child
The child executor behind a handle — for progress/drift reads and for feedingonMarketVolumeto VWAP/POV children. Do NOT report fills viachild(h).onFill(...): fills must go throughonFill(int, long, double)so the leg-balance ledger sees them.
-