Source code for climapan_lab.src.banks.Bank

import copy
from collections import OrderedDict

import ambr as am
import numpy as np
import numpy.random as random
import pandas as pd


[docs] class Bank(am.Agent): """A bank agent"""
[docs] def setup(self): self.loans = 0 self.numberOfUnemployed = 0 self.numberOfEmployed = 0 self.bankDataWriter = [] self.iL = 0 self.iD = 0.0001 self.iCB = self.p.bankICB self.profit = 0 self.actualSuppliedLoan = 0 self.totalLoanSupply = 0 self.totalLoanDemands = 0 self.deposits = 0 self.equities = 0 self.DTE = 0 self.B_cb = self.p.bankResInit self.reserves = self.p.bankResInit self.equity = 0 self.NPL = 0 self.R_cb = self.p.bankResInit self.F_cb = 0 self.F_cb_i = 0 self.Z_B = 0
[docs] def agentAssign(self): # short out agents without loan_demand > 0 # updateConsumerList = self.model.consumer_agents.select(self.model.consumer_agents.newCreditAsked > 0) updateCSFList = self.model.csfirm_agents.select( self.model.csfirm_agents.loan_demand > 0 ) updateCPList = self.model.cpfirm_agents.select( self.model.cpfirm_agents.loan_demand > 0 ) # print("DEBUG: updateCSFList.defaultProb shape:", updateCSFList.defaultProb.shape) # print("DEBUG: updateCPList.defaultProb shape:", updateCPList.defaultProb.shape) # print("DEBUG: greenEFirm.defaultProb shape:", self.model.greenEFirm.defaultProb.shape) # print("DEBUG: brownEFirm.defaultProb shape:", self.model.brownEFirm.defaultProb.shape) try: self.orderedAgentsInterestsRaw = np.argsort( np.concatenate( [ updateCSFList.defaultProb, updateCPList.defaultProb, self.model.greenEFirm.defaultProb, self.model.brownEFirm.defaultProb, ] ) ) except ValueError as e: print(f"Error concatenating defaultProb: {e}") print( f"updateCSFList.defaultProb: {updateCSFList.defaultProb}, type: {type(updateCSFList.defaultProb)}, shape: {getattr(updateCSFList.defaultProb, 'shape', 'N/A')}" ) print( f"updateCPList.defaultProb: {updateCPList.defaultProb}, type: {type(updateCPList.defaultProb)}, shape: {getattr(updateCPList.defaultProb, 'shape', 'N/A')}" ) print( f"greenEFirm.defaultProb: {self.model.greenEFirm.defaultProb}, type: {type(self.model.greenEFirm.defaultProb)}, shape: {getattr(self.model.greenEFirm.defaultProb, 'shape', 'N/A')}" ) print( f"brownEFirm.defaultProb: {self.model.brownEFirm.defaultProb}, type: {type(self.model.brownEFirm.defaultProb)}, shape: {getattr(self.model.brownEFirm.defaultProb, 'shape', 'N/A')}" ) raise e self.orderedAgentsInterests = np.concatenate( [ updateCSFList.id, updateCPList.id, self.model.greenEFirm.id, self.model.brownEFirm.id, ] )[self.orderedAgentsInterestsRaw]
def _calculate_consumer_networth(self, agent): """ This internal function of the Bank class is used to represent the demands between consumer agents and the bank --- Args: agent: The target consumer agent --- Returns: """ if agent.getCovidStateAttr("state") != "dead": self.deposits += np.sum(agent.wealthList[-1]) # self.loans += agent.obtainedCreditList[-1] self.profit += -np.sum(self.p.bankID * agent.get_deposit()) def _calculate_firm_networth(self, agent): """ This internal function of the Bank class is used to represent the demands between firm agents and the bank --- Args: agent: The target firm agent --- Returns: """ self.totalLoanDemands += np.sum(agent.loan_demand) self.profit += agent.iL * np.sum(agent.loanList) self.deposits += np.sum(agent.depositList[-1]) self.loans += np.sum(agent.loanList) self.totalBankruptFraction += np.sum(agent.defaultProb * np.sum(agent.loanList)) def _calculate_running_loan(self, agent_id): """ This internal function of the Bank class is used to represent the demands between agents and the bank --- Args: agent_id: The target agent id --- Returns: """ # print("bank start") agent = self.agentList.select(self.agentList.getIdentity() == agent_id)[0] bankruptFraction = agent.defaultProb * np.sum(agent.loanList[0]) L_CAR = 0 if ( bankruptFraction <= self.totalLoanSupply and self.runningLoan <= self.totalLoanSupply ): L_CAR = np.sum([agent.loan_demand]) if self.Z_B < 0: agent.adjustAccordingToBankState(0) # print("option 1") elif 0 <= self.Z_B <= L_CAR: # print("option 2") agent.adjustAccordingToBankState(self.Z_B) self.runningLoan += self.Z_B else: # print("option 3") self.runningLoan += L_CAR agent.adjustAccordingToBankState(L_CAR)
[docs] def sommaW(self, eps=1e-8): """ This internal function of the Bank class is used to propagate the main functions of the bank """ # Reserves calculation ## When the reserve is negative/lower than the RRR: if self.R_cb < self.p.gammaRRR * self.deposits: self.R_cb = self.p.gammaRRR * self.deposits # set reserve to 0 self.F_cb += ( self.R_cb - self.deposits - self.loans + self.equities ) # request funding from CB self.F_cb_i = self.R_cb * (1 + self.p.bankICB_P) # payoff the interest ## When the reserve is positive/bigger than the RRR: if self.R_cb > self.deposits - self.loans + self.equities: self.F_cb = 0 # set funding to 0 self.F_cb_i = self.R_cb * (1 + self.p.bankICB_P) # earn interest on reserve self.Z_B = self.R_cb - self.p.gammaRRR * self.deposits # Demands and Transactions # [self._calculate_wealth(agent) for agent in self.model.consumer_agents] # Consolidate consumer demands efficiently (Vectorized) live_consumers = [ c for c in self.model.aliveConsumers if c.covidState["state"] != "dead" ] if live_consumers: # Sum wealth (wealthList[-1]) and deposits # Using generator expression for memory efficiency total_wealth = sum(c.wealthList[-1] for c in live_consumers) total_deposits = sum(c.deposit for c in live_consumers) self.deposits += total_wealth self.profit += -np.sum(self.p.bankID * total_deposits) [self._calculate_firm_networth(agent) for agent in self.model.cpfirm_agents] self._calculate_firm_networth(self.model.greenEFirm[0]) self._calculate_firm_networth(self.model.brownEFirm[0]) # Calculate Bank statistics self.profit += self.p.bankICB_P * (self.R_cb - self.F_cb) self.equities = self.loans - self.deposits + self.reserves self.equity = self.profit + self.equities self.totalLoanSupply = copy.copy(self.Z_B) self.DTE = self.deposits / (self.equities + eps) self.iL = self.DTE / 100 self.agentAssign() self.runningLoan = 0 list(map(self._calculate_running_loan, self.orderedAgentsInterests)) self.actualSuppliedLoan += np.sum(self.runningLoan)
[docs] def reset_bank(self): # Reset temporary variables self.agentList = ( self.model.csfirm_agents + self.model.cpfirm_agents + self.model.greenEFirm + self.model.brownEFirm ) consumers = self.model.aliveConsumers # Optimization: Pre-fetch arrays to avoid method call overhead is_employed = np.array([c.employed for c in consumers]) # Direct dict access for covid state cov_states = np.array([c.covidState["state"] for c in consumers]) # Attribute access for type (AMBER handles this or we list comp) cons_types = np.array([c.consumerType for c in consumers]) is_worker = cons_types == "workers" not_dead = cov_states != "dead" # Unemployed: Worker AND Not Employed AND Not Dead # Employed: Worker AND Employed AND Not Dead self.numberOfUnemployed = np.sum((~is_employed) & is_worker & not_dead) self.numberOfEmployed = np.sum(is_employed & is_worker & not_dead) self.profit = 0 self.totalLoanDemands = 0 self.equities = 0 self.actualSuppliedLoan = 0 self.totalBankruptFraction = 0 self.deposits = 0 self.loans = 0 self.NPL = 0