38. Which Moments to Match?#

38.1. Overview#

Some structural economic models have the following two properties:

  • it is possible to compute an equilibrium and simulate it, given a known parameter vector

  • it is not possible to write down a likelihood function analytically

We can solve “the direct problem,” which maps a parameter vector into a probability distribution of the data.

We cannot use maximum likelihood to solve “the inverse problem,” which maps an observed data set into an estimate of the parameter vector.

Researchers with such models turn to the generalized method of moments of Hansen [1982] and compute the moments by simulation [Duffie and Singleton, 1993, McFadden, 1989, Pakes and Pollard, 1989].

They must decide which moments to match.

A. Ronald Gallant and George Tauchen studied this question in Gallant and Tauchen [1996].

They put two models on the table:

  • the economist’s structural model, indexed by a parameter vector \(\rho\), whose likelihood function cannot be written down

  • a distinct, descriptive auxiliary model, indexed by a parameter vector \(\theta\), whose likelihood function can be written down and maximized

The two parameter vectors usually differ in dimension and always differ in meaning.

  • \(\rho\) specifies the preferences, technologies, constraint sets and other features of the agents inside the structural model. The economist wants to learn \(\rho\).

  • \(\theta\) summarizes the observed data. Its components answer no economic question. The economist estimates \(\theta\) only as a step toward estimating \(\rho\).

structural model

auxiliary model

density

\(p(y_t \mid x_t, \rho)\)

\(f(y_t \mid x_t, \theta)\)

parameter

\(\rho \in \mathbb{R}^{k_\rho}\)

\(\theta \in \mathbb{R}^{k_\theta}\)

interpretation

economic

descriptive

likelihood

unavailable

available

role

object of inference

source of moment conditions

Here \(x_t\) collects lagged values of \(y_t\), possibly the whole history.

In the stochastic volatility example below, \(\rho = (a, r_w, r_y)\) governs a latent log-volatility process, while \(\theta = (\mu, \omega, \alpha, \beta)\) holds the parameters of a GARCH(1,1) model.

No component of \(\theta\) corresponds to a component of \(\rho\).

Gallant and Tauchen’s method uses the scores associated with the likelihood function of the auxiliary model as the moment vector for GMM.

They fit \(\theta\) to the observed data once.

They then choose \(\rho\) so that, in data simulated from the structural model, the auxiliary score evaluated at that fitted \(\theta\) averages as close to zero as possible.

Note

Section Why the score is the right thing to match shows that the score moment conditions are the negative gradient of a Kullback-Leibler divergence. Statistical Divergence Measures and Likelihood Ratio Processes study properties of the KL divergence.

Gallant and Tauchen prove that if the auxiliary model nests the structural model, their estimator is as efficient as maximum likelihood.

They argue that if the auxiliary model closely approximates the distribution of the data, the estimator is nearly as efficient.

Practitioners call their method the efficient method of moments, or EMM.

Gallant and Tauchen [2010] surveys the method and its applications.

Let’s start with some imports.

import numpy as np
import matplotlib.pyplot as plt
from numba import jit
from scipy.optimize import minimize
from scipy.stats import chi2

38.2. The estimation problem#

A structural model is a sequence of probability densities for an observed process \(\{y_t\}\),

\[ p(y_t \mid x_t, \rho), \qquad \rho \in \mathcal{R} \subset \mathbb{R}^{k_\rho} , \]

where \(x_t\) collects whatever the model conditions on, usually lagged values of \(y_t\).

We call \(\rho\) the structural parameter vector.

Nature generated the observed data \(\{\tilde y_t, \tilde x_t\}_{t=1}^n\) from \(\rho^0\).

Tildes mark observed data and statistics computed from them.

Hats mark simulated data and the final estimator \(\hat\rho_n\).

We assume two things about the structural model.

Simulation is feasible: given \(\rho\), we can generate a long artificial series \(\{\hat y_\tau, \hat x_\tau\}_{\tau=1}^N\).

The likelihood is unavailable: \(\prod_t p(\tilde y_t \mid \tilde x_t, \rho)\) has no expression that we can evaluate and maximize.

Gallant and Tauchen take a stationary Markov structural model as the leading case.

A single long simulation then suffices to compute any expectation under the model.

Their paper also covers independent cross-sectional data and processes whose laws of motion depend on \(t\).

38.3. The auxiliary model and its score#

An auxiliary model is a second sequence of densities for the same observed process,

\[ f(y_t \mid x_t, \theta), \qquad \theta \in \Theta \subset \mathbb{R}^{k_\theta} . \]

We call \(\theta\) the auxiliary parameter vector.

Gallant and Tauchen call the auxiliary model the score generator.

Two properties make an auxiliary model useful.

Its density has an analytic expression, so we can write down its likelihood and differentiate it.

It describes the data well.

The first property is required.

Consistency requires only that the scores identify \(\rho\), as Exercise 38.1 and Exercise 38.3 show.

Section Choosing the score generator explains what a close fit buys.

A GARCH model of asset returns, a vector autoregression, and a Hermite series expansion of a conditional density all qualify.

The score of the auxiliary model is the derivative of its log density with respect to its own parameters,

\[ s(y, x, \theta) = \frac{\partial}{\partial \theta} \ln f(y \mid x, \theta) . \]

The score is a \(k_\theta \times 1\) vector that we can evaluate at any \((y, x, \theta)\).

38.4. The estimator#

The estimator takes three steps.

Step 1. Projection. Fit the auxiliary model to the observed data by quasi-maximum likelihood,

\[ \tilde \theta_n = \arg\max_{\theta \in \Theta} \frac{1}{n} \sum_{t=1}^n \ln f(\tilde y_t \mid \tilde x_t, \theta) . \]

