Overview

In one of my earlier notes on GARCH models (see link here), I demonstrated how to simulate, estimate, and forecast volatility models. In this vignette, I would like to demonstrate how to deploy a machine learning (henceforth ML) approach for volatility forecasting.

Simulating Data

Applying a similar code from these notes I simulate a time series that exhibits a GARCH-like data over 1000 periods.

library(fGarch)
seed <- 17
sim_garch <- function(theta,n) {
  
  # set the model parameters
  con <- theta[1]
  w <- theta[2]
  a <- theta[3]
  b <- theta[4]
  
  sigma_t <- sqrt(w/(1-a-b)) # start with initial volatility 
  ht_seq <- numeric()
  R_seq <- numeric()
  
  set.seed(seed)
  
  for(i in 1:n) {
    R_t1 <- rnorm(1,con,sigma_t)
    R_seq <- c(R_seq,R_t1)
    ht1 <- w + a*(R_t1^2) + b*sigma_t^2
    ht_seq <- c(ht_seq,ht1)
    sigma_t <- sqrt(ht1)
  }
  
  list(R_t = R_seq,h_t = ht_seq)
}
# set the number simulated returns
n <- 10^3
con <- 0
w <- 0.1
a <- 0.2
b <- 0.5
par_0 <- c(con,w,a,b)
sim1 <- sim_garch(par_0,n)
h_t <- sim1$h_t
R_t <- sim1$R_t
plot(sim1$h_t, type = "l", ylab = expression(h[t]))
grid(10)

Main Objective

Suppose we would like to forecast the volatility over the last 10% periods on a dynamic basis. To do so, let’s first split the data:

N_test <- 0.1*n
index <- 1:n
index_test <- (n - N_test + 1):n
index_train <- index[!index %in% index_test]
h_train <- h_t[index_train]
h_test <- h_t[index_test]
R_train <- R_t[index_train]
R_test <- R_t[index_test]

The question remains how can we forecast the h_test process given the h_train? To answer this, I will consider two approaches. One is a model driven (GARCH) and the other is data driven (ML).

Model Based Forecast

A simple approach to forecast future volatility is to build the volatility term structure using GARCH. To do so, we need to calibrate a GARCH model, which requires an estimation procedure using actual data. The volatility term structure is constructed to evaluate the convergence of the current volatility to the long-term one. In other words, it helps us to assess whether the current volatility level is greater or less than the long-term one. Also, it answers the question how long it would take the current volatility to converge back to the long-term level.

For back-testing, it is more realistic to construct the forecast on a dynamic basis. This can be achieved by considering the realized volatility as an update in the next period volatility forecasting procedure. This can be achieved as follows

g_train <- garchFit(formula = ~ garch(1,1), data = R_train ,trace = F)
theta_hat <-  g_train@fit$coef
gamma <- theta_hat[3] + theta_hat[4]
w <- theta_hat[2]
h_for <-  h_t[length(index_train)]
h_fit <- numeric()
for(i in 1:length(index_test)){ 
  h_next <- w + gamma*h_t[length(index_train)+i-1]
  h_fit <- c(h_fit,h_next)
}
{
  plot(h_test~index_test, type = "l", ylab = expression(h[t]), col = 1)
  abline(h = w/(1-gamma),lty = 2)
  lines(h_fit~index_test,col = 3,lty = 1)
  legend("topleft",c("True","GARCH"), col = c(1,3))
  grid(10)
}

To see how the two compare, we can plot the realized versus the forecast.

{
  plot(h_test~h_fit,pch = 20,cex = 0.5)
  abline(a=0,b=1,lty  = 2)
  grid(10)
}

In terms of regression, we have

lm_garch <- lm(h_test~h_fit)
stargazer::stargazer(lm_garch,type = "html",covariate.labels = "GARCH Forecast",dep.var.labels = "Actual Volatility")
Dependent variable:
Actual Volatility
GARCH Forecast 0.970***
(0.086)
Constant -0.017
(0.028)
Observations 100
R2 0.562
Adjusted R2 0.558
Residual Std. Error 0.066 (df = 98)
F Statistic 125.816*** (df = 1; 98)
Note: p<0.1; p<0.05; p<0.01

