Ta4j Wiki

Documentation, examples and further information of the ta4j project

View the Wiki On GitHub

This project is maintained by ta4j Organization

When Signals Align: Correlation, Lead-Lag & Event Dependence

Pearson correlation answers one question: do two series move together in the same direction right now? Trading signals ask harder questions:

ta4j’s correlation and event-analysis surface (0.24.2+) answers all four. The four lenses ship in two phases and complement each other; this guide shows when to use which, with the exact API.

Association, not causation. Every tool on this page measures association between series or events. None of them proves that one signal causes another. Use them to describe and filter relationships, then validate causality with controlled experiments on out-of-sample data.

Choosing a lens

Question Tool Package
Which lag best relates two continuous indicators, and how strong is it? LeadLagCorrelationIndicator org.ta4j.core.indicators.statistics
How similar are two waves when timing may be distorted? DynamicTimeWarpingDistanceIndicator org.ta4j.core.indicators.statistics
How well do predicted events match reference events one-to-one? EventSynchronizationIndicator org.ta4j.core.indicators.statistics.event
How much does a continuous state reduce uncertainty about a future event? EventMutualInformationEvaluator org.ta4j.core.analysis.event

All four are deterministic and reproducible, and each ships a committed-data example (see End-to-end demos).

1. Lead/lag correlation profile (Phase 2)

LeadLagCorrelationIndicator scans a bounded lag range as a rolling indicator and reports, for every bar, the signed correlation at each lag plus one deterministic “best” lag.

LeadLagCorrelationIndicator leadLag = new LeadLagCorrelationIndicator(
        momentum, close, 32, 20, LagSelectionPolicy.MAXIMUM_ABSOLUTE_CORRELATION);
LeadLagCorrelationIndicator.Profile profile = leadLag.getProfile(lastIndex);

Construction

The sign convention matches LaggedCorrelationIndicator: a positive lag means the first indicator leads the second.

Selection policy

LagSelectionPolicy has two values:

The profile

getProfile(index) returns an immutable Profile anchored at index:

Accessor Meaning
points() One Point per lag in ascending lag order — undefined lags are retained (correlation NaN, sampleCount 0), so the profile is never silently trimmed
bestLags() Every lag tying for the best score under the policy, ascending; empty when no lag is defined
selectedLag() One deterministic pick: smallest absolute lag, then smallest signed lag (a symmetric [-k, k] tie resolves to -k)
selectedCorrelation() The signed correlation at the selected lag

Each Point carries lag, correlation (Pearson), and sampleCount. A defined correlation is guaranteed finite and within [-1, 1] up to rounding tolerance. getValue(index) returns the selected lag’s correlation directly when you only need the scalar.

When to prefer the profile: when the best lag changes over time or you want the whole correlation curve, read points(). The getValue scalar is the fastest way to feed a best-lag correlation into a strategy rule.

2. Dynamic time warping shape distance (Phase 2)

DynamicTimeWarpingDistanceIndicator computes the minimum-cost monotonic alignment between two rolling windows — the classic way to compare shape when level, scale, and timing differ.

DynamicTimeWarpingDistanceIndicator dtw = new DynamicTimeWarpingDistanceIndicator(
        momentum, close, 32, DynamicTimeWarpingDistanceIndicator.Config.shapeComparison(5));
Num distance = dtw.getValue(lastIndex);

Config.shapeComparison(radius) bundles the baseline shape-comparison setup:

If absolute level matters, use SequenceNormalization.NONE; LocalDistance.ABSOLUTE and PathCostNormalization.NONE are available for raw-cost semantics.

Alignment band

Complexity is O(W * min(W, 2r + 1)) time and O(W) memory for window W and radius r. The value is NaN until the window is fully available.

3. Event synchronization: F1 of sparse event streams (Phase 1)

When events are sparse and near-coincident rather than timestamp-identical, Pearson-style correlation is a poor measure. EventSynchronizationIndicator scores two Boolean event streams over the same series with deterministic one-to-one matching:

EventSynchronizationIndicator sync = new EventSynchronizationIndicator(
        belowZeroCrosses, swingHighConfirmation, barCount, 12, 12);
Num f1 = sync.getValue(lastIndex);                       // cached scalar
EventSynchronizationIndicator.Result r = sync.getResult(lastIndex); // full diagnostics

