権利付き最終日・権利落ち日・権利確定日の一覧を作るコード例【Python】

スポンサーリンク

配当や株主優待の権利付き最終日はいつか?

株主の権利確定日を示したカレンダーはどうやって作るのか?

この記事では、『権利日のカレンダーを作る手順』と『プログラムのコード例』を紹介します。

コード例はPython(パイソン)です。

なぜ権利日のカレンダーを作るのか?

Excelの日付に権利日を表示したかったから

1つは、Excelの表に『権利付き最終日、権利落ち日、権利確定日、株式市場の休日』などを表示したかったからです。

具体的には、Pythonで作った『権利日カレンダーのCSV』を『Excel VBA』で読み込んで、日付に『権利日関係の情報』を追加しようと思いました。

そうして出来たExcelの画面がこちらです。

セルのコメントに『第N曜日』と『権利日』を追加しています。

Excelに『第N曜日』と『権利日』を表示した例

ほかにも、株価予想や業績予想などの計算で、権利日をパラメータの1つにする目的がありました。

ところで、わざわざ自分で作らなくても、直近の権利日カレンダーなら証券会社のサイトで公開されています。

なぜ自分で作ろうと思ったのか?

『株価や業績の推移』を『広く過去や未来まで』分析しようとすると、直近のカレンダーだけでは足りなかったんですね。

そこで、広い範囲のカレンダーを得るために、自分で定義を調べて作る必要が出てきました。

どうしても自分で分析したかったので、頑張ってカレンダー生成プログラムを書きました。

以下、カレンダー作りの解説とコード例です。

権利日カレンダーはどうやって作るのか?

権利日カレンダーを作るには、事前にほかの色々なカレンダーが必要になります。

なぜ、ほかの各種カレンダーが必要なのか?

各種カレンダーが必要になる流れ

まず、『権利日』の定義が必要です。

株式の売買では、決済処理に時間を要するために、権利付き最終日と権利確定日が離れています。

その間隔は、証券取引所の営業日でカウントするので、『営業日』の定義が必要になります。

営業日は取引所の休業日の定義から決まるのですが、休業日の定義には『休日・祝日』と『第N曜日』が登場します。

スポンサーリンク

カレンダーの作成手順

そういうわけで、これらの流れを逆にたどって以下の順にカレンダーを作り、最後に目的の権利日カレンダーを作ることになりました。

  1. 第N曜日のカレンダー(Python辞書)を作る。
  2. 休日・祝日のカレンダー(Python辞書)を作る。
  3. 営業日のカレンダー(Python辞書)を作る。
  4. 権利日のカレンダー(Python辞書)を作る。

権利日カレンダーまでの道のりは長いですね。

実際、カレンダーを作りあげるのは大変でした。

カレンダーの日付計算は難しい

なぜか?

カレンダーというのは月によって日数が変わったり、うるう年(leap year, リープイヤー)で日数が変わったりするからです。

さらに、休日や祝日などは、事前に計算出来るものと、出来ないものがあります。

『地球の自転や公転周期という曖昧さ』と『人の都合で決めたお休みの曖昧さ』の相乗効果(シナジー効果)で、カレンダー計算が大変なものになっていたんですね。

そういった面倒な準備の先に、権利日カレンダーが存在していました。

目的のカレンダーを1発で求めるような便利な計算式は無かったです。

では、どうすればいいのか?

for文で日付を進めながら、1日ずつ定義に当てはめて各種カレンダーを作っていきます。

休日の定義や営業日(休業日)の定義は公開されていますので、それで判定関数を作ってカレンダーを作っていきます。

正確なカレンダーはどうやって維持するのか?

正確な権利日カレンダーを維持するには、カレンダー作りに使うリストや定義の更新をチェックして、メンテナンスしていくアプローチが必要になります。

『第N曜日』だけは不変(ふへん)の定義と思われますが、『休日・祝日』、『営業日』、『権利日』の定義は時々変わります。

なので、それぞれの1次情報源を毎年チェックして、変更があればプログラムに反映するといった作業が発生します。ちょっと大変です。

定義の1次情報源はどこか?

自分が調べた限りでは、以下の通りです。

  • 内閣府のサイトで公開されている祝日リスト
  • 証券取引所のサイトで公開されている市場の休日定義
  • 日本証券業協会のサイトで公開されている決済期間の定義

直近1年間の権利日カレンダーなら、各種定義が出揃っているので、十分に正確なカレンダーが作れます。

過去のカレンダーについては、これまでに使用された定義を各所から集めて作ります。

未来のカレンダーについては、現在の定義を当てはめて作ります。

こうして、過去・現在・未来の権利日カレンダーを作ることができます。

スポンサーリンク

Python コード例

main 関数

プログラムの一番上の部分です。

権利日カレンダーの生成に必要な辞書を揃えて行って、最後にカレンダーを作っています。

途中で各種辞書をCSVに保存していますが、これらはデバッグ用です。内容をチェックしたらコメントアウトしてしまいます。

最終的に活用するのは、最後に作る権利日カレンダーのCSV (04_calendar_jp.csv) だけです。

"""create_calendar.py"""
# 株式カレンダー作成

from os.path import join as os_join
from os.path import isdir as os_isdir

from datetime import datetime as datetime_datetime
from datetime import date as datetime_date
from datetime import timedelta as datetime_timedelta
DATETIME_TIMEDELTA_DAY_1 = datetime_timedelta(days=1)
DATETIME_TIMEDELTA_DAYS_2 = datetime_timedelta(days=2)
from dateutil.relativedelta import relativedelta

from collections import OrderedDict
from operator import itemgetter

from csv import writer as csv_writer
from csv import reader as csv_reader


def main():
    """main関数"""

    # データフォルダを設定
    # (用途)
    # ・祝日リストのCSVを入れておく
    # ・各種辞書のCSVを保存(デバッグ用)
    data_dir = r'(どこかにフォルダを用意してフォルダパスを指定)'
    if not os_isdir(data_dir):
        print('Error データフォルダが存在しません')
        print('data_dir: %s' % data_dir)
        print('(何もせずに終了)')
        return


    # 計算対象の期間を設定
    # (時刻情報は使わないので date型 を使っています)
    period = (
        datetime_date(1950, 1, 1),
        datetime_date(2050, 12, 31),
        )


    # 第N曜日の辞書を作成
    weekdays = create_weekdays_dict(period[0], period[1])

    # 辞書を『リストに変換 & 日付の昇順にソート』してCSVに保存
    weekdays_file = os_join(data_dir, '01_weekdays.csv')
    weekdays_head = [['日付', '曜日', '第N曜日']]
    weekdays_data = sorted(weekdays.values(), key=itemgetter(0), reverse=False)
    save_to_csv(weekdays_file, weekdays_head + weekdays_data, 'utf-8-sig', '\n')


    # 祝日の辞書を作成
    holidays_file = os_join(data_dir, 'holidays_jp.csv') # 祝日リストのCSV
    holidays = create_holidays_dict(holidays_file, 'utf-8-sig')


    # 東京証券取引所(tse)の営業日の辞書を作成
    tse_days = get_tse_days(period[0], period[1], weekdays, holidays)

    # 辞書を『リストに変換 & 日付の昇順にソート』してCSVに保存
    tse_days_file = os_join(data_dir, '02_tse_days.csv')
    tse_head = [['日付', '営業状態']]
    tse_datas = sorted(tse_days.values(), key=itemgetter(0), reverse=False)
    save_to_csv(tse_days_file, tse_head + tse_datas, 'utf-8-sig', '\n')


    # 権利日(権利付最終日、権利落日、権利確定日)の辞書を作成。
    rights_days = create_rights_days_dict(period[0], period[1], holidays, tse_days)

    # 辞書を『リストに変換 & 日付の昇順にソート』してCSVに保存
    rights_days_file = os_join(data_dir, '03_rights_days.csv')
    rights_head = [['日付', '権利関係']]
    rights_datas = sorted(rights_days.values(), key=itemgetter(0), reverse=False)
    save_to_csv(rights_days_file, rights_head + rights_datas, 'utf-8-sig', '\n')


    # カレンダーの辞書を作成
    dates = create_calendar_dict(period[0], period[1], weekdays, holidays, tse_days, rights_days)

    # 辞書を『リストに変換 & 日付の昇順にソート』してCSVに保存
    dates_file = os_join(data_dir, '04_calendar_jp.csv')
    dates_head = [['日付', '第N', '曜日', '祝日', '東証', '権利関係']]
    dates_datas = sorted(dates.values(), key=itemgetter(0), reverse=False)
    save_to_csv(dates_file, dates_head + dates_datas, 'utf-8-sig', '\n')
    return


