Raul Renteria

Introduction

Stock market prediction is an incredibly difficult task, due to the randomness and noisiness found in the market. Yet, predicting market behaviors is a very important task. Correctly predicting stock price directions can be used to maximize profits, as well as to minimize risk. There are two types of methods to predicting market behavior. One is predicting the future price of an asset. This is usually done using time series analysis to fit a specific model, like ARIMA or GARCH, to some historical data. The other is predicting the future trend of an asset. That is, whether one thinks it will go up or down in price, treating it as a classification problem. There are a few ways this can be achieved. Some examples are found in Khaidem, Saha, and Dey’s paper [1] and another example is found in Manojlovic and Staduhar [2]. Both of these papers utilize machine learning techniques to predict future trends in a stock’s price. The goal of this project is to create an intelligent model, using the Random Forest model, that can correctly forecast the behavior of a stock’s price n days out.

Data

The data used for this project consists of regular stock data (open, close, volume, etc.) from Yahoo finance, and ranges from the year 2000 to 2018. From this data, technical indicators were calculated for every stock. Many investors use technical indicators to generate signals to trade on. Below are all the technical indicators used for this model:

Relative Strength Index

Stochastic Oscillator

William %R

Moving Average Convergence Divergence

Price Rate of Change

On Balance Volume

The last step of pre-processing the data was calculating the response variable. Since we are treating this as a classification problem, the response variable was binary. The equation for calculating the response variable is below: \[ Response = Close_{t+n} - Close_{t} \] It states that the adjusted close price at t+n, where n is the number of days out you want to predict, minus the current adjusted close price will map to a value that says the stock price went up from the point at time t, or that it went down. Once the response variable was calculated, the new data sets were inspected to check for any class imbalance, below are the distributions of the response variables of each stock.

The histograms reveal that there isn’t much of a significant imbalance, therefore no further pre-processing is needed.

Methodology and Model

Decision trees are great for classification problems. The problem with using them is that they tend to over fit the training data, because if they are grown really deep they tend to learn the highly irregular patterns found in that particular data set. Thus, the Random Forest model is used instead, because it eliminates the problem of over fitting by training multiple decision trees on different subsamples of the feature space. The idea behind random forests is simple. First, the data is split into different partitions. Then a certain number of random features is used to create and train a decision tree. This will repeat n number of times, where n is the number of trees to grow (for this project, n was equal to 200). Each tree will then output a prediction. Each prediction will then be calculated for number of votes, and the prediction with the highest number of votes will be the final prediction.

Results

For evaluation of the model, the accuracy was measured. The formula for calculating the accuracy is given by: \[ Accuracy = {\dfrac{TP+TN}{TP+TN+FP+FN}} \] where TP stands for the number of true positives, and TN stands for the number of true negatives. TP are positive instances classified as positive and TN are negative instances classified as negative. FP stands for the number of false positives and FN stands for the number of false negatives. False positives are negative instances classified as positive and false negatives are positive instances classified as negative [2]. Below are all the accuracies for the model predicting from 1 to 20 days out, using AAPL,NFLX,MSFT,and AMZN.

As can be seen, the model produces very poor results with a low number or days out to predict. However, by increasing the days out, say 10 and up, the model produces significantly better results. The accuracy here ranges from 74% to 83%. Another measure of the accuracy is the Receiver Operating Characteristic. It plots the True positive rate against the False positive rate of the model. The closer the curve is to the upper left hand side, the more accurate the test is. Below is a ROC curve for AMAZN, with a 20 day out prediction window.

Conclusion

This post demonstrated that using machine learning models to predict future stock trends can yield satisfying results, given the right parameters. The optimal number of days out to make the predictions is between 12 to 20 days. Future improvements could include incorporating fundamental analysis data, and market sentiment on the individual stocks using sentiment analysis.

References

[1] Khaidem, L., Saha, S., & Dey, S.R. (2016). Predicting the direction of stock market prices using random forest. CoRR, abs/1605.00003.

[2] T. Manojlović and I. Štajduhar, “Predicting stock market trends using random forests: A sample of the Zagreb stock exchange,” 2015 38th International Convention on Information and Communication Technology, Electronics and Microelectronics (MIPRO), Opatija, 2015, pp. 1189-1193. doi: 10.1109/MIPRO.2015.7160456

Code to reproduce the results:

library(quantmod)
library(rpart)
library(rpart.plot)
library(ROCR)
library(caret)
library(randomForest)
library(plotly)
#start and end dates
start <- as.Date("2000-03-12")
end <- as.Date("2018-05-27")
#max period of days out to predict
period <- 20
#model will use daily historical data
stocks <- c("AAPL","NFLX", "MSFT", "AMZN")
getSymbols(stocks, src='yahoo', from=start, to=end)
get_indicators <- function(stock, period){
  #creating response variable. Predicting next days price, by using lag function in price_change
  price_change <- Ad(lag(stock,-period)) - Ad(stock)
  response <- ifelse(price_change > 0, "UP", "DOWN")
  #Calculating RSI
  rsi <- RSI(Ad(stock), n=14)
  #High, Low, and adjusted close xts object
  hlac <- as.xts(data.frame(x=Hi(stock), y=Lo(stock), z=Ad(stock)))
  
  #Stochastic Oscillator
  sto <- stoch(hlac, nFastK = 14) *100
  #Williams %R
  wpr <-WPR(hlac, n=14) * (-100)
  
  #MACD
  macd <- MACD(Ad(stock), nFast=12, nSlow=26, nSig=9)  
  #Price Rate of Change
  roc <- ROC(Ad(stock), n=14) *100
  #On Balance Volume
  obv <- OBV(Ad(stock), Vo(stock))
  
  #create data set with all indicators and labeled columns 
  indicators <- data.frame(rsi, sto, wpr, macd, roc, obv, response)
  colnames(indicators) <- c("RSI", "StoFASTK","StoFASTD","StoSLOWD", 
                          "WilliamPR", "MACD","MACDSignal", "PriceRateOfChange", 
                          "OnBalanceVolume",
                          "Response")
  #removing na values from calculations and keeping sizes of columns same
  indicators <- indicators[-1:-35,]
  
  #removing na values due to lag
  indicators <- head(indicators,-period)
  return(indicators)
}
get_histogram <- function(company) {
  y = c() 
  for (i in 1:20) {
    comp <- get_indicators(company, i)
    per <- round(table(comp$Response)[[2]] / (table(comp$Response)[[1]] + table(comp$Response)[[2]]),3)
    y <- c(y,as.character(per))
  }
  return(y)
}
x <- c()
for (i in 1:20){
  x <- c(x, as.character(i))
}
xform <- list(categoryorder = "array",
              categoryarray = x)
