diff --git a/genericagent.py b/genericagent.py new file mode 100644 index 0000000..8e58efe --- /dev/null +++ b/genericagent.py @@ -0,0 +1,7 @@ + +class GenericAgent(): + def __init__(self, *args, **kwargs): + self.init(*args, **kwargs) + + def init(*args, **kwargs): + assert False, "Error: GenericAgent init method should have been overridden but was not." diff --git a/genericagentabce.py b/genericagentabce.py new file mode 100644 index 0000000..c0eb884 --- /dev/null +++ b/genericagentabce.py @@ -0,0 +1,4 @@ +import abce + +class GenericAgent(abce.Agent): + pass diff --git a/insurancecontract.py b/insurancecontract.py index 2c1f274..c5073a7 100644 --- a/insurancecontract.py +++ b/insurancecontract.py @@ -1,11 +1,8 @@ import numpy as np import sys, pdb -#from insurancesimulation import InsuranceSimulation - class InsuranceContract(): - def __init__(self, insurer, properties, time, premium, runtime, deductible=0, excess=None, reinsurance=0): - #reinrisk=None): + def __init__(self, insurer, properties, time, premium, runtime, payment_period, deductible=0, excess=None, reinsurance=0): """Constructor method. Accepts arguments insurer: Type InsuranceFirm. @@ -13,6 +10,7 @@ def __init__(self, insurer, properties, time, premium, runtime, deductible=0, ex time: Type integer. The current time. premium: Type float. runtime: Type integer. + payment_period: Type integer. optional: deductible: Type float (or int) excess: Type float (or int or None) @@ -43,23 +41,32 @@ def __init__(self, insurer, properties, time, premium, runtime, deductible=0, ex self.excess = excess if excess is not None else self.value self.reinsurance = reinsurance - #self.rein_ID = reinrisk self.reinsurer = None self.reincontract = None self.reinsurance_share = None #self.is_reinsurancecontract = False - # Create obligation for premium payment - self.property_holder.receive_obligation(premium * (self.excess - self.deductible), self.insurer, time) + # setup payment schedule + self.payment_times = [time + i for i in range(runtime) if i % payment_period == 0] + self.payment_values = premium * (np.ones(len(self.payment_times)) / len(self.payment_times)) + + ## Create obligation for premium payment + #self.property_holder.receive_obligation(premium * (self.excess - self.deductible), self.insurer, time) # Embed contract in reinsurance network, if applicable if self.contract is not None: self.contract.reinsure(reinsurer=self.insurer, reinsurance_share=properties["reinsurance_share"], \ reincontract=self) - #self.contract.reinsurer = self.insurer #TODO: do not write into other object's attributes! - #self.contract.reinsurance_share = properties["reinsurance_share"] - #self.contract.reincontract = self + + def check_payment_due(self, time): + if len(self.payment_times) > 0 and time >= self.payment_times[0]: + # Create obligation for premium payment + #self.property_holder.receive_obligation(premium * (self.excess - self.deductible), self.insurer, time) + self.property_holder.receive_obligation(self.payment_values[0] * (self.excess - self.deductible), self.insurer, time) + # Remove current payment from payment schedule + self.payment_times = self.payment_times[1:] + self.payment_values = self.payment_values[1:] def explode(self, expire_immediately, time, uniform_value, damage_extent): """Explode method. @@ -132,20 +139,11 @@ def reinsure(self, reinsurer, reinsurance_share, reincontract): self.reincontract = reincontract assert self.reinsurance_share in [None, 0.0, 1.0] - #def reinsure(self, reinsurance_share): - # self.reinsurance = self.value * reinsurance_share - # self.reinsurance_share = reinsurance_share - # - # # Values other than 0.0 and 1.0 are not implemented (will break the risk model. - # # Assert that it breaks if other values are found. - # assert self.reinsurance_share in [None, 0.0, 1.0] - def unreinsure(self): """Unreinsurance Method. Accepts no arguments: No return value. Removes parameters for reinsurance of the current contract. To be called when reinsurance has terminated.""" - #self.rein_ID = None self.reinsurer = None self.reincontract = None self.reinsurance = 0 diff --git a/insurancefirm.py b/insurancefirm.py index 5257d55..ab6cca8 100644 --- a/insurancefirm.py +++ b/insurancefirm.py @@ -1,20 +1,27 @@ +import isleconfig import numpy as np import scipy.stats -from insurancecontract import InsuranceContract +from insurancecontract import InsuranceContract from reinsurancecontract import ReinsuranceContract import sys, pdb import uuid +import numba as nb -class InsuranceFirm(): - def __init__(self, simulation, simulation_parameters, agent_parameters): - self.simulation = simulation - ##or: ABCE style: - #def init(self, simulation_parameters, agent_parameters): - ##note that ABCE-style cannot have a pointer to the simulation. It should deal with customers directly instead. +if isleconfig.use_abce: + from genericagentabce import GenericAgent + #print("abce imported") +else: + from genericagent import GenericAgent + #print("abce not imported") + +class InsuranceFirm(GenericAgent): + def init(self, simulation_parameters, agent_parameters): + self.simulation = simulation_parameters['simulation'] self.contract_runtime_dist = scipy.stats.randint(simulation_parameters["mean_contract_runtime"] - \ simulation_parameters["contract_runtime_halfspread"], simulation_parameters["mean_contract_runtime"] \ + simulation_parameters["contract_runtime_halfspread"] + 1) + self.default_contract_payment_period = simulation_parameters["default_contract_payment_period"] self.id = agent_parameters['id'] self.cash = agent_parameters['initial_cash'] self.riskmodel = agent_parameters['riskmodel'] @@ -30,21 +37,15 @@ def __init__(self, simulation, simulation_parameters, agent_parameters): self.operational = True self.is_insurer = True self.is_reinsurer = False - - def iterate(self, time): - #"""realize income: not necessary""" - #pass - - #self.ans_reinsurance() + def iterate(self, time): """obtain investments yield""" - self.obtain_yield(time) """realize due payments""" self.effect_payments(time) print(time, ":", self.id, len(self.underwritten_contracts), self.cash, self.operational) - + """mature contracts""" print("Number of underwritten contracts ", len(self.underwritten_contracts)) maturing = [contract for contract in self.underwritten_contracts if contract.expiration <= time] @@ -52,50 +53,44 @@ def iterate(self, time): self.underwritten_contracts.remove(contract) contract.mature(time) contracts_dissolved = len(maturing) - + + """effect payments from contracts""" + [contract.check_payment_due(time) for contract in self.underwritten_contracts] + if self.operational: - # Only for ABCE: - #"""collect messages""" - #self.obligations += [obligation.content for obligation in self.get_messages('obligation')] - + """request risks to be considered for underwriting in the next period and collect those for this period""" - # Non-ABCE style new_risks = [] if self.is_insurer: new_risks += self.simulation.solicit_insurance_requests(self.id, self.cash) if self.is_reinsurer: new_risks += self.simulation.solicit_reinsurance_requests(self.id, self.cash) - ## ABCE style - #self.message("insurancecustomer", 0, 'solicit_insurance_requests', {"number": self.posession("money")}) - #new_risks = [] - #for new_risks in self.get_messages('new_risks') - # new_risks += new_risks.content contracts_offered = len(new_risks) try: assert contracts_offered > 2 * contracts_dissolved except: print("Something wrong; agent {0:d} receives too few new contracts {1:d} <= {2:d}".format(self.id, contracts_offered, 2*contracts_dissolved)) #print(self.id, " has ", len(self.underwritten_contracts), " & receives ", contracts_offered, " & lost ", contracts_dissolved) - - + + """make underwriting decisions, category-wise""" underwritten_risks = [{"excess": contract.value, "category": contract.category, \ "risk_factor": contract.risk_factor, "deductible": contract.deductible, \ "runtime": contract.runtime} for contract in self.underwritten_contracts if contract.reinsurance_share != 1.0] # TODO: Enable reinsurance shares other tan 0.0 and 1.0 - expected_profit, acceptable_by_category = self.riskmodel.evaluate(underwritten_risks, self.cash) - + expected_profit, acceptable_by_category = self.riskmodel.evaluate(underwritten_risks, self.cash) + #if expected_profit * 1./self.cash < self.profit_target: # self.acceptance_threshold = ((self.acceptance_threshold - .4) * 5. * self.acceptance_threshold_friction) / 5. + .4 #else: # self.acceptance_threshold = (1 - self.acceptance_threshold_friction * (1 - (self.acceptance_threshold - .4) * 5.)) / 5. + .4 - + growth_limit = max(50, 2 * len(self.underwritten_contracts) + contracts_dissolved) if sum(acceptable_by_category) > growth_limit: acceptable_by_category = np.asarray(acceptable_by_category) acceptable_by_category = acceptable_by_category * growth_limit / sum(acceptable_by_category) acceptable_by_category = np.int64(np.round(acceptable_by_category)) - + not_accepted_risks = [] for categ_id in range(len(acceptable_by_category)): categ_risks = [risk for risk in new_risks if risk["category"] == categ_id] @@ -103,69 +98,58 @@ def iterate(self, time): categ_risks = sorted(categ_risks, key = lambda risk: risk["risk_factor"]) i = 0 print("InsuranceFirm underwrote: ", len(self.underwritten_contracts), " will accept: ", acceptable_by_category[categ_id], " out of ", len(categ_risks), "acceptance threshold: ", self.acceptance_threshold) - #try: - if True: - while (acceptable_by_category[categ_id] > 0 and len(categ_risks) > i): #\ - #and categ_risks[i]["risk_factor"] < self.acceptance_threshold): - if categ_risks[i].get("contract") is not None: #categ_risks[i]["reinsurance"]: - if categ_risks[i]["contract"].expiration > time: # required to rule out contracts that have exploded in the meantime - #print("ACCEPTING", categ_risks[i]["contract"].expiration, categ_risks[i]["expiration"], categ_risks[i]["identifier"], categ_risks[i].get("contract").terminating) - contract = ReinsuranceContract(self, categ_risks[i], time, \ - self.simulation.get_market_premium(), categ_risks[i]["expiration"] - time) # TODO: make last agrument less convoluted, but consistent with insurancefirm - self.underwritten_contracts.append(contract) - #categ_risks[i]["contract"].reincontract = contract - # TODO: move this to insurancecontract (ca. line 14) -> DONE - # TODO: do not write into other object's properties, use setter -> DONE - - assert categ_risks[i]["contract"].expiration >= contract.expiration, "Reinsurancecontract lasts longer than insurancecontract: {0:d}>{1:d} (EXPIRATION2: {2:d} Time: {3:d})".format(contract.expiration, categ_risks[i]["contract"].expiration, categ_risks[i]["expiration"], time) - #else: - # pass - else: - contract = InsuranceContract(self, categ_risks[i], time, self.simulation.get_market_premium(), self.contract_runtime_dist.rvs()) + while (acceptable_by_category[categ_id] > 0 and len(categ_risks) > i): #\ + #and categ_risks[i]["risk_factor"] < self.acceptance_threshold): + if categ_risks[i].get("contract") is not None: #categ_risks[i]["reinsurance"]: + if categ_risks[i]["contract"].expiration > time: # required to rule out contracts that have exploded in the meantime + #print("ACCEPTING", categ_risks[i]["contract"].expiration, categ_risks[i]["expiration"], categ_risks[i]["identifier"], categ_risks[i].get("contract").terminating) + contract = ReinsuranceContract(self, categ_risks[i], time, \ + self.simulation.get_market_premium(), categ_risks[i]["expiration"] - time, self.default_contract_payment_period) # TODO: make second-to-last agrument less convoluted, but consistent with insurancefirm self.underwritten_contracts.append(contract) - acceptable_by_category[categ_id] -= 1 # TODO: allow different values per risk (i.e. sum over value (and reinsurance_share) or exposure instead of counting) - i += 1 - #except: - # print(sys.exc_info()) - # pdb.set_trace() - if self.is_insurer: - not_accepted_risks += categ_risks[i:] - not_accepted_risks = [risk for risk in not_accepted_risks] - - #return unacceptables - #print(self.id, " now has ", len(self.underwritten_contracts), " & returns ", len(not_accepted_risks)) - + #categ_risks[i]["contract"].reincontract = contract + # TODO: move this to insurancecontract (ca. line 14) -> DONE + # TODO: do not write into other object's properties, use setter -> DONE + + assert categ_risks[i]["contract"].expiration >= contract.expiration, "Reinsurancecontract lasts longer than insurancecontract: {0:d}>{1:d} (EXPIRATION2: {2:d} Time: {3:d})".format(contract.expiration, categ_risks[i]["contract"].expiration, categ_risks[i]["expiration"], time) + #else: + # pass + else: + contract = InsuranceContract(self, categ_risks[i], time, self.simulation.get_market_premium(), self.contract_runtime_dist.rvs(), self.default_contract_payment_period) + self.underwritten_contracts.append(contract) + acceptable_by_category[categ_id] -= 1 # TODO: allow different values per risk (i.e. sum over value (and reinsurance_share) or exposure instead of counting) + i += 1 + + not_accepted_risks += categ_risks[i:] + not_accepted_risks = [risk for risk in not_accepted_risks if risk.get("contract") is None] + + # seek reinsurance if self.is_insurer: # TODO: Why should only insurers be able to get reinsurance (not reinsurers)? (Technically, it should work) self.ask_reinsurance() + # return unacceptables + #print(self.id, " now has ", len(self.underwritten_contracts), " & returns ", len(not_accepted_risks)) self.simulation.return_risks(not_accepted_risks) - + #not implemented #"""adjust liquidity, borrow or invest""" #pass - else: - pass - #Non-ABCE style not required - #self.simulation.return_risks(self.simulation.solicit_insurance_requests(0)) - #ABCE style: - # ...requires collecting message with risks like above and sending the all back - + def enter_illiquidity(self, time): self.enter_bankruptcy(time) - + def enter_bankruptcy(self, time): [contract.dissolve(time) for contract in self.underwritten_contracts] # removing (dissolving) all risks immediately after bankruptcy (may not be realistic, they might instead be bought by another company) self.simulation.receive(self.cash) self.cash = 0 self.operational = False - + def receive_obligation(self, amount, recipient, due_time): obligation = {"amount": amount, "recipient": recipient, "due_time": due_time} self.obligations.append(obligation) - + def effect_payments(self, time): - due = [item for item in self.obligations if item["due_time"]<=time] + due = [item for item in self.obligations if item["due_time"]<=time] self.obligations = [item for item in self.obligations if item["due_time"]>time] sum_due = sum([item["amount"] for item in due]) if sum_due > self.cash: @@ -175,18 +159,12 @@ def effect_payments(self, time): for obligation in due: self.pay(obligation["amount"], obligation["recipient"]) - + def pay(self, amount, recipient): - ## ABCE style: - #self.give(self, recipient, "cash", amount) - # Non-ABCE style: self.cash -= amount recipient.receive(amount) - + def receive(self, amount): - ## Not necessary in ABCE style - #pass - # Non-ABCE style """Method to accept cash payments.""" self.cash += amount @@ -194,13 +172,24 @@ def obtain_yield(self, time): amount = self.cash * self.interest_rate self.simulation.receive_obligation(amount, self, time) + @nb.jit def ask_reinsurance(self): - nonreinsured = [contract - for contract in self.underwritten_contracts - if contract.reincontract == None] + nonreinsured = [] + for contract in self.underwritten_contracts: + if contract.reincontract == None: + nonreinsured.append(contract) + + #nonreinsured_b = [contract + # for contract in self.underwritten_contracts + # if contract.reincontract == None] + # + #try: + # assert nonreinsured == nonreinsured_b + #except: + # pdb.set_trace() nonreinsured.reverse() - + if len(nonreinsured) >= (1 - self.reinsurance_limit) * len(self.underwritten_contracts): counter = 0 limitrein = len(nonreinsured) - (1 - self.reinsurance_limit) * len(self.underwritten_contracts) @@ -211,15 +200,32 @@ def ask_reinsurance(self): "reinsurance_share": 1., "expiration": contract.expiration, "contract": contract, "risk_factor": contract.risk_factor} - - ## TODO: never write into other object's properties -> DONE - #contract.reinsure(1.) # TODO percentage to floating point number - ## TODO: never write into other object's properties -> OBSOLETE - #contract.rein_ID = risk["identifier"] - #print("CREATING", risk["expiration"], contract.expiration, risk["contract"].expiration, risk["identifier"]) self.simulation.append_reinrisks(risk) counter += 1 else: break + + def get_cash(self): + return self.cash + + def logme(self): + self.log('cash', self.cash) + self.log('underwritten_contracts', self.underwritten_contracts) + self.log('operational', self.operational) + + #def zeros(self): + # return 0 + + def len_underwritten_contracts(self): + return len(self.underwritten_contracts) + + def get_operational(self): + return self.operational + + def get_underwritten_contracts(self): + return self.underwritten_contracts + + def get_pointer(self): + return self diff --git a/insurancesimulation.py b/insurancesimulation.py index 0f6019f..7a1490a 100644 --- a/insurancesimulation.py +++ b/insurancesimulation.py @@ -2,47 +2,35 @@ from insurancefirm import InsuranceFirm from riskmodel import RiskModel from reinsurancefirm import ReinsuranceFirm -#from reinriskmodel import ReinriskModel import numpy as np import scipy.stats import math import sys, pdb +import numba as nb +import isleconfig + +if isleconfig.use_abce: + import abce + #print("abce imported") +#else: +# print("abce not imported") + + class InsuranceSimulation(): - def __init__(self, replic_ID=None, override_no_riskmodels=False, simulation_parameters={"no_categories": 2, \ - "no_insurancefirms": 20, \ - "no_reinsurancefirms": 2, \ - "no_riskmodels": 2, \ - "norm_profit_markup": 0.15, \ - "rein_norm_profit_markup": 0.15, \ - "mean_contract_runtime": 30, \ - "contract_runtime_halfspread": 10, \ - "max_time": 600, \ - "money_supply": 2000000000, \ - "event_time_mean_separation": 100 / 3., \ - "expire_immediately": True, \ - "risk_factors_present": False, \ - "risk_factor_lower_bound": 0.4, \ - "risk_factor_upper_bound": 0.6, \ - "initial_acceptance_threshold": 0.5, \ - "acceptance_threshold_friction": 0.9, \ - "initial_agent_cash": 10000, \ - "initial_reinagent_cash": 50000, \ - "interest_rate": 0, \ - "reinsurance_limit": 0.1, \ - "upper_price_limit": 1.2, \ - "lower_price_limit": 0.85, \ - "no_risks": 20000}): - + def __init__(self, override_no_riskmodels, replic_ID, simulation_parameters): # override one-riskmodel case (this is to ensure all other parameters are truly identical for comparison runs) if override_no_riskmodels: simulation_parameters["no_riskmodels"] = override_no_riskmodels # save parameters - self.background_run = False if replic_ID is None else True + if (replic_ID is None) or (isleconfig.force_foreground): + self.background_run = False + else: + self.background_run = True self.replic_ID = replic_ID self.simulation_parameters = simulation_parameters - + # unpack parameters, set up environment (distributions etc.) self.damage_distribution = scipy.stats.uniform(loc=0, scale=1) self.cat_separation_distribution = scipy.stats.expon(0, simulation_parameters["event_time_mean_separation"]) @@ -53,11 +41,11 @@ def __init__(self, replic_ID=None, override_no_riskmodels=False, simulation_para self.risk_factor_distribution = scipy.stats.uniform(loc=1.0, scale=0) #self.risk_value_distribution = scipy.stats.uniform(loc=100, scale=9900) self.risk_value_distribution = scipy.stats.uniform(loc=1000, scale=0) - - risk_factor_mean = self.risk_factor_distribution.mean() + + risk_factor_mean = self.risk_factor_distribution.mean() if np.isnan(risk_factor_mean): # unfortunately scipy.stats.mean is not well-defined if scale = 0 - risk_factor_mean = self.risk_factor_distribution.rvs() - + risk_factor_mean = self.risk_factor_distribution.rvs() + # set initial market price (normalized, i.e. must be multiplied by value or excess-deductible) if self.simulation_parameters["expire_immediately"]: assert self.cat_separation_distribution.dist.name == "expon" @@ -73,27 +61,24 @@ def __init__(self, replic_ID=None, override_no_riskmodels=False, simulation_para self.market_premium = self.norm_premium self.total_no_risks = simulation_parameters["no_risks"] - #print(self.norm_premium) - #pdb.set_trace() - # set up monetary system (should instead be with the customers, if customers are modeled explicitly) self.money_supply = self.simulation_parameters["money_supply"] self.obligations = [] - + # set up risk categories self.riskcategories = list(range(self.simulation_parameters["no_categories"])) self.rc_event_schedule = [] self.setup_risk_categories_caller() - + # set up risks - risk_value_mean = self.risk_value_distribution.mean() + risk_value_mean = self.risk_value_distribution.mean() if np.isnan(risk_value_mean): # unfortunately scipy.stats.mean is not well-defined if scale = 0 - risk_value_mean = self.risk_value_distribution.rvs() + risk_value_mean = self.risk_value_distribution.rvs() rrisk_factors = self.risk_factor_distribution.rvs(size=self.simulation_parameters["no_risks"]) rvalues = self.risk_value_distribution.rvs(size=self.simulation_parameters["no_risks"]) rcategories = np.random.randint(0, self.simulation_parameters["no_categories"], size=self.simulation_parameters["no_risks"]) self.risks = [{"risk_factor": rrisk_factors[i], "value": rvalues[i], "category": rcategories[i], "owner": self} for i in range(self.simulation_parameters["no_risks"])] - + # set up risk models inaccuracy = [[(0.5 if (i+j)%2==0 else 2.) for i in range(self.simulation_parameters["no_categories"])] for j in range(self.simulation_parameters["no_riskmodels"])] self.riskmodels = [RiskModel(self.damage_distribution, self.simulation_parameters["expire_immediately"], \ @@ -102,192 +87,187 @@ def __init__(self, replic_ID=None, override_no_riskmodels=False, simulation_para self.simulation_parameters["norm_profit_markup"], inaccuracy[i]) \ for i in range(self.simulation_parameters["no_riskmodels"])] - # set up insurance firms - self.insurancefirms = [] - for i in range(self.simulation_parameters["no_insurancefirms"]): + # prepare setting up agents (to be done from start.py) + self.agent_parameters = {"insurancefirm": [], "reinsurance": []} # TODO: rename reinsurance -> reinsurancefirm (also in start.py and below in method accept_agents + + # TODO: collapse the following two loops into one generic one? + for i in range(simulation_parameters["no_insurancefirms"]): + riskmodel = self.riskmodels[i % len(self.riskmodels)] + self.agent_parameters["insurancefirm"].append({'id': i, 'initial_cash': simulation_parameters["initial_agent_cash"], + 'riskmodel': riskmodel, 'norm_premium': self.norm_premium, + 'profit_target': simulation_parameters["norm_profit_markup"], + 'initial_acceptance_threshold': simulation_parameters["initial_acceptance_threshold"], + 'acceptance_threshold_friction': simulation_parameters["acceptance_threshold_friction"], + 'reinsurance_limit': simulation_parameters["reinsurance_limit"], + 'interest_rate': simulation_parameters["interest_rate"]}) + for i in range(simulation_parameters["no_reinsurancefirms"]): riskmodel = self.riskmodels[i % len(self.riskmodels)] - #print(riskmodel) - agent_parameters = {'id': i, 'initial_cash': simulation_parameters["initial_agent_cash"], \ - 'riskmodel': riskmodel, 'norm_premium': self.norm_premium, \ - 'profit_target': simulation_parameters["norm_profit_markup"], \ - 'initial_acceptance_threshold': simulation_parameters["initial_acceptance_threshold"], \ - 'acceptance_threshold_friction': simulation_parameters["acceptance_threshold_friction"], \ - 'reinsurance_limit': simulation_parameters["reinsurance_limit"], \ - 'interest_rate': simulation_parameters["interest_rate"]} - insurer = InsuranceFirm(self, simulation_parameters, agent_parameters) - self.insurancefirms.append(insurer) - self.insurancefirm_weights = [1 for i in self.insurancefirms] - self.insurancefirm_new_weights = [0 for i in self.insurancefirms] - - # - ## set up reinsurance risk models - #self.reinriskmodels = [ReinriskModel(self.damage_distribution, self.simulation_parameters["expire_immediately"], \ - # self.cat_separation_distribution, self.norm_premium, - # self.simulation_parameters["no_categories"], \ - # risk_value_mean, \ - # self.simulation_parameters["rein_norm_profit_markup"]) \ - # for i in range(self.simulation_parameters["no_reinriskmodels"])] - # + self.agent_parameters["reinsurance"].append({'id': i, 'initial_cash': simulation_parameters["initial_reinagent_cash"], + 'riskmodel': riskmodel, 'norm_premium': self.norm_premium, + 'profit_target': simulation_parameters["norm_profit_markup"], + 'initial_acceptance_threshold': simulation_parameters["initial_acceptance_threshold"], + 'acceptance_threshold_friction': simulation_parameters["acceptance_threshold_friction"], + 'reinsurance_limit': simulation_parameters["reinsurance_limit"], + 'interest_rate': simulation_parameters["interest_rate"]}) + + # set up remaining list variables - # set up reinsurance firms + # agent lists self.reinsurancefirms = [] - for i in range(self.simulation_parameters["no_reinsurancefirms"]): - riskmodel = self.riskmodels[i % len(self.riskmodels)] - agent_parameters = {'id': i, 'initial_cash': simulation_parameters["initial_reinagent_cash"], \ - 'riskmodel': riskmodel, 'norm_premium': self.norm_premium, \ - 'profit_target': simulation_parameters["norm_profit_markup"], \ - 'initial_acceptance_threshold': simulation_parameters["initial_acceptance_threshold"], \ - 'acceptance_threshold_friction': simulation_parameters["acceptance_threshold_friction"], \ - 'reinsurance_limit': simulation_parameters["reinsurance_limit"], \ - 'interest_rate': simulation_parameters["interest_rate"]} - reinsurer = ReinsuranceFirm(self, simulation_parameters, agent_parameters) - self.reinsurancefirms.append(reinsurer) - self.reinsurancefirm_weights = [1 for i in self.reinsurancefirms] - self.reinsurancefirm_new_weights = [simulation_parameters["initial_reinagent_cash"] for i in self.reinsurancefirms] - - # some output variables + self.insurancefirms = [] + + # lists of agent weights + self.insurancefirm_weights = [] + self.insurancefirm_new_weights = [] + self.reinsurancefirm_weights = [] + self.reinsurancefirm_new_weights = [] + + # list of reinsurance risks offered for underwriting + self.reinrisks = [] + + # lists for logging history + + # sum insurance firms self.history_total_cash = [] self.history_total_contracts = [] - self.history_individual_contracts = [[] for _ in range(simulation_parameters["no_insurancefirms"])] self.history_total_operational = [] - + # individual insurance firms + self.history_individual_contracts = [[] for _ in range(simulation_parameters["no_insurancefirms"])] + + # sum reinsurance firms self.history_total_reincash = [] self.history_total_reincontracts = [] self.history_total_reinoperational = [] - - self.reinrisks = [] - - def run(self): - for t in range(self.simulation_parameters["max_time"]): - print() - print(t, ": ", len(self.risks)) - - # adjust market premiums - self.adjust_market_premium() - - # pay obligations - self.effect_payments(t) - # identify perils and effect claims - for categ_id in range(len(self.rc_event_schedule)): - try: - if len(self.rc_event_schedule[categ_id]) > 0: - assert self.rc_event_schedule[categ_id][0] >= t - except: - print("Something wrong; past events not deleted") - if len(self.rc_event_schedule[categ_id]) > 0 and self.rc_event_schedule[categ_id][0] == t: - self.rc_event_schedule[categ_id] = self.rc_event_schedule[categ_id][1:] - affected_contracts = [contract for insurer in self.insurancefirms for contract in insurer.underwritten_contracts if contract.category == categ_id] - no_affected = len(affected_contracts) - damage = self.damage_distribution.rvs() - print("**** PERIL ", damage) - damagevalues = np.random.beta(1, 1./damage -1, size=no_affected) - uniformvalues = np.random.uniform(0, 1, size=no_affected) - [contract.explode(self.simulation_parameters["expire_immediately"], t, uniformvalues[i], damagevalues[i]) for i, contract in enumerate(affected_contracts)] - else: - print("Next peril ", self.rc_event_schedule[categ_id]) - - - # reset reinweights - self.reinsurancefirm_weights = np.asarray(self.reinsurancefirm_new_weights) / sum( - self.reinsurancefirm_new_weights) * len(self.reinrisks) - self.reinsurancefirm_weights = np.int64(np.floor(self.reinsurancefirm_weights)) - self.reinsurancefirm_new_weights = [0 for i in self.reinsurancefirms] - np.random.shuffle(self.reinrisks) - - # iterate reinagents - for reinagent in self.reinsurancefirms: - reinagent.iterate(t) - # remove all non-accepted reinsurance risks - self.reinrisks = [] - - # reset weights - self.insurancefirm_weights = np.asarray(self.insurancefirm_new_weights) / sum(self.insurancefirm_new_weights) * len(self.risks) - self.insurancefirm_weights = np.int64(np.floor(self.insurancefirm_weights)) - self.insurancefirm_new_weights = [0 for i in self.insurancefirms] - np.random.shuffle(self.risks) - - # iterate agents - for agent in self.insurancefirms: - agent.iterate(t) - - # collect data - total_cash_no = sum([insurancefirm.cash for insurancefirm in self.insurancefirms]) - total_contracts_no = sum([len(insurancefirm.underwritten_contracts) for insurancefirm in self.insurancefirms]) - total_reincash_no = sum([reinsurancefirm.cash for reinsurancefirm in self.reinsurancefirms]) - total_reincontracts_no = sum([len(reinsurancefirm.underwritten_contracts) for reinsurancefirm in self.reinsurancefirms]) - operational_no = sum([insurancefirm.operational for insurancefirm in self.insurancefirms]) - reinoperational_no = sum([reinsurancefirm.operational for reinsurancefirm in self.reinsurancefirms]) - self.history_total_cash.append(total_cash_no) - self.history_total_contracts.append(total_contracts_no) - self.history_total_operational.append(operational_no) - self.history_total_reincash.append(total_reincash_no) - self.history_total_reincontracts.append(total_reincontracts_no) - self.history_total_reinoperational.append(reinoperational_no) - - individual_contracts_no = [len(insurancefirm.underwritten_contracts) for insurancefirm in self.insurancefirms] - for i in range(len(individual_contracts_no)): - self.history_individual_contracts[i].append(individual_contracts_no[i]) - - self.log() - def log(self): - if self.background_run: - self.replication_log() + def build_agents(self, agent_class, agent_class_string, parameters, agent_parameters): + assert agent_parameters == self.agent_parameters[agent_class_string] + agents = [] + for ap in agent_parameters: + agents.append(agent_class(parameters, ap)) + return agents + + def accept_agents(self, agent_class_string, agents, agent_group): + if agent_class_string == "insurancefirm": + try: + self.insurancefirms += agents + self.insurancefirm_weights += [1 for i in agents] + self.insurancefirm_new_weights += [agent.cash for agent in agents] + self.insurancefirms_group = agent_group + except: + print(sys.exc_info()) + pdb.set_trace() + elif agent_class_string == "reinsurance": + try: + self.reinsurancefirms += agents + self.reinsurancefirm_weights += [1 for i in agents] + self.reinsurancefirm_new_weights += [agent.cash for agent in agents] + self.reinsurancefirms_group = agent_group + except: + print(sys.exc_info()) + pdb.set_trace() else: - self.single_log() + assert False, "Error: Unexpected agent class used" - def replication_log(self): - wfile = open("data/two_operational.dat","a") - wfile.write(str(self.history_total_operational)+"\n") - wfile.close() - wfile = open("data/two_contracts.dat","a") - wfile.write(str(self.history_total_contracts)+"\n") - wfile.close() - wfile = open("data/two_cash.dat","a") - wfile.write(str(self.history_total_cash)+"\n") - wfile.close() - def single_log(self): - wfile = open("data/operational.dat","w") - wfile.write(str(self.history_total_operational)+"\n") - wfile.close() - wfile = open("data/contracts.dat","w") - wfile.write(str(self.history_total_contracts)+"\n") - wfile.close() - wfile = open("data/cash.dat","w") - wfile.write(str(self.history_total_cash)+"\n") - wfile.close() - wfile = open("data/reinoperational.dat","w") - wfile.write(str(self.history_total_reinoperational)+"\n") - wfile.close() - wfile = open("data/reincontracts.dat","w") - wfile.write(str(self.history_total_reincontracts)+"\n") - wfile.close() - wfile = open("data/reincash.dat","w") - wfile.write(str(self.history_total_reincash)+"\n") - wfile.close() + def iterate(self, t): + print() + print(t, ": ", len(self.risks)) + + # adjust market premiums + sum_capital = sum([agent.get_cash() for agent in self.insurancefirms]) + self.adjust_market_premium(capital=sum_capital) + + # pay obligations + self.effect_payments(t) + + # identify perils and effect claims + for categ_id in range(len(self.rc_event_schedule)): + try: + if len(self.rc_event_schedule[categ_id]) > 0: + assert self.rc_event_schedule[categ_id][0] >= t + except: + print("Something wrong; past events not deleted") + if len(self.rc_event_schedule[categ_id]) > 0 and self.rc_event_schedule[categ_id][0] == t: + self.rc_event_schedule[categ_id] = self.rc_event_schedule[categ_id][1:] + + self.inflict_peril(categ_id=categ_id, t=t)# TODO: consider splitting the following lines from this method and running it with nb.jit + else: + print("Next peril ", self.rc_event_schedule[categ_id]) + + # shuffle risks (insurance and reinsurance risks) + self.shuffle_risks() + + # reset reinweights + self.reset_reinsurance_weights() + + # iterate reinsurnace firm agents + for reinagent in self.reinsurancefirms: + reinagent.iterate(t) + # TODO: is the following necessary for abce to work (log) properly? + #if isleconfig.use_abce: + # self.reinsurancefirms_group.iterate(time=t) + #else: + # for reinagent in self.reinsurancefirms: + # reinagent.iterate(t) + + # remove all non-accepted reinsurance risks + self.reinrisks = [] + + # reset weights + self.reset_insurance_weights() + + # iterate insurance firm agents + for agent in self.insurancefirms: + agent.iterate(t) + # TODO: is the following necessary for abce to work (log) properly? + #if isleconfig.use_abce: + # self.insurancefirms_group.iterate(time=t) + #else: + # for agent in self.insurancefirms: + # agent.iterate(t) + + def save_data(self): + # collect data + total_cash_no = sum([insurancefirm.cash for insurancefirm in self.insurancefirms]) + total_contracts_no = sum([len(insurancefirm.underwritten_contracts) for insurancefirm in self.insurancefirms]) + total_reincash_no = sum([reinsurancefirm.cash for reinsurancefirm in self.reinsurancefirms]) + total_reincontracts_no = sum([len(reinsurancefirm.underwritten_contracts) for reinsurancefirm in self.reinsurancefirms]) + operational_no = sum([insurancefirm.operational for insurancefirm in self.insurancefirms]) + reinoperational_no = sum([reinsurancefirm.operational for reinsurancefirm in self.reinsurancefirms]) + self.history_total_cash.append(total_cash_no) + self.history_total_contracts.append(total_contracts_no) + self.history_total_operational.append(operational_no) + self.history_total_reincash.append(total_reincash_no) + self.history_total_reincontracts.append(total_reincontracts_no) + self.history_total_reinoperational.append(reinoperational_no) - #fig = plt.figure() - #ax0 = fig.add_subplot(311) - #ax0.plot(range(len(self.history_total_cash)), self.history_total_cash) - #ax0.set_ylabel("Cash") - #ax1 = fig.add_subplot(312) - #ax1.plot(range(len(self.history_total_contracts)), self.history_total_contracts) - #ax1.set_ylabel("Contracts") - #ax2 = fig.add_subplot(313) - #for i in range(len(self.history_individual_contracts)): - # ax2.plot(range(len(self.history_individual_contracts[i])), self.history_individual_contracts[i]) - #ax2.set_ylabel("Contracts") - #ax2.set_xlabel("Time") - #plt.show() + individual_contracts_no = [len(insurancefirm.underwritten_contracts) for insurancefirm in self.insurancefirms] + for i in range(len(individual_contracts_no)): + self.history_individual_contracts[i].append(individual_contracts_no[i]) + + def advance_round(self, *args): + pass + + def finalize(self, *args): + self.log() + pass + def inflict_peril(self, categ_id, t): + affected_contracts = [contract for insurer in self.insurancefirms for contract in insurer.underwritten_contracts if contract.category == categ_id] + no_affected = len(affected_contracts) + damage = self.damage_distribution.rvs() + print("**** PERIL ", damage) + damagevalues = np.random.beta(1, 1./damage -1, size=no_affected) + uniformvalues = np.random.uniform(0, 1, size=no_affected) + [contract.explode(self.simulation_parameters["expire_immediately"], t, uniformvalues[i], damagevalues[i]) for i, contract in enumerate(affected_contracts)] + def receive_obligation(self, amount, recipient, due_time): obligation = {"amount": amount, "recipient": recipient, "due_time": due_time} self.obligations.append(obligation) - + def effect_payments(self, time): - due = [item for item in self.obligations if item["due_time"]<=time] + due = [item for item in self.obligations if item["due_time"]<=time] #print("SIMULATION obligations: ", len(self.obligations), " of which are due: ", len(due)) self.obligations = [item for item in self.obligations if item["due_time"]>time] sum_due = sum([item["amount"] for item in due]) @@ -310,8 +290,33 @@ def receive(self, amount): """Method to accept cash payments.""" self.money_supply += amount - def adjust_market_premium(self): - capital = sum([firm.cash for firm in self.insurancefirms]) + @nb.jit + def reset_reinsurance_weights(self): + self.reinsurancefirm_weights = np.asarray(self.reinsurancefirm_new_weights) / \ + sum(self.reinsurancefirm_new_weights) * len(self.reinrisks) + self.reinsurancefirm_weights = np.int64(np.floor(self.reinsurancefirm_weights)) + #self.reinsurancefirm_new_weights = [0 for i in self.reinsurancefirms] + #reinsurancefirm_new_weights2 = [0 for i in self.reinsurancefirms] + self.reinsurancefirm_new_weights = list(np.zeros(len(self.reinsurancefirms))) + #assert self.reinsurancefirm_new_weights == reinsurancefirm_new_weights2 + + #self.reinsurancefirm_new_weights = self.reinsurancefirms.zeros() + + @nb.jit + def reset_insurance_weights(self): + self.insurancefirm_weights = np.asarray(self.insurancefirm_new_weights) / \ + sum(self.insurancefirm_new_weights) * len(self.risks) + self.insurancefirm_weights = np.int64(np.floor(self.insurancefirm_weights)) + #self.insurancefirm_new_weights = [0 for i in self.insurancefirms] + self.insurancefirm_new_weights = list(np.zeros(len(self.insurancefirms))) + print('@', self.insurancefirm_weights) + + @nb.jit + def shuffle_risks(self): + np.random.shuffle(self.reinrisks) + np.random.shuffle(self.risks) + + def adjust_market_premium(self, capital): self.market_premium = self.norm_premium * (self.simulation_parameters["upper_price_limit"] - capital / (self.norm_premium * self.simulation_parameters["no_risks"])) if self.market_premium < self.norm_premium * self.simulation_parameters["lower_price_limit"]: self.market_premium = self.norm_premium * self.simulation_parameters["lower_price_limit"] @@ -333,14 +338,11 @@ def get_reinrisks(self): def solicit_insurance_requests(self, id, cash): self.insurancefirm_new_weights[id] = cash - risks_to_be_sent = self.risks[:self.insurancefirm_weights[id]] - self.risks = self.risks[self.insurancefirm_weights[id]:] + risks_to_be_sent = self.risks[:int(self.insurancefirm_weights[id])] + self.risks = self.risks[int(self.insurancefirm_weights[id]):] print("Number of risks", len(risks_to_be_sent)) return risks_to_be_sent - def return_risks(self, not_accepted_risks): - self.risks += not_accepted_risks - def solicit_reinsurance_requests(self, id, cash): self.reinsurancefirm_new_weights[id] = cash reinrisks_to_be_sent = self.reinrisks[:self.reinsurancefirm_weights[id]] @@ -348,6 +350,9 @@ def solicit_reinsurance_requests(self, id, cash): print("Number of risks",len(reinrisks_to_be_sent)) return reinrisks_to_be_sent + def return_risks(self, not_accepted_risks): + self.risks += not_accepted_risks + def return_reinrisks(self, not_accepted_risks): self.reinrisks += not_accepted_risks @@ -357,27 +362,102 @@ def setup_risk_categories(self): total = 0 while (total < self.simulation_parameters["max_time"]): separation_time = self.cat_separation_distribution.rvs() - total += separation_time + total += int(math.ceil(separation_time)) if total < self.simulation_parameters["max_time"]: - event_schedule.append(int(math.ceil(total))) + event_schedule.append(total) self.rc_event_schedule.append(event_schedule) def setup_risk_categories_caller(self): - if self.background_run: + #if self.background_run: + if self.replic_ID is not None: + if isleconfig.replicating: + self.restore_state_and_risk_categories() + else: + self.setup_risk_categories() + self.save_state_and_risk_categories() + else: self.setup_risk_categories() - - wfile = open("data/rc_event_schedule.dat","a") - wfile.write(str(self.rc_event_schedule)+"\n") - wfile.close() - + + def save_state_and_risk_categories(self): + # save numpy Mersenne Twister state + mersennetwoster_randomseed = str(np.random.get_state()) + mersennetwoster_randomseed = mersennetwoster_randomseed.replace("\n","").replace("array", "np.array").replace("uint32", "np.uint32") + wfile = open("data/replication_randomseed.dat","a") + wfile.write(mersennetwoster_randomseed+"\n") + wfile.close() + # save event schedule + wfile = open("data/replication_rc_event_schedule.dat","a") + wfile.write(str(self.rc_event_schedule)+"\n") + wfile.close() + + def restore_state_and_risk_categories(self): + rfile = open("data/replication_rc_event_schedule.dat","r") + found = False + for i, line in enumerate(rfile): + #print(i, self.replic_ID) + if i == self.replic_ID: + self.rc_event_schedule = eval(line) + found = True + rfile.close() + assert found, "rc event schedule for current replication ID number {0:d} not found in data file. Exiting.".format(self.replic_ID) + rfile = open("data/replication_randomseed.dat","r") + found = False + for i, line in enumerate(rfile): + #print(i, self.replic_ID) + if i == self.replic_ID: + mersennetwister_randomseed = eval(line) + found = True + rfile.close() + np.random.set_state(mersennetwister_randomseed) + assert found, "mersennetwister randomseed for current replication ID number {0:d} not found in data file. Exiting.".format(self.replic_ID) + + def log(self): + if self.background_run: + if isleconfig.oneriskmodel: + to_log = self.replication_log_prepare_oneriskmodel() + else: + to_log = self.replication_log_prepare() else: - self.setup_risk_categories() + to_log = self.single_log_prepare() + + for filename, data, operation_character in to_log: + wfile = open(filename, operation_character) + wfile.write(str(data) + "\n") + wfile.close() + def replication_log_prepare(self): + to_log = [] + to_log.append(("data/two_operational.dat", self.history_total_operational, "a")) + to_log.append(("data/two_contracts.dat", self.history_total_contracts, "a")) + to_log.append(("data/two_cash.dat", self.history_total_cash, "a")) + to_log.append(("data/two_reinoperational.dat", self.history_total_reinoperational, "a")) + to_log.append(("data/two_reincontracts.dat", self.history_total_reincontracts, "a")) + to_log.append(("data/two_reincash.dat", self.history_total_reincash, "a")) + return to_log + + def replication_log_prepare_oneriskmodel(self): + to_log = [] + to_log.append(("data/one_operational.dat", self.history_total_operational, "a")) + to_log.append(("data/one_contracts.dat", self.history_total_contracts, "a")) + to_log.append(("data/one_cash.dat", self.history_total_cash, "a")) + to_log.append(("data/one_reinoperational.dat", self.history_total_reinoperational, "a")) + to_log.append(("data/one_reincontracts.dat", self.history_total_reincontracts, "a")) + to_log.append(("data/one_reincash.dat", self.history_total_reincash, "a")) + return to_log + + def single_log_prepare(self): + to_log = [] + to_log.append(("data/operational.dat", self.history_total_operational, "w")) + to_log.append(("data/contracts.dat", self.history_total_contracts, "w")) + to_log.append(("data/cash.dat", self.history_total_cash, "w")) + to_log.append(("data/reinoperational.dat", self.history_total_reinoperational, "w")) + to_log.append(("data/reincontracts.dat", self.history_total_reincontracts, "w")) + to_log.append(("data/reincash.dat", self.history_total_reincash, "w")) + return to_log -# main entry point -if __name__ == "__main__": - arg = None - if len(sys.argv) > 1: - arg = int(sys.argv[1]) - S = InsuranceSimulation(replic_ID = arg) - S.run() +#if __name__ == "__main__": +# arg = None +# if len(sys.argv) > 1: +# arg = int(sys.argv[1]) +# S = InsuranceSimulation(replic_ID = arg) +# S.run() diff --git a/insurancesimulation_one.py b/insurancesimulation_one.py deleted file mode 100644 index 8c7c2d9..0000000 --- a/insurancesimulation_one.py +++ /dev/null @@ -1,38 +0,0 @@ - -from insurancesimulation import InsuranceSimulation -import pdb, sys - -class InsuranceSimulation_One(InsuranceSimulation): - - def replication_log(self): - wfile = open("data/one_operational.dat","a") - wfile.write(str(self.history_total_operational)+"\n") - wfile.close() - wfile = open("data/one_contracts.dat","a") - wfile.write(str(self.history_total_contracts)+"\n") - wfile.close() - wfile = open("data/one_cash.dat","a") - wfile.write(str(self.history_total_cash)+"\n") - wfile.close() - - def setup_risk_categories_caller(self): - if self.background_run: - rfile = open("data/rc_event_schedule.dat","r") - found = False - for i, line in enumerate(rfile): - print(i, self.replic_ID) - if i == self.replic_ID: - self.rc_event_schedule = eval(line) - found = True - rfile.close() - assert found, "rc event schedule for current replication ID number {0:d} not found in data file. Exiting.".format(self.replic_ID) - else: - self.setup_risk_categories() - -# main entry point -if __name__ == "__main__": - arg = None - if len(sys.argv) > 1: - arg = int(sys.argv[1]) - S = InsuranceSimulation_One(replic_ID = arg, override_no_riskmodels = 1) - S.run() diff --git a/isleconfig.py b/isleconfig.py new file mode 100644 index 0000000..0f0a476 --- /dev/null +++ b/isleconfig.py @@ -0,0 +1,32 @@ +use_abce = False +oneriskmodel = False +replicating = False +force_foreground = False + +simulation_parameters={"no_categories": 2, + "no_insurancefirms": 20, + "no_reinsurancefirms": 2, + "no_riskmodels": 2, + "norm_profit_markup": 0.15, + "rein_norm_profit_markup": 0.15, + "mean_contract_runtime": 36, + "contract_runtime_halfspread": 10, + "default_contract_payment_period": 12, + "max_time": 600, + "money_supply": 2000000000, + "event_time_mean_separation": 100 / 3., + "expire_immediately": False, + "risk_factors_present": False, + "risk_factor_lower_bound": 0.4, + "risk_factor_upper_bound": 0.6, + "initial_acceptance_threshold": 0.5, + "acceptance_threshold_friction": 0.9, + "initial_agent_cash": 10000, + "initial_reinagent_cash": 50000, + "interest_rate": 0, + "reinsurance_limit": 0.1, + "upper_price_limit": 1.2, + "lower_price_limit": 0.85, + "no_risks": 20000} + + diff --git a/reinsurancefirm.py b/reinsurancefirm.py index 2734971..3379019 100644 --- a/reinsurancefirm.py +++ b/reinsurancefirm.py @@ -4,12 +4,12 @@ class ReinsuranceFirm(InsuranceFirm): """ReinsuranceFirm class. Inherits from InsuranceFirm.""" - def __init__(self, simulation, simulation_parameters, agent_parameters): + def init(self, simulation_parameters, agent_parameters): """Constructor method. Accepts arguments Signature is identical to constructor method of parent class. Constructor calls parent constructor and only overwrites boolean indicators of insurer and reinsurer role of the object.""" - super(ReinsuranceFirm, self).__init__(simulation, simulation_parameters, agent_parameters) + super(ReinsuranceFirm, self).init(simulation_parameters, agent_parameters) self.is_insurer = False self.is_reinsurer = True diff --git a/riskmodel.py b/riskmodel.py index c2a9f24..711e0d4 100644 --- a/riskmodel.py +++ b/riskmodel.py @@ -3,6 +3,7 @@ import numpy as np import sys, pdb import scipy.stats +import numba as nb class RiskModel(): def __init__(self, damage_distribution, expire_immediately, cat_separation_distribution, norm_premium, \ @@ -27,6 +28,52 @@ def getPPF(self, tailSize): tailSize (float >=0, <=1): quantile Returns value-at-risk.""" return self.damage_distribution.ppf(1-tailSize) + + @nb.jit + def get_categ_risks(self, risks, categ_id): + #categ_risks2 = [risk for risk in risks if risk["category"]==categ_id] + categ_risks = [] + for risk in risks: + if risk["category"]==categ_id: + categ_risks.append(risk) + #assert categ_risks == categ_risks2 + return categ_risks + + @nb.jit + def compute_expectation(self, categ_risks, categ_id): #TODO: more intuitive name? + #average_exposure2 = np.mean([risk["excess"]-risk["deductible"] for risk in categ_risks]) + # + ##average_risk_factor = np.mean([risk["risk_factor"] for risk in categ_risks]) + #average_risk_factor2 = self.inaccuracy[categ_id] * np.mean([risk["risk_factor"] for risk in categ_risks]) + # + ## compute expected profits from category + #mean_runtime2 = np.mean([risk["runtime"] for risk in categ_risks]) + + exposures = [] + risk_factors = [] + runtimes = [] + for risk in categ_risks: + exposures.append(risk["excess"]-risk["deductible"]) + risk_factors.append(risk["risk_factor"]) + runtimes.append(risk["runtime"]) + average_exposure = np.mean(exposures) + average_risk_factor = self.inaccuracy[categ_id] * np.mean(risk_factors) + mean_runtime = np.mean(runtimes) + #assert average_exposure == average_exposure2 + #assert average_risk_factor == average_risk_factor2 + #assert mean_runtime == mean_runtime2 + + if self.expire_immediately: + incr_expected_profits = -1 + # TODO: fix the norm_premium estimation + #incr_expected_profits = (self.norm_premium - (1 - scipy.stats.poisson(1 / self.cat_separation_distribution.mean() * \ + # mean_runtime).pmf(0)) * self.damage_distribution.mean() * average_risk_factor) * average_exposure * len(categ_risks) + else: + incr_expected_profits = -1 + # TODO: expected profits should only be returned once the expire_immediately == False case is fixed + #incr_expected_profits = (self.norm_premium - mean_runtime / self.cat_separation_distribution.mean() * self.damage_distribution.mean() * average_risk_factor) * average_exposure * len(categ_risks) + + return average_risk_factor, average_exposure, incr_expected_profits def evaluate(self, risks, cash): acceptable_by_category = [] @@ -35,26 +82,21 @@ def evaluate(self, risks, cash): necessary_liquidity = 0 for categ_id in range(self.category_number): # compute number of acceptable risks of this category - categ_risks = [risk for risk in risks if risk["category"]==categ_id] + + categ_risks = self.get_categ_risks(risks=risks, categ_id=categ_id) + #categ_risks = [risk for risk in risks if risk["category"]==categ_id] + if len(categ_risks) > 0: - average_exposure = np.mean([risk["excess"]-risk["deductible"] for risk in categ_risks]) - #average_risk_factor = np.mean([risk["risk_factor"] for risk in categ_risks]) - try: - average_risk_factor = self.inaccuracy[categ_id] * np.mean([risk["risk_factor"] for risk in categ_risks]) - except: - print(sys.exc_info()) - pdb.set_trace() - # compute expected profits from category - mean_runtime = np.mean([risk["runtime"] for risk in categ_risks]) - if self.expire_immediately: - expected_profits += (self.norm_premium - (1 - scipy.stats.poisson(1 / self.cat_separation_distribution.mean() * \ - mean_runtime).pmf(0)) * self.damage_distribution.mean() * average_risk_factor) * average_exposure * len(categ_risks) - else: - expected_profits += (self.norm_premium - mean_runtime / self.cat_separation_distribution.mean() * self.damage_distribution.mean() * average_risk_factor) * average_exposure * len(categ_risks) + average_risk_factor, average_exposure, incr_expected_profits = self.compute_expectation(categ_risks=categ_risks, categ_id=categ_id) else: average_risk_factor = self.init_average_risk_factor average_exposure = self.init_average_exposure - expected_profits += 0 + + incr_expected_profits = -1 + # TODO: expected profits should only be returned once the expire_immediately == False case is fixed + #incr_expected_profits = 0 + + expected_profits += incr_expected_profits var_per_risk = self.getPPF(self.var_tail_prob) * average_risk_factor * average_exposure necessary_liquidity += var_per_risk * len(categ_risks) #print("RISKMODEL: ", self.getPPF(0.01) * average_risk_factor * average_exposure, " = PPF(0.01) * ", average_risk_factor, " * ", average_exposure, " vs. cash: ", cash, "TOTAL_RISK_IN_CATEG: ", self.getPPF(0.01) * average_risk_factor * average_exposure * len(categ_risks)) @@ -72,10 +114,16 @@ def evaluate(self, risks, cash): pdb.set_trace() acceptable_by_category.append(acceptable) remaining_acceptable_by_category.append(remaining) - if necessary_liquidity == 0: - assert expected_profits == 0 - expected_profits = self.init_profit_estimate * cash + + # TODO: expected profits should only be returned once the expire_immediately == False case is fixed; the else-clause conditional statement should then be raised to unconditional + if expected_profits < 0: + expected_profits = None else: - expected_profits / necessary_liquidity + if necessary_liquidity == 0: + assert expected_profits == 0 + expected_profits = self.init_profit_estimate * cash + else: + expected_profits /= necessary_liquidity + print("RISKMODEL returns: ", expected_profits, remaining_acceptable_by_category) return expected_profits, remaining_acceptable_by_category diff --git a/start.py b/start.py new file mode 100644 index 0000000..fa7a26f --- /dev/null +++ b/start.py @@ -0,0 +1,134 @@ +# import common packages +import numpy as np +import scipy.stats +import math +import sys, pdb +import numba as nb + +# import config file and apply configuration +import isleconfig + +simulation_parameters = isleconfig.simulation_parameters +replic_ID = None +override_no_riskmodels = False + +# handle command line arguments +if (len(sys.argv) > 1): + if "--abce" in sys.argv: + # if command line argument --abce is given, override use_abce from config file + argument_idx = sys.argv.index("--abce") + assert len(sys.argv) > argument_idx + 1, "Error: No argument given for keyword --abce" + isleconfig.use_abce = True if int(sys.argv[argument_idx + 1]) == 1 else False + if "--oneriskmodel" in sys.argv: + # allow overriding the number of riskmodels from standard config + isleconfig.oneriskmodel = True + override_no_riskmodels = True + if "--replicid" in sys.argv: + # if replication ID is given, pass this to the simulation so that the risk profile can be restored + argument_idx = sys.argv.index("--replicid") + assert len(sys.argv) > argument_idx + 1, "Error: No argument given for keyword --replicid" + replic_ID = int(sys.argv[argument_idx + 1]) + if "--replicating" in sys.argv: + # if this is a simulation run designed to replicate another, override the config filr parameter + isleconfig.replicating = True + assert replic_ID is not None, "Error: Replication requires a replication ID to identify run to be replicated" + if "--randomseed" in sys.argv: + # allow setting of numpy random seed + argument_idx = sys.argv.index("--randomseed") + assert len(sys.argv) > argument_idx + 1, "Error: No argument given for keyword --randomseed" + randomseed = float(sys.argv[argument_idx + 1]) + np.random.seed(randomseed) + if "--foreground" in sys.argv: + # force foreground runs even if replication ID is given (which defaults to background runs) + isleconfig.force_foreground = True + +# import isle and abce modules +if isleconfig.use_abce: + #print("Importing abce") + import abce + from abce import gui + +from insurancesimulation import InsuranceSimulation +from insurancefirm import InsuranceFirm +from riskmodel import RiskModel +from reinsurancefirm import ReinsuranceFirm + +# create conditional decorator +def conditionally(decorator_function, condition): + def wrapper(target_function): + if not condition: + return target_function + return decorator_function(target_function) + return wrapper + +# create non-abce placeholder gui decorator +# TODO: replace this with more elegant solution if possible. Currently required since script will otherwise crash at the conditional decorator below since gui is then undefined +if not isleconfig.use_abce: + def gui(*args, **kwargs): + pass + + +# main function + +#@gui(simulation_parameters, serve=True) +@conditionally(gui(simulation_parameters, serve=False), isleconfig.use_abce) +def main(simulation_parameters): + + # create simulation and world objects (identical in non-abce mode) + if isleconfig.use_abce: + simulation = abce.Simulation(processes=1) + + simulation_parameters['simulation'] = world = InsuranceSimulation(override_no_riskmodels, replic_ID, simulation_parameters) + + if not isleconfig.use_abce: + simulation = world + + # create agents: insurance firms + insurancefirms_group = simulation.build_agents(InsuranceFirm, + 'insurancefirm', + parameters=simulation_parameters, + agent_parameters=world.agent_parameters["insurancefirm"]) + + if isleconfig.use_abce: + insurancefirm_pointers = insurancefirms_group.get_pointer() + else: + insurancefirm_pointers = insurancefirms_group + world.accept_agents("insurancefirm", insurancefirm_pointers, insurancefirms_group) + + # create agents: reinsurance firms + reinsurancefirms_group = simulation.build_agents(ReinsuranceFirm, + 'reinsurance', + parameters=simulation_parameters, + agent_parameters=world.agent_parameters["reinsurance"]) + if isleconfig.use_abce: + reinsurancefirm_pointers = reinsurancefirms_group.get_pointer() + else: + reinsurancefirm_pointers = reinsurancefirms_group + world.accept_agents("reinsurance", reinsurancefirm_pointers, reinsurancefirms_group) + + # time iteration + for t in range(simulation_parameters["max_time"]): + + # abce time step + simulation.advance_round(t) + + # iterate simulation + world.iterate(t) + + # log data + if isleconfig.use_abce: + #insurancefirms.logme() + #reinsurancefirms.logme() + insurancefirms_group.agg_log(variables=['cash', 'operational'], len=['underwritten_contracts']) + #reinsurancefirms_group.agg_log(variables=['cash']) + else: + world.save_data() + + #print("here") + + # finish simulation, write logs + simulation.finalize() + +# main entry point +if __name__ == "__main__": + main(simulation_parameters) diff --git a/starter_one.sh b/starter_one.sh index 5f6c913..fb5e6e8 100644 --- a/starter_one.sh +++ b/starter_one.sh @@ -1,8 +1,12 @@ mv data/one_operational.dat data/one_operational.dat_$(date +%Y_%h_%d_%H_%M) mv data/one_contracts.dat data/one_contracts.dat_$(date +%Y_%h_%d_%H_%M) mv data/one_cash.dat data/one_cash.dat_$(date +%Y_%h_%d_%H_%M) +mv data/one_reinoperational.dat data/one_reinoperational.dat_$(date +%Y_%h_%d_%H_%M) +mv data/one_reincontracts.dat data/one_reincontracts.dat_$(date +%Y_%h_%d_%H_%M) +mv data/one_reincash.dat data/one_reincash.dat_$(date +%Y_%h_%d_%H_%M) -for ((i=0; i<300; i++)) do - python insurancesimulation_one.py $i +for ((i=0; i<3; i++)) do + #python insurancesimulation_one.py $i + python start.py --abce 0 --replicid $i --replicating --oneriskmodel done diff --git a/starter_two.sh b/starter_two.sh index 6c91099..0e3248f 100644 --- a/starter_two.sh +++ b/starter_two.sh @@ -1,8 +1,13 @@ -mv data/rc_event_schedule.dat data/rc_event_schedule.dat_$(date +%Y_%h_%d_%H_%M) +mv data/replication_rc_event_schedule.dat data/replication_rc_event_schedule.dat_$(date +%Y_%h_%d_%H_%M) +mv data/replication_randomseed.dat data/replication_randomseed.dat_$(date +%Y_%h_%d_%H_%M) mv data/two_operational.dat data/two_operational.dat_$(date +%Y_%h_%d_%H_%M) mv data/two_contracts.dat data/two_contracts.dat_$(date +%Y_%h_%d_%H_%M) mv data/two_cash.dat data/two_cash.dat_$(date +%Y_%h_%d_%H_%M) +mv data/two_reinoperational.dat data/two_reinoperational.dat_$(date +%Y_%h_%d_%H_%M) +mv data/two_reincontracts.dat data/two_reincontracts.dat_$(date +%Y_%h_%d_%H_%M) +mv data/two_reincash.dat data/two_reincash.dat_$(date +%Y_%h_%d_%H_%M) -for ((i=0; i<300; i++)) do - python insurancesimulation.py $i +for ((i=0; i<3; i++)) do + #python insurancesimulation.py $i + python start.py --abce 0 --replicid $i done