株のシステムトレードをしよう - 1から始める株自動取引システムの作り方

株式をコンピュータに売買させる仕組みを少しずつ作っていきます。できあがってから公開ではなく、書いたら途中でも記事として即掲載して、後から固定ページにして体裁を整える方式で進めていきます。

アイデアメモ - CSVに調整後終値を付与(株式の分割と併合をまとめて扱う)

前回の振り返り

前回までで、株式併合に対応できるように、終値に対してかける比率を計算し、調整後終値を計算できるようにした。

how-to-make-stock-trading-system.dogwood008.com

今回の内容

株式併合だけでなく、分割もあわせて扱って、1つの DataFrame にする。

変更内容

主に、下記の点を修正している。

  • 株式分割・併合に両対応
  • 全体的に構成を変更した
  • カラム名を半角文字にした
    • 扱いやすいようにするため

adj_close.py

import sys
import re
import pandas as pd
from bs4 import BeautifulSoup
from typing import List

def get_adj_rate(paths_to_csv: List[str]) -> pd.DataFrame:
    def _convert_to_ratio(nl: str) -> float: 
        before, after = re.split('[→:]', nl.replace('株', ''))
        return float(after) / float(before)

    def _html2df(path_to_csv: str) -> pd.DataFrame:
        html = open(path_to_csv).read()

        soup = BeautifulSoup(html, 'html.parser')
        table = soup.find('table', {'class': 'tbl01'})
        rows = table.findAll('tr')
        csv = [
            [cell.get_text() for cell in row.findAll(['td', 'th'])]
            for row in rows
        ]
        df = pd.DataFrame(csv, columns=csv.pop(0)) \
                .rename(columns={'銘柄コード': 'code',  # 扱いやすいように半角にしておく
                                '銘柄名': 'name',
                                '併合比率': 'rate', '割当比率': 'rate',
                                '権利付最終日': 'from'})
        return df

    def _adj_rates(df_in_desc: pd.DataFrame) -> list:
        '''
        Parameters
        ----------------------
        df_in_desc: pd.DataFrame
            日時降順でソートし与えること。
        '''
        adj_rates_in_each_codes: dict = {}
        if sys.version_info.major == 3 and sys.version_info.minor >= 8:
            pass
            # 動作未検証:
            # return [adj_rates_in_each_codes[code] := adj_rates_in_each_codes.get(code, 1.0) / rate
            #         for code, rate in df_in_desc[['code', 'rate']].values]
        else:
            def calc_adj_rates(code: str, rate: float):
                adj_rates_in_each_codes[code] = adj_rates_in_each_codes.get(code, 1.0) / rate
                return adj_rates_in_each_codes[code]
            return [calc_adj_rates(code, rate)
                for code, rate in df_in_desc[['code', 'rate']].values]

    def _reverse(df: pd.DataFrame) -> pd.DataFrame:
        # https://stackoverflow.com/a/20444256
        return df.iloc[::-1]

    dfs = pd.concat([_html2df(path_to_csv) for path_to_csv in paths_to_csv]) \
            .sort_values(['code', 'from'], ascending=False)
    dfs['rate'] = dfs['rate'].apply(_convert_to_ratio)
    dfs['adj_rate'] = _adj_rates(dfs)
    return _reverse(dfs)

実行

df = get_adj_rate(['heigou.html', 'bunkatsu.html'])
df[df['from'] == '2020/10/28']

出力

2020年10月28日 の終値から適用されるもの

2020年10月28日の終値にかける比率を adj_rate に格納
2020年10月28日の終値にかける比率を adj_rate に格納

分割と併合を繰り返した銘柄例 [8928] 穴吹興産(株)

df[df['code'] == '8928']

赤色は分割、青色は併合を表す
赤色は分割、青色は併合を表す

確認検証

では、この算出した終値調整比が正しいかを Yahoo! ファイナンスの提供する値と比較することで検証する。

ここでは、 [9143] SGホールディングス(株) を使用する。

まず、先ほど作成した DataFrame から、from: 2020年10月28日 からの終値調整比 adj_rate0.5 とわかる。

終値調整比 adj_rate は 0.5
終値調整比 adj_rate0.5

次に、 Yahoo! ファイナンスの時系列データにアクセスし、 2020年10月28日 の終値 A と調整後終値 B を控える。

Yahooファイナンスの時系列データ
Yahooファイナンスの時系列データ

B の値が A * adj_rate と等しければ、正しく計算できたと言える。

実際のデータを使用すると、権利上の問題がある可能性があるので、仮に A10000 円だったとして考える。この時、調整後終値 BA: 10000 * adj_rate: 0.5 = 5000 円となり、これは Yahoo! ファイナンス上の表示とも一致する。

よって上記より、少なくとも 2020年10月28日から適用される銘柄コード 9143 については、正しく計算できたと言える。

次回

次回は、この終値調整比を使用して、 KABU+ からダウンロードしたCSVに調整後終値を付与する。

(C) 2020 dogwood008 禁無断転載 不許複製 Reprinting, reproducing are prohibited.