diff --git a/PiCyberPreAnalysis/LICENSE b/PiCyberPreAnalysis/LICENSE new file mode 100644 index 0000000..1cbb673 --- /dev/null +++ b/PiCyberPreAnalysis/LICENSE @@ -0,0 +1,25 @@ +BSD 2-Clause License + +Copyright (c) 2017, Oasis Loss Modelling Framework +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PiCyberPreAnalysis/README.md b/PiCyberPreAnalysis/README.md new file mode 100644 index 0000000..d8d7620 --- /dev/null +++ b/PiCyberPreAnalysis/README.md @@ -0,0 +1,16 @@ +Oasis LMF logo + +# PiCyber Pre-Analysis + +A copy of the [PiCyber](../PiCyber) model with an exposure pre-analysis hook, to check that pre-analysis works for account only portfolios (no location file), as used by cyber and liability models. + +The hook, `src/exposure_modification/exposure_pre_analysis_revenue.py`, multiplies each account's `AnnualRevenue` by `revenue_scale_factor` (set in `tests/test_1/exposure_pre_analysis.json`). The lookup maps `AnnualRevenue` into revenue bands, so the scaled accounts can be assigned a different area peril and vulnerability. + +After a run, the modified account file is in `runs/losses-/input/account.csv`. + +## Running the model + +``` +cd tests/test_1 +oasislmf model run --config oasislmf.json +``` diff --git a/PiCyberPreAnalysis/keys_data/AnnualRevenueMap.csv b/PiCyberPreAnalysis/keys_data/AnnualRevenueMap.csv new file mode 100644 index 0000000..a6ab10d --- /dev/null +++ b/PiCyberPreAnalysis/keys_data/AnnualRevenueMap.csv @@ -0,0 +1,6 @@ +0 +100000 +1000000 +10000000 +50000000 +100000000 diff --git a/PiCyberPreAnalysis/keys_data/area_peril_dict.csv b/PiCyberPreAnalysis/keys_data/area_peril_dict.csv new file mode 100644 index 0000000..5553911 --- /dev/null +++ b/PiCyberPreAnalysis/keys_data/area_peril_dict.csv @@ -0,0 +1,7 @@ +area_peril_id,revenuecategory,countrycode,peril_id +1,1,GB,CSB +2,2,GB,CSB +3,3,GB,CSB +4,4,GB,CSB +5,5,GB,CSB +6,6,GB,CSB diff --git a/PiCyberPreAnalysis/keys_data/lookup_config.json b/PiCyberPreAnalysis/keys_data/lookup_config.json new file mode 100644 index 0000000..dc0a598 --- /dev/null +++ b/PiCyberPreAnalysis/keys_data/lookup_config.json @@ -0,0 +1,73 @@ +{ + "model": { + "supplier_id": "OasisLMF", + "model_id": "PiCyberPreAnalysis", + "model_version": "0.0.1" + }, + "builtin_lookup_type": "new_lookup", + "keys_data_path": "./", + "step_definition": { + "coverage_type": { + "type": "prepare", + "parameters": { + "coverage_type": { + "default": 10 + } + } + + }, + "annual_revenue_map": { + "type": "interval_to_index", + "columns": [ + "annualrevenue" + ], + "parameters": { + "sorted_array": "%%KEYS_DATA_PATH%%/AnnualRevenueMap.csv", + "value_column_name": "annualrevenue", + "index_column_name": "revenuecategory" + } + }, + "area_peril": { + "type": "merge", + "columns": [ + "revenuecategory", + "countrycode" + ], + "parameters": { + "file_path": "%%KEYS_DATA_PATH%%/area_peril_dict.csv", + "id_columns": ["area_peril_id"], + "usecols": [ + "revenuecategory", + "countrycode", + "area_peril_id", + "peril_id" + ] + + } + }, + "vulnerability": { + "type": "merge", + "columns": [ + "revenuecategory", + "countrycode" + ], + "parameters": { + "file_path": "%%KEYS_DATA_PATH%%/vulnerability_dict.csv", + "id_columns": ["vulnerability_id"], + "usecols": [ + "revenuecategory", + "countrycode", + "vulnerability_id", + "peril_id" + ] + + } + } + }, + "strategy": [ + "coverage_type", + "annual_revenue_map", + "area_peril", + "vulnerability" + ] +} diff --git a/PiCyberPreAnalysis/keys_data/vulnerability_dict.csv b/PiCyberPreAnalysis/keys_data/vulnerability_dict.csv new file mode 100644 index 0000000..d39c39e --- /dev/null +++ b/PiCyberPreAnalysis/keys_data/vulnerability_dict.csv @@ -0,0 +1,7 @@ +vulnerability_id,revenuecategory,countrycode,peril_id +1,1,GB,CSB +2,2,GB,CSB +3,3,GB,CSB +4,4,GB,CSB +5,5,GB,CSB +6,6,GB,CSB diff --git a/PiCyberPreAnalysis/model_data/damage_bin_dict.bin b/PiCyberPreAnalysis/model_data/damage_bin_dict.bin new file mode 100644 index 0000000..272416f Binary files /dev/null and b/PiCyberPreAnalysis/model_data/damage_bin_dict.bin differ diff --git a/PiCyberPreAnalysis/model_data/damage_bin_dict.csv b/PiCyberPreAnalysis/model_data/damage_bin_dict.csv new file mode 100644 index 0000000..fa4ec88 --- /dev/null +++ b/PiCyberPreAnalysis/model_data/damage_bin_dict.csv @@ -0,0 +1,3 @@ +bin_index,bin_from,bin_to,interpolation,damage_type +1,0,0.5,0.25,0 +2,0.5,1,0.75,0 diff --git a/PiCyberPreAnalysis/model_data/events.bin b/PiCyberPreAnalysis/model_data/events.bin new file mode 100644 index 0000000..8956160 Binary files /dev/null and b/PiCyberPreAnalysis/model_data/events.bin differ diff --git a/PiCyberPreAnalysis/model_data/events.csv b/PiCyberPreAnalysis/model_data/events.csv new file mode 100644 index 0000000..360ec03 --- /dev/null +++ b/PiCyberPreAnalysis/model_data/events.csv @@ -0,0 +1,13 @@ +event_id +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 \ No newline at end of file diff --git a/PiCyberPreAnalysis/model_data/footprint.bin.z b/PiCyberPreAnalysis/model_data/footprint.bin.z new file mode 100644 index 0000000..1f01928 Binary files /dev/null and b/PiCyberPreAnalysis/model_data/footprint.bin.z differ diff --git a/PiCyberPreAnalysis/model_data/footprint.csv b/PiCyberPreAnalysis/model_data/footprint.csv new file mode 100644 index 0000000..8878124 --- /dev/null +++ b/PiCyberPreAnalysis/model_data/footprint.csv @@ -0,0 +1,73 @@ +event_id,areaperil_id,intensity_bin_id,probability +1,1,1,1 +1,2,2,1 +1,3,1,1 +1,4,2,1 +1,5,1,1 +1,6,2,1 +2,1,1,1 +2,2,2,1 +2,3,1,1 +2,4,2,1 +2,5,1,1 +2,6,2,1 +3,1,1,1 +3,2,2,1 +3,3,1,1 +3,4,2,1 +3,5,1,1 +3,6,2,1 +4,1,1,1 +4,2,2,1 +4,3,1,1 +4,4,2,1 +4,5,1,1 +4,6,2,1 +5,1,1,1 +5,2,2,1 +5,3,1,1 +5,4,2,1 +5,5,1,1 +5,6,2,1 +6,1,1,1 +6,2,2,1 +6,3,1,1 +6,4,2,1 +6,5,1,1 +6,6,2,1 +7,1,1,1 +7,2,2,1 +7,3,1,1 +7,4,2,1 +7,5,1,1 +7,6,2,1 +8,1,1,1 +8,2,2,1 +8,3,1,1 +8,4,2,1 +8,5,1,1 +8,6,2,1 +9,1,1,1 +9,2,2,1 +9,3,1,1 +9,4,2,1 +9,5,1,1 +9,6,2,1 +10,1,1,1 +10,2,2,1 +10,3,1,1 +10,4,2,1 +10,5,1,1 +10,6,2,1 +11,1,1,1 +11,2,2,1 +11,3,1,1 +11,4,2,1 +11,5,1,1 +11,6,2,1 +12,1,1,1 +12,2,2,1 +12,3,1,1 +12,4,2,1 +12,5,1,1 +12,6,2,1 diff --git a/PiCyberPreAnalysis/model_data/footprint.idx.z b/PiCyberPreAnalysis/model_data/footprint.idx.z new file mode 100644 index 0000000..38b4735 Binary files /dev/null and b/PiCyberPreAnalysis/model_data/footprint.idx.z differ diff --git a/PiCyberPreAnalysis/model_data/model_to_bin.sh b/PiCyberPreAnalysis/model_data/model_to_bin.sh new file mode 100644 index 0000000..1dd34ff --- /dev/null +++ b/PiCyberPreAnalysis/model_data/model_to_bin.sh @@ -0,0 +1,24 @@ +#!/bin/bash + + +# damage bin dict +csvtobin damagebin -i damage_bin_dict.csv -o damage_bin_dict.bin + +# events +csvtobin eve -i events.csv -o events.bin + +# footprint +csvtobin footprint -z -m2 -n -i footprint.csv -o footprint.bin.z -x footprint.idx.z + +# occurrence +csvtobin occurrence -P10 -i occurrence.csv -o occurrence.bin + +# return periods +csvtobin returnperiods -i returnperiods.csv -o returnperiods.bin + +# vulnerability +csvtobin vulnerability -d2 -N -i vulnerability.csv -o vulnerability.bin + + + + diff --git a/PiCyberPreAnalysis/model_data/occurrence.bin b/PiCyberPreAnalysis/model_data/occurrence.bin new file mode 100644 index 0000000..cc55a37 Binary files /dev/null and b/PiCyberPreAnalysis/model_data/occurrence.bin differ diff --git a/PiCyberPreAnalysis/model_data/occurrence.csv b/PiCyberPreAnalysis/model_data/occurrence.csv new file mode 100644 index 0000000..8ce7ece --- /dev/null +++ b/PiCyberPreAnalysis/model_data/occurrence.csv @@ -0,0 +1,4 @@ +event_id,period_no,occ_year,occ_month,occ_day +1,1,2026,1,1 +2,4,2026,2,2 +3,10,2026,3,3 \ No newline at end of file diff --git a/PiCyberPreAnalysis/model_data/returnperiods.bin b/PiCyberPreAnalysis/model_data/returnperiods.bin new file mode 100644 index 0000000..2c77133 Binary files /dev/null and b/PiCyberPreAnalysis/model_data/returnperiods.bin differ diff --git a/PiCyberPreAnalysis/model_data/returnperiods.csv b/PiCyberPreAnalysis/model_data/returnperiods.csv new file mode 100644 index 0000000..179e053 --- /dev/null +++ b/PiCyberPreAnalysis/model_data/returnperiods.csv @@ -0,0 +1,5 @@ +return_period +10 +5 +2 +1 \ No newline at end of file diff --git a/PiCyberPreAnalysis/model_data/vulnerability.bin b/PiCyberPreAnalysis/model_data/vulnerability.bin new file mode 100644 index 0000000..9acd4d4 Binary files /dev/null and b/PiCyberPreAnalysis/model_data/vulnerability.bin differ diff --git a/PiCyberPreAnalysis/model_data/vulnerability.csv b/PiCyberPreAnalysis/model_data/vulnerability.csv new file mode 100644 index 0000000..1b589d8 --- /dev/null +++ b/PiCyberPreAnalysis/model_data/vulnerability.csv @@ -0,0 +1,13 @@ +vulnerability_id,intensity_bin_id,damage_bin_id,probability +1,1,1,1 +1,2,2,1 +2,1,1,1 +2,2,2,1 +3,1,1,1 +3,2,2,1 +4,1,1,1 +4,2,2,1 +5,1,1,1 +5,2,2,1 +6,1,1,1 +6,2,2,1 diff --git a/PiCyberPreAnalysis/src/exposure_modification/__init__.py b/PiCyberPreAnalysis/src/exposure_modification/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PiCyberPreAnalysis/src/exposure_modification/exposure_pre_analysis_revenue.py b/PiCyberPreAnalysis/src/exposure_modification/exposure_pre_analysis_revenue.py new file mode 100644 index 0000000..371e601 --- /dev/null +++ b/PiCyberPreAnalysis/src/exposure_modification/exposure_pre_analysis_revenue.py @@ -0,0 +1,26 @@ +import logging + + +class ExposurePreAnalysis: + """ + Example of custom module called by oasislmf/computation/hooks/pre_analysis.py + for an account only (cyber) portfolio - there is no location file. + + The account's AnnualRevenue is multiplied by "revenue_scale_factor" (from + exposure_pre_analysis.json), e.g. to bring revenues up to date. The lookup maps + AnnualRevenue into revenue bands, so the scaled accounts can move to a different + area peril / vulnerability. + """ + + def __init__(self, exposure_data, exposure_pre_analysis_setting, **kwargs): + self.exposure_data = exposure_data + self.exposure_pre_analysis_setting = exposure_pre_analysis_setting + + def run(self): + scale_factor = self.exposure_pre_analysis_setting.get('revenue_scale_factor', 1) + account_df = self.exposure_data.account.dataframe + + logging.info(f'Scaling AnnualRevenue by {scale_factor}') + account_df['AnnualRevenue'] = account_df['AnnualRevenue'] * scale_factor + + self.exposure_data.account.dataframe = account_df diff --git a/PiCyberPreAnalysis/tests/test_1/account.csv b/PiCyberPreAnalysis/tests/test_1/account.csv new file mode 100644 index 0000000..9932f1f --- /dev/null +++ b/PiCyberPreAnalysis/tests/test_1/account.csv @@ -0,0 +1,5 @@ +PortNumber,AccNumber,AccCurrency,PolNumber,PolInceptionDate,PolExpiryDate,LOB,PolPerilsCovered,LayerParticipation,LayerLimit,LayerAttachment,OEDVersion,CountryCode,AreaCode,IndustryScheme1,IndustryCode1,IndustryVintage1,IndustryDescription,AnnualRevenue,AnnualRevenueCurrency,PolDed,DedCurrency,PolCoverage,InsuredName,URL +PiCyber,1,GBP,P1,2026-01-01,2026-12-31,,CSB,1,10000000,0,5.0.0,GB,,NAICS,72111,2022,Laser Quest,50000,GBP,10000,GBP,CSB,A People,a.com +PiCyber,2,GBP,P2,2026-01-01,2026-12-31,,CSB,1,10000000,0,5.0.0,GB,,NAICS,72111,2022,Laser Quest,1000000,GBP,10000,GBP,CSB,Bee Keepers,b.com +PiCyber,3,GBP,P3,2026-01-01,2026-12-31,,CSB,1,10000000,0,5.0.0,GB,,NAICS,72111,2022,Laser Quest,10000000,GBP,10000,GBP,CSB,C Shanty,c.com +PiCyber,4,GBP,P4,2026-01-01,2026-12-31,,CSB,1,10000000,0,5.0.0,GB,,NAICS,72111,2022,Laser Quest,50000000,GBP,10000,GBP,CSB,Big D,d.com diff --git a/PiCyberPreAnalysis/tests/test_1/analysis_settings.json b/PiCyberPreAnalysis/tests/test_1/analysis_settings.json new file mode 100644 index 0000000..ae79687 --- /dev/null +++ b/PiCyberPreAnalysis/tests/test_1/analysis_settings.json @@ -0,0 +1,18 @@ +{ + "version": "3", + "model_name_id": "PiCyberPreAnalysis", + "model_supplier_id": "OasisLMF", + "gul_output": true, + "number_of_samples": 10, + "model_settings": {}, + "gul_summaries": [ + { + "id": 1, + "ord_output": { + "elt_moment": true, + "alt_period": true + } + } + ] +} + diff --git a/PiCyberPreAnalysis/tests/test_1/exposure_pre_analysis.json b/PiCyberPreAnalysis/tests/test_1/exposure_pre_analysis.json new file mode 100644 index 0000000..b856adb --- /dev/null +++ b/PiCyberPreAnalysis/tests/test_1/exposure_pre_analysis.json @@ -0,0 +1,3 @@ +{ + "revenue_scale_factor": 10 +} diff --git a/PiCyberPreAnalysis/tests/test_1/oasislmf.json b/PiCyberPreAnalysis/tests/test_1/oasislmf.json new file mode 100644 index 0000000..199fc11 --- /dev/null +++ b/PiCyberPreAnalysis/tests/test_1/oasislmf.json @@ -0,0 +1,9 @@ +{ + "analysis_settings_json": "analysis_settings.json", + "lookup_config_json": "../../keys_data/lookup_config.json", + "lookup_data_dir": "../../keys_data/", + "model_data_dir": "../../model_data/", + "oed_accounts_csv": "./account.csv", + "exposure_pre_analysis_module": "../../src/exposure_modification/exposure_pre_analysis_revenue.py", + "exposure_pre_analysis_setting_json": "exposure_pre_analysis.json" +}