class DocModel: """This is a model to test documentation.""" param_1 = def_parameter(dtype=int, description="This is parameter 1.") param_2 = def_parameter(dtype=int, description="This is parameter 2.") sensit_1 = def_sensitivity(dtype=int, default=1, description="This is sensitivity 1.") sensit_2 = def_sensitivity(dtype=int, default=2, description="This is sensitivity 2.") meta_1 = def_meta(meta="meta_1", description="This is meta 1.") meta_2 = def_meta(meta="meta_2", description="This is meta 2.") return_1 = def_return(dtype=int, description="This is return 1.") return_2 = def_return(dtype=int, description="This is return 2.") @step(uses=["param_1", "sensit_1"], impacts=["return_1"]) def _step_1(self): """Step 1 summary""" pass @step(uses=["param_2", "sensit_2"], impacts=["return_2"]) def _step_2(self): """Step 2 summary""" pass
class IntegerModel: """Integer model for testing.""" a = def_parameter(dtype=int, description="A number A") b = def_parameter(dtype=int, description="A number B") c = def_parameter(dtype=int, description="A number C") d = def_parameter(dtype=int, description="A number D") ret_1 = def_intermediate(dtype=int, description="Results a + b") ret_2 = def_intermediate(dtype=int, description="Results c - d") ret_3 = def_return(dtype=int, description="Result total of step_1 and step_2") @step(uses=["a", "b"], impacts=["ret_1"]) def _step_1(self): """Add a and b together.""" self.ret_1 = self.a + self.b @step(uses=["c", "d"], impacts=["ret_2"]) def _step_2(self): """Subtract d from c""" self.ret_2 = self.c - self.d @step(uses=["ret_1", "ret_2"], impacts=["ret_3"]) def _step_3(self): """Add total of steps 1 and 2.""" self.ret_3 = self.ret_1 + self.ret_2
class ModelParent: x = def_parameter() y = def_parameter() out = def_return() @step(uses=["x", "y"], impacts=["out"]) def _add(self): self.out = self.x + self.y
class Model2: k1 = def_parameter() k2 = def_parameter() a = def_parameter() b = def_parameter() r = def_return() @step(uses=["a", "b"], impacts=["r"]) def _subtract_a_b(self): self.r = self.a - self.b
class Model1: k1 = def_parameter() k2 = def_parameter() a = def_parameter() b = def_parameter() r = def_return() @step(uses=["a", "b"], impacts=["r"]) def _add_a_b(self): self.r = self.a + self.b
class Test: x = def_parameter() y = def_parameter() z = def_parameter() out = def_return() @step(uses=["x", "y"], impacts=["out"]) def _add(self): self.out = self.x + self.y @step(uses=["z", "out"], impacts=["out"]) def _subtract(self): self.out = self.out - self.z
class TestAttributes: param1 = def_parameter() param2 = def_parameter() sensitivity1 = def_sensitivity(default=1) sensitivity2 = def_sensitivity(default=2) meta1 = def_meta(meta="meta1", dtype=str) meta2 = def_meta(meta="meta2", dtype=str) intermediate1 = def_intermediate() intermediate2 = def_intermediate() return1 = def_return() return2 = def_return() @step(uses=["param1", "param2"], impacts=["return1"]) def _step1(self): pass
class AValResRPMD(AValBasePMD): """The active life reserve (ALR) valuation model for the cost of living adjustment (COLA) policy rider. This model is a child of the `AValBasePMD` with the only change being how the monthly benefit is calculated. The base model uses the benefit amount passed while this model calculates the benefit as the benefit amount x residual benefit percent. """ # parameter residual_benefit_percent = def_parameter( dtype=float, description= "The residual benefit percent to multiply by the benefit amount.", ) # meta claim_cost_model = def_meta( meta=claim_cost_model, dtype=callable, description="The claim cost model used.", ) coverage_id = def_meta( meta="RES", dtype=str, description="The coverage id which recognizes base policy vs riders.", )
class FailUsingValue: parameter = def_parameter(dtype=int) ret = 0 @step(uses=["parameter"], impacts=["ret"]) def _add(self): self.ret = self.ret + self.parameter
class FailStepImpactsWrong: parameter = def_parameter(dtype=int) ret = def_return(init_value=0) @step(uses=["ret", "parameter"], impacts=["x"]) def _add(self): self.ret = self.ret + self.parameter
class FailStepNoUses: parameter = def_parameter(dtype=int) ret = def_return(init_value=0) @step(impacts=["ret"]) def _add(self): self.ret = self.ret + self.parameter
class FailUsingAttrib: parameter = def_parameter(dtype=int) ret = attrib(default=0) @step(uses=["parameter"], impacts=["ret"]) def _add(self): self.ret = self.ret + self.parameter
class ModelNoSteps: parameter = def_parameter(dtype=int) intermediate = def_intermediate(init_value=0) @step(uses=["parameter"], impacts=["intermediate"]) def _add(self): self.intermediate = self.parameter
class DisabledLivesProjEMD: # parameters extract_base = def_parameter( dtype=pd.DataFrame, description="The base policy extract for disabled lives.") extract_riders = def_parameter( dtype=pd.DataFrame, description="The rider extract for disabled lives.") valuation_dt = param_valuation_dt assumption_set = param_assumption_set # sensitivities modifier_ctr = modifier_ctr modifier_interest = modifier_interest # meta model_version = meta_model_version last_commit = meta_last_commit run_date_time = meta_run_date_time
class ActiveLivesProjEMD: # parameters extract_base = def_parameter(dtype=pd.DataFrame, description="The active lives base extract.") extract_riders = def_parameter( dtype=pd.DataFrame, description="The active lives rider extract.") valuation_dt = param_valuation_dt assumption_set = param_assumption_set net_benefit_method = param_net_benefit_method # sensitivities modifier_ctr = modifier_ctr modifier_interest = modifier_interest modifier_incidence = modifier_incidence modifier_lapse = modifier_lapse modifier_mortality = modifier_mortality # meta model_version = meta_model_version last_commit = meta_last_commit run_date_time = meta_run_date_time
class Test: parameter = def_parameter(description="This is a parameter.") sensitivity = def_sensitivity(default=1, description="This is a sensitivity.") meta = def_meta(meta="meta", description="This is meta.") ret = def_return(dtype="int", description="This is a return.") @step(uses=["parameter"], impacts=["ret"]) def _add(self): """Do addition.""" pass @step(uses=["ret", "sensitivity"], impacts=["ret"]) def _subtract(self): """Do subtraction.""" pass
class DValResRPMD(DValBasePMD): """The disabled life reserve (DLR) valuation model for the cost of living adjustment (COLA) policy rider. This model is a child of the `DValBasePMD` with the only change being how the monthly benefit is calculated. The base model uses the benefit amount passed while this model calculates the benefit as the benefit amount x residual benefit percent. """ residual_benefit_percent = def_parameter( dtype=float, description="The residual benefit percent to multiply by the benefit amount.", ) coverage_id = def_meta( meta="RES", dtype=str, description="The coverage id which recognizes base policy vs riders.", ) @step( name="Calculate Monthly Benefits", uses=[ "frame", "valuation_dt", "start_pay_dt", "termination_dt", "residual_benefit_percent", "benefit_amount", ], impacts=["frame"], ) def _calculate_benefit_cost(self): """Calculate the monthly benefit amount for each duration.""" self.frame = frame_add_exposure( self.frame, begin_date=max(self.valuation_dt, self.start_pay_dt), end_date=self.termination_dt, exposure_name="EXPOSURE", begin_duration_col="DATE_BD", end_duration_col="DATE_ED", ) self.frame["BENEFIT_AMOUNT"] = ( self.frame["EXPOSURE"] * self.benefit_amount * self.residual_benefit_percent ).round(2)
class Test: ret = def_return() placeholder = def_intermediate() meta = def_meta(meta="meta") modifier = def_sensitivity(default=1) parameter = def_parameter()
class DisabledLivesValEMD: """Disabled lives deterministic valuation extract model. This model takes an extract of policies and runs them through the respective Policy Models based on the COVERAGE_ID column (i.e., whether it is base policy or rider). """ # parameters extract_base = def_parameter( dtype=pd.DataFrame, description="The disabled lives base extract.") extract_riders = def_parameter( dtype=pd.DataFrame, description="The disabled lives rider extract.") valuation_dt = param_valuation_dt assumption_set = param_assumption_set # sensitivities modifier_ctr = modifier_ctr modifier_interest = modifier_interest # meta model_version = meta_model_version last_commit = meta_last_commit run_date_time = meta_run_date_time # intermediates records = def_intermediate( dtype=dict, description="The extract transformed to records.") # return projected = def_return( dtype=pd.DataFrame, description="The projected reserves for the policyholders.") time_0 = def_return( dtype=pd.DataFrame, description="The time 0 reserve for the policyholders.") errors = def_return(dtype=list, description="Any errors captured.") @step( name="Create Records from Extracts", uses=["extract_base", "extract_riders"], impacts=["records"], ) def _create_records(self): """Turn extract into a list of records for each row in extract.""" frame = self.extract_base.copy() del frame["IDI_MARKET"] del frame["TOBACCO_USAGE"] records = convert_to_records(frame, column_case="lower") rider_data = self.extract_riders.copy() rider_data.columns = [col.lower() for col in rider_data.columns] rider_data = rider_data.pivot( index=["policy_id", "claim_id", "coverage_id"], columns="rider_attribute", values="value", ).to_dict(orient="index") def update_record(record): key = ( record["policy_id"], record["claim_id"], record["coverage_id"], ) kwargs_add = rider_data.get(key, None) if kwargs_add is not None: return {**record, **kwargs_add} return record self.records = [ record if record["coverage_id"] not in ["RES"] else update_record(record) for record in records ] @step( name="Run Records with Policy Models", uses=["records"] + list(FOREACH_PARAMS), impacts=["projected", "errors"], ) def _run_foreach(self): """Foreach record run through respective policy model based on COVERAGE_ID value.""" projected, errors = foreach_model(**get_kws(foreach_model, self)) if isinstance(projected, list): projected = pd.DataFrame( columns=list(DisabledLivesValOutput.columns)) self.projected = projected self.errors = errors @step(name="Get Time0 Values", uses=["projected"], impacts=["time_0"]) def _get_time0(self): """Filter projected reserves frame down to time_0 reserve for each record.""" cols = [ "MODEL_VERSION", "LAST_COMMIT", "RUN_DATE_TIME", "SOURCE", "POLICY_ID", "CLAIM_ID", "COVERAGE_ID", "DATE_DLR", "DLR", ] self.time_0 = self.projected.groupby(cols[4:7], as_index=False).head(1)[cols]
from footings.model import def_meta, def_parameter, def_sensitivity from footings.validators import isin try: import importlib.metadata as importlib_metadata except ModuleNotFoundError: import importlib_metadata MOD_VERSION = importlib_metadata.version("footings_idi_model") repo = git.Repo(search_parent_directories=True) GIT_REVISION = repo.head.object.hexsha param_n_simulations = def_parameter( description="The number of simulations to run.", default=1000, dtype=int, ) param_seed = def_parameter( description="The seed passed to numpy.random.seed.", default=42, dtype=int, ) param_valuation_dt = def_parameter( description="The valuation date which reserves are based.", dtype=pd.Timestamp, ) param_assumption_set = def_parameter( description="""The assumption set to use for running the model. Options are : * `stat` * `gaap`
class AValRopRPMD(AValBasePMD): """The active life reserve (ALR) valuation model for the return of premium (ROP) policy rider. This model is a child of the `AValBasePMD` and includes additional parameters for rop_return_freq, rop_return_percent and rop_claims_paid parameters. """ # additional parameters rop_return_freq = def_parameter( dtype="int", description="The return of premium (ROP) frequency in years.") rop_return_percent = def_parameter( dtype="float", description="The return of premium (ROP) percentage.") rop_claims_paid = def_parameter( dtype="float", description="The return of premium (ROP) benefits paid.") # meta coverage_id = def_meta( meta="ROP", dtype=str, description="The coverage id which recognizes base policy vs riders.", ) @step( name="Calculate Benefit Cost", uses=[ "frame", "rop_return_freq", "rop_claims_paid", "rop_return_percent", ], impacts=["frame"], ) def _calculate_benefit_cost(self): """Calculate benefit cost for each duration.""" # set payment intervals self.frame["INCIDENCE_RATE"] = 0 self.frame["ROP_INTERVAL"] = ( self.frame["DURATION_YEAR"].subtract(1).div( self.rop_return_freq).astype(int)) # add paid claims to frame self.frame["PAID_CLAIMS"] = 0 rngs = self.frame.groupby(["ROP_INTERVAL" ]).agg(START_DT=("DATE_BD", "first"), END_DT=("DATE_ED", "last")) end_dt = rngs.query( "START_DT <= @self.valuation_dt <= END_DT")["END_DT"].iat[0] row = self.frame[self.frame["DATE_ED"] == end_dt].index[0] self.frame.at[row, "PAID_CLAIMS"] = self.rop_claims_paid # calculate expected total premium per ROP payment interval self.frame["ROP_PREMIUM"] = _calculate_rop_interval_premium(self.frame) # calculate ROP benefit cost self.frame["ROP_RETURN_PERCENTAGE"] = self.rop_return_percent self.frame["BENEFIT_COST"] = ( self.frame["ROP_PREMIUM"] * self.frame["ROP_RETURN_PERCENTAGE"] - self.frame["PAID_CLAIMS"]).clip(lower=0)
class FailMissingStep: parameter = def_parameter(dtype=int) ret = def_return(init_value=0)
class FailStepNotDecorated: parameter = def_parameter(dtype=int) ret = def_return(init_value=0) def _add(self): self.ret = self.ret + self.parameter
class ModelChild(ModelParent): z = def_parameter() @step(uses=["z", "out"], impacts=["out"]) def _subtract(self): self.out = self.out - self.z
class ModelNoSteps: parameter = def_parameter(dtype=int) ret = def_return(init_value=0)