"""
Initialise model and agent types, compute initial values and distribute them among the model's sectors,
set initial market interactions and agents' personal values, leverage and liquidity measures.
"""
"""
init_model(; seed::UInt32 = UInt32(95129), properties...) → model
Initialise the model.
"""
function init_model(; seed::UInt32 = UInt32(95129), shock = "Missing",
properties...)
if shock in ("Missing", "Corridor", "Corridor-unique", "Expansionary")
model = ABM(Union{Government, CentralBank, Firm, Household, Bank};
properties = Parameters(; shock, properties...),
scheduler = Schedulers.Randomly(),
rng = Xoshiro(seed),
warn = false # turns off Agents.jl warning of Union types
)
init_agents!(model)
distribute_SS_values(model)
real_sector_interactions!(model)
credit_sector_interactions!(model)
init_liq_values(model)
init_lev(model)
else
error("You provided a shock named $(shock) that is not yet implemented.
Check for typos or add the shock!")
end
return model
end
"""
init_agents!(model::ABM) → model
Initialise and add agent types to the model.
"""
function init_agents!(model::ABM)
# initialise Households
for id = 1:model.n_hh
a = Household(
id = id,
value = init_personal_values(id, model.n_hh, 0, cumsum(model.value_dist)),
)
add_agent!(a, model)
end
# initialise Firms
for id = (model.n_hh + 1):(model.n_hh + model.n_f)
a = Firm(
id = id,
value = init_personal_values(id, model.n_f, model.n_hh, cumsum(model.value_dist)),
)
add_agent!(a, model)
end
# initialise commercial Banks
for id = (model.n_hh + model.n_f + 1):(model.n_hh + model.n_f + model.n_bj)
a = Bank(
id = id,
type = :commercial,
value = init_personal_values(id, model.n_bj, (model.n_hh + model.n_f), cumsum(model.value_dist)),
)
add_agent!(a, model)
end
# initialise business Banks
for id = (model.n_hh + model.n_f + model.n_bj + 1):(model.n_hh + model.n_f + model.n_bj + model.n_bk)
a = Bank(
id = id,
type = :business,
value = init_personal_values(id, model.n_bk, (model.n_hh + model.n_f + model.n_bj), cumsum(model.value_dist)),
)
add_agent!(a, model)
end
# initialise Government
a = Government(
id = model.n_hh + model.n_f + model.n_bj + model.n_bk + 1
)
add_agent!(a, model)
# initialise CentralBank
a = CentralBank(
id = model.n_hh + model.n_f + model.n_bj + model.n_bk + 2
)
add_agent!(a, model)
return model
end
"""
SS_initial(model) → all_variables
Compute the SS values of the model. Please note that the notation used in this function roughly matches the one used in the paper as
in the code we differentiate between business and commercial banks more explicitly. We thus denote with `j` variables
related to commercial banks and with `k` those related to business ones. The function returns `all_variables` which is an
NTuple that links variable names to their corrisponding initial values. More, it updates the SS-given value
of `model.α2`, trhows a warning if variables are negative and performs the initial Stock-Flow consistency checks.
"""
function SS_initial(model)
# exogenous variables and parameters
icb = model.ib # => Pcb = 0.0
ilh = 0.0335
ilf = 0.0325
id = 0.0155
levf = 0.5
levh = 0.5
Inv = 303.8
# eqs
Pcb = 0.0
Y = (model.g - Pcb)/((model.τ/ (1+model.ρ)) - model.r * model.ib)
W = Y /(1 + model.ρ)
T = model.τ * W
B = Y * model.r
Lf = levf * W
C = Y - model.g # - Inv
Yd = C
Df = Lf - Inv
Lh = levh * C
α2_SS = ((C * (1 - model.α1 - model.α3 * levh)) / ((B - Df + Lf)))
NWh = (C * (1 - model.α1 - model.α3 * levh)) / (α2_SS)
Dh = NWh + Lh
Hj = (model.μ + model.v) * Dh
Hk = (model.μ + model.v) * Df
H = Hj + Hk
Bj = max(0.0, Dh - Lh - model.μ * Dh)
Bk = max(0.0, Df - Lf - model.μ * Df)
Aj = if Bj == 0.0
Hj + Lh - Dh
else
model.v * Dh
end
Ak = if Bk == 0.0
Hk + Lf - Df
else
model.v * Df
end
A = Aj + Ak
Bcb = B - Bj - Bk
Pbj = ilh * Lh - id * Dh + model.ib * Bj + icb * Hj - icb * Aj
Pbk = ilf * Lf - id * Df + model.ib * Bk + icb * Hk - icb * Ak
Pf = C + model.g - W + id * Df - ilf * Lf
# sectoral net worths: balance sheet matrix
GD = B
# NWh = Dh - Lh
NWcb = Bcb - H + A
NWbj = Lh + Hj - Dh - Aj + Bj
NWbk = Lf + Hk - Df - Ak + Bk
NWf = Inv + Df - Lf
# collect all variables
all_variables = (Inv, Y, W, T, B, Lf, C,
Yd, Df, NWh, Lh, Dh, Hj, Hk,
H, Bj, Bk, Ak, Aj, A, Bcb,
Pbj, Pbk, Pf, ilh, ilf, id, NWbj, NWbk)
# checks for negative initial values
any(<(0.0), all_variables) && @warn "Some initial values are negative, check the parameters and/or the equations again!"
# checks for networth balance and hidden equation at SS
SS_SFC_checks(GD, NWh, NWcb, NWbj, NWbk, NWf, Inv)
# update SS-given parameters
model.α2 = α2_SS
return all_variables
end
"""
SS_SFC_checks(GD, NWh, NWcb, NWbj, NWbk, NWf, Inv; tol::Float64 = 1e-06) → nothing
Perform SFC checks for initial values calculation at the Steady State.
"""
function SS_SFC_checks(GD, NWh, NWcb, NWbj, NWbk, NWf, Inv; tol::Float64 = 1e-06)
if (GD - (NWh + NWcb + NWbj + NWbk + NWf)) - Inv > tol
@warn "Initial values calculation does not respect stock-flow consistency - Net Worth!
Check equations and parameters and try again."
end
if abs(NWcb) > tol
@warn "Initial values calculation does not respect stock-flow consistency - Hidden Equation!
Check equations and parameters and try again."
end
return nothing
end
"""
distribute_SS_values(model) → model
Distribute SS values to aggregate sectors (Government and Central Bank) and to heterogenous sectors homogeneously.
"""
function distribute_SS_values(model)
# take variables names and corresponding initial values
(Inv, Y, W, T, B, Lf, C,
Yd, Df, NWh, Lh, Dh, Hj, Hk,
H, Bj, Bk, Ak, Aj, A, Bcb,
Pbj, Pbk, Pf, ilh, ilf, id, NWbj, NWbk) = SS_initial(model)
for a in allagents(model)
if isa(a, Government)
a.taxes = T
a.bills = B
a.bills_prev = a.bills
elseif isa(a, CentralBank)
a.bills = Bcb
a.bills_prev = a.bills
a.hpm = H
a.hpm_prev = a.hpm
a.advances = A
a.advances_prev = a.advances
elseif isa(a, Household)
a.consumption = C / model.n_hh
a.income = Yd / model.n_hh
a.wages = W / model.n_hh
a.taxes = T / model.n_hh
a.loans = Lh / model.n_hh
a.loans_prev = a.loans
a.loans_demand = a.loans
a.deposits = Dh / model.n_hh
a.deposits_prev = a.deposits_prev
a.networth = NWh / model.n_hh
a.networth_prev = a.networth
elseif isa(a, Firm)
a.consumption = C / model.n_f
a.inventories = Inv / model.n_f
a.inventories_prev = a.inventories
a.GDP = Y / model.n_f
a.wages = W / model.n_f
a.wages_prev = a.wages
a.deposits = Df / model.n_f
a.deposits_prev = a.deposits
a.loans = Lf / model.n_f
a.loans_prev = a.loans
a.loans_demand = a.loans
a.profits = Pf / model.n_f
elseif isa(a, Bank) && a.type == :commercial
a.deposits = Dh / model.n_bj
a.deposits_prev = a.deposits
a.advances = Aj / model.n_bj
a.advances_prev = a.advances
a.bills = Bj / model.n_bj
a.bills_prev = a.bills
a.hpm = Hj / model.n_bj
a.hpm_prev = a.hpm
a.loans = Lh / model.n_bj
a.loans_prev = a.loans
a.funding_costs = model.icb
a.ilh_rate = ilh
a.id_rate = id
a.profits = Pbj / model.n_bj
a.networth = NWbj / model.n_bj
elseif isa(a, Bank) && a.type == :business
a.deposits = Df / model.n_bk
a.deposits_prev = a.deposits
a.advances = Ak / model.n_bk
a.advances_prev = a.advances
a.bills = Bk / model.n_bk
a.bills_prev = a.bills
a.hpm = Hk / model.n_bk
a.hpm_prev = a.hpm
a.loans = Lf / model.n_bk
a.loans_prev = a.loans
a.funding_costs = model.icb
a.ilf_rate = ilf
a.id_rate = id
a.profits = Pbk / model.n_bk
a.networth = NWbk / model.n_bk
end
end
return model
end
"""
real_sector_interactions!(model) → model
Match households and firms for consumption and wages exchanges in the real sector.
"""
function real_sector_interactions!(model)
HPF = round(Int, model.n_hh/model.n_f)
for i in 1:model.n_f
for id in (HPF*(i-1)+1):(HPF*i)
model[id].belongToFirm = model.n_hh + i
push!(model[model.n_hh + i].customers, id)
end
end
return model
end
"""
credit_sector_interactions!(model) → model
Match households and firms to banks in the credit market.
"""
function credit_sector_interactions!(model)
APBk = round(Int, model.n_f/model.n_bk)
for i in 1:model.n_bk
for id in (APBk*(i-1) + model.n_hh + 1):(APBk*i + model.n_hh)
model[id].belongToBank = model.n_hh + model.n_f + model.n_bj + i
push!(model[model.n_hh + model.n_f + model.n_bj + i].firms_customers, id)
end
end
APBj = round(Int, model.n_hh/model.n_bj)
for i in 1:model.n_bj
for id in (APBj*(i-1)+1):(APBj*i)
model[id].belongToBank = model.n_hh + model.n_f + i
push!(model[model.n_hh + model.n_f + i].hh_customers, id)
end
end
return model
end
"""
init_personal_values(id, n, x, value_dist; VALUES = (:C, :O, :SE, :ST)) → nothing
Initialise agents' value types based on their `id`, total number of agents of the same type `n`,
total number of previously initialised agents of different type `x`, and values distribution `value_dist` which
depends on the `scenario` implemented.
"""
function init_personal_values(id, n, x, value_dist; VALUES = (:C, :O, :SE, :ST))
for i in 1:lastindex(value_dist)
if id ≤ (value_dist[i] * n) + x || id ≈ (value_dist[i] * n) + x
return VALUES[i]
else
continue
end
end
return nothing
end
"""
init_liq_values(model) → model
Initialise banks' liquidity preferences depending on value types.
"""
function init_liq_values(model)
for id in ids_by_type(Bank, model)
if model[id].value == :C || model[id].value == :SE
if model[id].type == :business
model[id].liq_values = rand(model.rng, Uniform(0.1, 0.5))
else
model[id].liq_values = rand(model.rng, Uniform(0.6, 1.0))
end
elseif model[id].value == :O || model[id].value == :ST
if model[id].type == :business
model[id].liq_values = rand(model.rng, Uniform(0.6, 1.0))
else
model[id].liq_values = rand(model.rng, Uniform(0.1, 0.5))
end
end
model[id].liq_pref = model[id].liq_values
end
return model
end
"""
init_lev(model) → model
Initialise households' and firms' levarage measure depending on value types.
"""
function init_lev(model)
for id in ids_by_type(Household, model)
if model[id].value == :C || model[id].value == :ST
model[id].lev = rand(model.rng, Uniform(0.1, 0.5))
else
model[id].lev = rand(model.rng, Uniform(0.6, 1.0))
end
end
for id in ids_by_type(Firm, model)
if model[id].value == :C || model[id].value == :SE
model[id].lev = rand(model.rng, Uniform(0.1, 0.5))
else
model[id].lev = rand(model.rng, Uniform(0.6, 1.0))
end
end
return model
end