Skip to content

Repository files navigation

Continuo

releasetestscoverageGitLab CI

Continuous-time DSGE toolbox in the spirit of Dynare. A Python toolbox for solving continuous-time macroeconomic models, with perfect-foresight deterministic transitions, multi-revelation surprises, and permanent parameter changes.

Status: pre-alpha (v0.0.3, 2026-06-16). The pipeline runs end to end — a .mod file solves to a path — and a reference manual ships with the source, but interfaces may still change. See CHANGELOG.md for what is in this release.

Features

  • Continuous-time perfect-foresight solver: stacked collocation (Crank–Nicolson) + Newton on a sparse two-point boundary value problem.
  • Multi-segment information structures: anticipated, permanent, and MIT-style surprise shocks, stitched by state continuity.
  • Macroprocessor compatible with Dynare's @#define, @#if, @#for, @#include (plus function macros, comprehensions, and a real-math library).
  • Symbolic model representation, sparsity-aware Jacobians and C codegen via CasADi; sparse linear solve via SciPy.
  • A CLI (continuo model.mod) and a programmatic Python API (import continuo).

Deferred: discretisation schemes beyond Crank–Nicolson (Radau, Lobatto-IIIA), adaptive meshes, and HDF5/parquet output.

Documentation

Installation

Continuo is pure Python (≥ 3.13). Install the latest release without cloning — the wheel is attached to every GitHub release:

pip install https://github.com/stepan-a/continuo/releases/download/v0.0.3/continuo-0.0.3-py3-none-any.whl

With the optional extras for the Solution conversion methods (to_dataframe, to_xarray, HDF5 output):

pip install "continuo[pandas,xarray,hdf5] @ https://github.com/stepan-a/continuo/releases/download/v0.0.3/continuo-0.0.3-py3-none-any.whl"

To hack on the source, clone and install editable:

git clone https://github.com/stepan-a/continuo
cd continuo
pip install -e ".[dev]"

Usage

A small model, rbc.mod — a one-sector growth model with an anticipated rise in productivity z at t = 5, starting 20% below the steady state:

var(state) K;
var(jump) C;
var Y;
varexo z;
parameters alpha, delta, rho;
alpha = 0.33; delta = 0.1; rho = 0.05;
model;
diff(K) = Y - C - delta * K; // capital accumulation
diff(C) = C * (alpha * Y / K - delta - rho); // Euler equation
Y = z * K^alpha; // production
end;
steady_state_model;
K = (alpha * z / (rho + delta))^(1 / (1 - alpha));
Y = z * K^alpha;
C = Y - delta * K;
end;
initval; K = 0.8 * steady_state(K); end; // start below the steady state
shocks; var z; path = if(t >= 5, 1.1, 1.0); end;
simulate(T=50, N=250);

From Python:

importcontinuomodel=continuo.parse("rbc.mod")
sol=model.simul() # reads the simulate commandsol.t# time grid (numpy array)sol["C"] # the consumption pathsol.K# attribute-style accessmodel.steady_state(exogenous={"z": 1.1}) # steady state under the new TFPsol.to_dataframe() # pandas DataFrame (optional extra)

From the command line (writes rbc.csv):

continuo rbc.mod
continuo rbc.mod -o out.csv -T 100 -N 500 # override output / horizon / grid

Solver benchmarks

The Newton solve runs on a pluggable linear backend (see the Linear solvers manual page). The table below compares the available backends across the example models; regenerate it with python examples/benchmark_solvers.py --write.

Wall-clock per solve (median, ms)

Modelnsuperlukluklu-nobtfumfpackpardiso
cagan20118.218.419.718.3261.0
dornbusch90323.323.823.423.6261.1
goodwin4802133.8122.6122.3133.0375.2
nk150379.582.579.879.2318.0
nk-nonlinear3005113.1109.6113.1113.4360.6
rbc100428.026.027.026.2255.2
solow60225.724.225.525.4268.0
tobinq90328.228.327.828.4273.1

Isolated linear solve — refactor + solve, warm (µs)

Modelnsuperlukluklu-nobtfumfpackpardiso
cagan20144.711.311.018.5459.4
dornbusch903160.425.631.6255.51004
goodwin4802834.9108.198.912992999
nk1503275.042.245.9447.02583
nk-nonlinear3005476.262.4129.9868.82051
rbc1004294.529.747.2284.81218
solow602117.120.418.3136.3530.9
tobinq903135.624.825.7184.0982.0

Peak resident memory (MiB)

Modelnsuperlukluklu-nobtfumfpackpardiso
cagan201102100100101139
dornbusch903103101101102142
goodwin4802116113112114169
nk1503105102103104146
nk-nonlinear3005112108109110158
rbc1004105103102103144
solow602103101101101141
tobinq903105102103103142

Reading these: the wall-clock above is end-to-endModel.simul(), dominated by the (solver-independent) CasADi build and residual/Jacobian evaluation — so on these small models the linear backend barely moves it. KLU's edge is in the linear solve itself: the isolated refactor + solve table (the warm per-Newton-step cost) shows it ~4–10× faster than SuperLU, a gap that grows with problem size and Newton iterations. PARDISO is far slower here only because MKL oversubscribes threads on these tiny systems — reserve it for large models. See the Linear solvers manual page for the full isolated tables.

Median of 5 runs of end-to-end Model.simul() (includes the CasADi build). Wall-clock in milliseconds; peak resident memory in MiB (whole process — the Python/CasADi/SciPy baseline dominates, and PARDISO loads MKL). Measured 2026-06-19 on AMD Ryzen AI 9 HX 370 w/ Radeon 890M, 24 cores, Python 3.13.14.

Running the testsuite

The tests live in tests/ and are driven by pytest. The repository is configured for nox, which builds an isolated virtual environment for each task — mirroring how CI runs. Install nox once (globally, not into the project):

pipx install nox # recommended; provides a `nox` command# or: pip install --user nox# or, on Debian/Ubuntu: apt install python3-nox (then invoke as `python3 -m nox`)

The Debian python3-nox package does not install a nox launcher on your PATH; if you used it, replace nox with python3 -m nox below. Then run the sessions:

nox # default sessions: lint + tests on the current Python
nox -s tests # tests across Python 3.13 and 3.14
nox -s lint # ruff check + format check (as in CI)
nox -s fix # apply ruff autofixes and reformat in place
nox -s coverage # tests with a coverage report
nox -l # list all sessions

Arguments after -- are forwarded to pytest, so you can narrow a run:

nox -s tests -- tests/parser # only the parser tests
nox -s tests -- -k shocks # only tests whose name matches "shocks"

The matrix in nox -s tests needs the corresponding interpreters installed; run a single one with e.g. nox -s tests-3.13.

Without nox

You can also run the tools directly. Install the package with its development dependencies in editable mode, ideally inside a virtual environment:

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"

Then, from the repository root:

pytest # full suite (tests/ is discovered automatically)
pytest -q --tb=short # quiet output, short tracebacks (as in CI)
pytest tests/parser # only the parser tests
pytest -k shocks # only tests whose name matches "shocks"
pytest --cov=continuo # with a coverage report
ruff check src tests # lint (as in CI)
ruff format --check src tests # formatting check (as in CI)

License

Released into the public domain under the Unlicense. See LICENSE.

Repository

https://github.com/stepan-a/continuo

About

A continuous-time DSGE Toolbox à la Dynare implemented in Python

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages