Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error in get_historicals_options #160

Open
industrial-dreaming opened this issue May 20, 2022 · 4 comments
Open

Error in get_historicals_options #160

industrial-dreaming opened this issue May 20, 2022 · 4 comments
Assignees
Labels
api change bug Something isn't working
Projects

Comments

@industrial-dreaming
Copy link

Getting the following error when I run the example code for get_historicals_options:

Error in parse_url(url) : length(url) == 1 is not TRUE

Perhaps related to the changes behind the HTTP 400 Bad Request issues?

Thank you for your time!

@industrial-dreaming
Copy link
Author

Did some of my own investigation and it looks like the starting point for the error takes place after running the following, with the resulting output:

rawToChar(dta$content)
[1] "{"detail":"Invalid Authorization header."}"

I'm new to this, but it looks like the structure of dta$content has changed?

@JestonBlu JestonBlu self-assigned this May 21, 2022
@JestonBlu JestonBlu added bug Something isn't working api change labels May 21, 2022
@JestonBlu JestonBlu added this to To Do in 1.x via automation May 21, 2022
@JestonBlu
Copy link
Owner

Thanks for letting me know, this is one i forgot to test. The api seems okay, but something did change. The error is from the api_instruments_options function returning more than one url. The function is setup to expect 1 url I think. Ill investigate more and work on a patch.

