lifelib-dev / lifelib

Python package of actuarial models, tools, examples and learning materials.

Home Page:https://lifelib.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Creating an EV Model

kaustavSen opened this issue · comments

Hi Fumitoh

I am trying to create an EV projection model using modelx. Probably something that might be useful to package into lifelib in the future and so asking my query here.

Just as an example and also for my own learning, I started creating a model to calculate the PVFP (Present Value of Future Profits) for a simple Term product. Here is all my current coding to re-create the Model.

The setup is pretty basic. It contains a single "new business" model point with a policy term of 20 years and modelling is done at annual time steps. It comprises of the following spaces:

  • a Life space to calculate probabilities
  • a Term_Life space to calculate the Best Estimate cash flows. Currently I have just modelled premiums and death outgo. This space inherits from Life space.
  • a Term_Life_Res space to calculate the reserves using more prudent assumptions. This space inherits from Term_Life space.

As a final step, I want to compute the profit for each year and then discount the profit stream to get to PVFP. However, for this I need:

  • the premiums and death outgo to come from the Term_Life layer, and
  • the increase in reserve to be based on per-policy reserve from the Term_Life_Res layer but then rebased for the BE probabilities

At this stage I am stuck and can't think of a way to call some values from the Term_Life layer and some from the Term_Life_Res layer to get to my profit vector. Any suggestions on how this can be achieved? Happy to discuss further if anything is unclear.

Regards
Kaustav

I think I might have an approach here. What I did was create a brand new space called Term_Life_PVFP and then added the Term_Life and Term_Life_Res as child spaces within it.

# Creating a space to calculate PVFP for Term Life
Term_Life_PVFP = mx.new_space("Term_Life_PVFP")
Term_Life_PVFP.new_space("BE", bases=Term_Life)
Term_Life_PVFP.new_space("RES", bases=Term_Life_Res)

Post this I was able to call functions defined in both BE and RES in the Term_Life_PVFP space such as the following:

@mx.defcells(Term_Life_PVFP)
def reserve_if(t):
    return RES.reserve_pp(t) * BE.prob_if_e(t)

Does this seem like an appropriate approach to you? Would love to hear if there is a more elegant way of doing this.

