Class PortfolioExecutor

java.lang.Object
com.quantfinlib.execution.PortfolioExecutor

public final class PortfolioExecutor extends Object
True multi-symbol portfolio-level scheduling: a basket (rebalance, transition, program trade) executed as one coordinated schedule rather than N independent parents. Each symbol keeps its own 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:
  1. 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;
  2. Capacity allocationmaxIntervalNotional caps 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 streaming EwmaCovariance via useRiskModel(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
    Modifier and Type
    Class
    Description
    static final record 
     
  • Constructor Summary

    Constructors
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    int
    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 feeding onMarketVolume to VWAP/POV children.
    void
    decide(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.
    boolean
     
    double
    Signed net filled notional: buys − sells.
    void
    onFill(int handle, long qty, double price)
    A fill for one child: forwards to its executor and maintains the net ledger.
    int
     
    void
    Upgrades the capacity allocation from the diagonal approximation to true basket risk: with a covariance model, a binding maxIntervalNotional flows 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.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

  • Method Details

    • useRiskModel

      public void useRiskModel(EwmaCovariance model)
      Upgrades the capacity allocation from the diagonal approximation to true basket risk: with a covariance model, a binding maxIntervalNotional flows 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. Handle i maps to covariance symbol i; 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

      public int add(BenchmarkExecutor child)
      Registers a child parent order; returns its handle for decide/onFill.
    • decide

      public void decide(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. dueOut[handle] receives the shares to send per symbol.
      Parameters:
      scheduleFraction - elapsed fraction of the execution horizon
      states - 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

      public BenchmarkExecutor child(int handle)
      The child executor behind a handle — for progress/drift reads and for feeding onMarketVolume to VWAP/POV children. Do NOT report fills via child(h).onFill(...): fills must go through onFill(int, long, double) so the leg-balance ledger sees them.