Skip to content

Repository files navigation

ta4j Run Verify Discord License: MIT Maven Central JDK

Technical Analysis for Java

Documentation · Javadoc · Examples · Discord

ta4j is an open-source Java library for technical analysis and trading-system research. Build indicators and rules, backtest strategies with realistic costs and execution assumptions, inspect the results, and reuse the same strategy logic in live applications.

Why ta4j? · Install · Quick start · Workflow · Examples · Contributing


Why ta4j?

ta4j gives Java developers a typed, composable model for market data, indicators, rules, strategies, backtests, analysis, and live-style execution records. It fits naturally into JVM applications and tooling without requiring a Python bridge, a separate service, or a proprietary strategy DSL.

The goal is reproducible research, not guaranteed returns. ta4j helps you state a trading idea precisely, test it under explicit assumptions, and understand where the result came from. Market data and broker connectivity remain under your control.

Features at a glance

Capability What it gives you
Market-series model OHLCV BarSeries implementations for ordinary, moving-window, and concurrent workflows
Broad indicator and pattern catalog Moving averages, momentum, volatility, volume, candlestick patterns, market structure, and more
Composable strategies Fluent Indicator, Rule, and Strategy APIs with no required DSL
Backtesting and research Single-strategy runs, large candidate sets, ranking, parameter research, and walk-forward workflows
Execution realism Transaction and borrowing costs, slippage, stop-limit fills, position sizing, partial fills, and lot matching
Analysis and presentation Risk/return criteria, charting workflows, and JSON serialization support
Forecasting Causal forecast-state and projection APIs for Monte Carlo, analog, Kalman, and conformal workflows
Live integration Reuse strategy logic while your application owns data ingestion, order routing, reconciliation, and recovery

Install in seconds

ta4j requires Java 25+. Most applications need only ta4j-core:

<dependency>
  <groupId>org.ta4j</groupId>
  <artifactId>ta4j-core</artifactId>
  <version>0.24.1</version>
</dependency>

Use ta4j-examples for runnable demos, sample data sources, and charting workflows to learn from or copy into your own project. It is not required by ta4j-core.

Snapshots and the ta4j-examples dependency

Snapshot builds are published through the Sonatype Central snapshot repository:

<repository>
  <id>central-portal-snapshots</id>
  <url>https://central.sonatype.com/repository/maven-snapshots/</url>
  <releases><enabled>false</enabled></releases>
  <snapshots><enabled>true</enabled></snapshots>
</repository>

<dependency>
  <groupId>org.ta4j</groupId>
  <artifactId>ta4j-core</artifactId>
  <version>0.24.2-SNAPSHOT</version>
</dependency>

Stable examples artifact:

<dependency>
  <groupId>org.ta4j</groupId>
  <artifactId>ta4j-examples</artifactId>
  <version>0.24.1</version>
</dependency>

Snapshot examples artifact:

<dependency>
  <groupId>org.ta4j</groupId>
  <artifactId>ta4j-examples</artifactId>
  <version>0.24.2-SNAPSHOT</version>
</dependency>

Quick start: Your first strategy

Run the included example

git clone https://github.com/ta4j/ta4j.git
cd ta4j

# Build the reactor once, then run the default Quickstart example.
./mvnw -DskipTests install
./mvnw -pl ta4j-examples exec:java

On Windows, use mvnw.cmd instead of ./mvnw. The example loads bundled Bitcoin data, evaluates a strategy, prints performance metrics, and displays a chart when a graphical environment is available.

Run another example by overriding the configured main class:

./mvnw -pl ta4j-examples exec:java -Dexec.mainClass=ta4jexamples.backtesting.TradingRecordParityBacktest

Use the core API

The essential model is:

BarSeriesIndicatorRuleStrategyTradingRecord

