Documentation, examples and further information of the ta4j project
This project is maintained by ta4j Organization
⚠️ Early days: ta4j’s live-trading story is still evolving. The APIs below are intentionally bare bones and require custom glue (data ingestion, order routing, resilience) on your side. Treat this as a starting point rather than a fully featured framework.
ta4j is not just for backtests—its abstractions map directly onto production trading bots. This page outlines how to bootstrap a live engine, keep your BarSeries synchronized with the exchange, and execute trades responsibly.
series.getEndIndex(), and send orders to your broker.Num implementation consistent with your broker’s precision (decimal for FX/crypto, double for faster equity bots).setMaximumBarCount(n) to cap memory while keeping enough bars to cover every indicator period.TransactionCostModel and HoldingCostModel on your BarSeriesManager or strategy runner so live metrics align with backtest assumptions.BarSeries liveSeries = new BaseBarSeriesBuilder()
.withName("binance_eth_usd_live")
.withNumFactory(DecimalNumFactory.getInstance())
.build();
liveSeries.setMaximumBarCount(500);
bootstrapWithRecentBars(liveSeries, exchangeClient);
Strategy strategy = strategyFactory.apply(liveSeries);
TradingRecord tradingRecord = new BaseTradingRecord();
Most exchanges stream trades or candles you can convert into ta4j bars:
Bar bar = liveSeries.barBuilder()
.timePeriod(Duration.ofMinutes(1))
.endTime(candle.closeTime())
.openPrice(candle.open())
.highPrice(candle.high())
.lowPrice(candle.low())
.closePrice(candle.close())
.volume(candle.volume())
.build();
liveSeries.addBar(bar);
When updates arrive before the bar closes:
liveSeries.addTrade(liveSeries.numFactory().numOf(trade.volume()),
liveSeries.numFactory().numOf(trade.price()));
// Or replace the last bar entirely if the exchange sends a revised candle
liveSeries.addBar(replacementBar, true);
int endIndex = liveSeries.getEndIndex();
Num price = liveSeries.getBar(endIndex).getClosePrice();
if (strategy.shouldEnter(endIndex, tradingRecord)) {
orderService.submitBuy(price, desiredQuantity());
tradingRecord.enter(endIndex, price, desiredQuantity());
} else if (strategy.shouldExit(endIndex, tradingRecord)) {
orderService.submitSell(price, openQuantity());
tradingRecord.exit(endIndex, price, openQuantity());
}
Guidelines:
tradingRecord.isOpened()/isClosed() lines up with your broker state. If an order is partially filled, delay updating the record until the fill completes.TradeExecutionModel implementations to simulate your broker’s order semantics before going live.StrategySerialization.toJson(strategy) or keep NamedStrategy descriptors in configuration. This ensures bots can reload the exact same logic after restarts.BarSeries to disk or cache so warm restarts skip the backfill step.StrategyExecutionLogging from ta4j-examples is a good starting point.BacktestExecutor) on the most recent data to ensure live behavior matches expectations.