Skip to main content
Elliott Wave theory divides price movement into two structural patterns: a five-wave impulse sequence (waves 1–5) in the direction of the dominant trend, followed by a three-wave correction (waves A–B–C) against it. ta4j implements this analysis as a layered stack of indicators — you choose how much of the stack to expose. Two entry points are available:
  • ElliottWaveFacade — indicator-style, per-bar access. Use this in strategy rules and chart overlays when you want live signals at each bar index.
  • ElliottWaveAnalysisRunner — one-shot analysis pipeline. Use this when you need a complete snapshot: multi-degree scenario ranking, confidence breakdowns, and cross-degree compatibility scores.

ElliottWaveFacade

ElliottWaveFacade wires the full indicator stack from a single configuration and exposes each indicator as a lazily-created getter. All indicators share the same underlying swing detector, so they always analyse the same wave structure.

Creating a facade

// Symmetric window: 5 bars inspected before and after each pivot candidate
ElliottWaveFacade facade = ElliottWaveFacade.fractal(series, 5, ElliottDegree.INTERMEDIATE);

Quickstart (from the README)

BarSeries series = // ... your series
int index = series.getEndIndex();

ElliottWaveFacade facade = ElliottWaveFacade.fractal(series, 5, ElliottDegree.INTERMEDIATE);

ElliottPhase phase     = facade.phase().getValue(index);
ElliottScenarioSet scenarios = facade.scenarios().getValue(index);
Num invalidation       = facade.invalidationLevel().getValue(index);

Available indicators

Each indicator is created lazily on first access.
GetterReturn typeWhat it gives you
facade.phase()ElliottPhaseIndicatorCurrent wave phase as ElliottPhase
facade.ratio()ElliottRatioIndicatorFibonacci ratio between adjacent swings
facade.channel()ElliottChannelIndicatorUpper, lower, and median trend channel
facade.waveCount()ElliottWaveCountIndicatorRaw swing count
facade.filteredWaveCount()ElliottWaveCountIndicatorSwing count after noise compression
facade.confluence()ElliottConfluenceIndicatorConfluence score across ratio + channel signals
facade.invalidation()ElliottInvalidationIndicatorBoolean invalidation flag
facade.invalidationLevel()ElliottInvalidationLevelIndicatorNumeric invalidation price level
facade.scenarios()ElliottScenarioIndicatorRanked alternative wave interpretations
facade.projection()ElliottProjectionIndicatorFibonacci-based price targets
facade.trendBias()ElliottTrendBiasIndicatorDirectional bias derived from scenarios

Convenience methods on the facade

// Primary (highest-confidence) scenario at a bar index
Optional<ElliottScenario> primary = facade.primaryScenario(index);

// All other ranked alternatives
List<ElliottScenario> alternatives = facade.alternativeScenarios(index);

// Confidence score [0.0–1.0] for a specific phase
Num confidence = facade.confidenceForPhase(index, ElliottPhase.WAVE3);

// Check if high-confidence scenarios agree on the same phase
boolean consensus = facade.hasScenarioConsensus(index);

// Get the consensus phase (or ElliottPhase.NONE if disagreement)
ElliottPhase agreed = facade.scenarioConsensus(index);

// Human-readable summary of the current scenario set
String summary = facade.scenarioSummary(index);

Scenario modes for invalidation levels

ElliottInvalidationLevelIndicator supports three modes through ElliottScenarioIndicator:
  • PRIMARY — invalidation price from the top-ranked scenario only.
  • CONSERVATIVE — tightest (closest) invalidation across all high-confidence scenarios.
  • AGGRESSIVE — widest (farthest) invalidation across all scenarios.

Custom Fibonacci tolerance and swing compression

Num customTolerance = series.numFactory().numOf(0.25); // 25% Fibonacci tolerance
ElliottSwingCompressor compressor = new ElliottSwingCompressor(series); // 1% amplitude, 2-bar minimum

ElliottWaveFacade facade = ElliottWaveFacade.fractal(
        series,
        5,
        ElliottDegree.INTERMEDIATE,
        Optional.of(customTolerance),
        Optional.of(compressor));

ElliottDegree

ElliottDegree labels the expected time scale of your swings. Use it to keep indicator configurations consistent with the bar resolution of your series.
ValueTypical time horizonRecommended bar type
GRAND_SUPERCYCLEMulti-decade to centuryMonthly/weekly
SUPER_CYCLEDecadesWeekly
CYCLEYearsWeekly/daily
PRIMARYMonths to yearsDaily (400–1 000 bars)
INTERMEDIATEWeeks to monthsDaily (180–400 bars)
MINORDays to weeksDaily/hourly
MINUTEHours to daysDaily/hourly
MINUETTEMinutes to hoursMinute/hourly
SUB_MINUETTEMinutes and belowMinute
ElliottDegree can suggest appropriate degrees for your data at runtime:
// Returns a ranked list of degrees that fit this series
List<ElliottDegree> recommended = ElliottDegree.getRecommendedDegrees(
        series.getFirstBar().getTimePeriod(),
        series.getBarCount());

ElliottPhase

ElliottPhase identifies which leg of the wave structure the market is currently in. Impulse phases: WAVE1, WAVE2, WAVE3, WAVE4, WAVE5 Corrective phases: CORRECTIVE_A, CORRECTIVE_B, CORRECTIVE_C Special value: NONE — no qualifying swing structure detected yet.
ElliottPhase phase = facade.phase().getValue(index);

