IMS / src / model / SFC / banks.jl
banks.jl
Raw
"""
Define banks' actions.
"""

"""
    prev_vars!(agent::Bank)  nothing

Update banks' previous variables.
"""
function prev_vars!(agent::Bank)
    agent.deposits_prev = agent.deposits
    agent.hpm_prev = agent.hpm
    agent.loans_prev = agent.loans
    agent.il_rate_prev = agent.il_rate
    agent.id_rate_prev = agent.id_rate
    agent.advances_prev = agent.advances
    agent.bills_prev = agent.bills
    agent.bonds_prev = agent.bonds
    agent.npl_prev = agent.npl
    agent.lending_facility_prev = agent.lending_facility 
    agent.deposit_facility_prev = agent.deposit_facility
    agent.funding_costs_prev = agent.funding_costs
    # ib
    agent.ON_assets_prev = agent.ON_assets 
    agent.ON_liabs_prev = agent.ON_liabs
    agent.Term_assets_prev = agent.Term_assets 
    agent.Term_liabs_prev = agent.Term_liabs
    return nothing
end

"""
    reset_vars!(agent::Bank)  nothing

Reset banks' variables.
"""
function reset_vars!(agent::Bank, scenario)
    agent.ib_flag = false
    agent.flow = 0.0
    agent.loans = 0.0 
    agent.npl = 0.0
    agent.deposits = 0.0
    agent.loans_interests = 0.0
    agent.deposits_interests = 0.0
    agent.belongToBank = missing
    agent.on_demand = 0.0
    agent.term_demand = 0.0
    agent.on_supply = 0.0
    agent.term_supply = 0.0
    agent.tot_demand = 0.0
    agent.tot_supply = 0.0
    agent.tot_assets = 0.0
    agent.tot_liabilities = 0.0
    agent.lending_facility = 0.0
    agent.deposit_facility = 0.0
    empty!(agent.ib_customers)
    if scenario == "Maturity"
        agent.am = 0.0
        agent.bm = 0.0
        agent.margin_stability = 0.0
        agent.actual_borr_ratio = 0.0
        agent.target_borr_ratio = 0.0
        agent.actual_lend_ratio = 0.0
        agent.target_lend_ratio = 0.0
        agent.pmb = 0.0
        agent.pml = 0.0
    end
    return nothing
end

"""
    reset_after_status!(agent::Bank)  nothing

Reset banks' variables after interbank status.
"""
function reset_after_status!(agent::Bank)
    agent.ON_assets = 0.0
    agent.Term_assets = 0.0
    agent.ON_liabs = 0.0
    agent.Term_liabs = 0.0
    return nothing
end

"""
    hpm!(agent::Bank, μ, v)  agent.hpm

Update banks' holdings of reserves.
"""
function hpm!(agent::Bank, μ, v)
    agent.hpm = (μ + v) * agent.deposits
    return agent.hpm
end

"""
    bills!(agent::Bank, model)  agent.bills

Update banks' holdings of bills (buffer variable). If bills are negative they are set to zero and banks ask for advances as buffer variable.
"""
function bills!(agent::Bank, model) #id
    agent.bills = max(0.0, min(agent.deposits + agent.npl + agent.lending_facility - agent.loans - model.μ * agent.deposits - agent.bonds - agent.deposit_facility, 
        sum(a.bills for a in allagents(model) if a isa Government) / (model.n_bj + model.n_bk)))
    return agent.bills
end

"""
    bonds!(agent::Bank)  agent.bonds

Update banks' bonds as dependent on non-performing-loans.
"""
function bonds!(agent::Bank)
    agent.bonds = agent.npl
    return agent.bonds
end

"""
    advances!(agent::Bank, model)  agent.advances

Update banks' advances. If bills are negative, advances act as buffer variable, otherwise banks ask for a proportion of current deposits.
"""
function advances!(agent::Bank, model) # alternative id
    if agent.bills == 0.0
        agent.advances = agent.loans + agent.deposit_facility + agent.hpm + agent.bonds - agent.deposits - agent.npl - agent.lending_facility
    else
        agent.advances =  model.v * agent.deposits
    end
    return agent.advances
