あらゆる為替予測が越えなければならないハードルはランダムウォークである。何もしない予測、明日のレートは今日と同じ、というものだ。Meese and Rogoff (1983)以来、短期ではこれに勝てるものはほとんどなく、2025年のFEDワーキングペーパーは現代の機械学習でも同じ結論を再確認した。これは40年続く壁の実践編であり、データを取得し、ベンチマークを構築し、モデルを正直に採点する約30行のPythonコードである。
- あらゆるFX予測が上回らなければならないベンチマークはランダムウォークである、明日のレートは今日と同じ、というものだ。主要な日次通貨ペアでは、これに勝つことは驚くほど難しい。
- Plus/Proで全テストを実行できる: 系列を取得し、変化なしベンチマークを構築し、サンプル外R²とClark-West検定で採点する。
- 前方補完された週末・祝日の行(
is_forward_filledフラグ)を除外しないと、モデルが平坦な日曜日から静かに学習してしまう。 - モデルを複雑にするほどサンプル外の精度は悪化する。過学習がリアルタイムで起きる様子を観察できる。
- 関連記事では同じテストを5つの通貨ペアで実行し、いくつかの妥当な選択だけで「有意な」優位性をいかに簡単に作り出せるかを示している。
ベンチマーク: 「明日は今日と同じ」
1983年、Meese and Rogoffは、明日の為替レートは今日と同じだと言うモデルに勝てるマクロモデルは存在しないことを示した。金利差、マネーサプライ、購買力平価、どれも役に立たなかった。その「モデル」がランダムウォークであり、40年にわたる経済学の巧妙化はほとんどそれをはね返せずにきた。理由は為替レートは予測できるか?で扱っている。本ガイドはその実践編であり、この主張を自分で検証するためのコードである。
2025年9月、FEDのエコノミストが現代の機械学習に同じテストを課した。論文*Virtue or Mirage? Complexity in Exchange Rate Prediction*(Rehim Kılıç, FEDS 2025-089)は、ランダムフーリエ特徴量に対するリッジ回帰をランダムウォークと対決させ、サンプル外R²、Clark-West検定、Diebold-Mariano検定で判定した。結論は慎重で、複雑さがもたらすのは「わずかで局所的な利得」にとどまり、それは「脆弱でめったに統計的に有意ではなく」、ランダムウォークに対する体系的な優位性には決してつながらなかった。
この論文はレートAPIが提供しない月次のマクロファンダメンタルズを使っているため、完全には再現できない。しかし、多くのビルダーが実際に直面する核心的な問いは検証できる: 価格履歴だけを使って訓練したモデルは「明日は今日と同じ」に勝てるのか、という問いである。
データを取得する
/v1/rangeからEUR/USDの5年分の日次データをPlusかProのキーで取得する。
import requests, numpy as np, pandas as pddef fetch_range(base, symbol, start, end, key): rows, cursor = [], None headers = {"Authorization": f"Bearer {key}"} 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", "YOUR_API_KEY")ここで丁寧にやるべきことが一つある: 各行にはis_forward_filledフラグとsourceフィールドが付いている。週末と祝日は前方補完されているため、真面目な検証ではこれらの行を除外する。平坦な日曜日からモデルに「学習」させたくはない、先読みバイアスなしのバックフィルで扱っている先読みの罠の小さな一例である。
ベンチマークと挑戦者モデルを構築する
ランダムウォークは毎日ゼロリターンを予測する(Meese and Rogoffが使ったドリフトなしのマルチンゲール)。挑戦者には、価格系列だけから得られるあらゆる安価な優位性、モメンタム、平均回帰、実現ボラティリティ、を与え、リッジ回帰(論文自身の推定量)に明日のリターンを予測させる。
from sklearn.linear_model import Ridgefx["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検定を使う。
from scipy import statsr2 = 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²はゼロ以下、ほぼ横ばいから数パーセントのマイナスに収まり、訓練したモデルは概して「変化なし」と仮定するより成績が悪かったことを意味する。Clark-West統計量はめったに有意水準を超えず、有意に近づいたセルも、分割点や標準誤差を少しいじると動いてしまう。特徴量の数を増やして「複雑さ」を加えても、サンプル外の成績は悪化するだけである。過学習がリアルタイムで起きる様子を目の当たりにすることになる。5つのペアで完全なテストを実行し、すべての数字を公開した。
構築者にとってこれが重要な理由
「予測レート」という機能を出荷するなら、超えなければならないベースラインは「今日のレートをそのまま返す」であり、主要な日次通貨ペアのデータでは、そのベースラインに勝つのは驚くほど難しい。静かに劣化する予測エンドポイントは、誰かのチェックアウトフローや財務ダッシュボードに座り込む、自信満々の誤った数字である。
正当な価値は別の場所にある。現在の値を正確かつ高速に提供すること、次のレートに対する最良の点予測は本当に現在値だからである。点予測ではなく不確実性を定量化すること、ボラティリティと信頼区間は、ランダムウォークに勝つ必要なく本当に有用である。そして期間について正直であること:予測可能性は日次ではほぼゼロで、数か月かけてわずかに現れるにすぎず、文献がわずかな優位性を見出しているのはそこである。
あの平坦なランダムウォークの線そのものが情報である。それは、今日の価格がすでに明日について知りうることの大半を織り込んでいる、と市場が語っているのだ。データプロバイダーにとって勝ち筋は、その価格を誰よりも正確に、誰よりも速く公表し、予測の限界について率直であることである。