Install
$ agentstack add skill-lancegui-causal-powers-structural-estimation ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Structural Estimation
Overview
Reduced form measures a relationship that held in the data. Structural estimation recovers the primitives — the preferences, costs, information, and conduct that generated the data — so you can ask what happens in a world that has not occurred: a merger, a new product, a tax, a removed search friction, the consumer surplus from an entrant. The trade is steep, and the failure mode is the mirror image of reduced form's. In reduced form, confounding masquerades as an effect. In structural work, a misspecified model fits in-sample and lies confidently out-of-sample, or a parameter the data cannot identify still gets a number from the optimizer. A clean estimation run earns you nothing on its own: the model can be internally consistent, converge beautifully, and be wrong about every counterfactual you built it to answer.
Core principle: structural estimation buys policy-invariant primitives at the price of assumptions the data cannot test. Earn that price — justify the model over reduced form, name what identifies each parameter, prove the algorithm recovers truth, and stress every counterfactual against the assumption it leans on hardest.
Reduced form or structural? — choose the workflow before you model
This is the fork. These questions decide which of the three arms you're in:
- Does the decision live inside the support of the data? "What was the effect of the price cut we ran?" "Did the policy work?" → reduced form. A well-identified DiD / IV / RDD answers it and is more credible precisely because it leans on fewer assumptions. Use the reduced-form workflow →
causal-identification. - Does the decision require a world you haven't observed, a welfare number, or a mechanism the data can't separate? "What price would the merged firm set?" "How much of low uptake is taste vs. not knowing the product exists?" "What's the consumer surplus from a new entrant?" → structural. The relationship you'd estimate in reduced form shifts when the policy changes (the Lucas critique), so there is no reduced-form coefficient to extrapolate. Use this skill's workflow.
- Is the goal a prediction to act on, not an effect at all? ("which unit to flag / score / rank") → neither causal arm — use the prediction workflow →
predictive-modeling. (Route by goal, not algorithm: ML used to estimate an effect still belongs to the causal arms.)
Don't go structural for its own sake. If a quasi-experiment answers the question, it wins. Go structural only when the question genuinely lives outside the data.
First question (the analog of "what's your experiment?"): what counterfactual do you need, and which primitive must be policy-invariant for that counterfactual to be valid? This means naming the target world — "post-merger prices", "welfare with the entrant gone", "uptake if search cost were zero" — not designing the scenarios (that comes after the estimator is proven; see the pipeline). Naming is cheap and required: if you can't name the target counterfactual, you don't need a structural model yet. Designing it is expensive and runs last — don't let the gate pull it forward into the estimation stage.
The discipline
WRITE THE MODEL CARD (primitives + equilibrium + per-parameter identification + estimand + plan) → GET APPROVAL ‖ ← the gate, before any estimation machinery
→ PROVE RECOVERY (Monte Carlo: converge back to known θ from a distant start, across the parameter space)
→ GRADIENTS (AD or analytical derivation, group by group; check vs finite-difference)
→ ESTIMATE → VALIDATE FIT (untargeted moments + held-out; reconcile vs a reduced-form elasticity)
→ COUNTERFACTUALS (re-solve equilibrium; one scenario per mechanism) → DECOMPOSE & INTERPRET
Each arrow is a gate, not a suggestion. The first deliverable is the model card (next section) — the model and its per-parameter identification are written into the card, not as separate informal steps. Skipping "prove recovery" is how a coding bug or a non-identified parameter rides all the way into a published counterfactual.
Primitives — what you are actually estimating
A primitive is a parameter of the economic environment that is invariant to the policy you want to study — that invariance is the entire license for the counterfactual. Across IO models the primitives are some subset of:
- Preferences — utility parameters, including the distribution of heterogeneous tastes (random coefficients), price sensitivity, and switching/search costs as they enter utility.
- Technology / costs — marginal cost functions; fixed and sunk costs (entry); adjustment costs (dynamics).
- Information & choice sets — what agents know and which alternatives they actually evaluate: consideration sets, beliefs, information frictions.
- Conduct / equilibrium concept — how agents interact: Nash–Bertrand pricing, Cournot, collusion, Markov-perfect dynamics, auction equilibrium, single-agent optimal stopping.
The Lucas-critique test: a parameter is a primitive only if it would not change under your counterfactual. A "price elasticity" is not a primitive — it moves with the environment; the taste and cost parameters that generate it are. If your counterfactual would alter something you're treating as fixed, the model is the wrong tool for that counterfactual.
Mechanisms reduced form cannot recover
This is why you pay the structural price. Name the specific mechanism your model buys you over reduced form, or you are carrying the cost without the benefit.
- Separating non-preferred from non-considered from non-searched. A product with near-zero sales is equally consistent with low utility, not being in the consideration set, or a search cost that stopped the consumer before they found it. The three are observationally identical in reduced form and imply opposite policies — cut the price, advertise to expand awareness, or remove the search friction. Only a model with an explicit consideration/search stage — and a shifter that moves it (see identification) — can decompose them.
- Out-of-support substitution and welfare. Reduced form gives an elasticity at observed prices; a demand system gives the whole substitution matrix, counterfactual prices, and consumer surplus / compensating variation — numbers reduced form simply does not contain.
- Equilibrium responses. When the policy changes, firms re-optimize: merger price effects, entry/exit, re-pricing. The reduced-form relationship shifts, which is exactly why you can't extrapolate it.
- Decomposition of a reduced-form effect into channels. A structural model lets you turn one mechanism off at a time and read how much each contributes — which the reduced-form effect bundles into a single number.
Identification — name what moves each parameter
The discipline that separates a credible structural estimate from a curve-fit: for every parameter, name the feature of the data — the variation, or the moment — that identifies it, and argue why it moves that parameter and not another. "The model is identified because the optimizer converged" is not identification; a non-identified parameter converges too, to a value the data never pinned down.
- Per parameter, what determines its movement. Heterogeneity (random-coefficient) parameters are identified by variation in choice sets / market composition that changes who faces what — not by a single market. A mean price coefficient is identified by cost-shifter variation that moves price for reasons unrelated to demand — price is endogenous, so you need instruments here exactly as in IV. Dynamic parameters (e.g., a switching cost) are identified by how choices respond to state variation over time. Make this map explicit; the modern tool is the sensitivity of estimates to moments (Andrews–Gentzkow–Shapiro) — which moments, if perturbed, move which parameter.
- The untestable core, stated. Like every design, there is a load-bearing assumption no data tests — the distributional form of the unobservables, the conduct/equilibrium assumption, the exclusion of an instrument. Name it and argue it; the counterfactual rests on it.
- The consideration/search non-identification red-line. Preferences and consideration are not separately identified without an exclusion restriction — a consideration (or search) shifter that moves the set or the search process but not utility: advertising exposure, shelf or search-result position, a default option, the rollout of a price-comparison tool. This is the structural analog of an instrument. Claiming to recover consideration or search costs without such a shifter is the structural version of "an effect with no named design" — STOP.
Write the model card — immediately, and keep it living
A structural model is the most expensive, least-reversible commitment in the family — days to weeks of coding, and the modeling choices (utility form, the random-coefficient distribution, conduct, what's a primitive vs. held fixed) silently decide what every downstream number means. So the moment you understand the model — even roughly — write it down as a model card (the project's living spec, in a file): the structure, and above all what would move each parameter and what variation/instrument identifies it. Write it before it's right — a parameter with nothing under "what moves it" is one you can't yet identify, and you want to see that on day one. Like a confirmatory pre-analysis-plan, the card earns a gate: get the user's sign-off before building estimation machinery — where "choosing the model is the user's decision" actually bites, before the compute is spent.
You usually enter mid-pipeline — "estimate this model", "fix the recovery", "run the counterfactual" — with machinery seemingly already in place. That does not mean a card exists; it almost always means none was written, so the greenfield "sign-off before building machinery" gate can't fire as stated. The first move, whatever was asked, is to write or reconstruct the card and get explicit sign-off on it before doing the named step. "Reconstruct and confirm" is a real sign-off, not a mention you breeze past — do not proceed on an unconfirmed card.
The card states (its filled-in instance of the five modeling rows in references/model-classes.md, plus the estimand/decision on top):
- Target counterfactual + the decision it informs (the estimand) — and why reduced form can't answer it.
- Primitives estimated, and what's held fixed/calibrated — and why those are policy-invariant here.
- Model — utility/payoff, the equilibrium concept, the DGP mapping primitives → observables.
- Identification, per parameter — what moves each, the shifter/instrument it leans on, the load-bearing untestable assumption. The heart of the card; a blank here is a parameter not yet identified.
- Estimation plan — estimator (GMM/MoM, NLS, MSL…), moments/likelihood, instruments, and the Monte-Carlo-recovery design that validates it.
- Counterfactual design — one scenario per mechanism, primitives changed vs. held fixed. This is the one row that starts as a sketch and is completed after estimation — the gate needs the target counterfactual (row 1), not the finished scenario set. Don't let designing scenarios block Monte-Carlo recovery or estimation; they come last in the pipeline, once the estimator is proven and the fit validated.
The card is living. Every later change is an edit to it, not a note in your head — refining as you learn is the point. But editability is not a backdoor around the gate: a load-bearing change (conduct, the random-coefficient distribution, primitive-vs-fixed, the estimand) routes through analysis-checkpoints as the user's call. And every fix beyond a trivial edit gets a three-line mini-spec on the card first — what's wrong, what changes, what "fixed" looks like (recovers θ from a distant start; gradient matches finite differences) — before you touch code. Trivial = a rename/typo/one-liner with no estimand/spec/sample/model decision; that you just do (analysis-craft).
Prove the algorithm recovers truth — Monte Carlo, before real data
You estimate by optimizing an objective — GMM / method of (simulated) moments, NLS, maximum (simulated) likelihood. Two things can be silently broken: the objective as you coded it, and whether the data identifies the parameters at all. Monte Carlo recovery catches both, and it is not optional:
- **Simulate from the model at a known θ★, then estimate starting from a θ₀ deliberately far from θ★, and confirm it converges back to θ★ (pass criterion in MC-SE units — references/estimation-and-gradients.md §3). The distant cold start is the real test: it checks that the objective is coded right, that the parameters are identified, and that the optimizer finds the truth rather than a comfortable local min next to where it started. To keep this loop affordable — structural estimators are slow — shrink the SAMPLE (N, markets, simulation draws), never θ's dimension**: the recovery run must estimate the full parameter vector and model structure you will take to data; recovering a smaller toy certifies nothing about the real estimator. If you cannot recover parameters from data you generated yourself, you cannot believe estimates from real data — full stop.
- Do it across the parameter space, not at one point — several true-θ draws — so you don't certify recovery only in a lucky region.
- Vary the sample size and watch the estimator concentrate on truth (a consistency check). If it doesn't tighten as N grows, suspect non-identification or a coding bug.
- Map the objective surface around the optimum — profile each parameter one at a time; a flat axis means that parameter is not identified (the optimizer still returns a number, but it's meaningless). Profiles catch only axis flatness, so also compute the eigenvalues of the Hessian (or the GMM Jacobian) at the optimum: a near-zero smallest eigenvalue flags non-identification along a combination of parameters (a ridge) that single-parameter profiles miss, and its eigenvector names the unidentified combination. Weak identification shows up as a near-flat valley and enormous variance across MC repetitions.
Run this before touching real data, and keep it as a regression test — this is data-contracts discipline applied to the estimator: assert recovery, then freeze it. The recipe and a language-agnostic harness skeleton are in references/estimation-and-gradients.md.
Analytical gradients — when the estimator admits them, derive them group by group
The estimator is an optimizer, and its speed and stability hinge on the gradient. A numerical (finite-difference) gradient is slow and noisy; a noisy gradient forces loose convergence tolerances, and loose tolerances on a nested inner loop (e.g., a share-inversion contraction) silently bias the gradient and the estimates (Dubé–Fox–Su). So:
- Automatic differentiation is a co-equal first option — ForwardDiff/Enzyme in Julia, JAX/PyTorch in Python: exact like a hand derivation, priced like code, and it composes with the implicit-function theorem for fixed-point inner loops. Check it once against finite differences, then trust it; reserve hand derivation for objectives AD can't traverse (external solvers, kinks, black-box inner loops).
- Otherwise assess whether a closed-form gradient/Jacobian is achievable for your objective — this is a property of the estimation algorithm, not the model. For GMM/NLS it's the Jacobian of the moments/residuals w.r.t. parameters; for MSL it's the score. Many IO objectives have closed-form derivatives even when the model itself has no closed-form solution — the implicit-function theorem
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: lancegui
- Source: lancegui/causal-powers
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.