Stock Price Simulation using Geometric Brownian Motion (GBM) & Error Analysis

El GBM (Geometric Brownian Motion) es un modelo matemático utilizado en finanzas para describir la evolución de los precios de activos como acciones. Se basa en un movimiento browniano con un crecimiento exponencial, considerando una tasa de retorno constante y una volatilidad aleatoria. Es ampliamente usado en la valoración de opciones y en simulaciones de precios de activos.

Collect real stock price data from Yahoo Finance

Recolectamos datos de apple de la base de datos de yahoo finance con la ayuda de la librería quantmod de R

Instalar paquetes y llamar librerias

#install.packages("quantmod")
#install.packages("ggplot2")
#install.packages("dplyr")
#install.packages("tidyr")

library(quantmod)
library(ggplot2)
library(dplyr)
library(tidyr)

Paso 1: Recolectar datos reales de Yahoo Finance

symbol <- "AAPL"
start_date <- "2010-01-01"
end_date <- Sys.Date()

Descargar los datos

getSymbols(symbol, src = "yahoo", from = start_date, to = end_date)
## [1] "AAPL"

Extraer los precios de cierre

real_prices <- Cl(get(symbol))
summary(real_prices)
##      Index              AAPL.Close     
##  Min.   :2010-01-04   Min.   :  6.859  
##  1st Qu.:2013-10-14   1st Qu.: 20.600  
##  Median :2017-07-25   Median : 38.295  
##  Mean   :2017-07-24   Mean   : 71.104  
##  3rd Qu.:2021-05-05   3rd Qu.:131.560  
##  Max.   :2025-02-18   Max.   :259.020
head(real_prices)
##            AAPL.Close
## 2010-01-04   7.643214
## 2010-01-05   7.656429
## 2010-01-06   7.534643
## 2010-01-07   7.520714
## 2010-01-08   7.570714
## 2010-01-11   7.503929

Simulate stock prices using Geometric Brownian Motion (GBM)

Este fragmento de código en R simula precios utilizando el modelo Geométrico de Movimiento Browniano (GBM), que es útil para la modelización financiera y la predicción de precios futuros de activos basados en datos históricos.

Con μ=0.0009113= 0.0009113: Los precios tienen una ligera tendencia al alza. Con σ=0.01757= 0.01757: Los precios pueden mostrar variaciones diarias considerables alrededor de esa tendencia, reflejando la volatilidad del mercado. En conjunto, estos valores indican un activo financiero con un crecimiento esperado positivo a largo plazo, pero con fluctuaciones diarias moderadas. Esto es típico de muchos instrumentos financieros donde se espera un retorno a largo plazo pero con movimientos de precios diarios impredecibles.

Parámetros del modelo GBM

log_returns <- diff(log(real_prices))
mu <- mean(log_returns, na.rm = TRUE)
sigma <- sd(log_returns, na.rm = TRUE)
S0 <- as.numeric(head(real_prices, 1)) # precio inicial (primer precio real)

Verificar los parámetros calculados

cat("Mu: ", mu, "\n")
## Mu:  0.0009109554
cat("Sigma: ", sigma, "\n")
## Sigma:  0.01757749

Número de simulaciones y pasos

n_simulations <- 10  # Reducido para visualización
n_steps <- length(real_prices)
dt <- 1 / 252 # Asumimos 252 días de trading por año

Ajustar la tendencia con un modelo de regresión lineal

time_index <- 1:n_steps
lm_model <- lm(log(real_prices) ~ time_index)
trend <- lm_model$coefficients[1] + lm_model$coefficients[2] * time_index

Simular los precios usando el modelo GBM

set.seed(123)
simulated_prices <- matrix(0, nrow = n_steps, ncol = n_simulations)

for (i in 1:n_simulations) {
  W <- c(0, cumsum(rnorm(n_steps - 1, 0, sqrt(dt)))) # Caminata aleatoria de Wiener
  S <- exp(trend + (mu - 0.5 * sigma^2) * (0:(n_steps - 1)) * dt + sigma * W) # Modelo GBM ajustado con tendencia
  simulated_prices[, i] <- S
}

Verificar si hay NAs en los precios simulados

