The BasicTermASL_ME Model#

Overview#

The BasicTermASL_ME is an adjustable step length(ASL) model, and projects the cashflows of in-force policies at time 0 and future new business policies issued after time 0. As is the case with BasicTerm_ME, time-dependent cells in BasicTermASL_ME carry out calculations for all model points, and returns values for all the model points as pandas Series or numpy array objects.

Unlike BasicTerm_ME, with BasicTermASL_ME the user can specify the length of each projection step, from 1 month to 1 year. By default, the first 60 steps are monthly projections, while steps after that are annual. This model reads issue date information from model point input, and handles policy anniversaries precisely.

Space Inheritance#

BasicTermASL_ME has 3 spaces, namely Base, Projection and Pricing. Base is the base space of Projection and and Pricing, and most Cells and References are defined in Base. Pricing is for calculating premiums. In Pricing, the issue dates of all model points are set to the projection start date in model_point(). premium_pp() calculates premiums per 1000 sum assured per payment from loading_prem() and the present values of premiums and claims. Pricing.premium_pp() is brought in to Projection as pricing_premium_pp and referenced by Projection.premium_pp().

../../_images/diagram1.png

Projection Steps#

Projection steps are indexed with i. Step i starts from one day after date_(i) and ends on date_(i+1). date_(0) is the projection start date. The start date is specified by a date string assigned to date_init. date_(i) returns a Timestamp object. The length of each projection step can be specified by offset() cells. offset(i) should return the length of step i as a pandas DateOffset object, so that the object can be added to the Timestamp values of date_(i). date_(i) should always be an end-of-month date, so offset(i) should return DateOffset objects such that date_(i) are always end-of-month dates. YearEnd and MonthEnd objects are good examples. By default, ‘2021-12-31’ is set to date_init, and the first 60 steps are monthly and steps after that are annual.

>>> BasicTermASL_ME.Base.date_(0)
Timestamp('2021-12-31 00:00:00')

>>> BasicTermASL_ME.Base.date_(60)
Timestamp('2026-12-31 00:00:00')

>>> BasicTermASL_ME.Base.date_(61)
Timestamp('2027-12-31 00:00:00')

>>> BasicTermASL_ME.Base.offset(0)
<MonthEnd>

>>> BasicTermASL_ME.Base.offset(60)
<YearEnd: month=12>

Policy Anniversary#

When a policy’s anniversary date is in a projection step, some cells, such as pols_lapse() and premiums() calculate their values by separately calculating the parts before and after the anniversary and then adding them up. For example, the formula of pols_lapse() looks like below:

def pols_lapse(i, j=None):

    if j is None:
        return pols_lapse(i, 'LAST') + pols_lapse(i, 'NEXT')

    elif j == 'LAST':
        lapse = 1 - (1 - lapse_rate(i))**(last_part(i) / 12)
        return (pols_if_at(i, "BEG_STEP") - pols_death(i, 'LAST')) * lapse

    elif j == 'NEXT':
        lapse = 1 - (1 - lapse_rate(i+1))**(next_part(i) / 12)
        return (pols_if_at(i, "AFT_NB") - pols_death(i, 'NEXT')) * lapse

    else:
        raise ValueError('invalid j')

When the second parameter j is not given, pols_lapse(i) adds pols_lapse(i, 'LAST') and pols_lapse(i, 'NEXT') and returns the their sum. pols_lapse(i, 'LAST') returns the number of lapsed policies before the policy anniversary in Step i. pols_lapse(i, 'NEXT') returns the number of lapsed policies during Step i after the policy anniversary in Step i. If the length of Step i is shorter than a full year, then there may not be an anniversary date in Step i, in which case pols_lapse(i, 'LAST') should be the entire pols_lapse(i) and pols_lapse(i, 'NEXT') should be zero.

The figure below illustrates adjacent policy terms and the policy anniversary between them occurring during Step i. next_anniversary(i) returns the first anniversary date after date_(i) for all model points as a Series of Timestamp objects. months_in_step(i) returns the number of months in Step i as an integer.

last_part(i) returns the length of time from date_(i) to next_anniversary(i) in months. next_part(i) returns the length of time from next_anniversary(i) to date_(i+1) in months. The fractional portions of last_part(i) and next_part(i) represent residual days.

../../_images/policy_anniversary.png

Model Specifications#

The Base space#

The sole base Space in the BasicTermASL_ME model.

Base is the only base Space defined in the BasicTermASL_ME model, and it contains logic and data commonly used in the sub spaces of Base.

Parameters and References

(In all the sample code below, the global variable Projection refers to the Projection Space.)

model_point_table#

All model points as a DataFrame. The sample model point data was generated by generate_model_points_ASL.ipynb included in the library. By default, model_point() returns this entire model_point_table. The DataFrame has an index named point_id, and has the following columns:

  • age_at_entry

  • sex

  • policy_term

  • policy_count

  • sum_assured

  • issue_date

  • payment_freq

  • payment_term

Cells with the same names as these columns return the corresponding columns.

The DataFrame is saved in the Excel file model_point_table.xlsx located in the model folder. model_point_table is created by Projection’s new_pandas method, so that the DataFrame is saved in the separate file.

disc_rate_ann#

Annual discount rates by duration as a pandas Series.

>>> Projection.disc_rate_ann
year
0      0.00000
1      0.00555
2      0.00684
3      0.00788
4      0.00866