This step uses the data and the auxiliary model alone, so it happens once, before any structural parameter is estimated.

Step 2. Moments by simulation. Define the moment function \(m(\rho, \theta)\) as the expectation under the structural model of the auxiliary score,

(38.1)#\[m(\rho, \theta) = \int \! \! \int s(y, x, \theta) \, p(y \mid x, \rho) \, dy \, p(x \mid \rho) \, dx ,\]

where \(p(x \mid \rho)\) is the stationary density of \(x_t\) under the structural model.

A long simulation approximates the double integral at \(\theta = \tilde\theta_n\),

(38.2)#\[m(\rho, \tilde \theta_n) \approx \frac{1}{N} \sum_{\tau=1}^{N} s(\hat y_\tau, \hat x_\tau, \tilde \theta_n)\]

The right side of (38.2) averages the auxiliary score over a long simulation from the structural model at \(\rho\).

Each evaluation requires a simulation and no optimization.

Step 3. GMM. Estimate \(\rho\) by making the moment vector as close to zero as the GMM metric allows,

(38.3)#\[\hat \rho_n = \arg\min_{\rho \in \mathcal{R}} \; m(\rho, \tilde \theta_n)^\top \, (\tilde{\mathcal{I}}_n)^{-1} \, m(\rho, \tilde \theta_n) ,\]

where

(38.4)#\[\tilde{\mathcal{I}}_n = \frac{1}{n} \sum_{t=1}^n s(\tilde y_t, \tilde x_t, \tilde \theta_n) \, s(\tilde y_t, \tilde x_t, \tilde \theta_n)^\top .\]

The weighting matrix (38.4) is the outer product of the auxiliary scores at the data.

It estimates \(\mathcal{I}^0\), the asymptotic variance of \(n^{-1/2} \sum_{t=1}^n s(\tilde y_t, \tilde x_t, \theta^0)\), where \(\theta^0\) is the probability limit of \(\tilde\theta_n\).

It depends on the auxiliary model and the data alone.

The outer product is a valid estimate when the scores are serially uncorrelated.

An auxiliary model that describes the conditional distribution of the data correctly delivers serially uncorrelated scores.

Gaussian quasi-maximum likelihood scores are also serially uncorrelated when the auxiliary model specifies the conditional mean and variance correctly [Bollerslev and Wooldridge, 1992].

Otherwise Gallant and Tauchen recommend a heteroskedasticity and autocorrelation consistent estimate with Parzen weights [Gallant, 1987], and they cite Andrews [1991] for alternatives.

Exercise 38.4 shows how far the outer product can understate \(\mathcal{I}^0\) when the scores are serially correlated.

A researcher computes either estimate once, before the search over \(\rho\) begins.

GMM usually requires a preliminary estimate of the structural parameters to form an efficient weighting matrix, and with either of these estimates that requirement disappears.

The order condition for identification is \(k_\theta \ge k_\rho\).

The order condition is necessary but not sufficient, and Exercise 38.3 constructs a failure.

38.5. Why the score is the right thing to match#

Statistical Divergence Measures shows that maximum likelihood minimizes the KL divergence from the empirical distribution of the data to the parametric model.

Step 1 projects the data onto the auxiliary family by minimizing KL divergence.

For each \(\rho\), define the pseudo-true value

\[ \theta^0(\rho) = \arg\min_{\theta \in \Theta} \; D_{KL}\bigl( p(\cdot \mid \rho) \, \| \, f(\cdot \mid \theta) \bigr) , \]

the point in the auxiliary family closest to the structural model at \(\rho\) [White, 1982] [1].

The function \(\theta^0(\cdot)\) maps structural parameters into auxiliary parameters.

As \(n\) grows, \(\tilde\theta_n\) converges to \(\theta^0 = \theta^0(\rho^0)\).

Now differentiate the divergence with respect to \(\theta\):

(38.5)#\[\frac{\partial}{\partial \theta} D_{KL}\bigl( p(\cdot \mid \rho) \, \| \, f(\cdot \mid \theta) \bigr) = \frac{\partial}{\partial \theta} \left[ \int p \ln p - \int p \ln f_\theta \right] = - \, m(\rho, \theta)\]

The moment function (38.1) is the negative gradient of a Kullback-Leibler divergence.

Setting \(m(\rho, \theta) = 0\) states the first-order condition for a KL projection.

The estimator (38.3) searches for the structural parameter whose KL projection onto the auxiliary family lands where the data’s projection landed, or as close as the GMM metric allows when \(k_\theta > k_\rho\).

This reading explains why the structural model need not nest inside the auxiliary model.

At \(\rho = \rho^0\) the structural model generated the data, the two projections coincide, and \(m(\rho^0, \theta^0) = 0\).

Exercise 38.1 asks you to verify this chain of reasoning.

38.6. Efficiency#

Consistency and asymptotic normality hold under weak regularity conditions,

\[ \sqrt{n} \, (\hat \rho_n - \rho^0) \to N \Bigl( 0, \bigl[ (M^0)^\top (\mathcal{I}^0)^{-1} M^0 \bigr]^{-1} \Bigr) , \qquad M^0 = \frac{\partial}{\partial \rho^\top} m(\rho^0, \theta^0) , \]

which is Theorem 1 of Gallant and Tauchen [1996].

Here \(\mathcal{I}^0\) is the variance of the auxiliary score that \(\tilde{\mathcal{I}}_n\) in (38.4) estimates.

The formula assumes that the weighting matrix converges to \((\mathcal{I}^0)^{-1}\), as \((\tilde{\mathcal{I}}_n)^{-1}\) does when \(\tilde{\mathcal{I}}_n\) estimates \(\mathcal{I}^0\) consistently.