def save_to_csv(file, datas, encoding, lineterminator='\n'):
    """listをcsvに保存"""
    with open(file, 'w', encoding=encoding, newline='') as f:
        w = csv_writer(f, delimiter=',', lineterminator=lineterminator)
        w.writerows(datas)
    return


# (カレンダー計算に必要な関数群)


if __name__ == '__main__':
    main()

第N曜日の辞書を作る関数

日本のカレンダーで欠かせないのが、『第2土曜日』といった概念です。

休日や祝日の定義で頻繁に登場しますので、第N曜日を求めるための辞書が必要になります。

それを作る関数です。

事前に、Pythonライブラリの『python-dateutil (パイソン デート ユーティル)』をインストールしておきます。

このライブラリの『relativedelta (リレーティブ デルタ)』が、月ごとの日数の違いや閏年(うるうどし)の帳じり合わせをしてくれます。

def create_weekdays_dict(date_start, date_end):
    """第N曜日の辞書を作成"""

    # 休日定義では『第2土曜日が休み』といった表現が登場します。
    # そこで、何年何月何日は第N曜日であるという辞書が必要。

    # 辞書の作り方は、for文で月ごとに曜日を数えるアプローチが簡単。
    # ここでは、『月初(げっしょ)』から『月末(げつまつ)』まで
    # 各曜日の出現回数を数えて、辞書に追加していきます。

    # 月初から月末まで数えていく方法なので、開始日と終了日に関係なく、
    # 月初と月末の日付が必要になります。

    # 開始日の月の月初(げっしょ)を取得
    t_start = datetime_date(
        year=date_start.year,
        month=date_start.month,
        day=1,
        )

    # 終了日の月の月末(げつまつ)を取得
    # ところで、月末が『何日か?』は月や閏年か否かによって異なる。
    # 月末はどうやって求めるのか?
    # 月末とは、翌月の1日(ついたち)から、1日(いちにち)引いた日である。
    # だが、何日後が翌月なのかも、月や閏年か否かによって異なる。
    # そのあたりのカレンダー計算には python-dateutil の実装が使える。
    # python-dateutil の dateutil.relativedelta を使って月末を求める。
    t_end = datetime_date(
        year=date_end.year,
        month=date_end.month,
        day=1,
        ) + relativedelta(
            months=1,
            days=-1,
            )

    # 日数を取得
    n_days = (t_end - t_start).days + 1

    # 辞書を作成
    weekdays = OrderedDict()

    # weekdayカウンタの初期値を取得する関数
    get_counter_zero = lambda : [0, 0, 0, 0, 0, 0, 0]

    # 要素番号と曜日の対応は以下の通り
    # [0]月 [1]火 [2]水 [3]木 [4]金 [5]土 [6]日
    # date.weekday()が月曜に0を返すのでこうなっています

    # 最初のカウンタを作成
    w = get_counter_zero()

    # for文で月ごとに曜日を数える
    for n_day in range(0, n_days):
        # 開始日から日付を進める
        t = t_start + datetime_timedelta(days=n_day)

        # 月初なら新しいweekdayカウンタを取得
        if t.day == 1:
            w = get_counter_zero()

        # 曜日を取得
        t_weekday = t.weekday()

        # 対応する曜日に+1
        w[t_weekday] += 1

        # 日付はまだ辞書に入ってないはず
        assert t not in weekdays

        # 日付をキーにして辞書に(日付, 曜日, 第N曜日)のタプルを追加
        weekdays[t] = (t, t_weekday, w[t_weekday])
    return weekdays

休日・祝日の辞書を作る関数

事前に作っておいた休日・祝日リストのCSVを読み込んで、辞書を作る関数です。

コードのコメントに休日・祝日リストの作り方を書きました。

休日・祝日の制度は良く変わりますので、正確さを保つなら毎年『休日・祝日リスト』を更新していくメンテナンスが要るところです。

以下、コード例です。

