18 — Hedging & Protection

Pairs Trading#

Market NeutralStatistical1 min read
◆ The PatternLong/short correlated pairs to profit from spread convergence

Pairs trading exploits temporary mispricings between cointegrated securities. The spread = log(PA) − β · log(PB) should be stationary.

zt = (spreadt − μ) / σ   →   enter at |z| > 2, exit at |z| < 0.5
Cointegration (Engle-Granger or Johansen test) is stronger than correlation — it means the spread is mean-reverting. The Augmented Dickey-Fuller test checks stationarity.
// Interactive — entry z-score and spread convergence
Entry z-score
Regime risk. Structural breaks (e.g. mergers, sector shifts) can permanently break a pair's relationship.
Pattern bridge: Stationarity tests connect to The Toolkit — Hypothesis Testing.
Worked example

Two simulated log prices that share a random walk, with a mean-reverting gap between them. Regression recovers the hedge ratio: 1.21 (the true value is 1.2). Today the spread sits 0.54 standard deviations below its mean — no trade at a usual entry of 2. The spread’s half-life is 7.2 days, a guide to how long a trade should take.

Where it misleads
  • A good fit in the past is not cointegration in the future; mergers, new business lines or regulation can break a pair for good.
  • Testing many pairs and keeping the best guarantees some false ones. Use a proper test (Engle–Granger) and out-of-sample data.
  • Profits from simple pairs trading have fallen since it was published (Do & Faff 2010).
In code
rng = np.random.default_rng(11)
common = np.cumsum(rng.normal(0, 0.01, 500))               # a shared random walk (log prices)
spread_true = np.zeros(500)
for t in range(1, 500):
    spread_true[t] = 0.9 * spread_true[t - 1] + rng.normal(0, 0.005)   # a mean-reverting gap
a, b = 4.0 + 1.2 * common + spread_true, 3.0 + common
beta = np.polyfit(b, a, 1)[0]                              # hedge ratio
spread = a - beta * b
z = (spread[-1] - spread.mean()) / spread.std()
phi = np.polyfit(spread[:-1], spread[1:], 1)[0]            # AR(1) persistence of the spread
half_life = -np.log(2) / np.log(phi)
Python, in your browser — edit the code and run it again

Assumes import numpy as np and import pandas as pd. Each snippet carries its own example numbers; the comments say which are made up or simulated.

Sources
  • E. Gatev, W. N. Goetzmann & K. G. Rouwenhorst, “Pairs Trading: Performance of a Relative-Value Arbitrage Rule”, Review of Financial Studies 19(3), 2006
  • R. F. Engle & C. W. J. Granger, “Co-Integration and Error Correction: Representation, Estimation, and Testing”, Econometrica 55(2), 1987
  • B. Do & R. Faff, “Does Simple Pairs Trading Still Work?”, Financial Analysts Journal 66(4), 2010

Last reviewed October 2026 · How this site checks its content · Report a mistake

Linked from 1
← Previous
Stop-Loss Strategies
Open in the full reader, with the topic sidebar