Active Lives - Deterministic

Valuation Model

Documentation

class ActiveLivesValEMD(*, extract_base: pandas.core.frame.DataFrame, extract_riders: pandas.core.frame.DataFrame, valuation_dt: pandas._libs.tslibs.timestamps.Timestamp, assumption_set: str, net_benefit_method: str, modifier_ctr: float = 1.0, modifier_incidence=1.0, modifier_interest: float = 1.0, modifier_lapse=1.0, modifier_mortality=1.0)

Active lives deterministic valuation extract model.

This model takes an extract of policies and runs them through the respective Policy Models based on the COVERAGE_ID column (i.e., whether it is base policy or rider).

Parameters

extract_baseDataFrame

The active lives base extract.

extract_ridersDataFrame

The active lives rider extract.

valuation_dtTimestamp

The valuation date which reserves are based.

assumption_setstr

The assumption set to use for running the model. Options are :

  • stat

  • gaap

  • best-estimate

net_benefit_methodstr

The net benefit method. Options are :

  • NLP = Net level premium

  • PT1 = 1 year preliminary term

  • PT2 = 2 year preliminary term

Sensitivities

modifier_ctrfloat

Modifier for CTR.

modifier_incidence

The incidence rate modifier.

modifier_interestfloat

Interest rate modifier.

modifier_lapse

The withdraw rate modifier

modifier_mortality

The withdraw rate modifier

Meta

model_versionstr

The model version generated by versioneer.

last_commitstr

The last git commit.

run_date_timeTimestamp

The run date and time.

Intermediates

recordsdict

The extract transformed to records.

Returns

projectedDataFrame

The projected reserves for the policyholders.

time_0DataFrame

The time 0 reserve for the policyholders

errorslist

Any errors captured.

Steps

  1. Create Records from Extract - Turn extract into a list of records for each row in extract.

  2. Run Records with Policy Models - Foreach record run through respective policy model based on COVERAGE_ID value.

  3. Get Time0 Values - Filter projected reserves frame down to time_0 reserve for each record.

Usage

import pandas as pd
from footings_idi_model.models import ActiveLivesValEMD
extract_base = pd.read_csv(
  "active-lives-sample-base.csv",
  parse_dates=["BIRTH_DT", "POLICY_START_DT", "PREMIUM_PAY_TO_DT", "POLICY_END_DT"]
)
extract_base
POLICY_ID BIRTH_DT GENDER TOBACCO_USAGE COVERAGE_ID POLICY_START_DT PREMIUM_PAY_TO_DT POLICY_END_DT ELIMINATION_PERIOD GROSS_PREMIUM GROSS_PREMIUM_FREQ BENEFIT_AMOUNT IDI_OCCUPATION_CLASS IDI_CONTRACT IDI_BENEFIT_PERIOD IDI_MARKET COLA_PERCENT
0 M1 1978-08-01 F N BASE 2018-03-06 2048-07-31 2048-07-31 360 10 MONTH 100 M AS TO70 INDV 0.02
1 M1 1978-08-01 F N ROP 2018-03-06 2048-07-31 2048-07-31 360 10 MONTH 100 M AS TO70 INDV 0.02
2 M2 1967-11-03 M N BASE 2018-01-19 2032-11-02 2032-11-02 14 10 MONTH 100 4 AS 18M INDV 0.00
3 M3 1982-06-17 M N BASE 2007-06-19 2047-06-16 2047-06-16 180 10 MONTH 100 M AS TO65 INDV 0.03
4 M3 1982-06-17 M N ROP 2007-06-19 2047-06-16 2047-06-16 180 10 MONTH 100 M AS TO65 INDV 0.03
5 M4 1974-03-08 F Y BASE 2009-10-06 2041-03-07 2041-03-07 90 10 MONTH 100 1 AS TO67 INDV 0.03
6 M4 1974-03-08 F Y ROP 2009-10-06 2041-03-07 2041-03-07 90 10 MONTH 100 1 AS TO67 INDV 0.03
7 M5 1972-12-10 F Y BASE 2003-01-29 2037-12-09 2037-12-09 90 10 MONTH 100 2 AO TO65 INDV 0.03
8 M5 1972-12-10 F Y ROP 2003-01-29 2037-12-09 2037-12-09 90 10 MONTH 100 2 AO TO65 INDV 0.03
extract_riders = pd.read_csv("active-lives-sample-riders.csv")
extract_riders
POLICY_ID COVERAGE_ID RIDER_ATTRIBUTE VALUE
0 M1 ROP rop_return_freq 10.0
1 M1 ROP rop_return_percent 0.8
2 M1 ROP rop_claims_paid 0.0
3 M3 ROP rop_return_freq 7.0
4 M3 ROP rop_return_percent 0.5
5 M3 ROP rop_claims_paid 0.0
6 M4 ROP rop_return_freq 10.0
7 M4 ROP rop_return_percent 0.5
8 M4 ROP rop_claims_paid 1000.0
9 M5 ROP rop_return_freq 7.0
10 M5 ROP rop_return_percent 0.8
11 M5 ROP rop_claims_paid 0.0
model = ActiveLivesValEMD(
    extract_base=extract_base,
    extract_riders=extract_riders,
    valuation_dt=pd.Timestamp("2020-03-31"),
    assumption_set="STAT",
    net_benefit_method="NLP",
)

To run the model call the run method which returns -

  • the projected reserve for each policy in the extract (projected),

  • the reserve as of the valuation date for each policy (time0), and

  • policies that error out when the model runs (run).