Construction

Only Boolean.TRUE counts as an event; false and null do not. At bar index, the indicator evaluates the closed trailing window [index - barCount + 1, index].

What the F1 means here

Predicted and reference events are matched with a lexicographic objective:

  1. maximize the number of matched pairs;
  2. among maximum-cardinality assignments, minimize the total absolute offset;
  3. then minimize the worst absolute offset;
  4. then prefer the lexicographically earliest sequence of pairs.

A pair is eligible when -maxLagBars <= offset <= maxLeadBars, where offset = referenceIndex - predictedIndexa positive offset means the prediction leads the reference.

precision, recall, and f1Score follow the usual definitions from the matched counts:

Window and availability semantics (read this twice)

Full diagnostics

getResult(index) (recomputed per call — use getValue for the cached scalar) exposes Result:

Sparse streams only

The matcher has a hard capacity: a window whose alignment problem needs more than 8 million cells — (predicted events + 1) * (reference events + 1) > 8_000_000 — throws IllegalArgumentException from getResult/getValue, and so does a source whose cached event history exceeds the same limit. Sparse streams stay far below this bound; a dense stream firing on most bars inside a large window can hit it. Keep windows sparse enough, or handle the exception.

4. Event-aware mutual information (Phase 2)

EventMutualInformationEvaluator measures how much a continuous predictor state reduces uncertainty about whether a target event occurs in an explicit bar window ahead of the sample:

EventMutualInformationConfig config = new EventMutualInformationConfig(
        0, 3, 8, BinningStrategy.EQUAL_FREQUENCY); // current-or-next-3-bars window, 8 bins
EventMutualInformationEvaluator evaluator = new EventMutualInformationEvaluator();
EventMutualInformationResult mi = evaluator.evaluate(
        momentum, swingHighConfirmation, 0, lastIndex, config);

mi.mutualInformationNats();          // raw MI in nats
mi.targetEntropyNats();              // H(Y)
mi.normalizedMutualInformation();    // MI / H(Y), always in [0, 1]
mi.sampleCount();                    // samples actually evaluated
mi.positiveTargetCount();            // samples with >= 1 target event in window
mi.positiveTargetRate();             // positiveTargetCount / sampleCount
mi.effectiveBinCount();              // bins actually formed

The target window

A predictor sample at index i is labeled positive when at least one target event occurs in the inclusive bar window [i + targetWindowStartBars, i + targetWindowEndBars]:

Both offsets are non-negative, and every target index must lie inside the evaluation range: samples whose target window would cross the range boundary are excluded. Target windows never cross the evaluation partition boundary — no look-ahead into validation data.

Binning the predictor

predictorBinCount (at least 2, capped at MAX_PREDICTOR_BIN_COUNT) requests bins, then BinningStrategy decides how:

Reading the result

Rolling needs

EventMutualInformationEvaluator is a one-shot evaluator over an explicit range. For a rolling Indicator<Num> of equal-width mutual information, see MutualInformationIndicator; for regime-conditioned correlation, RegimeSegmentedCorrelationIndicator.

End-to-end demos

Both examples run on a committed, ossified daily BTC dataset from the examples classpath — fully reproducible, no network access:

Confirmation-time semantics (both demos): the ZigZag Boolean indicators are true at the bar where a prior pivot becomes confirmed — the first bar at which the reversal is causally known. The historical pivot bar may be several bars earlier. Matching and MI operate on the confirmation indexes; projecting a confirmation back to its pivot index is look-ahead information and must never enter a fitness calculation.

What’s next: Phase 3 (CF-455)

The event-relationship program ships in phases. Phase 1 delivered event synchronization (ta4j PR #1602); Phase 2 delivered the lead/lag profile, DTW shape distance, and event-aware mutual information (ta4j PR #1603).

Phase 3 (Linear CF-455, in Backlog) integrates all four lenses into ta4j’s unified parameter research workflow (ParameterResearch, from PR #1542), so the metrics become directly optimizable objectives:

Grid, genetic, and particle-swarm search become interchangeable search plans behind one public seam — switching algorithms is a one-line change — with seeded reproducibility, exact unique-evaluation budgets, and independent holdout/walk-forward validation of the training-selected top K. This page will grow a tuning guide when CF-455 lands.