1.0 data - etfs lists: ticker & name
filtered to remove leveraged/short/inversed etfs, price >= 10$
& mktcap >= 100m as of 2018
1.1 data - etf prices
from 2015-06-05 to 2022-06-07
etf_prices <- foreach(var = 1:nrow(etf_list), .combine = "bind_rows", .packages = "data.table") %dopar% {
price_api(
ticker = etf_list$ticker[var],
from = from_date,
to = to_date
)
}
etf_prices <- etf_prices %>%
rename_all(~tolower(.)) %>%
select(ticker, everything()) %>%
drop_na(close) %>%
mutate(date = ymd(date)) %>%
arrange(ticker, date)
head(etf_prices, 5)
2.0 spy - filtered for spy prices & calculated returns
daily simple returns = (last/first)-1
hold =
(close/lag(close))-1
overnight = (open/lag(close))-1
intraday = (close/open)-1
the first days of all returns set
to 0
head(spy_returns, 5)
2.1 spy - cumulative return timeseries

2.2 spy - return/inferential metrics
2015-06-05 to 2022-06-07
spy_returns %>%
pivot_longer(hold:intraday, names_to = "spy") %>%
group_by(spy) %>%
summarise(return = port(value, total = TRUE),
mean = mean(value),
median = median(value),
min = min(value),
q10 = quantile(value, .1),
q90 = quantile(value, .9),
max = max(value))
2.3 spy - return/portfolio metrics
spy_returns %>%
pivot_longer(hold:intraday, names_to = "spy") %>%
group_by(spy) %>%
mutate(portfolio = port(value, balance = 10000)) %>%
summarise(return = port(value, total = TRUE),
cagr = cagr(portfolio, days = as.numeric(last(date)-first(date))),
stdev = sd(value),
annual_vol = stdev*sqrt(252),
sharpe = cagr/annual_vol)
3.0 all etfs - return summary tbl grouped by ticker
ie. first row shows the simple returns of hold:intraday for aaxj, for
2015-06 to 2022-06. overnight was filtered for <= q95(overnight).
seemed to have some bad data (possible outliers) for 5-8 tickers, quick
attempt to remove them.
3.1 all etfs - summary tbl of the table from 3.0
etf_returns %>%
summarise(across(hold:intraday, ~port(., total = TRUE)),
trading_days = n()) %>%
filter(overnight <= quantile(overnight, .95)) %>%
summary()
ticker hold overnight intraday trading_days
Length:344 Min. :-0.70217 Min. :-0.76216 Min. :-0.85631 Min. :1106
Class :character 1st Qu.:-0.04497 1st Qu.:-0.04444 1st Qu.:-0.27788 1st Qu.:1765
Mode :character Median : 0.26180 Median : 0.33977 Median :-0.02531 Median :1765
Mean : 0.39452 Mean : 0.64582 Mean :-0.01074 Mean :1749
3rd Qu.: 0.75331 3rd Qu.: 1.11882 3rd Qu.: 0.17950 3rd Qu.:1765
Max. : 3.43673 Max. : 4.77693 Max. : 2.31032 Max. :1765
3.2 all etfs - boxplots of 3.0 data

3.3 all etfs - top3 overnight return tickers
wanted to take a closer look at the top tickers for overnight
returns: gvip, ptf, & xme. graphed a cumulative return timeseries
for them. question: highest overnight return doesn’t necessarily mean
overnight outperforms the buyhold strat per ticker. creating a new
metric next section to look into this.

3.4 all etfs - new metric: ‘excess overnight returns’ / ‘overnight
premium’
created new metric called ‘premium’ = overnight returns - hold
returns. arranged by descending order, top5 listed below
3.5 all etfs - graphed cumulative returns of top3 etfs from 3.4
the data for some of these might not be right

4.0 text eda - tokenized words from etf names
- calculated the total return by ticker
- split etf names into words
- grouped words, then aggregated ticker returns + counted how many
times each word was in data (frequency)
- distinct’d all return data to avoid words with same return values
(happens when words are shown multiple times, same value, same etf. ie.
“S & P…”)
- filtered for word count to be between 10 & 100 (wanted to see a
grouping effect/relationship, doesn’t help if one word is from one
ticker or if the word is etf, index, or fund)
- graphed a verticle column chart by word, by return type (ie.
hold/overnight/intraday), colored/filled by count
etf_returns %>%
summarise(across(hold:intraday, ~port(., total = TRUE))) %>%
left_join(etf_list, by = "ticker") %>%
unnest_tokens(words, name) %>%
group_by(words) %>%
summarise(across(hold:intraday, ~mean(.)),
count = n()) %>%
distinct(hold, overnight, intraday, .keep_all = TRUE) %>%
filter(count >= 10 & count <= 100) %>%
pivot_longer(hold:intraday) %>%
mutate(words = reorder_within(words, value, name)) %>%
ggplot(aes(value, words, fill = count)) +
geom_col() +
scale_y_reordered() +
facet_wrap(~name, scales = "free") +
labs(title = "aggregated returns by tokenized words",
subtitle = "avg total returns of 300+ etfs by words from name, 2015-2020",
x = "avg total returns (%)", y = "")

4.1 text eda - same concept as 4.0 except for the metric:
premium

5.0 date eda - boxplots of etf returns by year
how do these returns pane out in years like 2020?
etf_returns %>%
mutate(year = year(date)) %>%
group_by(ticker, year) %>%
summarise(across(hold:intraday, ~port(., total = TRUE)),
count = n()) %>%
filter(count >= 220) %>%
pivot_longer(hold:intraday) %>%
ggplot(aes(name, value, fill = name)) +
geom_boxplot(outlier.shape=NA) +
facet_wrap(~year, scales = "free") +
coord_cartesian(ylim = c(-.5, 1)) +
geom_hline(yintercept = 0, linetype = 2, alpha = 0.5, color = "red") +
labs(title = "etf returns by year",
subtitle = "2016-2021, full years",
x = "", y = "return (%)")
`summarise()` has grouped output by 'ticker'. You can override using the `.groups` argument.

