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.params
— TypeA 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`
HANK_MNS.steady_state
— TypeA 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).
HANK_MNS.transition_CompMkts
— TypeA 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`).
HANK_MNS.transition_full
— TypeA 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`).
HANK_MNS.EGM
— MethodEGM(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
.
HANK_MNS.EGM_SS
— MethodEGM_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.
HANK_MNS.aggregate_C_L
— Methodaggregate_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
.
HANK_MNS.check_steady_state
— Methodcheck_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.
HANK_MNS.egm_solve_constrained
— Methodegm_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
.
HANK_MNS.find_closest_lower
— Methodfind_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
.
HANK_MNS.forward_dist
— Methodforward_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.
HANK_MNS.forwardmat
— Methodforwardmat(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
.
HANK_MNS.get_cnbp
— Methodget_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
HANK_MNS.get_figures_3_4
— Methodget_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)
HANK_MNS.get_figures_5_6
— Methodget_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.
HANK_MNS.get_steady_state
— Methodget_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.
HANK_MNS.get_table_2
— Methodget_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.
HANK_MNS.get_transition_CompMkts
— Methodget_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))
HANK_MNS.get_transition_full
— Methodget_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.
HANK_MNS.inv_dist
— Methodinv_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.
HANK_MNS.lineartrans
— Methodlineartrans(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
.
HANK_MNS.logspaceshift
— Methodlogspaceshift(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.
HANK_MNS.logspaceshift
— Methodlogspaceshift(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.
HANK_MNS.logspaceshift
— Methodlogspaceshift(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.
HANK_MNS.makeknotd
— Methodmakeknotd(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.
HANK_MNS.makeknotd
— Methodmakeknotd(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.
HANK_MNS.margU
— FunctionmargU(c::Float64,par::params,order::Int64=1)
Helper function, computes marginal utility of consumption or second derivative of utility function (if order = 2).
HANK_MNS.reshape_c
— Methodreshape_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.
HANK_MNS.reshape_c
— Methodreshape_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.
HANK_MNS.set_parameters
— Methodset_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.
HANK_MNS.simulate_forward
— Methodsimulate_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
.
HANK_MNS.simulate_step
— Methodsimulate_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)
HANK_MNS.solve_for_transition
— Methodsolve_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.
HANK_MNS.solve_for_transition_CompMkts
— Methodsolve_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.
HANK_MNS.solveback
— Methodsolveback(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.
end