株式分割や株式併合を実施した企業は、株価が不連続になりました。
その株価を調整する計算方法を紹介します。
調整後終値と呼ばれる金額も、この計算方法で求まりました。
さて、分割・併合を繰り返して不連続になったチャートは、株価調整できれいに揃えることができます。
株価調整のイメージ

計算方法は簡単でした。
日付を過去にさかのぼっていって、2分割があれば、そこから株価を半分にします。
再び2分割があれば、さらに半分にします。
こうして、古い株価が4分の1になるわけですね。
しかしながら、これをプログラムに起こしたら、日付を見ながら係数を選んで乗じていくかたちになりました。
その関係で、Python コードが少しだけ複雑になりました。
『一律に同じ係数を乗じれば完了!』だったら良かったんですけれどもね。
株価調整を1発でこなす計算式は無いようでした。
それでは、株価調整の手順と具体例です。
後半にプログラムのコード例を書きました。
※ 東京証券取引所に株価データ購入について質問した時の記事はこちら
株価調整の手順
作業としては、『分割日(効力発生日)』と『分割割合』をリストアップするところから始まります。
これらは、『有価証券報告書』や『株式分割に関するお知らせ』に載っていました。
- 企業の開示情報から分割日と分割割合をリストアップ
- 分割割合から期間ごとの係数を計算
- 株価の日付を見ながら係数を乗じる
ここでは、Yahoo!ファイナンスのチャートの下にある『分割情報』をメモして使う方法を紹介します。
株価調整の具体例
まず、分割情報をテキストファイルに貼り付けて保存します 。
[1:100](19/01/12)、[1:2](19/01/15)、[10:1](19/01/18)
(これらは説明用に作った架空のデータです。[10:1]は株式併合です。)
分割情報から『分割日』と『分割割合(分割前 ⇒ 分割後)』を取得します。 分割日は降順に並べます。
分割日 | 分割前 | 分割後 |
---|---|---|
2019/01/18 | 1 | 0.1 |
2019/01/15 | 1 | 2 |
2019/01/12 | 1 | 100 |
期間ごとに『係数』を計算します。
分割日 | 次回の分割日 | 係数 | 分割前 | 分割後 |
---|---|---|---|---|
2019/01/18 | 9999/12/31 | 1 | None | None |
2019/01/15 | 2019/01/18 | 10 | 1 | 0.1 |
2019/01/12 | 2019/01/15 | 5 | 1 | 2 |
0001/01/01 | 2019/01/12 | 0.05 | 1 | 100 |
『株価(始値、高値、安値、終値)』を読み込みます。 日付は昇順に並べます。(こちらも架空のデータです)
日付 | 始値 | 高値 | 安値 | 終値 |
---|---|---|---|---|
2019/01/10 | 80000 | 84000 | 76000 | 81000 |
2019/01/11 | 81000 | 85000 | 77000 | 82000 |
2019/01/12 | 820 | 860 | 780 | 830 |
2019/01/13 | 830 | 870 | 790 | 840 |
2019/01/14 | 840 | 880 | 800 | 850 |
2019/01/15 | 425 | 445 | 405 | 430 |
2019/01/16 | 430 | 450 | 410 | 435 |
2019/01/17 | 435 | 455 | 415 | 440 |
2019/01/18 | 4400 | 4600 | 4200 | 4450 |
2019/01/19 | 4450 | 4650 | 4250 | 4500 |
これらの日付を見ながら、株価に『係数』を乗じます。
日付 | 始値 | 高値 | 安値 | 終値 | 係数 |
---|---|---|---|---|---|
2019/01/10 | 4000 | 4200 | 3800 | 4050 | 0.05 |
2019/01/11 | 4050 | 4250 | 3850 | 4100 | 0.05 |
2019/01/12 | 4100 | 4300 | 3900 | 4150 | 5 |
2019/01/13 | 4150 | 4350 | 3950 | 4200 | 5 |
2019/01/14 | 4200 | 4400 | 4000 | 4250 | 5 |
2019/01/15 | 4250 | 4450 | 4050 | 4300 | 10 |
2019/01/16 | 4300 | 4500 | 4100 | 4350 | 10 |
2019/01/17 | 4350 | 4550 | 4150 | 4400 | 10 |
2019/01/18 | 4400 | 4600 | 4200 | 4450 | 1 |
2019/01/19 | 4450 | 4650 | 4250 | 4500 | 1 |
これで株価調整ができました。チャートは以下のようになります。

とりあえず、株価調整ではいちいち日付を確認しながら掛け算するんだなってところがポイントです。
ここからは、実際のコード例を紹介します。
株価調整プログラムのコード例
関数ごとに分割して紹介します。
メイン関数
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', newline='') 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
株価の日付に応じて、乗じる係数を変更しています。
自分は最初、係数を乗じるときに、『分割日 ≦ 日付 < 次回の分割日』で判定しながら乗じていました。
ですが、途中で気づきました。
株価を昇順に並べて、係数を降順に並べておけば、『分割日 ≦ 日付』の比較だけで済むということに。
あと、係数リストの中で実際に使っているのは、分割日と係数だけです。
ほかの要素は省略可能でした。
以上で、株価調整が完了しました。
株価と業績の関係を見るときに役立つ
株価と業績を10年単位で見ようとすると、どうしても株価調整が必要になりました。
分割のたびに株価が小さくなっていったので、調整しないと上がっているのか下がっているのか、ぜんぜん分からなかったんですよね。
『調整済みの株価の推移』に『業績の推移』を重ねたことで、『2者の関係がどの程度のものなのか?』を、簡単に把握できるようになりました。
プログラミングの参考になれば幸いです。