AB型技術系 主に備忘録

ほぼプログラム関連の備忘録

東証が公開している東証上場銘柄一覧を元に板情報を取得する

東証が公開している東証上場銘柄一覧

www.jpx.co.jp

こちらから東証上場銘柄一覧(Excel)をダウンロードすることができます。

freelancer.hatenablog.jp

このデータがあればau株コム証券のkabuステーション®APIを利用して東証の全銘柄のその日の板情報が取得できると思って試してみました。

kabuステーション®APIを利用する時の注意点

kabucom.github.io

情報系のリクエストは秒間10件ていどの流量制限が設けられている。
リクエストされた銘柄は自動でAPI登録銘柄リストに登録され、
50銘柄を超えると以降のリクエストが400 BadRequestになる。

使用するライブラリ

Excelを操作するためopenpyxlをインストールする。

Excelにヘッダ項目を追加

変更前のExcel

板情報取得に必要なのはB列のコード。

変更後のExcel

K列に安値、L列に高値、M列に始値、N列に終値、O列に出来高を追加してxlsxで保存する。
xlsだとopenpyxlで操作することができません。

サンプルプログラム1

B列のコードを取得していた情報を取得しK列~O列に設定。
B列がブランクになるまで処理してExcelを保存するサンプルです。
1件処理した後に0.2秒待機させてます。

import urllib.request
import json
import time

import openpyxl
from openpyxl.workbook import Workbook

# エクセルファイルのパス
EXCEL_PATH = "c:\\data\\東証上場銘柄一覧.xlsx"

