orats clean - pull raw data
- ‘raw’ data is from orats/cores api
# raw data
orats_core <- readRDS(file = "../data/orats_core.rds")
# sample
head(orats_core, 3)
orats clean - liquidity filters, removing useless data, number
formatting, relabeling categorical values
- our predefined liquidity filter for this exercise is going to be:
- underlying price above $15
- average option volume 20days above 2000
# checking for any bad data in the numbers
# ie. there are tickers with price & volatility values at 0
renamed_orats_core %>%
summary()
ticker date price sector avgoptvolu20d atmivm1 dtexm1 atmivm2 dtexm2
Length:5875 Min. :2022-10-03 Min. : 0.00 Length:5875 Min. : 0 Min. : 0.00 Min. : 0.00 Min. : 0.00 Min. : 0.00
Class :character 1st Qu.:2022-10-03 1st Qu.: 7.10 Class :character 1st Qu.: 68 1st Qu.: 37.87 1st Qu.:19.00 1st Qu.: 36.30 1st Qu.: 47.00
Mode :character Median :2022-10-03 Median : 22.24 Mode :character Median : 276 Median : 52.94 Median :19.00 Median : 51.73 Median : 47.00
Mean :2022-10-03 Mean : 47.02 Mean : 8086 Mean : 63.22 Mean :18.86 Mean : 61.37 Mean : 46.64
3rd Qu.:2022-10-03 3rd Qu.: 49.92 3rd Qu.: 1354 3rd Qu.: 77.36 3rd Qu.:19.00 3rd Qu.: 75.43 3rd Qu.: 47.00
Max. :2022-10-03 Max. :11254.70 Max. :8375659 Max. :1000.00 Max. :75.00 Max. :1000.00 Max. :166.00
atmivm3 dtexm3 atmivm4 dtexm4
Min. : 0.00 Min. : 0.0 Min. : 0.00 Min. : 0.0
1st Qu.: 32.63 1st Qu.: 75.0 1st Qu.: 31.14 1st Qu.:138.0
Median : 46.92 Median :110.0 Median : 45.24 Median :166.0
Mean : 56.45 Mean : 97.9 Mean : 54.57 Mean :172.2
3rd Qu.: 70.68 3rd Qu.:110.0 3rd Qu.: 68.13 3rd Qu.:201.0
Max. :1000.00 Max. :229.0 Max. :1000.00 Max. :257.0
# let's see if they rename after filtering for liquidity
# they do, going to take a closer look at these tickers before filtering out values = 0
renamed_orats_core %>%
filter(price >= 15 & avgoptvolu20d >= 2000) %>%
summary()
ticker date price sector avgoptvolu20d atmivm1 dtexm1 atmivm2 dtexm2
Length:845 Min. :2022-10-03 Min. : 15.00 Length:845 Min. : 2005 Min. : 0.00 Min. : 0.00 Min. : 0.00 Min. : 0.00
Class :character 1st Qu.:2022-10-03 1st Qu.: 29.99 Class :character 1st Qu.: 3436 1st Qu.: 33.68 1st Qu.:19.00 1st Qu.: 34.43 1st Qu.:47.00
Mode :character Median :2022-10-03 Median : 57.20 Mode :character Median : 6393 Median : 45.45 Median :19.00 Median : 46.16 Median :47.00
Mean :2022-10-03 Mean : 112.45 Mean : 47547 Mean : 50.46 Mean :18.98 Mean : 51.40 Mean :46.94
3rd Qu.:2022-10-03 3rd Qu.: 112.80 3rd Qu.: 18987 3rd Qu.: 62.89 3rd Qu.:19.00 3rd Qu.: 64.12 3rd Qu.:47.00
Max. :2022-10-03 Max. :11254.70 Max. :8375659 Max. :309.60 Max. :19.00 Max. :232.52 Max. :47.00
atmivm3 dtexm3 atmivm4 dtexm4
Min. : 0.00 Min. : 0.00 Min. : 0.00 Min. : 0.0
1st Qu.: 32.70 1st Qu.: 75.00 1st Qu.: 32.16 1st Qu.:110.0
Median : 43.72 Median : 75.00 Median : 42.79 Median :110.0
Mean : 49.46 Mean : 88.07 Mean : 48.17 Mean :135.8
3rd Qu.: 61.39 3rd Qu.:110.00 3rd Qu.: 59.99 3rd Qu.:142.0
Max. :287.18 Max. :138.00 Max. :206.12 Max. :229.0
# seems it's just one occurrence $VIX - going to remove it later anyhow
renamed_orats_core %>%
filter(price >= 15 & avgoptvolu20d >= 2000,
if_any(starts_with("atmivm"), function(x) x == 0))
# going to see what we have for sector data
# seems we have 35 categories, there is one (the most frequent) category that has no value for the sector name
renamed_orats_core %>%
filter(price >= 15 & avgoptvolu20d >= 2000) %>%
count(sector) %>%
arrange(desc(n))
# upon closer inspecting, seems to be etfs/indicies - going to relabel this category to be "None"
renamed_orats_core %>%
filter(price >= 15 & avgoptvolu20d >= 2000, sector == "")
# relabeling sector = "" to "None
# filtering out atmivm{x} = 0
# rounding numeric values to the 2nd digit - since the implied vol numbers seem to be x100
clean_orats_core <- renamed_orats_core %>%
filter(price >= 15 & avgoptvolu20d >= 2000,
if_any(starts_with("atmivm"), function(x) x > 0)) %>%
mutate(sector = ifelse(sector == "", "None", sector),
across(where(is.numeric), function(x) round(x, 2))) %>%
arrange(ticker)
head(clean_orats_core, 3)
orats clean - closer inspection on different dtes per same monthly
contract
- dte seems to range at farther data contracts
- dont need to do anything about it now, but should be noted when
comparing between tickers
# noticed one last thing, the dte for the front monthlies seems to differ
# going to take a closer look at this, since i want the dte's to be the same for x contract for relative value trading
# seems
clean_orats_core %>%
select(ticker, starts_with("dtexm")) %>%
pivot_longer(dtexm1:dtexm4, names_to = "monthly_contract", values_to = "dte") %>%
group_by(monthly_contract, dte) %>%
count() %>%
arrange(desc(n))
# visualization
clean_orats_core %>%
select(ticker, starts_with("dtexm")) %>%
pivot_longer(dtexm1:dtexm4, names_to = "monthly_contract", values_to = "dte") %>%
ggplot(aes(monthly_contract, dte, fill = monthly_contract)) +
geom_boxplot() +
labs(title = "dte at dtexm{x} monthly contract",
subtitle = "i would want the boxplots to display one value (ie. dtexm1 & dtemx2 = straight line)",
x = "monthly contract", y = "days to expiration",
caption = "seems the 3rd & 4th monthly contracts has a range of different dtes")

orats clean - saving data
- same data that gets loaded into the relative value markdown exercise
(option_data)
saveRDS(clean_orats_core, "../data/clean_orats_core.rds")
price clean - helper function (yahoo api query)
- using yahoo api for price data (need adjusted prices)
# uses yahoo api - parameters are ticker, first_date, and last_date
# i've set it up to only return the date & adjusted close price since we'll be doing correlation analysis
# yahoo api rate seems to be 2k/hour, ~40k/day. since we are only pulling ~800 tickers, not going to set any sleep conditions
price_api <- function(ticker, first_date, last_date){
request <- GET(sprintf(
"https://query1.finance.yahoo.com/v7/finance/download/%s?period1=%s&period2=%s&interval=1d&events=history&includeAdjustedClose=true",
ticker, first_date, last_date))
if(status_code(request) == 200){
contents <- content(
x = request,
as = "parsed",
type = "text/csv",
encoding = "UTF-8",
show_col_types = FALSE,
col_select = c("date" = "Date", "adj_close" = "Adj Close"))
if(nrow(contents) > 0){
contents$ticker <- ticker
return(contents[,c("ticker", "date", "adj_close")])
} else {
message(sprintf("%s returned no data.", ticker))
return()
}
} else {
message(sprintf("%s returned status code: %s. No data pulled.", ticker, status_code(request)))
return()
}
}
price clean - query data
# pulling last date from our orats option data, adding +1 because yahoo api pulls between dates
orats_date <- as.Date(clean_orats_core$date[1]) + 1
# saving parameter variables for price api function
tickers <- clean_orats_core$ticker
first_date <- as.numeric(as.POSIXct(as.character(orats_date - 365)))
last_date <- as.numeric(as.POSIXct(orats_date))
# foreach loop ran with parallel processing
price_data <- foreach(var = 1:length(tickers), .combine = "bind_rows", .packages = "httr", .errorhandling = "pass") %dopar% {
price_api(
ticker = tickers[var],
first_date = first_date,
last_date = last_date)
}
head(price_data, 3)
price clean - checking how much tickers we pulled
- ended up missing some tickers, gathered remaining &
requeried
- then cleaned final price data
# seems we only pulled 419/844 of our tickers
price_data %>%
count(ticker) %>%
arrange(n)
# looking at our leftover tickers, there are some with underscores in the name - yahoo doesn't recognize that
# going to pre-filter out those tickers (~35 occurrences)
# yahoo also doesn't give historical data for indices & require there be a prefix "^DJX" to the name - these won't be collected
leftover_tickers <- clean_orats_core %>%
filter(!ticker %in% unique(price_data$ticker)) %>%
filter(!str_detect(ticker, "_")) %>%
pull(ticker)
leftover_tickers
[1] "DJX" "SPX" "XAU" "XSP"
# going to combine our price data tables and do some final cleaning
# the numbers doesn't seem bad (no 0's, etc)
clean_price_data <- price_data %>%
arrange(ticker, date) %>%
# summary()
mutate(adj_close = round(adj_close, 2))
# sample
head(clean_price_data, 3)
price clean - save cleaned price data
- same table used in relative value exercise (price_data)
LS0tDQp0aXRsZTogInJlbGF0aXZlIHZhbHVlIGV4ZXJjaXNlIC0gZGF0YSBjbGVhbiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyBvcmF0cyBjbGVhbiAtIHB1bGwgcmF3IGRhdGENCi0gJ3JhdycgZGF0YSBpcyBmcm9tIG9yYXRzL2NvcmVzIGFwaQ0KDQpgYGB7cn0NCiMgcmF3IGRhdGEgDQpvcmF0c19jb3JlIDwtIHJlYWRSRFMoZmlsZSA9ICIuLi9kYXRhL29yYXRzX2NvcmUucmRzIikNCg0KIyBzYW1wbGUNCmhlYWQob3JhdHNfY29yZSwgMykNCmBgYA0KDQojIyMgb3JhdHMgY2xlYW4gLSBzZWxlY3RpbmcgdmFyaWFibGVzLCByZW5hbWluZyAmIHR5cGUgZm9ybWF0dGluZw0KLSBzZWxlY3RlZCB2YWx1ZXMgYXJlIHRpY2tlciwgZGF0ZSwgcHJpY2UsIHNlY3RvciwgYXZnb3B0dm9sdTIwZCAmDQotIGF0bWl2bXt4fSA9IHRoZXNlIGFyZSB0aGUgYXQtdGhlLW1vbmV5IGltcGxpZWQgdm9sYXRpbGl0aWVzIGZvciB7eH0gbW9udGhseSBleHBpcmF0aW9uDQogIC0gaWUuIGF0bWl2bTEgc2hvd3MgdGhlIGF0bSBpbXBsaWVkIHZvbCBmb3IgdGhlIGZyb250IG1vbnRobHkNCi0gZHRleG17eH0gPSB0aGVzZSBhcmUgdGhlIGNvcnJlc3BvbmRpbmcgZHRlJ3Mgb2YgdGhlIGF0bWl2bXt4fSB2YWx1ZXMNCiAgLSBpZS4gZHRleG0xIHNob3dzIHRoZSBkdGVzIGZvciB0aGUgZnJvbnQgbW9udGhseQ0KLSAqKm9ubHkgbmVlZCB0b2RheSdzIG9wdGlvbiBjb250cmFjdCBkYXRhIGZvciB0aGlzIGV4ZXJjaXNlLCBzbyBwcmV0dHkgZmxleGlibGUgdG8gdXNlIG90aGVyIG9wdGlvbiBzb3VyY2VzIGxpa2UgcG9seWdvbiBvciBhIGJyb2tlcmFnZSBldGMqKg0KDQpgYGB7cn0NCiMgZm9ybWF0dGluZyB0aGUgY29sdW1uIG5hbWVzIHRvIGxvd2VyLWNhc2UNCiMgc2VsZWN0aW5nIHJlbGV2YW50IGRhdGENCiMgZm9ybWF0dGluZyBkYXRlIHRvIGJlIGluIGRhdGUgZm9ybWF0DQpyZW5hbWVkX29yYXRzX2NvcmUgPC0gb3JhdHNfY29yZSAlPiUNCiAgcmVuYW1lX2FsbCh+dG9sb3dlciguKSkgJT4lDQogIHNlbGVjdCh0aWNrZXIsIA0KICAgICAgICAgImRhdGUiID0gdHJhZGVkYXRlLCANCiAgICAgICAgICJwcmljZSIgPSBweGF0bWl2LCANCiAgICAgICAgICJzZWN0b3IiID0gc2VjdG9ybmFtZSwgDQogICAgICAgICBhdmdvcHR2b2x1MjBkLCBhdG1pdm0xLCBkdGV4bTEsIGF0bWl2bTIsIGR0ZXhtMiwgYXRtaXZtMywgZHRleG0zLCBhdG1pdm00LCBkdGV4bTQpICU+JQ0KICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZSkpDQoNCiMgc2FtcGxlDQpoZWFkKHJlbmFtZWRfb3JhdHNfY29yZSwgMykNCmBgYA0KDQojIyMgb3JhdHMgY2xlYW4gLSBsaXF1aWRpdHkgZmlsdGVycywgcmVtb3ZpbmcgdXNlbGVzcyBkYXRhLCBudW1iZXIgZm9ybWF0dGluZywgcmVsYWJlbGluZyBjYXRlZ29yaWNhbCB2YWx1ZXMNCi0gb3VyIHByZWRlZmluZWQgbGlxdWlkaXR5IGZpbHRlciBmb3IgdGhpcyBleGVyY2lzZSBpcyBnb2luZyB0byBiZToNCiAgLSB1bmRlcmx5aW5nIHByaWNlIGFib3ZlICQxNQ0KICAtIGF2ZXJhZ2Ugb3B0aW9uIHZvbHVtZSAyMGRheXMgYWJvdmUgMjAwMA0KDQpgYGB7ciBmaWcud2lkdGg9MTJ9DQojIGNoZWNraW5nIGZvciBhbnkgYmFkIGRhdGEgaW4gdGhlIG51bWJlcnMNCiMgaWUuIHRoZXJlIGFyZSB0aWNrZXJzIHdpdGggcHJpY2UgJiB2b2xhdGlsaXR5IHZhbHVlcyBhdCAwDQpyZW5hbWVkX29yYXRzX2NvcmUgJT4lDQogIHN1bW1hcnkoKQ0KDQojIGxldCdzIHNlZSBpZiB0aGV5IHJlbmFtZSBhZnRlciBmaWx0ZXJpbmcgZm9yIGxpcXVpZGl0eQ0KIyB0aGV5IGRvLCBnb2luZyB0byB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhlc2UgdGlja2VycyBiZWZvcmUgZmlsdGVyaW5nIG91dCB2YWx1ZXMgPSAwDQpyZW5hbWVkX29yYXRzX2NvcmUgJT4lDQogIGZpbHRlcihwcmljZSA+PSAxNSAmIGF2Z29wdHZvbHUyMGQgPj0gMjAwMCkgJT4lDQogIHN1bW1hcnkoKQ0KDQojIHNlZW1zIGl0J3MganVzdCBvbmUgb2NjdXJyZW5jZSAkVklYIC0gZ29pbmcgdG8gcmVtb3ZlIGl0IGxhdGVyIGFueWhvdw0KcmVuYW1lZF9vcmF0c19jb3JlICU+JQ0KICBmaWx0ZXIocHJpY2UgPj0gMTUgJiBhdmdvcHR2b2x1MjBkID49IDIwMDAsIA0KICAgICAgICAgaWZfYW55KHN0YXJ0c193aXRoKCJhdG1pdm0iKSwgZnVuY3Rpb24oeCkgeCA9PSAwKSkNCg0KIyBnb2luZyB0byBzZWUgd2hhdCB3ZSBoYXZlIGZvciBzZWN0b3IgZGF0YQ0KIyBzZWVtcyB3ZSBoYXZlIDM1IGNhdGVnb3JpZXMsIHRoZXJlIGlzIG9uZSAodGhlIG1vc3QgZnJlcXVlbnQpIGNhdGVnb3J5IHRoYXQgaGFzIG5vIHZhbHVlIGZvciB0aGUgc2VjdG9yIG5hbWUNCnJlbmFtZWRfb3JhdHNfY29yZSAlPiUNCiAgZmlsdGVyKHByaWNlID49IDE1ICYgYXZnb3B0dm9sdTIwZCA+PSAyMDAwKSAlPiUNCiAgY291bnQoc2VjdG9yKSAlPiUNCiAgYXJyYW5nZShkZXNjKG4pKQ0KDQojIHVwb24gY2xvc2VyIGluc3BlY3RpbmcsIHNlZW1zIHRvIGJlIGV0ZnMvaW5kaWNpZXMgLSBnb2luZyB0byByZWxhYmVsIHRoaXMgY2F0ZWdvcnkgdG8gYmUgIk5vbmUiDQpyZW5hbWVkX29yYXRzX2NvcmUgJT4lDQogIGZpbHRlcihwcmljZSA+PSAxNSAmIGF2Z29wdHZvbHUyMGQgPj0gMjAwMCwgc2VjdG9yID09ICIiKQ0KDQojIHJlbGFiZWxpbmcgc2VjdG9yID0gIiIgdG8gIk5vbmUNCiMgZmlsdGVyaW5nIG91dCBhdG1pdm17eH0gPSAwDQojIHJvdW5kaW5nIG51bWVyaWMgdmFsdWVzIHRvIHRoZSAybmQgZGlnaXQgLSBzaW5jZSB0aGUgaW1wbGllZCB2b2wgbnVtYmVycyBzZWVtIHRvIGJlIHgxMDANCmNsZWFuX29yYXRzX2NvcmUgPC0gcmVuYW1lZF9vcmF0c19jb3JlICU+JQ0KICBmaWx0ZXIocHJpY2UgPj0gMTUgJiBhdmdvcHR2b2x1MjBkID49IDIwMDAsDQogICAgICAgICBpZl9hbnkoc3RhcnRzX3dpdGgoImF0bWl2bSIpLCBmdW5jdGlvbih4KSB4ID4gMCkpICU+JQ0KICBtdXRhdGUoc2VjdG9yID0gaWZlbHNlKHNlY3RvciA9PSAiIiwgIk5vbmUiLCBzZWN0b3IpLA0KICAgICAgICAgYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCBmdW5jdGlvbih4KSByb3VuZCh4LCAyKSkpICU+JQ0KICBhcnJhbmdlKHRpY2tlcikNCg0KaGVhZChjbGVhbl9vcmF0c19jb3JlLCAzKQ0KYGBgDQoNCiMjIyBvcmF0cyBjbGVhbiAtIGNsb3NlciBpbnNwZWN0aW9uIG9uIGRpZmZlcmVudCBkdGVzIHBlciBzYW1lIG1vbnRobHkgY29udHJhY3QNCi0gZHRlIHNlZW1zIHRvIHJhbmdlIGF0IGZhcnRoZXIgZGF0YSBjb250cmFjdHMgDQotIGRvbnQgbmVlZCB0byBkbyBhbnl0aGluZyBhYm91dCBpdCBub3csIGJ1dCBzaG91bGQgYmUgbm90ZWQgd2hlbiBjb21wYXJpbmcgYmV0d2VlbiB0aWNrZXJzDQoNCmBgYHtyfQ0KIyBub3RpY2VkIG9uZSBsYXN0IHRoaW5nLCB0aGUgZHRlIGZvciB0aGUgZnJvbnQgbW9udGhsaWVzIHNlZW1zIHRvIGRpZmZlcg0KIyBnb2luZyB0byB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhpcywgc2luY2UgaSB3YW50IHRoZSBkdGUncyB0byBiZSB0aGUgc2FtZSBmb3IgeCBjb250cmFjdCBmb3IgcmVsYXRpdmUgdmFsdWUgdHJhZGluZw0KIyBzZWVtcyANCmNsZWFuX29yYXRzX2NvcmUgJT4lDQogIHNlbGVjdCh0aWNrZXIsIHN0YXJ0c193aXRoKCJkdGV4bSIpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGR0ZXhtMTpkdGV4bTQsIG5hbWVzX3RvID0gIm1vbnRobHlfY29udHJhY3QiLCB2YWx1ZXNfdG8gPSAiZHRlIikgJT4lDQogIGdyb3VwX2J5KG1vbnRobHlfY29udHJhY3QsIGR0ZSkgJT4lDQogIGNvdW50KCkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkNCg0KIyB2aXN1YWxpemF0aW9uDQpjbGVhbl9vcmF0c19jb3JlICU+JQ0KICBzZWxlY3QodGlja2VyLCBzdGFydHNfd2l0aCgiZHRleG0iKSkgJT4lDQogIHBpdm90X2xvbmdlcihkdGV4bTE6ZHRleG00LCBuYW1lc190byA9ICJtb250aGx5X2NvbnRyYWN0IiwgdmFsdWVzX3RvID0gImR0ZSIpICU+JQ0KICBnZ3Bsb3QoYWVzKG1vbnRobHlfY29udHJhY3QsIGR0ZSwgZmlsbCA9IG1vbnRobHlfY29udHJhY3QpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgbGFicyh0aXRsZSA9ICJkdGUgYXQgZHRleG17eH0gbW9udGhseSBjb250cmFjdCIsDQogICAgICAgc3VidGl0bGUgPSAiaSB3b3VsZCB3YW50IHRoZSBib3hwbG90cyB0byBkaXNwbGF5IG9uZSB2YWx1ZSAoaWUuIGR0ZXhtMSAmIGR0ZW14MiA9IHN0cmFpZ2h0IGxpbmUpIiwNCiAgICAgICB4ID0gIm1vbnRobHkgY29udHJhY3QiLCB5ID0gImRheXMgdG8gZXhwaXJhdGlvbiIsDQogICAgICAgY2FwdGlvbiA9ICJzZWVtcyB0aGUgM3JkICYgNHRoIG1vbnRobHkgY29udHJhY3RzIGhhcyBhIHJhbmdlIG9mIGRpZmZlcmVudCBkdGVzIikNCmBgYA0KDQojIyMgb3JhdHMgY2xlYW4gLSBzYXZpbmcgZGF0YQ0KLSBzYW1lIGRhdGEgdGhhdCBnZXRzIGxvYWRlZCBpbnRvIHRoZSByZWxhdGl2ZSB2YWx1ZSBtYXJrZG93biBleGVyY2lzZSAob3B0aW9uX2RhdGEpDQoNCmBgYHtyfQ0Kc2F2ZVJEUyhjbGVhbl9vcmF0c19jb3JlLCAiLi4vZGF0YS9jbGVhbl9vcmF0c19jb3JlLnJkcyIpDQpgYGANCg0KIyMjIHByaWNlIGNsZWFuIC0gaGVscGVyIGZ1bmN0aW9uICh5YWhvbyBhcGkgcXVlcnkpDQotIHVzaW5nIHlhaG9vIGFwaSBmb3IgcHJpY2UgZGF0YSAobmVlZCBhZGp1c3RlZCBwcmljZXMpDQoNCmBgYHtyfQ0KIyB1c2VzIHlhaG9vIGFwaSAtIHBhcmFtZXRlcnMgYXJlIHRpY2tlciwgZmlyc3RfZGF0ZSwgYW5kIGxhc3RfZGF0ZQ0KIyBpJ3ZlIHNldCBpdCB1cCB0byBvbmx5IHJldHVybiB0aGUgZGF0ZSAmIGFkanVzdGVkIGNsb3NlIHByaWNlIHNpbmNlIHdlJ2xsIGJlIGRvaW5nIGNvcnJlbGF0aW9uIGFuYWx5c2lzDQoNCiMgeWFob28gYXBpIHJhdGUgc2VlbXMgdG8gYmUgMmsvaG91ciwgfjQway9kYXkuIHNpbmNlIHdlIGFyZSBvbmx5IHB1bGxpbmcgfjgwMCB0aWNrZXJzLCBub3QgZ29pbmcgdG8gc2V0IGFueSBzbGVlcCBjb25kaXRpb25zDQpwcmljZV9hcGkgPC0gZnVuY3Rpb24odGlja2VyLCBmaXJzdF9kYXRlLCBsYXN0X2RhdGUpew0KICANCiAgcmVxdWVzdCA8LSBHRVQoc3ByaW50ZigNCiAgICAiaHR0cHM6Ly9xdWVyeTEuZmluYW5jZS55YWhvby5jb20vdjcvZmluYW5jZS9kb3dubG9hZC8lcz9wZXJpb2QxPSVzJnBlcmlvZDI9JXMmaW50ZXJ2YWw9MWQmZXZlbnRzPWhpc3RvcnkmaW5jbHVkZUFkanVzdGVkQ2xvc2U9dHJ1ZSIsIA0KICAgIHRpY2tlciwgZmlyc3RfZGF0ZSwgbGFzdF9kYXRlKSkNCiAgDQogIGlmKHN0YXR1c19jb2RlKHJlcXVlc3QpID09IDIwMCl7DQogICAgY29udGVudHMgPC0gY29udGVudCgNCiAgICAgIHggPSByZXF1ZXN0LA0KICAgICAgYXMgPSAicGFyc2VkIiwNCiAgICAgIHR5cGUgPSAidGV4dC9jc3YiLA0KICAgICAgZW5jb2RpbmcgPSAiVVRGLTgiLA0KICAgICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSwNCiAgICAgIGNvbF9zZWxlY3QgPSBjKCJkYXRlIiA9ICJEYXRlIiwgImFkal9jbG9zZSIgPSAiQWRqIENsb3NlIikpDQogICAgDQogICAgaWYobnJvdyhjb250ZW50cykgPiAwKXsNCiAgICAgIGNvbnRlbnRzJHRpY2tlciA8LSB0aWNrZXINCiAgICAgIHJldHVybihjb250ZW50c1ssYygidGlja2VyIiwgImRhdGUiLCAiYWRqX2Nsb3NlIildKQ0KICAgICAgDQogICAgfSBlbHNlIHsNCiAgICAgIG1lc3NhZ2Uoc3ByaW50ZigiJXMgcmV0dXJuZWQgbm8gZGF0YS4iLCB0aWNrZXIpKQ0KICAgICAgcmV0dXJuKCkNCiAgICB9DQogIH0gZWxzZSB7DQogICAgbWVzc2FnZShzcHJpbnRmKCIlcyByZXR1cm5lZCBzdGF0dXMgY29kZTogJXMuIE5vIGRhdGEgcHVsbGVkLiIsIHRpY2tlciwgc3RhdHVzX2NvZGUocmVxdWVzdCkpKQ0KICAgIHJldHVybigpDQogIH0NCn0NCmBgYA0KDQojIyMgcHJpY2UgY2xlYW4gLSBxdWVyeSBkYXRhDQoNCmBgYHtyfQ0KIyBwdWxsaW5nIGxhc3QgZGF0ZSBmcm9tIG91ciBvcmF0cyBvcHRpb24gZGF0YSwgYWRkaW5nICsxIGJlY2F1c2UgeWFob28gYXBpIHB1bGxzIGJldHdlZW4gZGF0ZXMNCm9yYXRzX2RhdGUgPC0gYXMuRGF0ZShjbGVhbl9vcmF0c19jb3JlJGRhdGVbMV0pICsgMQ0KDQojIHNhdmluZyBwYXJhbWV0ZXIgdmFyaWFibGVzIGZvciBwcmljZSBhcGkgZnVuY3Rpb24NCnRpY2tlcnMgPC0gY2xlYW5fb3JhdHNfY29yZSR0aWNrZXINCmZpcnN0X2RhdGUgPC0gYXMubnVtZXJpYyhhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihvcmF0c19kYXRlIC0gMzY1KSkpDQpsYXN0X2RhdGUgPC0gYXMubnVtZXJpYyhhcy5QT1NJWGN0KG9yYXRzX2RhdGUpKQ0KDQojIGZvcmVhY2ggbG9vcCByYW4gd2l0aCBwYXJhbGxlbCBwcm9jZXNzaW5nIA0KcHJpY2VfZGF0YSA8LSBmb3JlYWNoKHZhciA9IDE6bGVuZ3RoKHRpY2tlcnMpLCAuY29tYmluZSA9ICJiaW5kX3Jvd3MiLCAucGFja2FnZXMgPSAiaHR0ciIsIC5lcnJvcmhhbmRsaW5nID0gInBhc3MiKSAlZG9wYXIlIHsNCiAgcHJpY2VfYXBpKA0KICAgIHRpY2tlciA9IHRpY2tlcnNbdmFyXSwNCiAgICBmaXJzdF9kYXRlID0gZmlyc3RfZGF0ZSwNCiAgICBsYXN0X2RhdGUgPSBsYXN0X2RhdGUpDQp9DQoNCmhlYWQocHJpY2VfZGF0YSwgMykNCmBgYA0KDQojIyMgcHJpY2UgY2xlYW4gLSBjaGVja2luZyBob3cgbXVjaCB0aWNrZXJzIHdlIHB1bGxlZA0KLSBlbmRlZCB1cCBtaXNzaW5nIHNvbWUgdGlja2VycywgZ2F0aGVyZWQgcmVtYWluaW5nICYgcmVxdWVyaWVkDQotIHRoZW4gY2xlYW5lZCBmaW5hbCBwcmljZSBkYXRhDQoNCmBgYHtyfQ0KIyBzZWVtcyB3ZSBvbmx5IHB1bGxlZCA0MTkvODQ0IG9mIG91ciB0aWNrZXJzDQpwcmljZV9kYXRhICU+JQ0KICBjb3VudCh0aWNrZXIpICU+JQ0KICBhcnJhbmdlKG4pDQoNCiMgbG9va2luZyBhdCBvdXIgbGVmdG92ZXIgdGlja2VycywgdGhlcmUgYXJlIHNvbWUgd2l0aCB1bmRlcnNjb3JlcyBpbiB0aGUgbmFtZSAtIHlhaG9vIGRvZXNuJ3QgcmVjb2duaXplIHRoYXQNCiMgZ29pbmcgdG8gcHJlLWZpbHRlciBvdXQgdGhvc2UgdGlja2VycyAofjM1IG9jY3VycmVuY2VzKQ0KIyB5YWhvbyBhbHNvIGRvZXNuJ3QgZ2l2ZSBoaXN0b3JpY2FsIGRhdGEgZm9yIGluZGljZXMgJiByZXF1aXJlIHRoZXJlIGJlIGEgcHJlZml4ICJeREpYIiB0byB0aGUgbmFtZSAtIHRoZXNlIHdvbid0IGJlIGNvbGxlY3RlZA0KbGVmdG92ZXJfdGlja2VycyA8LSBjbGVhbl9vcmF0c19jb3JlICU+JQ0KICBmaWx0ZXIoIXRpY2tlciAlaW4lIHVuaXF1ZShwcmljZV9kYXRhJHRpY2tlcikpICU+JQ0KICBmaWx0ZXIoIXN0cl9kZXRlY3QodGlja2VyLCAiXyIpKSAlPiUNCiAgcHVsbCh0aWNrZXIpDQoNCmxlZnRvdmVyX3RpY2tlcnMNCg0KIyBnb2luZyB0byBjb21iaW5lIG91ciBwcmljZSBkYXRhIHRhYmxlcyBhbmQgZG8gc29tZSBmaW5hbCBjbGVhbmluZw0KIyB0aGUgbnVtYmVycyBkb2Vzbid0IHNlZW0gYmFkIChubyAwJ3MsIGV0YykNCmNsZWFuX3ByaWNlX2RhdGEgPC0gcHJpY2VfZGF0YSAlPiUNCiAgYXJyYW5nZSh0aWNrZXIsIGRhdGUpICU+JQ0KICAjIHN1bW1hcnkoKQ0KICBtdXRhdGUoYWRqX2Nsb3NlID0gcm91bmQoYWRqX2Nsb3NlLCAyKSkNCg0KIyBzYW1wbGUNCmhlYWQoY2xlYW5fcHJpY2VfZGF0YSwgMykNCmBgYA0KDQojIyMgcHJpY2UgY2xlYW4gLSBzYXZlIGNsZWFuZWQgcHJpY2UgZGF0YQ0KLSBzYW1lIHRhYmxlIHVzZWQgaW4gcmVsYXRpdmUgdmFsdWUgZXhlcmNpc2UgKHByaWNlX2RhdGEpDQoNCmBgYHtyfQ0Kc2F2ZVJEUyhvYmplY3QgPSBjbGVhbl9wcmljZV9kYXRhLCBmaWxlID0gIi4uL2RhdGEvY2xlYW5fcHJpY2VfZGF0YS5yZHMiKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K