FREDとStooqで1990年以降の為替・金利・原油・金価格データを取得してSQLiteに保存する
為替レート、経済指標、商品相場、原油、金価格の関係性を通貨ごとに整理していくには、まず過去を含めた時系列データが必要になります。
特にLTCM危機、日本の金融危機、アジア通貨危機、ITバブル、リーマンショック、コロナショックなどを比較するには、1990年代から現在までつながったデータが必要です。
今回は、FREDとStooqを使い、USDJPY、WTI、米国金利、FF金利、金価格をSQLiteへ保存するところまで試しました。
なお、この記事ではAPIキーは掲載しません。コマンド例やエラーURLにAPIキーが含まれる場合は、すべてダミー値に置き換えています。
今回の目的
今回の目的は、危機局面の比較や相関分析に使うため、1990年以降の主要時系列データをSQLiteへ保存することです。
最終的には、以下のような分析に使う想定です。
- USDJPYと米金利の関係
- WTIや金価格とドルの関係
- 1998年のLTCM期と2008年のリーマン期の比較
- 金利・為替・商品価格のローリング相関
- Neo4jによる関係性の可視化
なぜ1990年以降のデータが必要か
短期の相関だけを見るなら、直近数年のデータでも足ります。
しかし、金融危機やレジーム変化を分析するなら、過去の危機局面を含める必要があります。
例えば、以下のようなイベントを同じ時間軸で比較したいです。
- 1997年:アジア通貨危機
- 1997年〜1999年:日本の金融危機
- 1998年:LTCM危機
- 2000年:ITバブル崩壊
- 2008年:リーマンショック
- 2020年:コロナショック
- 2022年:インフレ・急速利上げ局面
このような比較には、長期で同じ形式のデータを集められるFREDが非常に使いやすいです。
最初に取得する系列
まずは、FREDから以下の系列を取得します。
| 系列ID | 内容 | 用途 |
|---|---|---|
DEXJPUS |
USD/JPY | 為替レート |
DCOILWTICO |
WTI原油 | 商品価格・インフレ要因 |
DGS10 |
米10年国債利回り | 長期金利 |
DGS2 |
米2年国債利回り | 短期金利市場 |
FEDFUNDS |
FF金利(月次) | 政策金利レジーム |
DFF |
Daily Effective Federal Funds Rate | 日次の短期金利・流動性分析 |
金価格については、FREDの金価格系列が取得できなかったため、Stooqを使う方針にしました。
SQLiteの保存テーブル
SQLiteには、まず最小構成として以下のテーブルを作ります。
CREATE TABLE IF NOT EXISTS series_observations (
series_id TEXT NOT NULL,
date TEXT NOT NULL,
value REAL,
PRIMARY KEY (series_id, date)
);
series_id と date を主キーにすることで、同じ系列・同じ日付のデータは重複しないようにします。
FRED APIキーの扱い
FRED APIを使うにはAPIキーが必要です。
APIキーはコードに直接書かず、環境変数から読み込みます。
export FRED_API_KEY="YOUR_FRED_API_KEY_HERE"
ここで注意点があります。
コピー&ペースト時に、半角のダブルクォートではなく全角の引用符が入ると、シェルが入力待ち状態になることがあります。
例えば、以下のような状態です。
dquote>
この場合は Ctrl + C で中断し、半角の " で入力し直します。
FREDからSQLiteへ保存するスクリプト
まず、FREDからデータを取得してSQLiteへ保存するスクリプトを作成します。
vim fetch_fred_to_sqlite.py
基本形は以下です。
import os
import sqlite3
import requests
FRED_API_KEY = os.environ["FRED_API_KEY"]
BASE = "https://api.stlouisfed.org/fred/series/observations"
SERIES = [
"DEXJPUS", # USD/JPY
"DCOILWTICO", # WTI
"DGS10", # US 10Y
"DGS2", # US 2Y
"FEDFUNDS", # Fed Funds Monthly
"DFF", # Daily Effective Federal Funds Rate
]
DB_PATH = "market.db"
def init_db(conn: sqlite3.Connection) -> None:
conn.execute("""
CREATE TABLE IF NOT EXISTS series_observations (
series_id TEXT NOT NULL,
date TEXT NOT NULL,
value REAL,
PRIMARY KEY (series_id, date)
);
""")
conn.commit()
def fetch_fred_series(series_id: str, observation_start: str = "1990-01-01"):
params = {
"series_id": series_id,
"api_key": FRED_API_KEY,
"file_type": "json",
"observation_start": observation_start,
}
r = requests.get(BASE, params=params, timeout=30)
r.raise_for_status()
data = r.json()
return data["observations"]
def upsert_observations(conn: sqlite3.Connection, series_id: str, observations) -> int:
rows = []
for o in observations:
d = o["date"]
v = o["value"]
if v == ".":
val = None
else:
try:
val = float(v)
except ValueError:
val = None
rows.append((series_id, d, val))
cur = conn.executemany("""
INSERT OR REPLACE INTO series_observations(series_id, date, value)
VALUES (?, ?, ?);
""", rows)
conn.commit()
return cur.rowcount
def main():
with sqlite3.connect(DB_PATH) as conn:
init_db(conn)
for sid in SERIES:
obs = fetch_fred_series(sid, observation_start="1990-01-01")
n = upsert_observations(conn, sid, obs)
print(f"[{sid}] upserted: {n} rows")
if __name__ == "__main__":
main()
FREDの金価格系列でエラー
最初は、金価格として以下のFRED系列を使おうとしました。
GOLDAMGBD228NLBM
しかし、実行すると以下のようなエラーになりました。
requests.exceptions.HTTPError: 400 Client Error: Bad Request
APIキーや通信が原因ではなく、FRED側で指定したseries_idが存在しない状態でした。
実際にseries APIで確認すると、以下のような応答になりました。
{"error_code":400,"error_message":"Bad Request. The series does not exist."}
つまり、GOLDAMGBD228NLBM はFRED上で取得できない系列になっていました。
金価格はStooqから取得する方針に変更
FREDで金価格系列を取得できなかったため、金価格はStooqから取得する方針にしました。
Stooqでは、日次データをCSV形式で取得できます。
https://stooq.com/q/d/l/?s=xauusd&i=d
ただし、現時点ではStooqのCSV取得でAPIキーが必要になる場合があります。
そのため、この記事のコードは「Stooqから取得したCSVをSQLiteへ保存する考え方」として残しますが、APIキーの取得方法、URLへの付与方法、環境変数での管理方法は別記事で扱う予定です。
APIキーを使う場合は、記事やGitHubに直接書かず、.env や環境変数で管理します。
Stooq APIキーの扱い
StooqのCSV取得でAPIキーが必要な場合は、以下のように環境変数へ設定して使います。
export STOOQ_API_KEY="YOUR_STOOQ_API_KEY_HERE"
APIキーはコードやブログ記事に直接書かず、環境変数や .env で管理します。
この記事では、APIキーの取得手順そのものは扱いません。後日、StooqのAPIキー取得とCSV取得の手順を別記事としてまとめる予定です。
Stooqから金価格を取得する関数
以下は、Stooqから日次CSVを取得し、終値を取り出す基本形です。
現時点では、StooqのCSV取得にAPIキーが必要になる場合があります。その場合は、URLに apikey を付ける必要があります。APIキー取得と設定方法は別記事でまとめます。
import csv
import io
import os
import requests
def fetch_stooq_daily(symbol: str):
# symbol例: "xauusd"
# 現在はapikeyが必要になる場合があるため、環境変数から読む形にしておく
apikey = os.environ.get("STOOQ_API_KEY")
url = f"https://stooq.com/q/d/l/?s={symbol}&i=d"
if apikey:
url += f"&apikey={apikey}"
r = requests.get(
url,
headers={"User-Agent": "Mozilla/5.0"},
timeout=30
)
r.raise_for_status()
# CSV: Date,Open,High,Low,Close,Volume
f = io.StringIO(r.text)
reader = csv.DictReader(f)
out = []
for row in reader:
d = row["Date"]
close = row.get("Close")
if close in (None, "", "null"):
val = None
else:
try:
val = float(close)
except ValueError:
val = None
out.append((d, val))
return out
def upsert_simple_series(conn: sqlite3.Connection, series_id: str, date_value_list) -> int:
rows = [(series_id, d, v) for d, v in date_value_list]
cur = conn.executemany("""
INSERT OR REPLACE INTO series_observations(series_id, date, value)
VALUES (?, ?, ?);
""", rows)
conn.commit()
return cur.rowcount
main() のFRED取得後に、以下を追加します。
gold = fetch_stooq_daily("xauusd")
n = upsert_simple_series(conn, "XAUUSD_STOOQ", gold)
print(f"[XAUUSD_STOOQ] upserted: {n} rows (Gold from Stooq)")
これで、FREDから取る系列とStooqから取る金価格を、同じテーブルに保存できます。
なお、StooqのAPIキーが必要な環境では、事前に STOOQ_API_KEY を設定しておきます。
取得結果
FREDから取得できる系列をSQLiteへ保存し、金価格についてはStooqから取得したCSVを同じテーブルへ保存する構成にしました。
python fetch_fred_to_sqlite.py
実行結果は以下です。
[DEXJPUS] upserted: 9420 rows [DCOILWTICO] upserted: 9421 rows [DGS10] upserted: 9423 rows [DGS2] upserted: 9423 rows [FEDFUNDS] upserted: 433 rows [DFF] upserted: 13191 rows [XAUUSD_STOOQ] upserted: 15181 rows (Gold from Stooq)
ただし、Stooqからの取得は現時点ではAPIキーが必要になる場合があります。そのため、StooqのAPIキー取得・設定方法については別記事で詳しくまとめます。
SQLiteで保存件数を確認する
SQLiteで系列ごとの件数を確認します。
sqlite3 market.db 'select series_id, count(*) from series_observations group by series_id order by series_id;'
結果は以下です。
DCOILWTICO|9421 DEXJPUS|9420 DFF|13191 DGS10|9423 DGS2|9423 FEDFUNDS|433 XAUUSD_STOOQ|15181
XAUUSD_STOOQ が入っているので、金価格も保存できています。
ただし、StooqのAPIキーが必要な場合、APIキー未設定のままだと取得に失敗する可能性があります。
FEDFUNDSの件数が少ない理由
FEDFUNDS は433行しかありません。
最初は少ないように見えますが、これは月次データなので正常です。
期間を確認します。
sqlite3 market.db " SELECT MIN(date), MAX(date), COUNT(*) FROM series_observations WHERE series_id='FEDFUNDS';"
結果は以下です。
1990-01-01|2026-01-01|433
1990年から2026年まで、月1レコードで入っているため、件数としては自然です。
DFFを追加した理由
相関分析や危機時の流動性を見るには、月次の FEDFUNDS だけでは粗すぎます。
そこで、日次のFF金利である DFF を追加しました。
DFFの期間を確認します。
sqlite3 market.db " SELECT MIN(date), MAX(date), COUNT(*) FROM series_observations WHERE series_id='DFF';"
結果は以下です。
1990-01-01|2026-02-11|13191
1990年以降の日次データとして、ほぼ全期間取得できています。
FEDFUNDSとDFFの使い分け
| 系列 | 頻度 | 用途 |
|---|---|---|
FEDFUNDS |
月次 | 政策金利レジームの確認 |
DFF |
日次 | 短期金利・流動性・危機局面分析 |
DGS2 |
日次 | 短期金利市場 |
DGS10 |
日次 | 長期金利市場 |
危機比較や相関分析では、主に DFF、DGS2、DGS10 を使い、FEDFUNDS は政策レジーム確認用として扱うのがよさそうです。
日付整合はまだDBに書き込まない
系列ごとに休日やデータ頻度が違うため、相関計算前に日付を揃える必要があります。
ただし、現時点では元データを加工してDB上で無理に揃える必要はありません。
理由は、価格データ、金利データ、月次指標で扱いが違うからです。
- 価格データは基本的に補完しない
- 金利データは必要に応じて前方補完してもよい
- 月次データはレジーム判定やイベント系列として扱う
したがって、元データはそのまま保存し、相関計算時にPandasやSQLで日付を揃える方針にします。
相関計算前にreturnsを作る
価格や金利の相関を計算する場合、価格水準そのものではなく、リターンや変化量を使う方が安全です。
価格そのものの相関を見ると、トレンドによる偽相関が出やすくなります。
そのため、次にやるべきことは returns テーブルの生成です。
流れとしては以下です。
- 各系列ごとに日付順で値を並べる
- 前日との差分または対数リターンを計算する
series_returnsのようなテーブルへ保存する- 相関計算時に系列同士を日付でinner joinする
今回の到達点
今回の作業で、以下まで完了しました。
- FREDからUSDJPY、WTI、米10年金利、米2年金利、FEDFUNDSを取得した
- FREDの金価格系列が取得できないことを確認した
- 金価格はStooqのXAUUSD日次CSVから取得する方針にした
- StooqのCSV取得はAPIキーが必要になる場合があるため、APIキー対応できる形のコードにした
- 日次FF金利として
DFFを追加した - すべて
market.dbのseries_observationsに保存した - FEDFUNDSが月次、DFFが日次であることを確認した
- 次はreturns生成に進む方針を決めた
ハマりどころ
FREDのseries_idは存在確認が必要
FREDで以前使われていたseries_idが、現在も使えるとは限りません。
存在しないseries_idを指定すると、以下のようなエラーになります。
Bad Request. The series does not exist.
series APIで存在確認するか、候補を複数持っておくと安全です。
APIキーをログに残さない
FRED APIのエラーURLには、APIキーが含まれる場合があります。
ブログやGitHubにログを貼るときは、必ず以下のようにダミー化します。
api_key=YOUR_FRED_API_KEY_HERE
StooqのAPIキーについても同様です。
STOOQ_API_KEY=YOUR_STOOQ_API_KEY_HERE
金価格はFREDだけに依存しない
今回、FREDの金価格系列は取得できませんでした。
そのため、金価格はStooqなど別ソースも使えるようにしておく方がよさそうです。
ただし、StooqのCSV取得はAPIキーが必要になる場合があるため、APIキーの取得・設定・安全な管理方法は別記事に分けて整理します。
FEDFUNDSは月次データ
FEDFUNDS は月次データです。
日次の短期金利分析には、DFF を使う方が適しています。
次にやること
次は、SQLiteに保存した時系列からreturnsを生成します。
series_returnsテーブルを作成する- 価格系は対数リターンを計算する
- 金利系は差分を計算する
- 1998年のLTCM期を切り出して相関を見る
- その後、2008年リーマン期と比較する
まずは元データを壊さず、returnsや相関は別テーブルとして作成していきます。
StooqのAPIキー取得については別記事にする
この記事では、Stooqから金価格CSVを取得してSQLiteに保存する考え方とコードだけを残しました。
現時点では、StooqのCSV取得でAPIキーが必要になる場合があります。
そのため、以下は別記事で扱う予定です。
- StooqのAPIキー取得方法
- CSVダウンロードURLへの
apikey付与方法 STOOQ_API_KEYを環境変数で管理する方法.envから読み込む方法- APIキーをログやGitHubに残さない運用
コード自体は、APIキー対応すれば今後も役立つ可能性があるため、この記事にも基本形として残しておきます。
まとめ
今回は、FREDとStooqを使って1990年以降のUSDJPY、WTI、米国金利、FF金利、金価格をSQLiteへ保存しました。
FREDの金価格系列は取得できなかったため、金価格はStooqのXAUUSD日次CSVから取得する構成にしました。
ただし、現時点ではStooqのCSV取得にAPIキーが必要になる場合があります。この記事では取得コードの考え方を残し、APIキーの取得・設定・安全な管理方法は別記事で扱う予定です。
また、月次の FEDFUNDS だけでなく、日次の DFF も追加することで、短期金利や流動性の変化をより細かく見られるようになりました。
次は、これらの時系列からreturnsを生成し、LTCM期やリーマン期の相関構造を比較できるようにしていきます。

コメント