Replication of McKay, Nakamura, and Steinsson (2016)

This is a replication of "The Power of Forward Guidance" by Alisdair McKay, Emi Nakamura, and Jón Steinsson (henceforth MNS).

Authors: @mhaense1, @000martin

The project was part of our evaluation for the course Numerical Methods at SciencesPo Paris in Spring 2021.

The original paper studies the Forward Guidance, an unconventionary monetary tool, which, in standard representative agent New Keynesian models, has strong effects on current economic outcomes.

However, as MNS (and we using their model) find, Forward Guidance has considerably less power in models with heterogenous agents facing unisurable idiosyncratic income risk.

Conceptually, MNS compute perfect foresight transition paths of their simple Heterogenous Agents New Keynesian (HANK) model following a one-time announcement of an interest change far in the future (20 quarters in the paper).

Our Julia code is able to replicate their main exercises and produce equivalents of Figures 3,4,5 and 6 as well as Table 2 in their paper. Unfortunalety, it does not (yet) extend to their Zero Lower Bound Analysis featuring time-varying subjective discount factors (Section II.C in the paper).

How does it work?

In case you want to simply reproduce these main results, you use our convenience functions, which allow you to obtain the results as simple as follows:


using HANK_MNS

#Displays our replications of figures 3 and 4
get_figures_3_4()

#Saves a table containing our replication of table 2 
tb2 = get_table_2()

#Displays our replications of figures 5 and 6
#This may take quite a while, since ~20 transition paths must be computed
get_figures_5_6()

These convenience functions can also take inputs, allowing to analyze different time horizons or rate changes. See the documentation below for details.

In case one wants to produce a transition path with a custom calibration using the incomplete contracts model, one would proceed as follows:

using HANK_MNS

#Example: choose a higher aggregate asset level and higher income risk
p = set_parameters(B = 7.8, σ = 0.03^0.5)

#Find the \beta consistent with the target interest rate and asset level in steady state
#and obtain a corresponding steady state structure SS.
#Depending on your calibration, you might have to choose a different betaRange,
#e.g [0.97,0.995] instead of [0.95,0.99]. See function documentation for details. 
b, y, SS = get_steady_state(p,0.9,[0.95,0.99])

#set the correct \beta value
p.β = b

#obtain the transition path for the announcemnt of a 22 quarter ahead interest rate reduction of 60 basis points.
trp = get_transition_full(;TR = 22, RChange = -0.006)

#trp is a structure containing the transition paths for, amongst others, aggregate output, inflation and the wage rate.
#Make a table, plot it,...

Function documentation

HANK_MNS.paramsType
A structure that stores the parameter vector of the model, including all grids.
It can be automatically generayed using function `set_parameters()`.
It contains the following elements: 
..* The discount factor `β` 
..* The CRRA risk aversion parameter `γ` 
..* Yhe inverse Frisch elasticity `ψ`
..* The labor disutility scaling parameter `ψ1` (only used in complete markets case)
..* The target asset level `B`
..* The markup/CES production function parameter `μ`
..* The Calvo price re-set probability `θ`
..* The target interest rate `Rbar`
..* The number of grid points on the labor productivity grid `nz`
..* The labor productivity grid `z`
..* The labor productivity state Markov transition matrix `Πz`
..* The invariant distribution over labor productivty states `Γ`
..* The tax weights `tax_weights`
..* The borrowing constraint `a_min`
..* The maximum value on the asset grid `a_max`
..* The number of points on the asset grid used for computing the wealth distribution `nk` 
..* The number of points on the asset grid used for computing HH policy functions `nb` 
..* The asset grid used for computing the wealth distribution `k_grid`
..* The asset grid used for computing the HH policy functions `b_grid`
source
HANK_MNS.steady_stateType
A structure that saves the steady state of the incomplete markets model.
It contains the household policy functions (`c_polcicies`), the invariant distribution of wealth (`D`),
output (`Y`), consumption (`C`), hours worked (`L`), aggregate assets (`K`), the wage (`w`), the tax rate (`τ`),
the dividebd (`div`) and the interest rate (`Rbar`). Note that `C`, `L` and `Y` should be equal (approximately).
source
HANK_MNS.transition_CompMktsType
A structure that stores the complete markets transition path, including the time paths of price dispersion (`S`), the wage (`w`),
inflation (`pΠ`), output (`Y`), the interest rate (`R`),  and the dividend (`div`).
source
HANK_MNS.transition_fullType
A structure that stores the incomplete markets model transition path, including the time paths of price dispersion (`S`), the wage (`w`),
inflation (`pΠ`), output (`Y`), the interest rate (`R`), the tax rate (`τ`) and the dividend (`div`).
source
HANK_MNS.EGMMethod
EGM(c_next::Array{Float64,2},β::Float64, Rs::Array{Float64,1}, ws::Array{Float64,1},
τs::Array{Float64,1}, div::Array{Float64,1},p::params)

