Class PortfolioBacktester

java.lang.Object
com.quantfinlib.backtest.portfolio.PortfolioBacktester

public final class PortfolioBacktester extends Object
Multi-asset, long/short portfolio backtester: rebalances positions (possibly fractional and negative) toward the strategy's target weights at a configurable cadence, charging commission on traded notional. This is where the optimization package meets the backtester — feed optimizer weights, vol-target overlays, or momentum rankings straight in.

The survivorship-aware overload additionally consumes a PointInTimeUniverse and per-symbol cash dividends, closing the engine-side gaps that make naive backtests survivorship-biased:

  • Delistings terminate positions at lastClose × (1 + delistingReturn) on the event bar — a bankruptcy really costs −100%, instead of a forward-filled flat line;
  • Mergers convert positions to cash and/or acquirer shares at the recorded deal terms;
  • Index drops (membership ends, security lives on) force a sale at that bar's close — non-members are untradeable thereafter;
  • Cash dividends credit position × amount on the ex-date (shorts pay), so unadjusted price series carry the full total return and cash drag is real.

Feed this overload unadjusted prices (dividends are cash here; adjusted prices would double-count them) aligned via SeriesAligner.unionForwardFill; the forward-filled ghost bars of a dead symbol are harmless because its position terminates at the event. Signals, however, must not be computed on unadjusted prices: a 2:1 split reads as a −50% return. The price-pair overload hands the strategy an adjusted copy for signals (PortfolioStrategy.init(Map, Map)) while the engine trades, values and pays dividends on the unadjusted series. The engine cannot remove the bias in the data: the universe and delisting returns must come from a point-in-time dataset that includes dead tickers.

Fill timing. Target weights are decided at a bar's close and filled according to PortfolioBacktester.Config.fillPolicy(): at the next bar's open (default), the next bar's close, or the decision bar's own close (CLOSE, the optimistic zero-latency mode). Next-open orders are sized against the portfolio value marked at that open. Lifecycle events and dividends of the fill bar are processed first; dead and non-member names are not traded. A decision on the last bar under a next-bar policy is dropped.

Financing (PortfolioBacktester.Config.withFinancing(PortfolioBacktester.Financing)) accrues each bar on the book held over it: interest on positive cash (short proceeds sit in cash, so the short rebate is implicit), a margin rate on negative cash, and a borrow fee on short notional, per symbol or by default. Without it shorts, cash and leverage are free and a 150/50 book shows the same equity as an unlevered one.