Back to Research
Portfolio Risk September 8, 2026 • 11 min read

The Epps Effect: Why Your Correlations Are Too Low, and How to Fix Them

Here is an experiment that ruins afternoons. Compute the correlation of two assets on daily returns; then on hourly; then on five-minute bars; then at the tick level, where you have the most data of all. The number does not stabilize — it melts toward zero. More data made the answer worse. That is not a bug in your code. It is the Epps effect, and it has a clean fix.

1. More data, worse answer

Thomas Epps documented it in 1979: measured correlation between assets falls as the sampling interval shrinks. Sample two stocks daily and you might see a correlation of 0.6; sample them every minute and the same pair reports something far weaker. This runs against every instinct — finer sampling means more observations, and more observations are supposed to mean a better estimate. Instead the estimate is biased, and biased in a consistent direction: toward zero. Forty-five years later, in fragmented, electronic, 24/7 markets, the effect is stronger than it was in Epps' data, not weaker.

2. The cause is almost embarrassingly simple

Two assets never trade at the exact same instant. Different instruments, different venues, different order flow — their ticks land on different clocks. Asset A prints at 10:00:00.012; asset B prints at 10:00:00.047; the next A print is at 10:00:00.100. There is no moment at which both are observed together. Yet every correlation formula in the textbook pairs returns contemporaneously — it assumes a return on A lines up with a return on B over the same interval. To use those formulas you first have to manufacture that alignment.

3. Why the shared clock biases toward zero

The standard fix is to impose a grid — sample both assets every minute, carry the last observed price forward (previous-tick interpolation), and pair the resulting returns. That manufactured alignment is exactly where the bias enters. When a grid point falls between B's trades, B's return for that interval is measured from a stale price while A's is fresh. The staleness is uncorrelated noise injected into one leg but not the other, and averaging a clean series against a noisy one drags the covariance down. The finer the grid, the more intervals contain stale prices, and the worse the attenuation. Sampling more often does not add signal; it adds mismatch.

This is the same family of problem as the microstructure noise that corrupts single-asset realized variance — but here it strikes the cross term, and it does not average out. It systematically hides co-movement.

4. Hayashi-Yoshida: measure on each asset's own clock

The better answer is to stop resampling entirely. In 2005, Takaki Hayashi and Nakahiro Yoshida published an estimator (On covariance estimation of non-synchronously observed diffusion processes, Bernoulli 11(2)) that works directly from each asset's own irregular trade times. No grid, no interpolation, no stale prices. Each price series defines its own return intervals — the span between consecutive trades. The estimator's rule is one line:

HY = Σi Σj  ΔXi · ΔYj  · 1{ interval i overlaps interval j }

For every pair of return intervals — one from A, one from B — that overlap in time, add the product of the two returns. Intervals that don't overlap contribute nothing. That's the whole estimator. It uses every trade, on its native timestamp, and never asks two prices to have been observed at the same moment. Hayashi and Yoshida prove it is consistent for the true integrated covariance: as you collect more data, it converges to the right answer instead of drifting away from it.

5. Why the overlap rule is the whole trick

The overlap indicator is doing the work that resampling botches. Two returns that share any stretch of clock time carry information about the same price move, so their product belongs in the covariance — regardless of whether their intervals have the same length or start at the same moment. Two returns from disjoint time spans share no information, so they are dropped. There is no interpolation because there is nothing to interpolate: the estimator never needs a price at a time it wasn't observed. And it degrades gracefully — feed it two series sampled on the same grid and every A interval overlaps exactly its matching B interval, so the double sum collapses back to the ordinary realized covariance Σ ΔXi·ΔYi. Hayashi-Yoshida is a strict generalization: you lose nothing on synchronous data and gain everything on asynchronous data.

6. Why it matters for a book, not just a number

An understated correlation is not an academic blemish; it is load-bearing. Hedge ratios derived from a biased covariance leave you systematically under-hedged — the offset you sized looks adequate on paper and isn't. Risk models that ingest an attenuated correlation matrix believe a book is more diversified than it is, right up to the stress day when everything moves together and the phantom diversification evaporates — the same crash-correlation blind spot we dissected in realized semicovariance. And pairs and basis trades, whose entire premise is a tight relationship between two names, misjudge how tight that relationship actually is. In each case the fix is not a safety margin bolted onto a wrong number; it is measuring the number correctly in the first place.

7. What it does not fix

Hayashi-Yoshida removes the bias from non-synchronous sampling; it is not a cure-all. It still needs enough overlapping activity — two assets that rarely trade in the same window give it little to work with, and a genuinely illiquid leg stays noisy. It estimates contemporaneous covariance, not lead-lag structure, so if A systematically leads B by a few seconds that relationship needs a lagged variant, not this one. And like any realized measure it assumes the prices it is fed are the signal; heavy quote noise still warrants a noise-robust treatment. The estimator solves the timing problem cleanly and leaves the others honestly visible, which is the right division of labour.

8. Computing it

Our open-source orderflow-metrics library ships it, dependency-free, in TypeScript and Python. You pass each series as timestamped prices on its own clock:

import {
  hayashiYoshidaCovariance,
  hayashiYoshidaCorrelation,
} from "orderflow-metrics";

// asset X and asset Y — their OWN, different trade times
const x = [ { time: 0, price: 100.0 }, { time: 1, price: 100.5 },
            { time: 3, price: 100.2 }, { time: 4, price: 100.4 } ];
const y = [ { time: 0, price: 50.0 },  { time: 2, price: 50.2 },
            { time: 3, price: 50.1 },  { time: 5, price: 50.4 } ];

hayashiYoshidaCovariance(x, y);  // 0.13   — sum over time-overlapping intervals
hayashiYoshidaCorrelation(x, y); // 0.5636 — normalized on each series' own variance

The two series share no common timestamps — yet the estimator reads their co-movement directly, with nothing resampled. On a shared grid the same call returns the ordinary realized covariance, so it is safe to use everywhere. The Python distribution exposes the same functions (hayashi_yoshida_covariance, hayashi_yoshida_correlation), taking TimedPrice observations sorted by time. They sit beside the realized covariance / correlation / beta and semicovariance tools inside the wider market-microstructure toolkit we build in the open — install it from our open-source page (npm and PyPI, MIT-licensed).

9. Conclusion

The correlation you compute is only as honest as the clock you compute it on. Snap two assets onto a shared grid and you quietly invent stale prices, and stale prices hide the very co-movement you are trying to measure — the finer you sample, the more you hide. Hayashi-Yoshida throws the grid away and reads covariance from the trades as they actually happened, converging to the truth instead of away from it, and collapsing to the familiar realized covariance when the clocks happen to agree. Measure co-movement on the clock the market actually trades on. Explore the rest of the toolkit in our quantitative research library, or read the implementation in our open-source metrics.

For more on market-microstructure analytics and open-source tooling, visit our official resources:

🧩 Open Source 💻 orderflow-metrics on GitHub 📚 More Research