VERSION 4.3 · OPEN SOURCE · MIT LICENSE · PYTHON 3.11+
Empirical macroeconomics in pure Python.
From SVARs and local projections to Bayesian DSGE, continuous-time heterogeneous agents and audited quantitative trade policy: one toolbox that runs on your laptop, in your browser and on an iPad.
pip install puremacro

16,200+
automated tests in the suite
90
example scripts and real-data replications
68
notebooks that run in your browser, in English and Spanish
0
compiled extensions of its own: pure Python on NumPy and SciPy
New in 4.3 · Gallery · From Stata & Dynare · Run it anywhere · Documentation · Changelog
MILESTONE 4.3
Audited structural solvers, consistent trade accounting, and Hicksian tariff policy
puremacro 4.3 checks its structural results against independent references: five live Dynare 7 models at orders two and three, scalar trade-accounting and primal expenditure benchmarks, and a complete 41-by-41 tariff-policy grid. A solve that fails no longer returns a payoff or a convergence certificate.
NEW IN 4.3 · SEPTEMBER 2026
Three new trade-policy tools, each with a notebook
Tariff games scored by consumption equivalent variation, solvers that recover from hard equilibria without relaxing the tolerance, and welfare that comes from an expenditure function instead of GDP. The figures come from notebooks 63–65, re-run for this page. The economies are small synthetic teaching tables, not forecasts. Click any figure to enlarge it.
TARIFF POLICY WITH HICKSIAN WELFARE · NOTEBOOK 63
Tariff games that separate a candidate from a verified best response
Unilateral optimal tariffs, fixed-action payoff matrices and multilateral Nash searches can now score outcomes by consumption equivalent variation against one fixed zero-tariff baseline. Every equilibrium in the search is audited. A candidate counts as converged only if the final simultaneous best-response gap and the regret relative to baseline consumption both pass. In the notebook’s economy both unilateral optima sit on the 30% ceiling, so they depend on that constraint, and the 0/10% payoff matrix is not a Prisoner’s Dilemma.
trade.compute_unilateral_optimal_tariff, trade.compute_welfare_payoff_matrix, trade.solve_multilateral_nash_tariffs
Open notebook 63 in your browser →

ROBUST EQUILIBRIUM SOLVING · NOTEBOOK 64
Keller continuation for equilibria that Newton cannot reach
Keller pseudo-arclength continuation lets the state and the continuation parameter move together, solving a bordered system that stays well conditioned through a turning point. solve_policy_equilibrium tries Newton, then a hybrid method, then Keller continuation, all at the requested tolerance. If all three fail it raises an error, so a failed deviation never yields a payoff. The notebook illustrates the numerics on a scalar fold; it does not claim an economic fold.
trade.solve_policy_equilibrium, trade.PolicyEquilibriumError, trade.solver.solve_keller_pac
Open notebook 64 in your browser →

CONSISTENT TRADE ACCOUNTING · NOTEBOOK 65
Welfare from an expenditure function, not from GDP
With accounting="consistent", trade equilibria keep producer and purchaser ledgers, complete duty schedules and independent physical and accounting checks. compute_hicksian_welfare computes EV and CV from a declared consumption expenditure function, excluding investment, and attributes EV exactly to purchaser prices, factor income and fiscal transfers, with the rebated duties counted once. In the notebook, a 10% duty raises the imposer’s nominal GDP by 3.6% while its consumption welfare barely moves. The attribution is endpoint accounting, not a causal terms-of-trade decomposition.
trade.compute_hicksian_welfare, trade.HicksianWelfareResult, trade.solve_trade_equilibrium(accounting="consistent")
Open notebook 65 in your browser →

Also in 4.3: native ingestion of the OECD 2023 regular ICIO tables with provenance and a frozen three-region fixture; corrected third-order DSGE risk-slope timing, with Dynare parity now checking steady states and every requested tensor; failed Markov-switching solves that no longer return finite certificates; and stricter convergence checks in VFI projection, collocation and Deep Macro. Full changelog.
UPGRADING FROM 4.2
- Historical trade accounting and welfare stay the defaults. To use the new model and objective, pass
accounting="consistent"andmetric="hicksian_ev"explicitly. - The historical TOT/Alloc/TariffRec welfare decomposition and the theorem-certification endpoints now raise
NotImplementedError, as do the unimplemented heterogeneous-agent IFT distribution and GE sensitivities. - Generated MRIO inputs require an explicit opt-in (
fallback_to_synthetic=True); real-data loaders expect a source file. solve_policy_equilibriumraisesPolicyEquilibriumErrorwhen Newton, hybrid and Keller recovery are exhausted, instead of returning an unconverged result.- Known issues targeted for 4.3.1 are listed in the changelog; the release baseline records 11 affected tests with workarounds.
LATEST RELEASES · UPDATED AUTOMATICALLY
- puremacro 4.3.0 adds audited structural solvers, consistent trade accounting and Hicksian tariff-policy analysis. Corrected VFI convergence and third-order DSGE risk terms, with ten frozen Dynare model/order comparisons. Opt-in accounting="consistent", expenditure-function EV/CV, and […]
- puremacro 4.2.0 — bilingual notebook polish, dual-mode b&w card styli…
- chore(release): bump version to 4.1.1
From the GitHub release feed. All releases · Changelog · PyPI history
Why puremacro
Built at ITAM–CIE for research and teaching, and engineered like production software.
PURE PYTHON
No compiler, no licence
The estimator core imports only NumPy, SciPy, pandas and Matplotlib. Nothing to compile, no MATLAB or Stata licence, and every documented result reproduces with Python alone.
RUNS ANYWHERE
Laptop, browser, iPad
The same code runs locally, in JupyterLite and under Pyodide. Heavy jobs offload to Google Colab; long bootstraps resume after the app is suspended.
SPEAKS DYNARE
Bring your .mod files
Load native Dynare models, solve to second order with pruning, handle the ZLB with OccBin and get oo_.dr-compatible decision rules.
PUBLICATION-READY
From estimate to LaTeX
Estimators return result objects with .summary(), .plot() and .to_latex(), so the last step to a paper table is one line.
What it can do
The empirical and structural toolkit of modern macroeconomics, in one library.
VARs & identification
Cholesky, long-run, sign and zero, narrative, proxy-IV, heteroskedasticity and non-Gaussian identification, with bootstrap and Bayesian bands.
Local projections & inference
LP-HAC, LP-IV, lag-augmented, panel, state-dependent, smooth and quantile LPs, with HAC, fixed-b and weak-IV-robust inference.
DSGE & Bayesian estimation
Native .mod files, second-order pruning, OccBin, perfect foresight, and NUTS sampling with exact likelihood gradients.
Heterogeneous agents
Sequence-space HANK with the Fake News algorithm, Aiyagari, Krusell–Smith, life-cycle and firm dynamics, continuous-time HJB–KFE, continuous-state and neural-network solvers.
Trade, space & climate
Caliendo–Parro trade, Allen–Arkolakis spatial equilibrium, spatial econometrics and a DICE climate–macro simulator.
Data, nowcasting & forecasting
FRED, OECD, IMF, ECB, Banxico and INEGI fetchers, real-time vintages, dynamic-factor nowcasts, text-based uncertainty indices, growth-at-risk and BVAR fan charts.
A REAL EXAMPLE
From live data to an impulse response
Pull U.S. real GDP and the federal funds rate straight from FRED, with no API key, estimate a Jordà local projection with 90% Newey–West bands, and plot it.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from puremacro.fetch import fetch_fred
from puremacro.lp.jorda import lp_hac
gdp = fetch_fred("GDPC1").dropna().resample("QS").last()
ff = fetch_fred("FEDFUNDS").dropna().resample("QS").mean()
df = pd.concat([np.log(gdp).rename("log_gdp"),
ff.rename("ff")], axis=1).dropna()
irf = lp_hac(df, y="log_gdp", x="ff",
horizons=range(0, 17), n_lags=2, alpha=0.10)
plt.plot(irf["h"], irf["beta"])
plt.fill_between(irf["h"], irf["lo"], irf["hi"], alpha=0.15)
Classic results, reproduced
Real-data replications ship with the package on frozen data snapshots and run fully offline. A built-in scorecard checks puremacro against each paper’s published numbers.