import org.ta4j.core.BarSeries;
import org.ta4j.core.BaseStrategy;
import org.ta4j.core.Rule;
import org.ta4j.core.Strategy;
import org.ta4j.core.TradingRecord;
import org.ta4j.core.backtest.BarSeriesManager;
import org.ta4j.core.indicators.averages.EMAIndicator;
import org.ta4j.core.indicators.helpers.ClosePriceIndicator;
import org.ta4j.core.rules.CrossedDownIndicatorRule;
import org.ta4j.core.rules.CrossedUpIndicatorRule;

BarSeries series = ...; // Load your OHLCV bars.

ClosePriceIndicator close = new ClosePriceIndicator(series);
EMAIndicator fastEma = new EMAIndicator(close, 12);
EMAIndicator slowEma = new EMAIndicator(close, 26);

Rule entry = new CrossedUpIndicatorRule(fastEma, slowEma);
Rule exit = new CrossedDownIndicatorRule(fastEma, slowEma);
Strategy strategy = new BaseStrategy("EMA crossover", entry, exit);

TradingRecord record = new BarSeriesManager(series).run(strategy);
System.out.printf("Positions: %d%n", record.getPositionCount());

The runnable Quickstart adds data loading, metrics, and charting around this same flow.

The core workflow

A typical ta4j application follows one path:

market data → BarSeries → indicators → rules → strategy → backtest or live evaluation → metrics and charts

Sourcing market data

ta4j-core works with any OHLCV source. The ta4j-examples module includes a shared BarSeriesDataSource model and ready-to-run loaders for:

For production systems, adapt your broker, exchange, database, REST API, or WebSocket feed into bars and keep normalization, gap handling, and corporate-action adjustments explicit.

Evaluate performance with metrics

Backtests can model costs before you calculate returns and risk:

TradingRecord record = new BarSeriesManager(
        series,
        new LinearTransactionCostModel(0.001),
        new LinearBorrowingCostModel(0.0001))
        .run(strategy);

Num netReturn = new NetReturnCriterion().calculate(series, record);
Num maximumDrawdown = new MaximumDrawdownCriterion().calculate(series, record);

Use TradeExecutionModel implementations when fill timing, slippage, stop-limit behavior, or partial execution matters. Use BacktestExecutor when you need to evaluate and rank many independent strategy candidates. The backtesting guide and realism checklist explain the assumptions that most often invalidate attractive results.

Visualize and share strategies

Charting helpers live in ta4j-examples and build on JFreeChart:

// Generate simplified chart - just price, indicators, and signals (no subchart)
ChartWorkflow chartWorkflow = new ChartWorkflow();
JFreeChart chart = chartWorkflow.builder()
        .withTitle("EMA Crossover Strategy")
        .withSeries(series) // Price bars (candlesticks)
        .withIndicatorOverlay(fastEma) // Overlay indicators on price chart
        .withIndicatorOverlay(slowEma)
        .withTradingRecordOverlay(record) // Mark entry/exit points with arrows
        .toChart();
chartWorkflow.saveChartImage(chart, series, "ema-crossover-strategy", "output/charts"); // Save as image

EMA crossover strategy chart

More charting examples

RSI in a separate subchart:

// Create indicators
ClosePriceIndicator close = new ClosePriceIndicator(series);
RSIIndicator rsi = new RSIIndicator(close, 14);

// RSI strategy: buy when RSI crosses below 30 (oversold), sell when RSI crosses
// above 70 (overbought)
Rule entry = new CrossedDownIndicatorRule(rsi, 30);
Rule exit = new CrossedUpIndicatorRule(rsi, 70);
Strategy strategy = new BaseStrategy("RSI Strategy", entry, exit);
TradingRecord record = new BarSeriesManager(series).run(strategy);

ChartWorkflow chartWorkflow = new ChartWorkflow();
JFreeChart chart = chartWorkflow.builder()
        .withTitle("RSI Strategy with Subchart")
        .withSeries(series) // Price bars (candlesticks)
        .withTradingRecordOverlay(record) // Mark entry/exit points
        .withSubChart(rsi) // RSI indicator in separate subchart panel
        .toChart();