With any other limiting weighting matrix \(W\), the asymptotic variance takes the sandwich form

\[ \bigl[ (M^0)^\top W M^0 \bigr]^{-1} (M^0)^\top W \, \mathcal{I}^0 \, W M^0 \bigl[ (M^0)^\top W M^0 \bigr]^{-1} . \]

Exercise 38.4 uses it.

Gallant and Tauchen assume that \(N\) is large enough to make simulation error negligible, so the formula treats \(m\) as computed exactly.

A simulation of length \(N\) adds independent noise to \(m\) and multiplies the asymptotic variance of \(\sqrt{n}\, m\), and hence of \(\hat\rho_n\), by \(1 + n/N\).

Gallant and Tauchen say that the structural model is smoothly embedded within the auxiliary model when a twice continuously differentiable map \(g\) carries structural parameters into auxiliary parameters,

\[ p(y \mid x, \rho) = f\bigl( y \mid x, g(\rho) \bigr) \qquad \text{for every } \rho \text{ near } \rho^0 . \]

Smooth embedding says that the auxiliary model nests the structural model.

In general \(p(\cdot \mid \rho)\) lies outside the auxiliary family, and no \(g\) satisfies this equation.

The function \(\theta^0(\rho)\) always exists.

Under smooth embedding \(\theta^0(\rho) = g(\rho)\).

Write \(G^0 = \partial g(\rho^0) / \partial \rho^\top\).

Their Theorem 2 states that smooth embedding implies

\[ \sqrt{n} \, (\hat \rho_n - \rho^0) \to N \Bigl( 0, \bigl[ (G^0)^\top \mathcal{I}^0 G^0 \bigr]^{-1} \Bigr) , \]

which is the asymptotic distribution of the maximum likelihood estimator of \(\rho\).

The proof turns on the identity \(M^0 = \mathcal{I}^0 G^0\), which converts the GMM variance into the ML variance.

The estimator attains maximum likelihood efficiency without the structural likelihood.

Smooth embedding is a strong assumption, and consistency and asymptotic normality do not require it.

Gallant and Tauchen argue that an auxiliary model that approximates the distribution of the data closely without nesting the structural model delivers an estimator that is nearly fully efficient.

Gallant and Tauchen read this as instruction about how to choose the auxiliary model: make it fit.

38.7. Comparison with matching the auxiliary parameters#

Indirect inference names the class of estimators that compare observed and simulated data through an auxiliary model.

Gourieroux et al. [1993] and Smith [1993] match the auxiliary parameters.

Gourieroux et al. [1993] call \(\theta^0(\rho)\) the binding function.

Their estimator is

\[ \hat \rho_{II} = \arg\min_{\rho} \; \bigl[ \tilde\theta_n - \theta^0(\rho) \bigr]^\top \, \Omega \, \bigl[ \tilde\theta_n - \theta^0(\rho) \bigr] \]

for a weighting matrix \(\Omega\).

Under smooth embedding \(\theta^0 = g\), and the choice \(\Omega = \mathcal{I}^0\) yields the minimum chi-square estimator, which is as efficient as maximum likelihood.

The binding function is unavailable whenever the structural likelihood is unavailable, so a practitioner replaces \(\theta^0(\rho)\) by the average of auxiliary estimates computed on \(H\) simulated data sets.

That replacement is expensive.

Each trial value of \(\rho\) requires \(H\) separate maximizations of the auxiliary likelihood, one per simulated data set.

The score-based estimator requires exactly one maximization of the auxiliary likelihood, in Step 1, and never repeats it.

With suitably chosen weighting matrices, the parameter-based and score-based estimators are asymptotically equivalent [Gourieroux et al., 1993].

The advantage of matching scores is computational.

Finite-sample behavior can differ.

Duffee and Stanton [2008] study highly persistent Gaussian autoregressions and find that score matching yields confidence intervals that are wider than those of parameter matching yet less likely to cover the true value.

They trace the problem to the large role that the population variance plays in the score moment conditions.

38.8. Choosing the score generator#

A model known to describe the data accurately should be the auxiliary model.

Lacking one, Gallant and Tauchen recommend two general purpose families.

The seminonparametric or SNP density of Gallant and Nychka [1987] and Gallant and Tauchen [1989] multiplies a Gaussian density by a squared Hermite polynomial whose coefficients depend on lagged values.

Tuning parameters nest an IID Gaussian, a Gaussian VAR, Gaussian ARCH and GARCH, and fully nonlinear nonparametric specifications, so a specification search selects the auxiliary model from the data [Gallant and Tauchen, 2010].

The neural net score approximates any nonlinear autoregression, including deterministic chaos.

Both families are series expansions, so adding terms drives the approximation error to zero.

Adding terms also drives the conditional mean of the score toward zero, so the scores approach martingale differences.

The outer product (38.4) then estimates \(\mathcal{I}^0\) accurately, and the weighting matrix needs no correction for serial correlation.

A close fit therefore buys two things: near efficiency and a valid outer-product weighting matrix.

Exercise 38.4 shows what happens with a score generator that lacks the second.

The number of terms governs a trade-off between bias and variance.

More terms reduce the approximation error of the auxiliary model and raise the sampling variance of \(\tilde\theta_n\).

Gallant and Tauchen warn against taking too many terms and then claiming that standard asymptotics apply.

38.9. A first example: an AR(1) inside an AR(2)#

An example in which we can compute the maximum likelihood estimator lets us check Theorem 2.

Take as the structural model a Gaussian first-order autoregression,

\[ y_t = \rho_1 y_{t-1} + \sigma \epsilon_t , \qquad \epsilon_t \sim N(0,1) , \]

with \(\rho = (\rho_1, \sigma)^\top\).

