The BasicTerm_M Model#
The BasicTerm_M model is a faster reimplementation of
BasicTerm_M model reproduces the same results as
BasicTerm_S much faster.
Each formula to be applied to all the model points
operates on the entire set of model points at once
with the help of Numpy and Pandas.
The default product specs, assumptions and input data
are the same as
The main advantage of the
BasicTerm_M model over the
BasicTerm_S model is its speed.
Below is the result of a simple speed comparison between the two models.
The machine used for this comparison is a consumer PC equipped
with Intel Core i5-6500T CPU and 16GB RAM.
>>> timeit.timeit("[Projection[i].pv_net_cf() for i in range(1, 101)]",globals=globals(), number=1) 7.6481730999998945
>>> timeit.timeit("pv_net_cf()",globals=globals(), number=1) 1.3366562999999587
Note that only the first 100 model points were run with
while all the 10000 model points were run with
BasicTerm_S took about 7.6 seconds for the 100 model points,
BasicTerm_M took only 1.3 seconds for the 10000 model points.
runs about 580 times faster than
Most formulas in the
are the same as those in
However, some formulas are updated since they cannot
be applied to vector operations without change.
For example, below shows how
pols_maturity, the number of maturing policies
at time t, is defined differently in
BasicTerm_S and in
def pols_maturity(t): if t == policy_term() * 12: return pols_if(t-1) - pols_lapse(t-1) - pols_death(t-1) else: return 0
def pols_maturity(t): return (t == policy_term() * 12) * (pols_if(t-1) - pols_lapse(t-1) - pols_death(t-1))
policy_term() returns an integer,
such as 10 indicating a policy term of the selected model point in years,
if clause checks if the value of
is equal to the policy term in month:
>>> policy_term() 120 >>> pols_maturity(120) 0.6534679117893804
a Series of policy terms of all the model points.
If the if clause were
defined in the same way as in the
it would result in an error,
because the condition
t == policy_term() * 12 for a certain
returns a Series of boolean values and it is ambiguous
for the Series to be the if condition.
Further more, whether the
if branch or the
else branch should
be evaluated needs to be determined element-wise,
if statement would not allow such element-wise branching.
Instead of using the
if statement, the formula in
achieves the element-wise conditional operation by multiplication
by a Series of boolean values.
In the formula in
(pols_if(t-1) - pols_lapse(t-1) - pols_death(t-1))
returns the numbers of policies at time t for all the model points
as a Series.
(t == policy_term() * 12) replaces
the numbers of policies with 0 for model points whose policy terms in month
are not equal to
t. This operation is effectively an element-wise if
>>> policy_term() point_id 1 10 2 20 3 10 4 20 5 15 .. 9996 20 9997 15 9998 20 9999 20 10000 15 Name: policy_term, Length: 10000, dtype: int64 >>> (120 == policy_term() * 12) point_id 1 True 2 False 3 True 4 False 5 False 9996 False 9997 False 9998 False 9999 False 10000 False Name: policy_term, Length: 10000, dtype: bool >>> pols_maturity(120) point_id 1 0.653468 2 0.000000 3 0.650917 4 0.000000 5 0.000000 9996 0.000000 9997 0.000000 9998 0.000000 9999 0.000000 10000 0.000000 Length: 10000, dtype: float64
Reading the model#
To read the model from Spyder, right-click on the empty space in MxExplorer,
and select Read Model.
Click the folder icon on the dialog box and select the
Getting the results#
By default, the model has Cells
for outputting projection results as listed in the
result_cf() outputs total cashflows of all the model points,
result_pv() outputs the present values of the cashflows
by model points.
Both Cells outputs the results as pandas DataFrame.
By following the same steps explained in the Quick Start page using this model, You can get the results in an MxConsole and show the results as tables in MxDataViewer.
Changing the model point#
>>> Projection.model_point.formula def model_point(): return model_point_table
The calculations in
Projection apply to all the model points
To limit the calculation target, change the
model_point() returns a DataFrame that contains
only the target rows.
For example, to select only the model point 1:
>>> Projection.model_point.formula def model_point(): return model_point_table.loc[1:1]
When selecting only one model point, make sure that
returns the model point as a DataFrame not as a Series.
In the code example above,
is specified instead of
model_point_table.loc would return the model point as a Series.
The Projection Space#
The main Space in the
Parameters and References
(In all the sample code below,
the global variable
Projection refers to the
All model point data as a DataFrame. The sample model point data was generated by generate_model_points.ipynb included in the library. By default,
model_point_table. The DataFrame has columns labeled
sum_assured. Cells defined in
Projectionwith the same names as these columns return the corresponding columns. (
policy_countis not used by default.)
>>> Projection.model_poit_table age_at_entry sex policy_term policy_count sum_assured point_id 1 47 M 10 1 622000 2 29 M 20 1 752000 3 51 F 10 1 799000 4 32 F 20 1 422000 5 28 M 15 1 605000 ... .. ... ... ... 9996 47 M 20 1 827000 9997 30 M 15 1 826000 9998 45 F 20 1 783000 9999 39 M 20 1 302000 10000 22 F 15 1 576000 [10000 rows x 5 columns]
The DataFrame is saved in the Excel file model_point_table.xlsx placed in the model folder.
model_point_tableis created by Projection’s new_pandas method, so that the DataFrame is saved in the separate file. The DataFrame has the injected attribute of
>>> Projection.model_point_table._mx_dataclient <PandasData path='model_point_table.xlsx' filetype='excel'>
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_annis created by Projection’s new_pandas method, so that the Series is saved in the separate file. The Series has the injected attribute of
>>> Projection.disc_rate_ann._mx_dataclient <PandasData path='disc_rate_ann.xlsx' filetype='excel'>
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_tableis created by Projection’s new_pandas method, so that the DataFrame is saved in the separate file. The DataFrame has the injected attribute of
>>> Projection.mort_table._mx_dataclient <PandasData path='mort_table.xlsx' filetype='excel'>
This is a new business model and all model points are issued at time 0.
The time step of the model is monthly. Cashflows and other time-dependent
variables are indexed with
Cashflows and other flows that accumulate throughout a period
t denotes the sums of the flows from
Balance items indexed with
t denotes the amount at
Projection length in months
The max of all projection lengths
Model point data#
The model point data is stored in an Excel file named model_point_table.xlsx under the model folder.
Target model points
The sex of the model points
The sum assured of the model points
The policy term of the model points.
The attained age at time t.
The age at entry of the model points
Duration in force in years
The mortality table is stored in an Excel file named mort_table.xlsx
under the model folder, and is read into
mort_table as a DataFrame.
mort_rate() looks up
mort_table and picks up
the annual mortality rates to be applied for all the
model point at time
t and returns them in a Series.
mort_rate() to the monthly mortality
rate to be applied during the month starting at time
The lapse by duration is defined by a formula in
expense_acq() holds the acquisition expense per policy at t=0.
expense_maint() holds the maintenance expense per policy per annum.
The maintenance expense inflates at a constant rate
of inflation given as
Mortality rate to be applied at time t
Monthly mortality rate to be applied at time t
Monthly discount rate
Acquisition expense per policy
Annual maintenance expense per policy
The inflation factor at time t
The payment method is monthly whole term payment for all model points.
The monthly premium per policy (
is calculated for each policy
as (1 +
The net premium is calculated so that the present value of the
net premiums equates to the present values of claims.
This product is assumed to have no surrender value.
Claim per policy
Net premium per policy
Loading per premium
Monthly premium per policy
The initial number of policies is set to 1 per model point by default, and decreases through out the policy term by lapse and death. At the end of the policy term the remaining number of policies mature.
Number of death occurring at time t
Number of policies in-force
Initial Number of Policies In-force
Number of lapse occurring at time t
Number of maturing policies
An acquisition expense at t=0 and maintenance expenses thereafter comprise expense cashflows.
Commissions are assumed to be paid out during the first year and the commission amount is assumed to be 100% premium during the first year and 0 afterwards.
Acquisition and maintenance expenses
The Cells whose names start with
pv_ are for calculating
the present values of the cashflows indicated by the rest of their names.
pols_if() is not a cashflow, but used as annuity factors
Present value of claims
Present value of commissions
Present value of expenses
Present value of net cashflows.
Present value of policies in-force
Present value of premiums
>>> result_cf() Premiums Claims Expenses Commissions Net Cashflow 0 828052.400000 240181.385376 3.000000e+06 828052.400000 -3.240181e+06 1 820758.893595 238066.700397 4.956055e+04 820758.893595 -2.876273e+05 2 813529.629362 235970.634461 4.912497e+04 813529.629362 -2.850956e+05 3 806364.041439 233893.023631 4.869321e+04 806364.041439 -2.825862e+05 4 799261.568951 231833.705414 4.826525e+04 799261.568951 -2.800990e+05 .. ... ... ... ... ... 236 175639.935592 255080.430556 1.065127e+04 0.000000 -9.009177e+04 237 175262.324017 254523.319976 1.063033e+04 0.000000 -8.989132e+04 238 174885.540149 253967.449257 1.060943e+04 0.000000 -8.969133e+04 239 174509.582137 253412.815586 1.058857e+04 0.000000 -8.949180e+04 240 0.000000 0.000000 0.000000e+00 0.000000 0.000000e+00 [241 rows x 5 columns]
result_pv() outputs the present values of the cashflows by model points:
>>> result_pv() PV Premiums PV Claims ... PV Commissions PV Net Cashflow point_id ... 1 8251.931435 5501.074678 ... 1084.601434 917.951731 2 8934.647903 5956.375886 ... 699.317588 1190.137329 3 13785.154420 9190.166764 ... 1814.196468 2033.119958 4 5771.417165 3847.385432 ... 452.022146 383.742941 5 4951.158886 3300.643396 ... 474.220266 245.572689 ... ... ... ... ... 9996 27755.139250 18503.269117 ... 2189.101714 5980.458717 9997 7338.893087 4892.682575 ... 703.088993 812.566152 9998 22878.042022 15252.462621 ... 1801.701611 4740.283791 9999 6029.228626 4019.657332 ... 473.273387 449.939683 10000 3804.512116 2536.489758 ... 364.193562 -27.270550 [10000 rows x 5 columns]
Result table of cashflows
Result table of present value of cashflows