Created
January 29, 2018 23:47
-
-
Save seventhsense/1c67c82e147cfaf119e64305cb9a9784 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
"""a stock order via SBI Securities. | |
"order_sbisec.py" is released under the MIT License | |
Copyright (c) 2017 Akihiro Yamamoto | |
__author__ = "Akihiro Yamamoto <ak1211@mail.ak1211.com>" | |
__version__ = "0.5.2" | |
__date__ = "Oct 2017" | |
""" | |
from abc import ABCMeta | |
from time import sleep | |
import lxml.html | |
import requests | |
import traceback | |
WAITING_TIME = 0.3 | |
class Securities (metaclass=ABCMeta): | |
"""A securities provider.""" | |
def login(self): | |
"""Login the website.""" | |
pass | |
def get_top_page(self): | |
"""Get top page on website.""" | |
pass | |
def logout(self): | |
"""Logout the website.""" | |
pass | |
def portfolio_assets(self): | |
"""My portfolio.""" | |
pass | |
def stocks_sell_order(self, code, quantity, price): | |
"""Sell order.""" | |
pass | |
class SBISecurities(Securities): | |
"""SBI securities provider.""" | |
def __init__(self, uid, upass, tpass, userAgent): | |
"""Init this instance.""" | |
self.userID = uid | |
self.userPassword = upass | |
self.tradePassword = tpass | |
self.defHeaders = { | |
'Accept': 'text/html', | |
'User-agent': userAgent | |
} | |
self.session = requests.session() | |
self.siteRoot = None | |
def login(self): | |
"""Login the website.""" | |
def the_first_post(htmltext): | |
custom = { | |
'JS_FLG': '1', | |
'BW_FLG': 'chrome,56', | |
'ACT_login.x': '39', | |
'ACT_login.y': '26', | |
'user_id': self.userID, | |
'user_password': self.userPassword | |
} | |
# htmlのルート要素 | |
root = lxml.html.fromstring(htmltext) | |
form_inputs = root.xpath('//form[@name="form_login"]//input') | |
# ログインフォームのinput要素 | |
inputs = {x.get('name'): x.get('value') for x in form_inputs} | |
# ログイン情報を用意する | |
del inputs['ACT_login'] | |
inputs.update(custom) | |
# ログイン情報を送信する(postで) | |
sleep(WAITING_TIME) | |
r = self.session.post('https://www.sbisec.co.jp/ETGate', | |
data=inputs, headers=self.defHeaders) | |
return r | |
def the_second_post(htmltext): | |
# 受け取ったページのformSwitchフォームを取り出す | |
root = lxml.html.fromstring(htmltext) | |
form = root.xpath('//form[@name="formSwitch"]') | |
form_attribute = form[0].attrib | |
form_inputs = root.xpath('//form[@name="formSwitch"]//input') | |
inputs = {x.get('name'): x.get('value') for x in form_inputs} | |
# formSwitchフォームの内容をそのまま送信する | |
act = form_attribute.get('action') | |
sleep(WAITING_TIME) | |
r = self.session.post(act, data=inputs, headers=self.defHeaders) | |
return r | |
# | |
# ログイン | |
r = self.session.get('https://www.sbisec.co.jp/ETGate', | |
headers=self.defHeaders) | |
if r.status_code == requests.codes.ok: | |
r = the_first_post(r.text) | |
if r.status_code == requests.codes.ok: | |
r = the_second_post(r.text) | |
if r.status_code == requests.codes.ok: | |
r.encoding = r.apparent_encoding | |
self.siteRoot = lxml.html.fromstring(r.text) | |
return r | |
def get_top_page(self): | |
"""Get top page on website.""" | |
return self.siteRoot | |
def logout(self): | |
"""Logout the website.""" | |
# a要素の子要素でalt属性が"ログアウト"のページを得る | |
return self._request_at_GET_method(self.siteRoot, | |
'//a[*[contains (@alt,"ログアウト")]]') | |
def fetch_portfolio_page(self): | |
"""Fetch a 'portfolio' page on website.""" | |
# a要素の子要素でalt属性が"ポートフォリオ"のページを得る | |
return self._request_at_GET_method(self.siteRoot, | |
'//a[*[contains(@alt,"ポートフォリオ")]]') | |
def portfolio_assets(self): | |
"""My portfolio.""" | |
# a要素の子要素でalt属性が"口座管理"のページを得る | |
xdoc = self._request_at_GET_method(self.siteRoot, | |
'//a[*[contains(@alt,"口座管理")]]') | |
return self._request_at_GET_method(xdoc, '//area[@title="保有証券"]') | |
def stocks_sell_order(self, code, quantity, price): | |
"""Sell order.""" | |
orderFormInputs = { | |
# "取引" ラジオボタン | |
# 0 -> 現物買 | |
# 1 -> 現物売 | |
# 2 -> 信用新規買 | |
# 3 -> 信用新規売 | |
'trade_kbn': '1', | |
# 銘柄コード | |
'stock_sec_code': code, | |
# "市場" セレクトボックス | |
# " " -> 当社優先市場/SOR | |
# "TKY" -> 東証 | |
# "NGY" -> 名証 | |
# "FKO" -> 福証 | |
# "SPR" -> 札証 | |
'input_market': ' ', | |
# 株数 | |
'input_quantity': quantity, | |
# "価格" ラジオボタン | |
# " " -> 指値 | |
# "N" -> 成行 | |
# "G" -> 逆指値 | |
'in_sasinari_kbn': ' ', | |
# | |
# 指値の"条件" セレクトボックス | |
# | |
# " " -> 条件なし | |
# "Z" -> 寄指(Y) | |
# "I" -> 引指(H) | |
# "F" -> 不成(F) | |
# "P" -> IOC指(I) | |
'sasine_condition': ' ', | |
# | |
# 成行の"条件" セレクトボックス | |
# | |
# "N" -> 条件なし | |
# "Y" -> 寄成(Y) | |
# "H" -> 引成(H) | |
# "O" -> IOC成(I) | |
'nariyuki_condition': 'N', | |
# | |
# 逆指値 | |
# | |
# 現在値が | |
'input_trigger_price': '', # 円 | |
# "以上" "以下" セレクトボックス | |
# "0" -> 以上 | |
# "1" -> 以下 | |
'input_trigger_zone': 1, # になった時点で | |
# 逆指値の"指値" "成行" ラジオボタン | |
# " " -> 指値 | |
# "N" -> 成行 | |
'gsn_sasinari_kbn': ' ', | |
# | |
# 逆指値の"指値" 条件セレクトボックス | |
# " " -> 条件なし | |
# "I" -> 引指(H) | |
# "F" -> 不成(F) | |
'gsn_sasine_condition': ' ', | |
# 逆指値の"指値" 値段: | |
'gsn_input_price': '', # 円で執行 | |
# | |
# 逆指値の"成行" 条件セレクトボックス | |
# "N" -> 条件なし | |
# "H" -> 引成(H) | |
'gsn_nariyuki_condition': 'N', # で執行 | |
# | |
# 値段 | |
'input_price': price, | |
# 期間 | |
# "this_day" -> 当日中 | |
# "kikan" -> 期間指定 | |
'selected_limit_in': 'this_day', | |
# 預り区分 | |
# "1" -> 一般預り | |
# "0" -> 特定預り | |
'hitokutei_trade_kbn': '0', | |
# 信用取引区分 | |
# "6" -> 制度 | |
# "9" -> 一般 | |
# "D" -> 日計り | |
'payment_limit': '6', | |
# 取引パスワード | |
'trade_pwd': self.tradePassword, | |
# 注文確認画面を省略 | |
'skip_estimate': 'on', | |
# 注文発注 | |
'ACT_place': '注文発注' | |
} | |
return self._stocks_buy_or_sell_order(orderFormInputs) | |
def _stocks_buy_or_sell_order(self, formInputs): | |
"""Buy or sell order.""" | |
# a要素の子要素でalt属性が"取引"のページを得る | |
root = self._request_at_GET_method(self.siteRoot, | |
'//a[*[contains(@alt,"取引")]]') | |
# フォームのinput要素 | |
inputs = {x.get('name'): x.get('value') | |
for x in root.xpath('//form[@name="FORM"]//input')} | |
# 期間指定セレクトボックスのoption | |
limit_in_options = root.xpath('//select[@name="limit_in"]//option') | |
options = map(lambda x: x.get('value'), limit_in_options) | |
# 入力情報を用意する | |
# 期間指定セレクトボックスのoptionはとりあえず先頭の物を選択する | |
inputs["limit_in"] = list(options)[0] | |
del inputs['ACT_estimate'] | |
del inputs['ACT_order'] | |
del inputs[None] | |
inputs.update(formInputs) | |
# 注文情報を送信する(postで) | |
sleep(WAITING_TIME) | |
r = self.session.post('https://site2.sbisec.co.jp/ETGate', | |
data=inputs, headers=self.defHeaders) | |
r.encoding = r.apparent_encoding | |
if r.status_code != requests.codes.ok: | |
r.raise_for_status() | |
return lxml.html.fromstring(r.text) | |
def _request_at_GET_method(self, xdoc, xp): | |
"""Request on website.""" | |
# リンクを取り出す | |
form = xdoc.xpath(xp) | |
link = 'https://site2.sbisec.co.jp' + form[0].get('href') | |
sleep(WAITING_TIME) | |
r = self.session.get(link, headers=self.defHeaders) | |
if r.status_code != requests.codes.ok: | |
r.raise_for_status() | |
r.encoding = r.apparent_encoding | |
return lxml.html.fromstring(r.text) | |
if __name__ == '__main__': | |
def row2list(tr): | |
"""<td>text</td> to list.""" | |
tds = list(tr) | |
return [w.text_content().replace(u'\xa0', ' ').strip() for w in tds] | |
sec = SBISecurities('enter user name here', | |
'enter password here', | |
'enter trade password here', | |
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' | |
'AppleWebKit/537.36 (KHTML, like Gecko) ' | |
'Chrome/56.0.2924.87 Safari/537.36') | |
r = sec.login() | |
if r.status_code == requests.codes.ok: | |
try: | |
# | |
print('ログイン後のホームページ') | |
tp = sec.get_top_page() | |
contents = tp.xpath('//form/table[3]/tr/td/table/tr') | |
w = list(map(row2list, contents)) | |
print(w) | |
# "ポートフォリオ"ページ | |
print('"ポートフォリオ"ページ') | |
pf = sec.fetch_portfolio_page() | |
trs = pf.xpath('/html/body/div[3]/div' | |
'/table/tr/td/table[4]/tr[2]/td/table/tr') | |
matrix = list(map(row2list, trs)) | |
print(matrix) | |
# 保有証券資産 | |
print('保有証券資産') | |
myStocks = sec.portfolio_assets() | |
trs = myStocks.xpath('//form/table[2]/tr[1]/td[2]' | |
'/table[6]/tr/td/table/tr') | |
tr = list(map(row2list, trs)) | |
print(tr) | |
# 注文 | |
print('注文') | |
so = sec.stocks_sell_order(4689, 100, 500) | |
w = so.xpath('/html/body/div/table/*') | |
print(w[0].text_content().split()) | |
except requests.HTTPError: | |
print('失敗しました。') | |
print(traceback.format_exc()) | |
finally: | |
# ログアウト | |
print('ログアウト') | |
root = sec.logout() | |
m = root.xpath('string(//div[@class="alC"]/.)') | |
print(m.split()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment