Tutorial/FX forecasting

FX 예측에서 랜덤워크를 이길 수 있을까? 30줄로 직접 검증하기

고전적인 결과에 따르면 "변화 없음" 예측이 표본 밖에서는 어떤 모델도 이깁니다. 실시간 일별 환율로 직접 검증할 수 있는 코드를 소개합니다.

MMexchangerate.dev·Jul 6, 2026·7분 읽기

모든 환율 예측이 넘어야 하는 기준선은 랜덤워크입니다. 내일의 환율이 오늘과 같다고 보는, 아무것도 하지 않는 예측입니다. Meese와 Rogoff(1983) 이후 단기 구간에서 이를 이긴 사례는 거의 없으며, 2025년 연준의 워킹페이퍼는 현대 머신러닝에서도 이를 재확인했습니다. 이 글은 40년째 무너지지 않는 벽의 실습편으로, 데이터를 가져와 벤치마크를 만들고 모델을 정직하게 채점하는 약 30줄의 파이썬 코드를 다룹니다.

Key points
모든 FX 예측이 이겨야 하는 벤치마크는 랜덤워크, 즉 내일의 환율이 오늘과 같다는 예측입니다. 일별 주요 통화쌍에서는 이를 이기기가 놀라울 정도로 어렵습니다.
전체 검증을 Free 등급에서 실행할 수 있습니다. 시계열을 가져와 변화 없음 벤치마크를 만들고, 표본 밖 R²와 Clark-West 검정으로 채점하면 됩니다.
주말과 공휴일에 이월된 행(is_forward_filled 플래그)을 제외하지 않으면, 모델이 조용히 변화 없는 일요일 값에서 잘못된 패턴을 학습하게 됩니다.
모델의 복잡도를 높이면 표본 밖 정확도는 좋아지는 것이 아니라 오히려 나빠집니다. 과적합이 실시간으로 일어나는 것을 직접 확인할 수 있습니다.
자매 글에서는 이 검증을 다섯 개 통화쌍에 적용해, 몇 가지 그럴듯한 선택만으로도 "유의미한" 우위가 얼마나 쉽게 만들어질 수 있는지 보여줍니다.

벤치마크: "내일은 오늘과 같다"

1983년 Meese와 Rogoff는 내일의 환율이 오늘과 같을 것이라는 모델을 능가하는 거시경제 모델은 없다는 것을 보여주었습니다. 금리 격차, 통화량, 구매력평가 등 어느 것도 도움이 되지 않았습니다. 이 "모델"이 바로 랜덤워크이며, 40년간 점점 더 정교해진 경제학도 대부분 이 벽에 부딪혀 왔습니다. 그 이유는 환율을 예측할 수 있을까?에서 다룹니다. 이 글은 그 실전편으로, 직접 그 주장을 검증할 수 있는 코드를 다룹니다.

2025년 9월, 한 연준 이코노미스트가 현대 머신러닝을 같은 검증대에 올렸습니다. *Virtue or Mirage? Complexity in Exchange Rate Prediction*(Rehim Kılıç, FEDS 2025-089)이라는 이 논문은 무작위 푸리에 특성에 대한 Ridge 회귀와 랜덤워크를 표본 밖 R², Clark-West 검정, Diebold-Mariano 검정으로 비교했습니다. 결론은 신중했습니다. 복잡도는 "미미하고 국소적인 이득"만을 주었으며, 그마저도 "불안정하고 통계적으로 유의미한 경우가 드물었고", 랜덤워크에 대한 체계적인 우위로 이어지지는 않았습니다.

재현할 수 있는 것과 없는 것
이 논문은 환율 API가 제공하지 않는 월별 거시 펀더멘털 데이터를 사용하므로 정확히 재현할 수는 없습니다. 하지만 실제로 대부분의 개발자가 마주치는 핵심 질문은 검증할 수 있습니다. 가격 이력만 사용해서, 학습된 모델이 "내일은 오늘과 같다"를 이길 수 있을까요?

데이터 가져오기

/v1/range 엔드포인트에서 5년치 일별 EUR/USD 데이터를 가져옵니다. Free 등급은 시작하는 데 키가 필요 없습니다.

python — fetch a daily seriescopy
import requests, numpy as np, pandas as pd

def fetch_range(base, symbol, start, end, key=None):
    rows, cursor = [], None
    headers = {"Authorization": f"Bearer {key}"} if key else {}
    while True:
        p = {"base": base, "symbols": symbol, "start_date": start, "end_date": end}
        if cursor: p["cursor"] = cursor
        d = requests.get("https://api.exchangerate.dev/v1/range",
                         params=p, headers=headers, timeout=30).json()
        rows += [{"date": r["date"], "rate": r["rates"][symbol]} for r in d["data"]]
        if not d.get("has_more"): break
        cursor = d["next_cursor"]
    df = pd.DataFrame(rows).drop_duplicates("date")
    df["date"] = pd.to_datetime(df["date"])
    return df.sort_values("date").set_index("date")

fx = fetch_range("EUR", "USD", "2021-01-01", "2026-01-01")

