Class LeadLagEstimator

java.lang.Object
com.quantfinlib.microstructure.LeadLagEstimator

public final class LeadLagEstimator extends Object
Streaming cross-asset lead-lag estimation: does instrument A's return now predict instrument B's return a few intervals from now? The classic pairs — EURUSD leads EURJPY, index futures lead the cash basket, the liquid large-cap leads its sector peers — are exactly this structure, and it is the basis of cross hedging and cross pricing.

Feed one onSample(double, double) per fixed sampling interval with the two instruments' returns over that interval (mid-to-mid log or simple returns; the caller owns the sampling clock, e.g. 100 ms for FX majors, 1 s for equities). The estimator keeps a small ring of the leader's recent returns and, for each candidate lag k = 0..maxLag, a time-decayed correlation between the leader's return k intervals ago and the follower's return now. O(maxLag) work per sample, zero allocation, single writer.

Reading the output: bestLag() is the k > 0 with the largest |correlation| — a genuine lead, because the leader's return was observable before the follower's. Compare it against correlationAtLag(0): if the contemporaneous correlation dominates every lagged one, the pair co-moves but neither side is tradeably ahead. expectedFollowerReturn() turns the best lag into a prediction via the regression beta at that lag. Like every streaming estimate here, treat a lead that appears and disappears with the decay window as noise; only a persistent best lag with stable sign is structure.

  • Constructor Summary

    Constructors
    Constructor
    Description
    maxLag 10 intervals, EWMA memory ~200 samples.
    LeadLagEstimator(int maxLag, double alpha)
     
  • Method Summary

    Modifier and Type
    Method
    Description
    double
    The signed correlation at bestLag(); 0 when bestLag() is 0.
    int
    The lag k >= 1 with the largest |correlation| — the estimated lead time in sampling intervals. 0 when no lagged correlation has been measured yet (fewer than 2 samples).
    double
    Time-decayed correlation between the leader's return lag intervals ago and the follower's return now. 0 until enough samples exist at that lag.
    double
    The regression prediction of the follower's next-interval return from the leader's return at the best lag: beta(k) x leaderReturn[t-k+1] with beta = cov/varLead. 0 when no lead has been measured.
    int
     
    void
    onSample(double leaderReturn, double followerReturn)
    One sampling interval: the leader's and follower's returns over the interval that just closed.
    void
    Restores the learned correlations; the leader ring resets, so post-restore samples never pair today's follower with yesterday's pre-close leader across the overnight gap — and lag k resumes updating only once the ring holds k+1 fresh samples, so the restored moments are never diluted by the empty ring either.
    long
     
    void
    Persists the per-lag correlation moments — see persist.Checkpoint.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • LeadLagEstimator

      public LeadLagEstimator(int maxLag, double alpha)
      Parameters:
      maxLag - largest lead to test, in sampling intervals (e.g. 10)
      alpha - EWMA weight of the correlation statistics, e.g. 0.01 (≈ a few-hundred-sample memory)
    • LeadLagEstimator

      public LeadLagEstimator()
      maxLag 10 intervals, EWMA memory ~200 samples.
  • Method Details

    • onSample

      public void onSample(double leaderReturn, double followerReturn)
      One sampling interval: the leader's and follower's returns over the interval that just closed. Non-finite inputs are a gap — the sample is dropped entirely (no moment updates, ring untouched) so a bad print can't poison the correlations. Gap caveat: after a dropped interval the ring holds the last valid samples, so lag k means "k valid samples ago", which is MORE than k wall-clock intervals across the gap. On a feed that gaps often, read bestLag() as a lead in valid samples, not wall-clock time.
    • correlationAtLag

      public double correlationAtLag(int lag)
      Time-decayed correlation between the leader's return lag intervals ago and the follower's return now. 0 until enough samples exist at that lag.
    • bestLag

      public int bestLag()
      The lag k >= 1 with the largest |correlation| — the estimated lead time in sampling intervals. 0 when no lagged correlation has been measured yet (fewer than 2 samples).
    • bestCorrelation

      public double bestCorrelation()
      The signed correlation at bestLag(); 0 when bestLag() is 0.
    • expectedFollowerReturn

      public double expectedFollowerReturn()
      The regression prediction of the follower's next-interval return from the leader's return at the best lag: beta(k) x leaderReturn[t-k+1] with beta = cov/varLead. 0 when no lead has been measured. This is a point estimate for hedging/pricing, subject to the same persistence caveat as bestLag().
    • maxLag

      public int maxLag()
    • samples

      public long samples()
    • writeState

      public void writeState(DataOutput out) throws IOException
      Persists the per-lag correlation moments — see persist.Checkpoint.
      Throws:
      IOException
    • readState

      public void readState(DataInput in) throws IOException
      Restores the learned correlations; the leader ring resets, so post-restore samples never pair today's follower with yesterday's pre-close leader across the overnight gap — and lag k resumes updating only once the ring holds k+1 fresh samples, so the restored moments are never diluted by the empty ring either. Throws on a maxLag or version mismatch.
      Throws:
      IOException