-
Notifications
You must be signed in to change notification settings - Fork 21
Create CAT bond module #186
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
Draft
KaiOBerg
wants to merge
112
commits into
develop
Choose a base branch
from
feature/cat_bonds
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 40 commits
Commits
Show all changes
112 commits
Select commit
Hold shift + click to select a range
93e924f
initial commit CAT Bonds
KaiOBerg f502208
change subarea building to resolution input
KaiOBerg 2c23ff5
alter subarea creation test notebook
KaiOBerg 3e9457e
take crs from exposure
KaiOBerg efe9a75
dont save exp_gdf and adjust plotting
KaiOBerg cbd9b94
add buffer to create subareas
KaiOBerg 716d3a0
streamline calculations
KaiOBerg 96c26c4
change calculation of attachment and prinipal
KaiOBerg cc4fdee
keep only grids with exposure point within
KaiOBerg 16caea0
fix bugs
KaiOBerg 6533b49
adjust calc savings
KaiOBerg bf54f9b
adjust test notebook to changed classes
KaiOBerg 688027d
inititate bond simulation class
KaiOBerg 0a35b87
delete pay_dam_subarea class
KaiOBerg 519dcb4
update function descriptions
KaiOBerg 8a2accf
fix bugs
KaiOBerg 08bd04e
add total payout and damage
KaiOBerg 3d59693
update function description
KaiOBerg edf6598
add net cash flow and premium simulation
KaiOBerg 2388d7c
adjust metric naming
KaiOBerg 7db291c
update test to classes
KaiOBerg f9c6cab
change function naming
KaiOBerg 1168e62
inititate premium class
KaiOBerg 8ef65cb
add IBRD CAT bonds data file
KaiOBerg 6d30ad7
add ibrd based premium calculation
KaiOBerg fc3edc8
improve logging and fix bug chatoro
KaiOBerg c7d1134
fix path to data dir
KaiOBerg 6bf627e
remove premium input
KaiOBerg c93b862
adjust test notebook to classes
KaiOBerg b820b8c
add benchmark premium calculation
KaiOBerg ce9952c
include all premium methods in test notebook
KaiOBerg 0d5fbcd
remove buffer grid size
KaiOBerg 82d185d
define variables
KaiOBerg d6a494c
rename bond_simulation to sng_bond_simulation
KaiOBerg 7322e02
initialize multi country bond simulation
KaiOBerg d6250ff
add function to derive VaR and ES
KaiOBerg ac4c317
add jamaica bond
KaiOBerg c4d2d99
fix bugs exp and haz jamaica
KaiOBerg 78e8b8e
test multi-country bond in notebook
KaiOBerg 4b9c735
add minimum simulation year
KaiOBerg a440927
Update climada_petals/engine/cat_bonds/mlt_bond_simulation.py
KaiOBerg 616bf60
make init loss more pythonic
KaiOBerg d50ceb5
change var es function
KaiOBerg ac20d79
adjust test notebook for confidence intervals
KaiOBerg 50ac5ef
remove nested loop from bond simulation
KaiOBerg da53e8a
change fucntion description
KaiOBerg c02d1eb
fix type of single events in df_loss_month
KaiOBerg 7c6458b
adjust to chanings in sng_simulation
KaiOBerg ad92d39
implement return simulation mlt bonds
KaiOBerg 9c5ca13
rename funciton names
KaiOBerg 0e95f18
init function to calculate returns with trances
KaiOBerg 805bbe1
add required principal calculation
KaiOBerg d692bea
save required principal to mlt_bond class
KaiOBerg 05a88ee
rename functions
KaiOBerg 61802b5
make bond term function private
KaiOBerg 2b3484b
adapt changes in mlt class to test notebook
KaiOBerg 1d5897d
fix min year in requ_principal and initialze min_year at class init
KaiOBerg 499bf97
add function for overlapping subareas
KaiOBerg 0fef13e
add belize
KaiOBerg a53e32e
make build_subareas optional
KaiOBerg 0b09fe5
minor adjustments
KaiOBerg 6051337
rename class names
KaiOBerg d2ec6c4
initialize pooling optimization problems
KaiOBerg edfb591
add function to derive optimal fixed pools
KaiOBerg 7efc9a2
move allocate tranche payout to utils
KaiOBerg 5a29cc3
rename test notebook to tutorial
KaiOBerg 58bf8e1
move tutorial
KaiOBerg 9166134
initialize subareas with resolution
KaiOBerg e5d2d47
initiate subareas by resolution
KaiOBerg 5abe3c1
implement pooling n pools
KaiOBerg d8f70a0
initialise class with optimized n pools
KaiOBerg fc1a62f
rename n to n_pools
KaiOBerg 7186418
rename fct process_n to process_n_pools
KaiOBerg 62e4808
add maximum principal wrapper function
KaiOBerg f5c774b
fix iterration bug
KaiOBerg 5e14044
return whole dictionary conatining multiple class instances
KaiOBerg b311758
add logging to simulate_bond_pool_n
KaiOBerg b262e71
add max_principal pooling optimization function
KaiOBerg 4dbea02
adapt to changes in mlt_cty_bond
KaiOBerg 20b08fa
update funciton descriptions
KaiOBerg c486f9c
comment tutorial script
KaiOBerg ad94804
change inital guess values to percentiles of hazard intensity
KaiOBerg df09dd3
implement option to plug in manuall init guess
KaiOBerg a84e767
add function to initialize subarea class with a gdf
KaiOBerg d422c83
update function description
KaiOBerg 9c4a252
move turotial to docs and create init
KaiOBerg e9078d4
fix file paths
KaiOBerg f30d74b
move explode into exp_gdf function
KaiOBerg fa871c8
initialize subaarea unit tests
KaiOBerg 16ce43d
remove padding of exposure resolution
KaiOBerg d784b82
add 1.2 padding to exposure resolution
KaiOBerg b7e95b4
add testing of merging polygons
KaiOBerg c1f5d4d
add more test cases to merging polyons
KaiOBerg 70cd21d
fromatting
KaiOBerg e5d515a
init test subarea_calculations
KaiOBerg 9635151
clearify variable origin
KaiOBerg b353942
fix bug
KaiOBerg f2e8f90
add pay_vs_dam test function
KaiOBerg cd7432a
add test objective_function
KaiOBerg b264001
initiate test script for single country bonds
KaiOBerg 56bee84
fix loop bug and remove simulated years
KaiOBerg 5de50d6
test simulate loss
KaiOBerg a7305bd
finish tests for single country bonds
KaiOBerg f7aae42
formatting
KaiOBerg 76b10bc
adjust descriptions
KaiOBerg f53bc2e
initialize test for premium calculations
KaiOBerg bcd29f7
format
KaiOBerg e63caaa
test benchmark sharpe ration function
KaiOBerg b8b65f1
formatting
KaiOBerg 9434653
update function description
KaiOBerg bd5b76d
test multi_level_es function
KaiOBerg a3ca2a0
formatting
KaiOBerg File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,244 @@ | ||
| import pandas as pd | ||
| import numpy as np | ||
| import logging | ||
|
|
||
| from utils_cat_bonds import multi_level_es | ||
|
|
||
| LOGGER = logging.getLogger(__name__) | ||
|
|
||
| class mlt_bond_simulation: | ||
|
|
||
| def __init__(self, subarea_calc_list, countries_list, term, number_of_terms, tranches): | ||
| self.countries = countries_list | ||
| self.term = term | ||
| self.simulated_years = number_of_terms * term | ||
| self.tranches = tranches | ||
| self.subarea_calc = subarea_calc_list | ||
|
|
||
|
|
||
|
|
||
| def _prepare_data(self): | ||
| self.pay_vs_dam_dic = {} | ||
| self.principal_dic_cty = {} | ||
| min_year_list = [] | ||
| for idx, cty in enumerate(self.countries): | ||
| self.pay_vs_dam_dic[cty] = self.subarea_calc[idx].pay_vs_dam | ||
| self.principal_dic_cty[cty] = self.subarea_calc[idx].principal | ||
| min_year_list.append(self.subarea_calc[idx].pay_vs_dam['year'].min()) | ||
|
|
||
| min_year = min(min_year_list) | ||
|
|
||
| return min_year | ||
|
|
||
|
|
||
|
|
||
|
|
||
| '''Simulate one term of bond to derive losses''' | ||
| def init_bond_loss(self, events_per_year, principal): | ||
| ''' | ||
| Simulates the expected losses and payouts for a multi-country catastrophe bond over its term. | ||
| This function iterates over each year (term) and processes event data for each country, calculating | ||
| payouts and damages based on the provided nominal values and per-country nominal allocations. It tracks | ||
| losses, damages, and payouts for each country and for the bond as a whole, and computes several summary | ||
| statistics. | ||
| Parameters | ||
| ---------- | ||
| self: mlt_bond_simulation | ||
| A class instance Dictionary mapping country codes to their allocated nominal values. | ||
| principal : float | ||
| The total principal value of the catastrophe bond. | ||
| events_per_year : list of pandas.DataFrame | ||
| List of DataFrames, one per year of the bond's term, each containing event data with columns: | ||
| 'month', 'country_code', 'pay', and 'damage'. | ||
| Returns | ||
| ------- | ||
| rel_ann_bond_losses : list of floats | ||
| List of relative annual losses (as a fraction of the total principal) for each year of the bond's term. | ||
| rel_ann_cty_losses : dict | ||
| Dictionary mapping country codes to arrays of relative annual losses for each year. | ||
| rel_bond_monthly_losses : pandas.DataFrame | ||
| DataFrame containing, for each year, the array of event payouts ('losses') and corresponding months ('months'), | ||
| both normalized by the total principal. | ||
| coverage_tot : dict | ||
| Dictionary with total payout and total damage over the bond's term: {'payout': ..., 'damage': ...}. | ||
| coverage_cty : dict | ||
| Dictionary mapping country codes to their cumulative payout and damage over the bond's term: | ||
| {country_code: {'payout': ..., 'damage': ...}, ...}. | ||
| Notes | ||
| ----- | ||
| - The function assumes that the term (number of years) is inferred from the length of `events_per_year`. | ||
| - Payouts are capped by the remaining principal value for the bond and by the per-country princpal allocation. | ||
| - All losses and payouts are normalized by the total principal value before being returned. | ||
| ''' | ||
| ann_loss = np.zeros(self.term) | ||
| loss_month_data = [] | ||
| cur_nominal = principal | ||
| cur_nom_cty = self.principal_dic_cty.copy() | ||
| tot_damage = [] | ||
| rel_ann_cty_losses = {country: np.zeros(self.term) for country in self.countries} | ||
| coverage_cty = {} | ||
| for code in self.countries: | ||
| coverage_cty[code] = {'payout': 0, 'damage': 0} | ||
|
|
||
| for k in range(self.term): | ||
| cty_losses_event = {country: [] for country in self.countries} | ||
| cty_damages_event = {country: [] for country in self.countries} | ||
| sum_payouts = np.zeros(len(events_per_year[k])) | ||
|
|
||
| if not events_per_year[k].empty: | ||
| events = events_per_year[k].sort_values(by='month') | ||
| months = events['month'].to_numpy() | ||
| cties = events['country_code'].to_numpy() | ||
| pay = events['pay'].to_numpy() | ||
| dam = events['damage'].to_numpy() | ||
|
|
||
| sum_payouts = np.zeros(len(events)) | ||
| sum_damages = np.zeros(len(events)) | ||
| for o in range(len(events)): | ||
| payout = pay[o] | ||
| cty = cties[o] | ||
| damage = dam[o] | ||
|
|
||
| if payout == 0 or cur_nominal == 0 or cur_nom_cty[int(cty)] == 0: | ||
| event_payout = 0 | ||
| else: | ||
| event_payout = payout | ||
| cur_nom_cty[int(cty)] -= event_payout | ||
| if cur_nom_cty[int(cty)] < 0: | ||
| event_payout += cur_nom_cty[int(cty)] | ||
| cur_nom_cty[int(cty)] = 0 | ||
| cur_nominal -= event_payout | ||
| if cur_nominal < 0: | ||
| event_payout += cur_nominal | ||
| cur_nominal = 0 | ||
|
|
||
| sum_payouts[o] = event_payout | ||
| sum_damages[o] = damage | ||
| cty_losses_event[cty].append(event_payout) | ||
| cty_damages_event[cty].append(damage) | ||
| losses = np.sum(sum_payouts) | ||
| damages = np.sum(sum_damages) | ||
| for cty, cty_loss in cty_losses_event.items(): | ||
| rel_ann_cty_losses[cty][k] = np.sum(cty_loss) | ||
| coverage_cty[cty]['payout'] += sum(cty_losses_event[cty]) | ||
| coverage_cty[cty]['damage'] += sum(cty_damages_event[cty]) | ||
| else: | ||
| losses = 0 | ||
| damages = 0 | ||
| months = [] | ||
|
|
||
| ann_loss[k] = losses | ||
| tot_damage.append(damages) | ||
| loss_month_data.append((sum_payouts, months)) | ||
|
|
||
| rel_bond_monthly_losses = pd.DataFrame(loss_month_data, columns=['losses', 'months']) | ||
|
|
||
| rel_ann_bond_losses = list(np.array(ann_loss) / principal) | ||
| for key in rel_ann_cty_losses.keys(): | ||
| rel_ann_cty_losses[key] = rel_ann_cty_losses[key] / principal | ||
| rel_bond_monthly_losses['losses'] = rel_bond_monthly_losses['losses'].values / principal | ||
| coverage_tot = {'payout': np.sum(ann_loss), 'damage': np.sum(tot_damage)} | ||
| return rel_ann_bond_losses, rel_ann_cty_losses, rel_bond_monthly_losses, coverage_tot, coverage_cty | ||
|
|
||
|
|
||
| '''Loop over all terms of bond to derive losses''' | ||
| def init_loss_simulation(self, principal, confidence_levels=[0.95, 0.99]): | ||
| """ | ||
| Simulates expected loss and attachment probability for a multi-country catastrophe bond over simulation period. | ||
| This function aggregates event data for multiple countries over a specified simulation period, computes annual and total losses, | ||
| calculates risk metrics (Value-at-Risk and Expected Shortfall) at given confidence levels, and evaluates coverage and expected loss | ||
| shares for each country. It also computes the probability that the bond is triggered (attachment probability) and can print summary statistics. | ||
| Parameters | ||
| ---------- | ||
| self: mlt_bond_simulation | ||
| A class instance containing a list of countrie codes and a list of subarea_calc classes with principal values, and pay_vs_dam tables. | ||
| principal : float | ||
| The total principal value of the catastrophe bond. | ||
| confidence_levels : list, optional | ||
| List of confidence levels (floats between 0 and 1) for risk metrics calculation (default is [0.95, 0.99]). | ||
| Returns | ||
| ------- | ||
| df_loss_month : pandas.DataFrame | ||
| DataFrame containing monthly relative losses for the entire bond. | ||
| loss_metrics : dict | ||
| Dictionary containing expected annual loss, annual attachment probability, payout, damage, and risk metrics (VaR and ES) at specified confidence levels for annual losses. | ||
| tot_coverage_cty : dict | ||
| Dictionary mapping each country code to its total payout, damage, coverage ratio, annual expected loss, and share of annual expected loss. | ||
| Notes | ||
| ----- | ||
| - The function relies on the helper functions `init_bond_loss` and `multi_level_es` for loss simulation and risk metric calculation. | ||
| - The function expects event data to be structured such that each country's DataFrame contains a 'year' and 'month' column for filtering events. | ||
|
|
||
| """ | ||
|
|
||
| min_year = self._prepare_data() | ||
|
|
||
| annual_losses = [] | ||
| total_losses = [] | ||
| list_loss_month = [] | ||
| ann_cty_losses = {cty: [] for cty in self.countries} | ||
| coverage = {'payout': 0, 'damage': 0} | ||
| self.tot_coverage_cty = {} | ||
| for cty in self.countries: | ||
| self.tot_coverage_cty[cty] = {'payout': [], 'damage': [], 'coverage': [], 'EL': 0, 'share_EL': 0} | ||
|
|
||
| for i in range(self.simulated_years-self.term): | ||
| events_per_year = [] | ||
| for j in range(self.term): | ||
| events_per_cty = [] | ||
| for cty in self.countries: | ||
| events = self.pay_vs_dam_dic[int(cty)][self.pay_vs_dam_dic[int(cty)]['year'] == (min_year+i)+j].copy() | ||
| events['country_code'] = cty | ||
| events_per_cty.append(events) | ||
| year_events_df = pd.concat(events_per_cty, ignore_index=True) if events_per_cty else pd.DataFrame() | ||
| events_per_year.append(year_events_df) | ||
|
|
||
| rel_ann_bond_losses, rel_ann_cty_losses, rel_bond_monthly_losses, coverage_tot, coverage_cty = self.init_bond_loss(events_per_year, principal) | ||
|
|
||
| list_loss_month.append(rel_bond_monthly_losses) | ||
| annual_losses.extend(rel_ann_bond_losses) | ||
| coverage['payout'] += coverage_tot['payout'] | ||
| coverage['damage'] += coverage_tot['damage'] | ||
|
|
||
| for key in coverage_cty.keys(): | ||
| self.tot_coverage_cty[key]['payout'].append(coverage_cty[key]['payout']) | ||
| self.tot_coverage_cty[key]['damage'].append(coverage_cty[key]['damage']) | ||
|
|
||
| for key in rel_ann_cty_losses: | ||
| ann_cty_losses[key].extend(rel_ann_cty_losses[key]) | ||
|
|
||
| self.df_loss_month = pd.concat(list_loss_month, ignore_index=True) | ||
|
|
||
| att_prob_ann = sum(1 for x in annual_losses if x > 0) / len(annual_losses) | ||
| exp_loss_ann = np.mean(annual_losses) | ||
|
|
||
| annual_losses = pd.Series(annual_losses) | ||
| total_losses = pd.Series(total_losses) | ||
|
|
||
| risk_metrics_annual = multi_level_es(annual_losses, confidence_levels) | ||
|
|
||
| for key in self.tot_coverage_cty.keys(): | ||
| self.tot_coverage_cty[key]['payout'] = sum(self.tot_coverage_cty[key]['payout']) | ||
| self.tot_coverage_cty[key]['damage'] = sum(self.tot_coverage_cty[key]['damage']) | ||
| self.tot_coverage_cty[key]['coverage'] = self.tot_coverage_cty[key]['payout'] / self.tot_coverage_cty[key]['damage'] | ||
| self.tot_coverage_cty[key]['EL'] = np.mean(ann_cty_losses[key]) | ||
|
|
||
| for key in self.tot_coverage_cty: | ||
| self.tot_coverage_cty[key]['share_EL'] = self.tot_coverage_cty[key]['EL'] / exp_loss_ann | ||
|
|
||
|
|
||
|
|
||
| self.loss_metrics = {'EL_ann': exp_loss_ann, | ||
| 'AP_ann': att_prob_ann, | ||
| 'Payout': coverage['payout'], | ||
| 'Damage': coverage['damage'], | ||
| 'VaR_99_ann': risk_metrics_annual[0.99]['VaR'], | ||
| 'VaR_95_ann': risk_metrics_annual[0.95]['VaR'], | ||
| 'ES_99_ann': risk_metrics_annual[0.99]['ES'], | ||
| 'ES_95_ann': risk_metrics_annual[0.95]['ES']} | ||
|
|
||
| LOGGER.info(f'Expected Loss = {exp_loss_ann}') | ||
| LOGGER.info(f'Attachment Probability = {att_prob_ann}') | ||
|
|
||
|
|
||
|
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.