제대로 해둘 만한 것 한 가지: 각 행에는 is_forward_filled 플래그와 source 필드가 함께 담겨 있습니다. 주말과 공휴일은 이월된 값이므로, 제대로 된 연구라면 이 행들을 제외해야 합니다. 모델이 변화 없는 일요일 값에서 "학습"하게 두어서는 안 되며, 이는 룩어헤드 편향 없이 백필하기에서 다루는 룩어헤드 함정의 축소판입니다.

벤치마크와 도전자 모델 만들기

랜덤워크는 매일 수익률 0을 예측합니다(Meese와 Rogoff가 사용한 드리프트 없는 마팅게일입니다). 도전자 모델에는 가격 시계열만으로 얻을 수 있는 저렴한 우위, 즉 모멘텀, 평균회귀, 실현 변동성을 모두 제공하고, 논문에서 사용한 추정량인 Ridge가 내일의 수익률을 예측하도록 합니다.

python — features and a Ridge modelcopy
from sklearn.linear_model import Ridge

fx["ret"] = np.log(fx["rate"]).diff()
f = pd.DataFrame(index=fx.index)
for lag in (1, 2, 3, 5, 10): f[f"lag{lag}"] = fx["ret"].shift(lag)
f["mom5"], f["mom20"] = fx["ret"].rolling(5).sum().shift(1), fx["ret"].rolling(20).sum().shift(1)
f["vol10"], f["y"] = fx["ret"].rolling(10).std().shift(1), fx["ret"]
f = f.dropna()

split = int(len(f) * 0.7)
X = [c for c in f.columns if c != "y"]
m = Ridge(alpha=1.0).fit(f.iloc[:split][X], f.iloc[:split]["y"])
pred, y = m.predict(f.iloc[split:][X]), f.iloc[split:]["y"].values

정직하게 채점하기

랜덤워크 대비 표본 밖 R²(양수면 이긴 것입니다)와, 랜덤워크가 모델 안에 내포되어 있을 때 적합한 Clark-West 검정을 함께 사용합니다.

python — out-of-sample R² and Clark-Westcopy
from scipy import stats
r2 = 1 - np.sum((y - pred)**2) / np.sum(y**2)
f_hat = y**2 - ((y - pred)**2 - pred**2)
t = f_hat.mean() / (f_hat.std(ddof=1) / np.sqrt(len(f_hat)))
print(f"OOS R2 {r2:+.4%} | Clark-West t {t:+.2f} p {1-stats.norm.cdf(t):.3f}")

EUR/USD, GBP/USD, USD/JPY와 몇 가지 구간에 걸쳐 실행해 보세요. 패턴은 완고합니다. 표본 밖 R²는 대체로 0이거나 그 아래, 즉 거의 변화가 없는 수준에서 몇 퍼센트 마이너스 사이에 머무는데, 이는 학습된 모델이 대부분 아예 변화가 없다고 가정하는 것보다도 성능이 나빴다는 뜻입니다. Clark-West 통계량이 유의성을 넘기는 경우는 드물며, 유의성에 근접한 셀들도 분할 비율이나 표준오차를 조금만 바꾸면 흔들립니다. 특성 개수를 늘려 "복잡도"를 더하면 표본 밖 성능은 더 나빠지기만 합니다. 과적합이 실시간으로 일어나는 것을 직접 지켜보는 셈입니다. 저희는 다섯 개 통화쌍에 대해 전체 검증을 실행하고 모든 수치를 공개했습니다.

이것이 개발에서 중요한 이유

"예측 환율" 기능을 출시한다면, 이겨야 할 기준선은 오늘의 환율을 그대로 반환이며, 일별 주요 통화쌍 데이터에서는 이 기준선을 이기기가 놀라울 정도로 어렵습니다. 조용히 더 나쁜 성능을 내는 예측 엔드포인트는 누군가의 결제 플로우나 재무 대시보드에 자신만만하게 틀린 숫자를 올려놓는 것과 같습니다.

진짜 방어할 수 있는 가치는 다른 곳에 있습니다. 다음 환율에 대한 최선의 점 예측은 실제로 현재 환율이므로, 현재 수준을 정확하고 빠르게 제공하세요. 점 예측 대신 불확실성을 수치화하세요. 변동성과 신뢰구간은 랜덤워크를 이길 필요 없이도 진정으로 유용합니다. 그리고 예측 가능성이 일 단위로는 거의 0에 가깝고 몇 달 단위로 가야 조금씩 나타난다는 점, 즉 문헌에서 근소한 우위가 발견되는 지점이 바로 거기라는 점에서 예측 구간에 대해서는 정직해야 합니다.

평평한 랜덤워크 선 자체가 정보입니다. 오늘의 가격이 이미 내일에 대해 알 수 있는 것 대부분을 담고 있다고 시장이 말하고 있는 것입니다. 데이터 제공자에게 승리하는 방법은 그 가격을 누구보다 정확하고 빠르게 공개하고, 예측의 한계에 대해 솔직한 것입니다.

MM
exchangerate.dev
인디케이티브 환율을 다루는 개발자를 위한 FX 데이터 가이드.

Keep reading

Blog다섯 개 통화쌍으로 랜덤워크에 도전해 봤습니다Read Guide환율을 예측할 수 있을까? 40년째 무너지지 않는 벽Read Guide룩어헤드 편향 없이 FX 환율 백필하기Read