一番雑な投げ方
まず、yfinanceでハマりやすいポイントを回避しつつ、あるティッカーの過去データを取ってくるなら、これで十分だよ。
import yfinance as yf
import pandas as pd
import requests
# キャッシュ回避のためにセッションを作る
session = requests.Session()
ticker_symbol = "MSFT" # ここは適当なティッカーに置き換えて
try:
data = yf.Ticker(ticker_symbol.upper().strip(), session=session).history(
period="1y", interval="1d", actions=False, auto_adjust=True, prepost=False
)
# MultiIndex平坦化
data.columns = ['_'.join(col).strip() if isinstance(col, tuple) else col for col in data.columns.values]
print(data.head())
except Exception as e:
print(f"データ取得中にエラーが発生したよ: {e}")
finally:
session.close() # セッションを閉じるのを忘れずに
これだけで、yfinance特有のいくつかの厄介な挙動(キャッシュ、ティッカーのブレ、MultiIndex)をある程度回避した状態でデータが手に入るはず。
もうちょい具体的に投げるパターン
特定の状況に合わせて、もうちょっとだけ手を加えるパターンも紹介するね。
1. キャッシュを完全に無視して最新データを強制取得する
yfinanceは内部でrequestsライブラリを使ってて、セッションをキャッシュすることがあるんだ。特にスクリプトを何度も実行したり、同じティッカーを短い時間で何度もリクエストしたりすると、古いデータが返ってくることがあるよ。これを強制的に避けるには、セッションオブジェクトを毎回作り直すのが手っ取り早い。
import yfinance as yf
import pandas as pd
import requests
ticker_symbol = "AAPL"
session = requests.Session() # 新しいセッションを作成
try:
data = yf.Ticker(ticker_symbol, session=session).history(
period="1mo", interval="1h", actions=False, auto_adjust=True, prepost=False
)
# MultiIndex平坦化
data.columns = ['_'.join(col).strip() if isinstance(col, tuple) else col for col in data.columns.values]
print(data.tail())
finally:
session.close() # セッションを閉じるのを忘れずに
2. ティッカーシンボルがブレても頑丈にする
「MSFT」と「msft」とか、大文字小文字が違うだけでエラーになることもある。あと、上場廃止とかでシンボルが変わることもあるから、簡単な正規化処理を挟むと安心だよ。
import yfinance as yf
import pandas as pd
def normalize_ticker(ticker):
# シンプルな例。実際にはもっと複雑な正規化が必要な場合もあるかも
return ticker.upper().strip()
raw_ticker = " goog " # 小文字だったり空白があったりする想定
normalized_ticker = normalize_ticker(raw_ticker)
data = yf.Ticker(normalized_ticker).history(
period="6mo", interval="1d", actions=False, auto_adjust=True, prepost=False
)
# MultiIndex平坦化
data.columns = ['_'.join(col).strip() if isinstance(col, tuple) else col for col in data.columns.values]
print(data.head())
3. テクニカル指標をバッファ込みで取得する
移動平均線とかRSIとか、過去データに基づいて計算する指標は、計算に必要な期間分の「バッファ」が必要になるよね。yfinanceは指定期間ピッタリのデータを返すから、自分でバッファを見越して少し長めにデータを取得してから、必要な期間に切り出すのが常套手段だよ。
import yfinance as yf
import pandas as pd
from datetime import datetime, timedelta
ticker_symbol = "TSLA"
buffer_days = 30 # 20日移動平均なら、20日以上のバッファが必要
analysis_period_days = 365 # 1年間のデータを分析したい場合
# バッファを見越して長めにデータ取得するための開始日を計算
end_date = datetime.now()
start_date_with_buffer = end_date - timedelta(days=analysis_period_days + buffer_days)
data = yf.Ticker(ticker_symbol).history(
start=start_date_with_buffer.strftime('%Y-%m-%d'),
end=end_date.strftime('%Y-%m-%d'),
interval="1d", actions=False, auto_adjust=True, prepost=False
)
# MultiIndex平坦化
data.columns = ['_'.join(col).strip() if isinstance(col, tuple) else col for col in data.columns.values]
# ここでテクニカル指標を計算
data['SMA_20'] = data['Close'].rolling(window=20).mean()
# 分析に必要な期間に切り出す
data_for_analysis = data[data.index >= (end_date - timedelta(days=analysis_period_days))]
print(data_for_analysis[['Close', 'SMA_20']].tail())
実践例 / 実録
俺たちの投資サーベイアプリでは、定期的に40銘柄のモメンタムやセンチメントを計算してるんだ。この時、yfinanceで過去の株価データを引っ張ってくるんだけど、結構ハマりどころがあったよ。
例えば、
-
「あれ?昨日と同じデータが返ってくるんだけど」
- 定期実行してるバッチ処理で、同じ日のうちに何度も同じティッカーのデータを取ろうとすると、たまに古いキャッシュが返ってくることがあった。俺はこれで「セッション作り直し」パターンを採用したんだ。
requests.Session()を毎回生成してyf.Ticker(..., session=session)って渡すようにしたら、バッチ処理で常に最新データが取れるようになったよ。処理が終わったらちゃんとsession.close()も忘れずに入れるようにしたんだ。
- 定期実行してるバッチ処理で、同じ日のうちに何度も同じティッカーのデータを取ろうとすると、たまに古いキャッシュが返ってくることがあった。俺はこれで「セッション作り直し」パターンを採用したんだ。
-
「GAFAMなのにデータ取れない!?何で!?」
- ある日、
GOOGじゃなくてGOOGLを使ってることを忘れがちだったり、ティッカー入力ミスで微妙に違ってたりしてデータ取得に失敗することがあったんだ。これを解消するために、俺たちは内部的にティッカーシンボルをupper()してstrip()するシンプルな正規化処理を挟むようにした。これで入力ミスに強くなったし、もしティッカーが変更されても、すぐにその変更をキャッチして正規化ルールを更新すれば対応できるようになったよ。
- ある日、
-
「OpenとCloseはいいんだけど、なんでカラム名がタプルになってんの?」
yf.history()でデータ取ってくると、('Close', '')みたいなMultiIndexのカラム名になることがあって、これだとPandasで扱いづらいんだ。DataFrameをJSONに変換してAPIで返すときとか、フロントエンドで利用するときに困る。だから、data.columns = ['_'.join(col).strip() if isinstance(col, tuple) else col for col in data.columns.values]って一行挟んで、CloseとかOpenみたいに平坦化するようにしてる。これで、バックエンドからフロントエンドまでデータの扱いが一貫するようになったね。
-
「移動平均線がおかしい。最初の何日かNaNだらけなんだけど!」
- 20日移動平均線を計算したいのに、
period="1mo"とかで1ヶ月分のデータしか取ってこないと、最初の20日分の移動平均は計算できないからNaNになっちゃうよね。俺たちは、分析に必要な期間よりも少し長めに(例えば30日分とか)データを取得するようにした。例えば1年分のデータが欲しかったら、start_dateを1年と30日前に設定してデータを取得する。そして、計算したい指標を全部計算してから、最後の1年分のデータだけを切り出して使うようにしてる。これで、計算の最初から正しい指標が利用できるようになったよ。
- 20日移動平均線を計算したいのに、
こんな感じで、yfinanceは便利だけど、ちょっとした「お作法」を知らないと足元をすくわれるから気をつけてね。
つまずきポイント
ここまで話したけど、特にハマりやすいのはこの2点だよ。
- キャッシュ問題: スクリプトを連続実行するときや、定期バッチで一番悩まされるのがこれ。
requests.Session()を毎回使って、確実に閉じる(finally句でsession.close()するとか)のが確実な対策になるよ。 - MultiIndex: Pandasを使い慣れてないと「なんでこんなカラム名なんだ?」ってなるポイント。PythonやPandasのバージョン、あるいは
yfinance自体の更新で挙動が変わる可能性もあるから、常にフラットなカラム名に変換する習慣をつけておくと後で困らないはず。
これらのパターンを覚えておけば、yfinanceとの付き合いもグッとラクになるはずだよ。