Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ta4j/ta4j/llms.txt

Use this file to discover all available pages before exploring further.

Rules are the boolean building blocks of a ta4j strategy. Each rule implements the Rule interface and returns true or false when evaluated at a given bar index.

The Rule interface

public interface Rule {
    // Core evaluation method — tradingRecord may be null for indicator-only rules
    boolean isSatisfied(int index, TradingRecord tradingRecord);

    // Convenience overload — passes null for tradingRecord
    default boolean isSatisfied(int index);
}
Rules that require open-position context (stop rules, duration rules) read from tradingRecord. Indicator-based rules ignore it.

Boolean combinators

Every Rule exposes four fluent combinators that each return a new Rule:
Rule a = new CrossedUpIndicatorRule(fastEma, slowEma);
Rule b = new OverIndicatorRule(rsi, 50);

Rule both        = a.and(b);       // AND — both must be satisfied
Rule either      = a.or(b);        // OR  — at least one must be satisfied
Rule oneNotOther = a.xor(b);       // XOR — exactly one must be satisfied
Rule notA        = a.negation();   // NOT — inverts the rule

ChainRule — sequential rule evaluation

ChainRule requires an initial rule followed by one or more ChainLink steps. Each step must be satisfied within a configurable number of bars after the previous one fires.
import org.ta4j.core.rules.ChainRule;
import org.ta4j.core.rules.helper.ChainLink;

// Entry fires only when a crossover is followed by RSI > 50 within 3 bars
Rule entry = new ChainRule(
    new CrossedUpIndicatorRule(fastEma, slowEma),
    new ChainLink(new OverIndicatorRule(rsi, 50), 3)
);

Indicator-based rules

Rules that fire when one indicator crosses another.
Satisfied when indicator1 crosses above indicator2 (or a fixed threshold).
// Two indicators
new CrossedUpIndicatorRule(fastEma, slowEma)

// Indicator crosses above a numeric threshold
new CrossedUpIndicatorRule(rsi, 30)

// Indicator crosses above a Num threshold
new CrossedUpIndicatorRule(rsi, series.numFactory().numOf(30))
Satisfied when indicator1 crosses below indicator2 (or a fixed threshold).
new CrossedDownIndicatorRule(fastEma, slowEma)
new CrossedDownIndicatorRule(rsi, 70)
Rules that compare an indicator’s value against a threshold or another indicator.
Satisfied when the indicator is strictly greater than the threshold or second indicator.
new OverIndicatorRule(rsi, 50)
new OverIndicatorRule(close, sma200)
Satisfied when the indicator is strictly less than the threshold or second indicator.
new UnderIndicatorRule(rsi, 30)
Satisfied when the indicator is greater than or equal to the threshold.
new OverOrEqualIndicatorRule(close, support)
Satisfied when the indicator is less than or equal to the threshold.
new UnderOrEqualIndicatorRule(close, resistance)
Satisfied when two indicators produce equal values at the current bar.
new IsEqualRule(indicator1, indicator2)
Satisfied when the indicator value falls between lower and upper (inclusive).
// RSI between 40 and 60
new InPipeRule(rsi, series.numFactory().numOf(40), series.numFactory().numOf(60))
Satisfied when the indicator’s slope (change from the previous bar) falls within [minSlope, maxSlope].
new InSlopeRule(close, series.numFactory().numOf(-0.5), series.numFactory().numOf(0.5))
Rules that evaluate multi-bar directional behavior.
Satisfied when the indicator has been rising for the last barCount bars.
new IsRisingRule(close, 5)   // close price rising for 5 consecutive bars
Satisfied when the indicator has been falling for the last barCount bars.
new IsFallingRule(close, 5)
Satisfied when the indicator’s current value is the highest over the last barCount bars.
new IsHighestRule(close, 20)
Satisfied when the indicator’s current value is the lowest over the last barCount bars.
new IsLowestRule(close, 20)
Rules that control when a signal may fire based on elapsed bars, time of day, or position duration.
After rule fires, prevents it from firing again for numberOfBars bars.
new WaitForRule(entrySignal, 5)
Wraps another rule so it can only fire once per backtest run.
new JustOnceRule(new CrossedUpIndicatorRule(fastEma, slowEma))
Satisfied only after the current position has been open for at least barCount bars.
new OpenedPositionMinimumBarCountRule(3)
Satisfied when the current open position has been held for a specified number of bars. Useful for time-based exits.
new OpenPositionDurationRule(10)
Satisfied on specific days of the week.
new DayOfWeekRule(DayOfWeek.MONDAY, DayOfWeek.FRIDAY)
Satisfied during specific hours of the day (UTC by default).
new HourOfDayRule(9, 16)   // 09:00–16:00
Satisfied at specific minutes within the hour.
new MinuteOfHourRule(0, 30)
Satisfied when the bar’s end time falls within a specified time-of-day range.
new TimeRangeRule(LocalTime.of(9, 30), LocalTime.of(16, 0), ZoneId.of("America/New_York"))
Wraps a Boolean-typed indicator as a Rule.
new BooleanIndicatorRule(myBooleanIndicator)
A constant rule that always returns the provided value. Useful as a placeholder or in tests.
new BooleanRule(true)   // always satisfied
new BooleanRule(false)  // never satisfied
Returns a pre-defined sequence of true/false values by bar index. Useful for unit tests.
new FixedRule(false, false, true, false, true)
Satisfied when at least requiredVotes of the supplied rules are satisfied simultaneously.
new VoteRule(List.of(rule1, rule2, rule3), 2)  // majority vote: 2-of-3
Threshold-aware AND/OR combinators that also incorporate a numeric threshold parameter alongside the boolean evaluation.
new AndWithThresholdRule(rule1, rule2, threshold)
new OrWithThresholdRule(rule1, rule2, threshold)
Satisfied when a momentum indicator is in a specified state (e.g., bullish or bearish momentum phase).
new MomentumStateRule(momentumIndicator, MomentumState.BULLISH)

Strategy composition example

The following example assembles a complete entry/exit strategy from multiple rule types:
import org.ta4j.core.*;
import org.ta4j.core.indicators.*;
import org.ta4j.core.indicators.helpers.ClosePriceIndicator;
import org.ta4j.core.rules.*;
import org.ta4j.core.backtest.BarSeriesManager;

BarSeries series = /* your data source */;

ClosePriceIndicator close = new ClosePriceIndicator(series);
EMAIndicator ema12 = new EMAIndicator(close, 12);
EMAIndicator ema26 = new EMAIndicator(close, 26);
RSIIndicator  rsi  = new RSIIndicator(close, 14);

// Entry: EMA golden cross AND RSI confirms momentum, but only on Mondays
Rule entry = new CrossedUpIndicatorRule(ema12, ema26)
        .and(new OverIndicatorRule(rsi, 50))
        .and(new DayOfWeekRule(DayOfWeek.MONDAY));

// Exit: reverse crossover, OR take profit at +5%, OR cut loss at -2%
Rule exit = new CrossedDownIndicatorRule(ema12, ema26)
        .or(new StopGainRule(close, 5.0))
        .or(new StopLossRule(close, 2.0));

Strategy strategy = new BaseStrategy("EMA + RSI", entry, exit);
TradingRecord record = new BarSeriesManager(series).run(strategy);
Combine WaitForRule or OpenedPositionMinimumBarCountRule with an exit rule to enforce a minimum hold period before exits are considered.