end

"""
    update_status!(agent::Bank)  agent.status

Update banks' status in the interbank market: deficit, surplus or neutral.
"""
function update_status!(agent::Bank)
    if agent.flow < 0 && abs(agent.flow) > (agent.hpm - agent.hpm_prev)
        agent.status = :deficit
    elseif agent.flow > 0 && agent.flow > (agent.hpm - agent.hpm_prev)
        agent.status = :surplus
    else
        agent.status = :neutral
    end
    return agent.status
end

"""
    NSFR!(agent::Bank, model)  model

Update the elements of the Net Stable Funding Ratio (NSFR). The function is called 
when the scenario is "Maturity" and the margin of stability is weighted for the residual maturities of the NSFR.
"""
function NSFR!(agent::Bank, model)
    # end function prematurely if agent is neutral
    agent.status == :neutral && return

    agent.am = (model.m4 * agent.deposits_prev + model.m5 * agent.Term_liabs_prev) / agent.tot_liabilities
    agent.bm = 
        if agent.type == :business 
            (model.m1 * (agent.loans_prev + agent.ON_assets_prev) + model.m2 * (agent.bills_prev + agent.Term_assets_prev) + 
                model.m3 * agent.bonds_prev) / agent.tot_assets
        elseif agent.type == :commercial
            (model.m1 * agent.ON_assets_prev + model.m2 * (agent.loans_prev + agent.bills_prev + agent.Term_assets_prev) + 
                model.m3 * agent.bonds_prev) / agent.tot_assets
        end
    # update margin of stability
    agent.margin_stability = agent.am / agent.bm
    return model
end

""" 
    portfolio!(agent::Bank)  agent.tot_assets, agent.tot_liabilities

Update banks' asset holdings and liabilities.   
"""
function portfolio!(agent::Bank)
    agent.tot_assets = agent.loans_prev + agent.hpm_prev + agent.bills_prev + agent.bonds_prev + agent.ON_assets_prev + agent.Term_assets_prev + agent.deposit_facility_prev
    agent.tot_liabilities = agent.deposits_prev + agent.ON_liabs_prev + agent.Term_liabs_prev + agent.npl_prev + agent.lending_facility_prev + agent.advances_prev
    return agent.tot_assets, agent.tot_liabilities
end

"""
    lending_targets!(agent::Bank, rng, arbitrary_threshold)  agent.pml

Update lenders' preferences for overnight interbank assets: banks' are assumed to be indifferent to maturity considerations in 
the `Baseline` scenario. When the `Maturity` scenario is active, preferences depend on NSFR weights.
"""
function lending_targets!(agent::Bank, rng, arbitrary_threshold)
    # end function prematurely if agent is not surplus
    agent.status != :surplus && return

    agent.actual_lend_ratio = 1 - agent.bm
    agent.target_lend_ratio = 
        if agent.margin_stability >= (1.0 + arbitrary_threshold)
            agent.actual_lend_ratio
        else 
            rand(rng, Uniform(0.0, agent.actual_lend_ratio))
        end
    agent.pml = max(0.0, min(agent.actual_lend_ratio - agent.target_lend_ratio, 1.0))

    return agent.pml
end

"""
    borrowing_targets!(agent::Bank, rng, arbitrary_threshold)  agent.pmb

Update borrowers' preferences for overnight interbank assets: banks' are assumed to be indifferent to maturity considerations in 
the `Baseline` scenario. When the `Maturity` scenario is active, preferences depend on NSFR weights.
"""
function borrowing_targets!(agent::Bank, rng, arbitrary_threshold)
    # end function prematurely if agent is not deficit
    agent.status != :deficit && return 

    agent.actual_borr_ratio = 1 - agent.am
    agent.target_borr_ratio = 
        if agent.margin_stability < (1.0 + arbitrary_threshold)
            agent.actual_borr_ratio
        else 
            rand(rng, Uniform(0.0, agent.actual_borr_ratio))
        end
    agent.pmb = max(0.0, min(agent.actual_borr_ratio - agent.target_borr_ratio, 1.0))

    return agent.pmb
