Scenario analysis via custom shocks

In this tutorial we will illustrate how to perform a scenario analysis by running the model multiple times under a specific shock and comparing the results with the unshocked model.

import BeforeIT as Bit
import StatsBase: mean, std
using Plots

parameters = Bit.AUSTRIA2010Q1.parameters;
initial_conditions = Bit.AUSTRIA2010Q1.initial_conditions;

Initialise the model

model = Bit.Model(parameters, initial_conditions);

Simulate the baseline model for T quarters, N_reps times, and collect the data

T = 16
N_reps = 64
model_vec_baseline = Bit.ensemblerun(model, T, N_reps);

Now, apply a shock to the model and simulate it again. A shock is simply a function that takes the model and changes some of its parameters for a specific time period. We do this by first defining a "struct" with useful attributes. For example, we can define an productivity and a consumption shock with the following structs

struct ProductivityShock
    productivity_multiplier::Float64    # productivity multiplier
end

struct ConsumptionShock
    consumption_multiplier::Float64    # productivity multiplier
    final_time::Int
end

and then by making the structs callable functions that change the parameters of the model, this is done in Julia using the syntax below

A permanent change in the labour productivities by the factor s.productivity_multiplier

function (s::ProductivityShock)(model::Bit.Model)
    model.firms.alpha_bar_i .= model.firms.alpha_bar_i .* s.productivity_multiplier
end

A temporary change in the propensity to consume model.prop.psi by the factor s.consumption_multiplier

function (s::ConsumptionShock)(model::Bit.Model)
    if model.agg.t == 1
        model.prop.psi = model.prop.psi * s.consumption_multiplier
    elseif model.agg.t == s.final_time
        model.prop.psi = model.prop.psi / s.consumption_multiplier
    end
end

Define specific shocks, for example a 2% increase in productivity

productivity_shock = ProductivityShock(1.02)
Main.ProductivityShock(1.02)

or a 4 quarters long 2% increase in consumption

consumption_shock = ConsumptionShock(1.02, 4)
Main.ConsumptionShock(1.02, 4)

Simulate the model with the shock

model_vec_shocked = Bit.ensemblerun(model, T, N_reps; shock = consumption_shock);

extract the data vectors from the model vectors

data_vector_baseline = Bit.DataVector(model_vec_baseline);
data_vector_shocked = Bit.DataVector(model_vec_shocked);

Compute mean and standard error of GDP for the baseline and shocked simulations

mean_gdp_baseline = mean(data_vector_baseline.real_gdp, dims = 2)
mean_gdp_shocked = mean(data_vector_shocked.real_gdp, dims = 2)
sem_gdp_baseline = std(data_vector_baseline.real_gdp, dims = 2) / sqrt(N_reps)
sem_gdp_shocked = std(data_vector_shocked.real_gdp, dims = 2) / sqrt(N_reps)
17×1 Matrix{Float64}:
   0.0
   1.7865068761941965
  21.110315985816616
  41.35949422351184
  55.40933271207089
  69.58970582798813
  86.87652447949039
 101.73678371578859
 111.77651010487111
 121.04474103015707
 129.66599186680705
 137.36395181230398
 146.50821156098633
 150.85098128866176
 162.43347921103492
 173.8016335454425
 178.78416339724694

Compute the ratio of shocked to baseline GDP

gdp_ratio = mean_gdp_shocked ./ mean_gdp_baseline
17×1 Matrix{Float64}:
 1.0
 1.0005003541765403
 1.0059634825383448
 1.008246011507844
 1.0094385770019694
 1.0061080519320071
 1.004501257752781
 1.0031727208012866
 1.00208183324786
 1.0008142987503215
 1.0002291265272192
 0.9999680473725676
 1.000396348679974
 1.0009213519792797
 1.0011619388125652
 1.0012566040096564
 1.0011844442201825

the standard error on a ratio of two variables is computed with the error propagation formula

sem_gdp_ratio = gdp_ratio .* ((sem_gdp_baseline ./ mean_gdp_baseline).^2 .+ (sem_gdp_shocked ./ mean_gdp_shocked).^2).^0.5
17×1 Matrix{Float64}:
 0.0
 2.7685467795758086e-5
 0.0004019072192263169
 0.0007586222394135282
 0.0010279555991530576
 0.0013259485352288282
 0.0016244141522447002
 0.0018861392656532442
 0.0020862127251288286
 0.002269706777348419
 0.002408084065851926
 0.0025976670729668176
 0.0028071312896316585
 0.002896757987779719
 0.003026575355825149
 0.0031874822535389634
 0.00328348087495681

Finally, we can plot the impulse response curve

plot(
    1:T+1,
    gdp_ratio,
    ribbon = sem_gdp_ratio,
    fillalpha = 0.2,
    label = "",
    xlabel = "quarters",
    ylabel = "GDP change",
)
Example block output

We can save the figure using: savefig("gdp_shock.png")