def create_holidays_dict(file, encoding):
    """祝日の辞書を作成"""

    # 事前に日本の祝日リストをCSVにまとめておきます。
    # それを読み込んで祝日の辞書を作ります。

    # では、祝日リストはどうやって作るのか?

    # 過去の祝日リストは各種情報サイトを参考に作ります。
    # 直近の祝日リストは内閣府のサイトにCSVがあります。
    # 未来の祝日リストは最新の祝日の定義から計算で求めます。

    # ご自身の目的に合うように、これらを組み合わせて
    # 祝日のCSVを作っておきます。

    # 一番重要な直近の祝日リストを内閣府が公開しているので、
    # 十分実用的な祝日リストが作れます。
    # 正確さを求めるなら、内閣府の祝日リストを毎年取り込めばOKです。

    # 以下は、既に用意したCSVから祝日辞書を作るコードです。

    # 辞書を作成
    holidays_dict = OrderedDict()
    datetime_strptime = datetime_datetime.strptime

    with open(file, 'r', encoding=encoding, newline='') as f:
        # CSVのフォーマット例
        # [0] [0]年月日 [1]祝日名
        # [1] [0]1950-01-01 [1]元日
        # [2] ...

        # 最初のラベル行を読み捨てる
        next(csv_reader(f))

        # 日付と祝日名の辞書を作成
        for c in csv_reader(f):
            # 日付列をdatetime型に変換 & 時刻情報は不要なのでdate型に変換
            t = datetime_strptime(c[0], '%Y-%m-%d').date()

            # 日付はまだ辞書に入ってないはず
            assert t not in holidays_dict

            # 日付をキーにして辞書に祝日名を追加
            holidays_dict[t] = c[1]
    return holidays_dict

東証の営業日の辞書を作る関数

東京証券取引所の休業日の定義は、内国株の売買制度『取引時間・休業日の変遷』のページにありました。

一応、書かれていた過去の定義をすべてコードに起こしました。ですが、実際には2009年以降の定義だけで十分でした。あまり古い日付に出番はなかったです。

休業日の定義は今後も変わっていくと思いますので、正確さを保つなら定義更新のメンテナンスが要るところです。
以下、コード例です。
先に作った『第N曜日の辞書』と『休日・祝日の辞書』を使って、『東証の休業日の辞書』を作っていきます。
def get_tse_days(t_start, t_end, weekdays, holidays):
    """東京証券取引所(tse)の営業日の辞書を作成"""

    # 東証の営業日の辞書はどうやって作るのか?
    # 東証のサイトで『取引時間・休業日の変遷』が公開されている。
    # このPDFに休業日の定義が載っているので、これを使って作る。

    # 具体的には、定義の数だけ判定関数を作って、
    # 判定結果を辞書に追加していく。

    # 東証の『営業状態』を定義 (営業日に1を設定)
    # 半休日は半日だけ『営業』しているので、営業日扱いにしています。
    tse_none = None # 未対応の日付(エラー時に設定)
    tse_close = 0 # 休日(クローズ)
    tse_open = 1 # 営業日(半休日を含む)

    # 日付定義
    date_20091230 = datetime_date(2009, 12, 30)
    date_19890201 = datetime_date(1989,  2,  1)
    date_19860801 = datetime_date(1986,  8,  1)
    date_19830801 = datetime_date(1983,  8,  1)
    date_19791001 = datetime_date(1979, 10,  1)
    # date_19730423 = datetime_date(1973,  4, 23)
    date_19490401 = datetime_date(1949,  4,  1)

    # 1973年04月23日 の定義は省略。
    # 1973年に振替休日が制定されてたことを受けて
    # 休業日の定義に振替休日が追加されているが、実際に
    # 判定関数を作ってみたら 1949年04月01日 の関数と
    # 全く同じになったため。

    # 日数を取得
    n_days = (t_end - t_start).days + 1

    # 辞書を作成
    stock_exchanges_days = OrderedDict()

    # 日付を判定関数にかけて営業状態を辞書に追加
    for n_day in range(0, n_days):
        # 開始日から日付を進める
        t = t_start + datetime_timedelta(days=n_day)

        # 日付に応じて判定関数にかける
        if t >= date_20091230:
            tse_day = get_tse_20091230(t, weekdays, holidays, tse_open, tse_close)
        elif t >= date_19890201:
            tse_day = get_tse_19890201(t, weekdays, holidays, tse_open, tse_close)
        elif t >= date_19860801:
            tse_day = get_tse_19860801(t, weekdays, holidays, tse_open, tse_close)
        elif t >= date_19830801:
            tse_day = get_tse_19830801(t, weekdays, holidays, tse_open, tse_close)
        elif t >= date_19791001:
            tse_day = get_tse_19791001(t, weekdays, holidays, tse_open, tse_close)
        elif t >= date_19490401:
            tse_day = get_tse_19490401_19730423(t, weekdays, holidays, tse_open, tse_close)
        else:
            # 未対応の日付
            tse_day = tse_none

        # 日付はまだ辞書に入ってないはず
        assert t not in stock_exchanges_days

        # 日付をキーにして辞書に(日付, 営業状態)のタプルを追加
        stock_exchanges_days[t] = (t, tse_day)
    return stock_exchanges_days