end

"""
    tot_demand!(agent::Bank)  agent.tot_demand

Deficit banks define their total demand for reserves as dependent on their current outflow and `ΔH`.
"""
function tot_demand!(agent::Bank)
    agent.status != :deficit && return
    
    agent.tot_demand = abs(agent.flow) - (agent.hpm - agent.hpm_prev)
    return agent.tot_demand
end

"""
    tot_supply!(agent::Bank)  agent.tot_supply

Surplus banks define their total demand for reserves as dependent on their current inflow and `ΔH`.
"""
function tot_supply!(agent::Bank)
    agent.status != :surplus && return
    
    agent.tot_supply = agent.flow - (agent.hpm - agent.hpm_prev)
    return agent.tot_supply
end

"""
    on_demand!(agent::Bank, model)  agent.on_demand

Banks define their demand for overnight interbank loans dependent on money market rates (`Baseline` scenario) 
and NSFR-based borrowing preferences (`Maturity` scenario). When the `Baseline` scenario is active, lenders accommodate borrowers' demand for funds 
in the overnight segment. Otherwise, see `on_supply!.
"""
function on_demand!(agent::Bank, model)
    if agent.status == :deficit && agent.ib_flag
        agent.on_demand = agent.tot_demand * (model.θ * agent.pmb)
        if model.scenario == "Baseline" 
            model[agent.belongToBank].on_supply += agent.on_demand
        end
    end
    return agent.on_demand
end

"""
    term_demand!(agent::Bank, model)  agent.term_demand

Banks define their demand for term interbank loans as a residual. When the `Baseline` scenario is active, lenders accommodate borrowers' demand for funds 
in the term segment. Otherwise, see `term_supply!`.
"""
function term_demand!(agent::Bank, model)
    if agent.status == :deficit && agent.ib_flag
        agent.term_demand = agent.tot_demand - agent.on_demand
        if model.scenario == "Baseline" 
            model[agent.belongToBank].term_supply += agent.term_demand
        end
    end
    return agent.term_demand
end

"""
    on_supply!(agent::Bank, LbW)  agent.on_supply

Banks define their supply for overnight interbank loans dependent on money market rates and NSFR-based lending preferences when the scenario
is "Maturity".
"""
function on_supply!(agent::Bank, LbW)
    if agent.status == :surplus && agent.ib_flag
        agent.on_supply = agent.tot_supply * (LbW * agent.pml)
    end
    return agent.on_supply
end

"""
    term_supply!(agent::Bank)  agent.term_supply

Banks define their supply for term interbank loans as a residual, when the scenario is "Maturity".
"""
function term_supply!(agent::Bank)
    if agent.status == :surplus && agent.ib_flag
        agent.term_supply = agent.tot_supply - agent.on_supply
    end
    return agent.term_supply
end

"""
    ib_on!(agent::Bank, model)  model

Updates overnight interbank assets and liabilities. When the `Baseline` scenario is active, lenders accommodate borrowers' demand for funds 
in the overnight segment. Otherwise, borrowers receive funds according to the short-side of the market.
"""
function ib_on!(agent::Bank, model)
    if agent.status == :deficit && agent.ib_flag
        # If the deficit bank's overnight demand exceeds available overnight supply of matching partner
        if agent.on_demand > model[agent.belongToBank].on_supply
            # Deficit bank's overnight liabilities are set to the available overnight supply
            agent.ON_liabs = model[agent.belongToBank].on_supply
        # Or else if the deficit bank's overnight demand is less than or equal to available overnight supply of partner
        elseif agent.on_demand <= model[agent.belongToBank].on_supply
            # Deficit bank's overnight liabilities are set to the available overnight demand
            agent.ON_liabs = agent.on_demand
        end
        # Increase the partner bank's overnight assets
        model[agent.belongToBank].ON_assets += agent.ON_liabs
        # Decrease available term supply for matching surplus bank
        model[agent.belongToBank].on_supply -= agent.ON_liabs
    end
    return model