projected, time0, errors = model.run()
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)

Note the time0 and projected have the same columns with time0 being a subset of the projected frame.

projected.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 207 entries, 2 to 34
Data columns (total 24 columns):
 #   Column          Non-Null Count  Dtype         
---  ------          --------------  -----         
 0   MODEL_VERSION   207 non-null    object        
 1   LAST_COMMIT     207 non-null    object        
 2   RUN_DATE_TIME   207 non-null    datetime64[ns]
 3   SOURCE          207 non-null    object        
 4   POLICY_ID       207 non-null    object        
 5   COVERAGE_ID     207 non-null    object        
 6   DATE_BD         207 non-null    datetime64[ns]
 7   DATE_ED         207 non-null    datetime64[ns]
 8   DURATION_YEAR   207 non-null    Int64         
 9   LIVES_BD        207 non-null    float64       
 10  LIVES_MD        207 non-null    float64       
 11  LIVES_ED        207 non-null    float64       
 12  DISCOUNT_BD     207 non-null    float64       
 13  DISCOUNT_MD     207 non-null    float64       
 14  DISCOUNT_ED     207 non-null    float64       
 15  BENEFIT_AMOUNT  207 non-null    int64         
 16  INCIDENCE_RATE  207 non-null    float64       
 17  BENEFIT_COST    206 non-null    float64       
 18  PVFB            207 non-null    float64       
 19  PVFNB           207 non-null    float64       
 20  ALR_BD          207 non-null    float64       
 21  ALR_ED          207 non-null    float64       
 22  ALR_DATE        207 non-null    datetime64[ns]
 23  ALR             207 non-null    float64       
dtypes: Int64(1), datetime64[ns](4), float64(13), int64(1), object(5)
memory usage: 40.6+ KB
projected
MODEL_VERSION LAST_COMMIT RUN_DATE_TIME SOURCE POLICY_ID COVERAGE_ID DATE_BD DATE_ED DURATION_YEAR LIVES_BD ... DISCOUNT_ED BENEFIT_AMOUNT INCIDENCE_RATE BENEFIT_COST PVFB PVFNB ALR_BD ALR_ED ALR_DATE ALR
2 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M1 BASE 2020-03-06 2021-03-06 3 0.997402 ... 0.915142 100 0.002228 30.688392 773.833863 746.848211 28.703659 41.720113 2020-03-31 29.45
3 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M1 BASE 2021-03-06 2022-03-06 4 0.995975 ... 0.888487 100 0.002365 32.215403 745.408331 707.382176 41.720113 53.600038 2021-03-31 42.44
4 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M1 BASE 2022-03-06 2023-03-06 5 0.994452 ... 0.862609 100 0.002525 33.930135 716.479136 669.120430 53.600038 64.120353 2022-03-31 54.26
5 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M1 BASE 2023-03-06 2024-03-06 6 0.992811 ... 0.837484 100 0.002684 35.552118 686.943074 632.029939 64.120353 73.336151 2023-03-31 64.71
6 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M1 BASE 2024-03-06 2025-03-06 7 0.991034 ... 0.813092 100 0.002875 37.431773 656.946344 596.079170 73.336151 80.948847 2024-03-31 73.83
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
30 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M5 ROP 2033-01-29 2034-01-29 31 0.921102 ... 0.399987 100 0.000000 0.000000 214.558780 150.518335 168.757613 264.151963 2033-03-31 182.07
31 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M5 ROP 2034-01-29 2035-01-29 32 0.913448 ... 0.388337 100 0.000000 0.000000 214.558780 118.046290 264.151963 363.484402 2034-03-31 278.83
32 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M5 ROP 2035-01-29 2036-01-29 33 0.905227 ... 0.377026 100 0.000000 0.000000 214.558780 86.782017 363.484402 467.074761 2035-03-31 379.26
33 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M5 ROP 2036-01-29 2037-01-29 34 0.896410 ... 0.366045 100 0.000000 0.000000 214.558780 56.701535 467.074761 575.287050 2036-03-31 483.86
34 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M5 ROP 2037-01-29 2038-01-29 35 0.886962 ... 0.355383 100 0.000000 672.000000 214.558780 27.781635 575.287050 0.000000 2037-03-31 0.00

207 rows × 24 columns

time0
MODEL_VERSION LAST_COMMIT RUN_DATE_TIME SOURCE POLICY_ID COVERAGE_ID ALR_DATE ALR
2 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M1 BASE 2020-03-31 29.45
2 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M1 ROP 2020-03-31 174.05
2 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M2 BASE 2020-03-31 26.68
12 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M3 BASE 2020-03-31 188.23
12 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M3 ROP 2020-03-31 339.68
10 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M4 BASE 2020-03-31 77.36
10 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M4 ROP 2020-03-31 0.00
17 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValBasePMD M5 BASE 2020-03-31 0.00
17 0.6.0 5d026f4756f03f9cb797de5a8f0c3c6d2b349ccb 2021-03-25 11:27:59.453717 AValRopRPMD M5 ROP 2020-03-31 277.58
errors
[]

An audit of the model is ran by calling the audit method as shown below.

model.audit("Audit-ActiveLivesValEMD.xlsx")
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)
/home/runner/work/footings-idi-model/footings-idi-model/.venv/lib/python3.8/site-packages/pandas/core/series.py:726: RuntimeWarning: divide by zero encountered in log
  result = getattr(ufunc, method)(*inputs, **kwargs)

The audit file can be downloaded here.

Projection Model

Documentation

To be completed.

Usage

To be completed.