Source code for oasislmf.preparation.oed

__all__ = [
    'load_oed_dfs',
    'OedValidator'
]
import os

from ods_tools.oed import fill_empty

from ..utils.data import get_dataframe, RI_INFO_DEFAULTS, RI_SCOPE_DEFAULTS


# TODO - add validator
[docs] class OedValidator(object): def __init__(self, ri_info_rules=None, ri_scope_rules=None):
[docs] self.rules_ode_scope = ri_info_rules
[docs] self.rules_ode_info = ri_scope_rules
[docs] self.ri_info_required_cols = list(RI_INFO_REQUIRED_COLS)
[docs] self.ri_info_defaults = { 'CededPercent': 1.0, 'RiskLimit': 0.0, 'RiskAttachment': 0.0, 'OccLimit': 0.0, 'OccAttachment': 0.0, 'TreatyShare': 0.0 }
[docs] self.ri_scope_required_cols = list(RI_SCOPE_REQUIRED_COLS)
[docs] self.error_structure = {}
def _unique_reins(self, reins_info_df): ''' check if only one reins type exisits ''' return (len(reins_info_df.ReinsType.unique()) == 1) def _find_missing(self, df_src, column_name, df_dest): ''' return list of values if df_dest[`column_name`] values do not exist in df_src ''' src_values = df_dest[column_name].unique().tolist() missing_df = df_src[~df_src.isin({column_name: src_values})].dropna() return missing_df[column_name].tolist() def _all_scope_non_specific(self, scope_df): return scope_df[['AccountNumber', 'PolicyNumber', 'LocationNumber' ]].isnull().all().all() def _all_scope_specific(self, scope_df): return scope_df[['AccountNumber', 'PolicyNumber', 'LocationNumber' ]].notnull().all().all() def _error_struture(self, check_type, chcek_scope, msg, info=''): return { "check": check_type, "scope": chcek_scope, "messsages": msg, "meta_data": info, } # def validate(self, account_df, location_df, ri_info_df, ri_scope_df):
[docs] def validate(self, ri_info_df, ri_scope_df): ''' Validate OED resinurance structure before running calculations. ''' error_list = [] for inuring_priority in range(1, ri_info_df['InuringPriority'].max() + 1): inuring_priority_ri_info_df = ri_info_df[ri_info_df.InuringPriority == inuring_priority] if inuring_priority_ri_info_df.empty: continue inuring_scope_ids = inuring_priority_ri_info_df.ReinsNumber.tolist() inuring_scopes = [ri_scope_df[ri_scope_df.ReinsNumber == ID] for ID in inuring_scope_ids] reins_types_found = inuring_priority_ri_info_df.ReinsType.unique().tolist() meta_data = { "InuringPriority": inuring_priority, "ReinsTypes": reins_types_found, "ri_info_ReinsNumbers": inuring_priority_ri_info_df.ReinsNumber.tolist(), "ri_info_line_nums": [idx + 2 for idx in inuring_priority_ri_info_df.index.tolist()], } # CHECK - only single ri_type is set per inuring priority if len(reins_types_found) > 1: error_list.append(self._error_struture( "inuring_reins_type", "RI_{}".format(inuring_priority), "Inuring layer must have a unique ReinsType.", meta_data )) continue elif len(reins_types_found) < 1: error_list.append(self._error_struture( "inuring_reins_type", "RI_{}".format(inuring_priority), "Inuring layer missing a ReinsType.", meta_data )) continue # CHECK - ri_type is supported ri_type = reins_types_found[0] if ri_type not in REINS_TYPES: error_list.append(self._error_struture( "inuring_reins_type", "RI_{}".format(inuring_priority), "Unsupported ReinsType", meta_data )) # CHECK scope of inuring layer for scope_df in inuring_scopes: scope_risk_levels = scope_df.RiskLevel.unique() meta_data.update({ "RiskLevels": scope_risk_levels.tolist(), "ri_scope_ReinsNumber": scope_df.ReinsNumber.tolist(), "ri_scope_line_nums": [idx + 2 for idx in scope_df.index.tolist()], }) # CHECK - each scope only has one risk level type if len(scope_risk_levels) > 1: error_list.append(self._error_struture( "inuring_risk_level", "RI_{}".format(inuring_priority), "Mix of risk levels in a single reinsurance scope", meta_data, )) continue elif len(scope_risk_levels) < 1: error_list.append(self._error_struture( "inuring_risk_level", "RI_{}".format(inuring_priority), "inuring layer has no reinsurance scope", meta_data, )) continue # CHECK - Risk level is supported risk_level_id = scope_risk_levels[0] if risk_level_id not in SUPPORTED_RISK_LEVELS[ri_type]: error_list.append(self._error_struture( "inuring_risk_level", "RI_{}".format(inuring_priority), "Unsupported risk level", meta_data, )) return (not error_list, error_list)
[docs] def load_oed_dfs(oed_ri_info_file, oed_ri_scope_file, show_all=False): """ Load OED data files. """ do_reinsurance = True oed_ri_info_file_exists = os.path.exists(oed_ri_info_file) oed_ri_scope_file_exists = os.path.exists(oed_ri_scope_file) if not oed_ri_info_file_exists and not oed_ri_scope_file_exists: ri_info_df = None ri_scope_df = None do_reinsurance = False elif oed_ri_info_file_exists and oed_ri_scope_file_exists: ri_info_df = get_dataframe( oed_ri_info_file, lowercase_cols=False, required_cols=RI_INFO_REQUIRED_COLS, col_defaults=RI_INFO_DEFAULTS, col_dtypes=RI_INFO_DTYPES) ri_scope_df = get_dataframe( oed_ri_scope_file, lowercase_cols=False, required_cols=RI_SCOPE_REQUIRED_COLS, col_defaults=RI_SCOPE_DEFAULTS, col_dtypes=RI_SCOPE_DTYPES) # Treat empty Risk Level as portfolio level scope. # Also need nan, as this is produced when # a single row with empty Risk Level is loaded. fill_empty(ri_info_df, 'RiskLevel', REINS_RISK_LEVEL_PORTFOLIO) else: print("Both reinsurance files must exist: {} {}".format( oed_ri_info_file, oed_ri_scope_file)) if do_reinsurance: optional_currency_cols = list({'originalcurrency', 'rateofexchange'} & set(ri_info_df.columns)) ri_info_df = ri_info_df[OED_REINS_INFO_FIELDS + optional_currency_cols].copy() ri_scope_df = ri_scope_df[OED_REINS_SCOPE_FIELDS].copy() # Ensure Percent feilds are float info_float_cols = ['CededPercent', 'PlacedPercent', 'TreatyShare'] scope_float_cols = ['CededPercent'] ri_info_df[info_float_cols] = ri_info_df[info_float_cols].astype(float) ri_scope_df[scope_float_cols] = ri_scope_df[scope_float_cols].astype(float) return (ri_info_df, ri_scope_df, do_reinsurance)
# # Ktools constants # DEDUCTIBLE_AND_LIMIT_CALCRULE_ID = 1 FRANCHISE_DEDUCTIBLE_AND_LIMIT_CALCRULE_ID = 3 DEDUCTIBLE_ONLY_CALCRULE_ID = 12 DEDUCTIBLE_AS_A_CAP_ON_THE_RETENTION_OF_INPUT_LOSSES_CALCRULE_ID = 10 DEDUCTIBLE_AS_A_FLOOR_ON_THE_RETENTION_OF_INPUT_LOSSES_CALCRULE_ID = 11 DEDUCTIBLE_LIMIT_AND_SHARE_CALCRULE_ID = 2 DEDUCTIBLE_AND_LIMIT_AS_A_PROPORTION_OF_LOSS_CALCRUKE_ID = 5 DEDUCTIBLE_WITH_LIMIT_AS_A_PROPORTION_OF_LOSS_CALCRUKE_ID = 9 LIMIT_ONLY_CALCRULE_ID = 14 LIMIT_AS_A_PROPORTION_OF_LOSS_CALCRULE_ID = 15 DEDUCTIBLE_AS_A_PROPORTION_OF_LOSS_CALCRULE_ID = 16 CALCRULE_ID_DEDUCTIBLE_AND_LIMIT = 1 CALCRULE_ID_DEDUCTIBLE_ATTACHMENT_LIMIT_AND_SHARE = 2 CALCRULE_ID_FRANCHISE_DEDUCTIBLE_AND_LIMIT = 3 CALCRULE_ID_DEDUCTIBLE_AND_LIMIT_PERCENT_TIV = 4 CALCRULE_ID_DEDUCTIBLE_AND_LIMIT_PERCENT_LOSS = 5 CALCRULE_ID_DEDUCTIBLE_PERCENT_TIV = 6 CALCRULE_ID_LIMIT_AND_MAX_DEDUCTIBLE = 7 CALCRULE_ID_LIMIT_AND_MIN_DEDUCTIBLE = 8 CALCRULE_ID_LIMIT_WITH_DEDUCTIBLE_PERCENT_LIMIT = 9 CALCRULE_ID_MAX_DEDUCTIBLE = 10 CALCRULE_ID_MIN_DEDUCTIBLE = 11 CALCRULE_ID_DEDUCTIBLE_ONLY = 12 CALCRULE_ID_MAIN_AND_MAX_DEDUCTIBLE = 13 CALCRULE_ID_LIMIT_ONLY = 14 CALCRULE_ID_LIMIT_PERCENT_LOSS = 15 CALCRULE_ID_DEDUCTIBLE_PERCENT_LOSS = 16 CALCRULE_ID_DEDUCTIBLE_PERCENT_LOSS_ATTACHMENT_LIMIT_AND_SHARE = 17 CALCRULE_ID_DEDUCTIBLE_PERCENT_TIV_ATTACHMENT_LIMIT_AND_SHARE = 18 CALCRULE_ID_DEDUCTIBLE_PERCENT_LOSS_WITH_MIN_AND_MAX = 19 CALCRULE_ID_REVERSE_FRANCHISE_DEDUCTIBLE = 20 CALCRULE_ID_SHARE_AND_LIMIT = 21 CALCRULE_ID_QUOTA_SHARE = 22 CALCRULE_ID_OCCURRENCE_LIMIT_AND_SHARE = 23 CALCRULE_ID_OCCURRENCE_CATASTROPHE_EXCESS_OF_LOSS = 24 CALCRULE_ID_FACULTATIVE_WITH_POLICY_SHARE = 25 NO_ALLOCATION_ALLOC_ID = 0 ALLOCATE_TO_ITEMS_BY_GUL_ALLOC_ID = 1 ALLOCATE_TO_ITEMS_BY_PREVIOUS_LEVEL_ALLOC_ID = 2 BUILDING_COVERAGE_TYPE_ID = 1 OTHER_BUILDING_COVERAGE_TYPE_ID = 2 CONTENTS_COVERAGE_TYPE_ID = 3 TIME_COVERAGE_TYPE_ID = 4 COVERAGE_TYPES = [ BUILDING_COVERAGE_TYPE_ID, OTHER_BUILDING_COVERAGE_TYPE_ID, CONTENTS_COVERAGE_TYPE_ID, TIME_COVERAGE_TYPE_ID] PERIL_WIND = 1 PERILS = [PERIL_WIND] GUL_INPUTS_FILES = [ 'coverages', 'gulsummaryxref', 'items'] IL_INPUTS_FILES = [ 'fm_policytc', 'fm_profile', 'fm_programme', 'fm_xref', 'fmsummaryxref'] OPTIONAL_INPUTS_FILES = [ 'events'] CONVERSION_TOOLS = { 'coverages': 'coveragetobin', 'events': 'evetobin', 'fm_policytc': 'fmpolicytctobin', 'fm_profile': 'fmprofiletobin', 'fm_programme': 'fmprogrammetobin', 'fm_xref': 'fmxreftobin', 'fmsummaryxref': 'fmsummaryxreftobin', 'gulsummaryxref': 'gulsummaryxreftobin', 'items': "itemtobin"} NOT_SET_ID = -1 LARGE_VALUE = 9999999999999 # --- OED constants --------------------------------------------------------- # RI_INFO_REQUIRED_COLS = [ 'ReinsNumber', 'PlacedPercent', 'InuringPriority', 'ReinsType', 'RiskLevel', 'ReinsCurrency' ] RI_SCOPE_REQUIRED_COLS = { 'ReinsNumber', } RI_INFO_DTYPES = { 'ReinsNumber': "int", 'CededPercent': "float", 'RiskLimit': "float", 'RiskAttachment': "float", 'OccLimit': "float", 'OccAttachment': "float", 'InuringPriority': "int", 'ReinsType': "str", 'RiskLevel': "str", 'PlacedPercent': "float", 'TreatyShare': "float", 'ReinsCurrency': 'str' } RI_SCOPE_DTYPES = { 'ReinsNumber': "int", 'PortNumber': "str", 'AccNumber': "str", 'LocGroup': "str", 'PolNumber': "str", 'LocNumber': "str", 'CedantName': "str", 'ProducerName': "str", 'LOB': "str", 'CountryCode': "str", 'ReinsTag': "str", 'CededPercent': "float" } POLICYITEM_LEVEL = 0 LOCATION_LEVEL = 1 POLICY_LEVEL = 2 ACCOUNT_LEVEL = 3 OCCURRENCE_LEVEL = 4 PASSTHROUGH_NODE_TYPE = 1 NOLOSS_NODE_TYPE = 1 REINS_TYPE_FAC = "FAC" REINS_TYPE_QUOTA_SHARE = "QS" REINS_TYPE_SURPLUS_SHARE = "SS" REINS_TYPE_PER_RISK = "PR" REINS_TYPE_CAT_XL = "CXL" REINS_TYPE_AGG_XL = "AXL" REINS_TYPES = [ REINS_TYPE_FAC, REINS_TYPE_QUOTA_SHARE, REINS_TYPE_SURPLUS_SHARE, REINS_TYPE_PER_RISK, REINS_TYPE_CAT_XL ] REINS_RISK_LEVEL_PORTFOLIO = "SEL" REINS_RISK_LEVEL_LOCATION = "LOC" REINS_RISK_LEVEL_LOCATION_GROUP = "LGR" REINS_RISK_LEVEL_POLICY = "POL" REINS_RISK_LEVEL_ACCOUNT = "ACC" REINS_RISK_LEVELS = [ REINS_RISK_LEVEL_LOCATION, REINS_RISK_LEVEL_LOCATION_GROUP, REINS_RISK_LEVEL_POLICY, REINS_RISK_LEVEL_ACCOUNT, REINS_RISK_LEVEL_PORTFOLIO, ] SUPPORTED_RISK_LEVELS = { REINS_TYPE_FAC: [REINS_RISK_LEVEL_LOCATION, REINS_RISK_LEVEL_LOCATION_GROUP, REINS_RISK_LEVEL_POLICY, REINS_RISK_LEVEL_ACCOUNT], REINS_TYPE_SURPLUS_SHARE: [REINS_RISK_LEVEL_LOCATION, REINS_RISK_LEVEL_LOCATION_GROUP, REINS_RISK_LEVEL_POLICY, REINS_RISK_LEVEL_ACCOUNT], REINS_TYPE_PER_RISK: [REINS_RISK_LEVEL_LOCATION, REINS_RISK_LEVEL_LOCATION_GROUP, REINS_RISK_LEVEL_POLICY, REINS_RISK_LEVEL_ACCOUNT], REINS_TYPE_CAT_XL: [REINS_RISK_LEVEL_PORTFOLIO], REINS_TYPE_QUOTA_SHARE: REINS_RISK_LEVELS } # Subset of the fields that are currently used OED_ACCOUNT_FIELDS = [ 'PortNumber', 'AccNumber', 'PolNumber', 'AccPeril', 'AccDed6All', 'AccLimit6All' ] OED_LOCATION_FIELDS = [ 'AccNumber', 'LocGroup', 'LocNumber', 'LocDed6All', 'LocLimit6All', 'BuildingTIV', 'OtherTIV', 'ContentsTIV', 'BITIV' ] OED_REINS_INFO_FIELDS = [ 'ReinsNumber', 'CededPercent', 'RiskLimit', 'RiskAttachment', 'OccLimit', 'OccAttachment', 'InuringPriority', 'ReinsType', 'RiskLevel', 'PlacedPercent', 'TreatyShare', 'ReinsCurrency', ] OED_REINS_SCOPE_FIELDS = [ 'ReinsNumber', 'PortNumber', 'AccNumber', 'PolNumber', 'LocGroup', 'LocNumber', 'CedantName', 'ProducerName', 'LOB', 'CountryCode', 'ReinsTag', 'CededPercent' ]