株価調整の計算方法とコード例【Python】

スポンサーリンク

株式分割や株式併合を実施した企業は株価が不連続になります。その株価を調整する計算方法を紹介します。調整後終値と呼ばれる金額もこの計算方法で求まります。

さて、分割・併合を繰り返して不連続になったチャートは、株価調整できれいに揃えることができます。

株価調整のイメージ

計算方法は簡単です。日付を過去にさかのぼって、2分割があればそこから株価を半分にします。また2分割があればさらに半分にします。こうして古い株価は4分の1になるわけですね。

しかしながら、これをプログラムに起こすと日付を見ながら係数を選んで乗じていくかたちになります。その関係でコードが少しだけ複雑になります。一律に同じ係数を乗じれば完了!だったらいいんですけれどもね。株価調整を1発でこなす計算式は無いようです。

それでは株価調整の手順と具体例です。後半でプログラムのコード例を紹介しています。

※ 東京証券取引所に株価データ購入について質問した時の記事はこちら

東証に株価データの購入プランと料金を質問しました

株価調整の手順

作業としては『分割日(効力発生日)』と『分割割合』をリストアップするところから始まります。これらは有価証券報告書や株式分割に関するお知らせに載っています。

  • 企業の開示情報から分割日分割割合をリストアップ
  • 分割割合から期間ごとの係数を計算
  • 株価の日付を見ながら係数を乗じる

ここでは、Yahoo!ファイナンスのチャートの下にある『分割情報』をメモして使う方法を紹介します。

株価調整の具体例

まず、分割情報をテキストファイルに貼り付けて保存します 。

[1:100](19/01/12)、[1:2](19/01/15)、[10:1](19/01/18)

(これらは説明用に作った架空のデータです。[10:1]は株式併合です。)

 

分割情報から分割日と分割割合を取得します。 分割日は降順に並べます。

分割日分割前分割後
2019/01/1810.1
2019/01/1512
2019/01/121100

 

期間ごとに係数を計算します。

分割日次回の分割日係数分割前分割後
2019/01/189999/12/311NoneNone
2019/01/152019/01/181010.1
2019/01/122019/01/15512
0001/01/012019/01/120.051100

 

株価を読み込みます。 日付は昇順に並べます。(こちらも架空のデータです)

日付始値高値安値終値
2019/01/1080000840007600081000
2019/01/1181000850007700082000
2019/01/12820860780830
2019/01/13830870790840
2019/01/14840880800850
2019/01/15425445405430
2019/01/16430450410435
2019/01/17435455415440
2019/01/184400460042004450
2019/01/194450465042504500

 

これらの日付を見ながら、株価に係数を乗じます。

日付始値高値安値終値係数
2019/01/1040004200380040500.05
2019/01/1140504250385041000.05
2019/01/1241004300390041505
2019/01/1341504350395042005
2019/01/1442004400400042505
2019/01/15425044504050430010
2019/01/16430045004100435010
2019/01/17435045504150440010
2019/01/1844004600420044501
2019/01/1944504650425045001

これで株価調整ができました。チャートは以下のようになります。

株価調整したチャート

とりあえず、株価調整ではいちいち日付を確認しながら掛け算するんだなってところがポイントです。

ここからは実際のコード例を紹介します。

株価調整プログラムのコード例

関数ごとに分割して紹介します。

メイン関数

main()関数です。分割情報の取得と係数計算を行ってから株価調整に入っています。

アジャスト(adjust)は調整、コエフィシエント(coefficient)は係数という意味です。

"""adjust_price.py"""

from os.path import join, dirname, basename
from csv import reader as csv_reader
from csv import writer as csv_writer
import datetime
datetime_strptime = datetime.datetime.strptime


