※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。
「AI投資に興味はあるけれど、どの銘柄を選べばいいのかわからない」「感情に左右されず、データに基づいた銘柄選定をしたい」——こうした課題を抱える個人投資家は年々増えています。SNSや掲示板の情報に振り回されて売買を繰り返し、結局パフォーマンスが振るわないという経験は、多くの方に心当たりがあるのではないでしょうか。
この記事では、Pythonと無料ライブラリ「yfinance」を使い、AI投資における銘柄選定プロセスを自動化する方法を解説します。財務データ・テクニカル指標・出来高といった複数の基準をプログラムで一括スクリーニングし、人間の感情バイアスを完全に排除した「システム的な銘柄選び」を実現するための具体的なコードと考え方を提供します。
AI投資における銘柄選定の基本的な考え方
コードを書く前に、AI投資で銘柄を選ぶ際の基本フレームワークを理解しておく必要があります。
AI投資とは何か
AI投資とは、人工知能やプログラムを活用して、投資判断の一部または全部を自動化・効率化する投資手法の総称です。
具体的には以下のようなアプローチが含まれます。
- 大量の株価データから統計的なパターンを抽出する
- 財務指標に基づいて有望銘柄を自動スクリーニングする
- 機械学習モデルで株価の方向性を予測する
- テクニカル指標を組み合わせた売買シグナルを自動生成する
本記事では、これらのうち最も実用的かつ初心者に取り組みやすい「自動スクリーニング」に焦点を当てます。
人間の銘柄選定が失敗しやすい3つの理由
手動での銘柄選定には、構造的な弱点が存在します。
| 失敗要因 | 具体例 | AI投資での解決策 |
|---|---|---|
| 感情バイアス | 話題の銘柄に飛びつく、損切りできない | ルールベースで機械的に判断 |
| 情報処理の限界 | 数千銘柄を毎日チェックできない | プログラムで一括スクリーニング |
| 一貫性の欠如 | 日によって判断基準がブレる | 同一基準を毎回適用 |
AI投資の本質は「予測精度を上げること」ではなく、「判断プロセスから人間の弱点を排除すること」にあります。完璧な予測は不可能でも、一貫した基準で銘柄を選び続けることで、長期的なパフォーマンスの安定が期待できます。
銘柄選定で使う3つのデータカテゴリ
プログラムで銘柄を選定する際に使用するデータは、大きく3つのカテゴリに分類されます。
- ファンダメンタルデータ: PER、PBR、配当利回り、売上成長率など企業の財務情報
- テクニカルデータ: 移動平均線、RSI、ボリンジャーバンドなど株価の統計指標
- マーケットデータ: 出来高、時価総額、セクター情報など市場全体の文脈情報
これら3つを組み合わせることで、多角的かつ客観的な銘柄スクリーニングが可能になります。
yfinanceで取得できる銘柄選定用データ
yfinanceは株価だけでなく、銘柄選定に必要なさまざまなデータを無料で取得できます。
ファンダメンタルデータの取得項目
yfinanceの `info` プロパティからは、以下のような財務指標を取得できます。
| 指標名 | yfinanceのキー | 意味 |
|---|---|---|
| PER(株価収益率) | `trailingPE` | 株価が利益の何倍かを示す |
| PBR(株価純資産倍率) | `priceToBook` | 株価が純資産の何倍かを示す |
| 配当利回り | `dividendYield` | 株価に対する年間配当の割合 |
| 時価総額 | `marketCap` | 企業の市場価値 |
| 売上高 | `totalRevenue` | 企業の年間売上 |
| 利益率 | `profitMargins` | 売上に対する利益の割合 |
テクニカルデータの算出方法
テクニカル指標はyfinanceから直接取得するのではなく、取得した株価データをもとにPythonで計算します。
- 移動平均線(SMA): `df[“Close”].rolling(window=25).mean()`
- RSI(相対力指数): 上昇幅と下落幅の比率から算出
- ボリンジャーバンド: 移動平均 ± 標準偏差 × 2
データ取得時の注意点
yfinanceで日本株のデータを取得する際には、いくつかの制約を理解しておく必要があります。
- 日本株の `info` データは一部欠損していることがある(特にPERやPBR)
- リアルタイムデータではなく、15〜20分程度の遅延がある
- 一度に大量のリクエストを送ると、一時的にアクセスが制限される場合がある
データの欠損は「エラー」ではなく「仕様」として想定し、コード内で適切にハンドリングすることが重要です。
【コピペOK】銘柄スクリーニングの自動化コード
ここからは実際にPythonコードを使い、複数銘柄を一括でスクリーニングする仕組みを構築します。
【コピペOK】ファンダメンタル指標による一括スクリーニング
以下のコードは、あらかじめ指定した銘柄リストに対してPER・PBR・配当利回り・時価総額を一括取得し、条件に合致する銘柄だけを抽出するものです。
import yfinance as yf
import pandas as pd
import time
# ==============================
# 設定エリア
# ==============================
# スクリーニング対象銘柄(東証コード + ".T")
SYMBOLS = [
"7203.T", # トヨタ自動車
"6758.T", # ソニーグループ
"9984.T", # ソフトバンクグループ
"8306.T", # 三菱UFJフィナンシャル
"6861.T", # キーエンス
"9433.T", # KDDI
"4502.T", # 武田薬品工業
"6501.T", # 日立製作所
"7974.T", # 任天堂
"8058.T", # 三菱商事
]
# フィルタ条件
MAX_PER = 20.0 # PER上限
MAX_PBR = 3.0 # PBR上限
MIN_DIVIDEND = 0.02 # 配当利回り下限(2%)
MIN_MARKET_CAP = 1e12 # 時価総額下限(1兆円)
# ==============================
# スクリーニング処理
# ==============================
def fundamental_screening():
print("=== ファンダメンタル・スクリーニング開始 ===\n")
results = []
for symbol in SYMBOLS:
try:
ticker = yf.Ticker(symbol)
info = ticker.info
per = info.get("trailingPE")
pbr = info.get("priceToBook")
div_yield = info.get("dividendYield")
market_cap = info.get("marketCap")
name = info.get("shortName", symbol)
results.append({
"銘柄コード": symbol,
"企業名": name,
"PER": round(per, 2) if per else None,
"PBR": round(pbr, 2) if pbr else None,
"配当利回り": round(div_yield * 100, 2) if div_yield else None,
"時価総額(億円)": round(market_cap / 1e8) if market_cap else None,
})
print(f"[OK] {symbol} ({name})")
time.sleep(1) # アクセス制限対策
except Exception as e:
print(f"[ERROR] {symbol}: {e}")
df = pd.DataFrame(results)
print("\n■ 全銘柄データ:")
print(df.to_string(index=False))
# フィルタ適用
filtered = df.copy()
filtered = filtered[filtered["PER"].notna() & (filtered["PER"] <= MAX_PER)]
filtered = filtered[filtered["PBR"].notna() & (filtered["PBR"] <= MAX_PBR)]
filtered = filtered[filtered["配当利回り"].notna() & (filtered["配当利回り"] >= MIN_DIVIDEND * 100)]
filtered = filtered[filtered["時価総額(億円)"].notna() & (filtered["時価総額(億円)"] >= MIN_MARKET_CAP / 1e8)]
print(f"\n■ フィルタ通過銘柄(PER≤{MAX_PER}, PBR≤{MAX_PBR}, 配当≥{MIN_DIVIDEND*100}%, 時価総額≥{MIN_MARKET_CAP/1e12}兆円):")
if filtered.empty:
print(" → 該当銘柄なし。フィルタ条件を緩和してください。")
else:
print(filtered.to_string(index=False))
# CSV保存
df.to_csv("fundamental_screening.csv", index=False, encoding="utf-8-sig")
print("\n--- fundamental_screening.csv に保存完了 ---")
if __name__ == "__main__":
fundamental_screening()
【コピペOK】テクニカル指標を加えた総合スクリーニング
ファンダメンタルだけでなく、テクニカル指標(RSI・移動平均トレンド)も組み合わせることで、より精度の高い銘柄選定が可能になります。
import yfinance as yf
import pandas as pd
import time
# ==============================
# 設定エリア
# ==============================
SYMBOLS = [
"7203.T", "6758.T", "9984.T", "8306.T", "6861.T",
"9433.T", "4502.T", "6501.T", "7974.T", "8058.T",
]
RSI_PERIOD = 14
RSI_LOWER = 30 # RSIがこの値以下 → 売られすぎ(買い候補)
RSI_UPPER = 70 # RSIがこの値以上 → 買われすぎ(除外)
SMA_SHORT = 5
SMA_LONG = 25
# ==============================
# RSI計算関数
# ==============================
def calc_rsi(series, period):
delta = series.diff()
gain = delta.where(delta > 0, 0.0)
loss = -delta.where(delta < 0, 0.0)
avg_gain = gain.rolling(window=period).mean()
avg_loss = loss.rolling(window=period).mean()
rs = avg_gain / avg_loss
return 100 - (100 / (1 + rs))
# ==============================
# 総合スクリーニング
# ==============================
def technical_screening():
print("=== テクニカル・スクリーニング開始 ===\n")
results = []
for symbol in SYMBOLS:
try:
ticker = yf.Ticker(symbol)
hist = ticker.history(period="6mo")
if hist.empty or len(hist) < SMA_LONG + RSI_PERIOD:
print(f"[SKIP] {symbol}: データ不足")
continue
close = hist["Close"]
# 指標計算
sma_short = close.rolling(window=SMA_SHORT).mean().iloc[-1]
sma_long = close.rolling(window=SMA_LONG).mean().iloc[-1]
rsi = calc_rsi(close, RSI_PERIOD).iloc[-1]
latest_price = close.iloc[-1]
avg_volume = hist["Volume"].tail(20).mean()
# トレンド判定
if sma_short > sma_long:
trend = "上昇トレンド"
else:
trend = "下降トレンド"
results.append({
"銘柄コード": symbol,
"最新終値": round(latest_price, 1),
"RSI": round(rsi, 1),
"SMA5": round(sma_short, 1),
"SMA25": round(sma_long, 1),
"トレンド": trend,
"20日平均出来高": int(avg_volume),
})
print(f"[OK] {symbol}")
time.sleep(1)
except Exception as e:
print(f"[ERROR] {symbol}: {e}")
df = pd.DataFrame(results)
print("\n■ 全銘柄テクニカルデータ:")
print(df.to_string(index=False))
# 有望銘柄の抽出(上昇トレンド + RSIが買われすぎでない)
candidates = df[
(df["トレンド"] == "上昇トレンド") &
(df["RSI"] < RSI_UPPER)
]
print(f"\n■ 有望銘柄(上昇トレンド + RSI<{RSI_UPPER}):")
if candidates.empty:
print(" → 該当銘柄なし。")
else:
print(candidates.to_string(index=False))
# 売られすぎ銘柄
oversold = df[df["RSI"] <= RSI_LOWER]
print(f"\n■ 売られすぎ銘柄(RSI≤{RSI_LOWER}):")
if oversold.empty:
print(" → 該当銘柄なし。")
else:
print(oversold.to_string(index=False))
df.to_csv("technical_screening.csv", index=False, encoding="utf-8-sig")
print("\n--- technical_screening.csv に保存完了 ---")
if __name__ == "__main__":
technical_screening()
スクリーニング条件のカスタマイズ方法
上記コードの「設定エリア」にあるパラメータを変更するだけで、さまざまなスクリーニング条件を試すことができます。
| カスタマイズ項目 | 変更箇所 | 例 |
|---|---|---|
| 対象銘柄の追加 | `SYMBOLS` リスト | 日経225全銘柄を列挙 |
| PERの上限変更 | `MAX_PER` | 15に変更で割安株に絞る |
| RSIの閾値変更 | `RSI_LOWER` / `RSI_UPPER` | 25/75に変更でシグナルを厳格化 |
| 移動平均の期間変更 | `SMA_SHORT` / `SMA_LONG` | 10/50に変更で中期トレンド重視 |
スクリーニング条件は「唯一の正解」が存在するわけではありません。複数のパラメータを変えながらバックテストを繰り返し、自分の投資スタイルに合った条件を見つけていくプロセスが重要です。
AI投資の銘柄選定で意識すべき5つの基準
コードが動くようになったら、次は「どのような基準で銘柄を選ぶべきか」という戦略面の理解を深めます。
基準①:流動性(出来高)
出来高が少ない銘柄は、売りたいときに売れない「流動性リスク」を抱えています。スクリーニングの段階で、1日の平均出来高が一定以上の銘柄に絞ることが基本です。
目安として、20日平均出来高が10万株以上の銘柄を対象とするのが安全です。
基準②:バリュエーション(割安度)
PER(株価収益率)とPBR(株価純資産倍率)は、銘柄が割安かどうかを判断する最も基本的な指標です。
- PERが低い: 利益に対して株価が割安(ただし成長性が低い可能性もある)
- PBRが1倍以下: 純資産を下回る株価(解散価値割れ)
基準③:トレンドの方向性
どれほど割安な銘柄でも、株価が下落トレンドにある場合は「さらに下がる」リスクがあります。移動平均線の方向を確認し、上昇トレンドの銘柄を優先的に選定することで、リスクを低減できます。
基準④:モメンタム(勢い)
RSIやMACDなどのモメンタム指標を使い、株価の「勢い」を数値化します。上昇トレンドにあり、かつモメンタムが加速している銘柄は、短期〜中期のパフォーマンスが良い傾向にあります。
基準⑤:分散投資
1銘柄に集中投資するのは、アルゴリズム取引においても危険です。スクリーニングで抽出した複数の銘柄に分散投資することで、個別銘柄リスクを軽減できます。
理想的なスクリーニングフローは、流動性 → バリュエーション → トレンド → モメンタム → 分散 の順に絞り込んでいく形です。各段階でフィルタをかけることで、最終的に質の高い銘柄リストが残ります。
銘柄選定を「運用可能なシステム」にするためのポイント
一度スクリーニングコードを書いて終わりではなく、継続的に運用できる仕組みにすることが重要です。
定期実行の仕組み化
スクリーニングは毎日同じ時間に実行することで、市場の変化を継続的に追跡できます。
- Windows: タスクスケジューラでPythonスクリプトを定時実行
- Mac/Linux: cronジョブで定時実行
- クラウド: Google Cloud FunctionsやAWS Lambdaで自動実行
結果のログ管理
毎日のスクリーニング結果をCSVで保存し、日付をファイル名に含めることで、過去の選定結果を振り返ることができます。
from datetime import date
filename = f"screening_{date.today()}.csv"
スクリーニング結果と実際の値動きの検証
選定した銘柄が「その後どうなったか」を定期的に検証することが、スクリーニング条件の改善に直結します。1ヶ月後・3ヶ月後のリターンを追跡し、条件の精度を継続的に向上させていくサイクルが理想的です。
よくあるエラーと対処法
yfinanceの `info` でデータが取得できない
日本株の場合、`info` プロパティの一部データが `None` で返ってくることがあります。これはyfinanceの仕様上の制約であり、エラーではありません。
対策としては、コード内で `if value is not None:` のように必ずNoneチェックを入れることが重要です。
大量銘柄のスクリーニングで処理が止まる
一度に数百銘柄をリクエストすると、yfinanceのデータソース側でアクセス制限がかかることがあります。
- 各リクエストの間に `time.sleep(1)` で1秒以上の待機を入れる
- 100銘柄を超える場合は、50銘柄ずつバッチ処理にする
スクリーニング結果が毎回0件になる
フィルタ条件が厳しすぎることが原因です。まずはフィルタ条件をすべて外した状態で全データを確認し、そこから段階的に条件を追加していく方法を推奨します。
まとめ
PythonとyfinanceによるAI投資の銘柄選定について、要点を整理します。
- AI投資の銘柄選定とは、プログラムに定義した基準で自動的に有望銘柄を抽出するプロセスです
- yfinanceを使えば、ファンダメンタル・テクニカル両面のデータを無料で取得できます
- スクリーニングの基準は流動性・バリュエーション・トレンド・モメンタム・分散の5つが基本です
- コードの「設定エリア」を変更するだけで、さまざまな投資スタイルに対応できます
- 一度きりの実行ではなく、定期実行と結果検証のサイクルを回すことで精度が向上します
今回のスクリーニングコードは、AI投資の入り口にすぎません。次のステップとして、機械学習による株価予測モデルの構築や、スクリーニング結果を自動でポートフォリオに組み込むシステムへの拡張に進むことで、より本格的なAI投資環境が実現できます。

