日本取引所グループ(JPX)が出しているJ-Quants APIという株関連の情報を取得できるAPIがあります。
データ範囲に制限がありますが無料でも使えますので今回はJ-Quants APIを使ってPER(株価収益率)、PBR(株価純資産倍率)を出したいと思います
J-Quants API とその他の株関連API
各種APIについて個人的な理解をまとめておきます
J-Quants API
- ファンダメンタル分析に向いている
- JPXが出しているので安心安全
- 株価はデイリーのみなのでデイトレには向いていない
- 無料だとお遊びにしか使えない(株価が三ヶ月前までのものしか取得できない)
- 株分割されたあとの過去株価の調整データがあるのでかなりこれは良い
- 地獄のEDINETタクソノミXBRLに比べたらかなり良い
Stooq
- Python pandas_datareaderから無料で株価がデイリーで取得できる
- デイリーなのでデイトレには向いていない
- 無料!アカウント登録なし!のしびれる仕様
- 株価取得だけなのでファンダメンタル分析には向いていない
- テクニカルチャートをバンバン出力するのに向いている
- 株分割の考慮されたデータがたぶんないので時前で用意する必要がある、なので株分割があると地獄
EDINET
- 金融庁産
- 有価証券報告書が入手できる
- APIがあるが中身はZIP😅、XBRL😅、タクソノミ😅 難解
- 株価でなくファンダメンタル分析的なデータを正確に取得できる
PERとPBRの算出方法
J-Quants APIのstatements(財務情報取得API)でESPとBSPが取得できますのでそれを使います
用語 | 日本語 |
---|---|
EPS | 1株当たり当期純利益 |
BPS | 1株当たり純資産 |
計算方法はこうなります
用語 | 算出方法 |
---|---|
PER | (ある時点の)株価 ÷ EPS |
PBR | (ある時点の)株価 ÷ BPS |
株価は/prices/daily_quotesで取得できます。無料だと遅延日数がありますが今回のような使用では問題にならないでしょう。
実装
ソースコードはこちら https://github.com/AKB428/pbr_per_poc1
あらかじめアカウント登録を行い環境変数にリフレッシュトークンを格納しておきます
export REFRESH_TOKEN=""
1 EPS BPSを取得
まずEPS、BPSを取得します
import requests
import os
import json
import pandas as pd
import sys
def format_number(value):
try:
return f"{int(value):,}"
except (ValueError, TypeError):
return value
# 銘柄コードを第一引数から取得
if len(sys.argv) < 2:
raise ValueError("銘柄コードを指定してください。")
code = sys.argv[1]
# 環境変数からREFRESH_TOKENを取得
REFRESH_TOKEN = os.getenv("REFRESH_TOKEN")
# REFRESH_TOKENが取得できなかった場合のエラーハンドリング
if not REFRESH_TOKEN:
raise ValueError("環境変数 'REFRESH_TOKEN' が設定されていません。")
# POSTリクエストを送信
r_post = requests.post(f"https://api.jquants.com/v1/token/auth_refresh?refreshtoken={REFRESH_TOKEN}")
# レスポンスからJSONデータを取得
response_data = r_post.json()
# idTokenを取得
idToken = response_data.get("idToken")
# idTokenが取得できなかった場合のエラーハンドリング
if not idToken:
raise ValueError("レスポンスに 'idToken' が含まれていません。")
# APIエンドポイントへのGETリクエスト
headers = {'Authorization': f'Bearer {idToken}'}
params = {"code": code}
r_get = requests.get("https://api.jquants.com/v1/fins/statements", headers=headers, params=params)
# レスポンスを取得
response_json = r_get.json()
# 指定された項目を抽出して整形
statements = response_json.get('statements', [])
data = []
for statement in statements:
data.append({
"DisclosedDate": statement.get("DisclosedDate"),
"NetSales": format_number(statement.get("NetSales")),
"EarningsPerShare": statement.get("EarningsPerShare"),
"BookValuePerShare": statement.get("BookValuePerShare")
})
# DataFrameを作成して表示
df = pd.DataFrame(data)
print(df.to_string(index=False, justify='left'))
出力結果
任天堂(7832)で試してみます
python3 getfinStatements.py 7974
DisclosedDate NetSales EarningsPerShare BookValuePerShare
2022-05-10 1,695,344,000,000 4046.69 17635.6
2022-08-03 307,460,000,000 1018.55
2022-11-08 656,974,000,000 197.61
2023-02-07 1,295,178,000,000 297.05
2023-05-09 1,601,677,000,000 371.41 1946.55
2023-08-03 461,341,000,000 155.48
2023-11-07 796,237,000,000 233.03
2024-02-06 1,394,796,000,000 350.48
うまくEPS、BPSが取得できています。
すべてのレコードにBPSがあるわけではないようです。
EPS、BPSがあるレコードに対してPER、PBRを算出
DisclosedDate(開示日)の株価を取得しPER、BSPを取得します。
import requests
import os
import json
import pandas as pd
import sys
from tokyo_stock_exchange import tse
def format_number(value):
try:
return f"{int(value):,}"
except (ValueError, TypeError):
return value
def get_closing_price(idToken, code, date):
headers = {'Authorization': f'Bearer {idToken}'}
params = {"code": code, "date": date}
r_get = requests.get("https://api.jquants.com/v1/prices/daily_quotes", headers=headers, params=params)
response_json = r_get.json()
daily_quotes = response_json.get('daily_quotes', [])
if daily_quotes:
return daily_quotes[0].get('Close')
return None
# 銘柄名またはコードを第一引数から取得
stock_name_or_code = sys.argv[1]
# 銘柄コードを取得
stock_info = tse.get_stock_info(stock_name_or_code)
if not stock_info:
raise ValueError(f"銘柄 '{stock_name_or_code}' の情報が見つかりません。")
code = stock_info[0]
# 環境変数からREFRESH_TOKENを取得
REFRESH_TOKEN = os.getenv("REFRESH_TOKEN")
# REFRESH_TOKENが取得できなかった場合のエラーハンドリング
if not REFRESH_TOKEN:
raise ValueError("環境変数 'REFRESH_TOKEN' が設定されていません。")
# POSTリクエストを送信
r_post = requests.post(f"https://api.jquants.com/v1/token/auth_refresh?refreshtoken={REFRESH_TOKEN}")
# レスポンスからJSONデータを取得
response_data = r_post.json()
# idTokenを取得
idToken = response_data.get("idToken")
# idTokenが取得できなかった場合のエラーハンドリング
if not idToken:
raise ValueError("レスポンスに 'idToken' が含まれていません。")
# APIエンドポイントへのGETリクエスト
headers = {'Authorization': f'Bearer {idToken}'}
params = {"code": code}
r_get = requests.get("https://api.jquants.com/v1/fins/statements", headers=headers, params=params)
# レスポンスを取得
response_json = r_get.json()
# 指定された項目を抽出して整形
statements = response_json.get('statements', [])
data = []
for statement in statements:
closing_price = get_closing_price(idToken, code, statement.get("DisclosedDate"))
earnings_per_share = statement.get("EarningsPerShare")
book_value_per_share = statement.get("BookValuePerShare")
if closing_price and earnings_per_share:
per = closing_price / float(earnings_per_share)
else:
per = None
if closing_price and book_value_per_share:
pbr = closing_price / float(book_value_per_share)
else:
pbr = None
data.append({
"DisclosedDate": statement.get("DisclosedDate"),
"EarningsPerShare": earnings_per_share,
"BookValuePerShare": book_value_per_share,
"ClosingPrice": format_number(closing_price),
"PER": f"{per:.1f}" if per is not None else None,
"PBR": f"{pbr:.2f}" if pbr is not None else None
})
# DataFrameを作成して表示
df = pd.DataFrame(data)
print(df.to_string(index=False, justify='left'))
実行
python3 getPerPbr.py 任天堂
結果
DisclosedDate EarningsPerShare BookValuePerShare ClosingPrice PER PBR
2022-05-10 4046.69 17635.6 56,360 13.9 3.20
2022-08-03 1018.55 56,700 55.7 None
2022-11-08 197.61 6,225 31.5 None
2023-02-07 297.05 5,624 18.9 None
2023-05-09 371.41 1946.55 5,730 15.4 2.94
2023-08-03 155.48 6,357 40.9 None
2023-11-07 233.03 6,385 27.4 None
2024-02-06 350.48 8,376 23.9 None
PER、PBRが算出できました😁!
検証 果たしてこれはあってるのか
四季報を見てみる
四季報を見てみると
- 実績PER 高値
- 実績PER 安値
- PBR
- 予想PER
といくつかわかれていました。
PER、PBRの算出方法もかいてあり、予想PERは直近株価、実績PERは過去3決算期を使用。
PBRは直近株価を使用と書かれてありました。
四季報のPER、PBRとはずれていましたが(というかそもそも過去の四季報を持っていない)算出方法自体はずれてなさそうです。
四季報にならって高値、安値株価からのPERも算出してみる
import requests
import os
import json
import pandas as pd
import sys
from tokyo_stock_exchange import tse
def format_number(value):
try:
return f"{int(value):,}"
except (ValueError, TypeError):
return value
def get_closing_price(idToken, code, date):
headers = {'Authorization': f'Bearer {idToken}'}
params = {"code": code, "date": date}
r_get = requests.get("https://api.jquants.com/v1/prices/daily_quotes", headers=headers, params=params)
response_json = r_get.json()
daily_quotes = response_json.get('daily_quotes', [])
if daily_quotes:
return daily_quotes[0].get('Close')
return None
def get_price_range(idToken, code, start_date, end_date):
headers = {'Authorization': f'Bearer {idToken}'}
params = {"code": code, "from": start_date, "to": end_date}
r_get = requests.get("https://api.jquants.com/v1/prices/daily_quotes", headers=headers, params=params)
response_json = r_get.json()
daily_quotes = response_json.get('daily_quotes', [])
if daily_quotes:
high_prices = [quote.get('High') for quote in daily_quotes]
low_prices = [quote.get('Low') for quote in daily_quotes]
return max(high_prices), min(low_prices)
return None, None
# 銘柄名またはコードを第一引数から取得
stock_name_or_code = sys.argv[1]
# 銘柄コードを取得
stock_info = tse.get_stock_info(stock_name_or_code)
if not stock_info:
raise ValueError(f"銘柄 '{stock_name_or_code}' の情報が見つかりません。")
print("Stock Info:", stock_info) # 取得したstock_infoを出力
code = stock_info[0]
# 環境変数からREFRESH_TOKENを取得
REFRESH_TOKEN = os.getenv("REFRESH_TOKEN")
# REFRESH_TOKENが取得できなかった場合のエラーハンドリング
if not REFRESH_TOKEN:
raise ValueError("環境変数 'REFRESH_TOKEN' が設定されていません。")
# POSTリクエストを送信
r_post = requests.post(f"https://api.jquants.com/v1/token/auth_refresh?refreshtoken={REFRESH_TOKEN}")
# レスポンスからJSONデータを取得
response_data = r_post.json()
# idTokenを取得
idToken = response_data.get("idToken")
# idTokenが取得できなかった場合のエラーハンドリング
if not idToken:
raise ValueError("レスポンスに 'idToken' が含まれていません。")
# APIエンドポイントへのGETリクエスト
headers = {'Authorization': f'Bearer {idToken}'}
params = {"code": code}
r_get = requests.get("https://api.jquants.com/v1/fins/statements", headers=headers, params=params)
# レスポンスを取得
response_json = r_get.json()
# 指定された項目を抽出して整形
statements = response_json.get('statements', [])
data = []
high_per = None
low_per = None
for statement in statements:
closing_price = get_closing_price(idToken, code, statement.get("DisclosedDate"))
earnings_per_share = statement.get("EarningsPerShare")
book_value_per_share = statement.get("BookValuePerShare")
if closing_price and earnings_per_share:
per = closing_price / float(earnings_per_share)
else:
per = None
if closing_price and book_value_per_share:
pbr = closing_price / float(book_value_per_share)
else:
pbr = None
data.append({
"DisclosedDate": statement.get("DisclosedDate"),
"NetSales": format_number(statement.get("NetSales")),
"EarningsPerShare": earnings_per_share,
"BookValuePerShare": book_value_per_share,
"ClosingPrice": format_number(closing_price),
"PER": f"{per:.1f}" if per is not None else None,
"PBR": f"{pbr:.2f}" if pbr is not None else None
})
# 最後のstatementから期間を取得
last_statement = statements[-1]
start_date = last_statement.get("CurrentPeriodStartDate")
end_date = last_statement.get("CurrentPeriodEndDate")
high_price, low_price = get_price_range(idToken, code, start_date, end_date)
if high_price and last_statement.get("EarningsPerShare"):
high_per = high_price / float(last_statement.get("EarningsPerShare"))
if low_price and last_statement.get("EarningsPerShare"):
low_per = low_price / float(last_statement.get("EarningsPerShare"))
# DataFrameを作成して表示
df = pd.DataFrame(data)
print(df.to_string(index=False, justify='left'))
print(f"\n対象期間: {start_date} ~ {end_date}")
print(f"High Price: {format_number(high_price)}")
print(f"Low Price: {format_number(low_price)}")
print(f"High PER: {high_per:.1f}" if high_per is not None else "High PER: N/A")
print(f"Low PER: {low_per:.1f}" if low_per is not None else "Low PER: N/A")
python3 getPerPbr2.py 任天堂
Stock Info: ('7974', '任天堂')
DisclosedDate NetSales EarningsPerShare BookValuePerShare ClosingPrice PER PBR
2022-05-10 1,695,344,000,000 4046.69 17635.6 56,360 13.9 3.20
2022-08-03 307,460,000,000 1018.55 56,700 55.7 None
2022-11-08 656,974,000,000 197.61 6,225 31.5 None
2023-02-07 1,295,178,000,000 297.05 5,624 18.9 None
2023-05-09 1,601,677,000,000 371.41 1946.55 5,730 15.4 2.94
2023-08-03 461,341,000,000 155.48 6,357 40.9 None
2023-11-07 796,237,000,000 233.03 6,385 27.4 None
2024-02-06 1,394,796,000,000 350.48 8,376 23.9 None
対象期間: 2023-04-01 ~ 2023-12-31
High Price: 7,395
Low Price: 5,150
High PER: 21.1
Low PER: 14.7
EDINETから有価証券報告書と比べる
EDINETから有価証券報告書の四半期のPDFをダウンロードして値を比べます
84期第3四半期のEPSは有価証券報告書でも350.48となっており
J-Quants APIから取得したEPSともあっているので少なくともEPSは正確な値のため
取得する株価を適切にすればPERはそこまで変なことにはならなそうです。
↓先ほどの結果
DisclosedDate EarningsPerShare BookValuePerShare ClosingPrice PER PBR
~~
2024-02-06 350.48 8,376 23.9 None
まとめ、得られたこと
巷でよく聞くようになったPER、PBRですが元となる値はどこから来ているのか?、対象とする株価はどの時点のものなのか?をしっかり理解できるいい機会となりました。株関連の理解を深めるためにも自分でプログラムを作って様々な指標を算出していくのはためになると思いました。
単純にPERを調べたい場合は有価証券報告書に記載があるので難解EDINETを使えばできます。
簡単にとりあえず算出したいみたいなときに無料のJ-Quants APIは役立ちそうです。