Take as the auxiliary model a Gaussian second-order autoregression,

\[ y_t = \theta_1 y_{t-1} + \theta_2 y_{t-2} + \varsigma e_t , \qquad e_t \sim N(0,1) , \]

with \(\theta = (\theta_1, \theta_2, \varsigma)^\top\).

The AR(2) nests the AR(1) through \(g(\rho_1, \sigma) = (\rho_1, 0, \sigma)\), so the structural model is smoothly embedded and \(k_\theta = 3\) exceeds \(k_\rho = 2\).

The components \(\theta_1\) and \(\rho_1\) share an interpretation only because the AR(2) nests the AR(1).

Theorem 2 predicts that our estimator and the maximum likelihood estimator have the same asymptotic distribution, up to the simulation factor \(1 + n/N\).

Writing \(u_t = y_t - \theta_1 y_{t-1} - \theta_2 y_{t-2}\), the auxiliary score is

\[\begin{split} s(y_t, x_t, \theta) = \begin{bmatrix} u_t y_{t-1} / \varsigma^2 \\ u_t y_{t-2} / \varsigma^2 \\ -1/\varsigma + u_t^2 / \varsigma^3 \end{bmatrix} . \end{split}\]
@jit(nopython=True)
def simulate_ar1(ρ1, σ, ε):
    "Simulate the structural AR(1) from a fixed array of shocks ε."
    n = ε.size
    y = np.empty(n)
    y[0] = σ * ε[0] / np.sqrt(1 - ρ1**2)
    for t in range(1, n):
        y[t] = ρ1 * y[t-1] + σ * ε[t]
    return y

def ar2_scores(θ, y):
    "Per-observation scores of the Gaussian AR(2) auxiliary model."
    θ1, θ2, ς = θ
    yt, y1, y2 = y[2:], y[1:-1], y[:-2]
    u = yt - θ1 * y1 - θ2 * y2
    return np.column_stack([u * y1 / ς**2, u * y2 / ς**2, -1/ς + u**2 / ς**3])

def ar2_qmle(y):
    "Quasi-maximum likelihood estimates of the AR(2), available in closed form."
    X = np.column_stack([y[1:-1], y[:-2]])
    b = np.linalg.lstsq(X, y[2:], rcond=None)[0]
    ς = np.sqrt(np.mean((y[2:] - X @ b)**2))
    return np.array([b[0], b[1], ς])

Let’s generate data from the structural model and compute the conditional maximum likelihood estimator, which for this model is least squares.

rng = np.random.default_rng(0)
T, ρ1_0, σ_0 = 1000, 0.8, 1.0
y_data = simulate_ar1(ρ1_0, σ_0, rng.standard_normal(T))

X1 = y_data[:-1]
ρ1_mle = float(X1 @ y_data[1:] / (X1 @ X1))
σ_mle = float(np.sqrt(np.mean((y_data[1:] - ρ1_mle * X1)**2)))
print(f"maximum likelihood:  ρ1 = {ρ1_mle:.4f}   σ = {σ_mle:.4f}")
maximum likelihood:  ρ1 = 0.8022   σ = 0.9784

Step 1 fits the auxiliary model and forms the weighting matrix.

θ_tilde = ar2_qmle(y_data)
S = ar2_scores(θ_tilde, y_data)
I_tilde = S.T @ S / S.shape[0]
W = np.linalg.inv(I_tilde)

print(f"auxiliary QMLE: θ1 = {θ_tilde[0]:.4f}, θ2 = {θ_tilde[1]:.4f}, ς = {θ_tilde[2]:.4f}")
auxiliary QMLE: θ1 = 0.8083, θ2 = -0.0075, ς = 0.9789

The estimate of \(\theta_2\) sits near zero, as the nesting map \(g\) anticipates.

Steps 2 and 3 simulate and minimize.

We draw the shocks \(\epsilon\) once and reuse them at every trial value of \(\rho\).

We call them common random numbers, and a figure below shows their effect on the criterion.

N = 20_000
ε_sim = rng.standard_normal(N)      # common random numbers

def moments_ar1(ρ):
    "Expectation under the structural model of the auxiliary score."
    y_sim = simulate_ar1(ρ[0], ρ[1], ε_sim)
    return ar2_scores(θ_tilde, y_sim).mean(axis=0)

def criterion_ar1(ρ):
    if not (abs(ρ[0]) < 0.999 and ρ[1] > 1e-6):
        return 1e10
    m = moments_ar1(ρ)
    return float(m @ W @ m)

res = minimize(criterion_ar1, np.array([0.5, 1.5]), method='Nelder-Mead',
               options=dict(xatol=1e-6, fatol=1e-12, maxiter=2000))

print(f"true values:         ρ1 = {ρ1_0:.4f}   σ = {σ_0:.4f}")
print(f"maximum likelihood:  ρ1 = {ρ1_mle:.4f}   σ = {σ_mle:.4f}")
print(f"score matching:      ρ1 = {res.x[0]:.4f}   σ = {res.x[1]:.4f}")
true values:         ρ1 = 0.8000   σ = 1.0000
maximum likelihood:  ρ1 = 0.8022   σ = 0.9784
score matching:      ρ1 = 0.8009   σ = 0.9830

The two sets of estimates nearly coincide, differing in the third decimal place.

The next figure evaluates the criterion on a grid of \(\rho_1\) values with \(\sigma\) held at its estimate.

One curve uses the common random numbers ε_sim, and the other draws fresh shocks at each grid point.

rng_fresh = np.random.default_rng(1)
ρ1_grid = np.linspace(0.77, 0.83, 61)
σ_hat = res.x[1]

def criterion_fresh(ρ):
    "The criterion with a fresh draw of shocks at each evaluation."
    y_sim = simulate_ar1(ρ[0], ρ[1], rng_fresh.standard_normal(N))
    m = ar2_scores(θ_tilde, y_sim).mean(axis=0)
    return float(m @ W @ m)

crn = [criterion_ar1(np.array([r1, σ_hat])) for r1 in ρ1_grid]
fresh = [criterion_fresh(np.array([r1, σ_hat])) for r1 in ρ1_grid]

fig, ax = plt.subplots()
ax.plot(ρ1_grid, crn, lw=2, label='common random numbers')
ax.plot(ρ1_grid, fresh, lw=2, label='fresh shocks at each point')
ax.set_xlabel(r'$\rho_1$')
ax.set_ylabel('criterion')
ax.legend()
plt.show()
_images/9a492f0f68435fdb6575ff3a1e48a375f28a6228b21f3111f5bec4445db64336.png

Fig. 38.1 Criterion with common and fresh shocks#

Fresh shocks make the criterion jagged, with many local minima.

Common random numbers make it a smooth function of \(\rho_1\) with a single minimum.

The minimized criterion tests the \(k_\theta - k_\rho = 1\) overidentifying restriction that the structural model places on the auxiliary model.

Under correct specification, \(n\) times the minimized criterion, divided by the simulation factor \(1 + n/N\), is asymptotically distributed as \(\chi^2(k_\theta - k_\rho)\) [2].

The code writes the sample size \(n\) as T.

J = T * criterion_ar1(res.x) / (1 + T / N)
df = 3 - 2
print(f"J = {J:.3f},  χ²({df}) 5% critical value = {chi2.ppf(0.95, df):.3f}")
J = 0.133,  χ²(1) 5% critical value = 3.841

Theorem 1 delivers standard errors.

We compute \(M^0\) by central differences of the simulated moment vector, which common random numbers make smooth in \(\rho\), and we multiply the variance by the simulation factor \(1 + n/N\).

For the AR(1), the maximum likelihood standard errors are \(\sqrt{(1 - \rho_1^2)/n}\) for \(\rho_1\) and \(\sigma / \sqrt{2n}\) for \(\sigma\).

def jacobian(moments, ρ, h=1e-4):
    "Central-difference Jacobian of a moment function with respect to ρ."
    cols = []
    for j in range(ρ.size):
        e = np.zeros(ρ.size)
        e[j] = h * max(1.0, abs(ρ[j]))
        cols.append((moments(ρ + e) - moments(ρ - e)) / (2 * e[j]))
    return np.column_stack(cols)

def std_errors(moments, ρ, W, n, N):
    "Asymptotic standard errors from Theorem 1, including the simulation factor."
    M = jacobian(moments, ρ)
    V = np.linalg.inv(M.T @ W @ M) * (1 + n / N) / n
    return np.sqrt(np.diag(V))

se_emm = std_errors(moments_ar1, res.x, W, T, N)
se_mle = np.array([np.sqrt((1 - ρ1_mle**2) / T), σ_mle / np.sqrt(2 * T)])

print(f"{'':20}{'se ρ1':>10}{'se σ':>10}")
print(f"{'maximum likelihood':20}{se_mle[0]:10.4f}{se_mle[1]:10.4f}")
print(f"{'score matching':20}{se_emm[0]:10.4f}{se_emm[1]:10.4f}")

e_data = y_data[1:] - ρ1_mle * y_data[:-1]
kurt_e = ((e_data - e_data.mean())**4).mean() / e_data.var()**2
print(f"\nkurtosis of the residuals = {kurt_e:.2f}")
                         se ρ1      se σ
maximum likelihood      0.0189    0.0219
score matching          0.0188    0.0241

kurtosis of the residuals = 3.29

The two standard errors for \(\rho_1\) agree to three decimal places.

The score matching standard error for \(\sigma\) exceeds the maximum likelihood one by about 10 percent.

The simulation factor \(1 + n/N = 1.05\) accounts for about 2.5 percent.

The weighting matrix \(\tilde{\mathcal{I}}_n\) accounts for the rest: it estimates the variance of the \(\sigma\) score from the sample fourth moment of the residuals, whose kurtosis is 3.3 in this sample and 3 in the population.

Exercise 38.2 asks you to compare the sampling distributions of the two estimators.

38.10. A second example: stochastic volatility#

Now we turn to a model whose likelihood function is intractable.

Section 4.2 of Gallant and Tauchen [1996] discusses a stochastic volatility model whose mean equation also contains an intercept and a lagged return.

We drop those two terms and study

\[\begin{split} \begin{aligned} y_t &= \exp(w_t) \, r_y \, z_t , \\ w_t &= a \, w_{t-1} + r_w \, \eta_t , \end{aligned} \end{split}\]

with \(z_t\) and \(\eta_t\) independent standard normals and \(\rho = (a, r_w, r_y)^\top\).

The observed return \(y_t\) has a volatility \(\exp(w_t) r_y\) driven by a latent autoregression \(w_t\).

Writing the likelihood of \(\{y_t\}_{t=1}^n\) requires integrating the latent volatility path out of an \(n\)-dimensional integral,

\[ p(y_1, \ldots, y_n \mid \rho) = \int \cdots \int \prod_{t=1}^n \phi\bigl( y_t ; 0, e^{2w_t} r_y^2 \bigr) \, p(w_1, \ldots, w_n \mid a, r_w) \, dw_1 \cdots dw_n , \]

which no analytic expression evaluates.

Simulating the model takes two lines of code.

The model generates fat tails and persistent volatility.

Gallant et al. [1997] fit the full model and extensions of it to 16,127 daily observations on the Standard and Poor’s composite index, using SNP score generators.

Section 4.1 of Gallant and Tauchen [1996] notes that ARCH and GARCH models fit returns data well.

We choose as the auxiliary model a Gaussian GARCH(1,1),

\[ y_t = \mu + u_t , \qquad u_t = \sigma_t \nu_t , \qquad \sigma_t^2 = \omega + \alpha u_{t-1}^2 + \beta \sigma_{t-1}^2 , \]

with \(\theta = (\mu, \omega, \alpha, \beta)^\top\) and \(\nu_t \sim N(0,1)\).

The GARCH conditional density depends on the entire past of \(y_t\) through the recursion for \(\sigma_t^2\), so here \(x_t\) is the whole history.

Gallant and Tauchen call such a score generator non-Markov.

The structural parameters \((a, r_w, r_y)\) describe a latent log-volatility process; the auxiliary parameters \((\mu, \omega, \alpha, \beta)\) describe a conditional variance recursion in observed returns.

The auxiliary model needs no economic interpretation.

Its credential is a good fit to the data.

We differentiate the GARCH log density numerically.

Numerical differentiation keeps the method general: an auxiliary model need only support evaluation of its log likelihood.

The code fits the GARCH model in the unconstrained coordinates \((\mu, \log \omega, \operatorname{logit} \alpha, \operatorname{logit} \beta)\).

The scores, the weighting matrix, and the moment vector all refer to those coordinates.

The reparameterization changes neither \(\hat\rho_n\) nor the test statistic.

It multiplies the score by a fixed invertible matrix, and the criterion (38.3) is invariant to that transformation.

@jit(nopython=True)
def simulate_sv(a, r_w, r_y, z, η):
    "Simulate the structural stochastic volatility model."
    n = z.size
    y = np.empty(n)
    w = r_w * η[0] / np.sqrt(1.0 - a * a)
    y[0] = np.exp(w) * r_y * z[0]
    for t in range(1, n):
        w = a * w + r_w * η[t]
        y[t] = np.exp(w) * r_y * z[t]
    return y

@jit(nopython=True)
def garch_ll(μ, ω, α, β, y):
    "Per-observation log densities of the Gaussian GARCH(1,1) auxiliary model."
    n = y.size
    ll = np.empty(n)
    s2 = ω / max(1e-8, 1.0 - α - β)
    for t in range(n):
        u = y[t] - μ
        ll[t] = -0.5 * (np.log(2 * np.pi) + np.log(s2) + u * u / s2)
        s2 = ω + α * u * u + β * s2
    return ll

def unpack(θ):
    "Map unconstrained parameters to ω > 0 and α, β in (0, 1)."
    μ, lω, lα, lβ = θ
    return μ, np.exp(lω), 1 / (1 + np.exp(-lα)), 1 / (1 + np.exp(-lβ))

def garch_negll(θ, y):
    μ, ω, α, β = unpack(θ)
    if α + β > 0.999:
        return 1e10
    return -garch_ll(μ, ω, α, β, y).sum()

def garch_scores(θ, y):
    "Per-observation auxiliary scores by central differences."
    p, n = θ.size, y.size
    S = np.empty((n, p))
    for j in range(p):
        h = 1e-5 * max(1.0, abs(θ[j]))
        θp, θm = θ.copy(), θ.copy()
        θp[j] += h
        θm[j] -= h
        S[:, j] = (garch_ll(*unpack(θp), y) - garch_ll(*unpack(θm), y)) / (2 * h)
    return S

Section 4.2 reports no parameter values, so we choose our own.

Let’s generate a long return series from the structural model.

rng = np.random.default_rng(5)
T = 2000
a_0, r_w0, r_y0 = 0.80, 0.20, 0.65
y_sv = simulate_sv(a_0, r_w0, r_y0, rng.standard_normal(T), rng.standard_normal(T))

kurt = ((y_sv - y_sv.mean())**4).mean() / y_sv.var()**2
print(f"standard deviation = {y_sv.std():.3f},  kurtosis = {kurt:.2f}")
standard deviation = 0.738,  kurtosis = 3.93

The population kurtosis of \(y_t\) is \(3 \exp\bigl( 4 r_w^2 / (1 - a^2) \bigr) = 4.7\) at these parameter values, above the Gaussian value of 3.

The sample kurtosis understates it, as sample kurtoses of heavy-tailed series often do.

fig, ax = plt.subplots()
ax.plot(y_sv, lw=0.6)
ax.set_xlabel('$t$')
ax.set_ylabel('$y_t$')
plt.show()
_images/e418ae724ec8712063ab7ccdcd7b948f81acff24c6eec7f44b538df7b1853e71.png

Fig. 38.2 Simulated returns from the SV model#

Step 1 fits the GARCH auxiliary model to these data.

θ_start = np.array([0.0, np.log(0.05), -2.0, 1.5])
r = minimize(garch_negll, θ_start, args=(y_sv,), method='Nelder-Mead',
             options=dict(maxiter=8000, xatol=1e-8, fatol=1e-8))
θ_sv = r.x
μ_h, ω_h, α_h, β_h = unpack(θ_sv)
print(f"auxiliary GARCH QMLE: μ = {μ_h:.4f}, ω = {ω_h:.4f}, "
      f"α = {α_h:.4f}, β = {β_h:.4f}   (α + β = {α_h + β_h:.4f})")

S_sv = garch_scores(θ_sv, y_sv)
W_sv = np.linalg.inv(S_sv.T @ S_sv / T)
auxiliary GARCH QMLE: μ = 0.0150, ω = 0.0922, α = 0.1209, β = 0.7127   (α + β = 0.8337)

An \(\alpha + \beta\) of 0.83 records the persistence of volatility that the auxiliary model reads off the data.

Steps 2 and 3 estimate the three structural parameters.

