Skip to content

Model card — GMM

iv_gmm · gmm_nonlinear

The generalized method of moments estimates parameters by forcing a set of sample moment conditions — things the model says should be zero in expectation — as close to zero as a weighting matrix allows. When there are more moments than parameters, the remaining slack is itself a specification test. This family covers the linear instrumental-variables case in closed form and arbitrary nonlinear moment systems through a Python callback.


iv_gmm — linear IV-GMM

What it estimates. The coefficients of a linear model y = X·beta + u where some columns of X are endogenous, identified by instruments Z (Hansen 1982). With more instruments than regressors the system is over-identified and the efficient GMM estimator uses a robust or HAC weighting matrix, plus the Hansen J-test of the over-identifying restrictions.

Assumptions. Instrument relevance (Z correlated with the endogenous regressors) and exogeneity (E[Z·u] = 0). Z must include the exogenous regressor columns (intercept, exogenous controls) alongside the excluded instruments — those regressors instrument themselves. HAC weighting assumes the moment process is stationary with summable autocovariances.

When to use (and when not). Use for endogenous regressors with valid instruments, over-identified systems where you want efficiency and a specification test, and time-series moments needing HAC weighting. Do not use with weak instruments (the estimator is biased toward OLS and the J-test misleads), and prefer plain ols when nothing is endogenous.

Key arguments and defaults (and why). method="2sls" (one-step, robust to weak-ID concerns) vs "2step" (efficient GMM, the usual default choice) vs "iterated" (iterate the weighting matrix to convergence). weight="robust" (heteroskedasticity-robust) or "hac" (adds autocorrelation robustness, with bandwidth). tol/max_iter govern the iterated variant.

How to read the output. params (in the column order of X), bse (robust/HAC standard errors), residuals, nmoments/nparams, steps. When over-identified (nmoments > nparams): the Hansen j_stat with j_dof degrees of freedom and j_pval — a small j_pval rejects the moment conditions (some instrument is invalid or the model is misspecified). A large j_pval is reassuring, not proof of validity.

Failure modes. Weak instruments (dominant failure — check the first stage); forgetting to put the exogenous columns into Z; reading a passing J-test as proof of exogeneity rather than absence of contradiction.

Validated against. linearmodels IVGMM — 2-step robust weighting, robust covariance, and the Hansen J statistic (fixtures/gmm.json).

References. Hansen (1982); Hansen, Heaton & Yaron (1996, iterated/CUE); Newey & West (1987, HAC).

import numpy as np, tsecon

rng = np.random.default_rng(0)
n = 500
z = rng.standard_normal((n, 2))              # two excluded instruments
u = rng.standard_normal(n)
x = z @ np.array([0.8, 0.5]) + 0.7 * u + 0.5 * rng.standard_normal(n)   # endogenous
w = rng.standard_normal(n)                    # an exogenous regressor
y = 1.0 + 0.3 * w + 1.5 * x + u               # true (const, w, x) = (1.0, 0.3, 1.5)

X = np.column_stack([np.ones(n), w, x])                  # regressors
Z = np.column_stack([np.ones(n), w, z])                  # instruments incl. exogenous cols
fit = tsecon.iv_gmm(X, Z, y, method="2step", weight="robust")
print("params [const, w, x]:", np.round(fit["params"], 3))   # ~[1.05, 0.29, 1.52]
print("robust SEs          :", np.round(fit["bse"], 3))
print(f"Hansen J = {fit['j_stat']:.3f} (dof {fit['j_dof']}), p = {fit['j_pval']:.3f}")

gmm_nonlinear — nonlinear GMM via a moment callback

What it estimates. GMM for an arbitrary moment system you write in Python: you supply a function mapping a parameter vector to an n-by-m matrix of per-observation moment contributions, and a derivative-free Nelder-Mead search minimizes the GMM objective ḡ' W ḡ. Handles exactly-identified and over-identified systems.

Assumptions. The moment conditions hold at the truth (E[g(θ₀)] = 0) and identify the parameters; the objective is smooth enough for Nelder-Mead to make progress from initial.

When to use (and when not). Use for custom estimators — Euler-equation moments, method-of-moments, simulated moments — where no closed form exists. For a linear IV model use iv_gmm (faster and analytic). Nelder-Mead is derivative-free and robust but slow in high dimensions; keep the parameter count modest.

Key arguments and defaults (and why). moments_fn returns an n×m array (rows = observations, columns = moments); initial is the starting parameter vector (its length sets nparams); weight is the flattened m×m weighting matrix (row-major) or None for the identity. Start with the identity, then optionally re-weight by the inverse moment covariance for efficiency.

How to read the output. params, objective (the minimized ḡ' W ḡ — near zero when exactly identified), gbar (the average moments at the optimum — should be ~0 when exactly identified), converged, and iterations/fevals/ nmoments/nparams. A non-zero objective in an over-identified system is the analogue of the J-statistic slack.

Failure modes. A moment function that returns the wrong shape, poor starting values (Nelder-Mead is local — try several initial points), and flat or discontinuous objectives that stall the simplex.

Validated against. No external golden; the crate property test recovers the closed-form mean/variance method-of-moments solution to ~1e-4, and the identity weight reproduces the default (Hansen 1982).

References. Hansen (1982); McFadden (1989, simulated moments).

import numpy as np, tsecon
rng = np.random.default_rng(0)
y = 2.0 + 1.5 * rng.standard_normal(400)

def moments(theta):                          # E[y-mu]=0, E[(y-mu)^2 - s2]=0
    resid = y - theta[0]
    return np.column_stack([resid, resid ** 2 - theta[1]])

g = tsecon.gmm_nonlinear(moments, initial=[0.0, 1.0])
print("(mean, var):", np.round(g["params"], 3), " converged:", g["converged"])
print("avg moments at optimum:", np.round(g["gbar"], 5))