0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

アクチュアリーのためのPython入門(月次キャッシュフロー1)

0
Last updated at Posted at 2026-05-22

アクチュアリーのためのPython入門(保険数理モデリング編第8回)

月次キャッシュフロー1

📚 アクチュアリーのためのPython入門
この記事は保険数理モデリング編の一部です。
目次はこちら
逆引きガイドはこちら

はじめに

前回は死亡リスクの計算を行い、
さらに、t年経過後の死亡リスクの計算もできるように修正しました。

今回は、また基本のキャッシュフローの話に戻り、
実務上のキャッシュフローの計算に近づけていきます。

今まで取り上げた保有契約の例では、
契約月が事業年度始という前提で計算していました。
ですが、実際は少なくとも月ごとにばらけています。
そこで今回は、契約年だけでなく
契約月も加えてキャシュフローを計算します
ただし、契約日は各月の1日の前提とします。

必要な準備

契約月を加えるにあたって、
キャシュフローが年単位の計算ままでは、
常に契約応当月が事業年度のどの位置にあるか確認する必要があります。
コード内に分岐が入り、非常に分かりにくくなります。

ですので、キャッシュフローを
すべて月単位のキャッシュフローに書き直します。
そして、最後に会計年度で集計をします。

今までは年単位のキャシュフローの前提でしたので、
かなりの修正が必要です。

  • 死亡率
  • 解約率
  • 保険期間(キャッシュフローの計算回数)
  • 契約年、契約月
  • 決算年度

などを作成・修正します。

計算の流れ

前回までと同様にキャッシュフローを計算する関数を作成します。
その中でキャッシュフローを計算するコードは、

  1. 残存表を作成
  2. 残存表の生存、死亡、解約それぞれに、
    保険料、保険金額、解約返戻金を乗じてキャッシュフローを計算

という流れになっていました。

今回、そこに年月が加わります。

最初に考えたのは、計算時に年月を1ずつ加えていく方法でした。
こうすると、先ほどの流れの中に年月を計算するプロセスが、
混ざってしまい、コードが分かりにくくなります。

そこで、満期(終身なら最終年齢)までの年月を先に計算します。
それとは別に月ごとのキャッシュフローを計算して、
それらを結合する方法を取ります。

  1. 年月と会計年度の計算
  2. 残存表を作成
  3. 残存表の生存、死亡、解約それぞれに、
    保険料、保険金額、解約返戻金を乗じてキャッシュフローを計算
  4. 年月と会計年度、キャッシュフローの結合
  5. 会計年度ごとにキャッシュフローを集計

コードは上記のような流れになっています。

キャッシュフローの計算

契約のデータcontr_ym.xlsxを次のとおりとします。
契約年policy_yearと契約月policy_monthを付け加えています。

image.png

続いてPythonのコード部分です。
まずはヘッダーです。
年月を扱うなら、実際の年月日を取り扱えるようにしたいので、
ライブラリdateutilをインストールします。
このライブラリにあるrelativedeltaを使います。
ExcelだとEDateのように年月日に月単位を加えることができます。

また、日付を扱うために、from datetime import dateの文も加えます。
datetimは基本ライブラリのため、インストールは不要です。

ファイル名は、cashflow_month.pyとしておきます。

cashflow_month.py
from core import *
from endowment import PremE, SVE
from term import PremT, SVT
from whole import PremW, SVW
import pandas as pd
from datetime import date
from dateutil.relativedelta import relativedelta

続いて、死亡率や解約率などの関数です。
死亡率×死亡指数と解約率を月単位に変換してます。

cashflow_m.py
# 死亡指数の関数化
def mort_k(product):
    match product:
        case "E":
            k = 0.8
        case "T":
            k = 0.7
        case "W":
            k = 0.85
    return k

# 死亡率
def qdx(product, sex, age):
    q = 1 - (1 - qx[sex][age] * mort_k(product)) ** (1/12)
    return q

# 解約率(養老保険)
def qwxE(t, m):
    if t == 0:
        w = 0.05
    elif t < 3:
        w = 0.03
    else:
        w = 0.02
    return w

# 解約率(定期保険)
def qwxT(t, m):
    if t == 0:
        w = 0.08
    elif t < 3:
        w = 0.05
    else:
        w = 0.03
    return w

# 解約率(終身保険)
def qwxW(t, m):
    if t == 0:
        w = 0.03
    elif t >= m:
        w = 0.01
    else:
        w = 0.02
    return w

