アクチュアリーのためのPython入門(応用編第3回)
保険種類を混ぜた将来キャッシュフロー
📚 アクチュアリーのためのPython入門
この記事は応用編の一部です。
▶ 目次はこちら
はじめに
前回は保有契約の将来収支を計算しました。
これまで、新契約のみを取り扱っていましたが、
保有契約の収支を取り扱う上で、
ポイントの理解ができたかと思います。
ただ、新契約のときも保有契約のときも、
一商品だけしか取り扱っていません。
生命保険会社の保有契約には、さまざまな商品があります。
今回は一商品だけではなく、商品を混ぜた場合の、
将来キャッシュフローを作成します。
商品の比較
今まで作成したの養老保険、定期保険、終身保険について考えます。
これらの商品を同時に取り扱う場合、問題となるのが、
- 満期保険金の有無(定期保険と養老保険)
- 保険期間の違い(終身保険とそれ以外)
- 保険料払込期間の違い(終身保険とそれ以外)
があります。
また、商品によって解約率が異なったり、
死亡指数が異なったりということがありますので、
その前提で進めます。
保有契約のファイル
保有契約のファイルは次のとおりとして、
ファイル名をcontr_all.xlsxとしておきます。
なお、productは商品種類で、Eは養老保険、
Tは定期保険、Wは終身保険です。
キャッシュフロー計算用ファイルの作成
前回までキャッシュフローや保険収支は、
各商品のファイルで作成してきましたが、
今回はキャシュフローを計算するファイルを新しく作成します。
ファイル名はcashflow.pyとしておきます。
このとき、endowment.py、term.py、whole.pyも
計算に必要な関数
- 保険料の計算
PremE、PremT、PremW - 保険料積立金の計算
ResE、ResT、ResW - 純保険料の計算
netResE、netResT、netResW - 解約返戻金の計算
SVE、SVT、SVW
を残して、他の計算式などは削除するか、
引用するときは動かないようにしておくのがベストです。
次回以降に、再度取り上げますが、
if __name__ == "__main__":
この中に入れてあげると、
引用したときは動作しなくなります。
それでは、cashflow.pyのヘッダー部分です。
core.pyから基本的な関数などを利用。
すでに作成しているendowment.py、term.py、whole.pyから、
保険料の関数と解約返戻金の関数を利用します。
また、pandasを使用します。
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とします。
具体的なコードは次のとおりです。
# 死亡指数の関数化
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を引数に加えた関数へ入れ替えます。
なお、補正値の計算位置を変更しています。
(前回の位置ですと何度も計算しなおすことになるので)
# 計算基準年度
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も入れておきます。
他に大きな変更はありません。
# 商品別のキャッシュフロー
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)
集計部分を少し修正します。
productとyearで集計するのは、
商品ごとのキャッシュフローの集計ですが、
それとは別に、productの区分をしないキャッシュフローも集計します。
これは商品合計のキャッシュフローになります。
このproductのラベルをTotalに書き換えて結合します。
ignore_index=Trueはラベルを無視して結合するという意味です。
あとは期間をフィルターにより絞ってresult_all.xlsxへ出力します。
# 商品別の集計
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)
結果のファイルを開くと次のようになります。
商品ごとのキャッシュフローと
合計のキャッシュフローが出力されています。

まとめ
今回は複数商品のキャッシュフローを一度に計算しました。
保有契約について計算するときは、
単一の商品ということはありません。
前回と合わせて、生命保険会社の保有契約全体についての
将来収支やキャッシュフローが計算できるようになるかと思います。
◀ 前の記事
保有契約の将来収支2
▶ 次の記事
キャッシュフローの現在価値
📚 目次
アクチュアリーのためのPython入門