RSI strategy with subchart

A performance criterion beside the strategy:

// Create indicators: multiple moving averages
ClosePriceIndicator close = new ClosePriceIndicator(series);
SMAIndicator sma20 = new SMAIndicator(close, 20);
EMAIndicator ema12 = new EMAIndicator(close, 12);

// Strategy: buy when EMA crosses above SMA, sell when EMA crosses below SMA
Rule entry = new CrossedUpIndicatorRule(ema12, sma20);
Rule exit = new CrossedDownIndicatorRule(ema12, sma20);
Strategy strategy = new BaseStrategy("EMA/SMA Crossover", entry, exit);
TradingRecord record = new BarSeriesManager(series).run(strategy);

ChartWorkflow chartWorkflow = new ChartWorkflow();
JFreeChart chart = chartWorkflow.builder()
        .withTitle("Strategy Performance Analysis")
        .withSeries(series) // Price bars (candlesticks)
        .withIndicatorOverlay(sma20) // Overlay SMA on price chart
        .withIndicatorOverlay(ema12) // Overlay EMA on price chart
        .withTradingRecordOverlay(record) // Mark entry/exit points
        .withSubChart(new MaximumDrawdownCriterion(), record) // Performance metric in subchart
        .toChart();

Strategy performance analysis

Multiple indicators and performance layers:

// Create indicators
ClosePriceIndicator close = new ClosePriceIndicator(series);
SMAIndicator sma50 = new SMAIndicator(close, 50);
EMAIndicator ema12 = new EMAIndicator(close, 12);
MACDIndicator macd = new MACDIndicator(close, 12, 26);
RSIIndicator rsi = new RSIIndicator(close, 14);

// Strategy: buy when EMA crosses above SMA and RSI > 50, sell when EMA crosses
// below SMA
Rule entry = new CrossedUpIndicatorRule(ema12, sma50).and(new OverIndicatorRule(rsi, 50));
Rule exit = new CrossedDownIndicatorRule(ema12, sma50);
Strategy strategy = new BaseStrategy("Advanced Multi-Indicator Strategy", entry, exit);
TradingRecord record = new BarSeriesManager(series).run(strategy);

ChartWorkflow chartWorkflow = new ChartWorkflow();
JFreeChart chart = chartWorkflow.builder()
        .withTitle("Advanced Multi-Indicator Strategy")
        .withSeries(series) // Price bars (candlesticks)
        .withIndicatorOverlay(sma50) // Overlay SMA on price chart
        .withIndicatorOverlay(ema12) // Overlay indicators on price chart
        .withTradingRecordOverlay(record) // Mark entry/exit points
        .withSubChart(macd) // MACD indicator in subchart
        .withSubChart(rsi) // RSI indicator in subchart
        .withSubChart(new NetProfitLossCriterion(), record) // Net profit/loss performance metric
        .toChart();

Advanced multi-indicator strategy

See the charting guide for layout, export, and time-axis options.

From backtest to live trading

The same Strategy can evaluate historical or newly arriving bars, but ta4j is not an order-management system. A live integration must:

  • choose deliberately between forming-candle and closed-candle evaluation
  • call shouldEnter(index, tradingRecord) and shouldExit(index, tradingRecord)
  • record broker-confirmed fills and keep TradingRecord synchronized
  • deduplicate orders, reconcile state after restart, and handle rejected or partial fills

Start with the core API decision guide, live-candle semantics, and the live trading runbook.

Advanced capabilities

Forecasting

Forecast indicators stay inside the normal Indicator model while producing a distribution at a fixed future horizon. A forecast evaluated at index i reads source values only through i, so it can later be compared with the realized value at i + horizon without look-ahead leakage.