Hi Kaustav (I was writing below before your second post, so I'm responding to your first post)

I guess your per-policy reserve calculation doesn't depend on the BE calculation, right?
Then I would use Term_Life as the abstract base space to define common formulas between Term_Life_Res
and Term_Life_PVFP, and use Term_Life_PVFP for BE and PVFP calculateion.

    Life
      |
  Term_Life
      |
+-----+---------------+
|                     |
Term_Life_Res         Term_Life_PVFP

To use Term_Life_Res.reserve_pp in Term_Life_PVFP, you can just define a reference in Term_Life_PVFP referring to Term_Life_Res.reserve_pp.

>>> Term_Life_PVFP.reserve_pp = Term_Life_Res.reseve_pp          # can be any name on the left side 

You can instead define a reference for Term_Life_Res

>>> Term_Life_PVFP.Res = Term_Life_Res

Then everything in Term_Life_Res can be accessible as Res.xxx in Term_Life_PVFP

Your second design should look like below.

    Life          Term_Life_PVFP
      |                 |
  Term_Life        +----+----+
      |            |         |              
Term_Life_Res     RES        BE

This should work too, but I would just make both RES and BE inherit from Term_Life and not define Term_Life_Res.

Thanks a lot, Fumitoh! This was really helpful and good learning for me.

Yes, you are right that the per-policy reserve calculations do not depend on the BE calculations. As such, your model tree structure looks more cleaner to me.

I think I have now got the basics of modelx cleared up in my head. As a next step in my learning, I think it would be a good idea to build a full fledged PVFP projection model based on the policy data / assumptions already present from existing lifelib models.
Is it okay if I submit a pull request to lifelib once I have an initial model ready? Of course I'll be happy to make changes based on your review.

Your contribution is welcome. A pull request may not work well because lifelib is essentially a collection of separate models and not a program as a whole. You can work on your model in your own repository and drop a link to it here.

Great! I was thinking of cloning lifelib and then working in a separate folder for the pvfp_model so that it does not conflict with the existing models.

Shouldn't this enable an easy merge from a pull request? Even if it causes issues, will have a separate repo to get the code from.

Are you planning to create a model based on BasicTerm_S model?

I was thinking about starting from scratch in a new folder (say, pvfp_model inside the "libraries" parent folder, similar to how there are folders for basiclife and savings) but in a similar format as BasicTerm_S.

I think instead of having all the cells in a single space it might be more manageable to have it compartmentalised into different spaces. But yes, the overall idea is to have something similar in lines of BasicTerm_S (which sits inside of the basiclife library).

Libraries should satisfy the conditions below. Adding a new library to lifelib needs a lot of time and effort, so it's better for you to have your model outside lifelib for now until it gets mature.

  • Practicality of the model: what types of work the mode can be utilized for, given the speed of the model.
  • Documentation: the docstring of each formula, space, model and also sphinx documents describing the library
  • Naming convention: names of cells, refs, spaces, models, model folders should follow the same naming convention as basiclife and savings
  • Generality: Reserving and capital requirements should not be country-specific. Country-specific requirements should be eliminated or thoroughly documented with references.
  • Automated tests

A much easier way to contribute is to write a tutorial in Jupyter notebook, which is easier to merge into lifelib. In your case, it would be valuable to show how to develop a compartmentalised model with best-estimate and lock-in reserving spaces derived from the same base space.

Thanks Fumitoh. All very valid points. Writing a tutorial in Jupyter notebook sounds like a great idea. Will proceed along those lines. Probably will also create an Excel workbook to replicate the results and try to keep it generic without any country specific calculations.

Hi Fumitoh

The coding logic for my PVFP Model is almost complete now. I have published the same in a separate repo here: https://github.com/kaustavSen/PVFP_Model

All my code is in the PVFP_Model.py file and these is also an Excel replica of the model in the repo as well.

However, is seem to be a bug in my code which I am unable to solve. On lines 125 to 132 I am instantiating a new UserSpace called Cashflows and defining its references:

Cashflows = mx.new_space("Cashflows")

# Define references
Cashflows.Data = Data
Cashflows.Assumptions = Assumptions
Cashflows.Basis = "" # can be either BE or RES
Cashflows.Probabilities = Probabilities
Cashflows.Probabilities.Basis = "" # can be either BE or RES

Later down, I use this Cashflow UserSpace to derive two new spaces called BE and RES. However, when I define the references for these UserSpaces, it seems to be having an over-writing effect. Basically I want the Basis reference to remain as "BE" in the BE UserSpace and "RES" in the RES UserSpace. I tried to use the set_ref() function to see if it solves the issue. However, this does not seem to be case.

Would it be possible for you to nudge in the direction where I might be going wrong?

Thanks again for your continuous help here and my sincere apologies for the many questions!

Regards
Kaustav

@kaustavSen Can't see the model. The link is broken.

My apologies. It got published as a Private repo. I have now changed it to Public instead. Hopefully the link should now work:
https://github.com/kaustavSen/PVFP_Model

BE and RES space in your model use the same Cashflows space as the top diagram below the shows.

What you want is the structure as shown in the middle below. To achieve that:

  • Create an empty space CashflowsParent and make Cashflows a child of CashflowsParent,
    by CashflowsParent.new_space method.
  • Then create empty spaces BE and RES using new_space
  • Make BE and RES inherit CashflowsParent using their add_bases method

The 2nd and 3rd steps can actually be done at once by passing CashflowsParent to new_sapce's, bases parameter.
Then you should have Cashflows under BE and RES.

But I think CashflowsParent is redundant. You can make BE and RES just inherit from Cashflows as the bottom diagram, in which case everything in Cashflows are copied into BE and RES so you have to remove Cashflows. from the formulas in RES and BE.

diagram

Thanks again, Fumitoh!

Your approach of using add_bases() on the BE and RES UserSpaces did the trick! I was also able to perfectly reconcile the PVFP stream between python and Excel. Just pushed my changes to the repo as well.

The coding logic is all ready and I'll work on creating a tutorial style Jupyter notebook on building this model. Closing this thread now. I'll open a new thread once the Jupyter notebook is ready.

Looking forward to talking with you again soon.