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入門(複数商品のキャッシュフロー)

0
Last updated at Posted at 2026-04-11

アクチュアリーのためのPython入門(応用編第3回)

保険種類を混ぜた将来キャッシュフロー

📚 アクチュアリーのためのPython入門
この記事は応用編の一部です。
目次はこちら

はじめに

前回は保有契約の将来収支を計算しました。
これまで、新契約のみを取り扱っていましたが、
保有契約の収支を取り扱う上で、
ポイントの理解ができたかと思います。

ただ、新契約のときも保有契約のときも、
一商品だけしか取り扱っていません。
生命保険会社の保有契約には、さまざまな商品があります。

今回は一商品だけではなく、商品を混ぜた場合の、
将来キャッシュフローを作成します。

商品の比較

今まで作成したの養老保険、定期保険、終身保険について考えます。
これらの商品を同時に取り扱う場合、問題となるのが、

  • 満期保険金の有無(定期保険と養老保険)
  • 保険期間の違い(終身保険とそれ以外)
  • 保険料払込期間の違い(終身保険とそれ以外)

があります。
また、商品によって解約率が異なったり、
死亡指数が異なったりということがありますので、
その前提で進めます。

保有契約のファイル

保有契約のファイルは次のとおりとして、
ファイル名をcontr_all.xlsxとしておきます。

image.png

なお、productは商品種類で、Eは養老保険、
Tは定期保険、Wは終身保険です。

キャッシュフロー計算用ファイルの作成

前回までキャッシュフローや保険収支は、
各商品のファイルで作成してきましたが、
今回はキャシュフローを計算するファイルを新しく作成します。
ファイル名はcashflow.pyとしておきます。

このとき、endowment.pyterm.pywhole.py
計算に必要な関数

  • 保険料の計算 PremEPremTPremW
  • 保険料積立金の計算 ResEResTResW
  • 純保険料の計算 netResEnetResTnetResW
  • 解約返戻金の計算 SVESVTSVW

を残して、他の計算式などは削除するか、
引用するときは動かないようにしておくのがベストです。
次回以降に、再度取り上げますが、

if __name__ == "__main__":

この中に入れてあげると、
引用したときは動作しなくなります。

それでは、cashflow.pyのヘッダー部分です。
core.pyから基本的な関数などを利用。
すでに作成しているendowment.pyterm.pywhole.pyから、
保険料の関数と解約返戻金の関数を利用します。
また、pandasを使用します。

cashflow.py
from core import *
from endowment import PremE, SVE
from term import PremT, SVT
from whole import PremW, SVW
import pandas as pd

関数の作成

先の商品の違いの箇所をなるべく関数化して、
計算のプログラム内では分岐を少なく見やすくします。
ここが今回のポイントになります。
前回の死亡率と解約率も含めて、関数は、

  • 死亡指数
  • 死亡率
  • 解約率
  • 保険料
  • 解約返戻金
  • 満期保険金
  • 保険料払込期間
  • 保険期間

を作成します。

死亡指数は養老保険が0.8、定期保険が0.7、終身保険が0.85とします。
解約率は養老保険が、1年目が0.05、2年目までが0.03、3年目以降は0.02。
定期保険が、1年目が0.08、2年目までが0.05、3年目以降は0.03。
終身保険が、1年目が0.03、保険料払込後は0.01、その他の期間は0.02とします。

具体的なコードは次のとおりです。

cashflow.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(sex, age):
    return qx[sex][age]

# 解約率(養老保険)
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
}
def qwx(product, t, m):
    return surrender_func[product](t, m)

# 保険料の計算式
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

終身保険は最終年齢で満期を迎えて、
満期保険金を払う養老保険(払込期間が異なりますが)と考えます。

続いて、キャッシュフローの計算です。
商品種類productを引数に加えた関数へ入れ替えます。

なお、補正値の計算位置を変更しています。
(前回の位置ですと何度も計算しなおすことになるので)

cashflow.py
# 計算基準年度
base_year = 2022

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

