Cforecast
Conditional Forecasting and Scenario Analysis Using VAR Models (R package)
Install / Use
/learn @timginker/CforecastREADME
cforecast
cforecast is an R package that provides tools for conducting scenario analysis in reduced-form vector autoregressive (VAR) models. It implements a Kalman filtering framework to generate forecasts under path restrictions on selected variables. The package enables decomposition of conditional forecasts into variable-specific contributions and extraction of observation weights. It also computes measures of overall and marginal variable importance to enhance the economic interpretation of forecast revisions. The framework is structurally agnostic and suited for policy analysis, stress testing, and macro-financial applications.
The example below replicates an empirical experiment from:
Caspi, I., & Ginker, T. (2026). What Drives the Scenario? Interpreting Conditional Forecasts in Reduced-Form VARs.
The illustration demonstrates the workflow for scenario design, conditional forecasting, and forecast attribution.
Installation
Install the development version from GitHub:
# install.packages("devtools")
devtools::install_github("timginker/cforecast")
Empirical Illustration: Conditional Inflation Forecasting under Financial and Energy Price Scenarios
This section demonstrates how the conditional forecasting tools in
cforecast can be used to:
- Assess ex ante which conditioning assumptions are likely to materially affect a target forecast.
- Attribute ex post forecast revisions to specific elements of a multi-period conditioning path.
We consider a stylized policy scenario combining:
- A tightening in financial conditions (corporate credit spreads), and
- A temporary oil price disruption.
Such scenarios are typical in monetary policy and macro-financial stress-testing applications.
We use U.S. quarterly macroeconomic data (1986Q2–2015Q4) from FRED.
The dataset includes the following series:
- GDPC1 — Real GDP
- PCEPILFE — Core PCE price index
- FEDFUNDS — Federal funds rate
- BAA10YM — Corporate credit spread (Moody’s Baa minus 10-year Treasury)
- DCOILWTICO — WTI crude oil price
The VAR includes five variables:
- Real GDP growth
- Core PCE inflation
- Federal funds rate
- Corporate credit spread
- Oil price
Below, we estimate a reduced-form VAR(2) with a constant term and compute a 20-quarter baseline (unconditional) forecast.
suppressPackageStartupMessages({
library(cforecast)
library(tidyverse)
library(vars)
library(lubridate)
library(scales)
library(patchwork)
})
# Load packaged dataset
data(fred_macro)
data("DCOILWTICO_level")
# Restrict estimation sample
df <- fred_macro %>%
filter(year(date) >= 1986,
year(date) <= 2015)
# Estimate VAR(2)
fit <- VAR(df[, -1], p = 2, type = "const")
# Baseline forecast
pred_base <- predict(fit, n.ahead = 20)
Scenario Design
We construct a two-variable conditioning path affecting:
- The corporate credit spread (BAA10YM)
- The oil price (DCOILWTICO)
The scenario is designed as follows:
- Credit spreads increase by 200 basis points for three quarters and then gradually decay back toward baseline.
- Oil prices initially surge and then revert gradually.
This structure mimics a temporary macro-financial stress episode with persistent but fading effects.
The following plot creates the scenario and plots it:
# Slicing Oil data
DCOILWTICO_level %>%
mutate(date = as.Date(date)) %>%
dplyr::filter(year(date) >= 1986, year(date) <= 2015) %>%
slice(-1) -> DCOILWTICO_level
# Baseline path for oil prices (reconstructed in levels)
pred_base <- predict(fit, n.ahead = 20)
oil_base <- rep(NA, 20)
oil_base[1] <- tail(DCOILWTICO_level$DCOILWTICO, 1) *
(1 + pred_base$fcst$DCOILWTICO[1, 1] / 100)
for (i in 2:20) {
oil_base[i] <- oil_base[i - 1] *
(1 + pred_base$fcst$DCOILWTICO[i, 1] / 100)
}
# Imposed scenario paths for credit spreads and oil prices
delta <- 0.7
BAA10YM_future <- c(
rep(tail(df$BAA10YM, 1) + 2, 3),
(tail(df$BAA10YM, 1) + 2 * delta^(1:17))
)
DCOILWTICO_future <- c(
tail(DCOILWTICO_level$DCOILWTICO, 1) * (1.155061^(1:4)),
tail(DCOILWTICO_level$DCOILWTICO, 1) + 33.06 * delta^(1:16)
)
# Extended date vector covering the forecast horizon
dates_0 <- fred_macro$date[1:(nrow(df) + 20)]
# Construct plotting data for the credit-spread scenario input:
# - y: historical series (in-sample)
# - y_base: baseline (unconditional) VAR forecast appended after the sample end
# - y_fcst: imposed scenario path appended after the sample end
# Missing values are used to prevent ggplot from drawing lines outside the
# intended segments (history vs forecast).
df_plot <- data.frame(
date = dates_0,
y = c(df$BAA10YM, rep(NA,length(BAA10YM_future))),
y_base = c(rep(NA,nrow(df)-1),
tail(df$BAA10YM,1),pred_base$fcst$BAA10YM[,1]),
y_fcst = c(rep(NA,nrow(df)-1),
tail(df$BAA10YM,1),BAA10YM_future)
)
# Plot the credit-spread path:
# Solid line: historical data
# Dashed line: imposed scenario path used for conditioning
# Dotted line: baseline path implied by the VAR
p_baa <- ggplot(df_plot, aes(x = date)) +
geom_line(aes(y = y), color = "black", linewidth = 0.8) +
geom_line(
aes(y = y_fcst),
color = "black",
linewidth = 0.7,
linetype = "dashed"
) +
geom_line(
aes(y = y_base),
color = "black",
linewidth = 0.7,
linetype = "dotted"
)+
labs(
title = "Moody's Seasoned Baa Corporate Bond Yield Relative to \nYield on 10-Year Treasury Constant Maturity",
#caption = "Notes: The solid line shows historical data. The dashed line denotes the conditional \nforecast under the imposed scenario. The dotted line shows the unconditional (baseline) forecast.",
x = NULL,
y = "Percentage points"
) +
theme_minimal(base_size = 12) +
theme(
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank(),
plot.title = element_text(size = 12),
axis.text.y = element_text(size = 8)
#plot.title = element_text(face = "bold")
)
# Construct plotting data for the oil price scenario input (in levels):
# - y: historical oil price (levels series)
# - y_fcst: imposed scenario oil price path in levels
# - y_base: baseline oil price path reconstructed from VAR forecasts
# As above, NA padding separates the historical segment from forecast segments.
df_plot_oil <- data.frame(
date = dates_0,
y = c(DCOILWTICO_level$DCOILWTICO, rep(NA,length(DCOILWTICO_future))),
y_fcst = c(rep(NA,nrow(df)-1),
tail(DCOILWTICO_level$DCOILWTICO,1),DCOILWTICO_future),
y_base = c(rep(NA,nrow(df)-1),
tail(DCOILWTICO_level$DCOILWTICO,1),oil_base)
)
# Plot the oil price path:
# Solid line: historical data
# Dashed line: imposed scenario path used for conditioning
# Dotted line: baseline path implied by the VAR (in levels)
p_oil <- ggplot(df_plot_oil, aes(x = date)) +
geom_line(aes(y = y), color = "black", linewidth = 0.8) +
geom_line(
aes(y = y_fcst),
color = "black",
linewidth = 0.7,
linetype = "dashed"
) +
geom_line(
aes(y = y_base),
color = "black",
linewidth = 0.7,
linetype = "dotted"
)+
labs(
title = "WTI Crude Oil Price",
subtitle = "History (solid), baseline (dotted), and scenario (dashed)",
x = NULL,
y = "USD per barrel"
) +
theme_minimal(base_size = 12) +
theme(
panel.grid.minor = element_blank(),
panel.grid.major.x = element_blank(),
plot.title = element_text(size = 12),
axis.text.y = element_text(size = 8)
)
# Align x-axis limits across the two panels to ensure consistent time coverage
# and facilitate direct visual comparison of the two conditioning paths.
x_rng <- range(c(df_plot_oil$date, df_plot$date), na.rm = TRUE)
# Combine the two scenario-input panels into a single figure:
# Oil price on top; credit spread below. X-axis annotations are removed from
# the top panel to avoid duplication and improve readability.
combined_plot_scenarios <-
(p_oil + scale_x_date(limits = x_rng) +
theme(axis.title.x = element_blank(),
axis.text.x = element_blank(),
axis.ticks.x = element_blank())) /
(p_baa + scale_x_date(limits = x_rng))
suppressWarnings(combined_plot_scenarios)
<img src="man/figures/README-unnamed-chunk-3-1.png" alt="" width="70%" />
Interpreting Variable Importance
We begin by examining overall variable importance in the conditional forecast of core inflation. These measures are computed ex ante, given the conditioning design: the future paths of the corporate credit spread (Moody’s Baa–10Y Treasury spread) and WTI crude oil prices are constrained, while the remaining variables are left unconstrained.
The decomposition therefore quantifies the relative contribution of:
- The imposed conditioning paths, and
- Historical information embedded in the data.
Importantly, this assessment does not require realized future values.
The plot below reports the overall variable importance:
v_imp=variable_importance_stat(fit=fit,
cond_var = 4:5,
target_var = 2,
horizon = 20)
# Visualize variable importance as stacked shares by horizon (normalized to 100%)
plt = ggplot(v_imp$variable_importance,
aes(x = factor(horizon),
y = share,
fill = variable)) +
geom_col(position = "fill", width = 0.75, color = "white", linewidth = 0.2) +
scale_y_continuous(
labels = percent_format(acc