146    0.03025
147    0.03033
148    0.03041
149    0.03049
150    0.03056
Name: disc_rate_ann, Length: 151, dtype: float64

The Series is saved in the Excel file disc_rate_ann.xlsx placed in the model folder. disc_rate_ann is created by Projection’s new_pandas method, so that the Series is saved in the separate file.

mort_table#

Mortality table by age and duration as a DataFrame. See basic_term_sample.xlsx included in this library for how the sample mortality rates are created.

>>> Projection.mort_table
            0         1         2         3         4         5
Age
18   0.000231  0.000254  0.000280  0.000308  0.000338  0.000372
19   0.000235  0.000259  0.000285  0.000313  0.000345  0.000379
20   0.000240  0.000264  0.000290  0.000319  0.000351  0.000386
21   0.000245  0.000269  0.000296  0.000326  0.000359  0.000394
22   0.000250  0.000275  0.000303  0.000333  0.000367  0.000403
..        ...       ...       ...       ...       ...       ...
116  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000
117  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000
118  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000
119  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000
120  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000

[103 rows x 6 columns]

The DataFrame is saved in the Excel file mort_table.xlsx placed in the model folder. mort_table is created by Projection’s new_pandas method, so that the DataFrame is saved in the separate file.

See also

date_init#

The projection start date as a string in the YYYY-MM-DD format. The start date needs to be an end-of-month date. By default, ‘2021-12-31’ is assigned.

np#

The numpy module.

pd#

The pandas module.

Projection parameters#

date_(i)

Date at each projection step.

months_(i)

Number of elapsed months

months_in_step(i)

Returns the number of months in step i

step_to_month(i)

Returns the number of months for step i

max_proj_len()

The upper bound for the time index i

month_to_step(m)

Returns step index for months

offset(i)

Time interval in step i

Model point data#

age(i)

The attained age at date_(i).

age_at_entry()

The age at entry of the model points

issue_date()

Issue ages of the model points

model_point()

Target model points

duration_m(i)

Duration of model points at i in months

duration_y(i)

Duration of model points at period i in years

sex()

The sex of the model points

sum_assured()

The sum assured of the model points

policy_term()

The policy term of the model points.

payment_freq()

Payment frequency

payment_lag(i, j)

Average timing of premium payments.

payment_term()

Premium payment period in years

Assumptions#

mort_rate(i)

Mortality rate to be applied at time t

mort_table_reindexed()

MultiIndexed mortality table

expense_acq()

Acquisition expense per policy

expense_maint()

Annual maintenance expense per policy

inflation_factor(i)

The inflation factor for Period i.

inflation_rate()

Inflation rate

lapse_rate(i)

Lapse rate

proj_len()

Projection length in months

disc_factors()

Discount factors.

disc_factors_prem(j)

Discount factors for premiums.

disc_rate(i)

Discount rate for period i

Policy attributes#

claim_pp(i)

Claim per policy

loading_prem()

Loading per premium

premium_pp()

Premium per policy

is_active(i)

Indicates if model points are active.

is_paying(i)

Indicates if premiums are being paid

is_maturing(i)

Indicates if model points are maturing in step i.

last_part(i[, freq_id])

Length of time till next anniversary in step i

next_part(i)

Lentgh of time after next anniversary

next_anniversary(i[, freq_id])

Nex anniversary dates

net_premium_rate()

Net premium per policy

pay_count(i[, j])

Number of premium payments in step i

Policy decrement#

pols_death(i[, j])

Number of death

pols_if(i)

Number of policies in-force

pols_if_at(i, timing)

Number of policies in-force

pols_if_avg(i)

Average number of policies in-force in step i

pols_if_init()

Initial number of policies in-force

pols_lapse(i[, j])

Number of lapse in step i

pols_maturity(i)

Number of maturing policies

pols_new_biz(i)

Number of new business policies

pols_if_pay(i, j)

Number of policies in-force for premium payment

Cashflows#

premiums(i[, j])

Premium income

claims(i)

Claims

commissions(i)

Commissions

expenses(i)

Expenses

net_cf(i)

Net cashflow

Present values#

pv_claims()

Present value of claims

pv_commissions()

Present value of commissions

pv_expenses()

Present value of expenses

pv_net_cf()

Present value of net cashflows.

pv_pols_if()

Present value of policies in-force

pv_pols_if_pay()

Present value of polices in-force for premium payments

pv_premiums()

Present value of premiums

Results#

result_cells(name[, point_id, j])

Output values of a given cells

result_cf()

Result table of cashflows

result_pols()

Result table of policy decrement

result_pv()

Result table of present value of cashflows

Validation#

check_pay_count()

Check pay_count().

The Pricing space#

Space to calculate premiums

This space is for calculating premiums. To calculate premiums, model_point() adjusts the issue ages read from model_point_table so that all the model points become new business issued the day after date_(0).

model_point()

Target model points

premium_pp()

Premium per policy

net_premium_rate()

Net premium 1000 sum assured.

The Projection space#

Space to carry out cashflow projections

This space is for carrying out cashflow projections. The only difference from its base space, Base is that Pricing.premium_pp is assigned to pricing_premium_pp in this space, and premium_pp() is overridden to reference pricing_premium_pp and returns the values of it.

pricing_premium_pp#

Pricing.premium_pp

premium_pp()

Premium per policy