if (phase.isImpulse()) {
    System.out.println("Impulse wave " + phase.impulseIndex()); // 1–5
} else if (phase.isCorrective()) {
    System.out.println("Corrective wave " + (char)('A' + phase.correctiveIndex() - 1));
}

if (phase.completesStructure()) {
    System.out.println("Structure complete — expecting reversal or higher-degree move");
}

ElliottWaveAnalysisRunner

ElliottWaveAnalysisRunner runs a complete one-shot analysis pipeline. It optionally validates scenarios across neighboring degrees and re-ranks them using cross-degree compatibility.
1

Build the runner

ElliottWaveAnalysisRunner runner = ElliottWaveAnalysisRunner.builder()
        .degree(ElliottDegree.INTERMEDIATE)   // base degree
        .higherDegrees(1)                     // include one degree above
        .lowerDegrees(1)                      // include one degree below
        .minConfidence(0.2)                   // prune weak scenarios
        .maxScenarios(5)                      // keep at most 5 alternatives
        .build();
2

Run the analysis

ElliottWaveAnalysisResult result = runner.analyze(series);
3

Read ranked scenarios

// Base-degree scenarios ranked by composite score
List<ElliottWaveAnalysisResult.BaseScenarioAssessment> ranked = result.rankedBaseScenarios();

for (ElliottWaveAnalysisResult.BaseScenarioAssessment assessment : ranked) {
    ElliottScenario scenario = assessment.scenario();
    double confidence   = assessment.confidenceScore();    // base confidence [0.0–1.0]
    double crossDegree  = assessment.crossDegreeScore();   // cross-degree compatibility [0.0–1.0]
    double composite    = assessment.compositeScore();     // blended final score
    System.out.printf("Phase=%-15s confidence=%.2f crossDegree=%.2f composite=%.2f%n",
            scenario.currentPhase(), confidence, crossDegree, composite);
}
4

Inspect per-degree analyses

// All degree snapshots (higher → base → lower)
for (ElliottWaveAnalysisResult.DegreeAnalysis analysis : result.degreeAnalyses()) {
    ElliottAnalysisResult degreeResult = analysis.analysis();
    System.out.printf("Degree=%-15s bars=%d trendBias=%s%n",
            analysis.degree(), analysis.barCount(), degreeResult.trendBias());
}

Builder options

MethodDefaultDescription
.degree(ElliottDegree)requiredBase degree that drives scenario ranking
.higherDegrees(int)1Number of higher degrees to include (0 to disable)
.lowerDegrees(int)1Number of lower degrees to include (0 to disable)
.baseConfidenceWeight(double)0.7Weight in [0.0, 1.0] for base confidence vs. cross-degree score
.minConfidence(double)0.15Minimum confidence threshold for scenario inclusion
.maxScenarios(int)library defaultMaximum number of scenarios to retain
.scenarioSwingWindow(int)5Swings passed to scenario generation (0 = all)
.swingDetector(SwingDetector)adaptive ZigZag (ATR 14)Override the swing detector
.swingFilter(SwingFilter)auto-scaled by degreeOverride the swing filter
.confidenceModel(ConfidenceModel)ConfidenceProfiles.defaultModel()Override the confidence scorer
.patternSet(PatternSet)PatternSet.all()Restrict which pattern types are generated
.seriesSelector(SeriesSelector)trims to max history per degreeOverride how series are sliced per degree
.analysisRunner(AnalysisRunner)built-in pipelineOverride the entire per-degree pipeline
The confidence scorer weights five factors: Fibonacci proximity (35%), time proportions (20%), alternation quality (15%), channel adherence (15%), and structure completeness (15%). Pass a custom ConfidenceModel via .confidenceModel(...) to change these weights.

Example classes

All examples are in the ta4j-examples module under ta4jexamples.analysis.elliottwave.
ClassWhat it demonstrates
ElliottWaveIndicatorSuiteDemoFull indicator suite — swings, phases, Fibonacci, channels, confidence, scenarios, charts. Defaults to an ossified dataset; supports YahooFinance or Coinbase via command-line arguments.
ElliottWavePresetDemoConsolidated launcher for preset ossified assets (btc, eth, sp500) and live mode with any ticker.
ElliottWaveAdaptiveSwingAnalysisAdaptive/composite swing detection for scenario generation.
ElliottWavePatternProfileDemoComparison of default and pattern-aware confidence profiles.
ElliottWaveMultiDegreeAnalysisDemoCross-degree validation and scenario recommendation.
ElliottWaveTrendBacktestTrend-bias directionality over backtest and walk-forward windows.
HighRewardElliottWaveBacktestHigh-reward Elliott Wave strategy presets.
Run any example with:
# Linux/macOS
./mvnw -pl ta4j-examples exec:java \
  -Dexec.mainClass=ta4jexamples.analysis.elliottwave.ElliottWaveIndicatorSuiteDemo

# Windows CMD
mvnw.cmd -pl ta4j-examples exec:java "-Dexec.mainClass=ta4jexamples.analysis.elliottwave.ElliottWaveIndicatorSuiteDemo"
ElliottWaveIndicatorSuiteDemo accepts optional command-line arguments to load live data: [dataSource] [ticker] [barDuration] [startEpoch] [endEpoch]. For example, pass YahooFinance AAPL PT1D 1672531200 1704067200 to analyse daily AAPL bars.