Author: Patrick Schmidt
Date: 2026-07-21

# GDP Greenbook forecasts

#### Patrick Schmidt

#### 2026-07-21

# Data

    library(PointFore)
    #>
    #> Attaching package: 'PointFore'
    #> The following object is masked from 'package:stats':
    #>
    #>     lag
    library(ggplot2)

    ggplot(GDP)+
      geom_line(aes(x=date,y=observation))+
      geom_point(aes(x=date,y=forecast), color = 'red', size = 2, shape=4)

Time series of one-quarter ahead Greenbook forecasts (crosses) and respective observations (solid line) of real GDP growth in the US.

Next to this forecast, which was generated by taking the forecast issued closest to the midpoint of the quarter before the considered quarter, we also consider the forecast issued latest in the respective quarter.

    ggplot(GDP)+
      geom_line(aes(x=date,y=forecast-forecast_late), color = 'red',alpha=.5)+
      geom_point(aes(x=date,y=forecast-forecast_late), color = 'red', size = .5, shape=4)+
      ylim(-10,10)

Time series of difference between the two considered forecasts.

We see there is considerable adjustement. Starting 1980, there are normally 2 instead of 3 forecasts issued each quarter. The publication dates vary across time.

# Analysis

Now, let us analyse the forecasts. First, we consider the forecast issued closest to the midpoint of the respective quarter. Then, we analyze the latest issued forecast with potentially more information.

We begin by estimating and testing constant quantile forecasts. First, for the main forecast:

    res <- estimate.functional(model = constant,
                               state = GDP$forecast, Y=GDP$observation,X=GDP$forecast)
    #> Drop  1 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res)
    #> $call
    #> estimate.functional(model = constant, Y = GDP$observation, X = GDP$forecast,
    #>     stateVariable = GDP$forecast)
    #>
    #> $coefficients
    #>           Estimate Std. Error  t value     Pr(>|t|)
    #> Theta[1] 0.6017409 0.04428422 13.58816 4.707906e-42
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 2  ##
    #>
    #>                 J-test    P-value
    #> Test E(g)=0:    4.682043  0.096229

Optimality is rejected with a p-value of 0.1. If we consider less restrictive information sets, the same holds true.

    res <- estimate.functional(model = constant,
                               instruments = c("lag(lag(Y))","X"),
                               state = GDP$forecast, Y=GDP$observation,X=GDP$forecast)
    #> Drop  2 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res)
    #> $call
    #> estimate.functional(model = constant, Y = GDP$observation, X = GDP$forecast,
    #>     stateVariable = GDP$forecast, instruments = c("lag(lag(Y))",
    #>         "X"))
    #>
    #> $coefficients
    #>           Estimate Std. Error  t value     Pr(>|t|)
    #> Theta[1] 0.6043907 0.04349627 13.89523 6.770319e-44
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 2  ##
    #>
    #>                 J-test    P-value
    #> Test E(g)=0:    5.190767  0.074617

However, if we consider the forecast issued later in the quarter, the tests cannot reject optimality.

    res <- estimate.functional(model = constant,
                               state = GDP$forecast_late, Y=GDP$observation,X=GDP$forecast_late)
    #> Drop  1 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res)
    #> $call
    #> estimate.functional(model = constant, Y = GDP$observation, X = GDP$forecast_late,
    #>     stateVariable = GDP$forecast_late)
    #>
    #> $coefficients
    #>          Estimate Std. Error  t value     Pr(>|t|)
    #> Theta[1] 0.587664  0.0408971 14.36933 8.060606e-47
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 2  ##
    #>
    #>                 J-test   P-value
    #> Test E(g)=0:    2.28091  0.31967

    car::linearHypothesis(res$gmm,"Theta[1]=.5")
    #>
    #> Linear hypothesis test:
    #> Theta[1] = 0.5
    #>
    #> Model 1: restricted model
    #> Model 2: res$gmm
    #>
    #>   Df  Chisq Pr(>Chisq)
    #> 1
    #> 2  1 4.5947    0.03207 *
    #> ---
    #> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Noteworthy, the median forecast is still rejected, which is indicative of an optimal but asymmetric forecast. In the following we try to understand under which conditions the main forecast may be optimal.

## Replication of the results in “Interpretation of Point Forecasts with Unknown Directive” by Schmidt, Katzfuss and Gneiting

First, we replicate the main result from the original paper.

    res <- estimate.functional(model = probit_linear,
                               state = GDP$forecast,
                               instruments = c("X","lag(Y,2)"),
                               Y=GDP$observation,
                               X=GDP$forecast)
    #> Drop  2 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res)
    #> $call
    #> estimate.functional(model = probit_linear, Y = GDP$observation,
    #>     X = GDP$forecast, stateVariable = GDP$forecast, instruments = c("X",
    #>         "lag(Y,2)"))
    #>
    #> $coefficients
    #>            Estimate Std. Error    t value   Pr(>|t|)
    #> Theta[1] -0.1051757 0.16848538 -0.6242421 0.53246863
    #> Theta[2]  0.1147590 0.04533712  2.5312375 0.01136608
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 1  ##
    #>
    #>                 J-test   P-value
    #> Test E(g)=0:    0.46912  0.49339
    plot(res,hline = TRUE)
    #> Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
    #> ℹ Please use `linewidth` instead.
    #> ℹ The deprecated feature was likely used in the PointFore package.
    #>   Please report the issue to the authors.
    #> This warning is displayed once per session.
    #> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
    #> generated.
    #> Warning: The dot-dot notation (`..scaled..`) was deprecated in ggplot2 3.4.0.
    #> ℹ Please use `after_stat(scaled)` instead.
    #> ℹ The deprecated feature was likely used in the PointFore package.
    #>   Please report the issue to the authors.
    #> This warning is displayed once per session.
    #> Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
    #> generated.

State-dependent quantile analysis with a linear model (with probit) link that depends on the current forecast.

The evidence can be summarized as follows. The forecast indicates overshooting in the presence of extreme expected growth. There is strong evidence of overshooting in times of high growth (current forecast > 3) and weak evidence in times of low growth (current forecast < 0, recession expected). Further, there is no evidence that the forecast is sub-optimal as the optimality test has a p-value of 0.49. Hence, the assumption that the forecast is generate as state-dependent quantile \[X_t = q_{m(X_{t},\theta)}(Y_t|\mathcal{F}_t).\] seems valid.

Further, there is evidence that the forecasted level actually depends on the current forecast as the Wald-test for \(\theta_2=0\) has a p-value of 0.011. That is the assumption that the forecast is generate as optimal constant quantile forecast does not seem to hold:

\[X_t = q_{\tau}(Y_t|\mathcal{F}_t).\]

## Different information sets (instrumental vectors)

In the following we apply the same analysis with different instrumental vectors. The standard in the estimate.functional function is c(“lag(Y,2)”,“X”) which signifies that the instruments are given by \[w_t=(1,y_{t-2},x_t)'.\]

As first alternative, we consider that the lagged outcome \(Y_{t-1}\) may not be in the information set of the forecast. As instruments we use just the forecasts \[W_t = \left( 1, X_t, X_{t-1} \right)' '.\]

    res <- estimate.functional(model = probit_linear,
                               instruments = c( "X", "lag(X)"),
                               state = GDP$forecast, Y=GDP$observation,X=GDP$forecast)
    #> Drop  1 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res)
    #> $call
    #> estimate.functional(model = probit_linear, Y = GDP$observation,
    #>     X = GDP$forecast, stateVariable = GDP$forecast, instruments = c("X",
    #>         "lag(X)"))
    #>
    #> $coefficients
    #>            Estimate Std. Error   t value   Pr(>|t|)
    #> Theta[1] -0.1159270 0.16684755 -0.694808 0.48717566
    #> Theta[2]  0.1142447 0.04517868  2.528730 0.01144762
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 1  ##
    #>
    #>                 J-test   P-value
    #> Test E(g)=0:    0.23476  0.62802
    plot(res,hline = TRUE)

State-dependent quantile analysis with a linear model (with probit link) that depends on the current forecast with instruments \(w_t=(1, X_t, X_{t-1})\).

We observe similar results to the main specification in the paper: The test of overidentifying restrictions does not reject optimality, strong evidence of overshooting in times of high expected growth and weak evidence of undershootin in times of low expected growth.

Finally, we consider the larger set of instruments. That is the instruments are given by \[W_t = \left( 1, X_t, X_{t-1} - Y_{t-1}, (X_{t-1} - Y_{t-1})^2, X_{t-1}, X_{t-2} - Y_{t-2}, (X_{t-2} - Y_{t-2})^2 \right)' '.\]

    res <- estimate.functional(model = probit_linear,
                               instruments = c( "X", "lag(X-Y)", "lag(X-Y)^2",
                                                "lag(X)", "lag(lag(X-Y))", "lag(lag(X-Y))^2"),
                               state = GDP$forecast, Y=GDP$observation,X=GDP$forecast)
    #> Drop  2 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res)
    #> $call
    #> estimate.functional(model = probit_linear, Y = GDP$observation,
    #>     X = GDP$forecast, stateVariable = GDP$forecast, instruments = c("X",
    #>         "lag(X-Y)", "lag(X-Y)^2", "lag(X)", "lag(lag(X-Y))",
    #>         "lag(lag(X-Y))^2"))
    #>
    #> $coefficients
    #>            Estimate Std. Error    t value    Pr(>|t|)
    #> Theta[1] -0.1533958 0.16848627 -0.9104352 0.362593023
    #> Theta[2]  0.1209742 0.04522485  2.6749505 0.007474029
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 5  ##
    #>
    #>                 J-test   P-value
    #> Test E(g)=0:    3.60776  0.60715
    plot(res,hline = TRUE)

State-dependent quantile analysis with a linear model (with probit link) that depends on the current forecast with instruments \(w_t=(1, X_t, X_{t-1} - Y_{t-1}, (X_{t-1} - Y_{t-1})^2, X_{t-1}, X_{t-2} - Y_{t-2}, (X_{t-2} - Y_{t-2})^2\).

Again, the results are robust.

## Different specification models

In this section, we want to allow for other specification models as the linear model with probit link.

First, we consider splines in the state variable. We limit our analysis to quadratic and cubic splines. As the spline models have more parameters, we use again the high dimensional instrumental vector from the simulation setting.

    res_quadratic <- estimate.functional(model = probit_spline2,
                               instruments = c( "X", "lag(X-Y)", "lag(X-Y)^2",
                                                "lag(X)", "lag(lag(X-Y))", "lag(lag(X-Y))^2"),
                               state = GDP$forecast, Y=GDP$observation,X=GDP$forecast)
    #> Drop  2 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res_quadratic)
    #> $call
    #> estimate.functional(model = probit_spline2, Y = GDP$observation,
    #>     X = GDP$forecast, stateVariable = GDP$forecast, instruments = c("X",
    #>         "lag(X-Y)", "lag(X-Y)^2", "lag(X)", "lag(lag(X-Y))",
    #>         "lag(lag(X-Y))^2"))
    #>
    #> $coefficients
    #>             Estimate Std. Error    t value  Pr(>|t|)
    #> Theta[1] -0.25183189 0.25632463 -0.9824724 0.3258671
    #> Theta[2]  0.27779338 0.18581749  1.4949797 0.1349197
    #> Theta[3] -0.03009163 0.03003766 -1.0017968 0.3164418
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 4  ##
    #>
    #>                 J-test   P-value
    #> Test E(g)=0:    2.18990  0.70088

    res_cubic <- estimate.functional(model = probit_spline3,
                               instruments = c( "X", "lag(X-Y)", "lag(X-Y)^2",
                                                "lag(X)", "lag(lag(X-Y))", "lag(lag(X-Y))^2"),
                               state = GDP$forecast, Y=GDP$observation,X=GDP$forecast)
    #> Drop  2 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res_cubic)
    #> $call
    #> estimate.functional(model = probit_spline3, Y = GDP$observation,
    #>     X = GDP$forecast, stateVariable = GDP$forecast, instruments = c("X",
    #>         "lag(X-Y)", "lag(X-Y)^2", "lag(X)", "lag(lag(X-Y))",
    #>         "lag(lag(X-Y))^2"))
    #>
    #> $coefficients
    #>              Estimate Std. Error    t value  Pr(>|t|)
    #> Theta[1] -0.481640452 1.02273171 -0.4709353 0.6376869
    #> Theta[2]  0.258979328 0.31638803  0.8185497 0.4130434
    #> Theta[3]  0.041431094 0.14404165  0.2876327 0.7736279
    #> Theta[4] -0.009815827 0.02797997 -0.3508162 0.7257263
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 3  ##
    #>
    #>                 J-test   P-value
    #> Test E(g)=0:    1.71441  0.63373

    plot(res_quadratic,hline = TRUE)
    plot(res_cubic,hline = TRUE)

The specification models include the linear model as a special case. Hence, it is without surprise that the optimality tests are passed in this case, too. Again we observe that the results are largely in line with the main analysis in the paper in that there is strong evidence for overshooting in times of high expected growth and weaker evidence in times of low growth (or recession). The large confidence bands for more extreme forecasts indicate that the linear model arguably is overly confident for those areas for its strong parametric assumption.

## Different state variables

In this section, we use another typical state variable, namely the lagged value of the observation \(Y_{t-1}\).

    res <- estimate.functional(model = probit_linear,
                               state = lag(GDP$observation), Y=GDP$observation,X=GDP$forecast)
    #> Drop  1 case(s) because of chosen instruments
    #> Choose parameter theta0 automatically.
    summary(res)
    #> $call
    #> estimate.functional(model = probit_linear, Y = GDP$observation,
    #>     X = GDP$forecast, stateVariable = lag(GDP$observation))
    #>
    #> $coefficients
    #>            Estimate Std. Error   t value  Pr(>|t|)
    #> Theta[1] 0.22120389 0.13520456 1.6360683 0.1018253
    #> Theta[2] 0.01725534 0.02819891 0.6119152 0.5405939
    #>
    #> $Jtest
    #>
    #>  ##  J-Test: degrees of freedom is 1  ##
    #>
    #>                 J-test    P-value
    #> Test E(g)=0:    3.978189  0.046093
    #plot(res,hline = TRUE)

In this case, the optimality of the forecast is rejected with a p-value of 0.05. That is the assumption that the forecast is generate as state-dependent quantile does not seem to hold:

\[X_t = q_{m(Y_{t-1},\theta)}(Y_t|\mathcal{F}_t).\] In particular, this also rejects the subcase that the forecast is simply a optimal quantile forecast for any level.