Stop Loss and Stop Gain Rules
ta4j now provides a full stop-rule toolkit that covers fixed %, fixed amount, trailing variants, and volatility/ATR-adaptive variants for both loss and gain exits.
Use this page as the operational guide for:
- Choosing the right stop type for your strategy style.
- Wiring stop rules into strategy exits.
- Applying stop logic safely in live trading.
Rule families
Fixed percentage (classic)
StopLossRuleStopGainRuleTrailingStopLossRuleTrailingStopGainRule
These use percentage distance from entry (or favorable move for trailing variants).
Percent inputs are percentage points (for example, 3 means 3%, not 0.03).
Best when:
- You want simple, interpretable risk limits.
- Price scale does not change dramatically over time.
Fixed amount (flat-dollar / flat-price-distance)
FixedAmountStopLossRuleFixedAmountStopGainRuleTrailingFixedAmountStopLossRuleTrailingFixedAmountStopGainRule
These use an absolute price distance (for example: $10 from reference).
Best when:
- Instruments trade in a stable price range.
- You size positions independently and want fixed distance exits.
Volatility-driven
VolatilityStopLossRuleVolatilityStopGainRuleVolatilityTrailingStopLossRuleVolatilityTrailingStopGainRuleAverageTrueRangeStopLossRuleAverageTrueRangeStopGainRuleAverageTrueRangeTrailingStopLossRuleAverageTrueRangeTrailingStopGainRule
These use a dynamic threshold based on volatility indicators (typically ATR multiplied by a coefficient).
From 0.22.3, ATR-based rules support constructors that accept a custom ATRIndicator directly (including trailing variants), so you can share precomputed ATR pipelines instead of rebuilding ATR inside each rule.
Best when:
- Volatility regimes shift frequently.
- You want stops that widen in high vol and tighten in low vol.
Entry hygiene rules (0.22.7)
These rules complement stop exits when gating new entries:
EdgeHealthyRule— requires an edge indicator (typicallyEntryEdgeIndicator) to stay above a minimum level and optionally above a minimum slope (EdgeDecaySlopeIndicator).LossTriggeredCooldownRule— blocks entries for a configurable number of bars after a losing closed position; supports optional reset rules and trade-side filters.
Use them with regime filters (TrendScoreIndicator, CompressionIndicator) rather than as standalone risk controls.
Quick selection guide
- Trend-following swing systems:
- Start with
AverageTrueRangeTrailingStopLossRulefor downside protection. - Pair with
TrailingStopGainRuleorAverageTrueRangeTrailingStopGainRulefor profit capture.
- Start with
- Mean-reversion systems:
- Use tighter
StopLossRule/FixedAmountStopLossRule. - Use non-trailing
StopGainRule/FixedAmountStopGainRuleat expected reversion targets.
- Use tighter
- Intraday scalping:
- Prefer fixed amount stops when spread/tick structure dominates.
- Add a volatility stop fallback for regime changes.
How to wire exits
ClosePriceIndicator close = new ClosePriceIndicator(series);
ATRIndicator atr = new ATRIndicator(series, 14);
Rule riskExit = new AverageTrueRangeTrailingStopLossRule(close, atr, 2.0)
.or(new AverageTrueRangeTrailingStopGainRule(close, atr, 3.0))
.or(new StopLossRule(close, series.numFactory().numOf(1.5))); // hard fail-safe
Rule signalExit = new CrossedDownIndicatorRule(fast, slow);
Rule exitRule = signalExit.or(riskExit);
Strategy strategy = new BaseStrategy(entryRule, exitRule);
Notes:
- Keep one hard fail-safe stop even when using adaptive stops.
- Avoid stacking many correlated stop rules unless each has a distinct purpose.
TrailingStopGainRuleandAverageTrueRangeTrailingStopGainRuleare two-stage: they activate only after reaching the initial gain threshold, then trigger on retracement from the favorable extreme.AverageTrueRangeTrailingStopGainRulerequires a positive ATR coefficient; invalid values fail fast withIllegalArgumentException.
Using stop-price models for risk analytics
Several rules implement:
StopLossPriceModelStopGainPriceModel
This lets you query stop prices directly (for example in risk budgeting or custom position sizing flows) without duplicating threshold math.
For fixed-percentage and fixed-distance calculations, you can also use helper methods from StopLossRule / StopGainRule directly:
Num stopLoss = StopLossRule.stopLossPrice(entryPrice, numOf(2), true);
Num stopGain = StopGainRule.stopGainPrice(entryPrice, numOf(4), true);
Num trailingGain = StopGainRule.trailingStopGainPrice(highestPrice, numOf(1.5), true);
Num fixedLoss = StopLossRule.stopLossPriceFromDistance(entryPrice, numOf(10), true);
Num fixedGain = StopGainRule.stopGainPriceFromDistance(entryPrice, numOf(15), true);
Num fixedTrailingGain = StopGainRule.trailingStopGainPriceFromDistance(highestPrice, numOf(5), true);
Pass true for long-position math and false for short-position math. The fixed-distance helpers are the same primitives used by fixed-amount and volatility stop rules internally.
If you evaluate strategy quality in risk units, pair your stop model with RMultipleCriterion (for example, new RMultipleCriterion(new StopLossPositionRiskModel(5))) so research and stop geometry stay aligned.
Live trading usage patterns
1) Match your reference price to execution reality
- If broker triggers on last trade, using
ClosePriceIndicatormay be acceptable. - If broker triggers on bid/ask or mark/index price, use a matching indicator source.
- Do not mix trigger source and backtest source silently.
2) Decide bar-close vs intrabar evaluation
- Bar-close only:
- Fewer false triggers, easier reproducibility.
- Slower reaction.
- Intrabar (tick/stream updates):
- Faster protection.
- More noise and higher order churn.
Pick one model explicitly and keep it consistent between research and production.
3) Protect against gaps and slippage
- Stops are trigger conditions, not guaranteed fills.
- Plan with slippage cushions:
- tighter position sizing,
- broker-native stop/stop-limit where possible,
- exchange outage safeguards.
4) Avoid stop churn
- Trailing rules can move often in noisy markets.
- Add guardrails:
- minimum update interval,
- minimum stop delta before replace,
- cancel/replace rate limits.
5) Keep a stop hierarchy
- Primary logic stop (strategy-level)
- Catastrophic fail-safe stop
- Portfolio kill-switch (max drawdown / exposure limit)
Do not rely on a single mechanism.
Parameter tuning tips
- ATR period:
- shorter (
7-14) reacts quickly, - longer (
20-50) is smoother and slower.
- shorter (
- ATR coefficient:
- lower values stop out earlier,
- higher values reduce churn but increase average loss size.
- Trailing lookback (
barCountwhere applicable):- short lookback tightens quickly,
- long lookback gives trends more room.
Practical tuning workflow:
- Optimize for robustness first, not max return.
- Validate on multiple volatility regimes.
- Compare with realistic fees/slippage.
- Stress test gap days and high-spread windows.
Complementary risk gating rules (0.22.2+)
Use these with stop rules when you need stronger entry/exit constraints:
RiskRewardRatioRule- Checks whether your current
price/stop/targettuple satisfies a minimum reward-to-risk threshold. - Useful before entering trades that rely on dynamic stop/target indicators.
- Checks whether your current
OverOrEqualIndicatorRule- Same semantics as
OverIndicatorRule, but inclusive (>=) for threshold boundaries. - Useful for exact trigger levels (for example, “allow signal when RSI is exactly 50”).
- Same semantics as
UnderOrEqualIndicatorRule- Same semantics as
UnderIndicatorRule, but inclusive (<=) for threshold boundaries. - Useful for exact lower bounds (for example, “allow exit when RSI is exactly 30”).
- Same semantics as
OrWithThresholdRule- OR composition evaluated over a rolling threshold window (current bar included).
- Useful when either of two confirmation signals can fire within the last
Nbars.
Rule rrGate = new RiskRewardRatioRule(close, stopPrice, targetPrice, true, 2.0);
Rule inclusiveState = new OverOrEqualIndicatorRule(rsi, 50);
Rule inclusiveOversold = new UnderOrEqualIndicatorRule(rsi, 30);
Rule delayedConfirm = new OrWithThresholdRule(macdCrossUp, breakoutRule, 3);
Rule entryRule = rrGate.and(inclusiveState).and(inclusiveOversold.negation()).and(delayedConfirm);
Common mistakes
- Using fixed % stops on instruments with changing volatility regimes and no adaptive fallback.
- Using adaptive stops without a maximum loss cap.
- Overfitting stop parameters to one market period.
- Backtesting on close-only, then trading on tick-level without recalibration.
- Ignoring order semantics (partial fills, reduce-only, stop-limit miss risk).
Live deployment checklist
- Confirm stop trigger source (
last,bid/ask,mark/index) matches indicator input. - Confirm order type behavior on your venue.
- Confirm replacement throttling and retry logic.
- Confirm persistence and restart behavior for open positions and trailing state.
- Confirm metrics/alerts for stop-trigger frequency, slippage, and rejected orders.
See also:
Maintainer rationale notes
- Stop-rule family coverage mirrors the expanded hierarchy added in commit
89cd2271(BaseVolatility*, fixed-amount, trailing, and stop-price model interfaces). - Documented entry hygiene rules
EdgeHealthyRuleandLossTriggeredCooldownRule(0.22.7). - ATR constructor guidance reflects commit
1fa097efand currentAverageTrueRange*constructors that acceptATRIndicatordirectly. - Added positive-coefficient note for
AverageTrueRangeTrailingStopGainRulebased on its input validation inrequirePositiveAtrCoefficient(...). - Stop-price helper examples now include
StopLossRule.stopLossPriceFromDistance(...),StopGainRule.stopGainPriceFromDistance(...), andStopGainRule.trailingStopGainPriceFromDistance(...), which are used by the fixed-amount and volatility rule implementations.