from puremacro.replication import scorecard
df = scorecard() # vs each paper's published number
df["passed"].all()
Every case compares puremacro’s estimate with the number printed in the paper, so a regression shows up as a failed row, not a silently different figure.
Coming from Stata, MATLAB or Dynare?
The commands you know, and their puremacro equivalents.
| Task | Stata / MATLAB / Dynare | puremacro |
|---|---|---|
| Cholesky SVAR | var y1 y2, lags(1/4) + irf create | var.identify.cholesky_svar(Y, p=4, horizon=20) |
| Local Projections (HAC) | jorda / manual OLS | lp.lp_hac(df, y="y", x="shock", horizon=20, lags=4) |
| Staggered DiD | csdid y, ivar(id) time(t) gvar(g) | did.callaway_santanna(df, unit="id", time="t", outcome="y", treat_time="g") |
| DSGE from equations / .mod | Dynare .mod file | dsge.load_mod("rbc.mod") / dsge.build_dynare(eqs) |
| Bayesian DSGE (MCMC) | Dynare estimation(...) (Metropolis-Hastings) | dsge.estimate_dsge_bayesian(m, data, priors, n_draws=10000) |
| Trade Exact Hat Algebra | Costinot-Rodríguez-Clare code | trade.CaliendoParroModel(...) / cp.solve_counterfactual(...) |
ON YOUR IPAD
Macroeconomics on an iPad, with Juno
Juno runs Python 3.13 natively on the iPad, offline, with NumPy, SciPy, pandas and Matplotlib preinstalled. puremacro is pure Python built on exactly those libraries, so it installs with Juno’s package manager and runs on the tablet itself.
- Knows where it is. puremacro recognises Juno, both its built-in Python and its in-browser Pyodide kernel, and works inside the iPad’s sandboxed file system.
- Brings its data. Pocket cartridges carry panels built on your workstation to the iPad, checked on arrival, even in airplane mode.
- Survives interruptions. Long bootstraps and MCMC chains resume after iPadOS suspends the app, with bit-identical results.
- Knows its limits.
runtime.fitsizes bootstraps to the device, and heavy jobs offload to Google Colab as a single notebook.
# In Juno, with puremacro installed
from puremacro import runtime
# What can this iPad do?
print(runtime.report())
# Size the bootstrap to the device
print(runtime.fit(n_boot=2000))
Detection and budgets are opt-in: the same script gives the same numbers on your laptop.
ENGINEERED FOR CORRECTNESS
Numbers you can defend
Research code has to be right before it is fast. puremacro treats correctness as a feature.
- Every push runs the test suite, about 16,200 tests, on three operating systems and three Python versions, plus mypy and a Pyodide-compatibility contract.
- A validation gallery cross-checks estimators against statsmodels, linearmodels and arch.
- A replication scorecard compares results with each paper’s published numbers.
- Clear diagnostic errors instead of silent garbage: singular matrices, non-positive-definite covariances and Blanchard–Kahn violations say what went wrong and where.
- Any released version that returned a wrong number is listed in a public correctness advisory.
Start in the browser, keep it on your laptop
Open the tour notebook in JupyterLite with nothing installed, then pip install puremacro when you are ready.
CITE PUREMACRO
@software{alonso_ortiz_puremacro,
author = {Alonso Ortiz, Jorge},
title = {puremacro: Production-grade Macroeconometric and
Heterogeneous-Agent Structural Modeling in Pure Python},
version = {4.3.0},
year = {2026},
url = {https://github.com/jalonso1979/puremacro}
}
Or use the repository’s CITATION.cff.

