Skip to main content
A ta4j strategy is built from two halves: an entry rule that decides when to open a position, and an exit rule that decides when to close it. Each half is a Rule — a pure boolean condition evaluated at a given bar index.

What is a Rule?

A Rule has a single core method:
boolean isSatisfied(int index, TradingRecord tradingRecord);
It returns true when the condition is met at the given bar index. The tradingRecord parameter is provided for rules that need to inspect current position state (e.g., stop-loss rules that compare against the entry price). For most indicator-based rules, tradingRecord is not used. A convenience overload omits the record:
boolean isSatisfied(int index); // delegates with tradingRecord = null

Combining rules

Rules compose with four logical operations, all returning a new Rule:
Rule a = /* ... */;
Rule b = /* ... */;

Rule both    = a.and(b);       // true when both are true
Rule either  = a.or(b);        // true when at least one is true
Rule exactly = a.xor(b);       // true when exactly one is true
Rule not     = a.negation();   // true when a is false
These methods return fresh Rule instances (AndRule, OrRule, XorRule, NotRule) and can be chained fluently.

Entry rules

Entry rules describe when to open a position. Common built-in options:
Satisfied when the first indicator crosses above the second indicator (or a fixed threshold) from below. Commonly used for EMA/SMA golden-cross entries.
import org.ta4j.core.rules.CrossedUpIndicatorRule;

// Buy when fast EMA crosses above slow EMA
Rule entry = new CrossedUpIndicatorRule(fastEma, slowEma);

// Buy when RSI crosses above 30 (exits oversold)
Rule oversoldExit = new CrossedUpIndicatorRule(rsi, 30);
Satisfied when an indicator’s value is above another indicator or a fixed threshold.
import org.ta4j.core.rules.OverIndicatorRule;

// True whenever RSI > 50
Rule bullish = new OverIndicatorRule(rsi, 50);
Satisfied when an indicator’s value is below another indicator or a fixed threshold.
import org.ta4j.core.rules.UnderIndicatorRule;

// True whenever RSI < 30 (oversold)
Rule oversold = new UnderIndicatorRule(rsi, 30);
Satisfied when an indicator has been rising over a given window.
import org.ta4j.core.rules.IsRisingRule;

// True when close has risen for 3 consecutive bars
Rule risingClose = new IsRisingRule(close, 3);

Exit rules

Exit rules describe when to close a position. Common built-in options:
Satisfied when the first indicator crosses below the second. Commonly used for EMA/SMA death-cross exits.
import org.ta4j.core.rules.CrossedDownIndicatorRule;

// Exit when fast EMA crosses below slow EMA
Rule exit = new CrossedDownIndicatorRule(fastEma, slowEma);
Satisfied when the current price falls by more than a given percentage from the entry price.
import org.ta4j.core.rules.StopLossRule;

// Cut losses at -2%
Rule stopLoss = new StopLossRule(close, 2.0);
Satisfied when the current price rises by more than a given percentage from the entry price.
import org.ta4j.core.rules.StopGainRule;

// Take profit at +5%
Rule takeProfit = new StopGainRule(close, 5.0);
Satisfied when the current price falls by a given percentage from the highest price seen since entry. The stop trails the price upward automatically.
import org.ta4j.core.rules.TrailingStopLossRule;

// Trail stop 3% below the peak
Rule trailingStop = new TrailingStopLossRule(close, series.numFactory().numOf(3));

Building a strategy with BaseStrategy

BaseStrategy is the standard Strategy implementation. Pass it an entry rule, an exit rule, and optionally a name and unstable-bar count:
import org.ta4j.core.BaseStrategy;
import org.ta4j.core.Strategy;

Strategy strategy = new BaseStrategy(
        "EMA Crossover",   // human-readable name
        entryRule,         // Rule: when to open a position
        exitRule,          // Rule: when to close a position
        26                 // unstableBars: ignore signals for the first 26 bars
);

Querying the strategy

The strategy exposes shouldEnter and shouldExit methods that wrap the underlying rules and automatically suppress signals during the unstable (warm-up) period:
int i = series.getEndIndex();

if (strategy.shouldEnter(i)) {
    // Open a position
}

if (strategy.shouldExit(i, tradingRecord)) {
    // Close the current position
}
Both methods skip the bar if index < unstableBars — no entry or exit signal is ever generated before the strategy is fully warmed up.

Starting trade type

By default, a strategy opens long positions (BUY first). To run a short-only strategy, pass TradeType.SELL as the starting type:
import org.ta4j.core.Trade.TradeType;

Strategy shortStrategy = new BaseStrategy(
        "Short Momentum",
        shortEntryRule,
        shortExitRule,
        TradeType.SELL
);

Combining strategies

Two Strategy objects can be merged with .and() or .or():
// Enter only when both strategies agree
Strategy combined = strategyA.and(strategyB);

// Enter when either strategy fires
Strategy either   = strategyA.or(strategyB);
The combined strategy’s unstableBars is automatically set to the maximum of the two component strategies.

A complete multi-rule strategy

import org.ta4j.core.*;
import org.ta4j.core.indicators.*;
import org.ta4j.core.indicators.helpers.*;
import org.ta4j.core.rules.*;

BarSeries series = /* your series */;

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

// Entry: golden cross AND RSI above 50 (confirms bullish momentum)
Rule entry = new CrossedUpIndicatorRule(fastEma, slowEma)
        .and(new OverIndicatorRule(rsi, 50));

// Exit: death cross OR 3% stop-loss OR 8% take-profit
Rule exit = new CrossedDownIndicatorRule(fastEma, slowEma)
        .or(new StopLossRule(close, 3.0))
        .or(new StopGainRule(close, 8.0));

// Ignore signals for the first 26 bars (slowEma warm-up)
Strategy strategy = new BaseStrategy("Trend + Momentum", entry, exit, 26);
Set unstableBars to at least the getCountOfUnstableBars() value of your slowest indicator. This prevents the strategy from generating signals against indicator values that are still in their warm-up phase.

JSON serialization

Strategies and rules can be serialized for persistence or sharing:
// Serialize
String json = strategy.toJson();

// Restore
Strategy restored = Strategy.fromJson(series, json);
Rules also serialize individually:
String ruleJson = rule.toJson();
Rule restoredRule = Rule.fromJson(series, ruleJson);