Portafolios de inversión basados en distintos criterios
Empresas
symbols <- c("GMXT.MX", "AMXL.MX", "GFNORTEO.MX", "FEMSAUBD.MX", "KOFUBL.MX",
"AGUA.MX", "SAN.MX","GRUMAB.MX", "OMAB.MX", "ASURB.MX", "BIMBOA.MX")
Precios y retornos
prices <- quantmod::getSymbols(
Symbols = symbols,
src = "yahoo",
from = "2010-12-31",
auto.assign = TRUE,
warnings = FALSE
) %>%
purrr::map(.f = ~ quantmod::Ad(get(x = .x))) %>%
purrr::reduce(.f = merge) %>%
`colnames<-`(value = symbols)
pausing 1 second between requests for more than 5 symbols
pausing 1 second between requests for more than 5 symbols
pausing 1 second between requests for more than 5 symbols
pausing 1 second between requests for more than 5 symbols
pausing 1 second between requests for more than 5 symbols
pausing 1 second between requests for more than 5 symbols
pausing 1 second between requests for more than 5 symbols
asset_returns_xts <- xts::to.monthly(
x = prices,
drop.time = TRUE,
indexAt = "lastof",
OHLC = FALSE
) %>%
PerformanceAnalytics::Return.calculate(method = "discrete") %>%
stats::na.omit()
## quitas todo y solamente dejas tus retornos, diferencia entre symbols y asset returns
## opcional
rm(list = setdiff(x = ls(), y = c("symbols", "precios", "asset_returns_xts")))
Portafolio 1: MÃnima varianza
# Crear un portafolio objetivo que es una lista de Portafolios
min_var_portfolio <- PortfolioAnalytics::portfolio.spec(assets = symbols)
# Agregar la restricci?n que los activos deben sumar 1
min_var_portfolio <- PortfolioAnalytics::add.constraint(
portfolio = min_var_portfolio,
type = "full_investment"
)
# Agreguemos una restricci?n tipo caja para asegurar que las ponderaciones est?n entre 0 y 1
min_var_portfolio <- PortfolioAnalytics::add.constraint(
portfolio = min_var_portfolio,
type = "box", min = 0.00, max = 1
)
# Agreguemos el objetivo para minimizar la varianza
min_var_portfolio <- PortfolioAnalytics::add.objective(
portfolio = min_var_portfolio,
# Minimizar riesgo
type = "risk",
# Correspondencia al riesgo
name = "var"
)
# Corramos la optimizaci?n --------------------------------
global_min_portfolio <- PortfolioAnalytics::optimize.portfolio(
R = asset_returns_xts,
portfolio = min_var_portfolio,
# Usar diferentes tipos de optimizaci?n, lo est?ndar es quadprog
optimize_method = "quadprog",
# Informaci?n adicional de los m?todos
trace = TRUE
)
# Examinemos el resultado
global_min_portfolio
***********************************
PortfolioAnalytics Optimization
***********************************
Call:
PortfolioAnalytics::optimize.portfolio(R = asset_returns_xts,
portfolio = min_var_portfolio, optimize_method = "quadprog",
trace = TRUE)
Optimal Weights:
GMXT.MX AMXL.MX GFNORTEO.MX FEMSAUBD.MX KOFUBL.MX AGUA.MX SAN.MX
0.2535 0.1149 0.0000 0.0000 0.2256 0.0406 0.0400
GRUMAB.MX OMAB.MX ASURB.MX BIMBOA.MX
0.0894 0.0000 0.0000 0.2360
Objective Measure:
StdDev
0.04245
Portafolio 2: Máximos rendimientos
# Crear otro nuevo Portafolio
max_exp_return_portfolio <- PortfolioAnalytics::portfolio.spec(assets = symbols)
# Restricci?n que la suma nos de 1
max_exp_return_portfolio <- PortfolioAnalytics::add.constraint(
portfolio = max_exp_return_portfolio,
type = "full_investment"
)
# Restricci?n tipo box, igualemos en 0.05 y .5
max_exp_return_portfolio <- PortfolioAnalytics::add.constraint(
portfolio = max_exp_return_portfolio,
type = "box", min = 0.05, max = .5
)
# Busquemos maximizar retorno
max_exp_return_portfolio <- PortfolioAnalytics::add.objective(
portfolio = max_exp_return_portfolio,
# Maximizar retornos esperados
type = "return",
# Funci?n tipo media del retorno hist?rico
name = "mean"
)
# Riesgo rendimiento, quadratic utility
max_exp_return_portfolio <- PortfolioAnalytics::add.objective(
portfolio = max_exp_return_portfolio,
# Maximizar retornos esperados
type = "return",
# Funci?n tipo media del retorno hist?rico
name = "mean"
)
# Correr la optimizaci?n
global_max_portfolio <- PortfolioAnalytics::optimize.portfolio(
R = asset_returns_xts,
portfolio = max_exp_return_portfolio,
# En este caso usa "glpk"
optimize_method = "glpk",
# Regresa con informaci?n adicional
trace = TRUE
)
# Examina el resultado
global_max_portfolio
***********************************
PortfolioAnalytics Optimization
***********************************
Call:
PortfolioAnalytics::optimize.portfolio(R = asset_returns_xts,
portfolio = max_exp_return_portfolio, optimize_method = "glpk",
trace = TRUE)
Optimal Weights:
GMXT.MX AMXL.MX GFNORTEO.MX FEMSAUBD.MX KOFUBL.MX AGUA.MX SAN.MX
0.05 0.05 0.05 0.05 0.05 0.05 0.05
GRUMAB.MX OMAB.MX ASURB.MX BIMBOA.MX
0.05 0.50 0.05 0.05
Objective Measure:
mean
0.01741
Portafolio 3: Rebalanceo mensual
# Computar retornos mensuales de la estrategia
portfolio_returns_xts_rebalanced_monthly <-
PerformanceAnalytics::Return.portfolio(
R = asset_returns_xts,
weights = weights,
# Rebalanceo Mensual, implica comprar/vender cada mes
reblance_on = "months",
geometric = FALSE
) %>%
`colnames<-`("Monthly_portfolio_returns")
# grafica los rendimientos acumulados
charts.PerformanceSummary(portfolio_returns_xts_rebalanced_monthly)

Portfolio 3 vs S&P500
#asumamos que el bmk es el S&P500 (SPY)
SPY <- quantmod::getSymbols(
Symbols = "SPY",
src = "yahoo",
from = "2015-12-31",
auto.assign = TRUE,
warnings = FALSE
) %>%
purrr::map(.f = ~ quantmod::Ad(get(x = .x))) %>%
purrr::reduce(.f = merge) %>%
`colnames<-`(value = "SPY")
SPY_returns_xts <- xts::to.monthly(
x = SPY,
drop.time = TRUE,
indexAt = "lastof",
OHLC = FALSE
) %>%
PerformanceAnalytics::Return.calculate(method = "discrete") %>%
stats::na.omit()
compara <- cbind(portfolio_returns_xts_rebalanced_monthly, SPY_returns_xts)
# c?mo se ve
charts.PerformanceSummary(compara)

Portafolio 4: Utilidad cuadrática
quat_ut_port <- PortfolioAnalytics::portfolio.spec(assets = symbols)
quat_ut_port <- PortfolioAnalytics::add.constraint(
portfolio = quat_ut_port,
type = "full_investment"
)
quat_ut_port <- PortfolioAnalytics::add.constraint(
portfolio = quat_ut_port,
type = "box", min = 0.00, max = 0.5
)
quat_ut_port <- PortfolioAnalytics::add.objective(quat_ut_port, type="quadratic_utility",
risk_aversion=2)
max_quat_ut_port<- PortfolioAnalytics::optimize.portfolio(
R = asset_returns_xts,
portfolio = quat_ut_port,
# En este caso usa "quadprog"
optimize_method = "quadprog",
# Regresa con informaci?n adicional
trace = TRUE
)
max_quat_ut_port
***********************************
PortfolioAnalytics Optimization
***********************************
Call:
PortfolioAnalytics::optimize.portfolio(R = asset_returns_xts,
portfolio = quat_ut_port, optimize_method = "quadprog", trace = TRUE)
Optimal Weights:
GMXT.MX AMXL.MX GFNORTEO.MX FEMSAUBD.MX KOFUBL.MX AGUA.MX SAN.MX
0.2506 0.0000 0.0000 0.0000 0.0000 0.0810 0.0000
GRUMAB.MX OMAB.MX ASURB.MX BIMBOA.MX
0.0000 0.1895 0.0000 0.4789
Objective Measure:
mean
0.01957
StdDev
0.05001
weights3 <- pluck(.x = max_quat_ut_port, "weights")
## Max mean and low ETL
meanETL_port <- PortfolioAnalytics::portfolio.spec(assets = symbols)
meanETL_port <- PortfolioAnalytics::add.constraint(
portfolio = meanETL_port,
type = "weight_sum",
min_sum=0.99,
max_sum=1.01
)
#meanETL_port <- PortfolioAnalytics::add.constraint(
# portfolio = meanETL_port,
# type = "full_investment"
#)
meanETL_port <- PortfolioAnalytics::add.constraint(
portfolio = meanETL_port,
type = "box", min = -0.05, max = 0.50
)
meanETL_port <- PortfolioAnalytics::add.objective(meanETL_port, type="return", name="mean")
meanETL_port <- PortfolioAnalytics::add.objective(meanETL_port, type="risk", name="ETL", arguments=list(p=0.95))
max_min_ETL_port<- PortfolioAnalytics::optimize.portfolio(
R = asset_returns_xts,
portfolio = meanETL_port,
# En este caso usa "quadprog"
optimize_method = "random",
search_size = 10000,
# Regresa con informaci?n adicional
trace = TRUE
)
weights <- pluck(.x = max_min_ETL_port, "weights")
portfolio_returns_xts_rebalanced_monthly <-
PerformanceAnalytics::Return.portfolio(
R = asset_returns_xts,
weights = weights,
# Rebalanceo Mensual, implica comprar/vender cada mes
reblance_on = "months",
geometric = FALSE
) %>%
`colnames<-`("Monthly_portfolio_returns")
bmk_returns <-
PerformanceAnalytics::Return.portfolio(
R = SPY_returns_xts,
# Rebalanceo Mensual, implica comprar/vender cada mes
reblance_on = "months",
geometric = FALSE
) %>%
`colnames<-`("Benchmark_Returns")
quad_returns <-
PerformanceAnalytics::Return.portfolio(
R = asset_returns_xts,
weights = weights3,
# Rebalanceo Mensual, implica comprar/vender cada mes
reblance_on = "months",
geometric = FALSE
) %>%
`colnames<-`("Quad_Returns")
Portafolio 4 vs S&P500
compara2 <- cbind(portfolio_returns_xts_rebalanced_monthly, bmk_returns, quad_returns)
charts.PerformanceSummary(compara2)

Back test
opt_quad <- optimize.portfolio.rebalancing(asset_returns_xts,
quat_ut_port,
optimize_method = "ROI",
rebalance_on="months",
training_period = 12,
rolling_window=24)
backtest_quad_ret <- Return.portfolio(asset_returns_xts, extractWeights((opt_quad)))
colnames(backtest_quad_ret) <- "Bkt_Quad"
#################3 Ejercicios sobre restricciones
quat_ut_35 <- PortfolioAnalytics::portfolio.spec(assets = symbols)
quat_ut_35 <- PortfolioAnalytics::add.constraint(
portfolio = quat_ut_35,
type = "full_investment"
)
# no puedes invertir m?s de 25%
quat_ut_35 <- PortfolioAnalytics::add.constraint(
portfolio = quat_ut_35,
type = "box", min = 0.02, max = .15
)
## Trabajar m?s a detalle
quat_ut_35 <- add.constraint(portfolio=quat_ut_35,
type="group",
groups=list(c(2, 3, 4, 5, 6, 11), c(1, 7, 8, 9, 10)),
group_min=c(0.0, 0.0),
group_max=c(.60, 0.4),
group_labels=c("ESG", "NON-ESG"),
group_pos=c(1, 2)) #se arman grupos dependiendo de las acciones
quat_ut_35 <- PortfolioAnalytics::add.constraint(
portfolio = quat_ut_35,
type = "weight_sum",
min_sum=1.00,
max_sum=1.00
)
quat_ut_35 <- PortfolioAnalytics::add.objective(quat_ut_35, type="quadratic_utility",
risk_aversion=2)
opt_quad_35 <- optimize.portfolio.rebalancing(asset_returns_xts,
quat_ut_35,
optimize_method = "ROI",
rebalance_on="days",
training_period = 12,
rolling_window=24)
opt_quad_35_last<- PortfolioAnalytics::optimize.portfolio(
R = asset_returns_xts,
portfolio = quat_ut_35,
optimize_method = "quadprog",
#momentargs=momentargs,
# Regresa con informaci?n adicional
trace = TRUE
)
Gráfica
library(viridisLite)
# Load the pals package
library(pals)
Attaching package: ‘pals’
The following objects are masked from ‘package:viridisLite’:
cividis, inferno, magma, plasma, turbo, viridis
# Create 11-color palette using the tol scheme with a stepped appearance
colores <- c("#A1D4F1", "#92D051", "#E57436","#68E185", "#4DB3A7", "#3595D9", "#FACE48", "#F29070", "#80E9C7", "#7D4A1B", "#204882")
#descriptores
chart.Weights(opt_quad_35, colorset=colores, main = "Distribución óptima del portafolio")

Grupos
(extractGroups(opt_quad_35_last))
$weights
GMXT.MX AMXL.MX GFNORTEO.MX FEMSAUBD.MX KOFUBL.MX AGUA.MX SAN.MX
0.15000000 0.08905094 0.04094906 0.02000000 0.15000000 0.15000000 0.02000000
GRUMAB.MX OMAB.MX ASURB.MX BIMBOA.MX
0.03236557 0.15000000 0.04763443 0.15000000
$category_weights
NULL
$group_weights
ESG NON-ESG
0.6 0.4
Backtest mensual a 3 años
(backtest_quad_35 <- Return.portfolio(asset_returns_xts, extractWeights((opt_quad_35))))
portfolio.returns
2020-05-31 0.020012568
2020-06-30 0.068477382
2020-07-31 -0.006331255
2020-08-31 0.023177273
2020-09-30 0.025340924
2020-10-31 -0.056091161
2020-11-30 0.124659928
2020-12-31 0.082333184
2021-01-31 -0.077083338
2021-02-28 0.049877732
2021-03-31 0.074031682
2021-04-30 0.014301295
2021-05-31 0.052641720
2021-06-30 -0.004848031
2021-07-31 0.011610090
2021-08-31 0.050786407
2021-09-30 -0.015461141
2021-10-31 0.040810897
2021-11-30 0.003624098
2021-12-31 0.066938160
2022-01-31 -0.002207682
2022-02-28 0.003833863
2022-03-31 0.001335146
2022-04-30 -0.016866747
2022-05-31 0.038365322
2022-06-30 -0.066343870
2022-07-31 0.039491954
2022-08-31 -0.018044931
2022-09-30 0.013792953
2022-10-31 0.129507975
2022-11-30 0.050108013
2022-12-31 -0.035063518
2023-01-31 0.079614095
2023-02-28 -0.013884676
2023-03-31 0.006998971
benchmark <- read_excel("/Users/rogelio/Desktop/benchmark.xlsx")
benchmark$Fecha <- as.Date(benchmark$Fecha, format = "%m/%d/%Y")
#convert it to an xts object, taking the Date column as the Date and Portfolio and IPC as the values
benchmark.xts <- xts(benchmark[,c(2,3)], order.by = benchmark$Fecha)
charts.PerformanceSummary(benchmark.xts, main = "Retornos del portafolio vs IPC México")

NA
NA
NA
LS0tCnRpdGxlOiAiUG9ydGFmb2xpbyBkZSBpbnZlcnNpw7NuIG5hY2lvbmFsIgphdXRob3I6ICJBTEZBIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgICBkZl9wcmludDogcGFnZWQKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShST0kpCmxpYnJhcnkoUk9JLnBsdWdpbi5nbHBrKQpsaWJyYXJ5KFJPSS5wbHVnaW4ucXVhZHByb2cpCmxpYnJhcnkoUk9JLnBsdWdpbi5zeW1waG9ueSkKbGlicmFyeShxdWFudG1vZCkKbGlicmFyeShwdXJycikKbGlicmFyeShQZXJmb3JtYW5jZUFuYWx5dGljcykKbGlicmFyeShQb3J0Zm9saW9BbmFseXRpY3MpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KERFb3B0aW1SKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShyZWFkeGwpCmBgYAoKIyBQb3J0YWZvbGlvcyBkZSBpbnZlcnNpw7NuIGJhc2Fkb3MgZW4gZGlzdGludG9zIGNyaXRlcmlvcwoKIyMjIEVtcHJlc2FzCmBgYHtyfQpzeW1ib2xzIDwtIGMoIkdNWFQuTVgiLCAiQU1YTC5NWCIsICJHRk5PUlRFTy5NWCIsICJGRU1TQVVCRC5NWCIsICJLT0ZVQkwuTVgiLAogICAgICAgICAgICAgIkFHVUEuTVgiLCAiU0FOLk1YIiwiR1JVTUFCLk1YIiwgIk9NQUIuTVgiLCAiQVNVUkIuTVgiLCAiQklNQk9BLk1YIikKCmBgYAoKIyMjIFByZWNpb3MgeSByZXRvcm5vcwoKYGBge3IsIHdhcm5pbmc9RkFMU0V9CnByaWNlcyA8LSBxdWFudG1vZDo6Z2V0U3ltYm9scygKICBTeW1ib2xzID0gc3ltYm9scywKICBzcmMgPSAieWFob28iLAogIGZyb20gPSAiMjAxMC0xMi0zMSIsCiAgYXV0by5hc3NpZ24gPSBUUlVFLAogIHdhcm5pbmdzID0gRkFMU0UKKSAlPiUKICBwdXJycjo6bWFwKC5mID0gfiBxdWFudG1vZDo6QWQoZ2V0KHggPSAueCkpKSAlPiUKICBwdXJycjo6cmVkdWNlKC5mID0gbWVyZ2UpICU+JQogIGBjb2xuYW1lczwtYCh2YWx1ZSA9IHN5bWJvbHMpCgoKYXNzZXRfcmV0dXJuc194dHMgPC0geHRzOjp0by5tb250aGx5KAogIHggPSBwcmljZXMsCiAgZHJvcC50aW1lID0gVFJVRSwKICBpbmRleEF0ID0gImxhc3RvZiIsCiAgT0hMQyA9IEZBTFNFCikgJT4lCiAgUGVyZm9ybWFuY2VBbmFseXRpY3M6OlJldHVybi5jYWxjdWxhdGUobWV0aG9kID0gImRpc2NyZXRlIikgJT4lCiAgc3RhdHM6Om5hLm9taXQoKQoKIyMgcXVpdGFzIHRvZG8geSBzb2xhbWVudGUgZGVqYXMgdHVzIHJldG9ybm9zLCBkaWZlcmVuY2lhIGVudHJlIHN5bWJvbHMgeSBhc3NldCByZXR1cm5zCiMjIG9wY2lvbmFsCgpybShsaXN0ID0gc2V0ZGlmZih4ID0gbHMoKSwgeSA9IGMoInN5bWJvbHMiLCAicHJlY2lvcyIsICJhc3NldF9yZXR1cm5zX3h0cyIpKSkKCgpgYGAKCiMjIFBvcnRhZm9saW8gMTogTcOtbmltYSB2YXJpYW56YQpgYGB7cn0KIyBDcmVhciB1biBwb3J0YWZvbGlvIG9iamV0aXZvIHF1ZSBlcyB1bmEgbGlzdGEgZGUgUG9ydGFmb2xpb3MKbWluX3Zhcl9wb3J0Zm9saW8gPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjpwb3J0Zm9saW8uc3BlYyhhc3NldHMgPSBzeW1ib2xzKQoKCiMgQWdyZWdhciBsYSByZXN0cmljY2k/biBxdWUgbG9zIGFjdGl2b3MgZGViZW4gc3VtYXIgMQptaW5fdmFyX3BvcnRmb2xpbyA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OmFkZC5jb25zdHJhaW50KAogIHBvcnRmb2xpbyA9IG1pbl92YXJfcG9ydGZvbGlvLAogIHR5cGUgPSAiZnVsbF9pbnZlc3RtZW50IgopCgoKCiMgQWdyZWd1ZW1vcyB1bmEgcmVzdHJpY2NpP24gdGlwbyBjYWphIHBhcmEgYXNlZ3VyYXIgcXVlIGxhcyBwb25kZXJhY2lvbmVzIGVzdD9uIGVudHJlIDAgeSAxCm1pbl92YXJfcG9ydGZvbGlvIDwtIFBvcnRmb2xpb0FuYWx5dGljczo6YWRkLmNvbnN0cmFpbnQoCiAgcG9ydGZvbGlvID0gbWluX3Zhcl9wb3J0Zm9saW8sCiAgdHlwZSA9ICJib3giLCBtaW4gPSAwLjAwLCBtYXggPSAxCikKCgojIEFncmVndWVtb3MgZWwgb2JqZXRpdm8gcGFyYSBtaW5pbWl6YXIgbGEgdmFyaWFuemEKbWluX3Zhcl9wb3J0Zm9saW8gPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjphZGQub2JqZWN0aXZlKAogIHBvcnRmb2xpbyA9IG1pbl92YXJfcG9ydGZvbGlvLAogICMgTWluaW1pemFyIHJpZXNnbwogIHR5cGUgPSAicmlzayIsCiAgIyBDb3JyZXNwb25kZW5jaWEgYWwgcmllc2dvCiAgbmFtZSA9ICJ2YXIiCikKCiMgQ29ycmFtb3MgbGEgb3B0aW1pemFjaT9uIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgpnbG9iYWxfbWluX3BvcnRmb2xpbyA8LSBQb3J0Zm9saW9BbmFseXRpY3M6Om9wdGltaXplLnBvcnRmb2xpbygKICBSID0gYXNzZXRfcmV0dXJuc194dHMsCiAgcG9ydGZvbGlvID0gbWluX3Zhcl9wb3J0Zm9saW8sCiAgIyBVc2FyIGRpZmVyZW50ZXMgdGlwb3MgZGUgb3B0aW1pemFjaT9uLCBsbyBlc3Q/bmRhciBlcyBxdWFkcHJvZwogIG9wdGltaXplX21ldGhvZCA9ICJxdWFkcHJvZyIsCiAgIyBJbmZvcm1hY2k/biBhZGljaW9uYWwgZGUgbG9zIG0/dG9kb3MKICB0cmFjZSA9IFRSVUUKKQoKIyBFeGFtaW5lbW9zIGVsIHJlc3VsdGFkbwpnbG9iYWxfbWluX3BvcnRmb2xpbwpgYGAKCiMjIFBvcnRhZm9saW8gMjogTcOheGltb3MgcmVuZGltaWVudG9zCmBgYHtyfQojIENyZWFyIG90cm8gbnVldm8gUG9ydGFmb2xpbwptYXhfZXhwX3JldHVybl9wb3J0Zm9saW8gPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjpwb3J0Zm9saW8uc3BlYyhhc3NldHMgPSBzeW1ib2xzKQoKIyBSZXN0cmljY2k/biBxdWUgbGEgc3VtYSBub3MgZGUgMQptYXhfZXhwX3JldHVybl9wb3J0Zm9saW8gPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjphZGQuY29uc3RyYWludCgKICBwb3J0Zm9saW8gPSBtYXhfZXhwX3JldHVybl9wb3J0Zm9saW8sCiAgdHlwZSA9ICJmdWxsX2ludmVzdG1lbnQiCikKCiMgUmVzdHJpY2NpP24gdGlwbyBib3gsIGlndWFsZW1vcyBlbiAwLjA1IHkgLjUKCm1heF9leHBfcmV0dXJuX3BvcnRmb2xpbyA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OmFkZC5jb25zdHJhaW50KAogIHBvcnRmb2xpbyA9IG1heF9leHBfcmV0dXJuX3BvcnRmb2xpbywKICB0eXBlID0gImJveCIsIG1pbiA9IDAuMDUsIG1heCA9IC41CikKCiMgQnVzcXVlbW9zIG1heGltaXphciByZXRvcm5vCm1heF9leHBfcmV0dXJuX3BvcnRmb2xpbyA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OmFkZC5vYmplY3RpdmUoCiAgcG9ydGZvbGlvID0gbWF4X2V4cF9yZXR1cm5fcG9ydGZvbGlvLAogICMgTWF4aW1pemFyIHJldG9ybm9zIGVzcGVyYWRvcwogIHR5cGUgPSAicmV0dXJuIiwKICAjIEZ1bmNpP24gdGlwbyBtZWRpYSBkZWwgcmV0b3JubyBoaXN0P3JpY28KICBuYW1lID0gIm1lYW4iCikKCiMgUmllc2dvIHJlbmRpbWllbnRvLCBxdWFkcmF0aWMgdXRpbGl0eQoKbWF4X2V4cF9yZXR1cm5fcG9ydGZvbGlvIDwtIFBvcnRmb2xpb0FuYWx5dGljczo6YWRkLm9iamVjdGl2ZSgKICBwb3J0Zm9saW8gPSBtYXhfZXhwX3JldHVybl9wb3J0Zm9saW8sCiAgIyBNYXhpbWl6YXIgcmV0b3Jub3MgZXNwZXJhZG9zCiAgdHlwZSA9ICJyZXR1cm4iLAogICMgRnVuY2k/biB0aXBvIG1lZGlhIGRlbCByZXRvcm5vIGhpc3Q/cmljbwogIG5hbWUgPSAibWVhbiIKKQoKCgojIENvcnJlciBsYSBvcHRpbWl6YWNpP24KZ2xvYmFsX21heF9wb3J0Zm9saW8gPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjpvcHRpbWl6ZS5wb3J0Zm9saW8oCiAgUiA9IGFzc2V0X3JldHVybnNfeHRzLAogIHBvcnRmb2xpbyA9IG1heF9leHBfcmV0dXJuX3BvcnRmb2xpbywKICAjIEVuIGVzdGUgY2FzbyB1c2EgImdscGsiIAogIG9wdGltaXplX21ldGhvZCA9ICJnbHBrIiwKICAjIFJlZ3Jlc2EgY29uIGluZm9ybWFjaT9uIGFkaWNpb25hbAogIHRyYWNlID0gVFJVRQopCiMgRXhhbWluYSBlbCByZXN1bHRhZG8KZ2xvYmFsX21heF9wb3J0Zm9saW8KYGBgCgojIyBQb3J0YWZvbGlvIDM6IFJlYmFsYW5jZW8gbWVuc3VhbApgYGB7cn0KIyBDb21wdXRhciByZXRvcm5vcyBtZW5zdWFsZXMgZGUgbGEgZXN0cmF0ZWdpYQpwb3J0Zm9saW9fcmV0dXJuc194dHNfcmViYWxhbmNlZF9tb250aGx5IDwtCiAgUGVyZm9ybWFuY2VBbmFseXRpY3M6OlJldHVybi5wb3J0Zm9saW8oCiAgICBSID0gYXNzZXRfcmV0dXJuc194dHMsCiAgICB3ZWlnaHRzID0gd2VpZ2h0cywKICAgICMgUmViYWxhbmNlbyBNZW5zdWFsLCBpbXBsaWNhIGNvbXByYXIvdmVuZGVyIGNhZGEgbWVzCiAgICByZWJsYW5jZV9vbiA9ICJtb250aHMiLAogICAgZ2VvbWV0cmljID0gRkFMU0UKICApICU+JQogIGBjb2xuYW1lczwtYCgiTW9udGhseV9wb3J0Zm9saW9fcmV0dXJucyIpCgoKIyBncmFmaWNhIGxvcyByZW5kaW1pZW50b3MgYWN1bXVsYWRvcwoKY2hhcnRzLlBlcmZvcm1hbmNlU3VtbWFyeShwb3J0Zm9saW9fcmV0dXJuc194dHNfcmViYWxhbmNlZF9tb250aGx5KQoKYGBgCgojIyMgUG9ydGZvbGlvIDMgdnMgUyZQNTAwCmBgYHtyfQojYXN1bWFtb3MgcXVlIGVsIGJtayBlcyBlbCBTJlA1MDAgKFNQWSkKClNQWSA8LSBxdWFudG1vZDo6Z2V0U3ltYm9scygKICBTeW1ib2xzID0gIlNQWSIsCiAgc3JjID0gInlhaG9vIiwKICBmcm9tID0gIjIwMTUtMTItMzEiLAogIGF1dG8uYXNzaWduID0gVFJVRSwKICB3YXJuaW5ncyA9IEZBTFNFCikgJT4lCiAgcHVycnI6Om1hcCguZiA9IH4gcXVhbnRtb2Q6OkFkKGdldCh4ID0gLngpKSkgJT4lCiAgcHVycnI6OnJlZHVjZSguZiA9IG1lcmdlKSAlPiUKICBgY29sbmFtZXM8LWAodmFsdWUgPSAiU1BZIikKClNQWV9yZXR1cm5zX3h0cyA8LSB4dHM6OnRvLm1vbnRobHkoCiAgeCA9IFNQWSwKICBkcm9wLnRpbWUgPSBUUlVFLAogIGluZGV4QXQgPSAibGFzdG9mIiwKICBPSExDID0gRkFMU0UKKSAlPiUKICBQZXJmb3JtYW5jZUFuYWx5dGljczo6UmV0dXJuLmNhbGN1bGF0ZShtZXRob2QgPSAiZGlzY3JldGUiKSAlPiUKICBzdGF0czo6bmEub21pdCgpCgoKCmNvbXBhcmEgPC0gY2JpbmQocG9ydGZvbGlvX3JldHVybnNfeHRzX3JlYmFsYW5jZWRfbW9udGhseSwgU1BZX3JldHVybnNfeHRzKQoKIyBjP21vIHNlIHZlCgpjaGFydHMuUGVyZm9ybWFuY2VTdW1tYXJ5KGNvbXBhcmEpCmBgYAoKIyMgUG9ydGFmb2xpbyA0OiBVdGlsaWRhZCBjdWFkcsOhdGljYQpgYGB7cn0KCnF1YXRfdXRfcG9ydCA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OnBvcnRmb2xpby5zcGVjKGFzc2V0cyA9IHN5bWJvbHMpCgpxdWF0X3V0X3BvcnQgPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjphZGQuY29uc3RyYWludCgKICBwb3J0Zm9saW8gPSBxdWF0X3V0X3BvcnQsCiAgdHlwZSA9ICJmdWxsX2ludmVzdG1lbnQiCikKCnF1YXRfdXRfcG9ydCA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OmFkZC5jb25zdHJhaW50KAogIHBvcnRmb2xpbyA9IHF1YXRfdXRfcG9ydCwKICB0eXBlID0gImJveCIsIG1pbiA9IDAuMDAsIG1heCA9IDAuNQopCgpxdWF0X3V0X3BvcnQgPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjphZGQub2JqZWN0aXZlKHF1YXRfdXRfcG9ydCwgdHlwZT0icXVhZHJhdGljX3V0aWxpdHkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJpc2tfYXZlcnNpb249MikKCm1heF9xdWF0X3V0X3BvcnQ8LSAgUG9ydGZvbGlvQW5hbHl0aWNzOjpvcHRpbWl6ZS5wb3J0Zm9saW8oCiAgUiA9IGFzc2V0X3JldHVybnNfeHRzLAogIHBvcnRmb2xpbyA9IHF1YXRfdXRfcG9ydCwKICAjIEVuIGVzdGUgY2FzbyB1c2EgInF1YWRwcm9nIiAKICBvcHRpbWl6ZV9tZXRob2QgPSAicXVhZHByb2ciLAogICMgUmVncmVzYSBjb24gaW5mb3JtYWNpP24gYWRpY2lvbmFsCiAgdHJhY2UgPSBUUlVFCikKCm1heF9xdWF0X3V0X3BvcnQKCndlaWdodHMzIDwtIHBsdWNrKC54ID0gbWF4X3F1YXRfdXRfcG9ydCwgIndlaWdodHMiKQoKIyMgTWF4IG1lYW4gYW5kIGxvdyBFVEwKCm1lYW5FVExfcG9ydCA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OnBvcnRmb2xpby5zcGVjKGFzc2V0cyA9IHN5bWJvbHMpCgptZWFuRVRMX3BvcnQgPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjphZGQuY29uc3RyYWludCgKICBwb3J0Zm9saW8gPSBtZWFuRVRMX3BvcnQsCiAgdHlwZSA9ICJ3ZWlnaHRfc3VtIiwKICBtaW5fc3VtPTAuOTksCiAgbWF4X3N1bT0xLjAxCikKCiNtZWFuRVRMX3BvcnQgPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjphZGQuY29uc3RyYWludCgKIyAgcG9ydGZvbGlvID0gbWVhbkVUTF9wb3J0LAojICB0eXBlID0gImZ1bGxfaW52ZXN0bWVudCIKIykKCm1lYW5FVExfcG9ydCA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OmFkZC5jb25zdHJhaW50KAogIHBvcnRmb2xpbyA9IG1lYW5FVExfcG9ydCwKICB0eXBlID0gImJveCIsIG1pbiA9IC0wLjA1LCBtYXggPSAwLjUwCikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCm1lYW5FVExfcG9ydCA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OmFkZC5vYmplY3RpdmUobWVhbkVUTF9wb3J0LCB0eXBlPSJyZXR1cm4iLCBuYW1lPSJtZWFuIikKCm1lYW5FVExfcG9ydCA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OmFkZC5vYmplY3RpdmUobWVhbkVUTF9wb3J0LCB0eXBlPSJyaXNrIiwgbmFtZT0iRVRMIiwgYXJndW1lbnRzPWxpc3QocD0wLjk1KSkKCm1heF9taW5fRVRMX3BvcnQ8LSAgUG9ydGZvbGlvQW5hbHl0aWNzOjpvcHRpbWl6ZS5wb3J0Zm9saW8oCiAgUiA9IGFzc2V0X3JldHVybnNfeHRzLAogIHBvcnRmb2xpbyA9IG1lYW5FVExfcG9ydCwKICAjIEVuIGVzdGUgY2FzbyB1c2EgInF1YWRwcm9nIiAKICBvcHRpbWl6ZV9tZXRob2QgPSAicmFuZG9tIiwKICBzZWFyY2hfc2l6ZSA9IDEwMDAwLAogICMgUmVncmVzYSBjb24gaW5mb3JtYWNpP24gYWRpY2lvbmFsCiAgdHJhY2UgPSBUUlVFCikKCndlaWdodHMgPC0gcGx1Y2soLnggPSBtYXhfbWluX0VUTF9wb3J0LCAid2VpZ2h0cyIpCgpwb3J0Zm9saW9fcmV0dXJuc194dHNfcmViYWxhbmNlZF9tb250aGx5IDwtCiAgUGVyZm9ybWFuY2VBbmFseXRpY3M6OlJldHVybi5wb3J0Zm9saW8oCiAgICBSID0gYXNzZXRfcmV0dXJuc194dHMsCiAgICB3ZWlnaHRzID0gd2VpZ2h0cywKICAgICMgUmViYWxhbmNlbyBNZW5zdWFsLCBpbXBsaWNhIGNvbXByYXIvdmVuZGVyIGNhZGEgbWVzCiAgICByZWJsYW5jZV9vbiA9ICJtb250aHMiLAogICAgZ2VvbWV0cmljID0gRkFMU0UKICApICU+JQogIGBjb2xuYW1lczwtYCgiTW9udGhseV9wb3J0Zm9saW9fcmV0dXJucyIpCgoKCmJta19yZXR1cm5zIDwtCiAgUGVyZm9ybWFuY2VBbmFseXRpY3M6OlJldHVybi5wb3J0Zm9saW8oCiAgICBSID0gU1BZX3JldHVybnNfeHRzLAogICAgIyBSZWJhbGFuY2VvIE1lbnN1YWwsIGltcGxpY2EgY29tcHJhci92ZW5kZXIgY2FkYSBtZXMKICAgIHJlYmxhbmNlX29uID0gIm1vbnRocyIsCiAgICBnZW9tZXRyaWMgPSBGQUxTRQogICkgJT4lCiAgYGNvbG5hbWVzPC1gKCJCZW5jaG1hcmtfUmV0dXJucyIpCgpxdWFkX3JldHVybnMgPC0KICBQZXJmb3JtYW5jZUFuYWx5dGljczo6UmV0dXJuLnBvcnRmb2xpbygKICAgIFIgPSBhc3NldF9yZXR1cm5zX3h0cywKICAgIHdlaWdodHMgPSB3ZWlnaHRzMywKICAgICMgUmViYWxhbmNlbyBNZW5zdWFsLCBpbXBsaWNhIGNvbXByYXIvdmVuZGVyIGNhZGEgbWVzCiAgICByZWJsYW5jZV9vbiA9ICJtb250aHMiLAogICAgZ2VvbWV0cmljID0gRkFMU0UKICApICU+JQogIGBjb2xuYW1lczwtYCgiUXVhZF9SZXR1cm5zIikKCmBgYAoKIyMjIFBvcnRhZm9saW8gNCB2cyBTJlA1MDAKYGBge3J9Cgpjb21wYXJhMiA8LSBjYmluZChwb3J0Zm9saW9fcmV0dXJuc194dHNfcmViYWxhbmNlZF9tb250aGx5LCBibWtfcmV0dXJucywgcXVhZF9yZXR1cm5zKQoKY2hhcnRzLlBlcmZvcm1hbmNlU3VtbWFyeShjb21wYXJhMikKCmBgYAoKCiMgQmFjayB0ZXN0CgpgYGB7cn0KCgpvcHRfcXVhZCA8LSBvcHRpbWl6ZS5wb3J0Zm9saW8ucmViYWxhbmNpbmcoYXNzZXRfcmV0dXJuc194dHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWF0X3V0X3BvcnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpbWl6ZV9tZXRob2QgPSAiUk9JIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYmFsYW5jZV9vbj0ibW9udGhzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWluaW5nX3BlcmlvZCA9IDEyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm9sbGluZ193aW5kb3c9MjQpCgpiYWNrdGVzdF9xdWFkX3JldCA8LSBSZXR1cm4ucG9ydGZvbGlvKGFzc2V0X3JldHVybnNfeHRzLCBleHRyYWN0V2VpZ2h0cygob3B0X3F1YWQpKSkKCmNvbG5hbWVzKGJhY2t0ZXN0X3F1YWRfcmV0KSA8LSAiQmt0X1F1YWQiCgoKIyMjIyMjIyMjIyMjIyMjIyMzIEVqZXJjaWNpb3Mgc29icmUgcmVzdHJpY2Npb25lcwoKcXVhdF91dF8zNSA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OnBvcnRmb2xpby5zcGVjKGFzc2V0cyA9IHN5bWJvbHMpCgpxdWF0X3V0XzM1IDwtIFBvcnRmb2xpb0FuYWx5dGljczo6YWRkLmNvbnN0cmFpbnQoCiAgcG9ydGZvbGlvID0gcXVhdF91dF8zNSwKICB0eXBlID0gImZ1bGxfaW52ZXN0bWVudCIKKQoKIyBubyBwdWVkZXMgaW52ZXJ0aXIgbT9zIGRlIDI1JQoKcXVhdF91dF8zNSA8LSBQb3J0Zm9saW9BbmFseXRpY3M6OmFkZC5jb25zdHJhaW50KAogIHBvcnRmb2xpbyA9IHF1YXRfdXRfMzUsCiAgdHlwZSA9ICJib3giLCBtaW4gPSAwLjAyLCBtYXggPSAuMTUKKQoKIyMgVHJhYmFqYXIgbT9zIGEgZGV0YWxsZQoKcXVhdF91dF8zNSA8LSBhZGQuY29uc3RyYWludChwb3J0Zm9saW89cXVhdF91dF8zNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlPSJncm91cCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBzPWxpc3QoYygyLCAzLCA0LCA1LCA2LCAxMSksIGMoMSwgNywgOCwgOSwgMTApKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfbWluPWMoMC4wLCAwLjApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwX21heD1jKC42MCwgMC40KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9sYWJlbHM9YygiRVNHIiwgIk5PTi1FU0ciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9wb3M9YygxLCAyKSkgI3NlIGFybWFuIGdydXBvcyBkZXBlbmRpZW5kbyBkZSBsYXMgYWNjaW9uZXMKCnF1YXRfdXRfMzUgPC0gUG9ydGZvbGlvQW5hbHl0aWNzOjphZGQuY29uc3RyYWludCgKICBwb3J0Zm9saW8gPSBxdWF0X3V0XzM1LAogIHR5cGUgPSAid2VpZ2h0X3N1bSIsCiAgbWluX3N1bT0xLjAwLAogIG1heF9zdW09MS4wMAopCgpxdWF0X3V0XzM1IDwtIFBvcnRmb2xpb0FuYWx5dGljczo6YWRkLm9iamVjdGl2ZShxdWF0X3V0XzM1LCB0eXBlPSJxdWFkcmF0aWNfdXRpbGl0eSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJpc2tfYXZlcnNpb249MikKCm9wdF9xdWFkXzM1IDwtIG9wdGltaXplLnBvcnRmb2xpby5yZWJhbGFuY2luZyhhc3NldF9yZXR1cm5zX3h0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF1YXRfdXRfMzUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcHRpbWl6ZV9tZXRob2QgPSAiUk9JIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYmFsYW5jZV9vbj0iZGF5cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpbmluZ19wZXJpb2QgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvbGxpbmdfd2luZG93PTI0KQoKb3B0X3F1YWRfMzVfbGFzdDwtICBQb3J0Zm9saW9BbmFseXRpY3M6Om9wdGltaXplLnBvcnRmb2xpbygKICBSID0gYXNzZXRfcmV0dXJuc194dHMsCiAgcG9ydGZvbGlvID0gcXVhdF91dF8zNSwKICBvcHRpbWl6ZV9tZXRob2QgPSAicXVhZHByb2ciLAogICNtb21lbnRhcmdzPW1vbWVudGFyZ3MsCiAgIyBSZWdyZXNhIGNvbiBpbmZvcm1hY2k/biBhZGljaW9uYWwKICB0cmFjZSA9IFRSVUUKKQoKCmBgYAoKIyMgR3LDoWZpY2EKYGBge3J9CmxpYnJhcnkodmlyaWRpc0xpdGUpCiMgTG9hZCB0aGUgcGFscyBwYWNrYWdlCmxpYnJhcnkocGFscykKCiMgQ3JlYXRlIDExLWNvbG9yIHBhbGV0dGUgdXNpbmcgdGhlIHRvbCBzY2hlbWUgd2l0aCBhIHN0ZXBwZWQgYXBwZWFyYW5jZQpjb2xvcmVzIDwtIGMoIiNBMUQ0RjEiLCAiIzkyRDA1MSIsICIjRTU3NDM2IiwiIzY4RTE4NSIsICIjNERCM0E3IiwgIiMzNTk1RDkiLCAiI0ZBQ0U0OCIsICIjRjI5MDcwIiwgIiM4MEU5QzciLCAiIzdENEExQiIsICIjMjA0ODgyIikKCgoKI2Rlc2NyaXB0b3JlcwpjaGFydC5XZWlnaHRzKG9wdF9xdWFkXzM1LCBjb2xvcnNldD1jb2xvcmVzLCBtYWluID0gIkRpc3RyaWJ1Y2nDs24gw7NwdGltYSBkZWwgcG9ydGFmb2xpbyIpCgpgYGAKCiMjIEdydXBvcwpgYGB7cn0KKGV4dHJhY3RHcm91cHMob3B0X3F1YWRfMzVfbGFzdCkpCmBgYAoKIyMgQmFja3Rlc3QgbWVuc3VhbCBhIDMgYcOxb3MKYGBge3J9CihiYWNrdGVzdF9xdWFkXzM1IDwtIFJldHVybi5wb3J0Zm9saW8oYXNzZXRfcmV0dXJuc194dHMsIGV4dHJhY3RXZWlnaHRzKChvcHRfcXVhZF8zNSkpKSkKCmBgYAoKYGBge3J9CmJlbmNobWFyayA8LSByZWFkX2V4Y2VsKCIvVXNlcnMvcm9nZWxpby9EZXNrdG9wL2JlbmNobWFyay54bHN4IikKCmJlbmNobWFyayRGZWNoYSA8LSBhcy5EYXRlKGJlbmNobWFyayRGZWNoYSwgZm9ybWF0ID0gIiVtLyVkLyVZIikKCiNjb252ZXJ0IGl0IHRvIGFuIHh0cyBvYmplY3QsIHRha2luZyB0aGUgRGF0ZSBjb2x1bW4gYXMgdGhlIERhdGUgYW5kIFBvcnRmb2xpbyBhbmQgSVBDIGFzIHRoZSB2YWx1ZXMKYmVuY2htYXJrLnh0cyA8LSB4dHMoYmVuY2htYXJrWyxjKDIsMyldLCBvcmRlci5ieSA9IGJlbmNobWFyayRGZWNoYSkKCgpjaGFydHMuUGVyZm9ybWFuY2VTdW1tYXJ5KGJlbmNobWFyay54dHMsIG1haW4gPSAiUmV0b3Jub3MgZGVsIHBvcnRhZm9saW8gdnMgSVBDIE3DqXhpY28iKQoKCgpgYGAKCgoKCg==