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
Posted at

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

保険負債の金利変動による影響

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

はじめに

前回は将来キャッシュフローの現在価値を計算しました。
今回は金利を変動させた場合の影響を見てみます。

金利を変動させた現在価値の平均と、
平均金利を用いた現在価値との差を
保証とオプションの時間価値といいます。

保証とオプションの時間価値

キャッシュフローの現在価値(以下、現在推計といいます)は
確率加重平均を使います。現在推計を計算するための

  • 死亡率
  • 解約率
  • 事業費(経費)
  • 金利

のそれぞれの確率加重平均で計算すれば、
その結果の現在推計も確率加重平均となるかと思いますが、
それは、平均に対して上下が対称である場合です。

金利の変動に対して対称でない場合があります。
例えば、変額保険の最低保証や、契約者配当などです。
詳しくはアクチュアリー会のホームページに資料があります。

以下のリンク先の

  • 「経済価値ベースのソルベンシー規制における解約・失効等に関する考察(中間報告)」
  • 「経済価値ベースのソルベンシー規制における割引率および金利リスク等に関する考察(中間報告)」
    を参照ください。

ここでは、そのうちの動的解約を取り上げます。
動的解約とは、貯蓄性のある保険商品において、
金利が上昇したときに解約が増加し、
金利が下降したときに解約が減少するというものです。

金利モデルや動的解約のモデルにも様々な種類があるのですが、
本題ではありませんので、ここでは単純なモデルとします。

前提条件

単純化するために前提条件は次のとおりです。

  • 前回と異なり、金利は全年限で一定
  • 金利と予定利率との差を元に解約率を変動させる
  • 金利は正規乱数を使って変動させる

まず、cashflow.pyに動的解約を加えます。
前回までコードはそのままにしておきたいので、
コピーして、ファイル名をcashflow_r.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全体のコードは次のとおりです。

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としておきます。

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というライブラリが必要です。
必要に応じてインストールしてください。

まずはヘッダー部分です。

presentvalue_r.py
# 動的解約
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は試行回数で、その回数だけ金利を生成します。

presentvalue_r.py
# 変動金利(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

金利を変動させて計算て、その結果の平均を取ります。

presentvalue_r.py
# 現価率
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がどんな偏りとなっているか視覚化してみました。
ヒストグラムは次のとおりです。
pv_distribution.png

かなり右側へ裾が広がっているのが確認できます。
このヒストグラムは、PythonのMatplotlibというライブラリを使ったのですが、
環境によって使えたり使えなかったり、
設定もいろいろとあるので、省略させてください。

Excelのグラフを使い慣れている方にとっては、
pandasでExcelへ出力してグラフ化した方が便利かと思います。

まとめ

今回は金利変動に伴う現在推計への影響について、
簡単なサンプルをあげてみました。

保証とオプションの時間価値という、
言葉だけではピンとこない内容ですが、
単純な数値でもいいので、イメージを持つことで
理解が深まるかと思います。

前の記事
キャッシュフローの現在価値

次の記事

📚 目次
アクチュアリーのための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?