Class Backtester

java.lang.Object
com.quantfinlib.backtest.Backtester

public final class Backtester extends Object
Event-driven, single-instrument, long-only backtesting engine.

Fills. The strategy decides at bar i's close (its indicators include close(i)). The order fills according to BacktestConfig.fillPolicy(): at open(i+1) by default, at close(i+1), or at close(i) (the optimistic zero-latency mode). Fills are adjusted by slippageRate against the trader and commission is charged on entry and exit notional. A signal on the last bar under a next-bar policy has nothing to fill against and is dropped.

Stops. Stop-loss and take-profit levels are checked intrabar against the bar's low and high, from the first bar whose whole range lies after the entry: the entry bar itself for a next-open entry, the following bar for close entries. A gap through the level fills at the open. Stop fills also pay slippageRate: a stop is a market order and fills worse than its trigger in a fast market. When both levels lie inside one bar's range the stop wins, since the bar data cannot say which was touched first and assuming the loss is the conservative reading.

Order of events on bar i. (1) a pending next-open order fills at open(i); (2) stops are checked; (3) a pending next-close order fills at close(i); (4) the strategy is asked for a signal via TradingStrategy.onBar(int, double) with the quantity now held; (5) under CLOSE the signal fills immediately, otherwise it waits. A pending order is dropped when it no longer applies (a stop already closed the position, or the book is already in the requested state).

Segments. runSegment(TradingStrategy, BarSeries, BacktestConfig, int, Backtester.State, int, boolean) runs over a window with a starting Backtester.State and returns the end state instead of liquidating, which is how WalkForwardAnalyzer carries a position across fold boundaries without a forced exit and re-entry per fold.

  • Method Details

    • run

      public static BacktestResult run(TradingStrategy strategy, BarSeries series, BacktestConfig config)
    • run

      public static BacktestResult run(TradingStrategy strategy, BarSeries series, BacktestConfig config, int tradeFrom)
      Variant with a warm-up prefix: indicators are initialised over the whole series, but no signal is acted on (and no equity recorded) before tradeFrom. Walk-forward analysis uses this to avoid re-computing every indicator cold on each test window. The returned equity curve covers [tradeFrom, n). Any position open at the last bar is closed there at close(n−1) (Trade.REASON_END_OF_DATA).

      The warm-up only warms what TradingStrategy.init(BarSeries) precomputes (all shipped strategies); onBar is not called for warm-up bars.

    • runWholeLots

      public static BacktestResult runWholeLots(TradingStrategy strategy, BarSeries series, BacktestConfig config, double lotSize)
      As run(TradingStrategy, BarSeries, BacktestConfig) but entries buy a whole number of lots instead of investing all cash.

      The plain idea: a real account cannot hold 37.4 shares. An entry buys the largest multiple of lotSize that cash covers after slippage and commission, and the rest stays in cash. Formally, with fill price f and commission rate c, the quantity is q = lot * floor(cash / (f (1 + c) lot)); the entry pays q f (1 + c). When q = 0 the signal is dropped. Worked example (own test): cash 1000, price 30, lot 1, no costs gives q = 33 and 10 left in cash, which is what the tick backtester and the paper gateway fill.

      Parameters:
      lotSize - positive lot size, 1 for whole shares
      Since:
      2.2.0
    • runSegment

      public static Backtester.Segment runSegment(TradingStrategy strategy, BarSeries series, BacktestConfig config, int tradeFrom, Backtester.State start, int indexOffset, boolean liquidateAtEnd)
      Runs bars [tradeFrom, n) of series starting from start. Trade indices are reported as local index + indexOffset, so a caller running on a slice of a longer series passes the slice's starting index and gets indices into the full series. start.entryIndex() is absolute in the same sense.
      Parameters:
      liquidateAtEnd - close any open position at close(n−1) (as run(TradingStrategy, BarSeries, BacktestConfig) does); otherwise carry it in the returned state