end

"""
    ib_term!(agent::Bank, model)  model

Updates term interbank assets and liabilities. When the `Baseline` scenario is active, lenders accommodate borrowers' demand for funds 
in the term segment. Otherwise, borrowers receive funds according to the short-side of the market.
"""
function ib_term!(agent::Bank, model)
    if agent.status == :deficit && agent.ib_flag
        # If the deficit bank's term demand exceeds available term supply of matching partner
        if agent.term_demand > model[agent.belongToBank].term_supply
            # Deficit bank's term liabilities are set to the available term supply
            agent.Term_liabs = model[agent.belongToBank].term_supply
        # Or else if the bank's term demand is less than or equal to available term supply
        elseif agent.term_demand <= model[agent.belongToBank].term_supply
            # Deficit bank's term liabilities are set to the available term demand
            agent.Term_liabs = agent.term_demand
        end
        # Increase the partner bank's term assets
        model[agent.belongToBank].Term_assets += agent.Term_liabs
        # Decrease available term supply for matching surplus bank
        model[agent.belongToBank].term_supply -= agent.Term_liabs
    end
    return model
end

"""
    restore_supply!(agent::Bank)  agent.term_supply, agent.on_supply

Restore overnight and term supply to its original value for computing interbank interest rates. Supply in both segments
was previously reduced to account for multiple partners for lending banks and a decreasing avaialability of loanable funds.
"""
function restore_supply!(agent::Bank)
    if agent.status == :surplus && agent.ib_flag
        agent.term_supply += agent.Term_assets
        agent.on_supply += agent.ON_assets
    end
    return agent.term_supply, agent.on_supply
end

"""
    restore_total_supply!(agent::Bank, model)  agent.total_supply

Restore total supply to its original value for computing clearing of the interbank market. Total supply
was previously reduced to account for multiple partners for lending banks and a decreasing avaialability of loanable funds in the matching
mechanism for the `Baseline` scenario.
"""
function restore_total_supply!(agent::Bank, model)
    if agent.status == :surplus && agent.ib_flag
        agent.tot_supply = sum(model[id].tot_demand for id in agent.ib_customers)
    end
    return agent.tot_supply
end

"""
    check_ib_stocks!(agent::Bank, model)  model

Check whether interbank stocks in the overnight and term segment match between deficit and surplus banks
"""
function check_ib_stocks!(agent::Bank, model; tol::Float64 = 1e-06)
    if agent.status == :surplus && agent.ib_flag
        if agent.ON_assets - sum(model[id].ON_liabs for id in agent.ib_customers) > tol
            @warn "ON assets do not correspond to ON liabilities!"
        elseif agent.Term_assets - sum(model[id].Term_liabs for id in agent.ib_customers) > tol
            @warn "Term assets do not correspond to Term liabilities!"
        end
    end
    return model
end

"""
    lending_facility!(agent::Bank)  agent.lending_facility

Deficit banks that do not find a suitable partner in the interbank market access the central bank's lending facility
to cover their outflows.    
"""
function lending_facility!(agent::Bank)
    if agent.status == :deficit
        agent.lending_facility = 
            if !agent.ib_flag
                agent.tot_demand
            else
                max(0.0, agent.tot_demand - agent.ON_liabs - agent.Term_liabs)
            end
    end
    return agent.lending_facility
end

"""
    deposit_facility!(agent::Bank)  agent.deposit_facility

Surplus banks that do not find a suitable partner in the interbank market access the central bank's deposit facility
to deposit their excess reserves deriving from payment inflows.    
"""
function deposit_facility!(agent::Bank)
    if agent.status == :surplus
        agent.deposit_facility = 
            if !agent.ib_flag
                agent.tot_supply
            else 
               max(0.0, agent.tot_supply - agent.ON_assets - agent.Term_assets)
            end
    end
    return agent.deposit_facility
