A central bank must set nominal interest rate so as to minimize deviations of inflation rate and GDP gap from established targets.
A monetary authority wishes to control the nominal interest rate $x$ in order to minimize the variation of the inflation rate $s_1$ and the gross domestic product (GDP) gap $s_2$ around specified targets $s^∗_1$ and $s^∗_2$, respectively. Specifically, the authority wishes to minimize expected discounted stream of weighted squared deviations
\begin{equation} L(s) = \frac{1}{2}(s − s^∗)'\Omega(s − s^∗) \end{equation}where $s$ is a $2\times 1$ vector containing the inflation rate and the GDP gap, $s^∗$ is a $2\times 1$ vector of targets, and $\Omega$ is a $2 \times 2$ constant positive definite matrix of preference weights. The inflation rate and the GDP gap are a joint controlled exogenous linear Markov process
\begin{equation} s_{t+1} = \alpha + \beta s_t + \gamma x_t + \epsilon_{t+1} \end{equation}where $\alpha$ and $\gamma$ are $2 \times 1$ constant vectors, $\beta$ is a $2 \times 2$ constant matrix, and $\epsilon$ is a $2 \times 1$ random vector with mean zero. For institutional reasons, the nominal interest rate $x$ cannot be negative. What monetary policy minimizes the sum of current and expected future losses?
This is an infinite horizon, stochastic model with time $t$ measured in years. The state vector $s \in \mathbb{R}^2$ contains the inflation rate and the GDP gap. The action variable $x \in [0,\infty)$ is the nominal interest rate. The state transition function is $g(s, x, \epsilon) = \alpha + \beta s + \gamma x + \epsilon$
In order to formulate this problem as a maximization problem, one posits a reward function that equals the negative of the loss function $f(s,x) = −L(s)$
The sum of current and expected future rewards satisfies the Bellman equation
\begin{equation} V(s) = \max_{0\leq x}\left\{-L(s) + \delta + E_\epsilon V\left(g(s,x,\epsilon)\right)\right\} \end{equation}Given the structure of the model, one cannot preclude the possibility that the nonnegativity constraint on the optimal nominal interest rate will be binding in certain states. Accordingly, the shadow-price function $\lambda(s)$ is characterized by the Euler conditions
\begin{align} \delta\gamma'E_\epsilon \lambda\left(g(s,x,\epsilon)\right) &= \mu \\ \lambda(s) &= -\Omega(s-s^*) + \delta\beta'E_\epsilon \lambda\left(g(s,x,\epsilon)\right) \end{align}where the nominal interest rate $x$ and the long-run marginal reward $\mu$ from increasing the nominal interest rate must satisfy the complementarity condition \begin{equation} x \geq 0, \qquad \mu \leq 0, \qquad x > 0 \Rightarrow \mu = 0 \end{equation}
It follows that along the optimal path
\begin{align} \delta\gamma'E_\epsilon \lambda_{t+1} &= \mu_t \\ \lambda_t &= -\Omega(s_t-s^*) + \delta\beta'E_\epsilon \lambda_{t+1}\\ x \geq 0, \qquad \mu \leq 0, &\qquad x > 0 \Rightarrow \mu = 0 \end{align}Thus, in any period, the nominal interest rate is reduced until either the long-run marginal
reward or the nominal interest rate is driven to zero.
%matplotlib inline
from warnings import simplefilter
simplefilter('ignore')
from compecon import BasisChebyshev, DPmodel, BasisSpline
from compecon.quad import qnwnorm
from demos.setup import np, plt, demo
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
alpha = np.array([[0.9, -0.1]]).T # transition function constant coefficients
beta = np.array([[-0.5, 0.2], [0.3, -0.4]]) # transition function state coefficients
gamma = np.array([[-0.1, 0.0]]).T # transition function action coefficients
omega = np.identity(2) # central banker's preference weights
sbar = np.array([[1, 0]]).T # equilibrium targets
sigma = 0.08 * np.identity(2), # shock covariance matrix
delta = 0.9 # discount factor
n = 21
smin = [-2, -3]
smax = [ 2, 3]
basis = BasisChebyshev(n, smin, smax, method='complete',
labels=['GDP gap', 'inflation'])
There is only one action variable x: the nominal interest rate, which must be nonnegative.
def bounds(s, i, j):
lb = np.zeros_like(s[0])
ub = np.full(lb.shape, np.inf)
return lb, ub
def reward(s, x, i, j):
s = s - sbar # todo make sure they broadcast (:,ones(1,n))'
f = np.zeros_like(s[0])
for ii in range(2):
for jj in range(2):
f -= 0.5 * omega[ii, jj] * s[ii] * s[jj]
fx = np.zeros_like(x)
fxx = np.zeros_like(x)
return f, fx, fxx
def transition(s, x, i, j, in_, e):
g = alpha + beta @ s + gamma @ x + e
gx = np.tile(gamma, (1, x.size))
gxx = np.zeros_like(s)
return g, gx, gxx
The continuous shock must be discretized. Here we use Gauss-Legendre quadrature to obtain nodes and weights defining a discrete distribution that matches the first 6 moments of the Normal distribution (this is achieved with m=3 nodes and weights) for each of the state variables.
m = [3, 3]
mu = [0, 0]
[e,w] = qnwnorm(m,mu,sigma)
bank = DPmodel(basis, reward, transition, bounds,
x=['interest'], discount=delta, e=e, w=w)
Compute Unconstrained Deterministic Steady-State
sstar, xstar, pstar = bank.lqapprox(sbar, [0])
If Nonnegativity Constraint Violated, Re-Compute Deterministic Steady-State
if xstar < 0:
I = np.identity(2)
xstar = 0
sstar = np.linalg.solve(np.identity(2) - beta, alpha)
frmt = '\t%-21s = %5.2f'
print('Deterministic Steady-State')
print(frmt % ('GDP Gap', sstar[0]))
print(frmt % ('Inflation Rate', sstar[1]))
print(frmt % ('Nominal Interest Rate', xstar))
We solve the model by calling the solve
method in bank
. On return, sol
is a pandas dataframe with columns GDP gap, inflation, value, interest, and resid. We set a refined grid nr=5
for this output.
sol = bank.solve(nr=5)
sol.columns
To make the 3D plots, we need to reshape the columns of sol
.
gdp_gap, inflation, value, interest, resid = [x.reshape((5*n, 5*n)) for x in sol.values.T]
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(gdp_gap, inflation, interest, cmap=cm.coolwarm)
plt.xlabel('GDP gap')
plt.ylabel('Inflation')
# plt.zlabel('Nomianal Interest Rate') #zlabel not defined!
plt.title('Optimal Monetary Policy')
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(gdp_gap, inflation, value, cmap=cm.coolwarm)
plt.xlabel('GDP gap')
plt.ylabel('Inflation')
# plt.zlabel('Nomianal Interest Rate') #zlabel not defined!
plt.title('Value Function')
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_surface(gdp_gap, inflation, resid, cmap=cm.coolwarm)
plt.xlabel('GDP gap')
plt.ylabel('Inflation')
# plt.zlabel('Nomianal Interest Rate') #zlabel not defined!
plt.title('Bellman Equation Residual')