Ta4j Wiki

Documentation, examples and further information of the ta4j project

View the Wiki On GitHub

This project is maintained by ta4j Organization

Backtesting

Backtesting estimates how a strategy would have performed historically. In ta4j this is the core workflow: load a BarSeries, pick a Strategy, and measure the resulting TradingRecord with analysis criteria.

Pick the right driver

Driver When to use Highlights
BarSeriesManager Quick explorations or single strategies. Minimal setup, returns a TradingRecord immediately.
BacktestExecutor (new in 0.19) Large parameter sweeps, grid searches, inventorying many strategies. Parallel execution, runtime telemetry, streaming top-K filtering, progress callbacks.
BarSeriesManager manager = new BarSeriesManager(series);
TradingRecord record = manager.run(strategy);
BacktestExecutor executor = new BacktestExecutor(series);
BacktestExecutionResult result = executor.executeWithRuntimeReport(
        strategies,
        series.numFactory().numOf(1),
        Trade.TradeType.BUY,
        ProgressCompletion.logging("wiki.backtesting"));
List<TradingStatement> ranked = result.getTopStrategies(
        20,
        new ReturnOverMaxDrawdownCriterion(),
        new NetReturnCriterion());

What this does: the first block runs a single strategy through the traditional BarSeriesManager. The second block spins up BacktestExecutor, runs a batch of strategies while collecting runtime telemetry, and then ranks the resulting TradingStatements by ROMAD and net return. For a full working sample see ta4jexamples.backtesting.TopStrategiesExampleBacktest.

Get useful results

Measure what matters

AnalysisCriterion net = new NetReturnCriterion();
AnalysisCriterion buyHold = new VersusEnterAndHoldCriterion(new NetReturnCriterion());
AnalysisCriterion romad = new ReturnOverMaxDrawdownCriterion();
AnalysisCriterion drawdownRisk = new MonteCarloMaximumDrawdownCriterion();
AnalysisCriterion commissions = new CommissionsImpactPercentageCriterion();

System.out.println("Net return: " + net.calculate(series, record));
System.out.println("Vs buy & hold: " + buyHold.calculate(series, record));
System.out.println("Return / Max DD: " + romad.calculate(series, record));
System.out.println("Drawdown risk p95: " + drawdownRisk.calculate(series, record));
System.out.println("Commission drag: " + commissions.calculate(series, record));

Ta4j includes dozens of criteria organized by package:

Mix and match to build your own evaluation stack.

Compare strategies

Use AnalysisCriterion#chooseBest(...) to rank alternatives:

List<Strategy> candidates = List.of(meanReversion, trendFollowing, breakout);
Strategy best = romad.chooseBest(manager, candidates);

With BacktestExecutor, keep a ranked leaderboard after a batch run:

BacktestExecutionResult batch = executor.executeWithRuntimeReport(
        candidates,
        series.numFactory().numOf(1));
List<TradingStatement> topFive = batch.getTopStrategies(5, romad, net);

Visualize your backtests

After you have a TradingRecord, you can render candlesticks, trades, and indicator overlays using the ChartMaker helper that lives in the ta4j-examples module.

The builder API provides a clean, composable way to create and display charts:

TradingRecord record = manager.run(strategy);
ChartMaker chartMaker = new ChartMaker("target/charts");

ClosePriceIndicator closePrice = new ClosePriceIndicator(series);
SMAIndicator sma = new SMAIndicator(closePrice, 50);
EMAIndicator ema = new EMAIndicator(closePrice, 200);

chartMaker.builder()
    .withTradingRecord(series, strategy.getName(), record)
    .addIndicators(sma, ema)
    .withAnalysisCriterion(series, record, new MaximumDrawdownCriterion())
    .build()
    .display()
    .save("my-strategy");

See the Charting guide for comprehensive documentation and more examples.

Legacy API

The original methods remain available for backward compatibility:

ChartMaker charts = new ChartMaker("target/charts");
charts.displayTradingRecordChart(
        series,
        strategy.getName(),
        record,
        new SMAIndicator(new ClosePriceIndicator(series), 50),
        new EMAIndicator(new ClosePriceIndicator(series), 200));

Saved images are JPEGs (see FileSystemChartStorage), so the directory you pass to the constructor ends up with files like mySeries_2023-01-01_to_2024-05-01_timestamp.jpg. ChartMaker composes JFreeChart visualizations through pluggable display and storage backends, so you can pop up Swing windows, stream bytes, or save those JPEGs after each backtest. Explore ta4jexamples.charting.ChartMaker and the ta4jexamples.strategies.NetMomentumStrategy sample, which loads data, runs a strategy, and drops charts into ta4j-examples/log/charts.

Avoid common pitfalls

Walk-forward optimization

Use the helper methods in ta4jexamples.walkforward.WalkForward to slice the series into alternating training/testing windows:

List<BarSeries> trainingSlices = WalkForward.splitSeries(series, Duration.ofDays(120), Duration.ofDays(90));

for (BarSeries training : trainingSlices) {
    BarSeries testing = WalkForward.subseries(series, training.getEndIndex() + 1, Duration.ofDays(30));
    Strategy tuned = tuneStrategy(training);
    TradingRecord forwardResult = new BarSeriesManager(testing).run(tuned);
    // evaluate and store the outcome
}

See the Walk Forward example for a turnkey implementation.

Debugging slow or flaky backtests

Ta4j comes with several analysis criteria; see the org.ta4j.core.criteria package in the project sources.

Visualizing backtest results

After running a backtest, you can visualize the results using ta4j’s charting capabilities. Charts can display price data, trading signals, indicators, and performance metrics:

// Create a chart showing the trading record
ChartWorkflow chartWorkflow = new ChartWorkflow();
chartWorkflow.builder()
    .withSeries(series)
    .withTradingRecordOverlay(tradingRecord)
    .display();

You can also visualize analysis criteria over time:

chartWorkflow.builder()
    .withSeries(series)
    .withTradingRecordOverlay(tradingRecord)
    .withAnalysisCriterionOverlay(new NetProfitCriterion(), tradingRecord)
    .display();

See the Charting Guide for comprehensive documentation on creating trading charts.

Walk-forward optimization

Ta4j allows you to perform a well-known Walk-forward optimization. An example can be found here.