orats clean - pull raw data

# raw data 
orats_core <- readRDS(file = "../data/orats_core.rds")

# sample
head(orats_core, 3)

orats clean - selecting variables, renaming & type formatting

# formatting the column names to lower-case
# selecting relevant data
# formatting date to be in date format
renamed_orats_core <- orats_core %>%
  rename_all(~tolower(.)) %>%
  select(ticker, 
         "date" = tradedate, 
         "price" = pxatmiv, 
         "sector" = sectorname, 
         avgoptvolu20d, atmivm1, dtexm1, atmivm2, dtexm2, atmivm3, dtexm3, atmivm4, dtexm4) %>%
  mutate(date = as.Date(date))

# sample
head(renamed_orats_core, 3)

orats clean - liquidity filters, removing useless data, number formatting, relabeling categorical values

# 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

# 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

saveRDS(clean_orats_core, "../data/clean_orats_core.rds")

price clean - helper function (yahoo api query)

# 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

# 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

LS0tDQp0aXRsZTogInJlbGF0aXZlIHZhbHVlIGV4ZXJjaXNlIC0gZGF0YSBjbGVhbiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMjIyBvcmF0cyBjbGVhbiAtIHB1bGwgcmF3IGRhdGENCi0gJ3JhdycgZGF0YSBpcyBmcm9tIG9yYXRzL2NvcmVzIGFwaQ0KDQpgYGB7cn0NCiMgcmF3IGRhdGEgDQpvcmF0c19jb3JlIDwtIHJlYWRSRFMoZmlsZSA9ICIuLi9kYXRhL29yYXRzX2NvcmUucmRzIikNCg0KIyBzYW1wbGUNCmhlYWQob3JhdHNfY29yZSwgMykNCmBgYA0KDQojIyMgb3JhdHMgY2xlYW4gLSBzZWxlY3RpbmcgdmFyaWFibGVzLCByZW5hbWluZyAmIHR5cGUgZm9ybWF0dGluZw0KLSBzZWxlY3RlZCB2YWx1ZXMgYXJlIHRpY2tlciwgZGF0ZSwgcHJpY2UsIHNlY3RvciwgYXZnb3B0dm9sdTIwZCAmDQotIGF0bWl2bXt4fSA9IHRoZXNlIGFyZSB0aGUgYXQtdGhlLW1vbmV5IGltcGxpZWQgdm9sYXRpbGl0aWVzIGZvciB7eH0gbW9udGhseSBleHBpcmF0aW9uDQogIC0gaWUuIGF0bWl2bTEgc2hvd3MgdGhlIGF0bSBpbXBsaWVkIHZvbCBmb3IgdGhlIGZyb250IG1vbnRobHkNCi0gZHRleG17eH0gPSB0aGVzZSBhcmUgdGhlIGNvcnJlc3BvbmRpbmcgZHRlJ3Mgb2YgdGhlIGF0bWl2bXt4fSB2YWx1ZXMNCiAgLSBpZS4gZHRleG0xIHNob3dzIHRoZSBkdGVzIGZvciB0aGUgZnJvbnQgbW9udGhseQ0KLSAqKm9ubHkgbmVlZCB0b2RheSdzIG9wdGlvbiBjb250cmFjdCBkYXRhIGZvciB0aGlzIGV4ZXJjaXNlLCBzbyBwcmV0dHkgZmxleGlibGUgdG8gdXNlIG90aGVyIG9wdGlvbiBzb3VyY2VzIGxpa2UgcG9seWdvbiBvciBhIGJyb2tlcmFnZSBldGMqKg0KDQpgYGB7cn0NCiMgZm9ybWF0dGluZyB0aGUgY29sdW1uIG5hbWVzIHRvIGxvd2VyLWNhc2UNCiMgc2VsZWN0aW5nIHJlbGV2YW50IGRhdGENCiMgZm9ybWF0dGluZyBkYXRlIHRvIGJlIGluIGRhdGUgZm9ybWF0DQpyZW5hbWVkX29yYXRzX2NvcmUgPC0gb3JhdHNfY29yZSAlPiUNCiAgcmVuYW1lX2FsbCh+dG9sb3dlciguKSkgJT4lDQogIHNlbGVjdCh0aWNrZXIsIA0KICAgICAgICAgImRhdGUiID0gdHJhZGVkYXRlLCANCiAgICAgICAgICJwcmljZSIgPSBweGF0bWl2LCANCiAgICAgICAgICJzZWN0b3IiID0gc2VjdG9ybmFtZSwgDQogICAgICAgICBhdmdvcHR2b2x1MjBkLCBhdG1pdm0xLCBkdGV4bTEsIGF0bWl2bTIsIGR0ZXhtMiwgYXRtaXZtMywgZHRleG0zLCBhdG1pdm00LCBkdGV4bTQpICU+JQ0KICBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZSkpDQoNCiMgc2FtcGxlDQpoZWFkKHJlbmFtZWRfb3JhdHNfY29yZSwgMykNCmBgYA0KDQojIyMgb3JhdHMgY2xlYW4gLSBsaXF1aWRpdHkgZmlsdGVycywgcmVtb3ZpbmcgdXNlbGVzcyBkYXRhLCBudW1iZXIgZm9ybWF0dGluZywgcmVsYWJlbGluZyBjYXRlZ29yaWNhbCB2YWx1ZXMNCi0gb3VyIHByZWRlZmluZWQgbGlxdWlkaXR5IGZpbHRlciBmb3IgdGhpcyBleGVyY2lzZSBpcyBnb2luZyB0byBiZToNCiAgLSB1bmRlcmx5aW5nIHByaWNlIGFib3ZlICQxNQ0KICAtIGF2ZXJhZ2Ugb3B0aW9uIHZvbHVtZSAyMGRheXMgYWJvdmUgMjAwMA0KDQpgYGB7ciBmaWcud2lkdGg9MTJ9DQojIGNoZWNraW5nIGZvciBhbnkgYmFkIGRhdGEgaW4gdGhlIG51bWJlcnMNCiMgaWUuIHRoZXJlIGFyZSB0aWNrZXJzIHdpdGggcHJpY2UgJiB2b2xhdGlsaXR5IHZhbHVlcyBhdCAwDQpyZW5hbWVkX29yYXRzX2NvcmUgJT4lDQogIHN1bW1hcnkoKQ0KDQojIGxldCdzIHNlZSBpZiB0aGV5IHJlbmFtZSBhZnRlciBmaWx0ZXJpbmcgZm9yIGxpcXVpZGl0eQ0KIyB0aGV5IGRvLCBnb2luZyB0byB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhlc2UgdGlja2VycyBiZWZvcmUgZmlsdGVyaW5nIG91dCB2YWx1ZXMgPSAwDQpyZW5hbWVkX29yYXRzX2NvcmUgJT4lDQogIGZpbHRlcihwcmljZSA+PSAxNSAmIGF2Z29wdHZvbHUyMGQgPj0gMjAwMCkgJT4lDQogIHN1bW1hcnkoKQ0KDQojIHNlZW1zIGl0J3MganVzdCBvbmUgb2NjdXJyZW5jZSAkVklYIC0gZ29pbmcgdG8gcmVtb3ZlIGl0IGxhdGVyIGFueWhvdw0KcmVuYW1lZF9vcmF0c19jb3JlICU+JQ0KICBmaWx0ZXIocHJpY2UgPj0gMTUgJiBhdmdvcHR2b2x1MjBkID49IDIwMDAsIA0KICAgICAgICAgaWZfYW55KHN0YXJ0c193aXRoKCJhdG1pdm0iKSwgZnVuY3Rpb24oeCkgeCA9PSAwKSkNCg0KIyBnb2luZyB0byBzZWUgd2hhdCB3ZSBoYXZlIGZvciBzZWN0b3IgZGF0YQ0KIyBzZWVtcyB3ZSBoYXZlIDM1IGNhdGVnb3JpZXMsIHRoZXJlIGlzIG9uZSAodGhlIG1vc3QgZnJlcXVlbnQpIGNhdGVnb3J5IHRoYXQgaGFzIG5vIHZhbHVlIGZvciB0aGUgc2VjdG9yIG5hbWUNCnJlbmFtZWRfb3JhdHNfY29yZSAlPiUNCiAgZmlsdGVyKHByaWNlID49IDE1ICYgYXZnb3B0dm9sdTIwZCA+PSAyMDAwKSAlPiUNCiAgY291bnQoc2VjdG9yKSAlPiUNCiAgYXJyYW5nZShkZXNjKG4pKQ0KDQojIHVwb24gY2xvc2VyIGluc3BlY3RpbmcsIHNlZW1zIHRvIGJlIGV0ZnMvaW5kaWNpZXMgLSBnb2luZyB0byByZWxhYmVsIHRoaXMgY2F0ZWdvcnkgdG8gYmUgIk5vbmUiDQpyZW5hbWVkX29yYXRzX2NvcmUgJT4lDQogIGZpbHRlcihwcmljZSA+PSAxNSAmIGF2Z29wdHZvbHUyMGQgPj0gMjAwMCwgc2VjdG9yID09ICIiKQ0KDQojIHJlbGFiZWxpbmcgc2VjdG9yID0gIiIgdG8gIk5vbmUNCiMgZmlsdGVyaW5nIG91dCBhdG1pdm17eH0gPSAwDQojIHJvdW5kaW5nIG51bWVyaWMgdmFsdWVzIHRvIHRoZSAybmQgZGlnaXQgLSBzaW5jZSB0aGUgaW1wbGllZCB2b2wgbnVtYmVycyBzZWVtIHRvIGJlIHgxMDANCmNsZWFuX29yYXRzX2NvcmUgPC0gcmVuYW1lZF9vcmF0c19jb3JlICU+JQ0KICBmaWx0ZXIocHJpY2UgPj0gMTUgJiBhdmdvcHR2b2x1MjBkID49IDIwMDAsDQogICAgICAgICBpZl9hbnkoc3RhcnRzX3dpdGgoImF0bWl2bSIpLCBmdW5jdGlvbih4KSB4ID4gMCkpICU+JQ0KICBtdXRhdGUoc2VjdG9yID0gaWZlbHNlKHNlY3RvciA9PSAiIiwgIk5vbmUiLCBzZWN0b3IpLA0KICAgICAgICAgYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCBmdW5jdGlvbih4KSByb3VuZCh4LCAyKSkpICU+JQ0KICBhcnJhbmdlKHRpY2tlcikNCg0KaGVhZChjbGVhbl9vcmF0c19jb3JlLCAzKQ0KYGBgDQoNCiMjIyBvcmF0cyBjbGVhbiAtIGNsb3NlciBpbnNwZWN0aW9uIG9uIGRpZmZlcmVudCBkdGVzIHBlciBzYW1lIG1vbnRobHkgY29udHJhY3QNCi0gZHRlIHNlZW1zIHRvIHJhbmdlIGF0IGZhcnRoZXIgZGF0YSBjb250cmFjdHMgDQotIGRvbnQgbmVlZCB0byBkbyBhbnl0aGluZyBhYm91dCBpdCBub3csIGJ1dCBzaG91bGQgYmUgbm90ZWQgd2hlbiBjb21wYXJpbmcgYmV0d2VlbiB0aWNrZXJzDQoNCmBgYHtyfQ0KIyBub3RpY2VkIG9uZSBsYXN0IHRoaW5nLCB0aGUgZHRlIGZvciB0aGUgZnJvbnQgbW9udGhsaWVzIHNlZW1zIHRvIGRpZmZlcg0KIyBnb2luZyB0byB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgdGhpcywgc2luY2UgaSB3YW50IHRoZSBkdGUncyB0byBiZSB0aGUgc2FtZSBmb3IgeCBjb250cmFjdCBmb3IgcmVsYXRpdmUgdmFsdWUgdHJhZGluZw0KIyBzZWVtcyANCmNsZWFuX29yYXRzX2NvcmUgJT4lDQogIHNlbGVjdCh0aWNrZXIsIHN0YXJ0c193aXRoKCJkdGV4bSIpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGR0ZXhtMTpkdGV4bTQsIG5hbWVzX3RvID0gIm1vbnRobHlfY29udHJhY3QiLCB2YWx1ZXNfdG8gPSAiZHRlIikgJT4lDQogIGdyb3VwX2J5KG1vbnRobHlfY29udHJhY3QsIGR0ZSkgJT4lDQogIGNvdW50KCkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkNCg0KIyB2aXN1YWxpemF0aW9uDQpjbGVhbl9vcmF0c19jb3JlICU+JQ0KICBzZWxlY3QodGlja2VyLCBzdGFydHNfd2l0aCgiZHRleG0iKSkgJT4lDQogIHBpdm90X2xvbmdlcihkdGV4bTE6ZHRleG00LCBuYW1lc190byA9ICJtb250aGx5X2NvbnRyYWN0IiwgdmFsdWVzX3RvID0gImR0ZSIpICU+JQ0KICBnZ3Bsb3QoYWVzKG1vbnRobHlfY29udHJhY3QsIGR0ZSwgZmlsbCA9IG1vbnRobHlfY29udHJhY3QpKSArDQogIGdlb21fYm94cGxvdCgpICsNCiAgbGFicyh0aXRsZSA9ICJkdGUgYXQgZHRleG17eH0gbW9udGhseSBjb250cmFjdCIsDQogICAgICAgc3VidGl0bGUgPSAiaSB3b3VsZCB3YW50IHRoZSBib3hwbG90cyB0byBkaXNwbGF5IG9uZSB2YWx1ZSAoaWUuIGR0ZXhtMSAmIGR0ZW14MiA9IHN0cmFpZ2h0IGxpbmUpIiwNCiAgICAgICB4ID0gIm1vbnRobHkgY29udHJhY3QiLCB5ID0gImRheXMgdG8gZXhwaXJhdGlvbiIsDQogICAgICAgY2FwdGlvbiA9ICJzZWVtcyB0aGUgM3JkICYgNHRoIG1vbnRobHkgY29udHJhY3RzIGhhcyBhIHJhbmdlIG9mIGRpZmZlcmVudCBkdGVzIikNCmBgYA0KDQojIyMgb3JhdHMgY2xlYW4gLSBzYXZpbmcgZGF0YQ0KLSBzYW1lIGRhdGEgdGhhdCBnZXRzIGxvYWRlZCBpbnRvIHRoZSByZWxhdGl2ZSB2YWx1ZSBtYXJrZG93biBleGVyY2lzZSAob3B0aW9uX2RhdGEpDQoNCmBgYHtyfQ0Kc2F2ZVJEUyhjbGVhbl9vcmF0c19jb3JlLCAiLi4vZGF0YS9jbGVhbl9vcmF0c19jb3JlLnJkcyIpDQpgYGANCg0KIyMjIHByaWNlIGNsZWFuIC0gaGVscGVyIGZ1bmN0aW9uICh5YWhvbyBhcGkgcXVlcnkpDQotIHVzaW5nIHlhaG9vIGFwaSBmb3IgcHJpY2UgZGF0YSAobmVlZCBhZGp1c3RlZCBwcmljZXMpDQoNCmBgYHtyfQ0KIyB1c2VzIHlhaG9vIGFwaSAtIHBhcmFtZXRlcnMgYXJlIHRpY2tlciwgZmlyc3RfZGF0ZSwgYW5kIGxhc3RfZGF0ZQ0KIyBpJ3ZlIHNldCBpdCB1cCB0byBvbmx5IHJldHVybiB0aGUgZGF0ZSAmIGFkanVzdGVkIGNsb3NlIHByaWNlIHNpbmNlIHdlJ2xsIGJlIGRvaW5nIGNvcnJlbGF0aW9uIGFuYWx5c2lzDQoNCiMgeWFob28gYXBpIHJhdGUgc2VlbXMgdG8gYmUgMmsvaG91ciwgfjQway9kYXkuIHNpbmNlIHdlIGFyZSBvbmx5IHB1bGxpbmcgfjgwMCB0aWNrZXJzLCBub3QgZ29pbmcgdG8gc2V0IGFueSBzbGVlcCBjb25kaXRpb25zDQpwcmljZV9hcGkgPC0gZnVuY3Rpb24odGlja2VyLCBmaXJzdF9kYXRlLCBsYXN0X2RhdGUpew0KICANCiAgcmVxdWVzdCA8LSBHRVQoc3ByaW50ZigNCiAgICAiaHR0cHM6Ly9xdWVyeTEuZmluYW5jZS55YWhvby5jb20vdjcvZmluYW5jZS9kb3dubG9hZC8lcz9wZXJpb2QxPSVzJnBlcmlvZDI9JXMmaW50ZXJ2YWw9MWQmZXZlbnRzPWhpc3RvcnkmaW5jbHVkZUFkanVzdGVkQ2xvc2U9dHJ1ZSIsIA0KICAgIHRpY2tlciwgZmlyc3RfZGF0ZSwgbGFzdF9kYXRlKSkNCiAgDQogIGlmKHN0YXR1c19jb2RlKHJlcXVlc3QpID09IDIwMCl7DQogICAgY29udGVudHMgPC0gY29udGVudCgNCiAgICAgIHggPSByZXF1ZXN0LA0KICAgICAgYXMgPSAicGFyc2VkIiwNCiAgICAgIHR5cGUgPSAidGV4dC9jc3YiLA0KICAgICAgZW5jb2RpbmcgPSAiVVRGLTgiLA0KICAgICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSwNCiAgICAgIGNvbF9zZWxlY3QgPSBjKCJkYXRlIiA9ICJEYXRlIiwgImFkal9jbG9zZSIgPSAiQWRqIENsb3NlIikpDQogICAgDQogICAgaWYobnJvdyhjb250ZW50cykgPiAwKXsNCiAgICAgIGNvbnRlbnRzJHRpY2tlciA8LSB0aWNrZXINCiAgICAgIHJldHVybihjb250ZW50c1ssYygidGlja2VyIiwgImRhdGUiLCAiYWRqX2Nsb3NlIildKQ0KICAgICAgDQogICAgfSBlbHNlIHsNCiAgICAgIG1lc3NhZ2Uoc3ByaW50ZigiJXMgcmV0dXJuZWQgbm8gZGF0YS4iLCB0aWNrZXIpKQ0KICAgICAgcmV0dXJuKCkNCiAgICB9DQogIH0gZWxzZSB7DQogICAgbWVzc2FnZShzcHJpbnRmKCIlcyByZXR1cm5lZCBzdGF0dXMgY29kZTogJXMuIE5vIGRhdGEgcHVsbGVkLiIsIHRpY2tlciwgc3RhdHVzX2NvZGUocmVxdWVzdCkpKQ0KICAgIHJldHVybigpDQogIH0NCn0NCmBgYA0KDQojIyMgcHJpY2UgY2xlYW4gLSBxdWVyeSBkYXRhDQoNCmBgYHtyfQ0KIyBwdWxsaW5nIGxhc3QgZGF0ZSBmcm9tIG91ciBvcmF0cyBvcHRpb24gZGF0YSwgYWRkaW5nICsxIGJlY2F1c2UgeWFob28gYXBpIHB1bGxzIGJldHdlZW4gZGF0ZXMNCm9yYXRzX2RhdGUgPC0gYXMuRGF0ZShjbGVhbl9vcmF0c19jb3JlJGRhdGVbMV0pICsgMQ0KDQojIHNhdmluZyBwYXJhbWV0ZXIgdmFyaWFibGVzIGZvciBwcmljZSBhcGkgZnVuY3Rpb24NCnRpY2tlcnMgPC0gY2xlYW5fb3JhdHNfY29yZSR0aWNrZXINCmZpcnN0X2RhdGUgPC0gYXMubnVtZXJpYyhhcy5QT1NJWGN0KGFzLmNoYXJhY3RlcihvcmF0c19kYXRlIC0gMzY1KSkpDQpsYXN0X2RhdGUgPC0gYXMubnVtZXJpYyhhcy5QT1NJWGN0KG9yYXRzX2RhdGUpKQ0KDQojIGZvcmVhY2ggbG9vcCByYW4gd2l0aCBwYXJhbGxlbCBwcm9jZXNzaW5nIA0KcHJpY2VfZGF0YSA8LSBmb3JlYWNoKHZhciA9IDE6bGVuZ3RoKHRpY2tlcnMpLCAuY29tYmluZSA9ICJiaW5kX3Jvd3MiLCAucGFja2FnZXMgPSAiaHR0ciIsIC5lcnJvcmhhbmRsaW5nID0gInBhc3MiKSAlZG9wYXIlIHsNCiAgcHJpY2VfYXBpKA0KICAgIHRpY2tlciA9IHRpY2tlcnNbdmFyXSwNCiAgICBmaXJzdF9kYXRlID0gZmlyc3RfZGF0ZSwNCiAgICBsYXN0X2RhdGUgPSBsYXN0X2RhdGUpDQp9DQoNCmhlYWQocHJpY2VfZGF0YSwgMykNCmBgYA0KDQojIyMgcHJpY2UgY2xlYW4gLSBjaGVja2luZyBob3cgbXVjaCB0aWNrZXJzIHdlIHB1bGxlZA0KLSBlbmRlZCB1cCBtaXNzaW5nIHNvbWUgdGlja2VycywgZ2F0aGVyZWQgcmVtYWluaW5nICYgcmVxdWVyaWVkDQotIHRoZW4gY2xlYW5lZCBmaW5hbCBwcmljZSBkYXRhDQoNCmBgYHtyfQ0KIyBzZWVtcyB3ZSBvbmx5IHB1bGxlZCA0MTkvODQ0IG9mIG91ciB0aWNrZXJzDQpwcmljZV9kYXRhICU+JQ0KICBjb3VudCh0aWNrZXIpICU+JQ0KICBhcnJhbmdlKG4pDQoNCiMgbG9va2luZyBhdCBvdXIgbGVmdG92ZXIgdGlja2VycywgdGhlcmUgYXJlIHNvbWUgd2l0aCB1bmRlcnNjb3JlcyBpbiB0aGUgbmFtZSAtIHlhaG9vIGRvZXNuJ3QgcmVjb2duaXplIHRoYXQNCiMgZ29pbmcgdG8gcHJlLWZpbHRlciBvdXQgdGhvc2UgdGlja2VycyAofjM1IG9jY3VycmVuY2VzKQ0KIyB5YWhvbyBhbHNvIGRvZXNuJ3QgZ2l2ZSBoaXN0b3JpY2FsIGRhdGEgZm9yIGluZGljZXMgJiByZXF1aXJlIHRoZXJlIGJlIGEgcHJlZml4ICJeREpYIiB0byB0aGUgbmFtZSAtIHRoZXNlIHdvbid0IGJlIGNvbGxlY3RlZA0KbGVmdG92ZXJfdGlja2VycyA8LSBjbGVhbl9vcmF0c19jb3JlICU+JQ0KICBmaWx0ZXIoIXRpY2tlciAlaW4lIHVuaXF1ZShwcmljZV9kYXRhJHRpY2tlcikpICU+JQ0KICBmaWx0ZXIoIXN0cl9kZXRlY3QodGlja2VyLCAiXyIpKSAlPiUNCiAgcHVsbCh0aWNrZXIpDQoNCmxlZnRvdmVyX3RpY2tlcnMNCg0KIyBnb2luZyB0byBjb21iaW5lIG91ciBwcmljZSBkYXRhIHRhYmxlcyBhbmQgZG8gc29tZSBmaW5hbCBjbGVhbmluZw0KIyB0aGUgbnVtYmVycyBkb2Vzbid0IHNlZW0gYmFkIChubyAwJ3MsIGV0YykNCmNsZWFuX3ByaWNlX2RhdGEgPC0gcHJpY2VfZGF0YSAlPiUNCiAgYXJyYW5nZSh0aWNrZXIsIGRhdGUpICU+JQ0KICAjIHN1bW1hcnkoKQ0KICBtdXRhdGUoYWRqX2Nsb3NlID0gcm91bmQoYWRqX2Nsb3NlLCAyKSkNCg0KIyBzYW1wbGUNCmhlYWQoY2xlYW5fcHJpY2VfZGF0YSwgMykNCmBgYA0KDQojIyMgcHJpY2UgY2xlYW4gLSBzYXZlIGNsZWFuZWQgcHJpY2UgZGF0YQ0KLSBzYW1lIHRhYmxlIHVzZWQgaW4gcmVsYXRpdmUgdmFsdWUgZXhlcmNpc2UgKHByaWNlX2RhdGEpDQoNCmBgYHtyfQ0Kc2F2ZVJEUyhvYmplY3QgPSBjbGVhbl9wcmljZV9kYXRhLCBmaWxlID0gIi4uL2RhdGEvY2xlYW5fcHJpY2VfZGF0YS5yZHMiKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K