IMS / src / model / init.jl
init.jl
Raw
"""
Initialise model and agent types, compute initial values and distribute them among the model's sectors
and set initial market interactions.
"""

"""
    init_model(; seed::UInt32 = UInt32(96100), scenario::String = "Baseline", shock::String = "Missing", properties...)  model

Initialise the model.    
"""
function init_model(; seed::UInt32 = UInt32(96100), scenario::String = "Baseline", shock::String = "Missing",
        properties...) 
        if shock in ["Missing", "Corridor", "Width", "Uncertainty"] && scenario in ["Baseline", "Maturity"]
            model = ABM(Union{Government, CentralBank, Firm, Household, Bank};
                properties = Parameters(; shock, scenario, properties...), 
                scheduler = Schedulers.Randomly(),
                rng = Xoshiro(seed),
                warn = false # turns off Agents.jl warning of Union types
            )

            init_agents!(model)
            model.a0 = rand(model.rng, Uniform(0.0, 1.0))
            distribute_SS_values(model)
            real_sector_interactions!(model)
            credit_sector_interactions!(model)
        else
            error("You provided a shock named $(shock) or a scenario named $(scenario) that is not yet implemented. Check for typos or add the scenario/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,
        )
        add_agent!(a, model)
    end

    # initialise Firms
    for id = (model.n_hh + 1):(model.n_hh + model.n_f)
        a = Firm(
            id = id,
        )
        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,
        )
        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, 
        )     
        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. 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
    icbt = model.ib # => Pcb = 0.0
    il = 0.0335
    id = 0.0155
    K = 3038.0
    # eqs
    Pcb = 0.0
    y = ((model.g  * model.ω * (1 + model.ρ)) / model.pr - Pcb) / (((model.τ * model.ω) / model.pr) - model.ib * model.r)
    N = y / model.pr
    W = model.ω * N
    T = model.τ * W
    UC = W / y
    p = UC * (1 + model.ρ)
    G = model.g * p
    B = y * model.r
    i = model.gk * K
    I = i * p
    c = y - i - model.g 
    C = c * p
    invent_target = y
    invent = invent_target
    Invent = invent * UC
    Df = model.γ * W
    Lf = K + Invent + Df # from I = ΔK or b.s. matrix
    Pf = C + G + I + id * Df - W - il * Lf
    Yd = C
    α2_SS = (C - model.α1 * Yd) / (B + Lf - Df)
    NWh = (C - model.α1 * Yd) / α2_SS
    Lh = model.γ * (C + NWh)
    Dh = NWh + Lh
    Hj = (model.μ + model.v) * Dh
    Hk = (model.μ + model.v) * Df
    H = Hj + Hk
    Bj = max(0.0, min(Dh - Lh - model.μ * Dh, B))
    Bk = max(0.0, min(Df - Lf - model.μ * Df, B))
    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 = il * Lh - id * Dh + model.ib * Bj + icbt * Hj - icbt * Aj
    Pbk = il * Lf - id * Df + model.ib * Bk + icbt * Hk - icbt * Ak
    # sectoral net worths: balance sheet matrix
    GD = B
    NWcb = Bcb - H + A
    NWbj = Lh + Hj - Dh - Aj + Bj
    NWbk = Lf + Hk - Df - Ak + Bk
    NWf = K + Invent + Df - Lf

    # collect all variables
    all_variables = (N, UC, p, K, y, Invent, invent, 
                    invent_target, W, T, B, I, i, Lf, 
                    C, c, G, Yd, Df, NWh, Lh, Dh, Hj, Hk, 
                    H, Bj, Bk, Ak, Aj, A, Bcb, 
                    Pbj, Pbk, Pf, il, id)

    # 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, K, Invent)
    
    # update SS-given parameters
    model.α2 = α2_SS

    return all_variables
end

"""
    SS_SFC_checks(GD, NWh, NWcb, NWbj, NWbk, NWf, K, Invent; 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, K, Invent; tol::Float64 = 1e-06)
    if abs((GD - (NWh + NWcb + NWbj + NWbk + NWf))) - abs((K + Invent)) > 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
    (N, UC, p, K, y, Invent, invent, 
    invent_target, W, T, B, I, i, Lf, 
    C, c, G, Yd, Df, NWh, Lh, Dh, Hj, Hk, 
    H, Bj, Bk, Ak, Aj, A, Bcb, 
    Pbj, Pbk, Pf, il, id) = SS_initial(model)

    for a in allagents(model)
        if isa(a, Government)
            a.spending = G
            a.taxes = T
            a.bills = B
        elseif isa(a, CentralBank)
            a.bills = Bcb
            a.hpm = H
            a.advances = A
        elseif isa(a, Household)
            a.consumption = c / model.n_hh
            a.nominal_consumption = C / model.n_hh
            a.income = Yd / model.n_hh
            a.income_exp = a.income
            a.wages = W / model.n_hh
            a.taxes = T / model.n_hh
            a.loans = Lh / model.n_hh
            a.deposits = Dh / model.n_hh
            a.networth = NWh / model.n_hh
        elseif isa(a, Firm)
            a.consumption = c / model.n_f
            a.nominal_consumption = C / model.n_f
            a.investments = i / model.n_f
            a.nominal_investments = I / model.n_f
            a.capital = K / model.n_f
            a.output = y / model.n_f
            a.workers = N / model.n_f
            a.invent = invent / model.n_f
            a.invent_exp = a.invent
            a.invent_target = invent_target / model.n_f
            a.sales = a.output
            a.sales_exp = a.sales
            a.Invent = Invent / model.n_f
            a.wages = W / model.n_f
            a.deposits = Df / model.n_f
            a.loans = Lf / model.n_f
            a.profits = Pf / model.n_f
            a.prices = p
            a.unit_costs = UC
        elseif isa(a, Bank) && a.type == :commercial
            a.deposits = Dh / model.n_bj
            a.advances = Aj / model.n_bj
            a.bills = Bj / model.n_bj
            a.hpm = Hj / model.n_bj
            a.loans = Lh / model.n_bj
            a.funding_costs = model.icbt
            a.il_rate = il
            a.id_rate = id
            a.profits = Pbj / model.n_bj
            # NSFR
            a.tot_assets = a.loans + a.hpm + a.bills
            a.tot_liabilities = a.deposits + a.advances
            if model.scenario == "Maturity"
                a.am = (model.m4 * a.deposits) / a.tot_liabilities
                a.bm = (model.m2 * (a.bills + a.loans)) / a.tot_assets
                a.margin_stability = a.am / a.bm
                a.pml = 0.0
                a.pmb = 0.0
            end
        elseif isa(a, Bank) && a.type == :business
            a.deposits = Df / model.n_bk
            a.advances = Ak / model.n_bk
            a.bills = Bk / model.n_bk
            a.hpm = Hk / model.n_bk
            a.loans = Lf / model.n_bk
            a.funding_costs = model.icbt
            a.il_rate = il
            a.id_rate = id
            a.profits = Pbk / model.n_bk
            # NSFR
            a.tot_assets = a.loans + a.hpm + a.bills
            a.tot_liabilities = a.deposits + a.advances
            if model.scenario == "Maturity" 
                a.am = (model.m4 * a.deposits) / a.tot_liabilities
                a.bm = (model.m1 * a.loans + model.m2 * a.bills) / a.tot_assets
                a.margin_stability = a.am / a.bm
                a.pml = 0.0
                a.pmb = 0.0
            end
        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