def get_tse_20091230(t, weekdays, holidays, tse_open, tse_close):
    """2009年12月30日~"""
    if t in holidays:
        return tse_close

    if t.weekday() == 5:
        return tse_close # 土曜日

    if t.weekday() == 6:
        return tse_close # 日曜日

    if t.month == 12:
        if t.day == 31:
            return tse_close # 大晦日

    if t.month == 1:
        #if t.day == 1:
        #    return tse_close # 年始3日間 (正月はholidaysで判定済み)
        if t.day == 2:
            return tse_close # 年始3日間
        if t.day == 3:
            return tse_close # 年始3日間
    return tse_open


def get_tse_19890201(t, weekdays, holidays, tse_open, tse_close):
    """1989年2月1日~"""
    if t in holidays:
        return tse_close

    if t.weekday() == 5:
        return tse_close # 土曜日

    if t.weekday() == 6:
        return tse_close # 日曜日

    if t.month == 12:
        if t.day == 31:
            return tse_close # 大晦日

    if t.month == 1:
        #if t.day == 1:
        #    return tse_close # 年始3日間 (正月はholidaysで判定済み)
        if t.day == 2:
            return tse_close # 年始3日間
        if t.day == 3:
            return tse_close # 年始3日間
    return tse_open


def get_tse_19860801(t, weekdays, holidays, tse_open, tse_close):
    """1986年8月1日~"""
    if t in holidays:
        return tse_close
    if t.weekday() == 5:
        if weekdays[t][2] == 2:
            return tse_close # 第2土曜日
        if weekdays[t][2] == 3:
            return tse_close # 第3土曜日
    if t.weekday() == 6:
        return tse_close # 日曜日

    if t.month == 12:
        if t.day == 29:
            return tse_close # 年末3日間
        if t.day == 30:
            return tse_close # 年末3日間
        if t.day == 31:
            return tse_close # 年末3日間 大晦日

    if t.month == 1:
        #if t.day == 1:
        #    return tse_close # 年始3日間 (正月はholidaysで判定済み)
        if t.day == 2:
            return tse_close # 年始3日間
        if t.day == 3:
            return tse_close # 年始3日間
    return tse_open


def get_tse_19830801(t, weekdays, holidays, tse_open, tse_close):
    """1983年8月1日~"""
    if t in holidays:
        return tse_close

    if t.weekday() == 5:
        if weekdays[t][2] == 2:
            return tse_close # 第2土曜日

    if t.weekday() == 6:
        return tse_close # 日曜日

    if t.month == 12:
        if t.day == 29:
            return tse_close # 年末3日間
        if t.day == 30:
            return tse_close # 年末3日間
        if t.day == 31:
            return tse_close # 年末3日間 大晦日

    if t.month == 1:
        #if t.day == 1:
        #    return tse_close # 年始3日間 (正月はholidaysで判定済み)
        if t.day == 2:
            return tse_close # 年始3日間
        if t.day == 3:
            return tse_close # 年始3日間
    return tse_open


def get_tse_19791001(t, weekdays, holidays, tse_open, tse_close):
    """1979年10月1日~"""
    if t in holidays:
        return tse_close

    if t.weekday() == 5:
        if weekdays[t][2] == 3:
            return tse_close # 第3土曜日

    if t.weekday() == 6:
        return tse_close # 日曜日
        
    if t.month == 12:
        if t.day == 29:
            return tse_close # 年末3日間
        if t.day == 30:
            return tse_close # 年末3日間
        if t.day == 31:
            return tse_close # 年末3日間 大晦日

    if t.month == 1:
        #if t.day == 1:
        #    return tse_close # 年始3日間 (正月はholidaysで判定済み)
        if t.day == 2:
            return tse_close # 年始3日間
        if t.day == 3:
            return tse_close # 年始3日間
    return tse_open


