1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

選ぶ前に捨てるんだ|Pythonにやらせる銘柄 1次スクリーニング

Posted at

雑草排除|ROE×PER×営業利益率で条件に合わない銘柄を一掃してやる

株式投資の第一関門、それは銘柄が多すぎる問題です。
東証プライム市場だけでも約2000社。すべてを精査するなんて無理。時間のムダだと思います。

「とりあえず全部見てから決めようと思ってるけど、永遠に選べない」
優柔不断な自分は、悩みまくって結局買わないことも多々。


🧹 だから最初に“捨てる”

決断はPythonに任せて、良さげな銘柄だけcsvに保存してくれると楽です。
このスクリーニングでは下記の3つの指標を基準としました。

● ROE(自己資本利益率)

会社がどれだけ効率よく利益を出しているか。
10%以上あれば優秀。

ROEが低い = 資金を持ってても活かせてない可能性あり。

● PER(株価収益率)

利益に対して株価がどれくらい割高かを見る指標。
15倍以下なら“割安”とされることが多い。

PERが高すぎる = 期待先行で中身が追いついてない可能性あり。

● 営業利益率

売上のうち、どれだけ利益が残ってるか。
10%以上あれば、しっかり稼げる体質。

利益率が低い = ビジネスの地力が弱い可能性あり。

今回は基準を3つにしました。
利益を出せていて、割安で、体質が強い企業。
それ以外は今は見ないことにします。

なお、ここで行っているのは、あくまで「一次スクリーニング」です。
就職活動で言うところの学歴フィルターのようなものです(※ちなみに自分は弾かれた側です…)。このフィルターに合致しなかったからといって、企業の価値を否定しているわけではありません。


✍️ ここから何をするか?

ここからは、この財務指標(ROE・PER・営業利益率)をもとに
東証プライム全体から"基準を満たさない銘柄"をPythonで自動排除
するコードを紹介します。

  • Google DriveからExcelで銘柄リストを読み込み
  • 東証プライムの銘柄だけを抽出
  • yfinanceから財務データ(ROE・PER・営業利益率)を取得
  • 条件を満たさない銘柄を除去
  • 結果をCSVに保存

🔧 実行環境

  • Python(3.9以上推奨)
  • Google Colab
  • Google Drive(ファイル入出力用)

📁入力ファイルについて

東証全銘柄が掲載されたExcelファイルを使います。
※データ取得元についてはご自身で最新版をご用意ください(例:JPXのサイトなど)
https://www.jpx.co.jp/markets/statistics-equities/misc/01.html
以下は、使用した銘柄リスト(Excelファイル)の一部です。
このデータから「市場・商品区分」が「プライム(内国株式)」のものだけを抽出し、スクリーニング対象としています。

日付 コード 銘柄名 市場・商品区分 33業種コード 33業種区分 17業種コード 17業種区分 規模コード 規模区分
2025/02/28 1301 極洋 プライム(内国株式) 50 水産・農林業 1 食品 7 TOPIX Small 2
2025/02/28 1305 iFreeETF TOPIX(年1回決算型) ETF・ETN - - - - - -
2025/02/28 1306 NEXT FUNDS TOPIX連動型上場投信 ETF・ETN - - - - - -
2025/02/28 1308 上場インデックスファンドTOPIX ETF・ETN - - - - - -
2025/02/28 1309 NEXT 中国株式・上証50連動型上場投信 ETF・ETN - - - - - -

サンプルコード

# 1st screening
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import yfinance as yf
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from sklearn.metrics import mean_squared_error
import os  
import time
import concurrent.futures
from google.colab import drive

# Google Drive をマウント
drive.mount('/content/drive')

# Tickerが記録されたファイルのパス
file_path = '/content/drive/MyDrive/stock_prediction/ver.1/online_data/data_j.xlsx'  # 使用する際は実際のパスに変更

# CSV読み込み
df_tickers = pd.read_excel(file_path)

# プライム市場の銘柄のみ抽出
df_tickers = df_tickers[df_tickers["市場・商品区分"] == "プライム(内国株式)"].copy()

# 銘柄コードを整数型に変換(先頭ゼロ落ち防止)
df_tickers["コード"] = df_tickers["コード"].astype(str).str.zfill(4)

