こんにちは。TATです。
今日のテーマは「【Python】SeleniumでSBI証券から日本株を売買する方法」です。
PythonとSeleniumを使って、SBI証券のサイトから日本株を売買する操作を自動化してみます。
注文に必要な、銘柄コードや注文タイプ(成行、指値等)、株数などの情報をプログラムから自動入力して、発注を確定するところまで自動化します。
これができると、プログラムを介して株を買ったり売ったりすることができるようになるので、完全な自動売買も可能になります。
SBI証券へのログイン
事前準備として、PythonとSeleniumでSBI証券にログインしておく必要があります。
Seleniumの準備やSBI証券へログインする方法についてはこちらの記事で解説しています。
ログインが完了したら、本記事で紹介するコードを実行することができます。
【Python】SeleniumでSBI証券に自動ログインする方法【自動売買への道】
続きを見る
日本株を売買するまでの流れ
次にPythonとSeleniumでSBI証券のサイトから日本株を売買するまでの流れについて確認しておきます。
ざっくりとこんな感じになります。
日本株を売買するまでの流れ
- (SeleniumでSBI証券にログイン)
- 新規注文ページに遷移する
- 新規注文に必要な情報(銘柄コードや注文タイプなど)を入力する
- 取引パスワードを入力して注文発注をクリックする
- 遷移後のページから注文が成功したかをチェックする
1についてはこちらの記事で解説している内容になるので、()書きにしました。
本記事では1はスキップして、2〜5を実装する方法を解説していきます。
PythonとSeleniumでSBI証券サイトから日本株の自動売買を実装する
それでは2~5の流れを順番に解説していきます。
新規注文ページに遷移する
まずは新規注文ページに遷移します。
SBI証券にログインして、トップページを確認します。
赤丸の「取引」をクリックすると新規注文ページに遷移することができます。
この「取引」の要素をChromeの検証を使って確認してみます。
上記の通り、title="取引"あるいはalt="取引"を利用すれば特定の要素(imgタグ)が取得できそうです。
Seleniumでこの条件で要素を取得してクリックする動作を実装すると次のようになります。
ここではtitle="取引"を使いました。
# 新規注文ページへ遷移 driver.find_element_by_css_selector("img[title=取引]").click()
これで対象の要素をクリックすることができ、新規注文ページに遷移することができます。
これで最初のステップは完了です。
新規注文に必要な情報(銘柄コードや注文タイプなど)を入力する
次に新規注文に必要な情報を入力していきます。
設定項目の確認
注文画面を確認するといろいろな設定項目があります。
本記事では、以下の項目を設定できるようにしていきます。
設定項目
- 取引タイプ(現物買、信用新規売など)
- 銘柄コード(7203など)
- 株数(100株、500株など)
- 注文タイプ(指値、成行、逆指値) *「条件なし」と表示されているドロップダウンメニューについては取り扱わない
- 期間(当日中、期間指定など)
- 預り区分(一般預り、NISA預りなど)
- 信用区分取引(制度、一般など)
ほぼほぼ網羅しました。
今回スキップしたのは、「市場」と価格で「条件なし」と表示されているドロップダウンメニューです。
「市場」では東証や名証などを選択することができますが、デフォルトの当社優先市場にしておけば勝手に選択してくれるので問題ないと判断しました。
価格で「条件なし」と表示されているドロップダウンメニューについては、寄成や引成などを指定することができますが、僕自身あまり使わないので扱わないことにしました。
本記事で紹介するコードを活用すれば、これらの設定も簡単に実装できると思います。
全ての項目を1つずつ説明していくと長くなってしまう、なおかつ似たような内容ばかりになってしまうので、ここでは次の3つの項目に絞って解説します。
入力タイプが異なるもの(文字入力、ラジオボタン、ドロップダウンメニュー)を選びました。
解説対象項目
- 取引タイプ(現物買、信用新規売など)
- 銘柄コード(7203など)
- 期間(当日中、期間指定など)
最終的には、全項目の設定方法が実装されているコードをご紹介します。
取引タイプ(現物買、信用新規売など)
まずは取引タイプです。
こちらはラジオボタンで選択する形式になります。
ラジオボタンはクリックすれば選択することができるので、対象の要素を取得してクリックすればOKです。
対象の要素を確認してみます。
こちらはそれぞれのラジオボタンにidが設定されているので、これを取得してクリックすればOKです。
実装の際には、利便性を上げるため、注文タイプとidを紐づける辞書を定義して、注文タイプを選ぶと自動的にラジオボタンが選択できるようにしました。
# 取引タイプ trade_type_dict = { "現物買": "genK", "現物売": "genU", "信用新規買": "shinK", "信用新規売": "shinU", } trade_type = "現物買" driver.find_element_by_id(id_=trade_type_dict.get(trade_type)).click()
trade_type_dictという辞書を用意して、ここに注文タイプと対応するidを追加しました。
これで、注文タイプを選べば対象のidが取得されて選択できるようになります。
各ラジオボタンにidが設定されている場合は、このように値とidを紐づけられるデータを用意してあげると便利です。
銘柄コード(7203など)
次に証券コードです。
これは値の入力になります。
まずは要素を確認します。
name="stock_sec_code"となっています。こちらを取得すれば入力できそうです。
Seleniumで値を入力する際には、対象の要素を取得してsend_keys()を使えばOKです。
例として証券コードに7203と入力してみます。
# 証券コード driver.find_element_by_name(name="stock_sec_code").send_keys(7203)
これで7203と入力されていることが確認できます。
値の入力については、このように対象要素を取得してsend_keys()で任意の値を入力することができます。
期間(当日中、期間指定など)
最後に期間です。
期間タイプの選択
まずは当日中、今週中、期間指定を選択するラジオボタンの要素を見てみます。
nameは全て共通して"selected_limited_in"となっており、valueの値がそれぞれ違いますね。
valueで要素を特定してもOKですし、nameから特定してもOKです。
ここでは共通のnameから任意の要素を特定する方法を紹介します。
次のように、driver.find_elements_by_nameを使うと、任意のnameである要素を取得できます。elementではなくてelements(最後にsがつきます)にすると、該当する要素を全てリスト形式で取得することができます。
そしてこのリストの要素それぞれが、当日中、今週中、期間指定に対応しています。
リスト形式なので、indexで指定してあげれば要素を取得することができます。
次のコードでは、このリストの中身と要素を一致させるための辞書を用意して、任意の期間を選択できるようにしました。
# period period_type_dict = { "当日中": 0, "今週中": 1, "期間指定": 2 } period_type = "期間指定" driver.find_elements_by_name(name="selected_limit_in")[period_type_dict.get(period_type)].click()
画面を見ると、期間指定が選択できていることが確認できます。
期間指定の値の選択
次に期間指定を選んだ際に、ドロップダウンメニューから特定の日付を選択する方法について解説します。
これまでと同様に、対象となる要素を取得して値を選択します。
これには2つのやり方があります。
どちらも解説します。
- valueで指定する
- indexで指定する
まずは対象要素を確認します。
name="limit_in"となっていることが確認できるので、これを利用して要素を取得します。
また、ドロップダウンのメニュー選択にはSeleniumのselenium.webdriver.support.select.Selectを使います。
from selenium.webdriver.support.select import Select # 期間指定のドロップダウン要素を取得 period_option_div = driver.find_element_by_name(name="limit_in") period_options = Select(period_option_div) # 値で選択 period_options.select_by_value('23/03/24') # indexで選択 period_options.select_by_index(2)
値で選択する際にはselect_by_value、indexで選択する際にはselect_by_indexを使います。
期間指定の選択肢は、常に翌営業日からの日付(営業日のみ)が並んでいるので、indexから選んだ方が楽かもしれません。
値で入力する場合、一致するデータがないとエラーになります。
画面を見ると、きちんと選択できていることが確認できます。
このように、ドロップダウンの場合はSelectを使って要素を選択することができます。
本記事でスキップした「市場」や価格で「条件なし」と表示されているドロップダウンメニューについても同じ方法で実装できます。
その他の項目についても、ここまでで紹介したいずれかのやり方で応用できるはずです。
全部網羅したコードは最後にまとめてご紹介します。
とりあえず一旦ここで次に進みます。
取引パスワードを入力して注文発注をクリックする
次のステップは、取引パスワードを入力して、注文発注をクリックすることです。
一番下のこの部分です。
発注するためには取引パスワードを入力する必要があります。
そして、「注文確認画面を省略」をクリックすると、「注文発注」にボタンの表記が変わります。
これをクリックすれば注文が完了します。
取引パスワードを入力する
まずは取引パスワードを入力します。
要素を確認すると、id="pwd3"となっているのでこれを利用してパスワードを入力します。
Seleniumで実装すると次のようになります。
# 取引パスワード password = "password" driver.find_element_by_id("pwd3").send_keys(password)
これで取引パスワードの入力は完了です。
注文発注をクリックする
次に注文発注をクリックします。
最初に「注文確認画面を省略」をクリックしてから「注文発注」をクリックする流れになります。
それぞれの要素を確認します。
まずは「注文確認画面を省略」です。
id="shouryaku"となっています。
次に「注文発注」ボタンです。
こちらはtitle="注文発注"となっています。
上記の要素を取得して、発注してみます。
# 注文画面を省略 driver.find_element_by_id(id_="shouryaku").click() # 注文発注をクリック driver.find_element_by_css_selector("img[title=注文発注]").click()
これで発注完了です。
遷移後のページから注文が成功したかをチェックする
最後の仕上げです。
発注が成功したかどうかを確認する方法について解説します。
発注が完了すると次のような画面が出てきます。(これはデモ用に適当に注文入れましたw)
ポイントは注文が完了すると「ご注文を受け付けました。」という文言が表示されることです。
注文内容に不備があるとここにエラーメッセージが表示されます。
つまり、ページ内に「ご注文を受け付けました。」という文言があれば発注成功、なければ失敗と判断すればOKです。
ここではBeautifulsoupを使って実装しました。
# 注文確認 html = BeautifulSoup(driver.page_source, "html.parser") if "ご注文を受け付けました。" in html.text: print("注文が完了しました。") else: print("注文に失敗しました。")
ページ全体を読み込んで、「ご注文を受け付けました」という文字列が存在するかどうかをチェックしています。
非常にシンプルですが、これで十分機能します。
最後にコードをまとめてどうぞ!
ここまでの内容で、SBI証券から日本株を自動売買するために必要な全てのプロセスが完了しました。
最後に、これまでのコードをきれいにまとめたものを共有します。
関数化して使いやすくしたつもりです。エラーもできる限りキャッチできるようにしました。
また、所々にデータの読み込み時間を考慮して、driver.implicitly_wait(60)を加えています。
これは次の処理を行うにあたり、対象となる要素が見つかるまで待機するという命令になります。
これがないとページが読み込まれる前に値を入力しようとしてエラーが発生する場合があるので、処理に時間がかかる箇所に追加しておくとプログラムの安定感が増します。
コードをどうぞ
それではコードをどうぞ。
少し長いです。
from selenium import webdriver from selenium.webdriver.support.select import Select from bs4 import BeautifulSoup # 取引タイプ trade_type_dict = { "現物買": "genK", "現物売": "genU", "信用新規買": "shinK", "信用新規売": "shinU", } # 注文タイプ order_type_dict = { "指値": 0, "成行": 1, "逆指値": 2, } # 逆指値タイプ stop_order_type_dict = { "指値": 0, "成行": 1, } # 期間 period_type_dict = { "当日中": 0, "今週中": 1, "期間指定": 2 } # 預り区分 trade_section_dict = { "一般預り": 0, "特定預り": 1, "NISA預り": 2 } # 信用取引区分 margin_trade_section_dict = { "制度": 0, "一般": 1, "日計り": 2 } def order(trade_type="現物買", ticker=None, unit=100, order_type="成行", limit_order_price=None, stop_order_trigger_price=None, stop_order_type="成行", stop_order_price=None, period_type="当日中", period_value=None, period_index=None, trade_section="特定預り", margin_trade_section="制度"): # 証券コードをチェック if ticker is None: raise ValueError("証券コードを設定してください。ticker") # 注文ページへ遷移 driver.find_element_by_css_selector("img[title=取引]").click() driver.implicitly_wait(60) # 取引タイプ driver.find_element_by_id(id_=trade_type_dict.get(trade_type)).click() # 証券コード driver.find_element_by_name(name="stock_sec_code").send_keys(ticker) # 株数 driver.find_element_by_name(name="input_quantity").send_keys(unit) # 注文タイプ driver.find_elements_by_name(name="in_sasinari_kbn")[order_type_dict[order_type]].click() # 成行 if order_type == "指値": # 指値価格 if limit_order_price is None: raise ValueError("指値価格を設定してください。limit_order_price") else: driver.find_element_by_name(name="input_price").send_keys(limit_order_price) if order_type == "逆指値": # 逆指値タイプ driver.find_elements_by_name(name="gsn_sasinari_kbn")[stop_order_type_dict.get(stop_order_type)].click() # 逆指値トリガー価格 if stop_order_trigger_price is None: raise ValueError("逆指値のトリガー価格を設定してください。stop_order_trigger_price") else: driver.find_element_by_name(name="input_trigger_price").send_keys(stop_order_trigger_price) # 逆指値価格 if stop_order_type == "指値": if stop_order_price is None: raise ValueError("逆指値価格を設定してください。stop_order_type") else: driver.find_element_by_name(name="gsn_input_price").send_keys(stop_order_price) # 期間タイプ driver.find_elements_by_name(name="selected_limit_in")[period_type_dict.get(period_type)].click() # 期間指定 if period_type=="期間指定": if period_value is None and period_index is None: raise ValueError("期間を指定してください。period_value or period_index") else: # 期間指定の要素を取得 period_option_div = driver.find_element_by_name(name="limit_in") period_options = Select(period_option_div) # 期間を設定 if period_value is not None: period_value_list = [i.get_attribute("value") for i in period_options.options] if not period_value in period_value_list: raise ValueError("期間の値が存在しません。period_value\n指定可能日: {}".format(",".join(period_value_list))) else: period_options.select_by_value(period_value) if period_index is not None: period_options.select_by_index(period_index) # 預り区分 driver.find_elements_by_name(name="hitokutei_trade_kbn")[trade_section_dict.get(trade_section)].click() # 信用取引区分 driver.find_elements_by_name(name="payment_limit")[margin_trade_section_dict.get(margin_trade_section)].click() # 取引パスワード driver.find_element_by_id("pwd3").send_keys("yama3727") # 注文画面を省略 driver.find_element_by_id(id_="shouryaku").click() driver.find_element_by_css_selector("img[title=注文発注]").click() # 注文確認画面へ #driver.find_element_by_css_selector("img[title=注文確認画面へ]").click() # 注文確認 html = BeautifulSoup(driver.page_source, "html.parser") if "ご注文を受け付けました。" in html.text: print("注文が完了しました。") return 0 else: return 1
引数の大部分には初期値を設定し、最低限必要な設定は証券コードのみです。
証券コードのみを設定すると、設定した証券コードを100株で成行注文します。預り区分は特定預りで、期間は当日中です。
ここから何かしらの変更を加えて注文したい場合は、追加で引数を指定する必要があります。
いろいろな売買条件による使用例
使用例をいくつかご紹介します。
スクショで恐縮ですが、こんな感じでいろいろできます。
注文が完了すると「注文が完了しました」と表示されて、返り値が0になります。失敗すると返り値は1です。
注文内容にエラーがあると、ある程度検出できます。
そして注文内容にエラーがあると、ある程度検出ができるようになっています。
例えば、指値注文で価格設定がない場合は次のようにメッセージが出てきます。
「指値価格を設定してください。」とのメッセージが表示されて、その右に設定が必要な引数名が表示されます。
このようなエラー処理をできる限り実装したので、ある程度のエラーは自動で検出することができるようになっています。
まとめ
本記事では、「【Python】SeleniumでSBI証券から日本株を売買する方法」について解説しました。
PythonとSeleniumを利用すれば、SBI証券に自動ログイン(参考記事)して、そこから日本株を売買することも可能になります。
注文時に設定する細かい条件もプログラムを介して入力することができます。
本記事で紹介したコードを活用すれば日本株の売買を完全自動化することも可能です。
最後まで読んでくださり、ありがとうございました。