We observe that the GARCH forecasts closely mimic the actual test data, i.e. intercept of zero, slope roughly 1, and \(R^2\) of 56%. Nonetheless, the main issue here is that the data set, by construction, follows a GARCH model. Hence, this should not come as a surprise. The more challenging and realistic task is whether the data in real-life is stationary and obeys to such data generating function.

Machine Learning Approach

A data-driven approach does not possess information about the underlying process of the data. Hence, an ML approach would learn the volatility behavior from the data without imposing a structural model. The task here is to investigate the capability of the ML to map the data into a volatility forecast and compare it with respect to its benchmark, the GARCH model.

I use a similar code from my previous notes for implementation of ML algorithms. In this case, I focus on the support vector machines (SVM) with a radial kernel. To do so, my feature space is constructed using the current process itself (squared) and the current log-realized volatility. The outcome variable, on the other hand, is given by the next period log-realized volatility. Note that we use a log transformation, followed by an exponential one, to make sure the fitted volatilities are non-negative.

Similar to the previous analysis, we split the data into in-sample and out-of-sample sets. Additionally, for a more efficient implementation, I refer to the doParallel package for parallel computing on an 8-core laptop as demonstrated below:

library(caret)
library(doParallel)
ds <- na.omit(data.frame(h = log(h_t), r = R_t^2, y = log(c(h_t[-1],NA)) ))
index <- 1:nrow(ds)
index_train2 <- index_train
index_train2 <- index_train2[-length(index_train2)] # avoid the look ahead observation
index_test <- index[!index %in% index_train2]
ds_train <- ds[index_train,]
ds_test <- ds[index_test,]
model_i <- "svmRadial"
trctrl <- trainControl(method = "repeatedcv", number = 10, repeats = 5)
cl <- makePSOCKcluster(8)
registerDoParallel(cl)
train_model <- train(y~ h + r, data = ds_train, method = model_i,
                     trControl=trctrl,
                     preProcess = c("center", "scale"),
                     tuneLength = 10)
stopCluster(cl)

Given the trained model, we forecast the next period using the test dataset and use the exponential transformation to retrieve the volatility level. As a summary, we plot altogether:

h_ml <- exp(predict(train_model,ds_test))
{
  plot(h_test~index_test,type = "l")
  lines(h_ml~index_test,col = 2)
  lines(h_fit~index_test,col = 3)
  legend("topleft",c("True","ML","GARCH"))
  grid(10)
}

From the above graph, we observe that the ML algorithm closely mimics the GARCH model in terms of forecasts. Since the data in our case is stationary, learning from historical data should be relevant for future applications. At the same time, we discern that the ML algorithm is able to uncover the auto-regressive component in the volatility process.

In terms of regression, we note the ML forecast closely corresponds to the GARCH statistics, with a slightly smaller \(R^2\). This can be inferred from the following regression:

lm_ml <- lm(h_test~h_ml)
stargazer::stargazer(list(lm_ml,lm_garch),type = "html",covariate.labels = c("ML Forecast","GARCH Forecast"),dep.var.labels = "Actual Volatility")
Dependent variable:
Actual Volatility
(1) (2)
ML Forecast 1.097***
(0.103)
GARCH Forecast 0.970***
(0.086)
Constant -0.014 -0.017
(0.029) (0.028)
Observations 100 100
R2 0.537 0.562
Adjusted R2 0.532 0.558
Residual Std. Error (df = 98) 0.068 0.066
F Statistic (df = 1; 98) 113.545*** 125.816***
Note: p<0.1; p<0.05; p<0.01

Concluding Remarks

The above experiment is conducted using simulated data from a GARCH model. For this, it is not surprising that the GARCH model provides a better approximation of future realized volatility. However, two major issues should be considered as a primary investigation. First, how does the result change if there is a structural break in the data? For instance, if the data is non-stationary, how does each model compare? Second, what is the economic value of deploying ML versus GARCH models?

For the first issue, we may add shocks to the training sample and test the sensitivity in the test set. In other words, which approach is more robust? We should get a better picture using real data. For the second issue, a portfolio selection problem is of high relevance. We can test the economic performance of global minimum portfolio constructed using either model. I leave both for future research.

LS0tCnRpdGxlOiAiVm9sYXRpbGl0eSBNb2RlbHMgdmlhIE1hY2hpbmUgTGVhcm5pbmciCiNvdXRwdXQ6IHJtYXJrZG93bjo6Z2l0aHViX2RvY3VtZW50Cm91dHB1dDoKICBodG1sX25vdGVib29rOiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0CmF1dGhvcjogTWFqZWVkIFNpbWFhbgpkYXRlOiBEZWMgMjgsIDIwMTkKZmlnX3dpZHRoOiA1MAotLS0KCiMgT3ZlcnZpZXcKSW4gb25lIG9mIG15IGVhcmxpZXIgbm90ZXMgb24gR0FSQ0ggbW9kZWxzIChzZWUgbGluayBbaGVyZV0oaHR0cHM6Ly9ycHVicy5jb20vc2ltYWFuODQvZ2FyY2gpKSwgSSBkZW1vbnN0cmF0ZWQgaG93IHRvIHNpbXVsYXRlLCBlc3RpbWF0ZSwgYW5kIGZvcmVjYXN0IHZvbGF0aWxpdHkgbW9kZWxzLiBJbiB0aGlzIHZpZ25ldHRlLCBJIHdvdWxkIGxpa2UgdG8gZGVtb25zdHJhdGUgaG93IHRvIGRlcGxveSBhIG1hY2hpbmUgbGVhcm5pbmcgKGhlbmNlZm9ydGggTUwpIGFwcHJvYWNoIGZvciB2b2xhdGlsaXR5IGZvcmVjYXN0aW5nLiAKCgojIFNpbXVsYXRpbmcgRGF0YQpBcHBseWluZyBhIHNpbWlsYXIgY29kZSBmcm9tIHRoZXNlIFtub3Rlc10oaHR0cHM6Ly9ycHVicy5jb20vc2ltYWFuODQvZ2FyY2gpIEkgc2ltdWxhdGUgYSB0aW1lIHNlcmllcyB0aGF0IGV4aGliaXRzIGEgR0FSQ0gtbGlrZSBkYXRhIG92ZXIgMTAwMCBwZXJpb2RzLiAKYGBge3IsZmlnLmFsaWduPSdjZW50ZXInLG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZkdhcmNoKQoKc2VlZCA8LSAxNwoKc2ltX2dhcmNoIDwtIGZ1bmN0aW9uKHRoZXRhLG4pIHsKICAKICAjIHNldCB0aGUgbW9kZWwgcGFyYW1ldGVycwogIGNvbiA8LSB0aGV0YVsxXQogIHcgPC0gdGhldGFbMl0KICBhIDwtIHRoZXRhWzNdCiAgYiA8LSB0aGV0YVs0XQogIAogIHNpZ21hX3QgPC0gc3FydCh3LygxLWEtYikpICMgc3RhcnQgd2l0aCBpbml0aWFsIHZvbGF0aWxpdHkgCiAgaHRfc2VxIDwtIG51bWVyaWMoKQogIFJfc2VxIDwtIG51bWVyaWMoKQogIAogIHNldC5zZWVkKHNlZWQpCiAgCiAgZm9yKGkgaW4gMTpuKSB7CiAgICBSX3QxIDwtIHJub3JtKDEsY29uLHNpZ21hX3QpCiAgICBSX3NlcSA8LSBjKFJfc2VxLFJfdDEpCiAgICBodDEgPC0gdyArIGEqKFJfdDFeMikgKyBiKnNpZ21hX3ReMgogICAgaHRfc2VxIDwtIGMoaHRfc2VxLGh0MSkKICAgIHNpZ21hX3QgPC0gc3FydChodDEpCiAgfQogIAogIGxpc3QoUl90ID0gUl9zZXEsaF90ID0gaHRfc2VxKQp9CgojIHNldCB0aGUgbnVtYmVyIHNpbXVsYXRlZCByZXR1cm5zCm4gPC0gMTBeMwoKY29uIDwtIDAKdyA8LSAwLjEKYSA8LSAwLjIKYiA8LSAwLjUKCnBhcl8wIDwtIGMoY29uLHcsYSxiKQoKc2ltMSA8LSBzaW1fZ2FyY2gocGFyXzAsbikKaF90IDwtIHNpbTEkaF90ClJfdCA8LSBzaW0xJFJfdAoKcGxvdChzaW0xJGhfdCwgdHlwZSA9ICJsIiwgeWxhYiA9IGV4cHJlc3Npb24oaFt0XSkpCmdyaWQoMTApCmBgYAoKCiMgTWFpbiBPYmplY3RpdmUKU3VwcG9zZSB3ZSB3b3VsZCBsaWtlIHRvIGZvcmVjYXN0IHRoZSB2b2xhdGlsaXR5IG92ZXIgdGhlIGxhc3QgMTAlIHBlcmlvZHMgb24gYSBkeW5hbWljIGJhc2lzLiBUbyBkbyBzbywgbGV0J3MgZmlyc3Qgc3BsaXQgdGhlIGRhdGE6CmBgYHtyfQpOX3Rlc3QgPC0gMC4xKm4KaW5kZXggPC0gMTpuCmluZGV4X3Rlc3QgPC0gKG4gLSBOX3Rlc3QgKyAxKTpuCmluZGV4X3RyYWluIDwtIGluZGV4WyFpbmRleCAlaW4lIGluZGV4X3Rlc3RdCmhfdHJhaW4gPC0gaF90W2luZGV4X3RyYWluXQpoX3Rlc3QgPC0gaF90W2luZGV4X3Rlc3RdClJfdHJhaW4gPC0gUl90W2luZGV4X3RyYWluXQpSX3Rlc3QgPC0gUl90W2luZGV4X3Rlc3RdCmBgYApUaGUgcXVlc3Rpb24gcmVtYWlucyBob3cgY2FuIHdlIGZvcmVjYXN0IHRoZSBgaF90ZXN0YCBwcm9jZXNzIGdpdmVuIHRoZSBgaF90cmFpbmA/IFRvIGFuc3dlciB0aGlzLCBJIHdpbGwgY29uc2lkZXIgdHdvIGFwcHJvYWNoZXMuIE9uZSBpcyBhIG1vZGVsIGRyaXZlbiAoR0FSQ0gpIGFuZCB0aGUgb3RoZXIgaXMgZGF0YSBkcml2ZW4gKE1MKS4KCiMjIE1vZGVsIEJhc2VkIEZvcmVjYXN0CkEgc2ltcGxlIGFwcHJvYWNoIHRvIGZvcmVjYXN0IGZ1dHVyZSB2b2xhdGlsaXR5IGlzIHRvIGJ1aWxkIHRoZSB2b2xhdGlsaXR5IHRlcm0gc3RydWN0dXJlIHVzaW5nIEdBUkNILiBUbyBkbyBzbywgd2UgbmVlZCB0byBjYWxpYnJhdGUgYSBHQVJDSCBtb2RlbCwgd2hpY2ggcmVxdWlyZXMgYW4gZXN0aW1hdGlvbiBwcm9jZWR1cmUgdXNpbmcgYWN0dWFsIGRhdGEuIFRoZSB2b2xhdGlsaXR5IHRlcm0gc3RydWN0dXJlIGlzIGNvbnN0cnVjdGVkIHRvIGV2YWx1YXRlIHRoZSBjb252ZXJnZW5jZSBvZiB0aGUgY3VycmVudCB2b2xhdGlsaXR5IHRvIHRoZSBsb25nLXRlcm0gb25lLiBJbiBvdGhlciB3b3JkcywgaXQgaGVscHMgdXMgdG8gYXNzZXNzIHdoZXRoZXIgdGhlIGN1cnJlbnQgdm9sYXRpbGl0eSBsZXZlbCBpcyBncmVhdGVyIG9yIGxlc3MgdGhhbiB0aGUgbG9uZy10ZXJtIG9uZS4gQWxzbywgaXQgYW5zd2VycyB0aGUgcXVlc3Rpb24gaG93IGxvbmcgaXQgd291bGQgdGFrZSB0aGUgY3VycmVudCB2b2xhdGlsaXR5IHRvIGNvbnZlcmdlIGJhY2sgdG8gdGhlIGxvbmctdGVybSBsZXZlbC4gCgpGb3IgYmFjay10ZXN0aW5nLCBpdCBpcyBtb3JlIHJlYWxpc3RpYyB0byBjb25zdHJ1Y3QgdGhlIGZvcmVjYXN0IG9uIGEgZHluYW1pYyBiYXNpcy4gVGhpcyBjYW4gYmUgYWNoaWV2ZWQgYnkgY29uc2lkZXJpbmcgdGhlIHJlYWxpemVkIHZvbGF0aWxpdHkgYXMgYW4gdXBkYXRlIGluIHRoZSBuZXh0IHBlcmlvZCB2b2xhdGlsaXR5ICBmb3JlY2FzdGluZyBwcm9jZWR1cmUuIFRoaXMgY2FuIGJlIGFjaGlldmVkIGFzIGZvbGxvd3MKCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIn0KZ190cmFpbiA8LSBnYXJjaEZpdChmb3JtdWxhID0gfiBnYXJjaCgxLDEpLCBkYXRhID0gUl90cmFpbiAsdHJhY2UgPSBGKQp0aGV0YV9oYXQgPC0gIGdfdHJhaW5AZml0JGNvZWYKZ2FtbWEgPC0gdGhldGFfaGF0WzNdICsgdGhldGFfaGF0WzRdCncgPC0gdGhldGFfaGF0WzJdCmhfZm9yIDwtICBoX3RbbGVuZ3RoKGluZGV4X3RyYWluKV0KaF9maXQgPC0gbnVtZXJpYygpCmZvcihpIGluIDE6bGVuZ3RoKGluZGV4X3Rlc3QpKXsgCiAgaF9uZXh0IDwtIHcgKyBnYW1tYSpoX3RbbGVuZ3RoKGluZGV4X3RyYWluKStpLTFdCiAgaF9maXQgPC0gYyhoX2ZpdCxoX25leHQpCn0KCnsKICBwbG90KGhfdGVzdH5pbmRleF90ZXN0LCB0eXBlID0gImwiLCB5bGFiID0gZXhwcmVzc2lvbihoW3RdKSwgY29sID0gMSkKICBhYmxpbmUoaCA9IHcvKDEtZ2FtbWEpLGx0eSA9IDIpCiAgbGluZXMoaF9maXR+aW5kZXhfdGVzdCxjb2wgPSAzLGx0eSA9IDEpCiAgbGVnZW5kKCJ0b3BsZWZ0IixjKCJUcnVlIiwiR0FSQ0giKSwgY29sID0gYygxLDMpKQogIGdyaWQoMTApCn0KYGBgCgpUbyBzZWUgaG93IHRoZSB0d28gY29tcGFyZSwgd2UgY2FuIHBsb3QgdGhlIHJlYWxpemVkIHZlcnN1cyB0aGUgZm9yZWNhc3QuCmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIn0KewogIHBsb3QoaF90ZXN0fmhfZml0LHBjaCA9IDIwLGNleCA9IDAuNSkKICBhYmxpbmUoYT0wLGI9MSxsdHkgID0gMikKICBncmlkKDEwKQp9CmBgYAoKSW4gdGVybXMgb2YgcmVncmVzc2lvbiwgd2UgaGF2ZQpgYGB7ciByZXN1bHRzID0gImFzaXMiLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0gCmxtX2dhcmNoIDwtIGxtKGhfdGVzdH5oX2ZpdCkKc3RhcmdhemVyOjpzdGFyZ2F6ZXIobG1fZ2FyY2gsdHlwZSA9ICJodG1sIixjb3ZhcmlhdGUubGFiZWxzID0gIkdBUkNIIEZvcmVjYXN0IixkZXAudmFyLmxhYmVscyA9ICJBY3R1YWwgVm9sYXRpbGl0eSIpCmBgYAoKV2Ugb2JzZXJ2ZSB0aGF0IHRoZSBHQVJDSCBmb3JlY2FzdHMgY2xvc2VseSBtaW1pYyB0aGUgYWN0dWFsIHRlc3QgZGF0YSwgaS5lLiBpbnRlcmNlcHQgb2YgemVybywgc2xvcGUgcm91Z2hseSAxLCBhbmQgJFJeMiQgb2YgNTZcJS4gTm9uZXRoZWxlc3MsIHRoZSBtYWluIGlzc3VlIGhlcmUgaXMgdGhhdCB0aGUgZGF0YSBzZXQsIGJ5IGNvbnN0cnVjdGlvbiwgZm9sbG93cyBhIEdBUkNIIG1vZGVsLiBIZW5jZSwgdGhpcyBzaG91bGQgbm90IGNvbWUgYXMgYSBzdXJwcmlzZS4gVGhlIG1vcmUgY2hhbGxlbmdpbmcgYW5kIHJlYWxpc3RpYyB0YXNrIGlzIHdoZXRoZXIgdGhlIGRhdGEgaW4gcmVhbC1saWZlIGlzIHN0YXRpb25hcnkgYW5kIG9iZXlzIHRvIHN1Y2ggZGF0YSBnZW5lcmF0aW5nIGZ1bmN0aW9uLiAKCgojIyBNYWNoaW5lIExlYXJuaW5nIEFwcHJvYWNoCkEgZGF0YS1kcml2ZW4gYXBwcm9hY2ggZG9lcyBub3QgcG9zc2VzcyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdW5kZXJseWluZyBwcm9jZXNzIG9mIHRoZSBkYXRhLiBIZW5jZSwgYW4gTUwgYXBwcm9hY2ggd291bGQgbGVhcm4gdGhlIHZvbGF0aWxpdHkgYmVoYXZpb3IgZnJvbSB0aGUgZGF0YSB3aXRob3V0IGltcG9zaW5nIGEgc3RydWN0dXJhbCBtb2RlbC4gVGhlIHRhc2sgaGVyZSBpcyB0byBpbnZlc3RpZ2F0ZSB0aGUgY2FwYWJpbGl0eSBvZiB0aGUgTUwgdG8gbWFwIHRoZSBkYXRhIGludG8gYSB2b2xhdGlsaXR5IGZvcmVjYXN0IGFuZCBjb21wYXJlIGl0IHdpdGggcmVzcGVjdCB0byBpdHMgYmVuY2htYXJrLCB0aGUgIEdBUkNIIG1vZGVsLiAgCgpJIHVzZSBhIHNpbWlsYXIgY29kZSBmcm9tIG15IHByZXZpb3VzIFtub3Rlc10oaHR0cHM6Ly9ycHVicy5jb20vc2ltYWFuODQvTUxfaG9yc2UpIGZvciBpbXBsZW1lbnRhdGlvbiBvZiBNTCBhbGdvcml0aG1zLiBJbiB0aGlzIGNhc2UsIEkgZm9jdXMgb24gdGhlIHN1cHBvcnQgdmVjdG9yIG1hY2hpbmVzIChTVk0pIHdpdGggYSByYWRpYWwga2VybmVsLiBUbyBkbyBzbywgbXkgZmVhdHVyZSBzcGFjZSBpcyBjb25zdHJ1Y3RlZCB1c2luZyB0aGUgY3VycmVudCBwcm9jZXNzIGl0c2VsZiAoc3F1YXJlZCkgYW5kIHRoZSBjdXJyZW50IGxvZy1yZWFsaXplZCB2b2xhdGlsaXR5LiBUaGUgb3V0Y29tZSB2YXJpYWJsZSwgb24gdGhlIG90aGVyIGhhbmQsIGlzIGdpdmVuIGJ5IHRoZSBuZXh0IHBlcmlvZCBsb2ctcmVhbGl6ZWQgdm9sYXRpbGl0eS4gTm90ZSB0aGF0IHdlIHVzZSBhIGxvZyB0cmFuc2Zvcm1hdGlvbiwgZm9sbG93ZWQgYnkgYW4gZXhwb25lbnRpYWwgb25lLCB0byBtYWtlIHN1cmUgdGhlIGZpdHRlZCB2b2xhdGlsaXRpZXMgYXJlIG5vbi1uZWdhdGl2ZS4KCgpTaW1pbGFyIHRvIHRoZSBwcmV2aW91cyBhbmFseXNpcywgd2Ugc3BsaXQgdGhlIGRhdGEgaW50byBpbi1zYW1wbGUgYW5kIG91dC1vZi1zYW1wbGUgc2V0cy4gQWRkaXRpb25hbGx5LCBmb3IgYSBtb3JlIGVmZmljaWVudCBpbXBsZW1lbnRhdGlvbiwgSSByZWZlciB0byB0aGUgYGRvUGFyYWxsZWxgIHBhY2thZ2UgZm9yIHBhcmFsbGVsIGNvbXB1dGluZyBvbiBhbiA4LWNvcmUgbGFwdG9wIGFzIGRlbW9uc3RyYXRlZCBiZWxvdzoKYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFLGZpZy5hbGlnbj0iY2VudGVyIn0KbGlicmFyeShjYXJldCkKbGlicmFyeShkb1BhcmFsbGVsKQoKZHMgPC0gbmEub21pdChkYXRhLmZyYW1lKGggPSBsb2coaF90KSwgciA9IFJfdF4yLCB5ID0gbG9nKGMoaF90Wy0xXSxOQSkpICkpCmluZGV4IDwtIDE6bnJvdyhkcykKaW5kZXhfdHJhaW4yIDwtIGluZGV4X3RyYWluCmluZGV4X3RyYWluMiA8LSBpbmRleF90cmFpbjJbLWxlbmd0aChpbmRleF90cmFpbjIpXSAjIGF2b2lkIHRoZSBsb29rIGFoZWFkIG9ic2VydmF0aW9uCmluZGV4X3Rlc3QgPC0gaW5kZXhbIWluZGV4ICVpbiUgaW5kZXhfdHJhaW4yXQoKZHNfdHJhaW4gPC0gZHNbaW5kZXhfdHJhaW4sXQpkc190ZXN0IDwtIGRzW2luZGV4X3Rlc3QsXQoKbW9kZWxfaSA8LSAic3ZtUmFkaWFsIgp0cmN0cmwgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJyZXBlYXRlZGN2IiwgbnVtYmVyID0gMTAsIHJlcGVhdHMgPSA1KQoKY2wgPC0gbWFrZVBTT0NLY2x1c3Rlcig4KQpyZWdpc3RlckRvUGFyYWxsZWwoY2wpCgp0cmFpbl9tb2RlbCA8LSB0cmFpbih5fiBoICsgciwgZGF0YSA9IGRzX3RyYWluLCBtZXRob2QgPSBtb2RlbF9pLAogICAgICAgICAgICAgICAgICAgICB0ckNvbnRyb2w9dHJjdHJsLAogICAgICAgICAgICAgICAgICAgICBwcmVQcm9jZXNzID0gYygiY2VudGVyIiwgInNjYWxlIiksCiAgICAgICAgICAgICAgICAgICAgIHR1bmVMZW5ndGggPSAxMCkKCnN0b3BDbHVzdGVyKGNsKQpgYGAKCkdpdmVuIHRoZSB0cmFpbmVkIG1vZGVsLCB3ZSBmb3JlY2FzdCB0aGUgbmV4dCBwZXJpb2QgdXNpbmcgdGhlIHRlc3QgZGF0YXNldCBhbmQgdXNlIHRoZSBleHBvbmVudGlhbCB0cmFuc2Zvcm1hdGlvbiB0byByZXRyaWV2ZSB0aGUgdm9sYXRpbGl0eSBsZXZlbC4gQXMgYSBzdW1tYXJ5LCB3ZSBwbG90IGFsdG9nZXRoZXI6CmBgYHtyLGZpZy5hbGlnbj0iY2VudGVyIn0KaF9tbCA8LSBleHAocHJlZGljdCh0cmFpbl9tb2RlbCxkc190ZXN0KSkKewogIHBsb3QoaF90ZXN0fmluZGV4X3Rlc3QsdHlwZSA9ICJsIikKICBsaW5lcyhoX21sfmluZGV4X3Rlc3QsY29sID0gMikKICBsaW5lcyhoX2ZpdH5pbmRleF90ZXN0LGNvbCA9IDMpCiAgbGVnZW5kKCJ0b3BsZWZ0IixjKCJUcnVlIiwiTUwiLCJHQVJDSCIpKQogIGdyaWQoMTApCn0KYGBgCkZyb20gdGhlIGFib3ZlIGdyYXBoLCB3ZSBvYnNlcnZlIHRoYXQgdGhlIE1MIGFsZ29yaXRobSBjbG9zZWx5IG1pbWljcyB0aGUgR0FSQ0ggbW9kZWwgaW4gdGVybXMgb2YgZm9yZWNhc3RzLiBTaW5jZSB0aGUgZGF0YSBpbiBvdXIgY2FzZSBpcyBzdGF0aW9uYXJ5LCBsZWFybmluZyBmcm9tIGhpc3RvcmljYWwgZGF0YSBzaG91bGQgYmUgcmVsZXZhbnQgZm9yIGZ1dHVyZSBhcHBsaWNhdGlvbnMuIEF0IHRoZSBzYW1lIHRpbWUsIHdlIGRpc2Nlcm4gdGhhdCB0aGUgTUwgYWxnb3JpdGhtIGlzIGFibGUgdG8gdW5jb3ZlciB0aGUgYXV0by1yZWdyZXNzaXZlIGNvbXBvbmVudCBpbiB0aGUgdm9sYXRpbGl0eSBwcm9jZXNzLiAKCkluIHRlcm1zIG9mIHJlZ3Jlc3Npb24sICB3ZSBub3RlIHRoZSBNTCBmb3JlY2FzdCBjbG9zZWx5IGNvcnJlc3BvbmRzIHRvIHRoZSBHQVJDSCBzdGF0aXN0aWNzLCB3aXRoIGEgc2xpZ2h0bHkgc21hbGxlciAkUl4yJC4gVGhpcyBjYW4gYmUgaW5mZXJyZWQgZnJvbSB0aGUgZm9sbG93aW5nIHJlZ3Jlc3Npb246CmBgYHtyIHJlc3VsdHMgPSAiYXNpcyIsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfSAKbG1fbWwgPC0gbG0oaF90ZXN0fmhfbWwpCnN0YXJnYXplcjo6c3RhcmdhemVyKGxpc3QobG1fbWwsbG1fZ2FyY2gpLHR5cGUgPSAiaHRtbCIsY292YXJpYXRlLmxhYmVscyA9IGMoIk1MIEZvcmVjYXN0IiwiR0FSQ0ggRm9yZWNhc3QiKSxkZXAudmFyLmxhYmVscyA9ICJBY3R1YWwgVm9sYXRpbGl0eSIpCmBgYAoKCiMgQ29uY2x1ZGluZyBSZW1hcmtzClRoZSBhYm92ZSBleHBlcmltZW50IGlzIGNvbmR1Y3RlZCB1c2luZyBzaW11bGF0ZWQgZGF0YSBmcm9tIGEgR0FSQ0ggbW9kZWwuIEZvciB0aGlzLCBpdCBpcyBub3Qgc3VycHJpc2luZyB0aGF0IHRoZSBHQVJDSCBtb2RlbCBwcm92aWRlcyBhIGJldHRlciBhcHByb3hpbWF0aW9uIG9mIGZ1dHVyZSByZWFsaXplZCB2b2xhdGlsaXR5LiBIb3dldmVyLCB0d28gbWFqb3IgaXNzdWVzIHNob3VsZCBiZSBjb25zaWRlcmVkIGFzIGEgcHJpbWFyeSBpbnZlc3RpZ2F0aW9uLiBGaXJzdCwgaG93IGRvZXMgdGhlIHJlc3VsdCBjaGFuZ2UgaWYgdGhlcmUgaXMgYSBzdHJ1Y3R1cmFsIGJyZWFrIGluIHRoZSBkYXRhPyBGb3IgaW5zdGFuY2UsIGlmIHRoZSBkYXRhIGlzIG5vbi1zdGF0aW9uYXJ5LCBob3cgZG9lcyBlYWNoIG1vZGVsIGNvbXBhcmU/ICBTZWNvbmQsIHdoYXQgaXMgdGhlIGVjb25vbWljIHZhbHVlIG9mIGRlcGxveWluZyBNTCB2ZXJzdXMgR0FSQ0ggbW9kZWxzPyAKCkZvciB0aGUgZmlyc3QgaXNzdWUsIHdlIG1heSBhZGQgc2hvY2tzIHRvIHRoZSB0cmFpbmluZyBzYW1wbGUgYW5kIHRlc3QgdGhlIHNlbnNpdGl2aXR5IGluIHRoZSB0ZXN0IHNldC4gSW4gb3RoZXIgd29yZHMsIHdoaWNoIGFwcHJvYWNoIGlzIG1vcmUgcm9idXN0PyBXZSBzaG91bGQgZ2V0IGEgYmV0dGVyIHBpY3R1cmUgdXNpbmcgcmVhbCBkYXRhLiBGb3IgdGhlIHNlY29uZCBpc3N1ZSwgYSBwb3J0Zm9saW8gc2VsZWN0aW9uIHByb2JsZW0gaXMgb2YgaGlnaCByZWxldmFuY2UuIFdlIGNhbiB0ZXN0IHRoZSBlY29ub21pYyBwZXJmb3JtYW5jZSBvZiBnbG9iYWwgbWluaW11bSBwb3J0Zm9saW8gY29uc3RydWN0ZWQgdXNpbmcgZWl0aGVyIG1vZGVsLiBJIGxlYXZlIGJvdGggZm9yIGZ1dHVyZSByZXNlYXJjaC4KCgoK