api_historicals_options <- function(RH, chain_symbol, type, strike_price, expiration_date,
                                    interval = NULL, span = NULL) {

  # Call to get the option instrument id
  dta <- api_instruments_options(RH, method = "symbol",
                                 chain_symbol = chain_symbol,
                                 type = type,
                                 strike_price = strike_price,
                                 expiration_date = expiration_date)
...

@Colleca
Copy link

Colleca commented Jun 6, 2023

I suggest a few changes:

  1. Force results to only active and tradeable contracts. Pulling price history for inactive/untradeable (aka "dead") contracts doesn't make much sense because the current price on a dead contract will always be the same.
  2. Expiration date filters dont seem to do anything within the api_instruments_options call. So, instead i suggest dropping it from that function and adding the filter step into the api_historicals_options call if its specified.
  3. I was able to address the multiple url issue by adding a for loop to the function, i also added try catch logic, not sure if absolutely necessary but considered that a robinhood url might possibly not result in data.
  4. When a user does not specify type, this pulls both puts and calls.
  5. When a user doesnt specify strike_price, this pulls all strike prices (except see number 7 below)
  6. When a user doesnt specify expiration date, this pulls all expiration dates.
  7. Added "Near the money" option which will look up the latest price for the stock and pull options with strikes that are within a set % of the last price (set this to 10% by default, but is adjustable).
  8. Added verbosity to allow user to see that the function is working when pulling more than 1 ticker. For example when I was pulling all apple option contracts I got 1,918 hits so it took a while for it to pull them all.

Hope this helps, below worked with the minimal testing I did against it.

library(httr)
library(RobinHood)
library(otp)
library(tictoc)
library(tidyverse)

RH <- RobinHood(your account stuff)

#For testing the function
chain_symbol<-"AAPL"
type<-"call"
strike_price <- 100
expiration_date_param<-"2023-06-09"
span = "week"
interval = "10minute"
near_the_money = TRUE
near_the_money_pct=0.1

#alternate set
type<-NULL
strike_price<-NULL
expiration_date_param<-NULL

api_historicals_options_v2<-function (RH, chain_symbol, type=NULL, strike_price=NULL,expiration_date_param = expiration_date_param,
near_the_money=near_the_money,near_the_money_pct=near_the_money_pct,
interval = NULL, span = NULL,verbose=verbose)
{

dta <- api_instruments_options(RH, method = "symbol", chain_symbol = chain_symbol,
type = type, strike_price = strike_price,
state="active",tradability="tradable")

if(!is.null(expiration_date_param)){
dta<-dta%>%
dplyr::filter(expiration_date==expiration_date_param)
}

#If don't want to set strike price but only want near the money options
if(isTRUE(near_the_money) && is.null(strike_price)){
latest_price<-try(get_quote(RH,chain_symbol)$last_trade_price)
if(inherits(latest_price,'try-error')){
print("Unable to retrieve latest price for ticker, check ticker.")
break
}

dta<-dta%>%
dplyr::filter(strike_price >= (1-near_the_money_pct)*latest_price)%>%
dplyr::filter(strike_price <= (1+near_the_money_pct)*latest_price)
}

option_id <- dta$id
url <- paste0(api_endpoints("historicals_options"), dta$id,
"/?interval=", interval, "&span=", span)

token <- paste("Bearer", RH$api_response.access_token)

loop_pull<-function(){
if(i > 1 && verbose==TRUE){
print(paste0("Pulling Option Data ",i," out of ",length(url)))
}
dta_loop <- GET(url[i], add_headers(Accept = "application/json",
Content-Type = "application/json", Authorization = token))
httr::stop_for_status(dta_loop)
dta_loop <- RobinHood::mod_json(dta_loop, "fromJSON")
dta_loop <- dta_loop$data_points
dta_loop <- dta_loop %>%
dplyr::mutate_at("begins_at", lubridate::ymd_hms) %>%
dplyr::mutate_at(c("open_price", "close_price", "high_price",
"low_price", "volume"), as.numeric)%>%
dplyr::mutate(id = option_id[i])%>%
tidyr::drop_na()%>%
dplyr::full_join(dta,by = join_by(id))

return(dta_loop)

}

full_data<-data.frame()
i=1
for(i in 1:length(url)){
dta_loop2<-try(loop_pull())
if(inherits(dta_loop2,'try-error')){
next
}
full_data<-dplyr::bind_rows(full_data,dta_loop2)
}
return(full_data)
}

get_historicals_options_v2<-function (RH, chain_symbol, type=NULL, strike_price=NULL, expiration_date_param = NULL,
interval = NULL, span = NULL,near_the_money=FALSE,near_the_money_pct=0.1,verbose=FALSE)
{
RobinHood::check_rh(RH)

historicals <- api_historicals_options_v2(RH, chain_symbol = chain_symbol,
type = type, strike_price = strike_price,
interval = interval, span = span,near_the_money = near_the_money,near_the_money_pct = near_the_money_pct,verbose=TRUE,expiration_date_param = expiration_date_param)

historicals$strike_price <- strike_price

historicals$expiration_date <- expiration_date

historicals$type <- type

historicals$chain_symbol <- chain_symbol

if(!is.null(expiration_date)){
historicals<-historicals%>%
dplyr::filter(expiration_date==lubridate::ymd(expiration_date))
}

historicals <- historicals %>% dplyr::select("chain_symbol",
"type", "expiration_date", "strike_price", "open_price",
"close_price", "low_price", "high_price", "volume", "begins_at",
"session", "interpolated")%>%
dplyr::relocate(begins_at)
return(historicals)
}

old_function<-get_historicals_options(RH,chain_symbol = "AAPL",type = "call",strike_price = 100,expiration_date = "2023-06-09",interval = "10minute",span = "day")

#For single option selection
tic()
new_function<-get_historicals_options_v2(RH,chain_symbol = "AAPL",type = "call",strike_price = 100,expiration_date_param = "2023-06-09",interval = "10minute",span = "day")
toc()

#For wildcard option selection using near the money option.
tic()
new_function<-get_historicals_options_v2(RH,chain_symbol = "AAPL",interval = "10minute",span = "day",near_the_money = TRUE,near_the_money_pct = 0.1)
toc()

@Colleca
Copy link

Colleca commented Jun 6, 2023

the markdown is making my comment look funny just copy and paste it into R studio and it looks fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api change bug Something isn't working
Projects
1.x
  
To Do
Development

No branches or pull requests

3 participants