Skip to content

actuarialopensource/ExperienceAnalysis.jl

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

47 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ExperienceAnalysis

Stable Dev Coverage lifecycle

Calculate exposures.

Quickstart

using ExperienceAnalysis
using DataFrames
using Dates

df = DataFrame(
    policy_id = 1:3,
    issue_date = [Date(2020,5,10), Date(2020,4,5), Date(2019, 3, 10)],
    termination_date = [Date(2022, 6, 10), Date(2022, 8, 10), nothing],
    status = ["claim", "lapse", "inforce"]
)

df.policy_year = exposure.(
    ExperienceAnalysis.Anniversary(Year(1)),
    df.issue_date,
    df.termination_date,
    df.status .== "claim"; # continued exposure
    study_start = Date(2020, 1, 1),
    study_end = Date(2022, 12, 31)
)

df = flatten(df, :policy_year)

df.exposure_fraction =
        map(e -> yearfrac(e.from, e.to + Day(1), DayCounts.Thirty360()), df.policy_year) 
# + Day(1) above because DayCounts has Date(2020, 1, 1) to Date(2021, 1, 1) as an exposure of 1.0
# here we end the interval at Date(2020, 12, 31), so we need to add a day to get the correct exposure fraction.
policy_id issue_date termination_date status policy_year exposure_fraction
1 2020-05-10 2022-06-10 claim (from = Date("2020-05-10"), to = Date("2021-05-09"), policy_timestep = 1) 1.0
1 2020-05-10 2022-06-10 claim (from = Date("2021-05-10"), to = Date("2022-05-09"), policy_timestep = 2) 1.0
1 2020-05-10 2022-06-10 claim (from = Date("2022-05-10"), to = Date("2023-05-09"), policy_timestep = 3) 1.0
2 2020-04-05 2022-08-10 lapse (from = Date("2020-04-05"), to = Date("2021-04-04"), policy_timestep = 1) 1.0
2 2020-04-05 2022-08-10 lapse (from = Date("2021-04-05"), to = Date("2022-04-04"), policy_timestep = 2) 1.0
2 2020-04-05 2022-08-10 lapse (from = Date("2022-04-05"), to = Date("2022-08-10"), policy_timestep = 3) 0.35
3 2019-03-10 inforce (from = Date("2020-01-01"), to = Date("2020-03-09"), policy_timestep = 1) 0.191667
3 2019-03-10 inforce (from = Date("2020-03-10"), to = Date("2021-03-09"), policy_timestep = 2) 1.0
3 2019-03-10 inforce (from = Date("2021-03-10"), to = Date("2022-03-09"), policy_timestep = 3) 1.0
3 2019-03-10 inforce (from = Date("2022-03-10"), to = Date("2022-12-31"), policy_timestep = 4) 0.808333

Discussion and Questions

If you have other ideas or questions, feel free to also open an issue, or discuss on the community Zulip or Slack #actuary channel. We welcome all actuarial and related disciplines!

References

Related Packages

API

The exposure function has the following type signature for Anniversary exposures:

function exposure(
    p::Anniversary,
    from::Date,
    to::Union{Date,Nothing},
    continued_exposure::Bool = false;
    study_start::Union{Date,Nothing} = nothing,
    study_end::Date,
    left_partials::Bool = false,
    right_partials::Bool = true,
)::Vector{NamedTuple{(:from, :to, :policy_timestep),Tuple{Date,Date,Int}}}

p, Exposure Basis

Anniversary

ExperienceAnalysis.Anniversary(DatePeriod) will give exposures periods based on the first date. Exposure intervals will fall on annniversaries, start_date + t * dateperiod. DatePeriod is a DatePeriod Type from the Dates standard library.

exposure(
    ExperienceAnalysis.Anniversary(Year(1)), # basis
    Date(2020,5,10),                         # from
    Date(2022, 6, 10);                       # to
    study_start = Date(2020, 1, 1),
    study_end = Date(2022, 12, 31)
)
# returns
# 3-element Vector{NamedTuple{(:from, :to, :policy_timestep), Tuple{Date, Date, Int64}}}:
#  (from = Date("2020-05-10"), to = Date("2021-05-09"), policy_timestep = 1)
#  (from = Date("2021-05-10"), to = Date("2022-05-09"), policy_timestep = 2)
#  (from = Date("2022-05-10"), to = Date("2022-06-10"), policy_timestep = 3)

Calendar

ExperienceAnalysis.Calendar(DatePeriod) will follow calendar periods (e.g. month or year). Quarterly exposures can be created with Month(3), the number of months should divide 12.

