# ta4j: a Java library for backtesting trading strategies

> ta4j models market data, indicators, rules and strategies as typed Java objects, then runs them through a backtest engine that accounts for costs and slippage. It is for JVM developers who want their research to live in the same language as their production code.

**ta4j/ta4j** — A Java library for technical analysis.

- Repository: https://github.com/ta4j/ta4j
- Website: http://www.ta4j.com
- Stars: 2,494 · Forks: 799
- Language: Java
- License: NOASSERTION
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/ta4j-ta4j

## What ta4j solves for JVM developers

Most technical-analysis tooling lives in Python. That is fine until the strategy has to run inside a Java service, at which point teams either build a Python sidecar and a serialization boundary, or reimplement the indicators a second time in Java and hope the two versions agree. ta4j exists to remove that fork. It is a Java library for technical analysis and trading-system research, and the README frames the goal as reproducible research rather than guaranteed returns: state a trading idea precisely, test it under explicit assumptions, and understand where the result came from.

The intended audience is a Java developer who already has a JVM application and wants strategy logic to be a normal part of it. The README is explicit that this fits without a Python bridge, a separate service, or a proprietary strategy DSL. That last point matters more than it sounds. Many backtesting products ask you to express a strategy in their own configuration language, which then has to be translated before it can run live. ta4j keeps the strategy as Java objects, so the same object graph that produced a backtest can be evaluated against incoming bars.

What ta4j does not do is equally clear. Market data and broker connectivity remain under your control. There is no bundled feed, no order router, no account reconciliation. The library covers the analytical middle of the pipeline and deliberately stops at the edges.

## The BarSeries to TradingRecord pipeline

The README states the essential model as a single chain: BarSeries, then Indicator, then Rule, then Strategy, then TradingRecord. Each link is a concrete type, and the data flows one way.

A BarSeries holds OHLCV bars. The README says there are implementations for ordinary, moving-window, and concurrent workflows, which covers the three shapes people actually need: a full history loaded from disk, a rolling window for live evaluation, and a series that is written by a feed thread while being read elsewhere.

Indicators wrap a series or another indicator. ClosePriceIndicator turns a series into a stream of closing prices; EMAIndicator wraps that stream with a period. Because indicators compose, an indicator over an indicator is just another indicator, and nothing special is required to nest them.

Rules are boolean conditions over indicators. CrossedUpIndicatorRule and CrossedDownIndicatorRule take two indicators and fire when one crosses the other, which is exactly the shape of an entry and an exit. A Strategy pairs an entry rule with an exit rule and carries a name. BarSeriesManager.run(strategy) walks the series and returns a TradingRecord, which is the list of positions the strategy would have taken.

That record is the boundary between simulation and reporting. The README lists risk/return criteria, charting workflows, and JSON serialization as the analysis layer on top, and the examples module adds data loading and metrics around the same flow. The design choice worth noting is that nothing in this chain knows about wall-clock time or live feeds. A backtest is just a run over a fixed series, which is why the same strategy object can be reused later.

## Installing ta4j-core and running a first backtest

ta4j requires Java 25 or newer. The README says most applications need only ta4j-core, and that ta4j-examples is for runnable demos, sample data sources, and charting workflows. The examples artifact is not required by core.

For a Maven build, add the dependency with the version the README pins:

```xml
<dependency>
  <groupId>org.ta4j</groupId>
  <artifactId>ta4j-core</artifactId>
  <version>0.25.0</version>
</dependency>
```

If you would rather start from the repository itself, the README gives a clone and two Maven wrapper commands. The first builds the reactor and skips tests; the second runs the default Quickstart example in the examples module.

```bash
git clone https://github.com/ta4j/ta4j.git
cd ta4j
./mvnw -DskipTests install
./mvnw -pl ta4j-examples exec:java
```

On Windows the README says to 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. If there is no display, expect the metrics on standard output and no window.

To run a different demo, the README overrides the configured main class. This is useful for seeing how a backtest with execution assumptions is wired before writing your own.

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

The first real use in your own code is short. The README's own snippet builds two EMAs over the close price, crosses them for entry and exit, wraps them in a BaseStrategy, and runs it:

```java
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 series itself is the one part you must supply. The README points at ta4j-examples for a shared BarSeriesDataSource model with loaders for Yahoo Finance and Coinbase, which is the fastest way to get real bars without writing a parser.

## Where ta4j stops and your application begins

The most important limitation is stated in the README rather than discovered in the code: market data and broker connectivity remain under your control. ta4j is not a trading system. It will not fetch a feed on a schedule, it will not place an order, and it will not reconcile a position after a disconnect. If you want a product that does those things out of the box, ta4j is the wrong layer and you will spend your time rebuilding the parts it omits.

The second constraint is the JDK requirement. Java 25 or newer is a hard floor, and it is a recent one. Teams on an older long-term-support release cannot simply drop ta4j-core in; they need to move their runtime first, which for a large service is a project of its own.

The third is subtler and worth saying plainly. The README lists execution realism as a capability: transaction and borrowing costs, slippage, stop-limit fills, position sizing, partial fills, and lot matching. Those features exist, but the README does not document their defaults. A backtest is only as honest as its cost model, and a default that is too optimistic will produce a flattering equity curve that does not survive contact with a real venue. Before you trust any number ta4j prints, read the source or the Javadoc for the cost and fill model in the version you pinned.

Finally, the repository metadata reports the licence as NOASSERTION while the README carries an MIT badge. That mismatch is a reason to open the licence file in the repository rather than rely on either signal.

## How ta4j differs from a Python research stack

The obvious alternative is a Python technical-analysis library paired with a dataframe library, which is where most quantitative research happens today. The difference is not indicator coverage. Both compute moving averages and candlestick patterns. The difference is where the strategy lives.

In a Python stack, the strategy is typically a script or notebook. Moving it into production means either running Python in production or translating the logic into the language of the service that executes it. The translation is where bugs enter, because two implementations of the same rule drift as soon as one is edited. ta4j's answer is that the strategy is a Java object graph from the start. There is no translation step, because there is no second language.

The cost of that choice is the ecosystem. Python has a much larger set of data providers, visualisation tools, and published research to copy from. ta4j's examples cover Yahoo Finance and Coinbase loaders, and the README points at charting workflows, but the breadth is not comparable. You are trading library variety for a single runtime and a typed model.

A second alternative is a proprietary backtesting platform with its own strategy DSL. Those give you data, execution simulation, and reporting in one package, at the price of expressing your idea in someone else's language and depending on their runtime. ta4j's README calls out the absence of a required DSL as a feature, and that is the honest framing of the trade: you write more glue code, and you own the pipeline end to end.

## Maintenance, versioning and the cost of upgrading

The repository is not archived, and the last push was on 2026-09-28, which is the same day as the most recent release listed, 0.25.0. Releases before it are 0.24.1 and 0.24.0. That cadence suggests a project that is still moving, and the version numbers tell you how: everything is still in the 0.x range, so there is no 1.0 compatibility promise to lean on.

For a library that a trading strategy depends on, that matters. A minor version bump in a 0.x project can change an API. The repository does carry a CHANGELOG.md at the top level, which is the file to read before bumping the version in your build, and the README's version blocks are generated, which is why they carry markers like TA4J_VERSION_BLOCK:core:stable:begin. If you copy a snippet from the README, check the version it names rather than assuming it matches your build.

The upgrade cost is mostly mechanical: change one version string in your build file, recompile, and let the compiler find the breakages. That works because the core model is typed. A renamed indicator or a changed constructor signature is a compile error, not a runtime surprise. The exception is anything you reach through reflection or configuration, and the JSON serialization support the README mentions is worth testing explicitly across an upgrade if you persist strategy definitions.

On licensing, the README badge points to MIT, which is permissive and generally allows commercial use, modification, and redistribution with the licence and copyright notice retained. The repository metadata, however, reports NOASSERTION, meaning the classification could not be determined automatically. This is not legal advice. If the licence matters to your organisation, read the licence file in the repository and have whoever handles licensing confirm it.

## Conclusion

Adopt ta4j if your strategy logic already belongs in a JVM service and you want one typed codebase for research and execution, and if you can run Java 25 or newer. Do not adopt it if you need a ready-made trading platform with broker connectivity, or if your team works in Python and would be maintaining a second language for no reason. Before committing, verify three things: that your build actually targets Java 25, since older JDKs will not run the artifacts; that the execution assumptions you care about (commission, slippage, position sizing, partial fills) are configurable in the version you pin, because the README lists them as capabilities without documenting their defaults; and that the licence file in the repository matches what you expect, since the metadata reports NOASSERTION while the README badge says MIT.

## FAQ

### What Java version does ta4j require?

The README states that ta4j requires Java 25 or newer, and the JDK badge in the README says JDK 25+. This is a hard floor, so older long-term-support releases will not run the published artifacts.

### How do I add ta4j to a Maven project?

Add the org.ta4j:ta4j-core dependency at version 0.25.0, which is what the README's install section shows. The README says most applications need only ta4j-core, and that ta4j-examples is optional and not required by core.

### Does ta4j connect to a broker or fetch market data for me?

No. The README says market data and broker connectivity remain under your control, and that live integration means reusing strategy logic while your application owns data ingestion, order routing, reconciliation, and recovery. The examples module does include loaders for sources such as Yahoo Finance and Coinbase if you want sample data.

## Sources

- [Issues](https://github.com/ta4j/ta4j/issues)
- [Project website](http://www.ta4j.com)
- [README](https://github.com/ta4j/ta4j/blob/master/README.md)
- [Releases](https://github.com/ta4j/ta4j/releases)
- [ta4j/ta4j on GitHub](https://github.com/ta4j/ta4j)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ta4j-ta4j