def main():
    """メイン関数"""

    # 分割情報から分割日と分割割合を取得
    split_file = r'***\split_datas.txt'
    split_datas = get_split_datas(split_file)


    # 係数を計算
    coefficients = calc_coefficients(split_datas)


    # 株価を取得
    price_file = r'***\price.csv'
    price_datas = get_price_datas(price_file)


    # 株価に係数を乗じて株価調整
    adj_price_datas = adjust_prices(price_datas, coefficients)


    # 調整した株価を保存
    adj_price_file = join(
        dirname(price_file), 'adj_%s' % basename(price_file),
        )
    with open(
        adj_price_file, 'w', encoding='utf-8-sig', newline='',
        ) as f:
        w = csv_writer(f)
        w.writerows(adj_price_datas)
    return

def get_split_datas(file):
    ...

def calc_coefficients(datas):
    ...

def get_price_datas(file):
    ...

def adjust_prices(price_datas, coefficients):
    ...

if __name__ == '__main__':
    main()

分割日と分割割合を取得

分割情報のテキストファイルから分割日と分割割合を取得するところです。

def get_split_datas(file):
    """分割情報から分割日と分割割合を取得"""
    datas = []

    with open(file, mode='r', encoding='utf-8-sig') as f:
        for line in f:
            # データ例
            # '[1:100](19/01/12)、[1:2](19/01/15)、[10:1](19/01/18)'

            # 読点で分割
            for t in line.split('、'):

                # 末尾の改行を削除
                t = t.rstrip('\r\n')

                # 分割情報と分割日を分ける
                t = t.split('](')

                # 分割情報から数値を取得
                t_split = t[0].strip('[')
                t_split = t_split.split(':')
                t_split = [float(x) for x in t_split]

                # 分割日を取得
                t_date = datetime_strptime(t[1], '%y/%m/%d)')

                # リストに追加
                # datas: [0]分割日 [1]分割前 [2]分割後
                datas.append([t_date, t_split[0], t_split[1]])

    # リストを分割日の降順でソート
    datas.sort(key=lambda x:x[0], reverse=True)

    return datas

リストは分割日の降順に並べておきます。 これで次に紹介する係数リストも降順になります。これらは株価に係数を乗じるときのコードを簡単にする目的があります。

株価に乗じる係数を計算

分割日と分割割合のリストから係数を計算するところです。係数は株式分割のたびに変わりますので、期間に応じたかたちのリストに仕上げます。

def calc_coefficients(datas):
    """分割情報から株価に乗じる係数を計算する"""

    ######################################
    # 係数は、以下のように期間を分けて計算します。
    # (1) 最後の分割日から、西暦9999年12月31日の直前までの期間
    # (2) 前回の分割日から、最後に分割される日の直前までの期間
    # ...
    # (3) 西暦0001年01月01日から、最初に分割される日の直前までの期間


    ######################################
    # 初期設定

    labels = ['分割日', '次回の分割日', '係数', '分割前', '分割後']
    coefficients = []

    # 9999-12-31 23:59:59.999999
    datetime_max = datetime.datetime.max

    # 0001-01-01 00:00:00
    datetime_min = datetime.datetime.min

    # 一時変数
    t_split_date = None # 分割日
    t_coefficient = 1.0 # 係数
    t_befor = None # 分割前
    t_after = None # 分割後


    ######################################
    # (1) 最後の分割日から、西暦9999年12月31日の直前までの期間

    #    分割日      分割前  分割後
    (split_date, befor, after) = datas[0]

    coefficients.append([
        # 分割日     次回の分割日   係数           分割前    分割後
        split_date, datetime_max, t_coefficient, t_befor, t_after,
        ])

    # 一時変数を更新
    t_split_date = split_date
    t_coefficient = (befor / after)
    t_befor = befor
    t_after = after


    ######################################
    # (2) 前回の分割日から、最後に分割される日の直前までの期間
    # ...

    for (split_date, befor, after) in datas[1:]:
        coefficients.append([
            split_date, t_split_date, t_coefficient, t_befor, t_after,
            ])

        # 一時変数を更新
        t_split_date = split_date
        t_coefficient = t_coefficient * (befor / after)
        t_befor = befor
        t_after = after


    ######################################
    # (3) 西暦0001年01月01日から、最初に分割される日の直前までの期間

    coefficients.append([
        datetime_min, t_split_date, t_coefficient, t_befor, t_after,
        ])

    return (labels, coefficients)