def get_tse_19490401_19730423(t, weekdays, holidays, tse_open, tse_close):
    """1949年4月1日~ 1973年4月23日~"""
    if t in holidays:
        return tse_close

    if t.weekday() == 6:
        return tse_close # 日曜日

    if t.month == 12:
        if t.day == 29:
            return tse_close # 年末3日間
        if t.day == 30:
            return tse_close # 年末3日間
        if t.day == 31:
            return tse_close # 年末3日間 大晦日

    if t.month == 1:
        #if t.day == 1:
        #    return tse_close # 年始3日間 (正月はholidaysで判定済み)
        if t.day == 2:
            return tse_close # 年始3日間
        if t.day == 3:
            return tse_close # 年始3日間
    return tse_open

権利日の辞書を作る関数

いよいよ権利日を求めるところです。

権利日とは『権利付き最終日、権利落ち日、権利確定日』などのことです。

権利日関係の日付は、月末から逆算して見つけていきます。

証券会社などのサイトを見ると、そのようなアプローチで調べる旨が書かれていましたので、そのようにしています。

一部の上場企業では、株主優待などの権利日を月末以外に設定しているケースがあります。

こういった権利日は、完全に個別対応になると思います。以下のコード例でも対応していません。

さて、コード例に注意点があります。

以下のコード例では、『権利落ち日』から『権利確定日』までの日数が『2日間』で固定になっています。

以前に『3日間』だった頃などの処理分けは作っていませんので、その時期の権利日カレンダーは正しくないものができます。

広く正確なカレンダーを作りたいときは、決済期間の変遷などを調べて、東証の営業日の辞書を作った要領で処理を分ければよいと思います。

株式などの決済期間は短縮化の流れにあります。
『3日間(けっこう前)⇒2日間(2018年 時点)⇒1日間(今後の予定)』
正確さを保つなら、決済期間の定義更新といったメンテナンスが要るところです。

以下、コード例です。

def create_rights_days_dict(t_start, t_end, holidays, tse_days):
    """権利日(権利付最終日、権利落日、権利確定日)の辞書を作成。"""

    # 辞書を作成
    rights_days = OrderedDict()

    # 月数を取得
    date_delta = relativedelta(t_end, t_start)
    n_months = date_delta.years * 12 + date_delta.months + 2

    for n_month in range(0, n_months):
        # 月を進める
        d = t_start + relativedelta(months=n_month)

        # 月末を取得
        month_end = d + relativedelta(months=1, days=-1)
        if month_end < t_start:
            continue
        elif month_end > t_end:
            continue


        # [権利確定日(けんりかくていび)を取得]
        # 初期値に『月末』を設定
        vesting_date = month_end

        # 初期値から1日ずつ遡って条件に合う日を探す
        vesting_date = get_rights_date(vesting_date, t_start, t_end, holidays, tse_days)

        # 権利確定日が見つかったら日付をキーにして辞書に追加
        if vesting_date:
            rights_days[vesting_date] = (vesting_date, '権利確定日')
        else:
            # ここは実行されないはず
            print('Error 権利確定日(vesting_date) が見つからなかった %s' % d)
            continue


        # [権利落日(けんりおちび)を取得]
        # 初期値に『権利確定日』の『2日前 ※注』を設定
        # ※注 この『2日』は決済期間によるもの。
        # もし、過去分まで正確に計算するなら、
        # 『株式等の決済期間短縮化』で調べて、
        # 時期ごとに決済期間を変える必要があります。
        # また、決済の高速化で日数が2日⇒1日に短縮される予定。
        ex_rights_date = vesting_date - DATETIME_TIMEDELTA_DAYS_2

        # 初期値から1日ずつ遡って条件に合う日を探す
        ex_rights_date = get_rights_date(ex_rights_date, t_start, t_end, holidays, tse_days)

        # 権利落日が見つかったら日付をキーにして辞書に追加
        if ex_rights_date:
            rights_days[ex_rights_date] = (ex_rights_date, '権利落日')
        else:
            # ここは実行されないはず
            print('Error 権利落日(ex_rights_date) が見つからなかった %s' % d)
            continue


        # [権利付最終日(けんりつきさいしゅうび)を取得]
        # 初期値に『権利落日』の『1日前』を設定
        rights_date = ex_rights_date - DATETIME_TIMEDELTA_DAY_1

        # 初期値から1日ずつ遡って条件に合う日を探す
        rights_date = get_rights_date(rights_date, t_start, t_end, holidays, tse_days)

        # 権利付最終日が見つかったら日付をキーにして辞書に追加
        if rights_date:
            rights_days[rights_date] = (rights_date, '権利付最終日')
        else:
            # ここは実行されないはず
            print('Error 権利付最終日(rights_date) が見つからなかった %s' % d)
            continue
    return rights_days