class KabuStationApi():

    def __init__(self):
        """コンストラクタ
        """
        # APIトークン取得
        obj = { "APIPassword": "password"}
        url = "http://localhost:18080/kabusapi/token"
        res = self.__get_content(url, obj, {}, 'POST')
        self.token = res["Token"]

    def add_board_info(self):

        # 処理開始時刻
        start_time = time.time() 

        # Excelファイルを読み込む
        wb = openpyxl.load_workbook(filename=EXCEL_PATH, data_only=True)

        # シート
        ws = wb.worksheets[0]

        # 開始行番号
        rowNo = 2
        cd = ws.cell(row=rowNo, column=2).value

        # 銘柄コードがブランクになるまでループ
        while cd is not None:

            # 銘柄コード
            cd = str(ws.cell(row=rowNo, column=2).value)
            print(f"銘柄コード:{cd} Start")
            # 板情報取得用のURL 全て東証の銘柄なので市場コードは1固定
            url = "http://localhost:18080/kabusapi/board/" + str(cd) +"@1"
            param = {"X-API-KEY":self.token}
            res = self.__get_content(url, {}, param)
            if len(res) > 0:
                print(res["SymbolName"])
                # 安値
                ws.cell(row=rowNo, column=11, value=res["LowPrice"])
                # 高値
                ws.cell(row=rowNo, column=12, value=res["HighPrice"])
                # 始値
                ws.cell(row=rowNo, column=13, value=res["OpeningPrice"])
                # 終値
                ws.cell(row=rowNo, column=14, value=res["CurrentPrice"])
                # 出来高
                ws.cell(row=rowNo, column=15, value=res["TradingVolume"])
            print(f"銘柄コード:{cd} End")
           
            # 0.2秒待機
            time.sleep(0.2)
            
            # 次の行へ
            rowNo += 1
            cd = ws.cell(row=rowNo, column=2).value

        # 処理終了時刻
        end_time = time.time() 

        elapsed_time = end_time - start_time

        elapsed_hour = str(int(elapsed_time // 3600)).zfill(2)
        elapsed_minute = str(int((elapsed_time % 3600) // 60)).zfill(2)
        elapsed_second = str(int((elapsed_time % 3600 % 60))).zfill(2)

        wb.save(EXCEL_PATH)

        print(f"経過時間:{elapsed_hour}:{elapsed_minute}:{elapsed_second}:")

    def __get_content(self, url, body_param, header_param, method="GET"):
        """指定したURLにリクエストを送信しレスポンスを返す

        Parameters
        ----------
        url : string
            url
        body_param : dict, optional
            Httpリクエストボディに設定するパラメーター, by default None
        header_param : dict, optional
            Httpヘッダに設定するパラメーター, by default {}
        method : str, optional
            HTTPメソッド, by default 'GET'

        Returns
        -------
        dict
            取得したデータ
        """
        req = None
        if body_param is not None:
            req = urllib.request.Request(url, json.dumps(body_param).encode('utf8'), method=method)
        else:
            req = urllib.request.Request(url, method=method)
        req.add_header('Content-Type', 'application/json')
        for key, param in header_param.items():
            req.add_header(key, param)
        try:
            with urllib.request.urlopen(req) as res:
                return json.loads(res.read())
        except urllib.error.HTTPError as e:
            print(e)
            return {}
        except Exception as e:
            print(e)
            return {}

# インスタンス生成
api = KabuStationApi()

# データを取得しExcelの板情報を更新
api.add_board_info()

実行結果

・
・
・
・
銘柄コード:1390 Start
UBS ETF MSCIアジア太平洋株(除く日本)
銘柄コード:1390 End
銘柄コード:1391 Start
HTTP Error 400: Bad Request
銘柄コード:1391 End
銘柄コード:1392 Start
HTTP Error 400: Bad Request
銘柄コード:1392 End
銘柄コード:1393 Start
HTTP Error 400: Bad Request
銘柄コード:1393 End
銘柄コード:1394 Start
HTTP Error 400: Bad Request
銘柄コード:1394 End
API登録銘柄リスト

リファレンスに記載された通りAPI登録銘柄リストに登録された銘柄が50銘柄を超えると以降のリクエストが400 BadRequestになります。

kabucom.github.io

こちらのリファレンスを参考に50件登録したらAPI登録銘柄リストをクリアするようにすればBad Requestは解消されました。

サンプルプログラム2

サンプルプログラム1にAPI登録銘柄リストクリア処理を追加したサンプルです。

import urllib.request
import json
import time

import openpyxl
from openpyxl.workbook import Workbook

# エクセルファイルのパス
EXCEL_PATH = "c:\\data\\東証上場銘柄一覧.xlsx"

class KabuStationApi():

    def __init__(self):
        """コンストラクタ
        """
        # APIトークン取得
        obj = { "APIPassword": "password"}
        url = "http://localhost:18080/kabusapi/token"
        res = self.__get_content(url, obj, {}, 'POST')
        self.token = res["Token"]
        print("APIトークン:" + self.token)

    def add_board_info(self):

        # 処理開始時刻
        start_time = time.time() 

        # Excelファイルを読み込む
        wb = openpyxl.load_workbook(filename=EXCEL_PATH, data_only=True)

        # シート
        ws = wb.worksheets[0]

        # 開始行番号
        rowNo = 2
        cd = ws.cell(row=rowNo, column=2).value

        # 処理前にAPI登録銘柄リストをクリア
        cnt = 0
        self.__clear_register()

        # 銘柄コードがブランクになるまでループ
        while cd is not None:

            # 銘柄コード
            cd = str(ws.cell(row=rowNo, column=2).value)
            print(f"銘柄コード:{cd} Start")
            # 板情報取得用のURL 全て東証の銘柄なので市場コードは1固定
            url = "http://localhost:18080/kabusapi/board/" + str(cd) +"@1"
            param = {"X-API-KEY":self.token}
            res = self.__get_content(url, {}, param)
            if len(res) > 0:
                print(res["SymbolName"])
                # 安値
                ws.cell(row=rowNo, column=11, value=res["LowPrice"])
                # 高値
                ws.cell(row=rowNo, column=12, value=res["HighPrice"])
                # 始値
                ws.cell(row=rowNo, column=13, value=res["OpeningPrice"])
                # 終値
                ws.cell(row=rowNo, column=14, value=res["CurrentPrice"])
                # 出来高
                ws.cell(row=rowNo, column=15, value=res["TradingVolume"])
            print(f"銘柄コード:{cd} End")
           
            # 0.2秒待機
            time.sleep(0.2)
            
            # 次の行へ
            rowNo += 1
            cd = ws.cell(row=rowNo, column=2).value

            cnt += 1

            # API登録銘柄が50になった場合登録状態をクリアする
            if cnt == 50:
                cnt = 0
                self.__clear_register()

        # 処理終了時刻
        end_time = time.time() 

        elapsed_time = end_time - start_time

        elapsed_hour = str(int(elapsed_time // 3600)).zfill(2)
        elapsed_minute = str(int((elapsed_time % 3600) // 60)).zfill(2)
        elapsed_second = str(int((elapsed_time % 3600 % 60))).zfill(2)

        wb.save(EXCEL_PATH)

        print(f"経過時間:{elapsed_hour}:{elapsed_minute}:{elapsed_second}:")

    def __get_content(self, url, body_param, header_param, method="GET"):
        """指定したURLにリクエストを送信しレスポンスを返す

        Parameters
        ----------
        url : string
            url
        body_param : dict, optional
            Httpリクエストボディに設定するパラメーター, by default None
        header_param : dict, optional
            Httpヘッダに設定するパラメーター, by default {}
        method : str, optional
            HTTPメソッド, by default 'GET'

        Returns
        -------
        dict
            取得したデータ
        """
        req = None
        if body_param is not None:
            req = urllib.request.Request(url, json.dumps(body_param).encode('utf8'), method=method)
        else:
            req = urllib.request.Request(url, method=method)
        req.add_header('Content-Type', 'application/json')
        for key, param in header_param.items():
            req.add_header(key, param)
        try:
            with urllib.request.urlopen(req) as res:
                return json.loads(res.read())
        except urllib.error.HTTPError as e:
            print(e)
            return {}
        except Exception as e:
            print(e)
            return {}

    def __clear_register(self):
        """API登録銘柄リストをクリアする
        """
        url = "http://localhost:18080/kabusapi/unregister/all"
        param = {"X-API-KEY":self.token}
        print("登録銘柄クリア Start")
        res = self.__put_content(url, {}, param)
        print("登録銘柄クリア End")
        return res

    def __put_content(self, url, body_param, header_param):
        """指定したURLにリクエストを送信しレスポンスを返す

        Parameters
        ----------
        url : string
            url
        body_param : dict, optional
            Httpリクエストボディに設定するパラメーター, by default None
        header_param : dict, optional
            Httpヘッダに設定するパラメーター, by default {}
        method : str, optional
            HTTPメソッド, by default 'GET'

        Returns
        -------
        dict
            取得したデータ
        """
        if body_param is not None:
            req = urllib.request.Request(url, json.dumps(body_param).encode('utf8'), method="PUT")
        else:
            req = urllib.request.Request(url, method="PUT")
        req.add_header('Content-Type', 'application/json')
        for key, param in header_param.items():
            req.add_header(key, param)
        try:
            with urllib.request.urlopen(req) as res:
                return json.loads(res.read())
        except urllib.error.HTTPError as e:
            print(e)
            return {}
        except Exception as e:
            print(e)
            return {}

# インスタンス生成
api = KabuStationApi()

# データを取得しExcelの板情報を更新
api.add_board_info()

実行結果

・
・
・
・
銘柄コード:9993 Start
ヤマザワ
銘柄コード:9993 End
銘柄コード:9994 Start
やまや
銘柄コード:9994 End
銘柄コード:9995 Start
グローセル
銘柄コード:9995 End
銘柄コード:9996 Start
サトー商会
銘柄コード:9996 End
銘柄コード:9997 Start
ベルーナ
銘柄コード:9997 End
経過時間:05:57:15:
Excel 板情報反映後
Excel 板情報反映後

一部ブランクの銘柄がありまが、おそらく出来高0の銘柄。
0.2秒待機しましたが、レスポンスが返ってくるまで数秒かかったので全銘柄取得するのに約6時間かかりました・・・

試す場合は待機時間等に気を付けて自己責任でお願いします。