見出し画像

J-Quants API で銘柄情報を取得する

ご注意:2023/04/03 より正式に運用が開始されました。正式版の接続URLは、β版と異るので読み替えて下さい。なお β版の運用は、2023/04/10で終了とのことです。

銘柄情報APIで取得できるデータ

取得できる情報は、

変数名, 説明, 型
Code, 銘柄コード, string
UpdateDate, 情報適用年月日, string
CompanyNameFull, 会社名フルネーム, string
MarketCode, 銘柄コード, streing
CompanyName, 会社名, string
CompanyNameEnglish, 英語会社名, string
SectorCode, 業種コード, string

だそうです。
いわゆる銘柄マスターですね。
メンテナンスを自力でするのは非常に大変です。
これは助かります。

2023.02.03 追記:
仕様が変更になったようです。
CompanyNameFull, 会社名フルネーム, string
などが無くなり、セクターコードが追加になったようです。
最新は、「銘柄情報 API」のページを参照してください。
https://jpx.gitbook.io/j-quants-api/api-reference/listed-api


銘柄情報APIのサンプルコードを使ってみる

前回記事のリフレッシュトークン、IDトークン取得の続きでプログラムを作っていきます。

サンプルプログラムは

import requests
import json

REFRESH_TOKEN = "YOUR refreshtokenID"
r_post = requests.post(f"https://api.jpx-jquants.com/v1/token/auth_refresh?refreshtoken={REFRESH_TOKEN}")
r_post.json()

となっています。

前回のコードは、変数「str_idtoken」にIDトークンを保存したので

idToken = str_idtoken
headers = {'Authorization': 'Bearer {}'.format(idToken)}
req_info = requests.get("https://api.jpx-jquants.com/v1/listed/info", headers=headers)
print(req_info.text)

と続け、走らせてみました。

すると、大量のテキストが画面を流れていきます。
・・・
・・・
・・・, {"Code": "46130", "UpdateDate": "20220928", "CompanyNameFull": "関西ペイント", "MarketCode": "A", "CompanyName": "関ペイント", "CompanyNameEnglish": "KANSAI PAINT CO.,LTD.", "SectorCode": "3200"}, {"Code": "71670", "UpdateDate": "20220928", "CompanyNameFull": "(株)めぶきフィナンシャルグループ", "MarketCode": "A", "CompanyName": "めぶきFG", "CompanyNameEnglish": "Mebuki Financial Group,Inc.", "SectorCode": "7050"}]}
ふーぅ、ようやく止まりました。助かりました。


データの形式を調べる

データの最後が
:"7050"}]}
となっていますので今回もjson形式のようです。

データの最初を見てみたいので最後のprint文を
print(req_info.text[:50])
に変更して、試してみます。