all_histograms <- function(){
  y = get_histogram(AAPL)
  p1 <- plot_ly(y=y, x=x, histfunc='sum', type = "histogram",legendgroup = "l", name = "AAPL") %>%
              layout(yaxis=list(type='linear'),
              hovermode = 'x',
              xaxis = xform,
              title = "Percentage of Up Days")
  y = get_histogram(NFLX)
  p2 <- plot_ly(y=y, x=x, histfunc='sum', type = "histogram", name = "NFLX") %>%
              layout(yaxis=list(type='linear'),
              hovermode = 'x',
              xaxis = xform,
              title = "Percentage of Up Days")
  y = get_histogram(MSFT)
  p3 <- plot_ly(y=y, x=x, histfunc='sum', type = "histogram", name = "MSFT") %>%
              layout(yaxis=list(type='linear'),
              hovermode = 'x',
              xaxis = xform,
              title = "Percentage of Up Days")
  y = get_histogram(AMZN)
  p4 <- plot_ly(y=y, x=x, histfunc='sum', type = "histogram", name = "AMZN") %>%
              layout(yaxis=list(type='linear'),
              hovermode = 'x',
              xaxis = xform,
              title = "Percentage of Up Days")
  subplot(p1,p2,p3, p4, nrows = 2)
}
get_accuracy <- function(model){
  #create predictions off model
  pred <- predict(model, test, type="class")
  acc <- confusionMatrix(pred, test$Response)$overall[[1]]
  return(acc)
}
get_roc_curve <- function(model){
  pred.roc <- predict(model, test, type="prob")[,2]
  f.pred <- prediction(pred.roc, test$Response)
  f.perf <- performance(f.pred, "tpr", "fpr")
  
  auc <- performance(f.pred, measure = "auc")
  
  plot(f.perf, colorize=T, lwd=3, 
       main="ROC Cruve", sub= 
       paste("\nThe area under curve (AUC) for this model is ", round(auc@y.values[[1]], 3)))
  abline(0,1)
}
compare_accuracy <- function(){
  x <- c(1:20)
  plot_ly(accuracys, x= ~x, y= ~accuracys$AAPL, name = 'AAPL', type = 'scatter', mode = 'lines', width = 950) %>%
          add_trace(y = ~accuracys$NFLX, name = 'NFLX', mode = 'lines') %>%
          add_trace(y = ~accuracys$MSFT, name = 'MSFT', mode = 'lines') %>%
          add_trace(y = ~accuracys$AMZN, name = 'AMZN', mode = 'lines') %>%
          layout(title = 'Accuracy',
          xaxis = list(title = 'Days Ahead',
                      zeroline = TRUE,
                      range = c(0, 20)),
          yaxis = list(title = 'Accuracy',
                      range = c(0.48,.85)),
          hovermode='x')
}
set.seed(100)
#temp row in df
accuracys <- data.frame(rep(0,period))
for(stock in stocks){
  stock <- get(stock)
  accuracy.day <- c()
  for(day in 1:period){
    stock_indicators <- get_indicators(stock,day)
    
    #Split the data in a 80-20 (train-test) ratio.
    index <- sample(1:nrow(stock_indicators), size=0.2*nrow(stock_indicators))
    test <- stock_indicators[index, ]
    train <- stock_indicators[-index, ]
    
    #creating model
    model <- randomForest(train$Response ~ ., train, importance=TRUE, ntree=200, mtry=4)
    
    #vector containing accuracies from 1-period
    accuracy.day <- c(accuracy.day,get_accuracy(model))
  }
  accuracys <- cbind(accuracys,data.frame(ticker=accuracy.day))
}
#remove temp row in df
accuracys <- accuracys[,-1]
colnames(accuracys) <- stocks
LS0tCnRpdGxlOiAiU3RvY2sgbWFya2V0IHRyZW5kIHByZWRpY3Rpb25zIHVzaW5nIHJhbmRvbSBmb3Jlc3RzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCipSYXVsIFJlbnRlcmlhKgoKIyMjKipJbnRyb2R1Y3Rpb24qKgpTdG9jayBtYXJrZXQgcHJlZGljdGlvbiBpcyBhbiBpbmNyZWRpYmx5IGRpZmZpY3VsdCB0YXNrLCBkdWUgdG8gdGhlIHJhbmRvbW5lc3MgYW5kIG5vaXNpbmVzcyBmb3VuZCBpbiB0aGUgbWFya2V0LiBZZXQsIHByZWRpY3RpbmcgbWFya2V0IGJlaGF2aW9ycyBpcyBhIHZlcnkgaW1wb3J0YW50IHRhc2suIENvcnJlY3RseSBwcmVkaWN0aW5nIHN0b2NrIHByaWNlIGRpcmVjdGlvbnMgY2FuIGJlIHVzZWQgdG8gbWF4aW1pemUgcHJvZml0cywgYXMgd2VsbCBhcyB0byBtaW5pbWl6ZSByaXNrLiBUaGVyZSBhcmUgdHdvIHR5cGVzIG9mIG1ldGhvZHMgdG8gcHJlZGljdGluZyBtYXJrZXQgYmVoYXZpb3IuIE9uZSBpcyBwcmVkaWN0aW5nIHRoZSBmdXR1cmUgcHJpY2Ugb2YgYW4gYXNzZXQuIFRoaXMgaXMgdXN1YWxseSBkb25lIHVzaW5nIHRpbWUgc2VyaWVzIGFuYWx5c2lzIHRvIGZpdCBhIHNwZWNpZmljIG1vZGVsLCBsaWtlIEFSSU1BIG9yIEdBUkNILCB0byBzb21lIGhpc3RvcmljYWwgZGF0YS4gVGhlIG90aGVyIGlzIHByZWRpY3RpbmcgdGhlIGZ1dHVyZSB0cmVuZCBvZiBhbiBhc3NldC4gVGhhdCBpcywgd2hldGhlciBvbmUgdGhpbmtzIGl0IHdpbGwgZ28gdXAgb3IgZG93biBpbiBwcmljZSwgdHJlYXRpbmcgaXQgYXMgYSBjbGFzc2lmaWNhdGlvbiBwcm9ibGVtLiBUaGVyZSBhcmUgYSBmZXcgd2F5cyB0aGlzIGNhbiBiZSBhY2hpZXZlZC4gU29tZSBleGFtcGxlcyBhcmUgZm91bmQgaW4gS2hhaWRlbSwgU2FoYSwgYW5kIERleSdzIHBhcGVyIFsxXSBhbmQgYW5vdGhlciBleGFtcGxlIGlzIGZvdW5kIGluIE1hbm9qbG92aWMgYW5kIFN0YWR1aGFyIFsyXS4gQm90aCBvZiB0aGVzZSBwYXBlcnMgdXRpbGl6ZSBtYWNoaW5lIGxlYXJuaW5nIHRlY2huaXF1ZXMgdG8gcHJlZGljdCBmdXR1cmUgdHJlbmRzIGluIGEgc3RvY2sncyBwcmljZS4gVGhlIGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGNyZWF0ZSBhbiBpbnRlbGxpZ2VudCBtb2RlbCwgdXNpbmcgdGhlIFJhbmRvbSBGb3Jlc3QgbW9kZWwsIHRoYXQgY2FuIGNvcnJlY3RseSBmb3JlY2FzdCB0aGUgYmVoYXZpb3Igb2YgYSBzdG9jaydzIHByaWNlICpuKiBkYXlzIG91dC4KCgoKIyMjKipEYXRhKioKVGhlIGRhdGEgdXNlZCBmb3IgdGhpcyBwcm9qZWN0IGNvbnNpc3RzIG9mIHJlZ3VsYXIgc3RvY2sgZGF0YSAob3BlbiwgY2xvc2UsIHZvbHVtZSwgZXRjLikgZnJvbSBZYWhvbyBmaW5hbmNlLCBhbmQgcmFuZ2VzIGZyb20gdGhlIHllYXIgMjAwMCB0byAyMDE4LiBGcm9tIHRoaXMgZGF0YSwgdGVjaG5pY2FsIGluZGljYXRvcnMgd2VyZSBjYWxjdWxhdGVkIGZvciBldmVyeSBzdG9jay4gTWFueSBpbnZlc3RvcnMgdXNlIHRlY2huaWNhbCBpbmRpY2F0b3JzIHRvIGdlbmVyYXRlIHNpZ25hbHMgdG8gdHJhZGUgb24uIEJlbG93IGFyZSBhbGwgdGhlIHRlY2huaWNhbCBpbmRpY2F0b3JzIHVzZWQgZm9yIHRoaXMgbW9kZWw6CgoqKlJlbGF0aXZlIFN0cmVuZ3RoIEluZGV4KioKCioqU3RvY2hhc3RpYyBPc2NpbGxhdG9yKioKCioqV2lsbGlhbSAlUioqCgoqKk1vdmluZyBBdmVyYWdlIENvbnZlcmdlbmNlIERpdmVyZ2VuY2UqKgoKKipQcmljZSBSYXRlIG9mIENoYW5nZSoqCgoqKk9uIEJhbGFuY2UgVm9sdW1lKioKCgpUaGUgbGFzdCBzdGVwIG9mIHByZS1wcm9jZXNzaW5nIHRoZSBkYXRhIHdhcyBjYWxjdWxhdGluZyB0aGUgcmVzcG9uc2UgdmFyaWFibGUuIFNpbmNlIHdlIGFyZSB0cmVhdGluZyB0aGlzIGFzIGEgY2xhc3NpZmljYXRpb24gcHJvYmxlbSwgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIHdhcyBiaW5hcnkuIFRoZSBlcXVhdGlvbiBmb3IgY2FsY3VsYXRpbmcgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIGlzIGJlbG93OgokJApSZXNwb25zZSA9IENsb3NlX3t0K259IC0gQ2xvc2Vfe3R9CiQkCkl0IHN0YXRlcyB0aGF0IHRoZSBhZGp1c3RlZCBjbG9zZSBwcmljZSBhdCAqdCtuKiwgd2hlcmUgKm4qIGlzIHRoZSBudW1iZXIgb2YgZGF5cyBvdXQgeW91IHdhbnQgdG8gcHJlZGljdCwgbWludXMgdGhlIGN1cnJlbnQgYWRqdXN0ZWQgY2xvc2UgcHJpY2Ugd2lsbCBtYXAgdG8gYSB2YWx1ZSB0aGF0IHNheXMgdGhlIHN0b2NrIHByaWNlIHdlbnQgdXAgZnJvbSB0aGUgcG9pbnQgYXQgdGltZSAqdCosIG9yIHRoYXQgaXQgd2VudCBkb3duLiBPbmNlIHRoZSByZXNwb25zZSB2YXJpYWJsZSB3YXMgY2FsY3VsYXRlZCwgdGhlIG5ldyBkYXRhIHNldHMgd2VyZSBpbnNwZWN0ZWQgdG8gY2hlY2sgZm9yIGFueSBjbGFzcyBpbWJhbGFuY2UsIGJlbG93IGFyZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgcmVzcG9uc2UgdmFyaWFibGVzIG9mIGVhY2ggc3RvY2suCgpgYGB7ciBlY2hvPUZBTFNFfQphbGxfaGlzdG9ncmFtcygpCmBgYAoKClRoZSBoaXN0b2dyYW1zIHJldmVhbCB0aGF0IHRoZXJlIGlzbid0IG11Y2ggb2YgYSBzaWduaWZpY2FudCBpbWJhbGFuY2UsIHRoZXJlZm9yZSBubyBmdXJ0aGVyIHByZS1wcm9jZXNzaW5nIGlzIG5lZWRlZC4KCgojIyMqKk1ldGhvZG9sb2d5IGFuZCBNb2RlbCoqCkRlY2lzaW9uIHRyZWVzIGFyZSBncmVhdCBmb3IgY2xhc3NpZmljYXRpb24gcHJvYmxlbXMuIFRoZSBwcm9ibGVtIHdpdGggdXNpbmcgdGhlbSBpcyB0aGF0IHRoZXkgdGVuZCB0byBvdmVyIGZpdCB0aGUgdHJhaW5pbmcgZGF0YSwgYmVjYXVzZSBpZiB0aGV5IGFyZSBncm93biByZWFsbHkgZGVlcCB0aGV5IHRlbmQgdG8gbGVhcm4gdGhlIGhpZ2hseSBpcnJlZ3VsYXIgcGF0dGVybnMgZm91bmQgaW4gdGhhdCBwYXJ0aWN1bGFyIGRhdGEgc2V0LiBUaHVzLCB0aGUgUmFuZG9tIEZvcmVzdCBtb2RlbCBpcyB1c2VkIGluc3RlYWQsIGJlY2F1c2UgaXQgZWxpbWluYXRlcyB0aGUgcHJvYmxlbSBvZiBvdmVyIGZpdHRpbmcgYnkgdHJhaW5pbmcgbXVsdGlwbGUgZGVjaXNpb24gdHJlZXMgb24gZGlmZmVyZW50IHN1YnNhbXBsZXMgb2YgdGhlIGZlYXR1cmUgc3BhY2UuIFRoZSBpZGVhIGJlaGluZCByYW5kb20gZm9yZXN0cyBpcyBzaW1wbGUuIEZpcnN0LCB0aGUgZGF0YSBpcyBzcGxpdCBpbnRvIGRpZmZlcmVudCBwYXJ0aXRpb25zLiBUaGVuIGEgY2VydGFpbiBudW1iZXIgb2YgcmFuZG9tIGZlYXR1cmVzIGlzIHVzZWQgdG8gY3JlYXRlIGFuZCB0cmFpbiBhIGRlY2lzaW9uIHRyZWUuIFRoaXMgd2lsbCByZXBlYXQgKm4qIG51bWJlciBvZiB0aW1lcywgd2hlcmUgKm4qIGlzIHRoZSBudW1iZXIgb2YgdHJlZXMgdG8gZ3JvdyAoZm9yIHRoaXMgcHJvamVjdCwgKm4qIHdhcyBlcXVhbCB0byAyMDApLiBFYWNoIHRyZWUgd2lsbCB0aGVuIG91dHB1dCBhIHByZWRpY3Rpb24uIEVhY2ggcHJlZGljdGlvbiB3aWxsIHRoZW4gYmUgY2FsY3VsYXRlZCBmb3IgbnVtYmVyIG9mIHZvdGVzLCBhbmQgdGhlIHByZWRpY3Rpb24gd2l0aCB0aGUgaGlnaGVzdCBudW1iZXIgb2Ygdm90ZXMgd2lsbCBiZSB0aGUgZmluYWwgcHJlZGljdGlvbi4gCgojIyMqKlJlc3VsdHMqKgpGb3IgZXZhbHVhdGlvbiBvZiB0aGUgbW9kZWwsIHRoZSBhY2N1cmFjeSB3YXMgbWVhc3VyZWQuIFRoZSBmb3JtdWxhIGZvciBjYWxjdWxhdGluZyB0aGUgYWNjdXJhY3kgaXMgZ2l2ZW4gYnk6CiQkCkFjY3VyYWN5ID0ge1xkZnJhY3tUUCtUTn17VFArVE4rRlArRk59fQokJAp3aGVyZSBUUCBzdGFuZHMgZm9yIHRoZSBudW1iZXIgb2YgdHJ1ZSBwb3NpdGl2ZXMsIGFuZCBUTiBzdGFuZHMgZm9yIHRoZSBudW1iZXIgb2YgdHJ1ZSBuZWdhdGl2ZXMuIFRQIGFyZSBwb3NpdGl2ZSBpbnN0YW5jZXMgY2xhc3NpZmllZCBhcyBwb3NpdGl2ZSBhbmQgVE4gYXJlIG5lZ2F0aXZlIGluc3RhbmNlcyBjbGFzc2lmaWVkIGFzIG5lZ2F0aXZlLiBGUCBzdGFuZHMgZm9yIHRoZSBudW1iZXIgb2YgZmFsc2UgcG9zaXRpdmVzIGFuZCBGTiBzdGFuZHMgZm9yIHRoZSBudW1iZXIgb2YgZmFsc2UgbmVnYXRpdmVzLiBGYWxzZSBwb3NpdGl2ZXMgYXJlIG5lZ2F0aXZlIGluc3RhbmNlcyBjbGFzc2lmaWVkIGFzIHBvc2l0aXZlIGFuZCBmYWxzZSBuZWdhdGl2ZXMgYXJlIHBvc2l0aXZlIGluc3RhbmNlcyBjbGFzc2lmaWVkIGFzIG5lZ2F0aXZlIFsyXS4gQmVsb3cgYXJlIGFsbCB0aGUgYWNjdXJhY2llcyBmb3IgdGhlIG1vZGVsIHByZWRpY3RpbmcgZnJvbSAxIHRvIDIwIGRheXMgb3V0LCB1c2luZyBBQVBMLE5GTFgsTVNGVCxhbmQgQU1aTi4gCgpgYGB7ciBlY2hvPUZBTFNFfQpjb21wYXJlX2FjY3VyYWN5KCkKYGBgCgpBcyBjYW4gYmUgc2VlbiwgdGhlIG1vZGVsIHByb2R1Y2VzIHZlcnkgcG9vciByZXN1bHRzIHdpdGggYSBsb3cgbnVtYmVyIG9yIGRheXMgb3V0IHRvIHByZWRpY3QuIEhvd2V2ZXIsIGJ5IGluY3JlYXNpbmcgdGhlIGRheXMgb3V0LCBzYXkgMTAgYW5kIHVwLCB0aGUgbW9kZWwgcHJvZHVjZXMgc2lnbmlmaWNhbnRseSBiZXR0ZXIgcmVzdWx0cy4gVGhlIGFjY3VyYWN5IGhlcmUgcmFuZ2VzIGZyb20gNzQlIHRvIDgzJS4gQW5vdGhlciBtZWFzdXJlIG9mIHRoZSBhY2N1cmFjeSBpcyB0aGUgUmVjZWl2ZXIgT3BlcmF0aW5nIENoYXJhY3RlcmlzdGljLiBJdCBwbG90cyB0aGUgVHJ1ZSBwb3NpdGl2ZSByYXRlIGFnYWluc3QgdGhlIEZhbHNlIHBvc2l0aXZlIHJhdGUgb2YgdGhlIG1vZGVsLiBUaGUgY2xvc2VyIHRoZSBjdXJ2ZSBpcyB0byB0aGUgdXBwZXIgbGVmdCBoYW5kIHNpZGUsIHRoZSBtb3JlIGFjY3VyYXRlIHRoZSB0ZXN0IGlzLiBCZWxvdyBpcyBhIFJPQyBjdXJ2ZSBmb3IgQU1BWk4sIHdpdGggYSAyMCBkYXkgb3V0IHByZWRpY3Rpb24gd2luZG93LgoKYGBge3IgZWNobz1GQUxTRX0KZ2V0X3JvY19jdXJ2ZShtb2RlbCkKYGBgCgoKCgojIyMqKkNvbmNsdXNpb24qKgpUaGlzIHBvc3QgZGVtb25zdHJhdGVkIHRoYXQgdXNpbmcgbWFjaGluZSBsZWFybmluZyBtb2RlbHMgdG8gcHJlZGljdCBmdXR1cmUgc3RvY2sgdHJlbmRzIGNhbiB5aWVsZCBzYXRpc2Z5aW5nIHJlc3VsdHMsIGdpdmVuIHRoZSByaWdodCBwYXJhbWV0ZXJzLiBUaGUgb3B0aW1hbCBudW1iZXIgb2YgZGF5cyBvdXQgdG8gbWFrZSB0aGUgcHJlZGljdGlvbnMgaXMgYmV0d2VlbiAxMiB0byAyMCBkYXlzLiBGdXR1cmUgaW1wcm92ZW1lbnRzIGNvdWxkIGluY2x1ZGUgaW5jb3Jwb3JhdGluZyBmdW5kYW1lbnRhbCBhbmFseXNpcyBkYXRhLCBhbmQgbWFya2V0IHNlbnRpbWVudCBvbiB0aGUgaW5kaXZpZHVhbCBzdG9ja3MgdXNpbmcgc2VudGltZW50IGFuYWx5c2lzLgoKIyMjKipSZWZlcmVuY2VzKioKClsxXSBLaGFpZGVtLCBMLiwgU2FoYSwgUy4sICYgRGV5LCBTLlIuICgyMDE2KS4gUHJlZGljdGluZyB0aGUgZGlyZWN0aW9uIG9mIHN0b2NrIG1hcmtldCBwcmljZXMgdXNpbmcgcmFuZG9tIGZvcmVzdC4gQ29SUiwgYWJzLzE2MDUuMDAwMDMuCgpbMl0gVC4gTWFub2psb3ZpxIcgYW5kIEkuIMWgdGFqZHVoYXIsICJQcmVkaWN0aW5nIHN0b2NrIG1hcmtldCB0cmVuZHMgdXNpbmcgcmFuZG9tIGZvcmVzdHM6IEEgc2FtcGxlIG9mIHRoZSBaYWdyZWIgc3RvY2sgZXhjaGFuZ2UsIiAyMDE1IDM4dGggICAgICAgICAgICAgICAgIEludGVybmF0aW9uYWwgQ29udmVudGlvbiBvbiBJbmZvcm1hdGlvbiBhbmQgQ29tbXVuaWNhdGlvbiBUZWNobm9sb2d5LCBFbGVjdHJvbmljcyBhbmQgTWljcm9lbGVjdHJvbmljcyAoTUlQUk8pLCBPcGF0aWphLCAyMDE1LCBwcC4gMTE4OS0xMTkzLgogICAgZG9pOiAxMC4xMTA5L01JUFJPLjIwMTUuNzE2MDQ1NgoKCkNvZGUgdG8gcmVwcm9kdWNlIHRoZSByZXN1bHRzOgpgYGB7cn0KbGlicmFyeShxdWFudG1vZCkKbGlicmFyeShycGFydCkKbGlicmFyeShycGFydC5wbG90KQpsaWJyYXJ5KFJPQ1IpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHBsb3RseSkKYGBgCgoKYGBge3J9CiNzdGFydCBhbmQgZW5kIGRhdGVzCnN0YXJ0IDwtIGFzLkRhdGUoIjIwMDAtMDMtMTIiKQplbmQgPC0gYXMuRGF0ZSgiMjAxOC0wNS0yNyIpCgojbWF4IHBlcmlvZCBvZiBkYXlzIG91dCB0byBwcmVkaWN0CnBlcmlvZCA8LSAyMApgYGAKCgpgYGB7cn0KI21vZGVsIHdpbGwgdXNlIGRhaWx5IGhpc3RvcmljYWwgZGF0YQpzdG9ja3MgPC0gYygiQUFQTCIsIk5GTFgiLCAiTVNGVCIsICJBTVpOIikKZ2V0U3ltYm9scyhzdG9ja3MsIHNyYz0neWFob28nLCBmcm9tPXN0YXJ0LCB0bz1lbmQpCmBgYAoKCgpgYGB7cn0KZ2V0X2luZGljYXRvcnMgPC0gZnVuY3Rpb24oc3RvY2ssIHBlcmlvZCl7CiAgI2NyZWF0aW5nIHJlc3BvbnNlIHZhcmlhYmxlLiBQcmVkaWN0aW5nIG5leHQgZGF5cyBwcmljZSwgYnkgdXNpbmcgbGFnIGZ1bmN0aW9uIGluIHByaWNlX2NoYW5nZQogIHByaWNlX2NoYW5nZSA8LSBBZChsYWcoc3RvY2ssLXBlcmlvZCkpIC0gQWQoc3RvY2spCiAgcmVzcG9uc2UgPC0gaWZlbHNlKHByaWNlX2NoYW5nZSA+IDAsICJVUCIsICJET1dOIikKCiAgI0NhbGN1bGF0aW5nIFJTSQogIHJzaSA8LSBSU0koQWQoc3RvY2spLCBuPTE0KQoKICAjSGlnaCwgTG93LCBhbmQgYWRqdXN0ZWQgY2xvc2UgeHRzIG9iamVjdAogIGhsYWMgPC0gYXMueHRzKGRhdGEuZnJhbWUoeD1IaShzdG9jayksIHk9TG8oc3RvY2spLCB6PUFkKHN0b2NrKSkpCiAgCiAgI1N0b2NoYXN0aWMgT3NjaWxsYXRvcgogIHN0byA8LSBzdG9jaChobGFjLCBuRmFzdEsgPSAxNCkgKjEwMAoKICAjV2lsbGlhbXMgJVIKICB3cHIgPC1XUFIoaGxhYywgbj0xNCkgKiAoLTEwMCkKICAKICAjTUFDRAogIG1hY2QgPC0gTUFDRChBZChzdG9jayksIG5GYXN0PTEyLCBuU2xvdz0yNiwgblNpZz05KSAgCgogICNQcmljZSBSYXRlIG9mIENoYW5nZQogIHJvYyA8LSBST0MoQWQoc3RvY2spLCBuPTE0KSAqMTAwCgogICNPbiBCYWxhbmNlIFZvbHVtZQogIG9idiA8LSBPQlYoQWQoc3RvY2spLCBWbyhzdG9jaykpCgogIAogICNjcmVhdGUgZGF0YSBzZXQgd2l0aCBhbGwgaW5kaWNhdG9ycyBhbmQgbGFiZWxlZCBjb2x1bW5zIAogIGluZGljYXRvcnMgPC0gZGF0YS5mcmFtZShyc2ksIHN0bywgd3ByLCBtYWNkLCByb2MsIG9idiwgcmVzcG9uc2UpCiAgY29sbmFtZXMoaW5kaWNhdG9ycykgPC0gYygiUlNJIiwgIlN0b0ZBU1RLIiwiU3RvRkFTVEQiLCJTdG9TTE9XRCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICJXaWxsaWFtUFIiLCAiTUFDRCIsIk1BQ0RTaWduYWwiLCAiUHJpY2VSYXRlT2ZDaGFuZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAiT25CYWxhbmNlVm9sdW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVzcG9uc2UiKQoKICAjcmVtb3ZpbmcgbmEgdmFsdWVzIGZyb20gY2FsY3VsYXRpb25zIGFuZCBrZWVwaW5nIHNpemVzIG9mIGNvbHVtbnMgc2FtZQogIGluZGljYXRvcnMgPC0gaW5kaWNhdG9yc1stMTotMzUsXQogIAogICNyZW1vdmluZyBuYSB2YWx1ZXMgZHVlIHRvIGxhZwogIGluZGljYXRvcnMgPC0gaGVhZChpbmRpY2F0b3JzLC1wZXJpb2QpCgogIHJldHVybihpbmRpY2F0b3JzKQp9CgpgYGAKCgpgYGB7cn0KZ2V0X2hpc3RvZ3JhbSA8LSBmdW5jdGlvbihjb21wYW55KSB7CiAgeSA9IGMoKSAKICBmb3IgKGkgaW4gMToyMCkgewogICAgY29tcCA8LSBnZXRfaW5kaWNhdG9ycyhjb21wYW55LCBpKQogICAgcGVyIDwtIHJvdW5kKHRhYmxlKGNvbXAkUmVzcG9uc2UpW1syXV0gLyAodGFibGUoY29tcCRSZXNwb25zZSlbWzFdXSArIHRhYmxlKGNvbXAkUmVzcG9uc2UpW1syXV0pLDMpCiAgICB5IDwtIGMoeSxhcy5jaGFyYWN0ZXIocGVyKSkKICB9CiAgcmV0dXJuKHkpCn0KCnggPC0gYygpCmZvciAoaSBpbiAxOjIwKXsKICB4IDwtIGMoeCwgYXMuY2hhcmFjdGVyKGkpKQp9Cnhmb3JtIDwtIGxpc3QoY2F0ZWdvcnlvcmRlciA9ICJhcnJheSIsCiAgICAgICAgICAgICAgY2F0ZWdvcnlhcnJheSA9IHgpCmBgYAoKYGBge3J9CmFsbF9oaXN0b2dyYW1zIDwtIGZ1bmN0aW9uKCl7CiAgeSA9IGdldF9oaXN0b2dyYW0oQUFQTCkKICBwMSA8LSBwbG90X2x5KHk9eSwgeD14LCBoaXN0ZnVuYz0nc3VtJywgdHlwZSA9ICJoaXN0b2dyYW0iLGxlZ2VuZGdyb3VwID0gImwiLCBuYW1lID0gIkFBUEwiKSAlPiUKICAgICAgICAgICAgICBsYXlvdXQoeWF4aXM9bGlzdCh0eXBlPSdsaW5lYXInKSwKICAgICAgICAgICAgICBob3Zlcm1vZGUgPSAneCcsCiAgICAgICAgICAgICAgeGF4aXMgPSB4Zm9ybSwKICAgICAgICAgICAgICB0aXRsZSA9ICJQZXJjZW50YWdlIG9mIFVwIERheXMiKQoKICB5ID0gZ2V0X2hpc3RvZ3JhbShORkxYKQogIHAyIDwtIHBsb3RfbHkoeT15LCB4PXgsIGhpc3RmdW5jPSdzdW0nLCB0eXBlID0gImhpc3RvZ3JhbSIsIG5hbWUgPSAiTkZMWCIpICU+JQogICAgICAgICAgICAgIGxheW91dCh5YXhpcz1saXN0KHR5cGU9J2xpbmVhcicpLAogICAgICAgICAgICAgIGhvdmVybW9kZSA9ICd4JywKICAgICAgICAgICAgICB4YXhpcyA9IHhmb3JtLAogICAgICAgICAgICAgIHRpdGxlID0gIlBlcmNlbnRhZ2Ugb2YgVXAgRGF5cyIpCgogIHkgPSBnZXRfaGlzdG9ncmFtKE1TRlQpCiAgcDMgPC0gcGxvdF9seSh5PXksIHg9eCwgaGlzdGZ1bmM9J3N1bScsIHR5cGUgPSAiaGlzdG9ncmFtIiwgbmFtZSA9ICJNU0ZUIikgJT4lCiAgICAgICAgICAgICAgbGF5b3V0KHlheGlzPWxpc3QodHlwZT0nbGluZWFyJyksCiAgICAgICAgICAgICAgaG92ZXJtb2RlID0gJ3gnLAogICAgICAgICAgICAgIHhheGlzID0geGZvcm0sCiAgICAgICAgICAgICAgdGl0bGUgPSAiUGVyY2VudGFnZSBvZiBVcCBEYXlzIikKCiAgeSA9IGdldF9oaXN0b2dyYW0oQU1aTikKICBwNCA8LSBwbG90X2x5KHk9eSwgeD14LCBoaXN0ZnVuYz0nc3VtJywgdHlwZSA9ICJoaXN0b2dyYW0iLCBuYW1lID0gIkFNWk4iKSAlPiUKICAgICAgICAgICAgICBsYXlvdXQoeWF4aXM9bGlzdCh0eXBlPSdsaW5lYXInKSwKICAgICAgICAgICAgICBob3Zlcm1vZGUgPSAneCcsCiAgICAgICAgICAgICAgeGF4aXMgPSB4Zm9ybSwKICAgICAgICAgICAgICB0aXRsZSA9ICJQZXJjZW50YWdlIG9mIFVwIERheXMiKQoKICBzdWJwbG90KHAxLHAyLHAzLCBwNCwgbnJvd3MgPSAyKQp9CmBgYAoKCgpgYGB7cn0KZ2V0X2FjY3VyYWN5IDwtIGZ1bmN0aW9uKG1vZGVsKXsKICAjY3JlYXRlIHByZWRpY3Rpb25zIG9mZiBtb2RlbAogIHByZWQgPC0gcHJlZGljdChtb2RlbCwgdGVzdCwgdHlwZT0iY2xhc3MiKQogIGFjYyA8LSBjb25mdXNpb25NYXRyaXgocHJlZCwgdGVzdCRSZXNwb25zZSkkb3ZlcmFsbFtbMV1dCiAgcmV0dXJuKGFjYykKfQpgYGAKCgpgYGB7cn0KZ2V0X3JvY19jdXJ2ZSA8LSBmdW5jdGlvbihtb2RlbCl7CiAgcHJlZC5yb2MgPC0gcHJlZGljdChtb2RlbCwgdGVzdCwgdHlwZT0icHJvYiIpWywyXQogIGYucHJlZCA8LSBwcmVkaWN0aW9uKHByZWQucm9jLCB0ZXN0JFJlc3BvbnNlKQogIGYucGVyZiA8LSBwZXJmb3JtYW5jZShmLnByZWQsICJ0cHIiLCAiZnByIikKICAKICBhdWMgPC0gcGVyZm9ybWFuY2UoZi5wcmVkLCBtZWFzdXJlID0gImF1YyIpCiAgCiAgcGxvdChmLnBlcmYsIGNvbG9yaXplPVQsIGx3ZD0zLCAKICAgICAgIG1haW49IlJPQyBDcnV2ZSIsIHN1Yj0gCiAgICAgICBwYXN0ZSgiXG5UaGUgYXJlYSB1bmRlciBjdXJ2ZSAoQVVDKSBmb3IgdGhpcyBtb2RlbCBpcyAiLCByb3VuZChhdWNAeS52YWx1ZXNbWzFdXSwgMykpKQogIGFibGluZSgwLDEpCn0KYGBgCgoKYGBge3J9CmNvbXBhcmVfYWNjdXJhY3kgPC0gZnVuY3Rpb24oKXsKICB4IDwtIGMoMToyMCkKICBwbG90X2x5KGFjY3VyYWN5cywgeD0gfngsIHk9IH5hY2N1cmFjeXMkQUFQTCwgbmFtZSA9ICdBQVBMJywgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsIHdpZHRoID0gOTUwKSAlPiUKICAgICAgICAgIGFkZF90cmFjZSh5ID0gfmFjY3VyYWN5cyRORkxYLCBuYW1lID0gJ05GTFgnLCBtb2RlID0gJ2xpbmVzJykgJT4lCiAgICAgICAgICBhZGRfdHJhY2UoeSA9IH5hY2N1cmFjeXMkTVNGVCwgbmFtZSA9ICdNU0ZUJywgbW9kZSA9ICdsaW5lcycpICU+JQogICAgICAgICAgYWRkX3RyYWNlKHkgPSB+YWNjdXJhY3lzJEFNWk4sIG5hbWUgPSAnQU1aTicsIG1vZGUgPSAnbGluZXMnKSAlPiUKICAgICAgICAgIGxheW91dCh0aXRsZSA9ICdBY2N1cmFjeScsCiAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAnRGF5cyBBaGVhZCcsCiAgICAgICAgICAgICAgICAgICAgICB6ZXJvbGluZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9IGMoMCwgMjApKSwKICAgICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdBY2N1cmFjeScsCiAgICAgICAgICAgICAgICAgICAgICByYW5nZSA9IGMoMC40OCwuODUpKSwKICAgICAgICAgIGhvdmVybW9kZT0neCcpCn0KYGBgCgoKCmBgYHtyfQpzZXQuc2VlZCgxMDApCgojdGVtcCByb3cgaW4gZGYKYWNjdXJhY3lzIDwtIGRhdGEuZnJhbWUocmVwKDAscGVyaW9kKSkKZm9yKHN0b2NrIGluIHN0b2Nrcyl7CiAgc3RvY2sgPC0gZ2V0KHN0b2NrKQogIGFjY3VyYWN5LmRheSA8LSBjKCkKICBmb3IoZGF5IGluIDE6cGVyaW9kKXsKICAgIHN0b2NrX2luZGljYXRvcnMgPC0gZ2V0X2luZGljYXRvcnMoc3RvY2ssZGF5KQogICAgCiAgICAjU3BsaXQgdGhlIGRhdGEgaW4gYSA4MC0yMCAodHJhaW4tdGVzdCkgcmF0aW8uCiAgICBpbmRleCA8LSBzYW1wbGUoMTpucm93KHN0b2NrX2luZGljYXRvcnMpLCBzaXplPTAuMipucm93KHN0b2NrX2luZGljYXRvcnMpKQogICAgdGVzdCA8LSBzdG9ja19pbmRpY2F0b3JzW2luZGV4LCBdCiAgICB0cmFpbiA8LSBzdG9ja19pbmRpY2F0b3JzWy1pbmRleCwgXQogICAgCiAgICAjY3JlYXRpbmcgbW9kZWwKICAgIG1vZGVsIDwtIHJhbmRvbUZvcmVzdCh0cmFpbiRSZXNwb25zZSB+IC4sIHRyYWluLCBpbXBvcnRhbmNlPVRSVUUsIG50cmVlPTIwMCwgbXRyeT00KQogICAgCiAgICAjdmVjdG9yIGNvbnRhaW5pbmcgYWNjdXJhY2llcyBmcm9tIDEtcGVyaW9kCiAgICBhY2N1cmFjeS5kYXkgPC0gYyhhY2N1cmFjeS5kYXksZ2V0X2FjY3VyYWN5KG1vZGVsKSkKICB9CiAgYWNjdXJhY3lzIDwtIGNiaW5kKGFjY3VyYWN5cyxkYXRhLmZyYW1lKHRpY2tlcj1hY2N1cmFjeS5kYXkpKQp9CgojcmVtb3ZlIHRlbXAgcm93IGluIGRmCmFjY3VyYWN5cyA8LSBhY2N1cmFjeXNbLC0xXQpjb2xuYW1lcyhhY2N1cmFjeXMpIDwtIHN0b2NrcwpgYGAKCgoKCgo=