5.1 date eda - summary tbl of data from 5.0
etf returns are aggregated by year
etf_returns %>%
mutate(year = year(date)) %>%
group_by(ticker, year) %>%
summarise(across(hold:intraday, ~port(., total = TRUE)),
count = n()) %>%
filter(count >= 220) %>%
mutate(premium = overnight-hold) %>%
ungroup() %>%
group_by(year) %>%
summarise(mean = mean(premium),
median = median(premium),
sd = sd(premium),
q10 = quantile(premium, .1),
q90 = quantile(premium, .9),
etfs_count = n())
`summarise()` has grouped output by 'ticker'. You can override using the `.groups` argument.
5.2 date eda - boxplots of etf returns by weekday
similar concept as 5.1, except for weekday. should be noted, the day
of overnight returns are of the day that they are closed, not opened.
ie. overnight returns for monday are actually the returns held from
friday close to monday open.
etf_returns %>%
mutate(weekday = weekdays(date)) %>%
group_by(ticker, weekday) %>%
summarise(across(hold:intraday, ~port(., total = TRUE)),
count = n()) %>%
filter(count >= 220) %>%
pivot_longer(hold:intraday) %>%
ggplot(aes(name, value, fill = name)) +
geom_boxplot(outlier.shape=NA) +
facet_wrap(~weekday, scales = "free") +
coord_cartesian(ylim = c(-.5, 1.2)) +
geom_hline(yintercept = 0, linetype = 2, alpha = 0.5, color = "red") +
labs(title = "etf returns by weekday",
subtitle = "2016-2021, full years",
x = "", y = "return (%)")
`summarise()` has grouped output by 'ticker'. You can override using the `.groups` argument.

5.3 date eda - boxplots of etf ‘excess overnight returns’ by year +
weekdays
similar concept as above boxplots, but combined for one metric:
excess overnight returns (overnight returns - hold returns)
etf_returns %>%
mutate(year = year(date),
weekday = weekdays(date, abbreviate = TRUE)) %>%
group_by(ticker, year, weekday) %>%
summarise(across(hold:overnight, ~port(., total = TRUE)),
count = n()) %>%
filter(count >= 45) %>%
mutate(premium = overnight-hold) %>%
ggplot(aes(weekday, premium, fill = weekday)) +
geom_boxplot(outlier.shape=NA) +
facet_wrap(~year, scales = "free") +
coord_cartesian(ylim = c(-.3, 0.3)) +
geom_hline(yintercept = 0, linetype = 2, alpha = 0.5, color = "red") +
labs(title = "'excess overnight returns' by year + weekdays",
subtitle = "2016-2021, full years",
x = "", y = "return (%)")
`summarise()` has grouped output by 'ticker', 'year'. You can override using the `.groups` argument.

5.4 date eda - boxplots of etf ‘excess overnight returns’ by year +
quarter
similar concept as above boxplots, but combined for one metric:
excess overnight returns (overnight returns - hold returns)
etf_returns %>%
mutate(year = year(date),
quarter = as.factor(quarter(date))) %>%
group_by(ticker, year, quarter) %>%
summarise(across(hold:overnight, ~port(., total = TRUE)),
count = n()) %>%
filter(year %in% c(2016:2021)) %>%
mutate(premium = overnight-hold) %>%
ggplot(aes(quarter, premium, fill = quarter)) +
geom_boxplot(outlier.shape=NA) +
facet_wrap(~year, scales = "free") +
coord_cartesian(ylim = c(-.25, 0.35)) +
geom_hline(yintercept = 0, linetype = 2, alpha = 0.5, color = "red") +
labs(title = "'excess overnight returns' by year + quarter",
subtitle = "2016-2021, full years",
x = "", y = "return (%)")
`summarise()` has grouped output by 'ticker', 'year'. You can override using the `.groups` argument.

