The purpose of this report is to walk through the various allocation procedures utilized in the shiny application.
In order to provide knowledgeable insights from this report, we have supplied reasonable inputs that mimic the user-driven aspects of the allocation procedures.
The first step is to ingest all of the various user-driven inputs and demo-data to form a dataset to perform the allocation with.
Specify various ‘user-defined’ inputs:
# specify experience period based on loss run
min_year <- min(loss_run$year)
max_year <- max(loss_run$year)
experience_period <- c(min_year:max_year)
experience_period_display <- paste0(
lubridate::ymd(paste0(min_year, "-01-01")) %>% format("%B %d, %Y"), " to ",
lubridate::ymd(paste0(max_year, "-12-31")) %>% format("%B %d, %Y")
)
# specify percent change capping threshold
cap_threshold <- .25
# A 5% budget guidance increase factor
budget_guidance_percent <- 0.05
we need to extract the costs to be allocated in the model, specifically:
To do this, I utilize an internal, custom utility function
extract_costs_for_allocation()
.
The resulting Costs for Allocation are:
# extract costs from renewal cost table
costs <- extract_costs(renewal_costs)
# derive current and prior overall rates and percent change
curr_rate <- costs$risk_transfer / sum(sov$tiv)
prior_rate <- sum(priors$prior_risk_transfer_premium) /
sum(priors$prior_tiv)
pct_change <- (curr_rate / prior_rate) - 1
tibble::as_tibble(costs) %>%
as.matrix() %>%
t() %>%
as.data.frame() %>%
tibble::rownames_to_column(var = "Cost Type") %>%
dplyr::rename("Dollar Amount" = V1) %>%
tibble::as_tibble() %>%
kkable(currency_cols = "Dollar Amount",
proper_cols = "Cost Type",
caption = "Extracted Costs for Allocation:")
This step involves taking the user-defined relativity’s and multiplying them by the initial Total Insured Values defined in the Schedule of Values (SOV).
Specifically, we take the user-input Relativity Table and
apply a custom utility function
derive_relativity_adjusted_tivs()
to derive our final
relativity adjusted TIVs.
First, lets look at the input relativities:
rel_tables <- list(bu_rels, combustible_rels, sprinkler_tier_rels) %>%
purrr::set_names(c("Business Unit Relativity",
"AOP Combustible Relativity",
"AOP Sprinkler Tier Relativity")) %>%
purrr::map(apply_labels, dict = dictionary, dataset_name = "rels") %>%
purrr::map(tibble::as_tibble)
purrr::map2(rel_tables, names(rel_tables), function(x, y) {
kkable(data = x, caption = y, col_names = NULL, digits = 3, add_digits = TRUE)
})
Relativities are then applied directly to each entity’s TIV. For each relativity type, a separate relativity-adjusted TIV is calculated and used as required in eithe catastrophy allocation, All Other Peril allocation, or terrorism allocation.
rels_list <- list(
relativity_data = list(
bu_rels[, c(1, 2)], bu_rels[, c(1, 3)], bu_rels[, c(1, 4)], bu_rels[, c(1, 5)],
bu_rels[, c(1, 6)], sprinkler_tier_rels, combustible_rels
),
coverage = list(
"aop", "cat_eq", "cat_wind", "cat_flood", "terrorism", "aop", "aop"
),
sov_linker = list(
"bu", "bu", "bu", "bu", "bu", "aop_sprinkler_tier", "aop_combustible"
)
)
rel_adjusted_tivs <- ingest_relativities(rels_list, sov = sov)
kkable(head(rel_adjusted_tivs),
proper_cols = "entity_id",
currency_cols = names(rel_adjusted_tivs[2:ncol(rel_adjusted_tivs)]))
For entities that have experienced claims, the client may wish to increase their allocated premium. The rules for doing so are limited in this application to: applying a % surcharge to the TIV for each claim made by the entity in a certain time period, or adding a $ surcharge to the allocated premium (at this stage in the calculation) for each claim made in a certain time period.
The demo data, count_buckets
, sets out a typical
specification for such functionality.
count_buckets %>%
select(name:dollar_surcharge) %>%
kkable(
col_names = c("Label", "Minimum", "Maximum", "Percent Surcharge", "Dollar Surcharge"),
caption = "User-Defined Claim Count Bucket Surcharges",
currency_cols = c("min", "max", "dollar_surcharge"),
percent_cols = c("percent_surcharge")
)
Here, count_buckets
are applied to the loss run ready
for surcharging the premiums calculated in the main part of the
model:
entity_loss_data <- entity_loss_summary(loss_run, count_buckets, experience_period)
col_names <- c("Entity ID",
count_buckets$name,
"Total Counts", "Total Incurred")
kkable(entity_loss_data %>%
# dplyr::mutate(entity_id = toproper(entity_id)) %>%
dplyr::arrange(dplyr::desc(total_incurred)) %>%
head(10),
col_names = col_names,
proper_cols = "entity_id",
currency_cols = length(col_names),
caption = "Summarized Loss Data by Entity (Top 10 Entities by Total Incurred)")
The steps involved are:
rates
to relativity_adjusted_tivs
and also to priors
(to simultaneously obtain an allocation
of prior premiums)entity_loss_data
to surcharge the resulting
premiums pased on count_buckets
and the loss run
experiencecosts
budget_guidance_percent
to prior premium
rates to perform this allocation scenariothreshold <- 0.25
), and if the prior premium allocated
for a particular entity was 1m USD, then roughly the current premium
allocation would be between 750k USD, and 1.25m USD.
# merge entity data (sov, rel adjusted tivs, loss data, market and model rates,
# and priors)
allocation_data <- merge_entity_data(
sov,
rel_adjusted_tivs,
entity_loss_data,
rates,
priors
) %>%
# perform initial preliminary allocation (CAT first, back into AOP, terror)
# this is uncapped. before surcharges, and excluding expenses
preliminary_allocation(costs, budget_guidance_percent) %>%
# apply surcharges
apply_surcharges(count_buckets) %>%
# adjust column names for apply threshold function
# TODO: add arguments to apply threshold functions for specifying column
# so don't have to add this step.
mutate(
prior_allocated = prior_risk_transfer_premium,
prior_allocated_rate = prior_allocated / prior_tiv,
uncapped_allocated = surcharged_premium
) %>%
# apply capping using a default 25% threshold
# TODO: add argument to apply threshold for whether or not to net the
# total pct change or not - currently it does this
apply_threshold(
total_pct_chg = pct_change,
threshold = cap_threshold
) %>%
# final rebalancing and allocate expenses
allocate_expenses(costs, weight_variable = "tiv")
And the result is:
# declutter results and output
allocation_data %>%
mutate(current_allocated_rate = rebalanced_allocated / tiv) %>% # excl expense
select(
entity_id,
tiv,
prior_tiv,
aop_adj_tiv:terrorism_adj_tiv,
model_aop_rate,
model_cat_eq_rate,
model_cat_wind_rate,
model_cat_flood_rate,
model_terrorism_rate,
prior_risk_transfer_premium,
preliminary_model_premium = total_model_premium_adj,
surcharge,
surcharged_premium,
prior_allocated,
prior_allocated_rate,
uncapped_allocated,
capped_allocated = allocated,
final_allocated = rebalanced_allocated,
current_allocated_rate,
rate_percent_change = capped_rate_percent_change,
allocated_expenses,
final_allocated_w_expense
) %>%
kkable()
propalloc
comes with functionality to split out the
effects of each of the stages of premium allocation as they contribute
to its change between prior and current premium allocations. This
function uses argument filter_vector
to show such a split
for a subset of entities if desired.
driver_summary <- prepare_driver_summary(allocation_data, filter_vector = c())
driver_summary_bu_b_only <- prepare_driver_summary(allocation_data, filter_vector = c(bu = "bu_b"))