Documentation, examples and further information of the ta4j project
This project is maintained by ta4j Organization
Trendlines and swing points are the core building blocks behind support/resistance analysis, breakout systems, and Elliott-style structure detection. Ta4j ships a full toolkit for detecting swings, turning them into markers, and projecting data-driven support and resistance lines that behave the way traders draw them by hand.
SwingPointMarkerIndicator converts swing indexes into chart-friendly markers (values only on swing bars, NaN elsewhere).TrendLineSupportIndicator / TrendLineResistanceIndicator that score every valid line in the lookback window and pick the best candidate with tunable weights and touch tolerance.getSwingPointIndexes() and getCurrentSegment() expose anchors, slope/intercept, and scoring so you can debug or annotate charts.BarSeries series = CsvFileBarSeriesDataSource.loadSeriesFromFile(); // daily AAPL sample
// 5–bar fractal swings (symmetric window, no equal lows/highs allowed)
LowPriceIndicator low = new LowPriceIndicator(series);
HighPriceIndicator high = new HighPriceIndicator(series);
RecentFractalSwingLowIndicator swingLows = new RecentFractalSwingLowIndicator(low, 5, 5, 0);
RecentFractalSwingHighIndicator swingHighs = new RecentFractalSwingHighIndicator(high, 5, 5, 0);
// Markers only where a swing is confirmed
SwingPointMarkerIndicator swingLowMarkers = new SwingPointMarkerIndicator(series, swingLows);
SwingPointMarkerIndicator swingHighMarkers = new SwingPointMarkerIndicator(series, swingHighs);
// Data-driven support/resistance lines over the last 200 bars
TrendLineSupportIndicator support = new TrendLineSupportIndicator(series, 5, 200);
TrendLineResistanceIndicator resistance = new TrendLineResistanceIndicator(series, 5, 200);
ChartWorkflow charts = new ChartWorkflow("temp/charts");
ChartPlan plan = charts.builder()
.withTitle("Trendlines with fractal swing markers")
.withSeries(series)
.withIndicatorOverlay(support).withLineColor(Color.GREEN).withOpacity(0.6f)
.withIndicatorOverlay(resistance).withLineColor(Color.RED).withOpacity(0.6f)
.withIndicatorOverlay(swingLowMarkers).withLineColor(Color.GREEN).withLineWidth(3f).withConnectAcrossNaN(true)
.withIndicatorOverlay(swingHighMarkers).withLineColor(Color.RED).withLineWidth(3f).withConnectAcrossNaN(true)
.toPlan();
charts.display(plan);
RecentFractalSwingHighIndicator / RecentFractalSwingLowIndicator confirm a swing when the candidate bar beats its neighbors inside a symmetric or asymmetric window.
precedingHigherBars / precedingLowerBars: how many bars immediately before must be strictly above/below the candidate. Must be ≥ 1 (default convenience constructor uses 3).followingHigherBars / followingLowerBars: how many bars after must be strictly above/below the candidate. Needs future bars, so swings confirm only after this window elapses.allowedEqualBars: maximum additional equal bars allowed in the candidate plateau. A plateau is emitted once at its canonical extreme instead of producing duplicate pivots.new RecentFractalSwingLowIndicator(series) → 3/3/0 on lows (highs mirror the parameters).Use fractals when you want visually obvious turning points and don’t mind waiting a few bars for confirmation.
// 7-bar symmetric window that tolerates one additional equal bar in the plateau
RecentFractalSwingHighIndicator majorHighs = new RecentFractalSwingHighIndicator(high, 7, 7, 1);
int latestHighIndex = majorHighs.getLatestSwingHighIndex(series.getEndIndex());
RecentZigZagSwingHighIndicator / RecentZigZagSwingLowIndicator track swings confirmed when price reverses by at least a configured threshold—no fixed lookahead window.
The OHLC-aware state constructors and state-only recent-swing constructors below are available in ta4j 0.22.9 and later. On 0.22.8, use the released single-price ZigZagStateIndicator(price, reversalAmount) and explicit-price recent-swing constructors.
ZigZagStateIndicator(high, low, confirmationPrice, reversalAmount); the older single-price constructor remains available.reversalAmount): indicator in price units. Defaults to ATR(14) in the convenience constructor; pass ConstantIndicator for fixed-point thresholds or any indicator for adaptive ones.NaN until a reversal large enough to confirm the prior extreme.HighPriceIndicator high = new HighPriceIndicator(series);
LowPriceIndicator low = new LowPriceIndicator(series);
ClosePriceIndicator close = new ClosePriceIndicator(series);
// Confirm swings after a 1.5 * ATR move
ATRIndicator atr = new ATRIndicator(series, 14);
Indicator<Num> atrThreshold = BinaryOperationIndicator.product(atr, 1.5);
ZigZagStateIndicator zigzagState = new ZigZagStateIndicator(high, low, close, atrThreshold);
RecentZigZagSwingLowIndicator zigzagLows = new RecentZigZagSwingLowIndicator(zigzagState);
RecentZigZagSwingHighIndicator zigzagHighs = new RecentZigZagSwingHighIndicator(zigzagState);
The state-only recent-swing constructors automatically reuse the matching low or high source. Use the explicit-source overloads only when custom pivot pricing is intentional.
No single formula is best for every turn shape:
| Detector | Best fit | Confirmation trade-off |
|---|---|---|
| Fractal | Distinct local extrema and fixed timing requirements | Waits for the configured right-side window. |
| ZigZag | Sharp reversals whose importance is defined by price distance or volatility | Confirms after the reversal threshold is crossed. |
These two sources implement RecentSwingIndicator and plug directly into trendline and marker indicators. Elliott analysis also supports slope-change and tolerant consensus backends through its separate SwingDetector contract; see Pluggable Swing Detection for those runner-specific APIs.
Both trendline swing sources are causal at the requested evaluation index: they use only bars available at that index, while the reported pivot can refer to the earlier bar where the confirmed extreme occurred.
SwingPointMarkerIndicator outputs prices only at swing indexes (NaN elsewhere) so chart overlays become discrete markers instead of lines.
SwingPointMarkerIndicator swingLowMarkers = new SwingPointMarkerIndicator(series, swingLows);
SwingPointMarkerIndicator swingHighMarkers = new SwingPointMarkerIndicator(series, swingHighs);
chart.builder()
.withSeries(series)
.withIndicatorOverlay(swingLowMarkers).withLineColor(Color.GREEN).withLineWidth(3f).withConnectAcrossNaN(true)
.withIndicatorOverlay(swingHighMarkers).withLineColor(Color.RED).withLineWidth(3f).withConnectAcrossNaN(true)
.toPlan();
TrendLineSupportIndicator and TrendLineResistanceIndicator project the “best” straight line across the last barCount bars using swing highs/lows as anchors. They:
barCount bars (use Integer.MAX_VALUE to span the full series; minimum is 2).NaN until at least two swings exist inside the window.RecentSwingIndicator (e.g., ZigZag swings).barCount caps how far back anchors can be chosen.surroundingHigherBars/surroundingLowerBars for quick fractal setups.// Fractal-based support line over the last 300 bars, heavier bias to touching many swings
TrendLineSupportIndicator support = new TrendLineSupportIndicator(
series,
5, // surrounding higher bars
300,
TrendLineSupportIndicator.ScoringWeights.touchCountBiasPreset()
);
// ZigZag-based resistance line using a custom swing detector
ZigZagStateIndicator zz = new ZigZagStateIndicator(high,
new ConstantIndicator<>(series, series.numFactory().numOf(25)));
RecentZigZagSwingHighIndicator swingHighs = new RecentZigZagSwingHighIndicator(zz, high);
TrendLineResistanceIndicator resistance = new TrendLineResistanceIndicator(
swingHighs,
5, 5, // preceding / following lower bars (kept for symmetry)
250
);
Weights must sum to 1.0;
Each lever tilts the search toward a different style of line:
TrendLineSupportIndicator.ScoringWeights customWeights =
TrendLineSupportIndicator.ScoringWeights.builder()
.weightForTouchingSwingPoints(0.55)
.weightForTouchingExtremeSwing(0.20)
.weightForKeepingSwingsInsideLine(0.10)
.weightForStayingCloseToSwings(0.10)
.weightForRecentAnchorPoints(0.05) // Default is now 0.05 (5%)
.build();
TrendLineSupportIndicator adaptiveSupport = new TrendLineSupportIndicator(series, 4, 180, customWeights);
Presets: touchCountBiasPreset() (connect as many swings as possible) and extremeSwingBiasPreset() (force the line through the extreme swing).
Tolerance is applied per swing to count a touch:
ToleranceSettings.percentage(fraction, minimumAbsolute), default 2% with a 1e-9 floor): scales with volatility so low-vol markets get tighter lines while wide ranges relax the band. Prioritize for cross-asset backtests and adaptive setups; deprioritize when a single massive bar would bloat tolerance. Theory: proportional error bands keep significance tied to recent price dispersion.ToleranceSettings.absolute(absolutePrice)): fixed currency/points band. Prioritize when you think in ticks/pips or the instrument’s price is range-bound; deprioritize if price can double/halve, because the tolerance will become too loose or too strict. Theory: mirrors how discretionary traders eyeball “about X dollars” of breathing room.ToleranceSettings.tickSize(tickSize)): integer multiples of the exchange’s minimum price increment. Prioritize for futures/FX where microstructure enforces tick steps; deprioritize on assets with fractional pricing or fragmented liquidity. Theory: snaps tolerance to the smallest executable move, reducing false misses from rounding.minimumAbsolute in percentage mode): guards against vanishingly small bands when the range compresses. Prioritize on low-priced or low-volatility products so minor rounding differences still count as touches; set conservatively if you want the band to shrink meaningfully in quiet markets. Theory: a floor avoids numerical precision artifacts masquerading as breaks.TrendLineResistanceIndicator tightResistance = new TrendLineResistanceIndicator(
series,
4,
150,
TrendLineResistanceIndicator.ScoringWeights.defaultWeights(),
TrendLineResistanceIndicator.ToleranceSettings.absolute(0.25) // 25 cents wide
);
64 swing points and 2048 candidate pairs. Increase for longer histories; decrease for very noisy intraday data.TrendLineSegment segment = support.getCurrentSegment(); exposes anchors, slope, intercept, touch/outside counts, score, and window start/end—great for logging or overlay labels.support.toJson() serializes parameters and the swing subtree for persistence.ta4jexamples.analysis.TrendLineAndSwingPointAnalysis is the companion example for this page. It performs regression-style headroom checks across bundled datasets for fractal and ZigZag swing sources, then renders a chart with support/resistance lines and swing markers.
Run it from the ta4j source checkout:
mvn -pl ta4j-examples -Dexec.mainClass=ta4jexamples.analysis.TrendLineAndSwingPointAnalysis exec:java
Use it after changing trendline scoring, swing detection, chart overlay rendering, or default search caps. In headless environments the checks still run; chart display is skipped or saved according to the example configuration.
ClosePriceIndicator close = new ClosePriceIndicator(series);
TrendLineResistanceIndicator resistance = new TrendLineResistanceIndicator(series, 5, 200);
TrendLineSupportIndicator support = new TrendLineSupportIndicator(series, 5, 200);
Rule breakoutLong = new CrossedUpIndicatorRule(close, resistance);
Rule bounceLong = new CrossedDownIndicatorRule(close, support)
.and(new CrossedUpIndicatorRule(close, support));
Strategy trendlineStrategy = new BaseStrategy("Trendline break/bounce", breakoutLong, bounceLong.negation());
CrossedUpIndicatorRule(close, resistance) / CrossedDownIndicatorRule(close, support).
Support/resistance lines from fractal swings. Default (red/green), touch-count bias (cyan/blue), and extreme-bias (orange/magenta) presets make different anchor choices.
ZigZag-based swings confirmed after ATR-sized reversals. Fewer, higher-conviction swings compared to fractals.
following* window; ZigZag confirms after price moves by the threshold. Guard against NaN when rules fire.RecentZigZagSwingHighIndicator with the ZigZag state’s high source and RecentZigZagSwingLowIndicator with its low source.series.setMaximumBarCount(250), keep trendline barCount <= 250 or earlier anchors will be evicted.maxSwingPointsForTrendline or raise the reversal/plateau thresholds to avoid combinatorial explosions..withConnectAcrossNaN(true) connects sparse swing markers into lines; leave it false when you want discrete marker points.ta4jexamples.analysis.TrendLineAndSwingPointAnalysis, introduced with support/resistance coverage in commit 39194498.AbstractTrendLineIndicator, TrendLineSupportIndicator, and TrendLineResistanceIndicator.