LS0tDQp0aXRsZTogIm92ZXJuaWdodCBlZGEiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciwgaW5jbHVkZT1GQUxTRX0NCiMgbGlicmFyaWVzL3V0aWxpdGllcw0Kc291cmNlKCJ1dGlscy9saWJyYXJpZXMuUiIpDQpzb3VyY2UoInV0aWxzL2hlbHBlcl9mdW5jdGlvbnMuUiIpDQoNCiMgb3B0aW9ucw0Kb3B0aW9ucyhzY2lwZW4gPSAxMDApDQpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXMgPSAxMCkNCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDUpDQpgYGANCg0KIyAxLjAgZGF0YSAtIGV0ZnMgbGlzdHM6IHRpY2tlciAmIG5hbWUNCmZpbHRlcmVkIHRvIHJlbW92ZSBsZXZlcmFnZWQvc2hvcnQvaW52ZXJzZWQgZXRmcywgcHJpY2UgPj0gMTAkICYgbWt0Y2FwID49IDEwMG0gYXMgb2YgMjAxOA0KDQpgYGB7cn0NCmV0Zl9saXN0ICU+JQ0KICBzZWxlY3QodGlja2VyLCBuYW1lKQ0KYGBgDQoNCiMgMS4xIGRhdGEgLSBldGYgcHJpY2VzDQpmcm9tIDIwMTUtMDYtMDUgdG8gMjAyMi0wNi0wNw0KDQpgYGB7cn0NCmV0Zl9wcmljZXMgPC0gZXRmX3ByaWNlcyAlPiUNCiAgcmVuYW1lX2FsbCh+dG9sb3dlciguKSkgJT4lDQogIHNlbGVjdCh0aWNrZXIsIGV2ZXJ5dGhpbmcoKSkgJT4lDQogIGRyb3BfbmEoY2xvc2UpICU+JQ0KICBtdXRhdGUoZGF0ZSA9IHltZChkYXRlKSkgJT4lDQogIGFycmFuZ2UodGlja2VyLCBkYXRlKQ0KDQpoZWFkKGV0Zl9wcmljZXMsIDUpDQpgYGANCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQpzcHlfcmV0dXJucyA8LSBldGZfcHJpY2VzICU+JQ0KICBmaWx0ZXIodGlja2VyID09ICJTUFkiKSAlPiUNCiAgbXV0YXRlKGhvbGQgPSBjbG9zZS9sYWcoY2xvc2UpLTEsDQogICAgICAgICBvdmVybmlnaHQgPSBvcGVuL2xhZyhjbG9zZSktMSwNCiAgICAgICAgIGludHJhZGF5ID0gY2xvc2Uvb3Blbi0xLA0KICAgICAgICAgYWNyb3NzKGhvbGQ6aW50cmFkYXksIH5yZXBsYWNlKC4sIDEsIDApKSkNCmBgYA0KDQojIDIuMCBzcHkgLSBmaWx0ZXJlZCBmb3Igc3B5IHByaWNlcyAmIGNhbGN1bGF0ZWQgcmV0dXJucw0KPGJyPmRhaWx5IHNpbXBsZSByZXR1cm5zID0gKGxhc3QvZmlyc3QpLTE8L2JyPg0KPGJyPmhvbGQgPSAoY2xvc2UvbGFnKGNsb3NlKSktMTwvYnI+DQo8YnI+b3Zlcm5pZ2h0ID0gKG9wZW4vbGFnKGNsb3NlKSktMTwvYnI+DQo8YnI+aW50cmFkYXkgPSAoY2xvc2Uvb3BlbiktMTwvYnI+DQo8YnI+dGhlIGZpcnN0IGRheXMgb2YgYWxsIHJldHVybnMgc2V0IHRvIDA8L2JyPg0KDQpgYGB7cn0NCmhlYWQoc3B5X3JldHVybnMsIDUpDQpgYGANCg0KIyAyLjEgc3B5IC0gY3VtdWxhdGl2ZSByZXR1cm4gdGltZXNlcmllcw0KDQpgYGB7cn0NCnNweV9yZXR1cm5zICU+JQ0KICBtdXRhdGUoYWNyb3NzKGhvbGQ6aW50cmFkYXksIH5wb3J0KC4pKSkgJT4lICAjIHN1bW1hcnkoKQ0KICBwaXZvdF9sb25nZXIoaG9sZDppbnRyYWRheSkgJT4lDQogIGdncGxvdChhZXMoZGF0ZSwgdmFsdWUsIGNvbG9yID0gbmFtZSkpICsNCiAgZ2VvbV9saW5lKCkgKw0KICBsYWJzKHRpdGxlID0gInNweSBjdW11bGF0aXZlIHJldHVybnMiLA0KICAgICAgIHN1YnRpdGxlID0gIjIwMTUtMDYtMDUgdG8gMjAyMi0wNi0wNyIsDQogICAgICAgeCA9ICIiLCB5ID0gInJldHVybnMgKCUpIikNCmBgYA0KDQojIDIuMiBzcHkgLSByZXR1cm4vaW5mZXJlbnRpYWwgbWV0cmljcw0KMjAxNS0wNi0wNSB0byAyMDIyLTA2LTA3DQoNCmBgYHtyfQ0Kc3B5X3JldHVybnMgJT4lDQogIHBpdm90X2xvbmdlcihob2xkOmludHJhZGF5LCBuYW1lc190byA9ICJzcHkiKSAlPiUNCiAgZ3JvdXBfYnkoc3B5KSAlPiUNCiAgc3VtbWFyaXNlKHJldHVybiA9IHBvcnQodmFsdWUsIHRvdGFsID0gVFJVRSksDQogICAgICAgICAgICBtZWFuID0gbWVhbih2YWx1ZSksDQogICAgICAgICAgICBtZWRpYW4gPSBtZWRpYW4odmFsdWUpLA0KICAgICAgICAgICAgbWluID0gbWluKHZhbHVlKSwNCiAgICAgICAgICAgIHExMCA9IHF1YW50aWxlKHZhbHVlLCAuMSksDQogICAgICAgICAgICBxOTAgPSBxdWFudGlsZSh2YWx1ZSwgLjkpLA0KICAgICAgICAgICAgbWF4ID0gbWF4KHZhbHVlKSkNCmBgYA0KDQojIDIuMyBzcHkgLSByZXR1cm4vcG9ydGZvbGlvIG1ldHJpY3MNCg0KYGBge3J9DQpzcHlfcmV0dXJucyAlPiUNCiAgcGl2b3RfbG9uZ2VyKGhvbGQ6aW50cmFkYXksIG5hbWVzX3RvID0gInNweSIpICU+JQ0KICBncm91cF9ieShzcHkpICU+JQ0KICBtdXRhdGUocG9ydGZvbGlvID0gcG9ydCh2YWx1ZSwgYmFsYW5jZSA9IDEwMDAwKSkgJT4lIA0KICBzdW1tYXJpc2UocmV0dXJuID0gcG9ydCh2YWx1ZSwgdG90YWwgPSBUUlVFKSwNCiAgICAgICAgICAgIGNhZ3IgPSBjYWdyKHBvcnRmb2xpbywgZGF5cyA9IGFzLm51bWVyaWMobGFzdChkYXRlKS1maXJzdChkYXRlKSkpLA0KICAgICAgICAgICAgc3RkZXYgPSBzZCh2YWx1ZSksDQogICAgICAgICAgICBhbm51YWxfdm9sID0gc3RkZXYqc3FydCgyNTIpLA0KICAgICAgICAgICAgc2hhcnBlID0gY2Fnci9hbm51YWxfdm9sKQ0KYGBgDQoNCiMgMy4wIGFsbCBldGZzIC0gcmV0dXJuIHN1bW1hcnkgdGJsIGdyb3VwZWQgYnkgdGlja2VyDQppZS4gZmlyc3Qgcm93IHNob3dzIHRoZSBzaW1wbGUgcmV0dXJucyBvZiBob2xkOmludHJhZGF5IGZvciBhYXhqLCBmb3IgMjAxNS0wNiB0byAyMDIyLTA2Lg0Kb3Zlcm5pZ2h0IHdhcyBmaWx0ZXJlZCBmb3IgPD0gcTk1KG92ZXJuaWdodCkuIHNlZW1lZCB0byBoYXZlIHNvbWUgYmFkIGRhdGEgKHBvc3NpYmxlIG91dGxpZXJzKSBmb3IgNS04IHRpY2tlcnMsIHF1aWNrIGF0dGVtcHQgdG8gcmVtb3ZlIHRoZW0uDQoNCmBgYHtyfQ0KIyBldGZfcmV0dXJucyA8LSBldGZfcHJpY2VzICU+JQ0KIyAgIGdyb3VwX2J5KHRpY2tlcikgJT4lDQojICAgbXV0YXRlKGhvbGQgPSBjbG9zZS9sYWcoY2xvc2UpLTEsDQojICAgICAgICAgIG92ZXJuaWdodCA9IG9wZW4vbGFnKGNsb3NlKS0xLA0KIyAgICAgICAgICBpbnRyYWRheSA9IGNsb3NlL29wZW4tMSwNCiMgICAgICAgICAgYWNyb3NzKGhvbGQ6aW50cmFkYXksIH5yZXBsYWNlKC4sIDEsIDApKSkgJT4lDQojICAgc2VsZWN0KHRpY2tlciwgZGF0ZSwgaG9sZDppbnRyYWRheSkNCg0KZXRmX3JldHVybnMgJT4lDQogIHN1bW1hcmlzZShhY3Jvc3MoaG9sZDppbnRyYWRheSwgfnBvcnQoLiwgdG90YWwgPSBUUlVFKSksDQogICAgICAgICAgICB0cmFkaW5nX2RheXMgPSBuKCkpICU+JSANCiAgZmlsdGVyKG92ZXJuaWdodCA8PSBxdWFudGlsZShvdmVybmlnaHQsIC45NSkpICU+JQ0KICBoZWFkKDUpDQpgYGANCg0KIyAzLjEgYWxsIGV0ZnMgLSBzdW1tYXJ5IHRibCBvZiB0aGUgdGFibGUgZnJvbSAzLjANCg0KYGBge3J9DQpldGZfcmV0dXJucyAlPiUNCiAgc3VtbWFyaXNlKGFjcm9zcyhob2xkOmludHJhZGF5LCB+cG9ydCguLCB0b3RhbCA9IFRSVUUpKSwNCiAgICAgICAgICAgIHRyYWRpbmdfZGF5cyA9IG4oKSkgJT4lIA0KICBmaWx0ZXIob3Zlcm5pZ2h0IDw9IHF1YW50aWxlKG92ZXJuaWdodCwgLjk1KSkgJT4lIA0KICBzdW1tYXJ5KCkgDQpgYGANCg0KIyAzLjIgYWxsIGV0ZnMgLSBib3hwbG90cyBvZiAzLjAgZGF0YQ0KDQpgYGB7cn0NCmV0Zl9yZXR1cm5zICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGhvbGQ6aW50cmFkYXksIH5wb3J0KC4sIHRvdGFsID0gVFJVRSkpKSAlPiUgDQogIGZpbHRlcihvdmVybmlnaHQgPD0gcXVhbnRpbGUob3Zlcm5pZ2h0LCAuOTUpKSAlPiUgDQogIHBpdm90X2xvbmdlcihob2xkOmludHJhZGF5KSAlPiUNCiAgZ2dwbG90KGFlcyhuYW1lLCB2YWx1ZSkpICsNCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAyLCBhbHBoYSA9IDAuNSwgY29sb3IgPSAicmVkIikgKw0KICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTEsMykpICsNCiAgbGFicyh0aXRsZSA9ICJldGYgcmV0dXJucyIsDQogICAgICAgc3VidGl0bGUgPSAiMjAxNS0wNi0wNSB0byAyMDIyLTA2LTA3IiwNCiAgICAgICB4ID0gIiIsIHkgPSAicmV0dXJucyAoJSkiKQ0KYGBgDQoNCiMgMy4zIGFsbCBldGZzIC0gdG9wMyBvdmVybmlnaHQgcmV0dXJuIHRpY2tlcnMNCndhbnRlZCB0byB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhlIHRvcCB0aWNrZXJzIGZvciBvdmVybmlnaHQgcmV0dXJuczogZ3ZpcCwgcHRmLCAmIHhtZS4gZ3JhcGhlZCBhIGN1bXVsYXRpdmUgcmV0dXJuIHRpbWVzZXJpZXMgZm9yIHRoZW0uDQpxdWVzdGlvbjogaGlnaGVzdCBvdmVybmlnaHQgcmV0dXJuIGRvZXNuJ3QgbmVjZXNzYXJpbHkgbWVhbiBvdmVybmlnaHQgb3V0cGVyZm9ybXMgdGhlIGJ1eWhvbGQgc3RyYXQgcGVyIHRpY2tlci4gY3JlYXRpbmcgYSBuZXcgbWV0cmljIG5leHQgc2VjdGlvbiB0byBsb29rIGludG8gdGhpcy4NCg0KYGBge3IsIGZpZy53aWR0aD0xMH0NCmV0Zl9yZXR1cm5zICU+JQ0KICBmaWx0ZXIodGlja2VyICVpbiUgYygiR1ZJUCIsICJQVEYiLCAiWE1FIikpICU+JQ0KICBtdXRhdGUoYWNyb3NzKGhvbGQ6aW50cmFkYXksIH5wb3J0KC4pKSkgJT4lDQogIHBpdm90X2xvbmdlcihob2xkOmludHJhZGF5KSAlPiUNCiAgZ2dwbG90KGFlcyhkYXRlLCB2YWx1ZSwgY29sb3IgPSBuYW1lKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGZhY2V0X3dyYXAofnRpY2tlciwgc2NhbGVzID0gImZyZWUiKSArDQogIGxhYnModGl0bGUgPSAidG9wMyBvdmVybmlnaHQgY3VtdWxhdGl2ZSByZXR1cm5zIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIyMDE1LTA2LTA1IHRvIDIwMjItMDYtMDciLA0KICAgICAgIHggPSAiIiwgeSA9ICJyZXR1cm5zICglKSIpDQpgYGANCg0KIyAzLjQgYWxsIGV0ZnMgLSBuZXcgbWV0cmljOiAnZXhjZXNzIG92ZXJuaWdodCByZXR1cm5zJyAvICdvdmVybmlnaHQgcHJlbWl1bScNCmNyZWF0ZWQgbmV3IG1ldHJpYyBjYWxsZWQgJ3ByZW1pdW0nID0gb3Zlcm5pZ2h0IHJldHVybnMgLSBob2xkIHJldHVybnMuDQphcnJhbmdlZCBieSBkZXNjZW5kaW5nIG9yZGVyLCB0b3A1IGxpc3RlZCBiZWxvdw0KDQpgYGB7cn0NCmV0Zl9yZXR1cm5zICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGhvbGQ6b3Zlcm5pZ2h0LCB+cG9ydCguLCB0b3RhbCA9IFRSVUUpKSkgJT4lDQogIG11dGF0ZShwcmVtaXVtID0gb3Zlcm5pZ2h0LWhvbGQpICU+JQ0KICBhcnJhbmdlKGRlc2MocHJlbWl1bSkpICU+JQ0KICBmaWx0ZXIob3Zlcm5pZ2h0IDw9IHF1YW50aWxlKG92ZXJuaWdodCwgLjk1KSkgJT4lDQogIGhlYWQoNSkNCmBgYA0KDQojIDMuNSBhbGwgZXRmcyAtIGdyYXBoZWQgY3VtdWxhdGl2ZSByZXR1cm5zIG9mIHRvcDMgZXRmcyBmcm9tIDMuNA0KdGhlIGRhdGEgZm9yIHNvbWUgb2YgdGhlc2UgbWlnaHQgbm90IGJlIHJpZ2h0DQoNCmBgYHtyLCBmaWcud2lkdGg9MTB9DQpldGZfcmV0dXJucyAlPiUNCiAgZmlsdGVyKHRpY2tlciAlaW4lIGMoIktXRUIiLCAiRkVOWSIsICJHVklQIikpICU+JQ0KICBtdXRhdGUoYWNyb3NzKGhvbGQ6aW50cmFkYXksIH5wb3J0KC4pKSkgJT4lDQogIHBpdm90X2xvbmdlcihob2xkOmludHJhZGF5KSAlPiUNCiAgZ2dwbG90KGFlcyhkYXRlLCB2YWx1ZSwgY29sb3IgPSBuYW1lKSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGZhY2V0X3dyYXAofnRpY2tlciwgc2NhbGVzID0gImZyZWUiKSArDQogIGxhYnModGl0bGUgPSAidG9wMyBvdmVybmlnaHQgY3VtdWxhdGl2ZSByZXR1cm5zIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIyMDE1LTA2LTA1IHRvIDIwMjItMDYtMDciLA0KICAgICAgIHggPSAiIiwgeSA9ICJyZXR1cm5zICglKSIpDQpgYGANCg0KDQojIDQuMCB0ZXh0IGVkYSAtIHRva2VuaXplZCB3b3JkcyBmcm9tIGV0ZiBuYW1lcw0KMS4gY2FsY3VsYXRlZCB0aGUgdG90YWwgcmV0dXJuIGJ5IHRpY2tlcg0KMi4gc3BsaXQgZXRmIG5hbWVzIGludG8gd29yZHMNCjMuIGdyb3VwZWQgd29yZHMsIHRoZW4gYWdncmVnYXRlZCB0aWNrZXIgcmV0dXJucyArIGNvdW50ZWQgaG93IG1hbnkgdGltZXMgZWFjaCB3b3JkIHdhcyBpbiBkYXRhIChmcmVxdWVuY3kpDQo0LiBkaXN0aW5jdCdkIGFsbCByZXR1cm4gZGF0YSB0byBhdm9pZCB3b3JkcyB3aXRoIHNhbWUgcmV0dXJuIHZhbHVlcyAoaGFwcGVucyB3aGVuIHdvcmRzIGFyZSBzaG93biBtdWx0aXBsZSB0aW1lcywgc2FtZSB2YWx1ZSwgc2FtZSBldGYuIGllLiAiUyAmIFAuLi4iKQ0KNS4gZmlsdGVyZWQgZm9yIHdvcmQgY291bnQgdG8gYmUgYmV0d2VlbiAxMCAmIDEwMCAod2FudGVkIHRvIHNlZSBhIGdyb3VwaW5nIGVmZmVjdC9yZWxhdGlvbnNoaXAsIGRvZXNuJ3QgaGVscCBpZiBvbmUgd29yZCBpcyBmcm9tIG9uZSB0aWNrZXIgb3IgaWYgdGhlIHdvcmQgaXMgZXRmLCBpbmRleCwgb3IgZnVuZCkNCjYuIGdyYXBoZWQgYSB2ZXJ0aWNsZSBjb2x1bW4gY2hhcnQgYnkgd29yZCwgYnkgcmV0dXJuIHR5cGUgKGllLiBob2xkL292ZXJuaWdodC9pbnRyYWRheSksIGNvbG9yZWQvZmlsbGVkIGJ5IGNvdW50DQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTEyfQ0KZXRmX3JldHVybnMgJT4lDQogIHN1bW1hcmlzZShhY3Jvc3MoaG9sZDppbnRyYWRheSwgfnBvcnQoLiwgdG90YWwgPSBUUlVFKSkpICU+JQ0KICBsZWZ0X2pvaW4oZXRmX2xpc3QsIGJ5ID0gInRpY2tlciIpICU+JQ0KICB1bm5lc3RfdG9rZW5zKHdvcmRzLCBuYW1lKSAlPiUNCiAgZ3JvdXBfYnkod29yZHMpICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGhvbGQ6aW50cmFkYXksIH5tZWFuKC4pKSwNCiAgICAgICAgICAgIGNvdW50ID0gbigpKSAlPiUNCiAgZGlzdGluY3QoaG9sZCwgb3Zlcm5pZ2h0LCBpbnRyYWRheSwgLmtlZXBfYWxsID0gVFJVRSkgJT4lDQogIGZpbHRlcihjb3VudCA+PSAxMCAmIGNvdW50IDw9IDEwMCkgJT4lDQogIHBpdm90X2xvbmdlcihob2xkOmludHJhZGF5KSAlPiUNCiAgbXV0YXRlKHdvcmRzID0gcmVvcmRlcl93aXRoaW4od29yZHMsIHZhbHVlLCBuYW1lKSkgJT4lDQogIGdncGxvdChhZXModmFsdWUsIHdvcmRzLCBmaWxsID0gY291bnQpKSArDQogIGdlb21fY29sKCkgKw0KICBzY2FsZV95X3Jlb3JkZXJlZCgpICsNCiAgZmFjZXRfd3JhcCh+bmFtZSwgc2NhbGVzID0gImZyZWUiKSArDQogIGxhYnModGl0bGUgPSAiYWdncmVnYXRlZCByZXR1cm5zIGJ5IHRva2VuaXplZCB3b3JkcyIsDQogICAgICAgc3VidGl0bGUgPSAiYXZnIHRvdGFsIHJldHVybnMgb2YgMzAwKyBldGZzIGJ5IHdvcmRzIGZyb20gbmFtZSwgMjAxNS0yMDIwIiwNCiAgICAgICB4ID0gImF2ZyB0b3RhbCByZXR1cm5zICglKSIsIHkgPSAiIikNCmBgYA0KDQojIDQuMSB0ZXh0IGVkYSAtIHNhbWUgY29uY2VwdCBhcyA0LjAgZXhjZXB0IGZvciB0aGUgbWV0cmljOiBwcmVtaXVtDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTh9DQpldGZfcmV0dXJucyAlPiUNCiAgc3VtbWFyaXNlKGFjcm9zcyhob2xkOm92ZXJuaWdodCwgfnBvcnQoLiwgdG90YWwgPSBUUlVFKSkpICU+JQ0KICBtdXRhdGUocHJlbWl1bSA9IG92ZXJuaWdodC1ob2xkKSAlPiUNCiAgbGVmdF9qb2luKGV0Zl9saXN0LCBieSA9ICJ0aWNrZXIiKSAlPiUNCiAgdW5uZXN0X3Rva2Vucyh3b3JkcywgbmFtZSkgJT4lDQogIGdyb3VwX2J5KHdvcmRzKSAlPiUNCiAgc3VtbWFyaXNlKHByZW1pdW0gPSBtZWFuKHByZW1pdW0pLA0KICAgICAgICAgICAgY291bnQgPSBuKCkpICU+JQ0KICBkaXN0aW5jdChwcmVtaXVtLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUNCiAgZmlsdGVyKGNvdW50ID49IDEwICYgY291bnQgPD0gMTAwKSAlPiUgDQogIGdncGxvdChhZXMocHJlbWl1bSwgZmN0X3Jlb3JkZXIod29yZHMsIHByZW1pdW0pLCBmaWxsID0gY291bnQpKSArDQogIGdlb21fY29sKCkgKw0KICBsYWJzKHRpdGxlID0gImFnZ3JlZ2F0ZWQgb3Zlcm5pZ2h0IGV4Y2VzcyByZXR1cm5zIGJ5IHRva2VuaXplZCB3b3JkcyIsDQogICAgICAgc3VidGl0bGUgPSAiYXZnIHRvdGFsICdvdmVybmlnaHQgZXhjZXNzIHJldHVybnMnIChvdmVybmlnaHQtaG9sZCkgb2YgMzAwKyBldGZzIGJ5IHdvcmRzIGZyb20gbmFtZSwgMjAxNS0yMDIyIiwNCiAgICAgICB4ID0gImF2ZyBvdmVybmlnaHQgZXhjZXNzIHJldHVybnMgKCUpIiwgeSA9ICIiKQ0KYGBgDQoNCiMgNS4wIGRhdGUgZWRhIC0gYm94cGxvdHMgb2YgZXRmIHJldHVybnMgYnkgeWVhcg0KaG93IGRvIHRoZXNlIHJldHVybnMgcGFuZSBvdXQgaW4geWVhcnMgbGlrZSAyMDIwPw0KDQpgYGB7cn0NCmV0Zl9yZXR1cm5zICU+JQ0KICBtdXRhdGUoeWVhciA9IHllYXIoZGF0ZSkpICU+JQ0KICBncm91cF9ieSh0aWNrZXIsIHllYXIpICU+JQ0KICBzdW1tYXJpc2UoYWNyb3NzKGhvbGQ6aW50cmFkYXksIH5wb3J0KC4sIHRvdGFsID0gVFJVRSkpLA0KICAgICAgICAgICAgY291bnQgPSBuKCkpICU+JQ0KICBmaWx0ZXIoY291bnQgPj0gMjIwKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGhvbGQ6aW50cmFkYXkpICU+JQ0KICBnZ3Bsb3QoYWVzKG5hbWUsIHZhbHVlLCBmaWxsID0gbmFtZSkpICsNCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsNCiAgZmFjZXRfd3JhcCh+eWVhciwgc2NhbGVzID0gImZyZWUiKSArDQogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygtLjUsIDEpKSArDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gMiwgYWxwaGEgPSAwLjUsIGNvbG9yID0gInJlZCIpICsNCiAgbGFicyh0aXRsZSA9ICJldGYgcmV0dXJucyBieSB5ZWFyIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIyMDE2LTIwMjEsIGZ1bGwgeWVhcnMiLA0KICAgICAgIHggPSAiIiwgeSA9ICJyZXR1cm4gKCUpIikNCmBgYA0KDQojIDUuMSBkYXRlIGVkYSAtIHN1bW1hcnkgdGJsIG9mIGRhdGEgZnJvbSA1LjANCmV0ZiByZXR1cm5zIGFyZSBhZ2dyZWdhdGVkIGJ5IHllYXINCg0KYGBge3J9DQpldGZfcmV0dXJucyAlPiUNCiAgbXV0YXRlKHllYXIgPSB5ZWFyKGRhdGUpKSAlPiUNCiAgZ3JvdXBfYnkodGlja2VyLCB5ZWFyKSAlPiUNCiAgc3VtbWFyaXNlKGFjcm9zcyhob2xkOmludHJhZGF5LCB+cG9ydCguLCB0b3RhbCA9IFRSVUUpKSwNCiAgICAgICAgICAgIGNvdW50ID0gbigpKSAlPiUNCiAgZmlsdGVyKGNvdW50ID49IDIyMCkgJT4lDQogIG11dGF0ZShwcmVtaXVtID0gb3Zlcm5pZ2h0LWhvbGQpICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGdyb3VwX2J5KHllYXIpICU+JQ0KICBzdW1tYXJpc2UobWVhbiA9IG1lYW4ocHJlbWl1bSksDQogICAgICAgICAgICBtZWRpYW4gPSBtZWRpYW4ocHJlbWl1bSksDQogICAgICAgICAgICBzZCA9IHNkKHByZW1pdW0pLA0KICAgICAgICAgICAgcTEwID0gcXVhbnRpbGUocHJlbWl1bSwgLjEpLA0KICAgICAgICAgICAgcTkwID0gcXVhbnRpbGUocHJlbWl1bSwgLjkpLA0KICAgICAgICAgICAgZXRmc19jb3VudCA9IG4oKSkNCmBgYA0KDQojIDUuMiBkYXRlIGVkYSAtIGJveHBsb3RzIG9mIGV0ZiByZXR1cm5zIGJ5IHdlZWtkYXkNCnNpbWlsYXIgY29uY2VwdCBhcyA1LjEsIGV4Y2VwdCBmb3Igd2Vla2RheS4gc2hvdWxkIGJlIG5vdGVkLCB0aGUgZGF5IG9mIG92ZXJuaWdodCByZXR1cm5zIGFyZSBvZiB0aGUgZGF5IHRoYXQgdGhleSBhcmUgY2xvc2VkLCBub3Qgb3BlbmVkLiBpZS4gb3Zlcm5pZ2h0IHJldHVybnMgZm9yIG1vbmRheSBhcmUgYWN0dWFsbHkgdGhlIHJldHVybnMgaGVsZCBmcm9tIGZyaWRheSBjbG9zZSB0byBtb25kYXkgb3Blbi4NCg0KYGBge3J9DQpldGZfcmV0dXJucyAlPiUNCiAgbXV0YXRlKHdlZWtkYXkgPSB3ZWVrZGF5cyhkYXRlKSkgJT4lDQogIGdyb3VwX2J5KHRpY2tlciwgd2Vla2RheSkgJT4lDQogIHN1bW1hcmlzZShhY3Jvc3MoaG9sZDppbnRyYWRheSwgfnBvcnQoLiwgdG90YWwgPSBUUlVFKSksDQogICAgICAgICAgICBjb3VudCA9IG4oKSkgJT4lDQogIGZpbHRlcihjb3VudCA+PSAyMjApICU+JQ0KICBwaXZvdF9sb25nZXIoaG9sZDppbnRyYWRheSkgJT4lDQogIGdncGxvdChhZXMobmFtZSwgdmFsdWUsIGZpbGwgPSBuYW1lKSkgKw0KICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT1OQSkgKw0KICBmYWNldF93cmFwKH53ZWVrZGF5LCBzY2FsZXMgPSAiZnJlZSIpICsNCiAgY29vcmRfY2FydGVzaWFuKHlsaW0gPSBjKC0uNSwgMS4yKSkgKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLCBsaW5ldHlwZSA9IDIsIGFscGhhID0gMC41LCBjb2xvciA9ICJyZWQiKSArDQogIGxhYnModGl0bGUgPSAiZXRmIHJldHVybnMgYnkgd2Vla2RheSIsDQogICAgICAgc3VidGl0bGUgPSAiMjAxNi0yMDIxLCBmdWxsIHllYXJzIiwNCiAgICAgICB4ID0gIiIsIHkgPSAicmV0dXJuICglKSIpDQpgYGANCg0KIyA1LjMgZGF0ZSBlZGEgLSBib3hwbG90cyBvZiBldGYgJ2V4Y2VzcyBvdmVybmlnaHQgcmV0dXJucycgYnkgeWVhciArIHdlZWtkYXlzDQpzaW1pbGFyIGNvbmNlcHQgYXMgYWJvdmUgYm94cGxvdHMsIGJ1dCBjb21iaW5lZCBmb3Igb25lIG1ldHJpYzogZXhjZXNzIG92ZXJuaWdodCByZXR1cm5zIChvdmVybmlnaHQgcmV0dXJucyAtIGhvbGQgcmV0dXJucykNCg0KYGBge3J9DQpldGZfcmV0dXJucyAlPiUNCiAgbXV0YXRlKHllYXIgPSB5ZWFyKGRhdGUpLA0KICAgICAgICAgd2Vla2RheSA9IHdlZWtkYXlzKGRhdGUsIGFiYnJldmlhdGUgPSBUUlVFKSkgJT4lDQogIGdyb3VwX2J5KHRpY2tlciwgeWVhciwgd2Vla2RheSkgJT4lDQogIHN1bW1hcmlzZShhY3Jvc3MoaG9sZDpvdmVybmlnaHQsIH5wb3J0KC4sIHRvdGFsID0gVFJVRSkpLA0KICAgICAgICAgICAgY291bnQgPSBuKCkpICU+JQ0KICBmaWx0ZXIoY291bnQgPj0gNDUpICU+JQ0KICBtdXRhdGUocHJlbWl1bSA9IG92ZXJuaWdodC1ob2xkKSAlPiUNCiAgZ2dwbG90KGFlcyh3ZWVrZGF5LCBwcmVtaXVtLCBmaWxsID0gd2Vla2RheSkpICsNCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGU9TkEpICsNCiAgZmFjZXRfd3JhcCh+eWVhciwgc2NhbGVzID0gImZyZWUiKSArDQogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygtLjMsIDAuMykpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAyLCBhbHBoYSA9IDAuNSwgY29sb3IgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gIidleGNlc3Mgb3Zlcm5pZ2h0IHJldHVybnMnIGJ5IHllYXIgKyB3ZWVrZGF5cyIsDQogICAgICAgc3VidGl0bGUgPSAiMjAxNi0yMDIxLCBmdWxsIHllYXJzIiwNCiAgICAgICB4ID0gIiIsIHkgPSAicmV0dXJuICglKSIpDQpgYGANCg0KIyA1LjQgZGF0ZSBlZGEgLSBib3hwbG90cyBvZiBldGYgJ2V4Y2VzcyBvdmVybmlnaHQgcmV0dXJucycgYnkgeWVhciArIHF1YXJ0ZXINCnNpbWlsYXIgY29uY2VwdCBhcyBhYm92ZSBib3hwbG90cywgYnV0IGNvbWJpbmVkIGZvciBvbmUgbWV0cmljOiBleGNlc3Mgb3Zlcm5pZ2h0IHJldHVybnMgKG92ZXJuaWdodCByZXR1cm5zIC0gaG9sZCByZXR1cm5zKQ0KDQpgYGB7cn0NCmV0Zl9yZXR1cm5zICU+JQ0KICBtdXRhdGUoeWVhciA9IHllYXIoZGF0ZSksDQogICAgICAgICBxdWFydGVyID0gYXMuZmFjdG9yKHF1YXJ0ZXIoZGF0ZSkpKSAlPiUNCiAgZ3JvdXBfYnkodGlja2VyLCB5ZWFyLCBxdWFydGVyKSAlPiUNCiAgc3VtbWFyaXNlKGFjcm9zcyhob2xkOm92ZXJuaWdodCwgfnBvcnQoLiwgdG90YWwgPSBUUlVFKSksDQogICAgICAgICAgICBjb3VudCA9IG4oKSkgJT4lDQogIGZpbHRlcih5ZWFyICVpbiUgYygyMDE2OjIwMjEpKSAlPiUNCiAgbXV0YXRlKHByZW1pdW0gPSBvdmVybmlnaHQtaG9sZCkgJT4lDQogIGdncGxvdChhZXMocXVhcnRlciwgcHJlbWl1bSwgZmlsbCA9IHF1YXJ0ZXIpKSArDQogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlPU5BKSArDQogIGZhY2V0X3dyYXAofnllYXIsIHNjYWxlcyA9ICJmcmVlIikgKw0KICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLS4yNSwgMC4zNSkpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAyLCBhbHBoYSA9IDAuNSwgY29sb3IgPSAicmVkIikgKw0KICBsYWJzKHRpdGxlID0gIidleGNlc3Mgb3Zlcm5pZ2h0IHJldHVybnMnIGJ5IHllYXIgKyBxdWFydGVyIiwNCiAgICAgICBzdWJ0aXRsZSA9ICIyMDE2LTIwMjEsIGZ1bGwgeWVhcnMiLA0KICAgICAgIHggPSAiIiwgeSA9ICJyZXR1cm4gKCUpIikNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==