print(any(is.na(simulated_prices)))
## [1] FALSE

Compare the real and simulated prices using error metrics and interpret the results

Dado el rango amplio de precios y la naturaleza volátil de los datos bursátiles, estos errores no son excesivamente altos. son relativamente buenos

  • MSE: 208.1941: Este valor puede parecer alto, pero en el contexto de precios que han oscilado desde 7.64 hasta 241.53, es menos preocupante.

  • MAE: 10.01358: Este valor sugiere que, en promedio, los precios simulados difieren en aproximadamente 10 unidades de los precios reales.

En el contexto de precios que van desde 7.64 hasta 241.53, un MAE de 10 unidades no es extremadamente alto e indica que hay una discrepancia promedio notable que podría necesitar ajustes si se requiere una precisión más alta.

En esta tabla podemos observar los valores de las simulaciones comparadas con el precio real

How well did GBM simulate the real stock prices? compare with results from part 01 Las simulaciones en el GBM fueron más acertadas que las de la parte 1

What do the error metrics indicate about the model’s accuracy? Ambos errores tanto el MSE como el MAE fueron más bajos en el modelo de GBM lo que significa que tiene una precisión mayor ya que un MSE más bajo indica que las simulaciones están muy cerca de los precios reales en promedio. Es una medida de precisión global y un MAE más bajo indica que las diferencias absolutas entre los precios simulados y reales son pequeñas, lo que sugiere una buena precisión del modelo.

Error métricas

real_prices_vector <- as.numeric(real_prices)

Error cuadrático medio (MSE) y Error absoluto medio (MAE)

MSE <- mean((real_prices_vector - simulated_prices[,1])^2, na.rm = TRUE)  
MAE <- mean(abs(real_prices_vector - simulated_prices[,1]), na.rm = TRUE)  

cat("MSE: ", MSE, "\n")
## MSE:  208.1369
cat("MAE: ", MAE, "\n")
## MAE:  10.01158
# Calcular R-squared
SST <- sum((real_prices_vector - mean(real_prices_vector))^2)  # Suma total de los cuadrados
SSE <- sum((real_prices_vector - simulated_prices[,1])^2)  # Suma de los cuadrados de los errores
R_squared <- 1 - (SSE / SST)

cat("R-squared: ", R_squared, "\n")
## R-squared:  0.9532094
# Calcular MAPE (Mean Absolute Percentage Error)
MAPE <- mean(abs((real_prices_vector - simulated_prices[,1]) / real_prices_vector) * 100, na.rm = TRUE)

cat("MAPE: ", MAPE, "%\n")
## MAPE:  17.53245 %

Visualize and interpret the results

Interpretación La línea azul muestra una tendencia ascendente en los precios de las acciones de Apple Inc. desde 2010, reflejando un crecimiento constante en su valor. Los picos indican momentos de máximos históricos, posiblemente ligados a lanzamientos de productos o anuncios financieros positivos, mientras que los valles reflejan caídas por factores como crisis de mercado o informes negativos. Además, las líneas de diferentes colores representan precios simulados, y su similitud con la línea azul sugiere que las simulaciones capturan correctamente las tendencias generales del mercado.

Crear un dataframe para los gráficos

simulated_df <- data.frame(Date = index(real_prices), Real = real_prices_vector)

Convertir simulated_prices en un dataframe con nombres explícitos para las simulaciones

simulated_df <- cbind(simulated_df, simulated_prices)
colnames(simulated_df)[-(1:2)] <- paste0("Sim_", 1:n_simulations)

Convertir el dataframe a formato largo para ggplot

simulated_df_long <- simulated_df %>%
  pivot_longer(cols = starts_with("Sim_"), 
               names_to = "Simulation", 
               values_to = "Price")

Graficar precios reales vs simulados

ggplot(simulated_df_long) +
  geom_line(aes(x = Date, y = Price, color = Simulation)) +  # Mostrar todas las simulaciones
  geom_line(data = simulated_df, aes(x = Date, y = Real), color = "blue", size = 1) +  # Mostrar precios reales
  labs(title = "Comparación de precios reales y simulados",
       x = "Fecha", y = "Precio") +
  theme_minimal() +
  theme(legend.title = element_blank())
## Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
## ℹ Please use `linewidth` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

LS0tDQp0aXRsZTogIlRhcmVhIDQgcGFydGUgMiINCmF1dGhvcjogIkVxdWlwbyAxIg0KZGF0ZTogIjIwMjUtMDItMTkiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFDQogICAgdGhlbWU6IGpvdXJuYWwNCi0tLQ0KIVtdKCkNCg0KIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjpibHVlOyI+U3RvY2sgUHJpY2UgU2ltdWxhdGlvbiB1c2luZyBHZW9tZXRyaWMgQnJvd25pYW4gTW90aW9uIChHQk0pICYgRXJyb3IgQW5hbHlzaXM8L3NwYW4+DQpFbCAqKkdCTSAoR2VvbWV0cmljIEJyb3duaWFuIE1vdGlvbikqKiBlcyB1biBtb2RlbG8gbWF0ZW3DoXRpY28gdXRpbGl6YWRvIGVuIGZpbmFuemFzIHBhcmEgZGVzY3JpYmlyIGxhIGV2b2x1Y2nDs24gZGUgbG9zIHByZWNpb3MgZGUgYWN0aXZvcyBjb21vIGFjY2lvbmVzLiBTZSBiYXNhIGVuIHVuIG1vdmltaWVudG8gYnJvd25pYW5vIGNvbiB1biBjcmVjaW1pZW50byBleHBvbmVuY2lhbCwgY29uc2lkZXJhbmRvIHVuYSB0YXNhIGRlIHJldG9ybm8gY29uc3RhbnRlIHkgdW5hIHZvbGF0aWxpZGFkIGFsZWF0b3JpYS4gRXMgYW1wbGlhbWVudGUgdXNhZG8gZW4gbGEgdmFsb3JhY2nDs24gZGUgb3BjaW9uZXMgeSBlbiBzaW11bGFjaW9uZXMgZGUgcHJlY2lvcyBkZSBhY3Rpdm9zLg0KDQojIDxzcGFuIHN0eWxlID0gImNvbG9yOmJsdWU7Ij5Db2xsZWN0IHJlYWwgc3RvY2sgcHJpY2UgZGF0YSBmcm9tIFlhaG9vIEZpbmFuY2U8L3NwYW4+DQpSZWNvbGVjdGFtb3MgZGF0b3MgZGUgYXBwbGUgZGUgbGEgYmFzZSBkZSBkYXRvcyBkZSB5YWhvbyBmaW5hbmNlIGNvbiBsYSBheXVkYSBkZSBsYSBsaWJyZXLDrWEgcXVhbnRtb2QgZGUgUg0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjpibHVlOyI+SW5zdGFsYXIgcGFxdWV0ZXMgeSBsbGFtYXIgbGlicmVyaWFzPC9zcGFuPg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiNpbnN0YWxsLnBhY2thZ2VzKCJxdWFudG1vZCIpDQojaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KI2luc3RhbGwucGFja2FnZXMoInRpZHlyIikNCg0KbGlicmFyeShxdWFudG1vZCkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOmJsdWU7Ij5QYXNvIDE6IFJlY29sZWN0YXIgZGF0b3MgcmVhbGVzIGRlIFlhaG9vIEZpbmFuY2U8L3NwYW4+DQpgYGB7cn0NCnN5bWJvbCA8LSAiQUFQTCINCnN0YXJ0X2RhdGUgPC0gIjIwMTAtMDEtMDEiDQplbmRfZGF0ZSA8LSBTeXMuRGF0ZSgpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6Ymx1ZTsiPkRlc2NhcmdhciBsb3MgZGF0b3M8L3NwYW4+DQpgYGB7cn0NCmdldFN5bWJvbHMoc3ltYm9sLCBzcmMgPSAieWFob28iLCBmcm9tID0gc3RhcnRfZGF0ZSwgdG8gPSBlbmRfZGF0ZSkNCmBgYA0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjpibHVlOyI+RXh0cmFlciBsb3MgcHJlY2lvcyBkZSBjaWVycmU8L3NwYW4+DQpgYGB7cn0NCnJlYWxfcHJpY2VzIDwtIENsKGdldChzeW1ib2wpKQ0Kc3VtbWFyeShyZWFsX3ByaWNlcykNCmhlYWQocmVhbF9wcmljZXMpDQpgYGANCg0KIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjpibHVlOyI+IFNpbXVsYXRlIHN0b2NrIHByaWNlcyB1c2luZyBHZW9tZXRyaWMgQnJvd25pYW4gTW90aW9uIChHQk0pPC9zcGFuPg0KRXN0ZSBmcmFnbWVudG8gZGUgY8OzZGlnbyBlbiBSIHNpbXVsYSBwcmVjaW9zIHV0aWxpemFuZG8gZWwgbW9kZWxvIEdlb23DqXRyaWNvIGRlIE1vdmltaWVudG8gQnJvd25pYW5vIChHQk0pLCBxdWUgZXMgw7p0aWwgcGFyYSBsYSBtb2RlbGl6YWNpw7NuIGZpbmFuY2llcmEgeSBsYSBwcmVkaWNjacOzbiBkZSBwcmVjaW9zIGZ1dHVyb3MgZGUgYWN0aXZvcyBiYXNhZG9zIGVuIGRhdG9zIGhpc3TDs3JpY29zLiANCg0KQ29uICoqzrw9MC4wMDA5MTEzXG11ID0gMC4wMDA5MTEzOioqIExvcyBwcmVjaW9zIHRpZW5lbiB1bmEgbGlnZXJhIHRlbmRlbmNpYSBhbCBhbHphLg0KQ29uICoqz4M9MC4wMTc1N1xzaWdtYSA9IDAuMDE3NTc6KiogTG9zIHByZWNpb3MgcHVlZGVuIG1vc3RyYXIgdmFyaWFjaW9uZXMgZGlhcmlhcyBjb25zaWRlcmFibGVzIGFscmVkZWRvciBkZSBlc2EgdGVuZGVuY2lhLCByZWZsZWphbmRvIGxhIHZvbGF0aWxpZGFkIGRlbCBtZXJjYWRvLg0KRW4gY29uanVudG8sIGVzdG9zIHZhbG9yZXMgaW5kaWNhbiB1biBhY3Rpdm8gZmluYW5jaWVybyBjb24gdW4gY3JlY2ltaWVudG8gZXNwZXJhZG8gcG9zaXRpdm8gYSBsYXJnbyBwbGF6bywgcGVybyBjb24gZmx1Y3R1YWNpb25lcyBkaWFyaWFzIG1vZGVyYWRhcy4gRXN0byBlcyB0w61waWNvIGRlIG11Y2hvcyBpbnN0cnVtZW50b3MgZmluYW5jaWVyb3MgZG9uZGUgc2UgZXNwZXJhIHVuIHJldG9ybm8gYSBsYXJnbyBwbGF6byBwZXJvIGNvbiBtb3ZpbWllbnRvcyBkZSBwcmVjaW9zIGRpYXJpb3MgaW1wcmVkZWNpYmxlcy4NCg0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjpibHVlOyI+UGFyw6FtZXRyb3MgZGVsIG1vZGVsbyBHQk08L3NwYW4+DQpgYGB7cn0NCmxvZ19yZXR1cm5zIDwtIGRpZmYobG9nKHJlYWxfcHJpY2VzKSkNCm11IDwtIG1lYW4obG9nX3JldHVybnMsIG5hLnJtID0gVFJVRSkNCnNpZ21hIDwtIHNkKGxvZ19yZXR1cm5zLCBuYS5ybSA9IFRSVUUpDQpTMCA8LSBhcy5udW1lcmljKGhlYWQocmVhbF9wcmljZXMsIDEpKSAjIHByZWNpbyBpbmljaWFsIChwcmltZXIgcHJlY2lvIHJlYWwpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6Ymx1ZTsiPlZlcmlmaWNhciBsb3MgcGFyw6FtZXRyb3MgY2FsY3VsYWRvczwvc3Bhbj4NCmBgYHtyfQ0KY2F0KCJNdTogIiwgbXUsICJcbiIpDQpjYXQoIlNpZ21hOiAiLCBzaWdtYSwgIlxuIikNCmBgYA0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjpibHVlOyI+TsO6bWVybyBkZSBzaW11bGFjaW9uZXMgeSBwYXNvczwvc3Bhbj4NCmBgYHtyfQ0Kbl9zaW11bGF0aW9ucyA8LSAxMCAgIyBSZWR1Y2lkbyBwYXJhIHZpc3VhbGl6YWNpw7NuDQpuX3N0ZXBzIDwtIGxlbmd0aChyZWFsX3ByaWNlcykNCmR0IDwtIDEgLyAyNTIgIyBBc3VtaW1vcyAyNTIgZMOtYXMgZGUgdHJhZGluZyBwb3IgYcOxbw0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOmJsdWU7Ij5BanVzdGFyIGxhIHRlbmRlbmNpYSBjb24gdW4gbW9kZWxvIGRlIHJlZ3Jlc2nDs24gbGluZWFsPC9zcGFuPg0KYGBge3J9DQp0aW1lX2luZGV4IDwtIDE6bl9zdGVwcw0KbG1fbW9kZWwgPC0gbG0obG9nKHJlYWxfcHJpY2VzKSB+IHRpbWVfaW5kZXgpDQp0cmVuZCA8LSBsbV9tb2RlbCRjb2VmZmljaWVudHNbMV0gKyBsbV9tb2RlbCRjb2VmZmljaWVudHNbMl0gKiB0aW1lX2luZGV4DQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6Ymx1ZTsiPlNpbXVsYXIgbG9zIHByZWNpb3MgdXNhbmRvIGVsIG1vZGVsbyBHQk08L3NwYW4+DQpgYGB7cn0NCnNldC5zZWVkKDEyMykNCnNpbXVsYXRlZF9wcmljZXMgPC0gbWF0cml4KDAsIG5yb3cgPSBuX3N0ZXBzLCBuY29sID0gbl9zaW11bGF0aW9ucykNCg0KZm9yIChpIGluIDE6bl9zaW11bGF0aW9ucykgew0KICBXIDwtIGMoMCwgY3Vtc3VtKHJub3JtKG5fc3RlcHMgLSAxLCAwLCBzcXJ0KGR0KSkpKSAjIENhbWluYXRhIGFsZWF0b3JpYSBkZSBXaWVuZXINCiAgUyA8LSBleHAodHJlbmQgKyAobXUgLSAwLjUgKiBzaWdtYV4yKSAqICgwOihuX3N0ZXBzIC0gMSkpICogZHQgKyBzaWdtYSAqIFcpICMgTW9kZWxvIEdCTSBhanVzdGFkbyBjb24gdGVuZGVuY2lhDQogIHNpbXVsYXRlZF9wcmljZXNbLCBpXSA8LSBTDQp9DQoNCmBgYA0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjpibHVlOyI+VmVyaWZpY2FyIHNpIGhheSBOQXMgZW4gbG9zIHByZWNpb3Mgc2ltdWxhZG9zPC9zcGFuPg0KYGBge3J9DQpwcmludChhbnkoaXMubmEoc2ltdWxhdGVkX3ByaWNlcykpKQ0KYGBgDQoNCiMgPHNwYW4gc3R5bGUgPSAiY29sb3I6Ymx1ZTsiPkNvbXBhcmUgdGhlIHJlYWwgYW5kIHNpbXVsYXRlZCBwcmljZXMgdXNpbmcgZXJyb3IgbWV0cmljcyBhbmQgaW50ZXJwcmV0IHRoZSByZXN1bHRzPC9zcGFuPg0KRGFkbyBlbCByYW5nbyBhbXBsaW8gZGUgcHJlY2lvcyB5IGxhIG5hdHVyYWxlemEgdm9sw6F0aWwgZGUgbG9zIGRhdG9zIGJ1cnPDoXRpbGVzLCBlc3RvcyBlcnJvcmVzIG5vIHNvbiBleGNlc2l2YW1lbnRlIGFsdG9zLiAgc29uIHJlbGF0aXZhbWVudGUgYnVlbm9zDQoNCiogTVNFOiAyMDguMTk0MTogRXN0ZSB2YWxvciBwdWVkZSBwYXJlY2VyIGFsdG8sIHBlcm8gZW4gZWwgY29udGV4dG8gZGUgcHJlY2lvcyBxdWUgaGFuIG9zY2lsYWRvIGRlc2RlIDcuNjQgaGFzdGEgMjQxLjUzLCBlcyBtZW5vcyBwcmVvY3VwYW50ZS4NCg0KKiBNQUU6IDEwLjAxMzU4OiBFc3RlIHZhbG9yIHN1Z2llcmUgcXVlLCBlbiBwcm9tZWRpbywgbG9zIHByZWNpb3Mgc2ltdWxhZG9zIGRpZmllcmVuIGVuIGFwcm94aW1hZGFtZW50ZSAxMCB1bmlkYWRlcyBkZSBsb3MgcHJlY2lvcyByZWFsZXMuDQoNCkVuIGVsIGNvbnRleHRvIGRlIHByZWNpb3MgcXVlIHZhbiBkZXNkZSA3LjY0IGhhc3RhIDI0MS41MywgdW4gTUFFIGRlIDEwIHVuaWRhZGVzIG5vIGVzIGV4dHJlbWFkYW1lbnRlIGFsdG8gZSBpbmRpY2EgcXVlIGhheSB1bmEgZGlzY3JlcGFuY2lhIHByb21lZGlvIG5vdGFibGUgcXVlIHBvZHLDrWEgbmVjZXNpdGFyIGFqdXN0ZXMgc2kgc2UgcmVxdWllcmUgdW5hIHByZWNpc2nDs24gbcOhcyBhbHRhLg0KDQpFbiBlc3RhIHRhYmxhIHBvZGVtb3Mgb2JzZXJ2YXIgbG9zIHZhbG9yZXMgZGUgbGFzIHNpbXVsYWNpb25lcyBjb21wYXJhZGFzIGNvbiBlbCBwcmVjaW8gcmVhbA0KDQohW10oQzpcXFVzZXJzXFxMdWlzIE1lbmRvemFcXERvd25sb2Fkc1xcU2ltdWxhY2lvbmVzLnBuZykNCg0KKipIb3cgd2VsbCBkaWQgR0JNIHNpbXVsYXRlIHRoZSByZWFsIHN0b2NrIHByaWNlcz8gY29tcGFyZSB3aXRoIHJlc3VsdHMgZnJvbSBwYXJ0IDAxKioNCkxhcyBzaW11bGFjaW9uZXMgZW4gZWwgR0JNIGZ1ZXJvbiBtw6FzIGFjZXJ0YWRhcyBxdWUgbGFzIGRlIGxhIHBhcnRlIDENCiANCioqV2hhdCBkbyB0aGUgZXJyb3IgbWV0cmljcyBpbmRpY2F0ZSBhYm91dCB0aGUgbW9kZWzigJlzIGFjY3VyYWN5PyoqDQpBbWJvcyBlcnJvcmVzIHRhbnRvIGVsIE1TRSBjb21vIGVsIE1BRSBmdWVyb24gbcOhcyBiYWpvcyBlbiBlbCBtb2RlbG8gZGUgR0JNIGxvIHF1ZSBzaWduaWZpY2EgcXVlIHRpZW5lIHVuYSBwcmVjaXNpw7NuIG1heW9yIHlhIHF1ZSB1biBNU0UgbcOhcyBiYWpvIGluZGljYSBxdWUgbGFzIHNpbXVsYWNpb25lcyBlc3TDoW4gbXV5IGNlcmNhIGRlIGxvcyBwcmVjaW9zIHJlYWxlcyBlbiBwcm9tZWRpby4gRXMgdW5hIG1lZGlkYSBkZSBwcmVjaXNpw7NuIGdsb2JhbCB5IHVuIE1BRSBtw6FzIGJham8gaW5kaWNhIHF1ZSBsYXMgZGlmZXJlbmNpYXMgYWJzb2x1dGFzIGVudHJlIGxvcyBwcmVjaW9zIHNpbXVsYWRvcyB5IHJlYWxlcyBzb24gcGVxdWXDsWFzLCBsbyBxdWUgc3VnaWVyZSB1bmEgYnVlbmEgcHJlY2lzacOzbiBkZWwgbW9kZWxvLg0KDQoNCiFbXShDOlxcVXNlcnNcXEx1aXMgTWVuZG96YVxcRG93bmxvYWRzXFxNQUUgTVNFLnBuZykNCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6Ymx1ZTsiPkVycm9yIG3DqXRyaWNhczwvc3Bhbj4NCmBgYHtyfQ0KcmVhbF9wcmljZXNfdmVjdG9yIDwtIGFzLm51bWVyaWMocmVhbF9wcmljZXMpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6Ymx1ZTsiPkVycm9yIGN1YWRyw6F0aWNvIG1lZGlvIChNU0UpIHkgRXJyb3IgYWJzb2x1dG8gbWVkaW8gKE1BRSk8L3NwYW4+DQpgYGB7cn0NCk1TRSA8LSBtZWFuKChyZWFsX3ByaWNlc192ZWN0b3IgLSBzaW11bGF0ZWRfcHJpY2VzWywxXSleMiwgbmEucm0gPSBUUlVFKSAgDQpNQUUgPC0gbWVhbihhYnMocmVhbF9wcmljZXNfdmVjdG9yIC0gc2ltdWxhdGVkX3ByaWNlc1ssMV0pLCBuYS5ybSA9IFRSVUUpICANCg0KY2F0KCJNU0U6ICIsIE1TRSwgIlxuIikNCmNhdCgiTUFFOiAiLCBNQUUsICJcbiIpDQoNCiMgQ2FsY3VsYXIgUi1zcXVhcmVkDQpTU1QgPC0gc3VtKChyZWFsX3ByaWNlc192ZWN0b3IgLSBtZWFuKHJlYWxfcHJpY2VzX3ZlY3RvcikpXjIpICAjIFN1bWEgdG90YWwgZGUgbG9zIGN1YWRyYWRvcw0KU1NFIDwtIHN1bSgocmVhbF9wcmljZXNfdmVjdG9yIC0gc2ltdWxhdGVkX3ByaWNlc1ssMV0pXjIpICAjIFN1bWEgZGUgbG9zIGN1YWRyYWRvcyBkZSBsb3MgZXJyb3Jlcw0KUl9zcXVhcmVkIDwtIDEgLSAoU1NFIC8gU1NUKQ0KDQpjYXQoIlItc3F1YXJlZDogIiwgUl9zcXVhcmVkLCAiXG4iKQ0KDQojIENhbGN1bGFyIE1BUEUgKE1lYW4gQWJzb2x1dGUgUGVyY2VudGFnZSBFcnJvcikNCk1BUEUgPC0gbWVhbihhYnMoKHJlYWxfcHJpY2VzX3ZlY3RvciAtIHNpbXVsYXRlZF9wcmljZXNbLDFdKSAvIHJlYWxfcHJpY2VzX3ZlY3RvcikgKiAxMDAsIG5hLnJtID0gVFJVRSkNCg0KY2F0KCJNQVBFOiAiLCBNQVBFLCAiJVxuIikNCmBgYA0KDQojIDxzcGFuIHN0eWxlID0gImNvbG9yOmJsdWU7Ij5WaXN1YWxpemUgYW5kIGludGVycHJldCB0aGUgcmVzdWx0czwvc3Bhbj4NCioqSW50ZXJwcmV0YWNpw7NuKioNCkxhIGzDrW5lYSBhenVsIG11ZXN0cmEgdW5hIHRlbmRlbmNpYSBhc2NlbmRlbnRlIGVuIGxvcyBwcmVjaW9zIGRlIGxhcyBhY2Npb25lcyBkZSBBcHBsZSBJbmMuIGRlc2RlIDIwMTAsIHJlZmxlamFuZG8gdW4gY3JlY2ltaWVudG8gY29uc3RhbnRlIGVuIHN1IHZhbG9yLiBMb3MgcGljb3MgaW5kaWNhbiBtb21lbnRvcyBkZSBtw6F4aW1vcyBoaXN0w7NyaWNvcywgcG9zaWJsZW1lbnRlIGxpZ2Fkb3MgYSBsYW56YW1pZW50b3MgZGUgcHJvZHVjdG9zIG8gYW51bmNpb3MgZmluYW5jaWVyb3MgcG9zaXRpdm9zLCBtaWVudHJhcyBxdWUgbG9zIHZhbGxlcyByZWZsZWphbiBjYcOtZGFzIHBvciBmYWN0b3JlcyBjb21vIGNyaXNpcyBkZSBtZXJjYWRvIG8gaW5mb3JtZXMgbmVnYXRpdm9zLiBBZGVtw6FzLCBsYXMgbMOtbmVhcyBkZSBkaWZlcmVudGVzIGNvbG9yZXMgcmVwcmVzZW50YW4gcHJlY2lvcyBzaW11bGFkb3MsIHkgc3Ugc2ltaWxpdHVkIGNvbiBsYSBsw61uZWEgYXp1bCBzdWdpZXJlIHF1ZSBsYXMgc2ltdWxhY2lvbmVzIGNhcHR1cmFuIGNvcnJlY3RhbWVudGUgbGFzIHRlbmRlbmNpYXMgZ2VuZXJhbGVzIGRlbCBtZXJjYWRvLg0KDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOmJsdWU7Ij5DcmVhciB1biBkYXRhZnJhbWUgcGFyYSBsb3MgZ3LDoWZpY29zPC9zcGFuPg0KYGBge3J9DQpzaW11bGF0ZWRfZGYgPC0gZGF0YS5mcmFtZShEYXRlID0gaW5kZXgocmVhbF9wcmljZXMpLCBSZWFsID0gcmVhbF9wcmljZXNfdmVjdG9yKQ0KYGBgDQoNCiMjIDxzcGFuIHN0eWxlID0gImNvbG9yOmJsdWU7Ij5Db252ZXJ0aXIgc2ltdWxhdGVkX3ByaWNlcyBlbiB1biBkYXRhZnJhbWUgY29uIG5vbWJyZXMgZXhwbMOtY2l0b3MgcGFyYSBsYXMgc2ltdWxhY2lvbmVzPC9zcGFuPg0KYGBge3J9DQpzaW11bGF0ZWRfZGYgPC0gY2JpbmQoc2ltdWxhdGVkX2RmLCBzaW11bGF0ZWRfcHJpY2VzKQ0KY29sbmFtZXMoc2ltdWxhdGVkX2RmKVstKDE6MildIDwtIHBhc3RlMCgiU2ltXyIsIDE6bl9zaW11bGF0aW9ucykNCmBgYA0KDQojIyA8c3BhbiBzdHlsZSA9ICJjb2xvcjpibHVlOyI+Q29udmVydGlyIGVsIGRhdGFmcmFtZSBhIGZvcm1hdG8gbGFyZ28gcGFyYSBnZ3Bsb3Q8L3NwYW4+DQpgYGB7cn0NCnNpbXVsYXRlZF9kZl9sb25nIDwtIHNpbXVsYXRlZF9kZiAlPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiU2ltXyIpLCANCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlNpbXVsYXRpb24iLCANCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJQcmljZSIpDQpgYGANCg0KIyMgPHNwYW4gc3R5bGUgPSAiY29sb3I6Ymx1ZTsiPkdyYWZpY2FyIHByZWNpb3MgcmVhbGVzIHZzIHNpbXVsYWRvczwvc3Bhbj4NCmBgYHtyfQ0KZ2dwbG90KHNpbXVsYXRlZF9kZl9sb25nKSArDQogIGdlb21fbGluZShhZXMoeCA9IERhdGUsIHkgPSBQcmljZSwgY29sb3IgPSBTaW11bGF0aW9uKSkgKyAgIyBNb3N0cmFyIHRvZGFzIGxhcyBzaW11bGFjaW9uZXMNCiAgZ2VvbV9saW5lKGRhdGEgPSBzaW11bGF0ZWRfZGYsIGFlcyh4ID0gRGF0ZSwgeSA9IFJlYWwpLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDEpICsgICMgTW9zdHJhciBwcmVjaW9zIHJlYWxlcw0KICBsYWJzKHRpdGxlID0gIkNvbXBhcmFjacOzbiBkZSBwcmVjaW9zIHJlYWxlcyB5IHNpbXVsYWRvcyIsDQogICAgICAgeCA9ICJGZWNoYSIsIHkgPSAiUHJlY2lvIikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KDQoNCg==