Conducts one iteration on consumption and labor supply using the endogenous grid method (EGM). Inputs need to be 2x1 arrays of wage, interest, etc. for the current and next period, as well as the HH policy function c_next for the next period (or iteration).

This function is used in the function solveback calls get_cnbp as well as egm_solve_constrained.

source
HANK_MNS.EGM_SSMethod
EGM_SS(c_guess::Array{Float64,2}, β::Float64,Y::Float64, p::params; T::Int=30, 
       maxit::Int = 500, tol::Float64 = 1e-7)

Using an initial guess c_guess, discount factor eta, aggregate output level Y and parameter structure p, this function computes the household consumption policy functions, using the function solveback.

Note that steady state factor prices can be computed from Y and don't need to be supplied separately.

The default values for T (the number of times policy function is iterated over during the solveback call), maxit (maximum number of iterations) and tol (tolerance value for convergence) are as in the MNS paper.

source
HANK_MNS.aggregate_C_LMethod
aggregate_C_L(D::Array{Float64,1}, c_policies::Array{Float64,1}, R::Float64, 
              w::Float64, τ::Float64, div::Float64)

Given wealth distribution D and HH consumption policy function c_policies as well as factor prices, this function computes the implied consumption and labor supply of the household sector.

The function uses the function get_cnbp.

source
HANK_MNS.check_steady_stateMethod
check_steady_state(β::Float64,Y::Float64,p::params,return_distance::Bool=false)

NOTE: This function is not used in the current implemenation of the model, so it is not extensively documented.

For given β, Y and parameters, this function computes consumption and labor suppy as implied by the household decisions and returns the distance between output implied by labor and output implied by consumption as well as the distance between household asset choices and the asset target. It can be used in an Root-Finding algorithm to find the β corresponding to the asset and interest rate targets, as in the MNS code.

However, we chose to use a simpler bisection algorithm as in get_steady_state instead.

source
HANK_MNS.egm_solve_constrainedMethod
egm_solve_constrained(bs::Array{Float64,1},ip::Int,w::Float64,τ::Float64,
                      div::Float64,R::Float64,p::params)

Backs out consumption level of constrained household using the same iterative procedure as MNS. Used in the function EGM.

source
HANK_MNS.find_closest_lowerMethod
find_closest_lower(x::Float64,grid::Array{Float64,1})

Helper function for lineartrans. For a given savings choice x, it finds the nearest point on the asset grid that is lower or equal than x.

source
HANK_MNS.forward_distMethod
forward_dist(D::Array{Float64,1},Pi::SparseMatrixCSC)

Calculates asset distribution in next period given transition matrix Pi and current distribution D. This is just multiplying a (sparse) matrix with a vector.

source
HANK_MNS.forwardmatMethod
forwardmat(c_opt::Array{Float64,2},R::Float64,w::Float64,τ::Float64,div::Float64)

Generates a (sparse) transition matrix for the aggregate wealth distribution, using the household policy function and factor prices.

Uses the helper functions get_cnbp and lineartrans.