def get_rights_date(t_date, t_start, t_end, holidays, tse_days):
    """日付をさかのぼって最初に現れる営業日を取得"""

    # なぜ、『日付をさかのぼって最初に現れる営業日』を求めるのか?
    # 権利付最終日、権利落ち日、権利確定日の間に休日をはさむ場合に対応するためです。
    # 何日後が何の権利日か?という通常の定義に加えて、
    # 営業日をさかのぼる処理を加えることで、休日を考慮した処理を実現しています。

    # 営業日で数えながらさかのぼって求める根拠は?
    # これは、各証券会社のサイトに、
    # 『権利を得るには、権利確定日から数えて N営業日前 までに株式を保有する必要がある』
    # などと書かれているからです。
    # これを手順に直すと、日付は『営業日』で数えながらさかのぼる、となります。

    # 営業日とは何の営業日?
    # 株式を売買できる営業日のことです。
    # 当プログラムでは『東証の営業日』を使っています。
    # なお、PTS市場などは権利関係の日付が異なる場合があります。

    # 日付の初期値を設定
    t = t_date

    # 月末を除いた最大30日間をさかのぼって調べる。
    for n in range(0, 31):
        # 日付が東証の休日辞書にあるか?
        if t in tse_days:

            # その日付は東証の休日か?
            if tse_days[t][1] == 0:

                # 東証が休日
                t = t - DATETIME_TIMEDELTA_DAY_1
                continue

        # 東証の営業日が現れたのでブレーク
        break
    else:
        # ここは実行されないはず
        print('Error 平日が見つからなかった t:%s date:%s start:%s end:%s' % (
            str(t), str(t_date), str(t_start), str(t_end)))
        return None

    # 日付の範囲チェック
    if t < t_start:
        # 範囲外
        return None
    elif t > t_end:
        # 範囲外
        return None
    return t

権利日カレンダーを作る関数

ここまでに準備した辞書を使って、実際にカレンダーを作る関数です。

def create_calendar_dict(t_start, t_end, weekdays, holidays, tse_days, rights_days):
    """カレンダーの辞書を作成"""

    # ここまでに準備した辞書を使ってカレンダーを作ります
    # weekdays: 第N曜日の辞書
    # holidays: 祝日の辞書
    # tse_days: 東証の休日辞書
    # rights_days: 権利日の辞書

    # 曜日が数字のままだと見づらいので、漢字リストを用意。
    youbi_list = ('月', '火', '水', '木', '金', '土', '日')

    # 日数を取得
    n_days = (t_end - t_start).days + 1

    # カレンダー辞書を作成
    dates = OrderedDict()

    # 東証の営業状態が数字のままだと見づらいので、名前辞書を用意。
    tse_days_dict = {None: '', 0: 'close', 1: 'open'}

    # 開始日から1日ずつ進めてカレンダーを作る
    for n_day in range(n_days):
        # 日付を進める
        t = t_start + datetime_timedelta(days=n_day)

        # 辞書に
        # ('日付', '第N', '曜日', '祝日', '東証', '権利関係')
        # のタプルを追加
        dates[t] = (
            t.strftime('%Y/%m/%d'),
            weekdays[t][2],
            youbi_list[t.weekday()],
            holidays[t] if t in holidays else '',
            tse_days_dict[tse_days[t][1]],
            rights_days[t][1] if t in rights_days else '',
            )
    return dates

以上です

(株価分析や銘柄分析プログラムを作る時の参考になれば幸いです)

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