Creating premium table

This notebook shows how to create the premium table from BasicTerm_M model to be used in BasicTerm_SE and BasicTerm_ME.

To calculate premiums, the present values of claims and annuities need to be taken at the beginning of the policy terms, but BasicTerm_SE and BasicTerm_ME start projections for in-force model points at time zero after their issues, so the present values are not available. Such being the case, a premium rate table needs to be fed into the models. This notebook demonstrates how to create the premium table using BasicTerm_M model.

By default, BasicTerm_M and BasicTerm_ME models the same product using the same assumption, and BasicTerm_M is the new business version of BasicTerm_ME.

Here’re the steps to create the premium table.

  • Read the BasicTerm_M model.

  • Replace the model point table with the one for the premium table. By default, the premium rates vary only by age_at_entry and policy_term. The range of age_at_entry is 20 to 59, and policy_term takes the value of either 10, 15 or 20. The model point data for the premium table is created from the combinations of the two attributes.

  • Calculate per-policy premiums for the model points.

  • Create the premium table by processing the result of the above.

[1]:
import itertools
import modelx as mx
import pandas as pd

The code below reads the BasicTerm_M model and assign it to model and the Pojection space to space for later use.

[2]:
model = mx.read_model('BasicTerm_M')
space = model.Projection

The code below creates the model point table for the premium table. To calculate the premium rates as premium per sum assured, 1’s are entered in the sum_assured column.

[3]:
df = pd.DataFrame(itertools.product(range(20, 60), [10, 15, 20]), columns=['age_at_entry', 'policy_term'])
df.index += 1
df.index.name = 'point_id'
df["sum_assured"] = 1
df
[3]:
age_at_entry policy_term sum_assured
point_id
1 20 10 1
2 20 15 1
3 20 20 1
4 21 10 1
5 21 15 1
... ... ... ...
116 58 15 1
117 58 20 1
118 59 10 1
119 59 15 1
120 59 20 1

120 rows × 3 columns

The code below replaces the default model point table with the new one just created above.

[4]:
space.model_point_table = df

Since the sum assured of all the model points are all 1, premium_pp returns 0 because of the rounding operation in its formula. Below defines the premium_rate by modifying the premium_pp formula and removing the rounding so that the formula can be used for creating the premium rates.

[5]:
space.premium_pp.formula
[5]:
def premium_pp():
    """Monthly premium per policy

    Monthly premium amount per policy defined as::

        round((1 + loading_prem()) * net_premium(), 2)

    .. versionchanged:: 0.2.0
       The ``t`` parameter is removed.

    .. seealso::

        * :func:`loading_prem`
        * :func:`net_premium_pp`

    """
    return np.around((1 + loading_prem()) * net_premium_pp(), 2)
[6]:
@mx.defcells
def premium_rate():
    return (1 + loading_prem()) * net_premium_pp()
[7]:
premium_rate()
[7]:
array([4.64097542e-05, 5.20142069e-05, 5.74162410e-05, 4.76584950e-05,
       5.35875249e-05, 5.93504754e-05, 4.90032815e-05, 5.52810109e-05,
       6.14332619e-05, 5.04508083e-05, 5.71035347e-05, 6.36761718e-05,
       5.20083923e-05, 5.90648080e-05, 6.60919024e-05, 5.36840347e-05,
       6.11754713e-05, 6.86943971e-05, 5.54864901e-05, 6.34471903e-05,
       7.14989789e-05, 5.74253436e-05, 6.58927638e-05, 7.45224992e-05,
       5.95110960e-05, 6.85262433e-05, 7.77835053e-05, 6.17552588e-05,
       7.13630677e-05, 8.13024264e-05, 6.41704608e-05, 7.44202123e-05,
       8.51017838e-05, 6.67705658e-05, 7.77163572e-05, 8.92064251e-05,
       6.95708052e-05, 8.12720745e-05, 9.36437881e-05, 7.25879257e-05,
       8.51100388e-05, 9.84441965e-05, 7.58403545e-05, 8.92552633e-05,
       1.03641193e-04, 7.93483847e-05, 9.37353646e-05, 1.09271914e-04,
       8.31343827e-05, 9.85808605e-05, 1.15377512e-04, 8.72230213e-05,
       1.03825505e-04, 1.22003627e-04, 9.16415414e-05, 1.09506665e-04,
       1.29200929e-04, 9.64200458e-05, 1.15665745e-04, 1.37025717e-04,
       1.01591830e-04, 1.22348667e-04, 1.45540604e-04, 1.07193757e-04,
       1.29606410e-04, 1.54815292e-04, 1.13266673e-04, 1.37495622e-04,
       1.64927437e-04, 1.19855887e-04, 1.46079307e-04, 1.75963645e-04,
       1.27011700e-04, 1.55427611e-04, 1.88020582e-04, 1.34790017e-04,
       1.65618702e-04, 2.01206244e-04, 1.43253028e-04, 1.76739777e-04,
       2.15641388e-04, 1.52469982e-04, 1.88888191e-04, 2.31461155e-04,
       1.62518071e-04, 2.02172755e-04, 2.48816911e-04, 1.73483423e-04,
       2.16715197e-04, 2.67878332e-04, 1.85462240e-04, 2.32651824e-04,
       2.88835763e-04, 1.98562080e-04, 2.50135419e-04, 3.11902899e-04,
       2.12903328e-04, 2.69337392e-04, 3.37319808e-04, 2.28620863e-04,
       2.90450228e-04, 3.65356364e-04, 2.45865958e-04, 3.13690280e-04,
       3.96316114e-04, 2.64808454e-04, 3.39300945e-04, 4.30540656e-04,
       2.85639234e-04, 3.67556282e-04, 4.68414557e-04, 3.08573055e-04,
       3.98765135e-04, 5.10370897e-04, 3.33851788e-04, 4.33275832e-04,
       5.56897468e-04, 3.61748118e-04, 4.71481528e-04, 6.08543708e-04])

The code below creates the premium table as a Series indexed by age_at_entry and policy_term from the result above. The table is created by modifying the copy of the model point table and assigning the result of premium_rate above.

[8]:
premium_rate_table = df.copy()
premium_rate_table["premium_rate"] = space.premium_rate()
del premium_rate_table["sum_assured"]
premium_rate_table.set_index(["age_at_entry", "policy_term"], inplace=True)
premium_rate_table["premium_rate"]   # As Series
[8]:
age_at_entry  policy_term
20            10             0.000046
              15             0.000052
              20             0.000057
21            10             0.000048
              15             0.000054
                               ...
58            15             0.000433
              20             0.000557
59            10             0.000362
              15             0.000471
              20             0.000609
Name: premium_rate, Length: 120, dtype: float64