【第4回】無尽蔵から自動で株価データを取得し株価データベースを更新
December 11, 2020
株価サーバー python無一物中Project記事一覧
- 【第1回】株価サーバーの構築の概要
- 【第2回】無尽蔵から株価データの一括取得
- 【第3回】無尽蔵株価データから銘柄毎の時系列データを生成
- 【第4回】無尽蔵から自動で株価データを取得し株価データベースを更新(本記事)
- 【第5回】出来ず株価の補填と株式分割を自動調整してチャート表示
- 【第6回】mplfinanceを使用してテクニカルチャート表示
- 【第7回】テクニカル指標による銘柄スクリーニング
- 【第8回】株価チャート配信サーバー
- 【第9回】チャート形状の認識
はじめに
毎日の日本株の株価データを公開している無尽蔵というサイトがあります。 自分で株価データを持つために、ここから、毎日定時に、その日の株価一覧の取得を試みます。なお、稼働させるには、python3が使える環境が必要ですが、本記事では、python3実行環境をdocker-composeで用意します。従って、本記事を追試する場合は、python3実行環境は特に必要なく、docker, docker-composeが稼働する環境が必要になります。
無尽蔵では、以下の形式のcsvファイルで毎日の株価一覧が公開されています。
2022/7/1,1001,10,1001 日経225,26460,26531,25841,25936,1349730000,東証
2022/7/1,1002,10,1002 TOPIX,1875,1881,1836,1845,1349730000,東証
2022/7/1,1301,11,1301 極洋,3490,3490,3435,3445,16900,東証P
2022/7/1,1305,10,1305 ダイワTPX,1996.5,2008,1962,1970.5,415170,東証
2022/7/1,1306,10,1306 TOPIX投,1976,1987,1938.5,1946.5,3697750,東証
...
2022/7/1,1712,30,1712 ダイセキ環境ソリューション,0,0,0,0,0,名証
2022/7/1,1712,11,1712 ダイセキ環境ソリューション,790,836,788,826,472800,東証P
...
2022/7/1,9996,12,9996 サトー商会,1254,1256,1254,1254,700,東証S
2022/7/1,9997,11,9997 ベルーナ,747,756,739,743,252700,東証P
無尽蔵サイトから株価データを取得するpythonスクリプト
無尽蔵サイトのデータ取得のurlは、例えば2022/07/01の株価データなら、
http://mujinzou.com/k_data/2022/22_07/T220701.zip
以下のスクリプトは、当日の株価データを取得し、zipを展開するスクリプトの例です。
# -*- coding: utf-8 -*-
import urllib.request
import zipfile
import datetime
import os
import logging
import sys
def download_csv_zip(output_dir, year, month, day):
base_url = 'http://mujinzou.com/k_data/'+str(year)+'/'
subdir = '{0:02d}_{1:02d}/'.format(year-2000, month)
filename = 'T20{0:02d}{1:02d}.zip'.format(month, day)
url = base_url + subdir + filename
now = datetime.datetime.now()
try:
urllib.request.urlretrieve(url, filename)
with zipfile.ZipFile(filename, 'r')as zf:
zf.extractall(output_dir)
os.remove(filename)
logging.info(str(now)+' success to get file: '+filename)
except urllib.error.HTTPError:
logging.error(str(now)+' error')
def main():
args = sys.argv
if len(args)==2:
output_dir = args[1]
if output_dir[-1]!='/':
output_dir = output_dir + '/'
else:
output_dir = './'
logging.basicConfig(level=logging.INFO)
today = datetime.date.today()
download_csv_zip(output_dir, today.year, today.month, today.day)
if __name__ == '__main__':
main()
使い方の例:
$ python get_todays_csv_zip_from_muzinzo.py ~/tmp
引数としてダウンロード先のディレクトリを指定できます。指定しなければ、ダウンロード先はカレントディレクトリになります。
サイトに当日の株価データが置かれるには概ね夕方以降であることを留意してください。つまり、このスクリプトで当日の株価データが取得できるのは、当日の夕方から深夜0時までです。
docker-composeでpythonスクリプトを動かす
pythonスクリプトの定時実行の布石として、docker-composeでpythonスクリプトを動かします。
docker-compose.ymlとDockerfileは、以下から借用。
https://qiita.com/reflet/items/4b3f91661a54ec70a7dc dockerで簡易にpython3の環境を作ってみる
ディレクトリ構成
python3/
Dockerfile
docker-compose.yml
opt/
get_todays_csv_zip_from_muzinzo.py ※1
import_todays_data.py ※2
exec_cmd.sh ※3
fetch.log ※4
update.log ※5
data/ ※6
1000/
1001.csv
...
2000/
...
9000/
9001.csv
...
Tyymmdd.csv ※7
- ※1; 無尽蔵サイトから当日の株価を取得するスクリプト
- ※2; 当日の株価を銘柄毎の時系列データに変換するスクリプト
- ※3; 毎日定時に一連の処理を実行するスクリプト
- ※4; 株価取得のログ
- ※5; 変換のログ
- ※6; 時系列データが入った株価データベース
- ※7; 取得した株価データ
Dockerfileはpython3を動かすためのイメージです
FROM python:3.9-slim
USER root
RUN apt-get update
RUN apt-get -y install locales && \
localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm
ENV DEBIAN_FRONTEND noninteractive
ENV DEBCONF_NOWARNINGS yes
RUN apt-get install -y vim less cron
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
#RUN echo '0 20 * * 1-5 root /root/opt/exec_cmd.sh' >> /etc/crontab
#CMD ["cron", "-f"]
docker-composeファイルは以下。volumeとして、ホストの/etc/group, /etc/passwdをマウントしていますが、これはスクリプトで生成するファイルのパーミッションがroot:rootになってしまうので、これをホストのアカウントの権限に変えるため、アカウントを参照するためのものです。
version: '3'
services:
python3:
restart: always
build: .
container_name: 'python3'
working_dir: '/root/opt'
tty: true
volumes:
- ./opt:/root/opt
- /etc/group:/etc/group:ro
- /etc/passwd:/etc/passwd:ro
起動
$ docker-compose up -d --build
pythonスクリプトの実行
$ docker-compose exec python3 python /root/opt/get_todays_csv_zip_from_muzinzo.py
これでoptディレクトリ内に、当日の株価データ「Tyymmdd.csv」が生成されます。
pythonスクリプトを毎日定時実行する
先のDockerfileの末尾2行をコメントアウトします。 これは、cronにより、月曜日~金曜日の20時に、スクリプトexec_cmd.shを実行するための設定です。
...
RUN echo '0 20 * * 1-5 root /root/opt/exec_cmd.sh' >> /etc/crontab
CMD ["cron", "-f"]
毎日定時実行するスクリプトは以下。スクリプトで生成するファイルのオーナーとパーミッションがroot:rootになるのを無理やり変更しています。本来ならもっとスマートにアカウント名を環境変数でコンテナに渡して云々とかすべきなんでしょうが、それはやりたい方がやったらいいだけで、これでお手軽で十分です。カッコ悪いですが。
#!/bin/bash
/usr/local/bin/python /root/opt/get_todays_csv_zip_from_muzinzo.py /root/opt >> /root/opt/fetch.log 2>&1
chmod a+rw /root/opt/fetch.log
chown nobody:nobody /root/opt/fetch.log
chmod a+rw /root/opt/daily_data/*.csv
chown nobody:nobody /root/opt/daily_data/*.csv
実行権限を忘れずに
$ chmod a+x exec_cmd.sh
ここまで用意したら、常時稼働サーバで起動しておく。
$ docker-compose up -d --build
以上で株価データを無尽蔵から毎日定時に取得する環境が整いました。
次節では、取得した株価データで、自前の株価データベースを更新する機能を追加します。
株価データベースの更新
自前の株価データベースを無尽蔵サイトから一括取得した株価データを元に生成する例を、こちらの記事で解説しました。
無尽蔵サイトから取得した当日の株価データを使用して、この株価データベースを自動で日々更新する機能を追加していきます。 今回想定する株価データベースは、ファイルシステム上の簡易なもので、以下のような構成のファイルです。dataディレクトリには、証券コードの1000の桁毎のサブディレクトリを切り、その中に、銘柄毎の株価日足時系列データのcsvファイルが置かれるものとします。
working_root/
muzinzo_data/
Tyymmdd.csv
...
data/
1000/
1001.csv
1002.csv
...
2000/
...
9000/
9001.csv
...
以下はスクリプト例です。
import datetime
import os
import sys
import logging
def add_data(data_dir, code, date_str, cv1, cv2, cv3, cv4, cvc):
code_dir = code[0]+'000'
out_path = data_dir + code_dir + '/' + code + '.csv'
line = date_str+','+cv1+','+cv2+','+cv3+','+cv4+','+str(int(float(cvc)*1000))+'\n'
with open(out_path,'a') as f:
f.write(line)
def import_daily_data(daily_file, data_dir):
now = datetime.datetime.now()
filename = os.path.basename(daily_file)
yy = 2000 + int(filename[1:3])
mm = int(filename[3:5])
dd = int(filename[5:7])
date_str = '{0:4d}/{1:02d}/{2:02d}'.format(yy, mm, dd)
with open(daily_file, mode='rb') as fd:
lines = fd.readlines()
for i, line in enumerate(lines):
try:
line = line.decode('cp932')
except:
logging.error('{} error file {}: line {}'.format(str(now), daily_file, i))
continue
line = line.rstrip()
line_list = line.split(',')
if len(line_list)==10:
if len(line_list[1])==4:
code = str(line_list[1])
name = line_list[3]
market = line_list[9]
if '名証' in market:
next_line = lines[i+1]
next_line = next_line.decode('cp932')
next_line_list = next_line.split(',')
next_code = str(next_line_list[1])
if next_code!=code:
add_data(data_dir, code, date_str, line_list[4], line_list[5], line_list[6], line_list[7], line_list[8])
else:
add_data(data_dir, code, date_str, line_list[4], line_list[5], line_list[6], line_list[7], line_list[8])
else:
logging.error('{} error file {}: line {} : {}'.format(str(now), daily_file, i, line))
logging.info(str(now)+' success to update by: '+daily_file)
def main():
logging.basicConfig(level=logging.INFO)
args = sys.argv
if len(args)==3:
if os.path.isdir(args[1]):
input_dir = args[1]
if input_dir[-1]!='/':
input_dir = input_dir + '/'
today = datetime.date.today()
filename = 'T{0:02d}{1:02d}{2:02d}.csv'.format(today.year-2000, today.month, today.day)
daily_file = input_dir + filename
else:
daily_file = args[1]
if os.path.isdir(args[2]):
data_dir = args[2]
if data_dir[-1]!='/':
data_dir = data_dir + '/'
else:
return
import_daily_data(daily_file, data_dir)
if __name__ == '__main__':
main()
使用例
$ python import_todays_data.py /full/pathof/working_root/muzinzo_data /full/pathof/working_root/data
第1引数で無尽蔵サイトから取得したファイルが置かれたディレクトリをフルパスで指定し、第2引数で株価データベースのディレクトリをフルパスで指定します。
このスクリプトを、無尽蔵サイトから当日の株価データを取得した直後に引き続き実行させるには、先のcron実行スクリプトexec_cmd.shを次のように修正します。
#!/bin/bash
/usr/local/bin/python /root/opt/get_todays_csv_zip_from_muzinzo.py /root/opt >> /root/opt/fetch.log 2>&1
chmod a+rw /root/opt/fetch.log
chown nobody:nobody /root/opt/fetch.log
chmod a+rw /root/opt/daily_data/*.csv
chown nobody:nobody /root/opt/daily_data/*.csv
/usr/local/bin/python /root/opt/import_todays_data.py /root/opt /root/opt/data >> /root/opt/update.log 2>&1
chmod a+rw /root/opt/update.log
chown nobody:nobody /root/opt/update.log
以上で、毎日自動で無尽蔵サイトから当日の株価データを取得した後株価データベースを更新する環境が整いました。
まとめ
無一物中Project第4回では、毎日自動で無尽蔵サイトから当日の株価データを取得した後、株価データベースを更新するdocker-composeによる環境構築方法を紹介しました。