アクチュアリーのためのPython入門(応用編第5回)
保険負債の金利変動による影響
📚 アクチュアリーのためのPython入門
この記事は応用編の一部です。
▶ 目次はこちら
はじめに
前回は将来キャッシュフローの現在価値を計算しました。
今回は金利を変動させた場合の影響を見てみます。
金利を変動させた現在価値の平均と、
平均金利を用いた現在価値との差を
保証とオプションの時間価値といいます。
保証とオプションの時間価値
キャッシュフローの現在価値(以下、現在推計といいます)は
確率加重平均を使います。現在推計を計算するための
- 死亡率
- 解約率
- 事業費(経費)
- 金利
のそれぞれの確率加重平均で計算すれば、
その結果の現在推計も確率加重平均となるかと思いますが、
それは、平均に対して上下が対称である場合です。
金利の変動に対して対称でない場合があります。
例えば、変額保険の最低保証や、契約者配当などです。
詳しくはアクチュアリー会のホームページに資料があります。
以下のリンク先の
- 「経済価値ベースのソルベンシー規制における解約・失効等に関する考察(中間報告)」
- 「経済価値ベースのソルベンシー規制における割引率および金利リスク等に関する考察(中間報告)」
を参照ください。
ここでは、そのうちの動的解約を取り上げます。
動的解約とは、貯蓄性のある保険商品において、
金利が上昇したときに解約が増加し、
金利が下降したときに解約が減少するというものです。
金利モデルや動的解約のモデルにも様々な種類があるのですが、
本題ではありませんので、ここでは単純なモデルとします。
前提条件
単純化するために前提条件は次のとおりです。
- 前回と異なり、金利は全年限で一定
- 金利と予定利率との差を元に解約率を変動させる
- 金利は正規乱数を使って変動させる
まず、cashflow.pyに動的解約を加えます。
前回までコードはそのままにしておきたいので、
コピーして、ファイル名をcashflow_r.pyとしておきます。
金利による解約率の上昇は強めに、
下降は弱めに設定しています。
これで解約率を非対称にします。
この解約率の係数をもとの解約率に乗じます。
# 金利による解約率の係数
def dyn_qw(r):
dr = r - 0.006
w = 1 + 15 * max(dr, 0) + 0.5 * min(dr, 0)
return w
解約率への反映は貯蓄性の養老保険と終身保険のみに適用します。
キャッシュフローを計算する関数create_cashflowの
引数には金利rを付け加えます。
cashflow_r.py全体のコードは次のとおりです。
# 動的解約
from core import *
from endowment import PremE, SVE
from term import PremT, SVT
from whole import PremW, SVW
import pandas as pd
# 死亡指数の関数化
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]
# 保険料の計算式
premium_func = {
"E":PremE,
"T":PremT,
"W":PremW
}
def prem_func(product, sex, age, term):
return premium_func[product](sex, age, term)
# 金利による解約率の係数
def dyn_qw(r):
dr = r - 0.006
w = 1 + 15 * max(dr, 0) + 0.5 * min(dr, 0)
return w
# 解約率(養老保険)金利によるによる変化あり
def qwxE(t, m, r):
dw = dyn_qw(r)
if t == 0:
w = 0.05
elif t < 3:
w = 0.03
else:
w = 0.02
return w * dw
# 解約率(定期保険)
def qwxT(t, m, r):
if t == 0:
w = 0.08
elif t < 3:
w = 0.05
else:
w = 0.03
return w
# 解約率(終身保険)金利によるによる変化あり
def qwxW(t, m, r):
dw = dyn_qw(r)
if t == 0:
w = 0.03
elif t >= m:
w = 0.01
else:
w = 0.02
return w * dw
# 解約率の関数
surrender_func = {
"E":qwxE,
"T":qwxT,
"W":qwxW
}
def qwx(product, t, m, r):
return surrender_func[product](t, m, r)
# 解約返戻金の計算式
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
# キャッシュフロー計算の関数化 base_yearは計算基準年度
def create_cashflow(r, 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, r) # 金利を加える
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)
# キャッシュフローの項目
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(benefit[t] + surrender[t] \
+ expenses[t] - inprem[t])
# 補整値の計算
af = amount / lx[base_year - policy + 1]
# 商品別のキャッシュフロー
results1.append({"product":product,
"year":policy + t,
"benefit":int(benefit[t] * af),
"surrender":int(surrender[t] * af),
"expenses":int(expenses[t] * af),
"inprem":int(inprem[t] * af),
"cashflow":int(cashflow[t] * af)
})
result_df = pd.DataFrame(results1)
# 商品別の集計
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]
return summary
# キャッシュフローの出力
if __name__ == "__main__":
summary = create_cashflow()
summary.to_excel("result_all2.xlsx", index=False)
まず、金利の変動がない状態で、現在推計を計算します。
固定金利は前回の経過9年の金利を用います。
ファイル名を前回とは変えてpresentvalue_r.pyとしておきます。
from core import *
from cashflow_r import create_cashflow
rate0 = 0.00877
# 現価率
def DF(t, r):
return (1 + r) ** (-t-0.5)
# 計算基準年度
base_year = 2022
# キャッシュフローの計算
summary = create_cashflow(rate0, base_year)
# Totalのみ抽出
summary = summary[summary["product"] == "Total"]
# 経過年数を追加
summary["t"] = summary["year"] - base_year - 1
# 現在価値を計算
PV = 0
for _, row in summary.iterrows():
t = int(row["t"])
cf = row["cashflow"]
PV += cf * DF(t, rate0)
print(int(PV))
結果は、
2689570
と表示されます。
続いて、動的解約を反映させますが、
その前に準備として、乱数を生成するために、
numpyというライブラリが必要です。
必要に応じてインストールしてください。
まずはヘッダー部分です。
# 動的解約
from core import *
from cashflow_r import create_cashflow
import numpy as np
np.random.seed(42)
rate0 = 0.00877
Pythonでは疑似乱数が使われていて、
シード値が同じならば、乱数の並びは同じになります。
ここでは、みなさんの結果を同じにするため、
np.random.seed(42)でシード値を42と固定しています。
次に変動金利部分です。epsは正規乱数で、
標準偏差sigmaに乗じて変動させています。
scenarioは試行回数で、その回数だけ金利を生成します。
# 変動金利(10年金利)
def delta_rate(scenario):
rates = []
sigma = 0.011204
for i in range(scenario):
eps = np.random.normal(0, 1)
dr = rate0 + sigma * eps
rates.append(dr)
return rates
金利を変動させて計算て、その結果の平均を取ります。
# 現価率
def DF(t, r):
return (1 + r) ** (-t-0.5)
# 計算回数
scenario = 1000
# 計算基準年度
base_year = 2022
# 10年金利の推計
d = delta_rate(scenario)
# 計算結果のリスト
PV_list = []
for n in range(scenario):
# キャッシュフローの計算
summary = create_cashflow(d[n], base_year)
# Totalのみ抽出
summary = summary[summary["product"] == "Total"]
# 経過年数を追加
summary["t"] = summary["year"] - base_year - 1
# 現在価値を計算
PV = 0
for _, row in summary.iterrows():
t = int(row["t"])
cf = row["cashflow"]
PV += cf * DF(t, d[n])
PV_list.append(round(PV,0))
average = sum(PV_list) / len(PV_list)
print(int(average))
1000回キャッシュフローを計算していますので、
少し時間はかかります。
計算結果は、
3147709
と表示されます。
動的解約を取り入れることで、
現在推計が増加する簡単な例をあげました。
結果の視覚化
PV_listがどんな偏りとなっているか視覚化してみました。
ヒストグラムは次のとおりです。

かなり右側へ裾が広がっているのが確認できます。
このヒストグラムは、PythonのMatplotlibというライブラリを使ったのですが、
環境によって使えたり使えなかったり、
設定もいろいろとあるので、省略させてください。
Excelのグラフを使い慣れている方にとっては、
pandasでExcelへ出力してグラフ化した方が便利かと思います。
まとめ
今回は金利変動に伴う現在推計への影響について、
簡単なサンプルをあげてみました。
保証とオプションの時間価値という、
言葉だけではピンとこない内容ですが、
単純な数値でもいいので、イメージを持つことで
理解が深まるかと思います。
◀ 前の記事
キャッシュフローの現在価値
▶ 次の記事
📚 目次
アクチュアリーのためのPython入門