駄犬さんの紹介でKABU+さんのスタンダードプランに申し込んでみました。
はい、KABU+使ってます。Webからクローリングしてくる手間を考えると、楽ができて便利ですよー
— 駄犬 (@daken_in_market) July 4, 2018
KABU+は株価データ・投資指標データ等の株式投資関連データを提供する情報サービスです。
KABU+からCSVをダウンロードする
KABU+サービスを申し込むとPaypalで決済したのちIDとパスワードが送られてきます。
Basic認証なのでプログラム的にダウンロードするにはヘッダーにIDとpasswordをbase64でエンコードしてやつをヘッダー名Authorizationの値としてつけて送らないといけません。
requests
のHTTPBasicAuth
を使うとその辺よしなにやってくれます。
def retrieve_csv_file(url): res = requests.get(url, auth=HTTPBasicAuth('YOUR_ID', 'YOUR_PASSWORD')) data = res.content.decode('shift-jis') return data
この戻り値をcsvにします。
def to_csv(data, file=None): if file: with open(file, 'w', encoding='utf-8') as f: writer = csv.writer(f) reader = csv.reader(data.splitlines()) for row in reader: writer.writerow(row)
あとはこれらのメソッドを実行するだけ。カレントディレクトリにtest.csvが作成されているはずです。
if __name__ == '__main__': URL = 'https://csvex.com/kabu.plus/csv/japan-all-stock-prices-2/daily/japan-all-stock-prices-2_20180104.csv' data = retrieve_csv_file(URL) to_csv(data, './test.csv')
Pandasで利用する
KABU+からダウンロードしたcsvのヘッダーは日本語なので後々の処理も考えてdataframeのカラム変換用のマッピングを作ります。
mapping = {'SC': 'Code', '名称': 'Name', '市場': 'Market', '業種': 'Industry', '日時': 'Timestamp', '株価': 'Price', '前日比': 'Change', '前日比(%)': 'ChangeInPercent', '前日終値': 'PreviousClosePx', '始値': 'Open', '高値': 'High', '安値': 'Low', 'VWAP': 'VWAP', '出来高': 'Volume', '出来高率': 'VolumeInPercent', '売買代金(千円)': 'TradingVolume', '時価総額(百万円)': 'MarketCap', '値幅下限': 'LowerRange', '値幅上限': 'UpperRange', '高値日付': 'HighDate', '年初来高値': 'YTDHigh', '年初来高値乖離率': 'DeviationFromYTDHigh', '安値日付': 'LowDate', '年初来安値': 'YTDLow', '年初来安値乖離率': 'DeviationFromYTDLow'}
あと後処理しやすいよう、データ型をそろえてあげます。
non_double_precisions = ['Code', 'Name', 'Market', 'Timestamp', 'Industry', 'HighDate', 'LowDate'] df = pd.read_csv(file) df = df.rename(columns=mapping) df['Timestamp'] = list( map(lambda x: datetime.strptime(x, '%Y/%m/%d %H:%M') if type(x) == str and x != '-' else x, df['Timestamp'])) df['HighDate'] = list( map(lambda x: datetime.strptime(x, '%Y/%m/%d') if type(x) == str and x != '-' else x, df['HighDate'])) df['LowDate'] = list( map(lambda x: datetime.strptime(x, '%Y/%m/%d') if type(x) == str and x != '-' else x, df['LowDate'])) df = df.replace('-', np.nan) for column_type in mapping.values(): if column_type not in non_double_precisions: df[column_type] = list(map(lambda x: float(x), df[column_type])) df = df.set_index('Code')
これでdataframeが出来上がったので、いろいろ加工できます。
データベースに突っ込む
ここから先、時系列のデータをDBに突っ込んで必要な時にDBからPandasで読み込むのがスマートなやり方ではないでしょうか。
その方法としていくつかあると思います。考えつくのが
PostgreSQLのCOPYコマンドを使ってKABU+からダウンロードしたcsvを突っ込む
PythonのORMライブラリを使ってcsvを読み込み、dbにinsert
Pandasでcsvを読み込んだものをそのまま、to_sqlする
かと思います。私は3番目のやり方にしました。
コードとTimestampの組み合わせでUniqueなIDをつくるとTimestampが欠損しているケースがあり、こんな感じでDBから怒られます。
ERROR: could not create unique index "ix_stockpx2_id" Detail: Key (id)=(5602540528120821380) is duplicated.
原因はこのレコードのせいでした。
7857 セキ
おそらく出合いがなくプライスが付かなかったのが4日分あったのでしょう。
対処としてやったのが、Timestampの欠損時にはKABU+のCSVのファイル名から日付を取得。このDatetimeを充てる、です。
これでIdをuniqueなprimary keyにすることができました。
SELECT * from stockpx2 where "Code" = 1333 order by "Timestamp"
で結果を確認
これで準備は整った感じがあります。
Localにサーバー立ててlocalhost/service/api/history?code=1333みたいにRestfulなAPIを作って、適宜Pandas側からプライスを呼び出せるようにしてもいいかもしれません。