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: how many bars on each side may equal the candidate value (helps catch rounded tops/bottoms instead of rejecting plateaus).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 equal neighbor on each side
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.
ZigZagStateIndicator(price, reversalAmount).HighPriceIndicator, LowPriceIndicator, or ClosePriceIndicator.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.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(close, atrThreshold);
RecentZigZagSwingLowIndicator zigzagLows = new RecentZigZagSwingLowIndicator(zigzagState, close);
RecentZigZagSwingHighIndicator zigzagHighs = new RecentZigZagSwingHighIndicator(zigzagState, close);
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.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.RecentZigZagSwing* and the trendline.series.setMaximumBarCount(250), keep trendline barCount <= 250 or earlier anchors will be evicted.maxSwingPointsForTrendline or raise the reversal/plateau thresholds to avoid combinatorial explosions.