Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

๐Ÿšง ๐Ÿšง ๐Ÿšง Not the final version ๐Ÿšง ๐Ÿšง ๐Ÿšง

Today we estimate the retirement model of Class 18 by the method of simulated moments of Class 19: simulated data, moments, the criterion, the disutility of work and the discount factor recovered, and the standard errors checked against a Monte Carlo.

What we have and what we writeยถ

The course architecture again: model object, solver, simulator, estimator. The first three exist, and todayโ€™s work is the fourth:

PieceWhereStatus
retirement, solve_dcegm, workerretirement.py, Class 18given
simulate(m, sol, M0, seed)retirement.py, Class 18given, with a prob=True switch that also returns the choice probabilities
estimate, jacobian, vcov, jtestmsm.py, Class 19given, model-free
moments(M, C, D)this notebookwe write it
simmoments(theta, S, seed)this notebookwe write it
criterion(theta, W, S)this notebookwe write it

The estimator in msm.py takes the criterion and the simulated moments as function arguments, so the three functions we write are the whole interface between the model and the method.

Source
---------------------------------------------------------------------------
HTTPError                                 Traceback (most recent call last)
Cell In[1], line 9
      5 if not CODE.exists():                                     # otherwise fetch the modules from the code repository
      6     CODE = pathlib.Path(tempfile.mkdtemp())
      7     url = 'https://raw.githubusercontent.com/fediskhakov/sb-dse-code/main/session20-nov3/'
      8     for f in ('retirement.py', 'msm.py'):
----> 9         urllib.request.urlretrieve(url + f, CODE / f)
     10 sys.path.insert(0, str(CODE))
     11 from retirement import retirement, solve_dcegm, simulate
     12 from msm import estimate, jacobian, vcov, jtest

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/urllib/request.py:240, in urlretrieve(url, filename, reporthook, data)
    223 """
    224 Retrieve a URL into a temporary location on disk.
    225 
   (...)    236 data file as well as the resulting HTTPMessage object.
    237 """
    238 url_type, path = _splittype(url)
--> 240 with contextlib.closing(urlopen(url, data)) as fp:
    241     headers = fp.info()
    243     # Just return the local path and the "headers" for file://
    244     # URLs. No sense in performing a copy unless requested.

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/urllib/request.py:215, in urlopen(url, data, timeout, cafile, capath, cadefault, context)
    213 else:
    214     opener = _opener
--> 215 return opener.open(url, data, timeout)

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/urllib/request.py:521, in OpenerDirector.open(self, fullurl, data, timeout)
    519 for processor in self.process_response.get(protocol, []):
    520     meth = getattr(processor, meth_name)
--> 521     response = meth(req, response)
    523 return response

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/urllib/request.py:630, in HTTPErrorProcessor.http_response(self, request, response)
    627 # According to RFC 2616, "2xx" code indicates that the client's
    628 # request was successfully received, understood, and accepted.
    629 if not (200 <= code < 300):
--> 630     response = self.parent.error(
    631         'http', request, response, code, msg, hdrs)
    633 return response

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/urllib/request.py:559, in OpenerDirector.error(self, proto, *args)
    557 if http_err:
    558     args = (dict, 'default', 'http_error_default') + orig_args
--> 559     return self._call_chain(*args)

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/urllib/request.py:492, in OpenerDirector._call_chain(self, chain, kind, meth_name, *args)
    490 for handler in handlers:
    491     func = getattr(handler, meth_name)
--> 492     result = func(*args)
    493     if result is not None:
    494         return result

File /opt/hostedtoolcache/Python/3.12.15/x64/lib/python3.12/urllib/request.py:639, in HTTPDefaultErrorHandler.http_error_default(self, req, fp, code, msg, hdrs)
    638 def http_error_default(self, req, fp, code, msg, hdrs):
--> 639     raise HTTPError(req.full_url, code, msg, hdrs, fp)

HTTPError: HTTP Error 404: Not Found

The โ€œdataโ€ are simulated as in Class 18, with a seed of their own: 1000 workers with initial wealth uniform on [2,20][2, 20], taste shocks ฯƒ=0.1\sigma = 0.1 and income shocks ฯƒy=0.2\sigma_y = 0.2, over T=10T = 10 periods. The distribution of initial wealth is treated as part of the model, known to the estimator. The parameters we pretend not to know:

true valueestimated
disutility of work ฮด\delta0.5yes
discount factor ฮฒ\beta0.95yes
taste shock scale ฯƒ\sigma0.1fixed, see what breaks
income shock ฯƒy\sigma_y, return RR, wage yy0.2, 1.05, 1fixed

Every column is a period of life: the share working falls from 83% to zero in the last period, where no one works, and wealth is run down from 11 to below 2.

  • Which of the two profiles would move if ฮด\delta doubled, and in which direction?

  • Which would move if ฮฒ\beta fell to 0.9, and why is the first period of each profile the least informative?

Momentsยถ

The two profiles above are the moments: the share working in periods 0,โ€ฆ,Tโˆ’20, \dots, T-2 and mean wealth in periods 1,โ€ฆ,Tโˆ’11, \dots, T-1, q=18q = 18 numbers. The last period of work status is left out because it is zero whatever the parameters, and the first period of wealth because it is the observed initial condition.

The function returns one row per individual, the contributions h(zi)h(z_i), so that the data moments are a column mean and their covariance ฮฉ\Omega is one call of np.cov:

The two kinds of moments live on different scales: a residual in a work share is of order 0.01, a residual in mean wealth of order 0.1, and the inverse variances in Wdiag put them on the same footing. With the identity matrix the wealth residuals would decide everything and the work shares nothing.

  • Consumption is in the data too. Why is it left out of the moments here, and what would it add?

  • Retirement is absorbing, so the work status of one individual in periods 4 and 5 is strongly correlated. Where does this show up in ฮฉ\Omega, and does the diagonal WW see it?

The simulator with fixed drawsยถ

The simulated moments solve the model at ฮธ\theta and simulate SS copies of the data, initial wealth included, with the same seed at every ฮธ\theta, as in the criterion of Class 19. The simulated sample is independent of the data, which the asymptotics require.

The parameter walls keep the optimizer where the solver works: ฮฒ<1\beta < 1 and ฮด>0\delta > 0.

The criterion at the true parameters is not zero: the data moments carry sampling noise and the simulated moments carry simulation noise, and the criterion measures their distance. The figure cuts the criterion along ฮด\delta and along ฮฒ\beta through the truth, and zooms in on a window of ฮด\delta one thousandth wide.

Source
  • Where exactly along a wealth path does one draw crossing the threshold change the simulated moments, and does the effect stay in one period?

  • The wealth moments are continuous in ฮธ\theta given the work status. Why do they still jump at the steps?

Estimation and standard errorsยถ

The first step minimizes the criterion with the diagonal weighting matrix from a deliberately poor start, and then restarts Nelderโ€“Mead from its own solution: on a criterion with steps the simplex can collapse early, and a restart from the solution with a fresh simplex costs a second or two and catches it.

The first step lands within a tenth of a standard error of the truth in both parameters. On other data sets drawn from the same model it can land two standard errors away, and that is not a bug either: eighteen moments from an absorbing retirement process and a persistent wealth path are strongly correlated, and a diagonal WW treats them as independent, so the estimate follows whichever noise the sample has in common across periods.

The second step uses the optimal weighting matrix ฮฉ^โˆ’1\hat\Omega^{-1}, starts from the first-step estimate, and delivers the JJ-test with qโˆ’K=16q - K = 16 degrees of freedom.

The second step lands within half a standard error of ฮด=0.5\delta = 0.5 and ฮฒ=0.95\beta = 0.95, with standard errors three times smaller than the first step for ฮด\delta and five times smaller for ฮฒ\beta. That is what the optimal WW buys when the moments are correlated: it weights the differences between neighboring periods, which the diagonal WW cannot see.

The JJ-test does not reject the model that generated the data: J=16.9J = 16.9 on 16 degrees of freedom, pโ‰ˆ0.39p \approx 0.39. It does not always come out this way. On nine data sets drawn from the same model with different seeds the p-values ranged from 0.02 to 0.99, with one rejection at the 5% level, and one rejection in twenty is what a 5% test does. Altonji & Segal (1996) is also in play: ฮฉ^\hat\Omega has a condition number of 2โ‹…1042 \cdot 10^4 and is estimated from one thousand individuals, and tests based on its inverse over-reject in samples of this size.

The table every MSM paper carries is the fit of the moments, here as a figure: data moments with two standard error bands against the simulated moments at the estimate.

Source

Both profiles are matched within the bands in every period, which is what the JJ-test summarizes. When the test does reject, the figure usually shows why: the optimal WW does not close the gaps one by one, it minimizes a quadratic form in which a run of positively correlated gaps on the same side of the data is cheap, and such a run is what the JJ-test picks up.

  • The second-step standard errors are much smaller than the first-step ones. Which part of the sandwich changed, and why does the gain depend on the correlation of the moments?

  • Eighteen moments and one thousand individuals: how many numbers does ฮฉ^\hat\Omega estimate, and would you trust the JJ-test more with nine moments?

Monte Carlo check of the standard errorsยถ

The standard errors come from a formula with a numerical Jacobian computed over steps. The honest check is a Monte Carlo: draw ten data sets from the true model with different seeds, initial wealth included, run the first-step estimator on each, and compare the spread of the estimates to the formula.

Every replication keeps the same simulation seed in simmoments, so only the data change, and the first step is enough for the check because its formula and the second stepโ€™s share every ingredient but WW.

The estimator is centered on the truth, and the Monte Carlo spread matches the formula within 10% for both parameters, with a Jacobian that averages over the steps. Ten replications is a rough check, not a verdict: the standard error of a standard deviation from ten draws is about a quarter of its value, so agreement within 30% is all the check can promise.

  • The Monte Carlo keeps the simulation seed fixed across replications. Which source of noise in the (1+1/S)(1 + 1/S) formula does it then not measure?

  • How many replications would it take to tell a formula standard error of 0.003 from a true one of 0.004?

What breaks: the taste shock scaleยถ

The scale ฯƒ\sigma of the taste shocks was fixed at its true value. Adding it to the parameter vector is one line in model and one in the walls, and it is where the method shows its limits. The criterion along ฯƒ\sigma through the truth, computed with a version of model that takes three parameters:

Compare with the cuts along ฮด\delta and ฮฒ\beta above, which climb more than an order of magnitude within a few hundredths of the truth. Along ฯƒ\sigma the minimum is at the truth, 0.007, but the criterion is flat: it rises to 0.02 at ฯƒ=0.08\sigma = 0.08 and ฯƒ=0.15\sigma = 0.15, and to 0.06โ€“0.07 only at ฯƒ=0.05\sigma = 0.05 and ฯƒ=0.2\sigma = 0.2. The moments barely respond to ฯƒ\sigma, which is the small entry in DD that the asymptotics of Class 19 warned about:

What would identify ฯƒ\sigma is a moment that measures how retirement varies given wealth: the dispersion of the retirement age among workers with similar initial wealth, for instance. The practical task asks for it.

Referencesยถ
  1. Altonji, J. G., & Segal, L. M. (1996). Small-Sample Bias in GMM Estimation of Covariance Structures. Journal of Business & Economic Statistics, 14(3), 353โ€“366. 10.1080/07350015.1996.10524661
  2. Iskhakov, F., Jรธrgensen, T., Rust, J., & Schjerning, B. (2017). The endogenous grid method for discrete-continuous dynamic choice models with (or without) taste shocks. Quantitative Economics, 8(2), 317โ€“365. https://doi.org/10.3982/QE643
  3. McFadden, D. (1989). A Method of Simulated Moments for Estimation of Discrete Response Models Without Numerical Integration. Econometrica, 57(5), 995. 10.2307/1913621
  4. French, E. (2005). The Effects of Health, Wealth, and Wages on Labour Supply and Retirement Behaviour. The Review of Economic Studies, 72(2), 395โ€“427. 10.1111/j.1467-937X.2005.00337.x
  5. Gourinchas, P.-O., & Parker, J. A. (2002). Consumption over the Life Cycle. Econometrica, 70(1), 47โ€“89. 10.1111/1468-0262.00269
  6. Corbae, D., & Guvenen, F. (2024). Method of Moments Estimators. In Quantitative Economics with Heterogeneity: A Guidebook. Princeton University Press.