Loading libraries
library(tidyverse)
library(highcharter)
library(quantmod)
library(doParallel)
library(lubridate)
registerDoParallel(cores = 10)
options(scipen = 100)
Helper functions
For future summaries/transformations, implied correlation equation at the bottom of the markdown
# Helper Functions
divide <- function(x){
x/100
}
# implied correlation equation: more about this at the end of the markdown
imp_corr_value <- function(x = x, comp_data = comp_data, factor = "iv10d"){
adj_weight = x$weight/sum(x$weight)
ticker = unique(x$composite_ticker)
etf_data = filter(comp_data, composite_ticker == ticker)
etf_var = etf_data[,factor]^2
value = etf_var/(sum(x[,factor]*adj_weight)^2)
return(value)
}
imp_corr_tbl <- function(x, comp_data){
ticker = unique(x$composite_ticker)
df = data.frame(
ticker = ticker,
dte10 = imp_corr_value(x = x, comp_data = comp_data),
dte20 = imp_corr_value(x = x, comp_data = comp_data, factor = "iv20d"),
dte30 = imp_corr_value(x = x, comp_data = comp_data, factor = "iv30d"),
dte60 = imp_corr_value(x = x, comp_data = comp_data, factor = "iv60d"),
dte90 = imp_corr_value(x = x, comp_data = comp_data, factor = "iv90d"),
dte6m = imp_corr_value(x = x, comp_data = comp_data, factor = "iv6m"),
dte1yr = imp_corr_value(x = x, comp_data = comp_data, factor = "iv1yr"),
row.names = NULL
)
return(df)
}
Downloading Data
holdings_raw has all the holdings and corresponding weights for each etf of “2021-11-01”. quandl_data has all the vol data. vol_data is subsetting the implied vol dte’s i’m going to use for this table. the data comes with vol*100, so i transformed the vol data back to it’s percentage with the divide helper function
# Downloading Data
holdings_raw <- read_csv("holdings_data.csv")
quandl_data <- read_csv("quandl_data.csv")
vol_data <- quandl_data %>%
select(ticker, tradedate, iv10d, iv20d, iv30d, iv60d, iv90d, iv6m, iv1yr) %>%
mutate_if(is.numeric, divide) %>%
arrange(ticker)
holdings_raw data (first 100 rows)
vol_data data
# Transforms
# Filtering for tickers that quandl has vol data for
holdings_raw <- na.omit(holdings_raw[,2:5]) %>%
mutate(as_of_date = ymd(as_of_date)) %>%
filter(
composite_ticker %in% vol_data$ticker,
constituent_ticker %in% vol_data$ticker
)
# Filtering out ETFs with summed weights of less than 50%
comp_tickers_weight_filtered <- holdings_raw %>%
group_by(composite_ticker) %>%
summarise(value = sum(weight)) %>%
arrange(desc(value)) %>%
filter(value >= 0.50) %>%
pull(composite_ticker)
holdings_df <- holdings_raw %>%
filter(
composite_ticker %in% comp_tickers_weight_filtered
) %>%
select(-c(as_of_date))
# Separating ETF data
comp_tickers = holdings_df %>%
select(-c(constituent_ticker, weight)) %>%
distinct()
holdings_df
const_data: merged dataframe of constituent tickers and it’s corresponding iv + weight values
implied correlation summaries
mapped heped function imp_cprr_tabl to the listed_data. then innerjoined the resulting dataframe with a new column called “sum_weights”, this tells us the sum weights of the holdings that i had vol data for (and applied the imp correlation with)
merging data
sourcing composite (etf) name, merging all dataframes
metrics <- yahooQF(c("Name", "Change in Percent"))
tickers_yf = getQuote(df$ticker, what = metrics) %>%
mutate(ticker = rownames(.),
Name = ifelse(is.na(Name), ticker, Name))
ticker_names <- tickers_yf %>%
mutate(ticker = rownames(tickers_yf)) %>%
select(ticker, everything())
final = df %>%
left_join(ticker_names[,c("ticker", "Name")], by = c("ticker")) %>%
mutate(date = "2021-11-01") %>%
select(ticker, Name, date, pxatmiv, everything()) %>%
rename("Price" = pxatmiv)
final
cleaning df
added ‘leveraged’ column, string detected for “2”, “3”, “ultra”, or “direxion” and labeled them TRUE to filter out leveraged etfs. changed all column name to lower-case, and renamed the imp corr column values to their corresponding ivol[dte]
imp corr equation used
x = constituent data. comp_data = composite (etf) data. factor = ivol[dte] used.
adj_weight = constituent weight divided by the sum of all constituent weights (some etfs vary in the amount of weight data available. so this is to adjusted it so that an initial sum of 70% weight = 100% weight)
ticker = composite (etf) tickers
etf_data = composite (etf) vol data
etf_var = etf variance for selected factor (ie. SPY iv[dte]^2)
value = imp corr equation 
etf variance/sum(constituent vol * constituent adjusted_weight)^2
imp_corr_value <- function(x = x, comp_data = comp_data, factor = "iv10d"){
adj_weight = x$weight/sum(x$weight)
ticker = unique(x$composite_ticker)
etf_data = filter(comp_data, composite_ticker == ticker)
etf_var = etf_data[,factor]^2
value = etf_var/(sum(x[,factor]*adj_weight)^2)
return(value)
}
LS0tDQp0aXRsZTogIkltcGxpZWQgQ29ycmVsYXRpb24gVGFibGUiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIExvYWRpbmcgbGlicmFyaWVzDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGhpZ2hjaGFydGVyKQ0KbGlicmFyeShxdWFudG1vZCkNCmxpYnJhcnkoZG9QYXJhbGxlbCkNCmxpYnJhcnkobHVicmlkYXRlKQ0KDQpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXMgPSAxMCkNCm9wdGlvbnMoc2NpcGVuID0gMTAwKQ0KYGBgDQoNCiMgSGVscGVyIGZ1bmN0aW9ucw0KRm9yIGZ1dHVyZSBzdW1tYXJpZXMvdHJhbnNmb3JtYXRpb25zLCBpbXBsaWVkIGNvcnJlbGF0aW9uIGVxdWF0aW9uIGF0IHRoZSBib3R0b20gb2YgdGhlIG1hcmtkb3duDQoNCmBgYHtyLCBlY2hvPVRSVUV9DQojIEhlbHBlciBGdW5jdGlvbnMNCg0KZGl2aWRlIDwtIGZ1bmN0aW9uKHgpew0KICB4LzEwMA0KfQ0KDQojIGltcGxpZWQgY29ycmVsYXRpb24gZXF1YXRpb246IG1vcmUgYWJvdXQgdGhpcyBhdCB0aGUgZW5kIG9mIHRoZSBtYXJrZG93bg0KDQppbXBfY29ycl92YWx1ZSA8LSBmdW5jdGlvbih4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhLCBmYWN0b3IgPSAiaXYxMGQiKXsNCiAgDQogIGFkal93ZWlnaHQgPSB4JHdlaWdodC9zdW0oeCR3ZWlnaHQpDQogIHRpY2tlciA9IHVuaXF1ZSh4JGNvbXBvc2l0ZV90aWNrZXIpDQogIA0KICBldGZfZGF0YSA9IGZpbHRlcihjb21wX2RhdGEsIGNvbXBvc2l0ZV90aWNrZXIgPT0gdGlja2VyKQ0KICBldGZfdmFyID0gZXRmX2RhdGFbLGZhY3Rvcl1eMg0KICANCiAgdmFsdWUgPSBldGZfdmFyLyhzdW0oeFssZmFjdG9yXSphZGpfd2VpZ2h0KV4yKQ0KICByZXR1cm4odmFsdWUpDQp9DQoNCmltcF9jb3JyX3RibCA8LSBmdW5jdGlvbih4LCBjb21wX2RhdGEpew0KICANCiAgdGlja2VyID0gdW5pcXVlKHgkY29tcG9zaXRlX3RpY2tlcikNCiAgDQogIGRmID0gZGF0YS5mcmFtZSgNCiAgICB0aWNrZXIgPSB0aWNrZXIsDQogICAgZHRlMTAgPSBpbXBfY29ycl92YWx1ZSh4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhKSwNCiAgICBkdGUyMCA9IGltcF9jb3JyX3ZhbHVlKHggPSB4LCBjb21wX2RhdGEgPSBjb21wX2RhdGEsIGZhY3RvciA9ICJpdjIwZCIpLA0KICAgIGR0ZTMwID0gaW1wX2NvcnJfdmFsdWUoeCA9IHgsIGNvbXBfZGF0YSA9IGNvbXBfZGF0YSwgZmFjdG9yID0gIml2MzBkIiksDQogICAgZHRlNjAgPSBpbXBfY29ycl92YWx1ZSh4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhLCBmYWN0b3IgPSAiaXY2MGQiKSwNCiAgICBkdGU5MCA9IGltcF9jb3JyX3ZhbHVlKHggPSB4LCBjb21wX2RhdGEgPSBjb21wX2RhdGEsIGZhY3RvciA9ICJpdjkwZCIpLA0KICAgIGR0ZTZtID0gaW1wX2NvcnJfdmFsdWUoeCA9IHgsIGNvbXBfZGF0YSA9IGNvbXBfZGF0YSwgZmFjdG9yID0gIml2Nm0iKSwNCiAgICBkdGUxeXIgPSBpbXBfY29ycl92YWx1ZSh4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhLCBmYWN0b3IgPSAiaXYxeXIiKSwNCiAgICByb3cubmFtZXMgPSBOVUxMDQogICkNCiAgcmV0dXJuKGRmKQ0KfQ0KYGBgDQoNCiMgRG93bmxvYWRpbmcgRGF0YQ0KaG9sZGluZ3NfcmF3IGhhcyBhbGwgdGhlIGhvbGRpbmdzIGFuZCBjb3JyZXNwb25kaW5nIHdlaWdodHMgZm9yIGVhY2ggZXRmIG9mICIyMDIxLTExLTAxIi4gcXVhbmRsX2RhdGEgaGFzIGFsbCB0aGUgdm9sIGRhdGEuIHZvbF9kYXRhIGlzIHN1YnNldHRpbmcgdGhlIGltcGxpZWQgdm9sIGR0ZSdzIGknbSBnb2luZyB0byB1c2UgZm9yIHRoaXMgdGFibGUuIHRoZSBkYXRhIGNvbWVzIHdpdGggdm9sKjEwMCwgc28gaSB0cmFuc2Zvcm1lZCB0aGUgdm9sIGRhdGEgYmFjayB0byBpdCdzIHBlcmNlbnRhZ2Ugd2l0aCB0aGUgZGl2aWRlIGhlbHBlciBmdW5jdGlvbg0KDQpgYGB7ciBlY2hvPVRSVUV9DQojIERvd25sb2FkaW5nIERhdGENCg0KaG9sZGluZ3NfcmF3IDwtIHJlYWRfY3N2KCJob2xkaW5nc19kYXRhLmNzdiIpDQpxdWFuZGxfZGF0YSA8LSByZWFkX2NzdigicXVhbmRsX2RhdGEuY3N2IikNCg0Kdm9sX2RhdGEgPC0gcXVhbmRsX2RhdGEgJT4lDQogIHNlbGVjdCh0aWNrZXIsIHRyYWRlZGF0ZSwgaXYxMGQsIGl2MjBkLCBpdjMwZCwgaXY2MGQsIGl2OTBkLCBpdjZtLCBpdjF5cikgJT4lIA0KICBtdXRhdGVfaWYoaXMubnVtZXJpYywgZGl2aWRlKSAlPiUNCiAgYXJyYW5nZSh0aWNrZXIpDQpgYGANCg0KIyBob2xkaW5nc19yYXcgZGF0YSAoZmlyc3QgMTAwIHJvd3MpDQoNCmBgYHtyfQ0KaG9sZGluZ3NfcmF3WzE6MTAwLF0NCmBgYA0KDQojIHZvbF9kYXRhIGRhdGENCg0KYGBge3J9DQp2b2xfZGF0YQ0KYGBgDQoNCmBgYHtyfQ0KIyBUcmFuc2Zvcm1zDQoNCiMgRmlsdGVyaW5nIGZvciB0aWNrZXJzIHRoYXQgcXVhbmRsIGhhcyB2b2wgZGF0YSBmb3INCmhvbGRpbmdzX3JhdyA8LSBuYS5vbWl0KGhvbGRpbmdzX3Jhd1ssMjo1XSkgJT4lDQogIG11dGF0ZShhc19vZl9kYXRlID0geW1kKGFzX29mX2RhdGUpKSAlPiUNCiAgZmlsdGVyKA0KICAgIGNvbXBvc2l0ZV90aWNrZXIgJWluJSB2b2xfZGF0YSR0aWNrZXIsDQogICAgY29uc3RpdHVlbnRfdGlja2VyICVpbiUgdm9sX2RhdGEkdGlja2VyDQogICkNCg0KIyBGaWx0ZXJpbmcgb3V0IEVURnMgd2l0aCBzdW1tZWQgd2VpZ2h0cyBvZiBsZXNzIHRoYW4gNTAlDQpjb21wX3RpY2tlcnNfd2VpZ2h0X2ZpbHRlcmVkIDwtIGhvbGRpbmdzX3JhdyAlPiUNCiAgZ3JvdXBfYnkoY29tcG9zaXRlX3RpY2tlcikgJT4lDQogIHN1bW1hcmlzZSh2YWx1ZSA9IHN1bSh3ZWlnaHQpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHZhbHVlKSkgJT4lDQogIGZpbHRlcih2YWx1ZSA+PSAwLjUwKSAlPiUNCiAgcHVsbChjb21wb3NpdGVfdGlja2VyKQ0KDQpob2xkaW5nc19kZiA8LSBob2xkaW5nc19yYXcgJT4lDQogIGZpbHRlcigNCiAgICBjb21wb3NpdGVfdGlja2VyICVpbiUgY29tcF90aWNrZXJzX3dlaWdodF9maWx0ZXJlZA0KICApICU+JQ0KICBzZWxlY3QoLWMoYXNfb2ZfZGF0ZSkpDQoNCg0KIyBTZXBhcmF0aW5nIEVURiBkYXRhDQpjb21wX3RpY2tlcnMgPSBob2xkaW5nc19kZiAlPiUNCiAgc2VsZWN0KC1jKGNvbnN0aXR1ZW50X3RpY2tlciwgd2VpZ2h0KSkgJT4lDQogIGRpc3RpbmN0KCkNCg0KYGBgDQoNCmhvbGRpbmdzX2RmDQoNCmBgYHtyfQ0KaG9sZGluZ3NfZGYNCmBgYA0KDQpgYGB7cn0NCg0KIyBKb2luaW5nIGhvbGRpbmdzIGRhdGEgd2l0aCB2b2wgZGF0YQ0KDQpjb21wX2RhdGEgPC0gY29tcF90aWNrZXJzICU+JQ0KICBpbm5lcl9qb2luKHZvbF9kYXRhLCBieSA9IGMoImNvbXBvc2l0ZV90aWNrZXIiID0gInRpY2tlciIpKQ0KDQpjb25zdF9kYXRhIDwtIGhvbGRpbmdzX2RmICU+JQ0KICBpbm5lcl9qb2luKHZvbF9kYXRhLCBieSA9IGMoImNvbnN0aXR1ZW50X3RpY2tlciIgPSAidGlja2VyIikpIA0KDQojIFB1dHRpbmcgZGF0YSBpbnRvIGxpc3Qgd2hpY2ggaXMgaWQnZCBieSB0aGVpciBwYXJlbnQgZXRmDQpsaXN0ZWRfZGF0YSA9IHNwbGl0KGNvbnN0X2RhdGEsIGYgPSBjb25zdF9kYXRhJGNvbXBvc2l0ZV90aWNrZXIpDQoNCmBgYA0KDQpjb25zdF9kYXRhOiBtZXJnZWQgZGF0YWZyYW1lIG9mIGNvbnN0aXR1ZW50IHRpY2tlcnMgYW5kIGl0J3MgY29ycmVzcG9uZGluZyBpdiArIHdlaWdodCB2YWx1ZXMNCg0KYGBge3J9DQpjb25zdF9kYXRhDQpgYGANCg0KIyBpbXBsaWVkIGNvcnJlbGF0aW9uIHN1bW1hcmllcw0KDQptYXBwZWQgaGVwZWQgZnVuY3Rpb24gaW1wX2NwcnJfdGFibCB0byB0aGUgbGlzdGVkX2RhdGEuIHRoZW4gaW5uZXJqb2luZWQgdGhlIHJlc3VsdGluZyBkYXRhZnJhbWUgd2l0aCBhIG5ldyBjb2x1bW4gY2FsbGVkICJzdW1fd2VpZ2h0cyIsIHRoaXMgdGVsbHMgdXMgdGhlIHN1bSB3ZWlnaHRzIG9mIHRoZSBob2xkaW5ncyB0aGF0IGkgaGFkIHZvbCBkYXRhIGZvciAoYW5kIGFwcGxpZWQgdGhlIGltcCBjb3JyZWxhdGlvbiB3aXRoKQ0KDQpgYGB7cn0NCiMgSW1wbGllZCBDb3JyZWxhdGlvbiBDYWxjdWxhdGlvbg0KDQp2YWx1ZXMgPSBtYXBfZGYobGlzdGVkX2RhdGEsIGZ1bmN0aW9uKHgpIGltcF9jb3JyX3RibCh4ID0geCwgY29tcF9kYXRhID0gY29tcF9kYXRhKSkNCg0KDQojIEV4dHJhOiBBZGRpbmcgc3VtbWVkX3dlaWdodHMgYXMgYSBjb2x1bW4gdG8gdGhlIGZpbmFsIGNhbGN1bGF0aW9ucw0KDQpzdW1tZWRfd2VpZ2h0cyA8LSBob2xkaW5nc19yYXcgJT4lDQogIGdyb3VwX2J5KGNvbXBvc2l0ZV90aWNrZXIpICU+JQ0KICBzdW1tYXJpc2Uoc3VtX3dlaWdodHMgPSBzdW0od2VpZ2h0KSkgJT4lDQogIGZpbHRlcihzdW1fd2VpZ2h0cyA+PSAwLjUwKSANCg0KZGYgPSB2YWx1ZXMgJT4lDQogIGlubmVyX2pvaW4oc3VtbWVkX3dlaWdodHMsIGJ5ID0gYygidGlja2VyIiA9ICJjb21wb3NpdGVfdGlja2VyIikpICU+JQ0KICBpbm5lcl9qb2luKHF1YW5kbF9kYXRhWyxjKCJ0aWNrZXIiLCAicHhhdG1pdiIpXSwgYnkgPSBjKCJ0aWNrZXIiKSkNCg0KYGBgDQoNCmBgYHtyfQ0KZGYNCmBgYA0KDQojIG1lcmdpbmcgZGF0YQ0KDQpzb3VyY2luZyBjb21wb3NpdGUgKGV0ZikgbmFtZSwgbWVyZ2luZyBhbGwgZGF0YWZyYW1lcw0KDQpgYGB7cn0NCg0KbWV0cmljcyA8LSB5YWhvb1FGKGMoIk5hbWUiLCAiQ2hhbmdlIGluIFBlcmNlbnQiKSkNCg0KdGlja2Vyc195ZiA9IGdldFF1b3RlKGRmJHRpY2tlciwgd2hhdCA9IG1ldHJpY3MpICU+JQ0KICBtdXRhdGUodGlja2VyID0gcm93bmFtZXMoLiksDQogICAgICAgICBOYW1lID0gaWZlbHNlKGlzLm5hKE5hbWUpLCB0aWNrZXIsIE5hbWUpKQ0KDQp0aWNrZXJfbmFtZXMgPC0gdGlja2Vyc195ZiAlPiUNCiAgbXV0YXRlKHRpY2tlciA9IHJvd25hbWVzKHRpY2tlcnNfeWYpKSAlPiUNCiAgc2VsZWN0KHRpY2tlciwgZXZlcnl0aGluZygpKQ0KDQpmaW5hbCA9IGRmICU+JQ0KICBsZWZ0X2pvaW4odGlja2VyX25hbWVzWyxjKCJ0aWNrZXIiLCAiTmFtZSIpXSwgYnkgPSBjKCJ0aWNrZXIiKSkgJT4lDQogIG11dGF0ZShkYXRlID0gIjIwMjEtMTEtMDEiKSAlPiUNCiAgc2VsZWN0KHRpY2tlciwgTmFtZSwgZGF0ZSwgcHhhdG1pdiwgZXZlcnl0aGluZygpKSAlPiUNCiAgcmVuYW1lKCJQcmljZSIgPSBweGF0bWl2KQ0KDQpgYGANCg0KZmluYWwNCg0KYGBge3J9DQpmaW5hbA0KYGBgDQoNCiMgY2xlYW5pbmcgZGYNCg0KYWRkZWQgJ2xldmVyYWdlZCcgY29sdW1uLCBzdHJpbmcgZGV0ZWN0ZWQgZm9yICIyIiwgIjMiLCAidWx0cmEiLCBvciAiZGlyZXhpb24iIGFuZCBsYWJlbGVkIHRoZW0gVFJVRSB0byBmaWx0ZXIgb3V0IGxldmVyYWdlZCBldGZzLg0KY2hhbmdlZCBhbGwgY29sdW1uIG5hbWUgdG8gbG93ZXItY2FzZSwgYW5kIHJlbmFtZWQgdGhlIGltcCBjb3JyIGNvbHVtbiB2YWx1ZXMgdG8gdGhlaXIgY29ycmVzcG9uZGluZyBpdm9sW2R0ZV0NCg0KYGBge3J9DQpmaW5hbCAgPSBmaW5hbCAlPiUNCiAgbXV0YXRlKA0KICAgIGxldmVyYWdlZCA9IHN0cl9kZXRlY3QoZmluYWwkTmFtZSwgIjN8MnxVbHRyYXxEaXJleGlvbiIpIA0KICApIA0KDQpjb2xuYW1lcyhmaW5hbCkgPC0gdG9sb3dlcihjb2xuYW1lcyhmaW5hbCkpDQoNCmZpbmFsID0gZmluYWwgJT4lDQogIG11dGF0ZV9pZihpcy5udW1lcmljLCByb3VuZCwgMykgJT4lDQogIHJlbmFtZSgNCiAgICAiZHRlMTAiID0gaXYxMGQsDQogICAgImR0ZTIwIiA9IGl2MjBkLA0KICAgICJkdGUzMCIgPSBpdjMwZCwNCiAgICAiZHRlNjAiID0gaXY2MGQsDQogICAgImR0ZTkwIiA9IGl2OTBkLA0KICAgICJkdGU2bSIgPSBpdjZtLA0KICAgICJkdGUxeXIiID0gaXYxeXINCiAgKQ0KYGBgDQoNCmBgYHtyfQ0KZmluYWwNCmBgYA0KDQojIGltcCBjb3JyIGVxdWF0aW9uIHVzZWQNCg0KeCA9IGNvbnN0aXR1ZW50IGRhdGEuDQpjb21wX2RhdGEgPSBjb21wb3NpdGUgKGV0ZikgZGF0YS4NCmZhY3RvciA9IGl2b2xbZHRlXSB1c2VkLg0KDQphZGpfd2VpZ2h0ID0gY29uc3RpdHVlbnQgd2VpZ2h0IGRpdmlkZWQgYnkgdGhlIHN1bSBvZiBhbGwgY29uc3RpdHVlbnQgd2VpZ2h0cyAoc29tZSBldGZzIHZhcnkgaW4gdGhlIGFtb3VudCBvZiB3ZWlnaHQgZGF0YSBhdmFpbGFibGUuIHNvIHRoaXMgaXMgdG8gYWRqdXN0ZWQgaXQgc28gdGhhdCBhbiBpbml0aWFsIHN1bSBvZiA3MCUgd2VpZ2h0ID0gMTAwJSB3ZWlnaHQpDQoNCnRpY2tlciA9IGNvbXBvc2l0ZSAoZXRmKSB0aWNrZXJzDQoNCmV0Zl9kYXRhID0gY29tcG9zaXRlIChldGYpIHZvbCBkYXRhDQoNCmV0Zl92YXIgPSBldGYgdmFyaWFuY2UgZm9yIHNlbGVjdGVkIGZhY3RvciAoaWUuIFNQWSBpdltkdGVdXjIpDQoNCnZhbHVlID0gaW1wIGNvcnIgZXF1YXRpb24NCiFbQ2FwdGlvbiBmb3IgdGhlIHBpY3R1cmUuXShpbXBfY29ycl9pbWFnZS5wbmcpDQoNCmV0ZiB2YXJpYW5jZS9zdW0oY29uc3RpdHVlbnQgdm9sICogY29uc3RpdHVlbnQgYWRqdXN0ZWRfd2VpZ2h0KV4yDQoNCmBgYHtyfQ0KaW1wX2NvcnJfdmFsdWUgPC0gZnVuY3Rpb24oeCA9IHgsIGNvbXBfZGF0YSA9IGNvbXBfZGF0YSwgZmFjdG9yID0gIml2MTBkIil7DQogIA0KICBhZGpfd2VpZ2h0ID0geCR3ZWlnaHQvc3VtKHgkd2VpZ2h0KQ0KICB0aWNrZXIgPSB1bmlxdWUoeCRjb21wb3NpdGVfdGlja2VyKQ0KICANCiAgZXRmX2RhdGEgPSBmaWx0ZXIoY29tcF9kYXRhLCBjb21wb3NpdGVfdGlja2VyID09IHRpY2tlcikNCiAgZXRmX3ZhciA9IGV0Zl9kYXRhWyxmYWN0b3JdXjINCiAgDQogIHZhbHVlID0gZXRmX3Zhci8oc3VtKHhbLGZhY3Rvcl0qYWRqX3dlaWdodCleMikNCiAgcmV0dXJuKHZhbHVlKQ0KfQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K