Getting Started#
macroforecast runs a pseudo-out-of-sample forecasting study from a single
declarative specification. You describe the data, the targets, the forecast
horizons, the evaluation window, the competing models, and the scoring rule
once, and run_pipeline executes every combination and scores it. This page
takes you from install to a first result, then to a full study.
Installation#
For normal use, install the published package from PyPI:
pip install "macroforecast"
Install optional extras only when you need the corresponding backends:
Extra |
Adds |
|---|---|
|
Parquet checkpoint/result-store support through |
|
XGBoost model family. |
|
LightGBM model family. |
|
CatBoost model family. |
|
ARCH/GARCH volatility backends. |
|
Matplotlib-backed paper figure helpers. |
|
Macro Random Forest support utilities. |
|
SHAP/anatomy interpretation backends. |
|
Torch and Captum neural/attribution backends. |
|
Markdown/table rendering helpers. |
|
Sphinx documentation build dependencies. |
|
Common optional model, reporting, and interpretation extras. |
For example:
pip install "macroforecast[all]"
pip install "macroforecast[xgboost,arch]"
From a source checkout, use an editable install:
git clone https://github.com/NanyeonK/macroforecast.git
cd macroforecast
pip install -e ".[dev]"
Python 3.10 or later is required. Torch is not installed by default and is only needed for the neural-network model families.
Check a PyPI or wheel install without a repository checkout:
python - <<'PY'
import macroforecast as mf
print("macroforecast", mf.__version__)
print(mf.models.list_model_specs()[["name", "family", "default_preset"]].head())
PY
Key concepts#
Five ideas cover almost everything. The User Guide stage pages explains each in full and the Glossary defines every term.
The spec. A pipeline_spec bundles the data, targets, horizons, window,
arms, and evaluation rule into one frozen object. Running it returns a
PipelineReport with accuracy tables, significance tests, and raw forecasts.
Arms. An arm is one complete recipe of a preprocessing choice, a feature set, and a single model. The report compares arms head to head, so swapping a model or feature set means adding another arm rather than rewriting the pipeline.
Targets. A TargetSpec names the series you forecast and carries its own
transform and forecast policy. For FRED series these resolve from the official
transformation code, so INDPRO becomes a growth-rate forecast automatically.
Windows. A window defines the expanding or rolling estimation sample, an optional validation block for model selection, the test points, and the refit cadence. Everything is leak-aware, so no observation dated after the forecast origin can enter training.
Evaluation. Each arm is scored against a benchmark arm. Beyond RMSE, the report adds the Diebold-Mariano and Clark-West tests for pairwise significance and the Model Confidence Set for the joint set of best-performing models.
Quickstart#
The snippets below move from the smallest possible run, to a full study, to reproducing a published paper. Each one is self-contained.
A single forecast#
The minimal run pits an AR benchmark against a random forest on one target over a short test span.
import macroforecast as mf
from macroforecast.pipeline import Arm, EvalSpec, TargetSpec, pipeline_spec, run_pipeline
# Load FRED-MD (downloads if not cached; returns DataBundle).
bundle = mf.data.load_fred_md()
# Two arms: an AR benchmark (its own lags only) and a random forest that
# explicitly names a handful of panel predictors. Leaving an arm's `features`
# unset does NOT mean "no predictors" -- it resolves to every OTHER panel
# column at lags 0/1 with no feature engineering (a `UserWarning` says so if
# you hit it), which is rarely what an "AR vs RF" comparison wants for either
# side. `model_selection` turns off RF's per-origin hyperparameter search so
# this first run stays quick; "A full study" below tunes it.
arms = [
Arm(
name="AR", model="ar", is_benchmark=True,
features=mf.feature_engineering.feature_spec(
target="INDPRO", predictors=[], lags=None, target_lags=(1, 2, 3),
),
),
Arm(
name="RF", model="random_forest",
features=mf.feature_engineering.feature_spec(
target="INDPRO",
predictors=["UNRATE", "CPIAUCSL", "FEDFUNDS", "HOUST", "PAYEMS"],
lags=(0, 1),
),
model_selection={"random_forest": None},
),
]
# A minimal expanding window over a short test span; one target.
window = mf.window.from_cutoffs(test_start="2010-01-01", test_end="2019-12-01")
targets = [TargetSpec(name="INDPRO")]
spec = pipeline_spec(
data=bundle,
targets=targets,
horizons=[1],
window=window,
arms=arms,
evaluation=EvalSpec(benchmark="AR"),
)
report = run_pipeline(spec)
print(report.accuracy) # relative-accuracy table by target/horizon/arm
target horizon contender ... n_common is_benchmark benchmark_present
0 INDPRO 1 AR ... 95 True True
1 INDPRO 1 RF ... 95 False True
[2 rows x 9 columns]
This first run is for a fast, genuine result, not a claim that random forest
beats AR: with only five untuned predictors at horizon 1, AR has the lower
RMSE here (relative_mse above 1 for RF). “A full study” below adds real
preprocessing, a MARX feature ladder over the whole panel, and several
horizons – the comparison most papers actually care about.
A full study#
A full study adds preprocessing, feature engineering, several horizons, and the significance tests. The arms now differ in their feature sets, and the report compares them across the whole horizon grid.
import macroforecast as mf
from macroforecast.pipeline import Arm, EvalSpec, TargetSpec, pipeline_spec, run_pipeline
mf.configure(random_seed=42, n_jobs=1)
bundle = mf.data.load_fred_md()
# Preprocessing: official t-code transforms, IQR outliers, EM-factor imputation.
prep = mf.preprocessing.preprocess_spec(
transform="official", outliers="iqr", impute="em_factor", standardize="zscore",
)
# Expanding window with a last-block validation split for model selection.
window = mf.window.from_cutoffs(
test_start="1985-01-01", test_end="2019-12-01",
mode="expanding", val_method="last_block", horizon=1, step=1,
)
# Feature engineering: a MARX moving-average lag ladder over all predictors.
features = mf.feature_engineering.feature_spec(
target="INDPRO", predictors="all", lags=None,
feature_steps=[mf.feature_engineering.marx_step(name="MARX_X", max_lag=12)],
)
# AR benchmark uses target-only lags; RF adds the MARX predictors.
arms = [
Arm(
name="AR", model="ar", is_benchmark=True,
features=mf.feature_engineering.feature_spec(
target="INDPRO", predictors=[], lags=None, target_lags=range(1, 13),
),
),
Arm(name="RF", model="random_forest", preprocessing=prep, features=features),
]
spec = pipeline_spec(
data=bundle,
targets=[TargetSpec(name="INDPRO")],
horizons=[1, 3, 6, 12],
window=window,
arms=arms,
evaluation=EvalSpec(
benchmark="AR",
metrics=("rmse", "relative_mse", "r2_oos"),
tests=("dm", "cw", "mcs"),
),
)
report = run_pipeline(spec)
print(report.accuracy) # relative-accuracy table by target/horizon/arm
print(report.significance) # DM and CW p-values
print(report.mcs) # Model Confidence Set membership
A fully annotated, step-by-step version of this pipeline lives in the Replication Gallery.
Reproduce a published study#
Replications ship as runnable scripts rather than a single call. Run one in smoke mode to check your install, then see the Replication Gallery for the full studies and the paper-versus-code comparison notes.
python -m scripts.replication.gcls_2021_pipeline.run_pipeline_full --smoke
What Comes Next#
The User Guide stage pages explain each step in detail. The Glossary defines every term used above. The Models and Features page lists every feature step and registered model. The Replication Gallery shows full published-paper replication examples.