We search over unconstrained coordinates and reuse one pair of shock arrays throughout.

N = 50_000
z_sim, η_sim = rng.standard_normal(N), rng.standard_normal(N)

def moments_sv(ρ):
    y_s = simulate_sv(ρ[0], ρ[1], ρ[2], z_sim, η_sim)
    return garch_scores(θ_sv, y_s).mean(axis=0)

def to_ρ(u):
    "Map unconstrained coordinates to a in (0,1) and r_w, r_y > 0."
    return np.array([1 / (1 + np.exp(-u[0])), np.exp(u[1]), np.exp(u[2])])

def criterion_sv(u):
    if np.max(np.abs(u)) > 25:
        return 1e10
    m = moments_sv(to_ρ(u))
    return float(m @ W_sv @ m)

u_0 = np.array([np.log(0.6 / 0.4), np.log(0.3), np.log(0.5)])
res_sv = minimize(criterion_sv, u_0, method='Nelder-Mead',
                  options=dict(maxiter=400, xatol=1e-4, fatol=1e-10))
ρ_hat = to_ρ(res_sv.x)

Theorem 1 delivers standard errors.

se_sv = std_errors(moments_sv, ρ_hat, W_sv, T, N)

print(f"{'':17}{'a':>9}{'r_w':>9}{'r_y':>9}")
print(f"{'true values':17}{a_0:9.4f}{r_w0:9.4f}{r_y0:9.4f}")
print(f"{'score matching':17}" + "".join(f"{x:9.4f}" for x in ρ_hat))
print(f"{'standard errors':17}" + "".join(f"{x:9.4f}" for x in se_sv))
                         a      r_w      r_y
true values         0.8000   0.2000   0.6500
score matching      0.8447   0.1794   0.6700
standard errors     0.0404   0.0372   0.0244

The estimator locates all three parameters of a model whose likelihood function we never wrote down.

Each estimate lies within about one standard error of its true value.

The data pin down the scale \(r_y\) most precisely.

38.11. Testing the structural model#

The auxiliary model has four parameters and the structural model has three, so one overidentifying restriction remains to be tested.

J_sv = T * criterion_sv(res_sv.x) / (1 + T / N)
df_sv = 4 - 3
print(f"J = {J_sv:.3f},  χ²({df_sv}) 5% critical value = {chi2.ppf(0.95, df_sv):.3f}")
J = 0.606,  χ²(1) 5% critical value = 3.841

The data give the structural model no trouble, as they should, because the structural model generated them.

A misspecified structural model fares differently.

Let’s impose \(r_w = 0\), which shuts down stochastic volatility and leaves a Gaussian white noise with one free parameter.

def moments_const_vol(v):
    "Moments of the constant volatility model r_w = 0, with v = log r_y; a has no effect."
    return moments_sv(np.array([0.5, 1e-8, np.exp(v[0])]))

def criterion_const_vol(v):
    m = moments_const_vol(v)
    return float(m @ W_sv @ m)

r_cv = minimize(criterion_const_vol, np.array([0.0]), method='Nelder-Mead',
                options=dict(maxiter=200, xatol=1e-6))
J_cv = T * criterion_const_vol(r_cv.x) / (1 + T / N)
df_cv = 4 - 1
print(f"constant volatility model: J = {J_cv:.1f}, "
      f"χ²({df_cv}) 5% critical value = {chi2.ppf(0.95, df_cv):.3f}")
constant volatility model: J = 21.0, χ²(3) 5% critical value = 7.815

The test rejects the restricted model at the 1% level [3].

Gallant and Tauchen emphasize this second use of the machinery.

An auxiliary model that approximates the data well makes the test powerful against structural model error, and the individual score components say which features of the data a rejected model fails to match.

Gallant and Tauchen’s quasi-t-ratios divide each component of \(\sqrt{n}\, m(\hat\rho_n, \tilde\theta_n)\) by its standard error.

The asymptotic variance of \(\sqrt{n}\, m(\hat\rho_n, \tilde\theta_n)\) is

\[ \Bigl(1 + \frac{n}{N}\Bigr) \Bigl[ \mathcal{I}^0 - M^0 \bigl( (M^0)^\top (\mathcal{I}^0)^{-1} M^0 \bigr)^{-1} (M^0)^\top \Bigr] . \]

Estimating \(\rho\) removes the variance in the directions that the columns of \(M^0\) span.

The unconstrained coordinates transform each GARCH parameter separately through an increasing function.

The t-ratios are therefore the same as in the coordinates \((\mu, \omega, \alpha, \beta)\).

I_sv = S_sv.T @ S_sv / T
M_cv = jacobian(moments_const_vol, r_cv.x)
V_cv = (I_sv - M_cv @ np.linalg.inv(M_cv.T @ W_sv @ M_cv) @ M_cv.T) * (1 + T / N)
t_cv = np.sqrt(T) * moments_const_vol(r_cv.x) / np.sqrt(np.diag(V_cv))

for name, t in zip(['μ', 'ω', 'α', 'β'], t_cv):
    print(f"{name:>4}: {t:6.2f}")
   μ:  -0.75
   ω:  -3.04
   α:  -3.95
   β:  -2.79

The components for \(\omega\), \(\alpha\) and \(\beta\), which govern the conditional variance, carry the rejection.

The \(\mu\) component does not.

Each negative sign says that, holding the other auxiliary parameters fixed, data from the constant volatility model would pull that parameter below the value that the observed data choose.

The constant volatility model matches the mean of returns and fails to reproduce the dependence of today’s variance on yesterday’s squared return.

Gallant et al. [1997] use the score vector of a rejected stochastic volatility model to diagnose its defects, and they then repair the model by thickening the tails of \(z_t\) and lengthening the memory of the volatility equation.

