Calculating EGARCH-NIG Expected Shortfall with Standardized Quantiles
Summary
The document describes a troubleshooting example for estimating conditional expected shortfall, also called CVaR, from a fitted EGARCH model with a Normal Inverse Gaussian return distribution. The author computes rolling distribution parameters, integrates the lower-tail quantile function up to a chosen probability threshold, and compares the resulting series with observed Exxon returns. The initial expected shortfall values appear implausibly small relative to the returns.
The accepted answer identifies a scaling mismatch: the package’s NIG quantile function is standardized, so the proposed calculation should use its output directly with the fitted location and scale parameters as defined by the package, rather than applying an extra location-and-scale transformation inside the quantile function. The document reports that this change produces a more plausible plot, but offers no formal validation, numerical comparison, or discussion of tail-probability conventions. The example is implementation-specific, so parameterization and sign conventions should be checked against the relevant package documentation.
Key ideas
- The example estimates lower-tail expected shortfall from rolling EGARCH-NIG distribution parameters.
- The calculation integrates quantiles across the tail probability and divides by that probability.
- The reported issue comes from treating a standardized quantile output as if it required an additional transformation.
- A visual comparison is presented, but the document does not provide formal validation of the estimates.
Tags
Full text
# Backtesting EGARCH-NIG CVaR in R
# Backtesting EGARCH-NIG CVaR in R
I fitted an EGARCH model with a NIG distribution to a series of returns.
Using the following link I tried got how I should calculate the CVaR of the model
http://r.789695.n4.nabble.com/CVaR-with-NIG-GARCH-1-1-td4666903.html
The answer was from the person that created the rugarch package, which stunned me.
Anyway, I calculated the CVaR at each step as he exlained and I plotted the returns and the CVaR vector together the CVaR vector is filled with very small values, much smaller then the return values how can this be ? What am I doing wrong ?
The code I used is the following:
```
exon <- as.data.frame(Ad(getSymbols("XOM", src="yahoo", adjust = T, auto.assign = F, to = "2019-04-01"))) %>%
tibble::rownames_to_column() %>%
setNames(c("Date", "EXXON")) %>%
dplyr::arrange(Date)%>%
dplyr::mutate(Date = as.Date(Date))
returns_df <- as.data.frame(diff(log(exon$EXXON)))
n <- round((80 * nrow(returns_df))/100,0)
sarima_train <- returns_df %>% head(n)
egarch_spec <- ugarchspec(mean.model = list(armaOrder = c(2, 3), include.mean = T),
variance.model = list(model = "eGARCH", garchOrder = c(1, 1)),
distribution.model="nig")
egarch_fit <- ugarchfit(spec = egarch_spec, data = sarima_train[,"diff(log(exon$EXXON))"])
res_roll <- ugarchroll(egarch_spec, sarima_train[,"diff(log(exon$EXXON))"],n.start = 750, refit.every = 50, refit.window = "moving",
solver = "hybrid", calculate.VaR = TRUE, VaR.alpha = c(0.01, 0.025, 0.05), keep.coef = T,
solver.control = ctrl, fit.control = list(scale = 1))
gnigdist <- as.data.frame(res_roll, which = 'density')
f <- function(x, mu, sigma, skew, shape){
return(mu + qdist("nig", p=x, mu, sigma, skew=skew, shape=shape)*sigma)
}
es_nig <- function(p = 0.05){
mu <- gnigdist[, 'Mu']
sigma <- gnigdist[, 'Sigma']
shape <- gnigdist[, 'Shape']
skew <- gnigdist[, 'Skew']
lambda <- gnigdist[, 'Shape(GIG)']
ES <- rep(0, length(mu))
for(i in 1:length(mu)) {
ES[i] <- integrate(f, 0, p, mu = mu[i], sigma=sigma[i], shape =
shape[i], skew=skew[i])$value/p
}
return(ES)
}
exxon_es <- es_nig()
plot(tail(sarima_train[,"diff(log(exon$EXXON))"],1420))
lines(exxon_es, col = "red")
```
As you will see you will get the follwing plot :
And the CVaR values (in red) are really small compared to the real returns (in black)
Can someone please enlighten me ?
## Answer by meteoeliot (score 0, accepted)
https://quant.stackexchange.com/a/45868
The qdist function turned out to be standardized and so the VaR function is:
```
f <- function(x, mu, sigma, skew, shape){
return(qdist("nig", p=x, mu =mu , sigma = sigma, skew=skew, shape=shape))
}
```
Making this adjustment to the f function and then proceeding the same way you will obtain a good graph for the CVaR
```
es_nig <- function(p = 0.05){
mu <- gnigdist[, 'Mu']
sigma <- gnigdist[, 'Sigma']
shape <- gnigdist[, 'Shape']
skew <- gnigdist[, 'Skew']
lambda <- gnigdist[, 'Shape(GIG)']
ES <- rep(0, length(mu))
for(i in 1:length(mu)) {
ES[i] <- integrate(f, 0, p, mu = mu[i], sigma=sigma[i], shape =
shape[i], skew=skew[i])$value/p
}
return(ES)
}
exxon_es <- es_nig()
plot(tail(sarima_train[,"diff(log(exon$EXXON))"],1420))
lines(exxon_es, col = "red")
```Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.