Correlation and Cointegration (A Light Treatment) TODO
Concept
The correlation coefficient standardizes the degree of linear co-movement between two variables to a value between -1 and 1; it does not imply causation and fails to capture nonlinear relationships properly. In time series, when two series each have their own trend, correlation and regression coefficients can come out large even with no real relationship — the spurious regression problem — so correlation between levels shouldn't be trusted at face value. Cointegration describes a relationship where each series is individually non-stationary (has a unit root), but some linear combination of the two is stationary and mean-reverting — a statement that a long-run equilibrium exists between the two series. The standard procedure is to test each series for a unit root, estimate the cointegrating relationship, then test whether its residuals are stationary; if cointegration holds, an error-correction model can describe how fast short-term deviations revert to equilibrium. In short: correlation is a statement about simultaneous movement, and cointegration is a statement about a long-run relationship.
Pairs trading, hedge-ratio sizing, and analyzing peg deviations for stablecoins or LSTs are all really asking whether the levels of two series stay tied together over the long run — which is cointegration's territory, not correlation's.
Code & Formula
# 상관관계와 공적분(가볍게) — 수준(level) 상관의 함정 vs 스프레드의 평균회귀
# 두 계열이 각자 추세를 가지면 아무 관계가 없어도 수준끼리는 강하게 "상관"돼 보인다(허위회귀).
import random
import statistics
random.seed(3)
N = 500
def corr(xs, ys):
mx, my = statistics.mean(xs), statistics.mean(ys)
cov = sum((x - mx) * (y - my) for x, y in zip(xs, ys))
sx = (sum((x - mx) ** 2 for x in xs)) ** 0.5
sy = (sum((y - my) ** 2 for y in ys)) ** 0.5
return cov / (sx * sy)
# 1) 서로 무관한 두 랜덤워크(각자 추세만 가짐) — 진짜 관계는 없다
a_level = [100.0]
b_level = [50.0]
for _ in range(N):
a_level.append(a_level[-1] + random.gauss(0.15, 1.0)) # 독립적인 상승 추세(추세가 노이즈를 압도)
b_level.append(b_level[-1] + random.gauss(0.10, 1.0))
a_ret = [a_level[i] - a_level[i - 1] for i in range(1, len(a_level))]
b_ret = [b_level[i] - b_level[i - 1] for i in range(1, len(b_level))]
print(f"[무관한 두 랜덤워크] 수준(level) 상관 = {corr(a_level, b_level):.3f} (허위로 높게 나옴)")
print(f"[무관한 두 랜덤워크] 수익률(return) 상관 = {corr(a_ret, b_ret):.3f} (실제로는 0에 가까움)")
# 2) 공적분 관계: 두 계열은 각자 비정상(추세)이지만 스프레드는 평균회귀하도록 구성
x_level = [100.0]
for _ in range(N):
x_level.append(x_level[-1] + random.gauss(0.0, 1.0))
spread = [0.0]
for _ in range(N):
# 스프레드가 커질수록 되돌아오는 힘(AR(1), 계수<1 => 평균회귀) + 노이즈
spread.append(spread[-1] * 0.8 + random.gauss(0.0, 0.5))
y_level = [x_level[i] - spread[i] for i in range(N + 1)]
print(f"\n[공적분 쌍] 수준 상관 = {corr(x_level, y_level):.3f}")
print(f"[공적분 쌍] 스프레드 평균/표준편차 = {statistics.mean(spread):.3f} / {statistics.stdev(spread):.3f}")
print(f"[공적분 쌍] 스프레드가 [-3, 3] 범위 안에 머문 비율 = {sum(-3 <= s <= 3 for s in spread) / len(spread):.2%}")
print("-> 스프레드가 특정 범위를 벗어나지 않고 되돌아온다면 두 계열이 장기적으로 묶여있다는 신호(공적분).")
Exercise
Take price time series for two assets, compute the correlation of log-price levels and the correlation of log returns separately, compare how different the values are, and plot whether the spread between the two series mean-reverts.
Practical Connection
Prices of different markets covering the same event, or of complementary outcome tokens, should stay tied together in the long run — so if the spread does not mean-revert, that's grounds to suspect a liquidity shortfall or a difference in settlement terms.
Where it lands in Jayverse
- Verex: run a cointegration test, not just a correlation check, between complementary outcome tokens or duplicate-event markets. Unit-root test each series, then test the residual of their relationship for stationarity — a correlation on price levels can look fine while the peg is actually drifting.
- Auditor: when the spread fails to mean-revert, name the cause in the methodology note. State explicitly whether it's a liquidity shortfall or a settlement-terms mismatch between the two markets — two different fixes for one symptom.
- Number: publish the cointegration/spread-reversion routine as a reusable indicator. Point it at any pair of related Verex markets on demand instead of re-deriving the test per incident.
Key expressions
| Expression | 뜻 · 쓰이는 자리 |
|---|---|
| co-movement | 동반 움직임·같이 변동함 · 두 변수가 같은 방향으로 함께 움직이는 정도. "the degree of linear co-movement between two variables" |
| spurious regression | 가짜 회귀·허위 회귀 관계 · 실제 관련 없는 두 추세가 통계상 관계 있어 보이는 함정. "the spurious regression problem" |
| mean-reverting | 평균으로 회귀하는 · 값이 벌어져도 결국 평균 수준으로 돌아오는 성질. "stationary and mean-reverting" |
| tied together | 서로 묶여 있다·연동되어 있다 · 두 시계열이 장기적으로 같이 움직인다고 말할 때. "stay tied together over the long run" |
| at face value | 액면 그대로·곧이곧대로 · 겉으로 드러난 숫자를 검증 없이 믿지 말라고 할 때. "correlation between levels shouldn't be trusted at face value" |
| long-run equilibrium | 장기 균형 · 단기적으로 벗어나도 결국 되돌아오는 안정 상태를 가리킬 때. "a long-run equilibrium exists between the two series" |
| grounds to suspect | ~을 의심할 근거 · 어떤 이상 신호가 특정 문제를 의심하게 만들 때. "that's grounds to suspect a liquidity shortfall" |
| LSTs | 유동성 스테이킹 토큰(Liquid Staking Tokens) · 스테이블코인처럼 페그 유지 여부를 확인해야 하는 자산의 예시. "peg deviations for stablecoins or LSTs" |
| unit root | 단위근 · 시계열이 평균으로 돌아오지 않고 추세를 따라 계속 벗어나는(비정상) 상태를 판정하는 통계 개념. "each series is individually non-stationary (has a unit root)" |
If you study this on a given day, add a note link and a ✅ to this line in the source curriculum (docs/knowledge/math-50-curriculum.md) and this spot will lead straight to the note body. You can also write directly on this page — but regenerating overwrites it, so it's safer to keep anything you want to save as markdown under docs/algorithms/.