coefficientsに入れた『分割前』と『分割後』はデバッグ用です。係数の正しさを確認するために残しています。株価調整がうまくいったらコードから取り除いて大丈夫です。

株価を取得

ファイルから株価データを読み込むところです。

def get_price_datas(file):
    """株価を取得"""

    with open(file, 'r', encoding='utf-8-sig') as f:
        # [0]日付 [1]始値 [2]高値 [3]安値 [4]終値

        # ラベル行を取得
        labels = next(csv_reader(f))

        # データ行を取得(型変換も行う)
        datas = []
        for c in csv_reader(f):
            datas.append([
                datetime_strptime(c[0], '%Y/%m/%d'),
                float(c[1].replace(',', '')) if c[1] else None,
                float(c[2].replace(',', '')) if c[2] else None,
                float(c[3].replace(',', '')) if c[3] else None,
                float(c[4].replace(',', '')) if c[4] else None,
                ])

    # 日付の昇順でソート
    datas.sort(key=lambda x:x[0], reverse=False)

    return (labels, datas)

csvモジュールで読み込んだデータは文字列なので、そこからdatetime型やfloat型に変換しています。

if文に変数だけ渡しているところは、空のセルを処理しています。if文に空文字列を渡すとFalse扱いになるので、それを使ってNoneを設定しています。

Noneの代わりにfloat('nan')のnan(ナン:Not a Number)を設定する方法も考えられます。Noneと数値の掛け算はエラーになりますが、nanと数値の掛け算はnanになります。とりあえずNoneでいいと思います。

replace()でカンマを削除していますが、これはfloat()がカンマを受けるとエラーになるためです。

株価は日付の昇順に並べておきます。株価に係数を乗じるときのコードを簡単にする目的があります。

株価に係数を乗じる

実際に株価を調整するところです。

def adjust_prices(price_datas, coefficients):
    """株価に係数を乗じて株価調整"""

    # ラベル行を分離
    price_labels = price_datas[0]
    prices_datas = price_datas[1]

    coefficient_labels = coefficients[0]
    coefficients = coefficients[1]

    # ラベル行を生成
    # ['日付', '始値', '高値', '安値', '終値'] + ['係数']
    labels = price_labels + coefficient_labels[2:3]


    datas = []
    datas.append(labels)

    # 株価リスト(日付の昇順)
    for (日付, 始値, 高値, 安値, 終値) in prices_datas:

        # 係数リスト(分割日の降順)
        for (分割日, 次回の分割日, 係数, 分割前, 分割後) in coefficients:
            if 日付 >= 分割日:
                # 分割日 以降の日付でbreak
                break

        # 株価に係数を乗じる
        datas.append([
            日付,
            None if 始値 is None else 始値 * 係数,
            None if 高値 is None else 高値 * 係数,
            None if 安値 is None else 安値 * 係数,
            None if 終値 is None else 終値 * 係数,
            係数,
            ])

    return datas

株価の日付に応じて、乗じる係数を変更しています。

もともと係数は『分割日 ≦ 日付 < 次回の分割日』で判定しながら乗じていたのですが、株価を昇順、係数を降順に並べておくことで、分割日との比較だけで済ませています。

(日付, 始値, 高値, 安値, 終値)のように変数をアンパックしているのは説明用です。このままでも動きますが、通常は for x in prices_datas: などとして、x[0]のように使います。

係数リストで使っているのは分割日係数だけです。ほかの要素は省略可能です。

以上で株価調整は完了します。

株価と業績の関係を見るときに役立つ

株価と業績を10年単位で見ようとすると、どうしても株価調整が必要になります。分割のたびに株価が小さくなっていくので、調整しないと上がっているのか下がっているのか分からないんですね。

調整済みの株価の推移に業績の推移を重ねることで、2者の関係がどの程度のものなのかを簡単に把握できるようになりました。

プログラミングの参考になれば幸いです。

タイトルとURLをコピーしました