Life is Like a Boat

忘備録や経済、投資、プログラミングに関するメモやtipsなど

KABU+を使ってみた。csvのダウンロードからPandasで利用するまで。

駄犬さんの紹介でKABU+さんのスタンダードプランに申し込んでみました。

KABU+は株価データ・投資指標データ等の株式投資関連データを提供する情報サービスです。

KABU+からCSVをダウンロードする

KABU+サービスを申し込むとPaypalで決済したのちIDとパスワードが送られてきます。 Basic認証なのでプログラム的にダウンロードするにはヘッダーにIDとpasswordをbase64でエンコードしてやつをヘッダー名Authorizationの値としてつけて送らないといけません。 requestsHTTPBasicAuthを使うとその辺よしなにやってくれます。

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.

原因はこのレコードのせいでした。

f:id:nerimplo:20180705195611p:plain

7857 セキおそらく出合いがなくプライスが付かなかったのが4日分あったのでしょう。

対処としてやったのが、Timestampの欠損時にはKABU+のCSVのファイル名から日付を取得。このDatetimeを充てる、です。

これでIdをuniqueなprimary keyにすることができました。

SELECT * from stockpx2 where "Code" = 1333 order by "Timestamp"で結果を確認

f:id:nerimplo:20180705201859p:plain

これで準備は整った感じがあります。
Localにサーバー立ててlocalhost/service/api/history?code=1333みたいにRestfulなAPIを作って、適宜Pandas側からプライスを呼び出せるようにしてもいいかもしれません。