LogReturnIndicator returns = new LogReturnIndicator(series);
ReturnForecastStateIndicator<ReturnForecastState> state =
        new EwmaReturnForecastStateIndicator(returns);

ForecastProjectionIndicator fiveBarForecast =
        new MonteCarloPriceForecastIndicator(state, 5);

Indicator<Num> median = fiveBarForecast.median();
Indicator<Num> downside = fiveBarForecast.quantile(0.05);

Use deterministic seeds and explicit projection indicators when results must be repeatable. The examples module includes complete rolling conformal and kinematic Kalman walkthroughs.

Strategy and component serialization

Supported indicators, rules, and strategies can be serialized for persistence, sharing, and integration with other systems.

JSON serialization examples
// Serialize an indicator (RSI) to JSON
ClosePriceIndicator close = new ClosePriceIndicator(series);
RSIIndicator rsi = new RSIIndicator(close, 14);
String rsiJson = rsi.toJson();
LOG.info("Output: {}", rsiJson);
// Output:
// {"type":"RSIIndicator","parameters":{"barCount":14},"components":[{"type":"ClosePriceIndicator"}]}
// Serialize a rule (AndRule) to JSON
Rule rule1 = new OverIndicatorRule(rsi, 50);
Rule rule2 = new UnderIndicatorRule(rsi, 80);
Rule andRule = new AndRule(rule1, rule2);
String ruleJson = ComponentSerialization.toJson(RuleSerialization.describe(andRule));
LOG.info("Output: {}", ruleJson);
// Output:
// {"type":"AndRule","label":"AndRule","components":[{"type":"OverIndicatorRule","label":"OverIndicatorRule","components":[{"type":"RSIIndicator","parameters":{"barCount":14},"components":[{"type":"ClosePriceIndicator"}]}],"parameters":{"threshold":50.0}},{"type":"UnderIndicatorRule","label":"UnderIndicatorRule","components":[{"type":"RSIIndicator","parameters":{"barCount":14},"components":[{"type":"ClosePriceIndicator"}]}],"parameters":{"threshold":80.0}}]}
// Serialize a strategy (EMA Crossover) to JSON
EMAIndicator fastEma = new EMAIndicator(close, 12);
EMAIndicator slowEma = new EMAIndicator(close, 26);
Rule entry = new CrossedUpIndicatorRule(fastEma, slowEma);
Rule exit = new CrossedDownIndicatorRule(fastEma, slowEma);
Strategy strategy = new BaseStrategy("EMA Crossover", entry, exit);
String strategyJson = strategy.toJson();
LOG.info("Output: {}", strategyJson);
// Output: {"type":"BaseStrategy","label":"EMA
// Crossover","parameters":{"unstableBars":0},"rules":[{"type":"CrossedUpIndicatorRule","label":"entry","components":[{"type":"EMAIndicator","parameters":{"barCount":12},"components":[{"type":"ClosePriceIndicator"}]},{"type":"EMAIndicator","parameters":{"barCount":26},"components":[{"type":"ClosePriceIndicator"}]}]},{"type":"CrossedDownIndicatorRule","label":"exit","components":[{"type":"EMAIndicator","parameters":{"barCount":12},"components":[{"type":"ClosePriceIndicator"}]},{"type":"EMAIndicator","parameters":{"barCount":26},"components":[{"type":"ClosePriceIndicator"}]}]}]}

Restore supported components with Indicator.fromJson(series, json) and Strategy.fromJson(series, json). See migration and version compatibility before persisting descriptors across releases.

Specialized research tools

Beyond the basic strategy loop, ta4j includes building blocks for batch backtests, parameter research, position sizing, causal swing detection, rolling correlations, regime-aware rules, candlestick patterns, Elliott Wave analysis, LPPL residuals, streaming trade ingestion, and fill-aware live records.

These capabilities are intentionally not expanded into mini-manuals here. Use the examples index, core API guide, Javadoc, and wiki to go deeper without losing the onboarding path.

Performance

