Skip to content

Illustration

import marimo as mo

mo.md(
    """
    # OPERA mixed-frequency forecasting

    This notebook runs OLS and MIDAS forecasts, decomposes nowcast news,
    combines the forecasts, and compares accuracy across horizons.

    Install the ecosystem and notebook dependencies first:

    ```bash
    pip install "opera-eco[modules,notebooks]"
    ```
    """
)
import forecast_combo as fc
import forecast_evaluation as fe
import forecast_realtime as rt
import news_decomp as nd

from opera.sample_realtime_data import create_realtime_mixed_freq_data

target = "quarterly_a"
ols_regressors = [
    "quarterly_b",
    "quarterly_c",
    "quarterly_d",
    "quarterly_e",
]
midas_regressor = "monthly_a"
all_regressors = ols_regressors + [midas_regressor]

first_vintage = "2026-01-31"
last_vintage = "2026-12-31"
steps = 2
mixed_freq_data = create_realtime_mixed_freq_data()
data = fe.NowcastData(outturns_data=mixed_freq_data)
ols = rt.models.ForecastOLS(
    label="ols",
    formula=f"{target} ~ " + " + ".join(ols_regressors),
)

midas = rt.models.ForecastMIDAS(
    label="midas",
    method="almon",
    n_lags=5,
    estimator="ols",
    horizons=list(range(steps)),
    n_ar_lags=1,
    formula=f"{target} ~ {midas_regressor}",
)
realtime_model = rt.RealTimeModel(data=data, models=[ols, midas])

realtime_model.forecast(
    X_variables=all_regressors,
    data_transformation=dict.fromkeys([target, *all_regressors], "levels"),
    X_imputation="last",
    y_variables=[target],
    step_frequency="Q",
    steps=steps,
    first_vintage=first_vintage,
    last_vintage=last_vintage,
    decomp=True,
)

realtime_model.data.summary()
news = nd.NewsData(realtime_model.decompositions)
combo = fc.ForecastCombo(forecast_data=realtime_model.data)

combo.fit(
    sources=["ols", "midas"],
    variables=[target],
    method="rmse",
    metric="levels",
    label="rmse combo",
)
stats = fe.compute_accuracy_statistics(
    combo.forecast_data,
    variable=target,
).to_df()
stats = stats.loc[stats["metric"] == "levels"]
keep = [
    column
    for column in ("source", "forecast_horizon", "n_obs", "mae", "rmse")
    if column in stats
]
stats = stats[keep].sort_values(keep[:2]).reset_index(drop=True)

print("\nAccuracy:\n", stats)