Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Refactor #40

Closed
alecloudenback opened this issue Oct 28, 2020 · 2 comments · Fixed by #41
Closed

API Refactor #40

alecloudenback opened this issue Oct 28, 2020 · 2 comments · Fixed by #41

Comments

@alecloudenback
Copy link
Member

alecloudenback commented Oct 28, 2020

Refactor to make the types of insurances, well, types instead of functions. Example:

Current:

annuity_due(ins) currently calculates the present value of an annuity due.

Alternative:

a = AnnuityDue(ins) constructs an object, for which the following functions would operate:

  • cashflows(a) would produce the vector of cashflows
  • survival(a) would produce the survivorship vector
  • timepoints(a) would produce the timepoints associated with the cashflows
  • discount(a) would produce the discount vector
  • Replicating the current functionality, present_value(a) would extend ActuaryUtilities.present_value and calculate the APV via the present_value(ins.interest,cashflows(a) .* survival(a),timepoints(a))
    • Could specialize this function to the existing calculation if the duplication causes material speed slowdown

Advantages of this:

  • allows for breaking the constituent pieces apart
    • e.g. facilitates calculating the liability duration via the cashflow analysis methods in ActuaryUtilities
  • the insurances are now combine-able:
    • e.g. Insurance(ins) - AnnuityDue(ins) would create a new instance of a ContingencyCombination where it combines the insurance benefit less the annuity payments, resulting in the net product cashflow.

Disadvantages:

  • potentially duplicating calculations? E.g. the timepoints may need to be calculated three times for an APV instead of just once.
@alecloudenback
Copy link
Member Author

alecloudenback commented Oct 29, 2020

Proposed in #41:

mt = UltimateMortality([0.5,0.5])

# whole life insurance
ins = Insurance(
        SingleLife(mort = mt,issue_age = 0),
        Yields.Constant(0.05)
) 

@test survival(ins) == [1.0,0.5]
@test discount(ins) == [1.0 / 1.05, 1 / 1.05^2]
@test benefit(ins) == [1.0,1.0]
@test probability(ins) == [0.5,0.25]
@test cashflows(ins) == [0.5,0.25]
@test cashflows(ins) == benefit(ins) .* probability(ins)
@test timepoints(ins) == [1.0,2.0]
@test present_value(ins)   0.5 / 1.05 + 0.5 * 0.5 / 1.05 ^ 2

Status Quo:

ins = LifeContingency(
    SingleLife(
        mort = mt,
        issue_age = 0
    ),
    Yields.Constant(0.05)
)

And then all you can do is this:

@test insurance(ins)  0.5 / 1.05 + 0.5 * 0.5 / 1.05 ^ 2

@alecloudenback
Copy link
Member Author

Benchmarks indicate that the new API would be slower, but that's solvable (specialize on the functions, build a pipeline that doesn't allocate the intermediate steps, etc)

julia> @benchmark present_value(Insurance($ins   ))		#new
BenchmarkTools.Trial: 
  memory estimate:  111.30 KiB
  allocs estimate:  1579
  --------------
  minimum time:     81.200 μs (0.00% GC)
  median time:      83.301 μs (0.00% GC)
  mean time:        92.890 μs (6.73% GC)
  maximum time:     3.461 ms (96.72% GC)
  --------------
  samples:          10000
  evals/sample:     1

julia> @benchmark insurance($ins   )              		#old
BenchmarkTools.Trial: 
  memory estimate:  83.88 KiB
  allocs estimate:  740
  --------------
  minimum time:     35.000 μs (0.00% GC)
  median time:      48.000 μs (0.00% GC)
  mean time:        48.172 μs (6.57% GC)
  maximum time:     2.160 ms (96.04% GC)
  --------------
  samples:          10000**

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant