OU Pairs Trading with Half-Life Sizing

Pairs trading begins with a relative price: long one asset, short another in a ratio that makes their combined exposure plausibly stationary. The most useful first distinction is between a correlated pair and a mean-reverting spread. Correlation describes contemporaneous movement; it does not ensure a relative mispricing will close. See pairs trading in crypto and stocks for the market-structure differences that make this distinction especially important.

For a log-price spread xt = log(At) - beta log(B_t), the Ornstein-Uhlenbeck process writes:

dx_t = kappa (mu - x_t) dt + sigma dW_t
half-life = ln(2) / kappa

kappa is the speed of pull toward equilibrium. A half-life is operationally useful: it informs signal horizon, position scale, holding-period expectations, and when a position has failed its premise. It is an estimate, not a deadline at which an open trade is guaranteed to converge.

Estimating the process discretely

At daily frequency, fit the equivalent AR(1) regression:

x_t = a + b x_(t-1) + epsilon_t
kappa = -ln(b) / Delta_t,   mu = a / (1 - b)

Only use estimates with 0 < b < 1; a negative coefficient can look strongly mean-reverting at one sampling interval while representing oscillation, stale prices, or mismatched trading hours. Estimate on an expanding or rolling window entirely before the trade date, then inspect confidence intervals. Short samples make half-lives highly unstable near b = 1.

EstimateTrading interpretationCaution
b near 1slow convergenceestimates are noisy and capital is tied up
low half-lifefrequent opportunitiescosts can dominate small moves
high residual sigmalarger raw swingsnot necessarily more alpha
changing mulikely regime shifta static z-score is misleading
import numpy as np
import statsmodels.api as sm

def ou_parameters(spread, periods_per_year=252):
    y = spread.iloc[1:].to_numpy()
    lag = spread.iloc[:-1].to_numpy()
    fit = sm.OLS(y, sm.add_constant(lag)).fit()
    a, b = fit.params
    if not 0 < b < 1:
        return {"valid": False}
    kappa = -np.log(b) * periods_per_year
    half_life_days = np.log(2) / (-np.log(b))
    mu = a / (1 - b)
    sigma_eps = fit.resid.std(ddof=1)
    return {"valid": True, "beta_ar": b, "mu": mu,
            "half_life_days": half_life_days, "sigma_eps": sigma_eps}

The hedge ratio is a separate estimation problem. A regression of one log price on another can produce a useful spread, but cointegration tests and residual diagnostics should decide whether it is stable. For multiple related assets, a Johansen test and VECM is usually more coherent than selecting pairwise regressions after searching many candidates.

Signal and half-life-aware sizing

Define a standardized displacement using an OU equilibrium variance or a rolling residual scale:

z_t = (x_t - mu) / sigma_x

Enter at |z| > 1.5--2.5, reduce near zero, and place a hard risk limit beyond the ordinary signal threshold. A compact sizing rule combines conviction and speed:

gross_t = risk_budget * min(|z_t| / z_max, 1) * f(half_life)

where f declines for very slow spreads. A two-standard-deviation deviation with a 120-day half-life has much less attractive annualized opportunity than the same deviation with a 10-day half-life, all else equal. Do not simply allocate inversely to half-life: extremely fast apparent reversion can be bid-ask bounce that disappears after executable prices and a one-bar delay.

DecisionHalf-life inputBetter implementation
Entry thresholdexpected speedrequire net expected edge after costs
Position sizeconvergence rate and volatilitycap gross and single-name risk
Time stopseveral estimated half-livesreassess model, do not average blindly
Rebalance cadenceavoid overreacting to noisematch data frequency and liquidity

Map the spread position into shares so the hedge ratio, beta, and dollar exposure are all controlled. If the pair uses securities with different volatilities, a unit spread may still have concentrated leg risk. Recalculate target shares at controlled intervals; continuous hedge-ratio adjustment creates hidden turnover.

Model risk is trade risk

The OU model assumes a fixed equilibrium and Gaussian diffusion. Real spreads jump on earnings, regulation, index changes, takeover rumors, borrow recalls, and capital structure events. A statistically attractive short can become impossible or expensive to maintain. Test signals excluding the estimation period, reserve a separate validation set, and correct for the many pair and threshold combinations considered.

Use executable bid/ask prices, financing, dividends, borrow fees, and locate failures. For ETFs and futures, account for roll and creation mechanics; for international shares, synchronize closes. A close-to-close backtest that flips at the same close systematically overstates a fast strategy.

Monitor rolling beta, half-life, residual volatility, and realized correlation. Suspend entry when stationarity tests fail persistently or the equilibrium moves beyond a predefined band. A stop is not a claim that mean reversion is false; it recognizes that the distribution has changed faster than capital can safely wait.

Key takeaways

  • Half-life translates a mean-reversion estimate into a holding-period and sizing input.
  • Estimate the hedge ratio and OU dynamics out of sample and with lagged data.
  • Fast apparent reversion can be microstructure noise; slow reversion has opportunity cost.
  • Stops, borrow constraints, and regime monitoring are core parts of a pairs strategy.
#pairs trading #Ornstein-Uhlenbeck #half-life #mean reversion #hedge ratio