# 確認
print(df_tickers.head())

# Ticker.Tの形式に変更
ticker_list = df_tickers["コード"].astype(str) + ".T"

# スクリーニング条件
roe_threshold = 10    # ROE > 10%
per_threshold = 15    # PER < 15
opm_threshold = 10    # 営業利益率 > 10%

def fetch_and_filter(ticker, index, total):
    stock = yf.Ticker(ticker)
    try:
        time.sleep(2)  # 2秒の待機(アクセス制限回避)
        info = stock.info  # 実行日の財務データを取得してます。
        roe = info.get("returnOnEquity", None) * 100 if info.get("returnOnEquity") is not None else None
        per = info.get("trailingPE", None)
        opm = info.get("operatingMargins", None) * 100 if info.get("operatingMargins") is not None else None

        if roe is None or per is None or opm is None:
            return None
            
        
        if roe > roe_threshold and per < per_threshold and opm > opm_threshold: #ここが雑草取りの本丸
            return {
                "Ticker": ticker,
                "ROE": roe,
                "PER": per,
                "営業利益率": opm
            }
    except Exception as e:
        print(f"Error processing {ticker}: {e}")
    finally:
        
        progress = (index + 1) / total * 100 # 進行状況の表示
        print(f"Processing {ticker} ({index + 1}/{total} - {progress:.2f}%)")
    return None

# 並列処理の実行(Timesleepで遅くなるのを緩和したい)
total_tickers = len(ticker_list)
filtered_stocks = []
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
    futures = [executor.submit(fetch_and_filter, ticker, i, total_tickers) for i, ticker in enumerate(ticker_list)]
    for future in concurrent.futures.as_completed(futures):
        result = future.result()
        if result:
            filtered_stocks.append(result)

# 結果をデータフレームに
df_filtered = pd.DataFrame(filtered_stocks)

# スクリーニング結果表示
print(df_filtered)

# 保存先パス(Drive内の好きな場所に合わせて書く)
output_path = '/content/drive/MyDrive/stock_prediction/ver.1/results/1st/1st_filtered_prime_20250406.csv'

# CSVとして保存
df_filtered.to_csv(output_path, index=False)

print(f"Saved to {output_path}")  

以下はスクリーニング結果(2025-04-06実行)の一部です。
(ROE > 10%、PER < 15、営業利益率 > 10%)

2000銘柄近くあったところが、約160ほどに絞られました。

Ticker ROE (%) PER 営業利益率 (%)
1377.T 11.77 8.47 13.80
1429.T 18.54 13.06 11.16
1662.T 18.32 3.38 17.26
1871.T 16.73 10.47 13.61
1879.T 10.83 7.75 12.16

🏁 今後の展望

この記事はあくまで第一段階(一次スクリーニング)です。
この後は、テクニカル分析やクラスタリング/PCA/機械学習モデルを用いたさらなる選抜を行っていく予定です。
今後の記事では、どの条件で買うと安定的に勝てるのかを探る検証も紹介していきます!


💡補足・注意

yfinance は時々データが欠損している場合が多々あります。完璧な網羅性を求めるなら、有料APIの検討も必要です。
銘柄数が多い場合は、2〜3秒スリープを入れないとアクセス制限がかかります。

今回のサンプルコードの作成には、ChatGPT(OpenAI)を活用しています。
文法や構文のヒント、処理の構成などをAIに支援してもらいながら、その後自身で実行・デバッグ・改善を行っています。

実際にGoogle Colabでは動作確認済みです。
もし不具合や改善点があれば、お気軽にコメントで教えていただけると嬉しいです。


📝 最後に

本記事で紹介したスクリーニングは、ROE・PER・営業利益率という“入口の基準”に基づいて、 投資対象としての候補を絞り込むための一手法です。

冒頭で読者を引き込むために、「雑草・捨てる」などという言葉を多用していますが、
このフィルターに合致しなかった企業に対して、価値がないという意図は一切ありません。
むしろ、企業の魅力や将来性というものは、財務指標だけでは測れない多面的なものです。

今回の処理は、その中の一断面だけを取り出した機械的な選抜にすぎません。
多様な視点を持ちつつ、分析の入口を自分で選ぶ力を育てていくことが、
このプロセスの本当の目的だと思っています。

1
2
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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?