df = pd.read_excel("contr_all.xlsx")

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

    # 保険収支の計算
    l0 = amount      # 初期保険金額

    # 残存表の作成
    lx = [l0]        # 保有保険金額
    dx = []          # 死亡保険金額
    wx = []          # 解約保険金額
    mx = []          # 満期保険金額
    
    n = InsPeriod(product, sex, age, term) # 保険期間
    m = PremPeriod(product, sex, age, term) # 保険料払込期間
    # 生命表の作成
    for t in range(n):
        q = qdx(sex, age + t)
        w = qwx(product, t, m)
        next_d = lx[-1] * q * mort_k(product)
        next_w = lx[-1] * w
        # 満期のとき
        if t == n - 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)

    # 補整値の計算
    af = amount / lx[base_year - policy + 1]

    # 保険収支の項目
    inprem = []    # 保険料収入
    benefit = []   # 保険金支払い
    surrender = [] # 解約返戻金支払い
    expenses = []  # 事業費
    cashflow = []  # キャッシュフロー

    P = float(prem_func(product, sex, age, term))

    # キャッシュフローの計算
    for t in range(n):
        # 保険料の収入
        if t < m:
            inprem.append(lx[t] * P)
        else:
            inprem.append(0)
        # 保険金の支払い
        if t == n - 1:
            # 死亡+満期保険金の支払い
            pay_ben = dx[t] + mx[t] * maturity(product)
        else:
            # 死亡保険金の支払い
            pay_ben = dx[t]
        benefit.append(pay_ben)
        # 解約返戻金の支払い
        W = float(sv_func(product, sex, age, term, t + 1))
        surrender.append(wx[t] * W)
        # 事業費の支払い
        expenses.append(inprem[t] * 0.3)
        # キャッシュフロー
        cashflow.append(inprem[t] - benefit[t] \
                      - surrender[t] - expenses[t])

結果results1には集計のキーとなるproductも入れておきます。
他に大きな変更はありません。

cashflow.py
        # 商品別のキャッシュフロー
        results1.append({"product":product,
                         "year":policy + t,
                         "lx":int(lx[t] * af),
                         "dx":int(dx[t] * af),
                         "wx":int(wx[t] * af),
                         "mx":int(mx[t] * af),
                         "inprem":int(inprem[t] * af),
                         "benefit":int(benefit[t] * af),
                         "surrender":int(surrender[t] * af), 
                         "expenses":int(expenses[t] * af),
                         "cashflow":int(cashflow[t] * af)
        })

result_df = pd.DataFrame(results1)

集計部分を少し修正します。
productyearで集計するのは、
商品ごとのキャッシュフローの集計ですが、
それとは別に、productの区分をしないキャッシュフローも集計します。

これは商品合計のキャッシュフローになります。
このproductのラベルをTotalに書き換えて結合します。
ignore_index=Trueはラベルを無視して結合するという意味です。

あとは期間をフィルターにより絞ってresult_all.xlsxへ出力します。

cashflow.py
# 商品別の集計
grouped_product = result_df.groupby(["product", "year"]).sum()
summary_product = grouped_product.reset_index()

# 合計の集計
grouped_total = result_df.groupby("year").sum()
summary_total = grouped_total.reset_index()
# Totalのラベルを付ける
summary_total["product"] = "Total"

# 結合
summary = pd.concat([summary_product, summary_total], ignore_index=True)
# 年度のフィルタ
summary = summary[(summary["year"] >= base_year + 1) & (summary["year"] <= 2030)]
# 出力
summary.to_excel("result_all.xlsx", index=False)

結果のファイルを開くと次のようになります。
商品ごとのキャッシュフローと
合計のキャッシュフローが出力されています。
image.png

まとめ

今回は複数商品のキャッシュフローを一度に計算しました。
保有契約について計算するときは、
単一の商品ということはありません。
前回と合わせて、生命保険会社の保有契約全体についての
将来収支やキャッシュフローが計算できるようになるかと思います。

前の記事
保有契約の将来収支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?