source
HANK_MNS.get_cnbpMethod
get_cnbp(xthis::Array{Float64},c::Array{Float64},R::Float64,w::Float64,
         τ::Float64,p::params,inc_idx::Int)

Helper function that, given the HH policy function and factor prices, taxes, etc., interpolates the HH policy function for an agent income state inc_idx to the values in xthis and computes corresponding labor supply and savings.

Returns interpolated consumption policies c and correspoding labor supply n and savings bp

source
HANK_MNS.get_figures_3_4Method
get_figures_3_4(;TR::Int64 = 20, T::Int64 = 200, RChange::Float64 = -0.005)

Convenience function that replicates Figures 3 and 4 from the MNS paper.

Just running get_figures_3_4() will display equivalents to Figures 3 and 4 in the MNS paper.

The function relies on set_parameters, get_steady_state, get_transition_full and get_transition_CompMkts.

Optional argument: Time horizon of interest rate change (TR). Size of interest rate change (RChange)

source
HANK_MNS.get_figures_5_6Method
get_figures_5_6(; Horizon::StepRange{Int64,Int64} = 1:2:41, T::Int64 = 200, RChange::Float64 = -0.005)

Convenience function that replicates Figures 5 and 6 from the MNS paper.

Just running get_figures_5_6() will display equivalents to Figures 5 and 6 in the MNS paper.

The function relies on set_parameters, get_steady_state, get_transition_full and get_transition_CompMkts.

Optional argument: ..* Horizons of interest rate changes to consider (Horizon), to be supplied as StepRange. ..* Total length of transition period to compute (T), i.e. assuming that after T periods economy will be back in SS ..* Size of interest rate change (RChange).

All default values corresppond to the values used by MNS.

source
HANK_MNS.get_steady_stateMethod
get_steady_state(p::params,Y_guess::Float64,beta_Range::Array{Float64,1};tolβ::Float64=1e-6,tolY::Float64=1e-6)

Finds the steady state for given a target interest rate and asset level (conatined in the parameter structure p) by iterating over β, using a simple bisection type updating algorithm.

It calls the function EGM_SS, forwardmat, inv_dist and aggregate_C_L.

For the biscetion algorithm, it is necessary to supply and upper and lower bound for β as betaRange, so that aggregate household savings are higher than the target asset level for the upper bound and lower for the lower bound.

I typically used Y = 0.6 as guess for Y and beta_Range = [0.95,0.99] for the baseline calibration and [0.97,0.995] for the high asset calibration, which works fine. Y_guess not particularly important, I typically use 0.6 as do MNS.

The function returns the value of β that is consistent with a steady state with the target interest rate and asset level, the output level of said steady state and a steady_state structure.

The default tolerance levels are as in the MNS paper.

source
HANK_MNS.get_table_2Method
get_table_2(;Horizon::Int=20,T::Int=200,RChange::Float64 = -0.005)

Convenience function that replicates Table 2 from the MNS paper.

Just running get_table_2() will return an equivalent to Table 2 as a table object.

The function relies on set_parameters, get_steady_state, get_transition_full and get_transition_CompMkts.

Optional argument: ..* Time horizon of interest rate change (TR) ..* Total length of transition period to compute (T), i.e. assuming that after T periods economy will be back in SS ..* Size of interest rate change (RChange)

Default values correspond to the values chosen by MNS.

Note: The returned values will not be identical to the ones obtained by MNS.

However, notice that chosen unit is Basis Points (=0.01 percent), so the actual numerical difference between the MNS solution and ours is small.

source
HANK_MNS.get_transition_CompMktsMethod
get_transition_CompMkts(TR::Int, T::Int, p::params; RChange::Float64 = -0.005)

Solves for the complete markets transition path. Inputs: TR (time until single-quarter interest rate change), T (time horizon transition path), (Change of R at time TR))

source
HANK_MNS.get_transition_fullMethod
get_transition_full(TR::Int64,T::Int64,p::params,SS::steady_state; RChange::Float64 = -0.005)

Convenience function to solve for a transition path. Relies on function solve_for_transition.

Always uses the steady state factor prices, price dispersion, etc. as initial guess for the transition path.

Inputs: TR (time until single-quarter interest rate change), T (time horizon transition path), p (parameter structure), SS (steady state structure), RChange (Change of R at time TR). The default RChange value corresponds to the 50 basis points decrease considered in the MNS paper.

source
HANK_MNS.inv_distMethod
inv_dist(Π::SparseMatrixCSC)

Find invariant steady state wealth distribution by solving for the eigenvector corresponding to the unit eigenvalue of the (sparse) wealth transition matrix.

The idea for a procedure to do this efficiently was taken from the following discussion in the Julia forum.

source
HANK_MNS.lineartransMethod
lineartrans(bp::Array{Float64,1},par::params)

Calculates the new positions and transition probabilities on the asset grid for given savings choices. Used the to conduct a non-stochastic simulation procedure for the wealth distribution a la Young (2010, JEDC).

Uses the helper function find_closest_lower.

source
HANK_MNS.logspaceshiftMethod
logspaceshift(xa::Float64,xb::Float64,n::Int,x2::Float64,n_at_x2::Float64)

A function involved in replicating a log-spaced asset grids exactly as in the MNS code. Several methdos available.

source
HANK_MNS.logspaceshiftMethod
logspaceshift(xa::Float64,xb::Float64,n::Int,x2::Float64)

A function involved in replicating a log-spaced asset grids exactly as in the MNS code. Several methdos available. This is one of them.

source
HANK_MNS.logspaceshiftMethod
logspaceshift(xa::Float64,xb::Float64,n::Int)

A function involved in replicating a log-spaced asset grids exactly as in the MNS code. Several methdos available. This is one of them.

source
HANK_MNS.makeknotdMethod
makeknotd(kmin::Float64,kmax::Float64,n::Int,logshift::Float64)

A Function that produces an asset grid using the function logspaceshift. Several methods available. This is one of them.

source
HANK_MNS.makeknotdMethod
makeknotd(kmin::Float64,kmax::Float64,n::Int,logshift::Float64)

A Function that produces an asset grid using the function logspaceshift. Several methods available. This os one of them.

source
HANK_MNS.margUFunction
margU(c::Float64,par::params,order::Int64=1)

Helper function, computes marginal utility of consumption or second derivative of utility function (if order = 2).

source
HANK_MNS.reshape_cMethod
reshape_c(c::Array{Float64,2},par::params)

Helper functions that gets the HH consumption policy functions into a different form. If they are supplied as a nb*nz matrix, they will be turned into a vector of length nb+nz and vice versa. This is the method doing the former.

source
HANK_MNS.reshape_cMethod
reshape_c(c::Array{Float64},p::params)

Helper functions that gets the HH consumption policy functions into a different form. If they are supplied as a nb*nz matrix, they will be turned into a vector of length nb+nz and vice versa. This is the method doing the latter.

source
HANK_MNS.set_parametersMethod
set_parameters(;β::Float64 = 0.986,    # Discount factor
                γ::Float64 = 2.0,      # Risk aversion
                ψ::Float64 = 2.0,      # Inverse Frisch elasticity
                ψ1::Float64 = 1.0,     # Labor disutility scaling (only in complete markets)
                B::Float64 = 5.5,      # Supply of Assets: 1.4 times annual GDP = 5.6 times quarterly GDP
                μ::Float64 = 1.2,      # Mark-up
                θ::Float64 = 0.15,     # Probability that Calvo fairy visits
                Rbar::Float64 = 1.005, # Target quarterly interest rate
                nz::Int64 = 3,         # Number of income grid states
                ρ::Float64 = 0.96566,            # Persistence parameter income process
                σ::Float64 = 0.01695^0.5,        # Std of income process random comp
                tax_weights::Array{Float64,1} = [0.0,0.0,1.0],   # Tax weights
                a_min::Float64  = 0.0,           # Borrowing constraint
                a_max::Float64 =75.0,            # Maximum asset holdings in grid
                x_min::Float64 = 0.001,          # Parameter to generate the consumption knots
                nk::Int64 = 1000,                # Number of grid points used for quadrature grid
                nb::Int64 = 200)

