Models¶
All built-in models live under rt.models and inherit from ForecastModel.
Lags are supplied at fit/forecast time (y_lags/X_lags), not in the
constructor.
Optional models are imported when their public attribute is first accessed.
Install the extra for the model you use, for example pip install
"forecast-realtime[ridge]".
| Model | Description |
|---|---|
ForecastOLS, ForecastRidge, ForecastLasso, ForecastElasticNet |
Linear models (shared LinearRegression base; support scale, recursive/direct strategy, formula) |
RandomForest, XGBoost |
Tree models (shared TreeRegression base; support standardise, formula, recursive/direct strategy) |
ForecastBVAR |
Bayesian VAR with conditional forecasting |
ForecastMIDAS, ForecastMultiMIDAS, ForecastMIDASCombo |
Mixed-frequency (MIDAS) models |
ForecastRlm |
Wrapper for an R-based lm() model |
import forecast_evaluation as fe
import forecast_realtime as rt
sample_data = rt.generate_synthetic_data(
N=2,
first_period="2015-01-31",
endpoint="2024-12-31",
)
forecast_data = fe.NowcastData(outturns_data=sample_data)
ols = rt.models.ForecastOLS(
label="ols",
formula="quarterly_1 ~ quarterly_2",
)
ridge = rt.models.ForecastRidge(cv=5, scale=True)
# Pass several models at once — each is run across every vintage
rt_model = rt.RealTimeModel(data=forecast_data, models=[ols, ridge])
Example with a built-in model¶
import forecast_evaluation as fe
import forecast_realtime as rt
sample_data = rt.generate_synthetic_data(
N=2,
first_period="2015-01-31",
endpoint="2024-12-31",
)
forecast_data = fe.NowcastData(outturns_data=sample_data)
ridge_model = rt.models.ForecastRidge(label="Ridge", cv=5, scale=True)
lasso_model = rt.models.ForecastLasso(label="LASSO", cv=5, scale=True)
rt_model = rt.RealTimeModel(data=forecast_data, models=[ridge_model, lasso_model])
rt_model.forecast(
y_variables=["quarterly_1"],
X_variables=["quarterly_2"],
data_transformation={"quarterly_1": "pop", "quarterly_2": "pop"},
steps=2,
y_lags=4,
first_vintage="2024-01-31",
last_vintage="2024-06-30",
X_imputation="last",
)
# Optional interactive dashboard:
# rt_model.data.run_dashboard()
Autoregressive penalisation¶
ForecastRidge, ForecastLasso, and ForecastElasticNet default to
penalise_ar=False, so retained target lags generated by y_lags are
unpenalised. Set penalise_ar=True to shrink those lags as well. Outlier
dummies remain unpenalised in either mode; ForecastOLS does not use this
option.
Cross-validation for regularised models¶
Set cv to an integer or a scikit-learn-compatible splitter. An integer uses
unshuffled K-fold cross-validation; a splitter, such as TimeSeriesSplit, is
used as supplied. Choose a splitter that matches the time ordering and
information available at the forecast origin. Ordinary K-fold CV can be valid
for autoregressive prediction when the fitted model captures the dependence
and its errors are uncorrelated (Bergmeir, Hyndman and Koo, 2018).
CV supports unpenalised AR terms and dummies. These fits minimise mean
validation squared error in the target's units, with FWL projection and scaling
fitted on each training fold. Validation scores use held-out design rows, not
recursive multi-step forecasts. With penalise_ar=False, AR-only designs use
least squares and skip CV;
alpha_ (Ridge) or best_alpha (Lasso and ElasticNet) is then None.
When alphas is omitted, Ridge uses 100 log-spaced values from 1e-4 to
1e2 in alpha_scaling units in both CV paths. Explicit alphas override
this grid.
Lasso and ElasticNet use 100 data-dependent, log-spaced candidates. With
unpenalised terms, CV compares relative strengths from each training fold's
grid; best_alpha reports the final full-sample penalty. Supply alphas to
compare the same absolute penalties across folds. ElasticNet also accepts a
sequence of l1_ratio values; include alphas when using l1_ratio=0.
For a time-ordered validation scheme, pass a scikit-learn-compatible splitter explicitly:
from sklearn.model_selection import TimeSeriesSplit
ridge = rt.models.ForecastRidge(cv=TimeSeriesSplit(n_splits=5), scale=True)
Reference: Bergmeir, C., Hyndman, R. J. and Koo, B. (2018), "A note on the validity of cross-validation for evaluating autoregressive time series prediction", Computational Statistics & Data Analysis, 120, 70-83. https://doi.org/10.1016/j.csda.2017.11.003
Models in other languages¶
Models written in R, MATLAB or Julia can be wrapped with RModel,
MATLABModel and JuliaModel. You provide fit and forecast functions in
the target language; the wrapper handles data exchange (Parquet), parameters
and the subprocess.
The supplied R, MATLAB or Julia script is executed by the corresponding runtime and must therefore be trusted code. The wrapper protects generated command and path literals, but it does not sandbox the external script.
from pathlib import Path
import forecast_realtime as rt
from forecast_realtime import RModel, MATLABModel, JuliaModel
# Resolve the script relative to this file so it works from any directory
model = RModel(str(Path(__file__).parent / "my_model.R"), p=4, shrinkage=0.5)
ets = rt.models.RFableETS(error="A", trend="A", season="N")
arima = rt.models.RFableARIMA(p=1, d=0, q=0)
See adding_a_model.md for the expected function signatures.
Density forecasts¶
Built-in density support is available for ForecastOLS and ForecastBVAR.
Penalised regressions (ForecastRidge, ForecastLasso, and
ForecastElasticNet), ForecastBridgeOLS, RandomForest, and XGBoost do
not support quantile forecasts.
ForecastOLS¶
OLS quantiles are the textbook prediction interval, the one returned by R's
predict.lm(interval = "prediction") and statsmodels'
get_prediction().summary_frame(). For n retained observations, design
rank r, and a forecast row x0 (intercept included):
The interval accounts for residual noise, coefficient uncertainty, and
uncertainty in s. The pseudo-inverse keeps rank-deficient designs valid. The
median equals the point forecast, and quantiles require positive residual
degrees of freedom.
Both forecast strategies are supported:
- Recursive: one regression; each future row has its own leverage. Target lags are not supported, because later rows would contain earlier forecasts and no closed form exists.
- Direct: one regression per horizon
h, ofy[t + h]on the origin row. Target lags are supported, because at the origin they are observed.
Future regressor rows must be complete. Supplied or imputed regressor paths are treated as known and add no regressor uncertainty.
ForecastBVAR¶
ForecastBVAR density forecasts require posterior draws. Keep
mode_only=False, the default; mode_only=True rejects quantile requests.
Leave N_burn=None unless you need to set it explicitly, so the backend can
derive burn-in from the effective draw count.
Quantiles are returned in the fitted model's native metric. Realtime does not
reconstruct levels or derive another metric from marginal quantile curves.
Regressor imputation¶
When X_imputation is requested, every regressor must have at least one
observed value. An all-missing regressor is rejected with a ValueError
rather than being silently removed from the fitted specification. Models that
own their own missing-value handling are not sent through this generic
imputation path.
Fable models¶
RFableModel, RFableETS and RFableARIMA wrap the R fable package.
They require an R installation with the arrow, fable, fabletools and
tsibble packages available; tests and fits invoke Rscript directly.
spec(RFableModelonly): a generic R fable model expression, e.g."ARIMA(value ~ 1 + pdq(1, 0, 0))". The response column is always namedvalue; regressors keep their Python column names.specis parsed and evaluated as trusted R code.RFableETSandRFableARIMAbuild this expression for you from their keyword arguments.index: how the date index is converted to a tsibble index — one of"auto"(inferred from the data's spacing),"quarter","month"or"date".allow_xreg/xreg:RFableModelaccepts anallow_xregflag that controls whether regressor columns inXare permitted.RFableARIMAinstead takes anxregstring naming the regressor term to add to the ARIMA formula (e.g.xreg="indicator"), which also setsallow_xreg=True.xregis an R expression and must come from a trusted source.