0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python3ではじめるシステムトレード:yfinance復活 米株も日本株も無料でダウンロード

0
Last updated at Posted at 2026-08-11

yfinance が YFRateLimitError で動かないときの確認手順と、その背景

yfinanceを使って、米株を無料でダウンロードしていた人は、多いのではないでしょうか?そして、ある日突然、動いていたコードがエラーメッセージを表示し、ダウンロードできなくなった。

YFRateLimitError: Too Many Requests. Rate limited. Try after a while.

「リクエストの出しすぎ」というメッセージだが、1回目の呼び出しで出ている場合、原因はたいてい別のところにある。本記事では復旧手順を先に示し、その後でなぜこうなるのかを説明する。


まずバージョンの確認

import yfinance as yf
print(yf.__version__)

0.2.x が表示されたら、それが原因である可能性が高い。理由は後述するが、0.2.54 以前は Yahoo 側の遮断に対応できていない

トレースバックからも判別できる。以下のように proxy 引数がシグネチャに並んでいたら旧バージョンである。

self._get_ticker_tz(self.proxy, timeout=10)
cache_get(self, url, user_agent_headers=None, params=None, proxy=None, timeout=30)

1.0 以降は proxy 設定が yf.config に移行しているため、この形は残っていない。


アップデートする

pip install -U yfinance curl_cffi

Jupyter / JupyterLab を使っている場合、ここで2つの罠がある

罠1: カーネルを再起動していない

pip install を実行しても、すでに import 済みのモジュールは差し替わらない。Kernel → Restart を必ず実行する。これを忘れると、更新したのにバージョンが変わらないという現象が起きる。

罠2: pip の入れ先がカーネルと違う

ノートブックのカーネルと pip が別の環境を指していることがある。ノートブック内から実行するなら、確実に同じ環境へ入れるためこう書く。

import sys
!{sys.executable} -m pip install -U yfinance curl_cffi

再起動後、バージョンを再確認する。

import yfinance as yf
print(yf.__version__)   # 1.5.2 など

まだ古いままなら、パスのズレを確認する。

import sys, yfinance
print(sys.executable)
print(yfinance.__file__)

動作確認

最小限のコードで通ることを確かめる。

import yfinance as yf

dat = yf.Ticker("AAPL")
df = dat.history(period="1mo", auto_adjust=True)
print(df.tail())
                                 Open        High         Low       Close  \
Date                                                                        
2026-08-05 00:00:00-04:00  309.093424  311.441405  305.406631  310.732025   
2026-08-06 00:00:00-04:00  314.069138  316.017470  308.963556  312.140808   
2026-08-07 00:00:00-04:00  311.181643  314.538733  310.472233  313.059998   
2026-08-10 00:00:00-04:00  306.829987  308.260010  304.609985  308.260010   
2026-08-11 00:00:00-04:00  307.750000  309.970001  305.670013  306.470001   

                             Volume  Dividends  Stock Splits  
Date                                                          
2026-08-05 00:00:00-04:00  49438800       0.00           0.0  
2026-08-06 00:00:00-04:00  46139900       0.00           0.0  
2026-08-07 00:00:00-04:00  34437200       0.00           0.0  
2026-08-10 00:00:00-04:00  44775100       0.27           0.0  
2026-08-11 00:00:00-04:00   8201653       0.00           0.0  

auto_adjust は明示する。デフォルト値が過去に変更されており、指定しないと配当・分割調整の有無が環境依存になる。リターン計算をするなら死活問題になる。

日本株なら証券コードに .T を付ける。

dat=yf.Ticker("7203.T").history(period="1mo", auto_adjust=True)   # トヨタ
dat.tail()
	Open	High	Low	Close	Volume	Dividends	Stock Splits