{"info": [{"Code": "79860", "UpdateDate": "2022092

構造は、
{"info":[{"code":"79860", "UpdateDate": "2022092・・・},{・・・,"SectorCode": "7050"}]}
のようです。
"key"が"info"のjson形式です。


銘柄情報を取り出す

リフレッシュトークンやIDトークンと同じ形なので、
dic_info = json.loads(req_info.text)
dic_value = dic_info.get('info')
で"info"の内容(value)を取り出してみます。

コードは、

idToken = str_idtoken
headers = {'Authorization': 'Bearer {}'.format(idToken)}
req_info = requests.get("https://api.jpx-jquants.com/v1/listed/info", headers=headers)
dic_info = json.loads(req_info.text)
dic_value = dic_info.get('info')
print(dic_value[0])
print(dic_value[0].get('Code'))

としてみました。

出力は、
{'Code': '79860', 'UpdateDate': '20220928', 'CompanyNameFull': '日本アイ・エス・ケイ', 'MarketCode': 'B', 'CompanyName': '日本アイエスケイ', 'CompanyNameEnglish': 'NIHON ISK Company,Limited', 'SectorCode': '3800'}
79860
最初の銘柄の情報{'Code': '79860',・・・: '3800'}と、銘柄コード「79860」を取り出せました。


銘柄情報のソート

何だかスイスイ出来ますね。嬉しくなっちゃいます(笑
でも、よく見ると最初の銘柄が「79860」です。普通は13010極洋から始まります。どうも銘柄コード順に並んでいないようです。

jsonでのソートをネット検索してみると、ストレートにできないようです。
通常はDBに入れてしまいソートはDBにお任せが常道だと思います。
私のローカル環境にはDBをまだ用意していません。早くpostgreSQLを入れてpythonから接続できる様にしないといけないですね。
などと将来のことを語っても解決しないのでローカルのプログラムで何とかすることにします。

取り敢えず、取り出した銘柄リスト全体データ( [ ] で囲まれた部分)である
dic_info = json.loads(req_info.text)
と、[ ] の中の { } で囲まれた1銘柄のデータである
dic_value = dic_info.get('info')
の型を調べてみます。

コードは

dic_info = json.loads(req_info.text)
print('type(dic_info): ', type(dic_info))

dic_value = dic_info.get('info')
print('type(dic_value): ', type(dic_value))

です。

結果は、
type(dic_info): <class 'dict'>
type(dic_value): <class 'list'>

json.loads(req_info.text)で取り出したdic_infoは、予想通り辞書型でした。
一方、dic_info.get('info') で取り出したdic_value はリスト型でした。

どちらも辞書型だと思っていたので驚きました。コーディングは、初級者レベル、それも永遠の初級者レベルなのでお許し下さい(笑
変数名はlist_valueにしないといけませんね(単なるマイルールですw)。

list型だと、リストのソートが使えそうです。

sorted_list = sorted(list_value, key=lambda x:x['Code'])
print(sorted_list[0])
print(sorted_list[1])
print(sorted_list[2])

と追加してみると、13010、13050、13060 と銘柄コード順に出力されました。

横道にそれて銘柄リストで取得したデータの型を見てみる

key "info" の value が、リスト型だったのでソートが簡単にできました。
それで目的は達成なので良いのですが、改めて銘柄リストのデータを処理する各段階でどのような型なのか気になりました。

調べてみると
type(req_info): <class 'requests.models.Response'>
これは、{"info":[{ },・・・,{ }]} という返値全体です。この型は「requests ライブラリの get 関数などが返す requests.models.Response オブジェクト」のようです。

APIの戻り値のテキストをjson.loadsしたデータは、
dic_info = json.loads(req_info.text)
type(dic_info): <class 'dict'>
これは、{"info":[{ },・・・,{ }]} という返値全体を json.loads した結果の型です。辞書型ですね。

上の辞書型でvalueを読み出します。
list_value = dic_info.get('info')
type(list_value): <class 'list'>
これは key "info" の value 部分の [{ },・・・,{ }] で、リスト型です。
a = [ 'x', 'y', 'z' ] とリストを作る時 [ ] を使いますが、そのままですね。

次はこのリスト型の1要素を調べます。
type(list_value[0]): <class 'dict'>
これは、key "info" の value 部分の [{ },・・・,{ }] 中の要素 { } 部分です。辞書型ですね。

こう考えるとjsonといっても [ ] や { } をリストや辞書型で利用するのと同じ構造になっているようですね。永遠の初級者なのでこんなことでも分かって嬉しいです。

銘柄リストのCSV保存

ソートもできたので保存します。せっかくjson形式になっているのですからそのまま保存した方がよいという気がします。
それでは身も蓋もないのでCSVデータにしてみます(笑

csvに加工してみると、「CompanyNameEnglish, 英語会社名, string」の項目の中にcsvの区切り記号 "," が使われています。データ崩れを防ぐため各データ項目は " " で囲むことが必要でした。

コードは、出力ファイル名 jq_info.csv として

# csvで保存
str_fname_output = 'jq_info.csv'
try:
    with open(str_fname_output, 'w', encoding = 'utf_8') as fout:
        print('file open at w, "fout": ', str_fname_output )

        # タイトル行
        str_text = ''
        str_text = str_text + '"' + 'Code' + '",'
        str_text = str_text + '"' + 'CompanyName' + '",'
        str_text = str_text + '"' + 'Sector17Code' + '",'
        str_text = str_text + '"' + 'Sector17CodeName' + '",'
        str_text = str_text + '"' + 'Sector33Code' + '",'
        str_text = str_text + '"' + 'Sector33CodeName' + '",'
        str_text = str_text + '"' + 'ScaleCategory' + '",'
        str_text = str_text + '"' + 'MarketCode' + '",'
        str_text = str_text + '"' + 'MarketCodeName' + '",'
        str_text = str_text + '"' + 'Date' + '"\n'
        fout.write(str_text)     

        # データ行
        for i in range(len(sorted_list)):
            str_text = ''
            str_text = str_text + '"' + sorted_list[i].get('Code') + '",'
            str_text = str_text + '"' + sorted_list[i].get('CompanyName') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Sector17Code') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Sector17CodeName') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Sector33Code') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Sector33CodeName') + '",'
            str_text = str_text + '"' + sorted_list[i].get('ScaleCategory') + '",'
            str_text = str_text + '"' + sorted_list[i].get('MarketCode') + '",'
            str_text = str_text + '"' + sorted_list[i].get('MarketCodeName') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Date') + '"\n'
            fout.write(str_text)     

        fout.close
        print('file close: ', str_fname_output)
        print('銘柄数: ', i+1 )                  

except IOError as e:
    print('Can not Write!!!')
    print(type(e))
    #print(line)

結果は

"Code","UpdateDate","CompanyNameFull","MarketCode","CompanyName","CompanyNameEnglish","SectorCode"
"13010","20220929","(株)極洋","A","極洋","KYOKUYO CO.,LTD.","0050"
"13050","20220929","大和アセットマネジメント株式会社 ダイワ上場投信−トピックス","5","ETF・TPX","Daiwa ETF-TOPIX","9999"
"13060","20220929","野村アセットマネジメント株式会社 NEXT FUNDS TOPIX連動型上場投信","5","NFTOPIX","NEXT FUNDS TOPIX Exchange Traded Fund","9999"
"13080","20220929","日興アセットマネジメント株式会社  上場インデックスファンドTOPIX","5","上場TPX","Nikko Exchange Traded Index Fund TOPIX","9999"
"13090","20220929","野村アセットマネジメント株式会社 NEXT FUNDS ChinaAMC・中国株式・上証50連動型上場投信","5","NF中国株上証50","NEXT FUNDS ChinaAMC SSE50 Index Exchange Traded Fund","9999"
"13110","20220929","野村アセットマネジメント株式会社 NEXT FUNDS TOPIX Core 30連動型上場投信","5","NFTPX30","NEXT FUNDS TOPIX Core 30 Exchange Traded Fund","9999"

と銘柄順にcsv形式で保存されました。


今回のツボ

銘柄データの取得までは順調でした。しかし銘柄のソートのところで、データの型を勘違いしていて躓きました。型を調べてリスト型と分かり、解決できました。きちんと一つづつ押さえて行かないとダメですね。反省です。


今回のコード

# リフレッシュトークン取得
data={"mailaddress":"<YOUR EMAIL_ADDRESS>", "password":"<YOUR PASSWORD>"}
r_post_rftoken = requests.post("https://api.jpx-jquants.com/v1/token/auth_user", data=json.dumps(data))

# IDトークン取得
dic_rftoken = json.loads(r_post_rftoken.text)  # 辞書型に変換
REFRESH_TOKEN = dic_rftoken.get('refreshToken')    # リフレッシュトークンの内容を取得
r_post_idtoken = requests.post(f"https://api.jpx-jquants.com/v1/token/auth_refresh?refreshtoken={REFRESH_TOKEN}")
dic_idtoken = json.loads(r_post_idtoken.text)  # 辞書型に変換
str_idtoken = dic_idtoken.get('idToken')    # IDトークンの内容を取得


# 銘柄情報取得
idToken = str_idtoken
headers = {'Authorization': 'Bearer {}'.format(idToken)}
req_info = requests.get("https://api.jpx-jquants.com/v1/listed/info", headers=headers)

dic_info = json.loads(req_info.text)
list_value = dic_info.get('info')

# 銘柄コード順にソート
sorted_list = sorted(list_value, key=lambda x:x['Code'])

# csvで保存
str_fname_output = 'jq_info.csv'     # 出力ファイル名を指定
try:
    with open(str_fname_output, 'w', encoding = 'utf_8') as fout:
        print('file open at w, "fout": ', str_fname_output )

        # タイトル行
        str_text = ''
        str_text = str_text + '"' + 'Code' + '",'
        str_text = str_text + '"' + 'CompanyName' + '",'
        str_text = str_text + '"' + 'Sector17Code' + '",'
        str_text = str_text + '"' + 'Sector17CodeName' + '",'
        str_text = str_text + '"' + 'Sector33Code' + '",'
        str_text = str_text + '"' + 'Sector33CodeName' + '",'
        str_text = str_text + '"' + 'ScaleCategory' + '",'
##        str_text = str_text + '"' + 'CompanyNameFull' + '",'
##        str_text = str_text + '"' + 'CompanyNameEnglish' + '",'
        str_text = str_text + '"' + 'MarketCode' + '",'
        str_text = str_text + '"' + 'MarketCodeName' + '",'
##        str_text = str_text + '"' + 'SectorCode' + '",'
##        str_text = str_text + '"' + 'UpdateDate' + '"\n'
        str_text = str_text + '"' + 'Date' + '"\n'
        fout.write(str_text)     

        # データ行
        for i in range(len(sorted_list)):
            str_text = ''
            str_text = str_text + '"' + sorted_list[i].get('Code') + '",'
            str_text = str_text + '"' + sorted_list[i].get('CompanyName') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Sector17Code') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Sector17CodeName') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Sector33Code') + '",'
            str_text = str_text + '"' + sorted_list[i].get('Sector33CodeName') + '",'
            str_text = str_text + '"' + sorted_list[i].get('ScaleCategory') + '",'
##            str_text = str_text + '"' + sorted_list[i].get('CompanyNameFull') + '",'
##            str_text = str_text + '"' + sorted_list[i].get('CompanyNameEnglish') + '",'
            str_text = str_text + '"' + sorted_list[i].get('MarketCode') + '",'
            str_text = str_text + '"' + sorted_list[i].get('MarketCodeName') + '",'
##            str_text = str_text + '"' + sorted_list[i].get('SectorCode') + '",'
##            str_text = str_text + '"' + sorted_list[i].get('UpdateDate') + '"\n'
            str_text = str_text + '"' + sorted_list[i].get('Date') + '"\n'
            fout.write(str_text)     

        fout.close
        print('file close: ', str_fname_output)
        print('銘柄数: ', i+1 )                  

except IOError as e:
    print('Can not Write!!!')
    print(type(e))
    #print(line)

毎回リフレッシュトークンを取得する動きなので、保存してそれを使う樣に変えたほうが良さそうです。

次回は財務情報の取得をしてみたいと思います。


本記事上の私が作成したプログラムは自由にご使用ください。但し、この記事のソフトウェアを使用したことによって生じたすべての障害・損害・不具合等に関して、私と私の関係者および私の所属するいかなる団体・組織とも、一切の責任を負いません。各自の責任においてご使用ください。

この記事が気に入ったらサポートをしてみませんか?