Function to construct the parameter structure. The default parameters correspond to the baseline calibration from the MNS paper. The function applies the Rouwenhorst method (using its QuantEcon implementation) to discretize the AR(1) process for (log) labor productivity with persistence ρ and innovation variance σ. Note that while eta is set automatically, it will typically be replaced after the steady state is computed, since β is numerically computed to match a target interest rate and aggregate asset level.

To generate the asset grids, the functions makeknotd and logspaceshift are used.

source
HANK_MNS.simulate_forwardMethod
simulate_forward(D0::Array{Float64,1},cpol_path::Array{Float64,2},Rpath::Array{Float64,1},
                 wpath::Array{Float64,1},div_path::Array{Float64,1},τ_path::Array{Float64,1},p::params)

Given an initial wealth distribution D0, household policy functions cpol_path and prices for factor prices, taxes and dividends, this function simulates the implied aggregate consumption, labor supply and asset holdings for a transition period.

Relies on the function aggregate_C_L and forward_dist.

source
HANK_MNS.simulate_stepMethod
simulate_step(D::Array{Float64,1},c_pol::Array{Float64,2},R::Float64,w::Float64,
              τ::Float64,div::Float64,p::params)

Conducts forward simulation for one period. Helper function to simulateforward, equivalent to simulatestep() in MNS code.Originally used in loop in Simulateforward, replaced it to do better pre-allocation there.

Since the function is not used in the final implementation, no more documentation. (The function is in principle usable though.)

The following was originally in the simulate forward loop:

Cpath[t], Lpath[t], Bpath[t], Dpath[:,t+1] = simulate_CLB(Dpath[:,t],reshape_c(cpol_path[:,t],p),Rpath[t],wpath[t],τ_path[t],div_path[t],p)

source
HANK_MNS.solve_for_transitionMethod
solve_for_transition(Rpath::Array{Float64,1},wguess::Array{Float64,1},div_path::Array{Float64,1},
                     Spath::Array{Float64,1},SS::steady_state,p::params,S_tol::Float64=1e-6,w_tol::Float64=1e-6)

Solves for the perfect foresight transition path for a given interest interest rate path Rpath, assuming that the economy will be back in steady state as of the final entry of Rpath.

The other _path inputs constitue initial guesses.

Given initial guesses, the function applies an iterative procedure to solve for the equilibrium transition path of wages and price dispersion. Specifically, it first iteratively solves for a wage path so that, given the current guess for the path of S, output is (approximately) equal to labor supply. By Walras' law, this means that the asset market must be cleared as well.

Afterwards, the algorithm computes the price dispersion path S implied by the wage and output paths, which will be the guess of S for the next iteration.

The function relies on the functions solveback and simulate_forward.

Default tolerance values correspond to the values used for the MNS code.

source
HANK_MNS.solve_for_transition_CompMktsMethod
solve_for_transition_CompMkts(Rpath, wagepath, dividendpath, Spath, stst, name, p::params)

Solves for the complete markets transition path for a given interest rate path, wpage path, dividend path, etc.

source
HANK_MNS.solvebackMethod
solveback(c_final::Array{Float64,1},w_path::Array{Float64,1},R_path::Array{Float64,1},
          τ_path::Array{Float64},div_path::Array{Float64,1},β::Float64,p::params)

Solves household problem backwards from a period in which HH policy functions are know (e.g. steady state), for given time paths of factor prices, taxes and dividends, using the Endogenous Grid Method (EGM).

It is also used for computing the steady state policy function, for which c_final would be the current guess for the policy function and for which it iterates T (length of w_path) times over the policy function.

This function is used in EGM_SS as well as solve_for transition and calls the function EGM for conducting an individual EGM step.

In the supplied objects w_path, τ_path etc., the final values must correspond to the period to solve back from, e.g. the steady state.

source

end