38.12. Applications#

Gallant and Tauchen [2010] collects applications of the method to continuous-time models in finance.

Andersen et al. [2002] estimate stochastic volatility diffusions for the Standard and Poor’s 500 index and reject them, and jump diffusions pass the same tests.

Other applications fit multifactor stochastic volatility models, term structure models of interest rates, and exchange rate models.

Bansal et al. [1995] fit a two-country general equilibrium monetary model to high-frequency currency market data, and Gallant and Tauchen [1996] propose fitting a one-sector stochastic growth model to consumption and stock returns.

Empirical auction models form another class of applications.

Game theory delivers the density of the winning bid under independent private values, and that density has a tractable form only for special valuation distributions.

Simulating from it is easy for any valuation distribution, so score matching brings all of the restrictions of the theory to bear.

38.13. Recovering the latent state#

Having estimated \(\rho\), a researcher often wants the latent state, such as the volatility path behind a return series.

Reprojection recovers it [Gallant and Tauchen, 2010].

Simulate a long path of states and observables at \(\hat\rho_n\), calibrate inside the simulation the conditional distribution of the latent state given current and lagged observables, then evaluate those calibrated functions at the observed data.

Reprojection is a simulation-based nonlinear filter.

38.15. Exercises#

Exercise 38.1

Let the structural model at \(\rho\) have density \(p(\cdot \mid \rho)\) and let the auxiliary family be \(\{f(\cdot \mid \theta) : \theta \in \Theta\}\).

a. Show that

\[ \frac{\partial}{\partial \theta} D_{KL}\bigl( p(\cdot \mid \rho) \, \| \, f(\cdot \mid \theta) \bigr) = - \, m(\rho, \theta) , \]

where \(m(\rho,\theta) = \mathbb{E}_{p(\cdot \mid \rho)} \bigl[ \partial \ln f(\cdot \mid \theta) / \partial\theta \bigr]\).

b. Conclude that the pseudo-true value \(\theta^0(\rho)\) satisfies \(m\bigl(\rho, \theta^0(\rho)\bigr) = 0\).

c. Explain why \(m(\rho^0, \theta^0) = 0\), and why the structural model does not need to nest inside the auxiliary family for this to hold.

Exercise 38.2

Theorem 2 of Gallant and Tauchen [1996] says that the score matching estimator and the maximum likelihood estimator have the same asymptotic distribution when the structural model is smoothly embedded within the auxiliary model and simulation error is negligible.

Check this claim by Monte Carlo in the AR(1) and AR(2) example.

Generate 60 independent samples of length \(T = 500\) from the structural AR(1) with \(\rho_1 = 0.8\) and \(\sigma = 1\).

For each sample compute both estimators, then compare their means, their standard deviations, and the correlation between them across replications.

Compare the Monte Carlo standard deviations with the asymptotic standard deviations that Theorem 2 delivers.

Exercise 38.3

The order condition \(k_\theta \ge k_\rho\) does not guarantee identification.

Gallant and Tauchen warn that the parameters of the mixing process of a stochastic volatility model need third and fourth moment information, and that the score of a Gaussian vector autoregression will not supply it.

Investigate their warning.

Replace the GARCH auxiliary model of the stochastic volatility example by a Gaussian AR(2), which has \(k_\theta = 3\) parameters against \(k_\rho = 3\) structural parameters, so the order condition holds.

With \(k_\theta = k_\rho\) the model leaves no overidentifying restriction to test.

Minimize the resulting criterion from several different starting values and report the estimates together with the criterion values.

Explain what you find, and say which function of \((a, r_w, r_y)\) the Gaussian AR(2) score does pin down.

Exercise 38.4

The stochastic volatility example sets \(a = 0.8\).

Rerun it with \(a = 0.95\), \(r_w = 0.2\) and \(r_y = 0.65\).

Log volatility then has standard deviation \(r_w / \sqrt{1 - a^2} = 0.64\), and the population kurtosis of \(y_t\) is about 15.5.

a. Compute the first three autocorrelations of each component of the GARCH score at the data.

b. Estimate \(\mathcal{I}^0\) two more ways.

First, use Gallant and Tauchen’s heteroskedasticity and autocorrelation consistent estimate. It adds to the outer product the sample autocovariances of the score at lags \(\tau = 1, \ldots, \ell\), with \(\ell = [n^{1/5}]\), weighted by the Parzen kernel \(w(\tau / \ell)\), where

\[\begin{split} w(x) = \begin{cases} 1 - 6x^2 + 6x^3 & 0 \le x \le 1/2 , \\ 2 (1 - x)^3 & 1/2 < x \le 1 . \end{cases} \end{split}\]

Second, at the estimate \(\hat\rho_n\), simulate 300 independent data sets of length \(n\) from the structural model, compute \(\sqrt{n}\) times the average GARCH score at \(\tilde\theta_n\) in each, and take the covariance matrix across data sets.

Compare the diagonals of the three estimates.

c. Re-estimate \(\rho\) with each estimate of \(\mathcal{I}^0\), and compare the Theorem 1 standard errors.

The Theorem 1 formula \(\bigl[ M^\top W M \bigr]^{-1}\) assumes that the weighting matrix \(W\) equals \((\mathcal{I}^0)^{-1}\).

For the outer-product and Parzen estimators, also compute the sandwich standard errors

\[ \Bigl(1 + \frac{n}{N}\Bigr) \frac{1}{n} \bigl( M^\top W M \bigr)^{-1} M^\top W \, \mathcal{I} \, W M \bigl( M^\top W M \bigr)^{-1} , \]

with the simulated estimate of \(\mathcal{I}^0\) in place of \(\mathcal{I}\).

d. Explain what you find.