# 解約率の関数
surrender_func = {
    "E":qwxE,
    "T":qwxT,
    "W":qwxW
}
# 解約率(f:経過月)
def qwx(product, t, m):
    q = surrender_func[product](t, m)
    w = 1 - (1 - q) ** (1/12)
    return w

保険料と解約返戻金の計算式、
保険期間や保険料払込期間などに変更はありませんので、
前回までと同じです。

:cashflow_m.py
# 保険料の計算式
premium_func = {
    "E":PremE,
    "T":PremT,
    "W":PremW
}
def prem_func(product, sex, age, term):
    return premium_func[product](sex, age, term)
    
# 解約返戻金の計算式
surrendervalue_func = {
    "E":SVE,
    "T":SVT,
    "W":SVW
}
def sv_func(product, sex, age, term ,t):
    return surrendervalue_func[product](sex, age, term, t)

# 満期保険金
def maturity(product):
    if product == "T":
        value = 0   # 定期保険は0
    else:
        value = 1   # 他は1
    return value

# 最終年齢(終身保険用)
def omega(sex):
    o = len(qx[sex]) - 1
    return o

# 保険料払込期間
def PremPeriod(product, sex, age, term):
    n = parse_term(term, age)
    return n

# 保険期間
def InsPeriod(product, sex, age, term):
    if product == "W":
        value = omega(sex) - age       # 終身は最終年齢 
    else:
        value = parse_term(term, age)  # 他はそのまま
    return value

ここから本題に入ります。
キャッシュフローを計算する関数です。
年月と年度を入れる変数time_axisを作っておきます。

cashflow_m.py
# キャッシュフロー計算の関数化 base_yearは計算基準年度
def create_cashflow(base_year = 2022):    

    # 結果を入れる変数
    results1 = []

    # 年月を入れる変数
    time_axis = []

契約ファイルを読み込み、
契約年と契約月も取得します。

cashflow_m.py
    # データの取得
    df = pd.read_excel("contr_ym.xlsx")

    for _, row in df.iterrows():
        product = row["product"]
        policy_year = int(row["policy_year"])
        policy_month = int(row["policy_month"])
        sex = row["sex"]
        age = int(row["age"])
        term = row["term"]
        amount = int(row["amount"])

契約年月のままだと扱いにくいので、一旦、契約日にします。
dateはDate型にする関数です。Excelと同様です。

cashflow_m.py
        # 契約日
        policy_date = date(policy_year, policy_month, 1)
        # 保険期間
        n = InsPeriod(product, sex, age, term)
        # 保険料払込期間
        m = PremPeriod(product, sex, age, term)

一旦、契約日から満期までの年月と、会計年度をリスト化します。
policy_date + relativedelta(months = f)で、
契約日policy_tateに経過月数fを加えます。

次に、会計年度を計算します。
計算年cf_year、計算月cf_monthと会計年度fiscal_year
time_axisに追加していきます。
time_axisは、年月と会計年度のリストになります。

cashflow_m.py
        # 計算年月と会計年度の計算
        for f in range(n * 12):
            # 計算年月
            dt = policy_date + relativedelta(months = f)

            # 会計年度
            if dt.month >= 4:
                fiscal_year = dt.year
            else:
                fiscal_year = dt.year - 1
            
            time_axis.append({"cf_year":dt.year,
                              "cf_month":dt.month,
                              "fiscal_year":fiscal_year
            })

そして、残存表の部分です。
ここはすでに死亡率と解約率は月単位で計算済みなので、
大きな修正はありません。
計算回数を年ではなく月単位にするので、
保険期間×12とするくらいです。

cashflow_m.py
        l0 = amount      # 初期保険金額

        # 残存表の作成
        lx = [l0]        # 保有保険金額
        dx = []          # 死亡保険金額
        wx = []          # 解約保険金額
        mx = []          # 満期保険金額

        # 生命表の作成
        for f in range(n * 12) :
            t = int(f / 12) # 経過年数

            q = qdx(product, sex, age + t)
            w = qwx(product, t, m)
            next_d = lx[-1] * q
            next_w = lx[-1] * w
            if f == n * 12 - 1:
                next_m = lx[-1] - next_d - next_w
                next_l = 0
            else:
                next_m = 0
                next_l = lx[-1] - next_d - next_w

            lx.append(next_l)
            dx.append(next_d)
            wx.append(next_w)
            mx.append(next_m)

今回はここまでとして、
続きのキャッシュフロー部分は次回とさせてください。


📚 ナビゲーション
前の記事
死亡リスク額の計算2

次の記事
月次キャッシュフロー2

📚 目次
アクチュアリーのためのPython入門

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?