Date							
2026-08-04 00:00:00+09:00	2948.0	3028.0	2871.0	2918.5	70011500	0.0	0.0
2026-08-05 00:00:00+09:00	2903.0	2924.5	2869.5	2914.5	48003400	0.0	0.0
2026-08-06 00:00:00+09:00	2930.0	2993.5	2918.0	2983.5	31969900	0.0	0.0
2026-08-07 00:00:00+09:00	2995.0	3022.0	2964.5	2980.0	24563900	0.0	0.0
2026-08-10 00:00:00+09:00	2989.5	2989.5	2939.5	2981.0	23489400	0.0	0.0
dat=yf.download("USDJPY=X", period="1y")                          # ドル円
[*********************100%***********************]  1 of 1 completed
Price	Close	High	Low	Open	Volume
Ticker	USDJPY=X	USDJPY=X	USDJPY=X	USDJPY=X	USDJPY=X
Date					
2026-08-05	157.692001	157.863007	157.302002	157.735001	0
2026-08-06	157.600006	158.479996	157.598999	157.664001	0
2026-08-07	158.408997	158.561005	156.830994	158.447998	0
2026-08-10	157.891006	159.046005	157.848999	157.899994	0
2026-08-11	159.227997	159.389999	158.919998	159.222000	0

それでも 429 が出る場合

cookie キャッシュを消す

古い cookie が残っていると復旧を妨げる。

  • Windows: %LOCALAPPDATA%\py-yfinance
  • macOS / Linux: ~/.cache/py-yfinance

ブラウザ偽装セッションを明示的に渡す

import yfinance as yf
from curl_cffi import requests

session = requests.Session(impersonate="chrome")
dat = yf.Ticker("AAPL", session=session)
print(dat.history(period="1mo"))

IP 単位で遮断されている場合は待つ

上記すべてで駄目なら、IP レベルでブロックされている可能性がある。過去の報告では数時間〜24時間以上続いた例もある。テザリングなど別回線で試すと切り分けられる。

大量取得は最初から前提を変える

銘柄ごとにループで Ticker を作るのは最も 429 を踏みやすい。まとめて取得し、待機を挟む。

import time

for chunk in chunks(tickers, 50):
    df = yf.download(chunk, start=start, auto_adjust=True)
    save(df)
    time.sleep(2)

ここから背景: なぜ「使ってもいないのに」429 なのか

以下は動作復旧には不要だが、同じ問題を繰り返さないために知っておく価値がある。

yfinance は公式ライブラリではない

Ran Aroussi 氏によるオープンソースプロジェクトで、Yahoo, Inc. とは無関係である。README にも明記されている。

yfinance is not affiliated, endorsed, or vetted by Yahoo, Inc.

さらに重要なのは、Yahoo Finance の公式 API は2017年に提供終了しているという点だ。現在あるのは Yahoo Finance のサイト自身が内部で使うエンドポイントだけで、yfinance はそこを叩いている。契約に基づく安定供給ではなく、相手の都合でいつでも変わるインターフェースに追随し続けているプロジェクトである。

これが「定期的に壊れる」ことの構造的な理由になっている。

壊れ方の変遷

CHANGELOG を辿ると、時代ごとに攻防のテーマが変わってきたことが分かる。

2017年 — Selenium からの脱却

初期はブラウザ自動化ツールだった。0.0.2 に「ChromeDriver の場所を指定するオプション」があり、0.0.5 で「Selenium・PyVirtualDisplay・ChromeDriver 不要になった」と記録されている。0.1.36fix-yahoo-finance から yfinance に改名された。

2022〜2023年 — 暗号化との消耗戦

Yahoo がページ埋め込みの JSON を暗号化した。CHANGELOG の並びがそのまま疲弊の記録になっている。

0.2.4   Fix Yahoo data decryption
0.2.5   Fix Yahoo data decryption again
0.2.7   Fix Yahoo decryption, smarter this time
0.2.10  add another backup decrypt option

決着は 0.2.14。HTML の復号をやめ、API エンドポイントから直接取得する方式へ移行した。以降この種の問題は再発していない。

2023年 — cookie と crumb

Yahoo が CSRF トークン(crumb)を導入。0.2.32 で cookie & crumb 対応が入った。今でもデバッグログを有効にするとこの手順が見える。

2025年 — TLS フィンガープリントによる遮断(今回の原因)

2025年2月、Yahoo Finance がサイトを刷新し、新しいクォータ制を導入した。4月には YFRateLimitError の報告が GitHub に殺到する。特徴的だったのは、報告者が口を揃えて**「IP を変えても直らない」**と述べていた点だ。

正体は JA3 フィンガープリントによる識別だった。TLS ハンドシェイク時に送られる暗号スイートの組み合わせや拡張の順序は、クライアント実装ごとに固有のパターンを持つ。Python の requests(OpenSSL ベース)と実際の Chrome では、これが明確に異なる。User-Agent を偽装しても無意味である。ヘッダより下の層で判別されているからだ。

対策として採用されたのが curl_cffi で、これは実際のブラウザの TLS ライブラリ(Chrome なら BoringSSL)を使い、フィンガープリントごと模倣する。

この時期の CHANGELOG は正直だ。

0.2.58  Fix false rate-limit problem
0.2.59  Fix the fix for rate-limit
0.2.60  Fix cookie reuse, DNS blocking fc.yahoo.com への対応
0.2.62  crumb取得時のレート制限検出 / curl_cffi への置き換え

"Fix the fix for rate-limit" ——修正の修正——という一行が、当時の状況を何より雄弁に表している。

0.2.54 が最悪の位置にある理由

冒頭で「0.2.54 以前は対応できていない」と書いた根拠がここにある。

0.2.54 は 2025年2月、つまりサイト刷新の直後に出たリリースである。上に並べた対策(0.2.580.2.62)はすべてその後に入った。TLS フィンガープリント対策も、cookie 再利用の修正も、crumb 取得段階のハンドリングも含まれていない。

だから 0.2.54 は、Yahoo の遮断に対して丸腰である。一発目のリクエストで 429 が返るのは「使いすぎ」ではなく、リクエストの出し方そのものが「ブラウザではない」と判定されているためだ。

エラーメッセージが Too Many Requests なので使用量の問題と誤解しやすいが、実態は識別による遮断である。ここが今回の要点になる。

その後の経緯

  • 2025年12月 バージョン 1.0 到達。リリースノートいわく「yfinance been stable a long time now, time to grow up」
  • 2026年4月 CVE 対応で curl_cffi>=0.15 を強制
  • 2026年5月 1.4.0 で curl_cffi をオプション化、requests へのフォールバックを追加
  • 2026年7月23日 1.5.2 で「curl_cffi 0.16 以降での破損」を修正

最後の 1.5.2 は興味深い。原因が Yahoo 側ではなく依存パッケージのバージョンアップだった。レート制限を回避するために導入した curl_cffi が、今度はそれ自体が破損要因になったという皮肉な展開である。


実務上の指針

バージョンを固定する。ただし依存も含めて。

yfinance==1.5.2
curl_cffi>=0.15,<0.16

1.5.2 の件が示す通り、本体だけ固定しても不十分である。pip freezeuv lock で環境全体を固定するのが確実だ。

取得したデータは必ずローカルに保存する。

from pathlib import Path
import pandas as pd, yfinance as yf

CACHE = Path("data/prices.parquet")
if CACHE.exists():
    df = pd.read_parquet(CACHE)
else:
    df = yf.download(tickers, start="2015-01-01", auto_adjust=True)
    CACHE.parent.mkdir(exist_ok=True)
    df.to_parquet(CACHE)

Yahoo は過去データを遡って修正することがある。取得と分析を分離しておかないと、再現性が保てない。

構造的な制約は把握しておく。

  • 分足は 1m で7日、それ以外でも60日程度しか遡れない
  • 上場廃止銘柄が取得できないため、現在の構成銘柄で過去を分析すると生存バイアスが入る
  • 利用規約上は個人利用が前提。商用利用はグレー

マクロ指標や金利なら FRED(fredapi)や日銀の時系列統計、有料でよければ Polygon.io、Tiingo、EOD Historical Data という選択肢がある。無料の代替では Stooq も使える。


まとめ

  • YFRateLimitError1回目の呼び出しで出るなら、使いすぎではなくバージョンが古い可能性が高い
  • Jupyter では pip install 後のカーネル再起動を忘れない
  • 0.2.5x 系は2025年春の遮断に対応していない。1.x 系に上げる
  • このライブラリは公式 API ではなく、壊れることを前提に設計するのが正しい付き合い方

参考

※2026年8月時点の情報です。

参考

Python3ではじめるシステムトレード【第2版】環境構築と売買戦略

「画像をクリックしていただくとpanrollingのホームページから書籍を購入していただけます。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?