exposure(
    ExperienceAnalysis.Calendar(Year(1)), # basis
    Date(2020,5,10),                      # from
    Date(2022, 6, 10);                    # to
    study_start = Date(2020, 1, 1),
    study_end = Date(2022, 12, 31)
)
# returns
# 3-element Vector{NamedTuple{(:from, :to), Tuple{Date, Date}}}:
#  (from = Date("2020-05-10"), to = Date("2020-12-31"))
#  (from = Date("2021-01-01"), to = Date("2021-12-31"))
#  (from = Date("2022-01-01"), to = Date("2022-06-10"))

AnniversaryCalendar

ExperienceAnalysis.AnniversaryCalendar(DatePeriod,DatePeriod) will split into the smaller of the calendar or policy anniversary period. We can ensure that each exposure interval entirely falls within a single calendar year.

exposure(
    ExperienceAnalysis.AnniversaryCalendar(Year(1), Year(1)), # basis
    Date(2020,5,10),                                          # from
    Date(2022, 6, 10);                                        # to
    study_start = Date(2020, 1, 1),
    study_end = Date(2022, 12, 31)
)
# returns
# 5-element Vector{NamedTuple{(:from, :to, :policy_timestep), Tuple{Date, Date, Int64}}}:
#  (from = Date("2020-05-10"), to = Date("2020-12-31"), policy_timestep = 1)
#  (from = Date("2021-01-01"), to = Date("2021-05-09"), policy_timestep = 1)
#  (from = Date("2021-05-10"), to = Date("2021-12-31"), policy_timestep = 2)
#  (from = Date("2022-01-01"), to = Date("2022-05-09"), policy_timestep = 2)
#  (from = Date("2022-05-10"), to = Date("2022-06-10"), policy_timestep = 3)

from, to, study_start, study_end

  • from is the date the policy was issued
  • to is the date the policy was terminated, or nothing if the policy is still in-force
  • study_start is the start of the study period, or nothing if the study period is unbounded on the left
  • study_end is the end of the study period

from and study_end are required to be Date types. to and study_start can be Date or nothing.

continued_exposure

When doing a lapse study, lapsed policies will be given a full year of exposure in the policy year of the lapse. This is accomplished by setting continued_exposure = true. continued_exposure is not a keyword argument so that it can support broadcasting.

The continued exposure may extend beyond the end of the study.

exposure(
    ExperienceAnalysis.AnniversaryCalendar(Year(1), Year(1)), # basis
    Date(2020,5,10),                                          # from
    Date(2022, 6, 10),                                        # to
    true;                                                     # continued_exposure
    study_start = Date(2020, 1, 1),
    study_end = Date(2022, 12, 31)
)
# returns
# 6-element Vector{NamedTuple{(:from, :to, :policy_timestep), Tuple{Date, Date, Int64}}}:
#  (from = Date("2020-05-10"), to = Date("2020-12-31"), policy_timestep = 1)
#  (from = Date("2021-01-01"), to = Date("2021-05-09"), policy_timestep = 1)
#  (from = Date("2021-05-10"), to = Date("2021-12-31"), policy_timestep = 2)
#  (from = Date("2022-01-01"), to = Date("2022-05-09"), policy_timestep = 2)
#  (from = Date("2022-05-10"), to = Date("2022-12-31"), policy_timestep = 3)
#  (from = Date("2023-01-01"), to = Date("2023-05-09"), policy_timestep = 3) # this is the continued exposure

left_partials and right_partials

Assumptions like lapse rates can have uneven distributions within policy years, so we may only want to look at full policy years. This can be accomplished by setting left_partials = false and right_partials = false.

See that by default there are partial exposures at the beginning and end of the study period.

exposure(
    ExperienceAnalysis.Anniversary(Year(1)), # basis
    Date(2019,5,10),                         # from
    Date(2022, 6, 10);                       # to
    study_start = Date(2020, 1, 1),
    study_end = Date(2021, 12, 31)
)

# returns
# 3-element Vector{NamedTuple{(:from, :to, :policy_timestep), Tuple{Date, Date, Int64}}}:
#  (from = Date("2020-01-01"), to = Date("2020-05-09"), policy_timestep = 1)
#  (from = Date("2020-05-10"), to = Date("2021-05-09"), policy_timestep = 2)
#  (from = Date("2021-05-10"), to = Date("2021-12-31"), policy_timestep = 3)

But we can remove these partial exposures by setting left_partials = false and right_partials = false.

exposure(
    ExperienceAnalysis.Anniversary(Year(1)), # basis
    Date(2019,5,10),                         # from
    Date(2022, 6, 10);                       # to
    study_start = Date(2020, 1, 1),
    study_end = Date(2021, 12, 31),
    left_partials = false,
    right_partials = false
)
# returns
# 1-element Vector{NamedTuple{(:from, :to, :policy_timestep), Tuple{Date, Date, Int64}}}:
#  (from = Date("2020-05-10"), to = Date("2021-05-09"), policy_timestep = 2)

Calendar basis does not have left_partials and right_partials because the same effect can always be achieved by setting study_start and study_end.

About

Experience Analysis Utility Functions

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Julia 100.0%