ta4j lets you choose DecimalNum for precision-first workflows or DoubleNum for throughput-first workflows with accepted floating-point tradeoffs. Moving series can cap retained history, indicator values are cached, and independent strategy candidates can be evaluated in parallel.

Measure changes on your own workload rather than relying on generic claims. Use the BacktestPerformanceTuningHarness, the Num guide, and Performance Characterization for repeatable comparisons.

Real-world examples

The ta4j-examples module is organized as progressive learning tracks:

Goal Start here
First strategy and metrics Quickstart, StrategyAnalysis
Data sourcing YahooFinanceBacktest, CoinbaseBacktest
Execution semantics TradingRecordParityBacktest, TradeFillRecordingExample
Parameter research SimpleMovingAverageRangeBacktest
Forecasting and calibration RollingConformalForecastExample, KinematicKalmanForecastExample
Charting and diagnostics IndicatorsToChart, CashFlowToChart

See ta4j-examples/README.md for the complete learning sequence and runnable commands.

Documentation

New to ta4j? Follow the Canonical User Journey and run the examples in order. Moving from exploration to serious research? Apply the realism checklist before trusting a result. Moving toward live execution? Build around the live trading runbook, especially its reconciliation and recovery guidance.

Need Resource
A guided first hour Canonical User Journey
Core API and execution choices ta4j-core/README.md
Runnable examples ta4j-examples/README.md
Backtesting concepts Backtesting guide
Research quality gate Backtesting Realism Checklist
Live operations Live Trading Runbook
API changes Migration and Version Compatibility
Troubleshooting Troubleshooting Hub
Complete API reference Javadoc

Community & support

Contributing

Read the contribution guide, Code of Conduct, and roadmap. Contributions should include focused tests and an appropriate CHANGELOG.md entry.

Contributor reference:

Build commands: Maven

Contributor build and verification commands

The repository requires Java 25+ and includes Maven Wrapper scripts pinned to Maven 3.9.16.

  • Standard wrapper: use ./mvnw ... on macOS/Linux, mvnw.cmd ... on Windows, or system Maven 3.9+ intentionally.
  • Canonical quality gate: Use scripts/run-full-build-quiet.sh on macOS/Linux/Git Bash/WSL or scripts/run-full-build-quiet.ps1 on Windows PowerShell with Git Bash available on PATH.
  • Maven-only repair and verification: ./mvnw -B clean license:format spotless:apply verify -Dta4j.excludedTestTags=analysis-demo,benchmark,requires-display,requires-headless
  • CI-equivalent validation: ./mvnw -B clean license:check spotless:check verify -Dta4j.excludedTestTags=analysis-demo,benchmark,requires-display,requires-headless
  • Focused formatting repair: ./mvnw -B license:format spotless:apply
  • SpotBugs gate: ./mvnw -pl ta4j-core -am clean compile spotbugs:check
  • JaCoCo gate: ./mvnw -pl ta4j-core -am test jacoco:report jacoco:check
  • Focused coverage report: ./mvnw -pl ta4j-core -am -Dtest=BarSeriesManagerTest -Dsurefire.failIfNoSpecifiedTests=false test jacoco:report

Run the full quality path before opening or updating a pull request and review any license-header or formatting repairs before committing them. Eclipse users can import code-formatter.xml; IntelliJ users should use an Eclipse-format-profile integration. Maven remains the authoritative formatter entrypoint.

Release & snapshot publishing

Snapshot and stable publishing are automated. Maintainers should use RELEASE_PROCESS.md for the supported release path, recovery procedures, required configuration, and audit artifacts.

License and risk

ta4j is released under the MIT License. It is provided as-is, without a warranty or any promise of correctness, profitability, or fitness for a particular purpose. Backtests are models of the assumptions you encode; they are not evidence that future trading will be profitable.

Powered by

JetBrains logo

ta4j contributors

Releases

Packages

Used by

Contributors

Languages