end

"""
    funding_costs!(agent::Bank, icbt, ion, iterm, icbl)  agent.funding_costs

Banks compute their funding costs according to their recourse to the interbank market and/or the central bank's lending facility.
"""
function funding_costs!(agent::Bank, icbt, ion, iterm, icbl)
    if agent.status == :deficit
        if agent.ib_flag
            if agent.ON_liabs > 0.0 && agent.Term_liabs > 0.0
                agent.funding_costs = (icbt + ion + iterm)/3
            elseif agent.ON_liabs > 0.0 && agent.Term_liabs == 0.0
                agent.funding_costs = (icbt + ion)/2
            elseif agent.ON_liabs == 0.0 && agent.Term_liabs > 0.0
                agent.funding_costs = (icbt + iterm)/2
            end
        else
            agent.funding_costs = (icbt + icbl)/2
        end
    else
        agent.funding_costs = icbt
    end
    return agent.funding_costs
end

"""
    credit_rates!(agent::Bank, χ1)  agent.il_rate, agent.id_rate

Banks determine lending and deposit rates on the credit market based on their previous period funding costs.
"""
function credit_rates!(agent::Bank, χ1)
    agent.il_rate = agent.funding_costs_prev + χ1
    agent.id_rate = agent.funding_costs_prev - χ1
    return agent.il_rate, agent.id_rate
end

"""
    interests_payments!(agent::Bank, model)  model

Banks make interest payments on bills, bonds, reserves, advances and central bank's facilities.
"""
function interests_payments!(agent::Bank, model)
    agent.bills_interests = model.ib * agent.bills_prev
    agent.bonds_interests = model.iblr * agent.bonds_prev
    agent.hpm_interests = model.icbt * agent.hpm_prev
    agent.advances_interests = model.icbt * agent.advances_prev
    agent.lending_facility_interests = model.icbl * agent.lending_facility_prev
    agent.deposit_facility_interests = model.icbd * agent.deposit_facility_prev
    return model
end

"""
    profits!(agent::Bank)  agent.profits

Banks compute their profits.
"""
function profits!(agent::Bank)
    agent.profits = agent.loans_interests + agent.hpm_interests + agent.bills_interests + agent.bonds_interests + agent.deposit_facility_interests - 
                agent.deposits_interests - agent.advances_interests - agent.lending_facility_interests
    return agent.profits
end

"""
    networth!(agent::Bank)  agent.networth

Banks update their networth.
"""
function networth!(agent::Bank)
    agent.networth = agent.loans + agent.deposit_facility + agent.hpm  + agent.bills + agent.bonds - agent.deposits - agent.advances - agent.lending_facility
    return agent.networth
end

"""
    current_balance!(agent::Bank)  agent.balance_current

Update banks' current balances for SFC checks.
"""
function current_balance!(agent::Bank)
    agent.balance_current =  agent.loans_interests + agent.hpm_interests + agent.bills_interests + agent.bonds_interests + agent.deposit_facility_interests - 
        agent.deposits_interests - agent.advances_interests - agent.lending_facility_interests - agent.profits
    return agent.balance_current
end

"""
    capital_balance!(agent::Bank)  agent.balance_capital

Update banks' capital balances for SFC checks.
"""
function capital_balance!(agent::Bank)
    agent.balance_capital = agent.advances + agent.deposits + agent.npl - agent.loans - agent.bills - agent.bonds - agent.hpm + 
        agent.lending_facility - agent.deposit_facility
    return agent.balance_capital
end

"""
    SFC!(agent::Bank, model)  model

Define banks' SFC actions and update their accounting.
"""
function SFC!(agent::Bank, model)
    IMS.hpm!(agent, model.μ, model.v)
    IMS.bills!(agent, model)
    IMS.advances!(agent, model)
    IMS.networth!(agent)
    IMS.capital_balance!(agent)
    return model
end