The R package RobinHood allows users to connect to their accounts and do various things. All of the functions within the package rely on one function called RobinHood which takes in your username and password and returns a oauth2 token from Robinhood.
# Establishes a connection with your account and generates an oauth2 token
# Returns a list style object of relevant API keys and IDs needed to interact with your account
RH = RobinHood(username = "username", password = "password")
This package allows you to do a lot of cool things like, fetch your account information, view historical prices, and... place orders. The RobinHood function stores your oauth2 token information within whatever you assign the RobinHood function to.
RobinHood <- function(username, password) {
# Login to RobinHood, returns RobinHood object with access tokens
RH <- api_login(username, password)
# Get account data for the main purpose of returning the position url
accounts <- api_accounts(RH)
url_positions <- accounts$positions
url_account_id <- accounts$url
# Return object
RH <- c(RH, url = list(positions = url_positions,
account_id = url_account_id))
# Check to see if connection was successful
if (is.null(RH$tokens.access_token)) {
cat("Login not successful, check username and password.")
}
class(RH) <- "RobinHood"
return(RH)
}
For examples the function to place an order helps the package user construct the url for the investment instrument they want to invest in.
place_order <- function(RH, symbol, type, time_in_force, trigger, price, stop_price = NA, quantity, side) {
# Check if RH is valid
check_rh(RH)
# Set up error checks
if (!type %in% c("market", "limit")) stop("type must be 'market' or 'limit'")
if (!time_in_force %in% c("gfd", "gtc", "ioc", "opg")) stop(" time_in_fore must be one of 'gfd', 'gtc', 'ioc', 'opg'")
if (!trigger %in% c("immediate", "stop")) stop("trigger must be 'immediate' or 'stop'")
if (trigger == "stop" & is.na(stop_price) == TRUE) stop("stop price cant be null if trigger == 'stop'")
if (quantity < 1) stop("quantity must be > 0")
if (!side %in% c("buy", "sell")) stop("side must be 'buy' or 'sell'")
# Convert NAs to NULL and numeric to character
if (is.na(stop_price) == TRUE) stop_price <- ""
quantity <- as.character(quantity)
price <- as.character(price)
# Given a symbol, return the instrument_id
instrument_url <- paste(api_endpoints(endpoint = "quotes"), symbol, sep = "")
instrument <- api_quote(RH, instrument_url)
instrument_id <- instrument$instrument
# Place an order
orders <- api_orders(RH = RH,
action = "order",
instrument_id = instrument_id,
symbol = symbol,
type = type,
time_in_force = time_in_force,
trigger = trigger,
price = price,
stop_price = stop_price,
quantity = quantity,
side = side)
return(orders)
}
The place order function is then sent to the api orders function which constructs whatever request you are sending to robinhood and sends it via POST or GET. Your oauth2 token will be placed in the authorization namespace of the headers.
api_orders <- function(RH, action, order_url = NULL, instrument_id = NULL, symbol = NULL, type = NULL,
time_in_force = NULL, trigger = NULL, price = NULL, stop_price = NULL, quantity = NULL,
side = NULL) {
if (action == "order") {
url <- api_endpoints("orders")
token <- paste("Bearer", RH$tokens.access_token)
detail <- data.frame(account = RH$url.account_id,
instrument = instrument_id,
symbol = symbol,
type = type,
time_in_force = time_in_force,
trigger = trigger,
price = price,
stop_price = stop_price,
quantity = quantity,
side = side,
client_id = RH$api_client_id)
# If trigger = "stop" then stop_price must be included, otherwise it must be excluded
if (trigger == "immediate") {
detail <- detail[, c("account", "instrument", "symbol", "type", "time_in_force",
"trigger", "price", "quantity", "side", "client_id")]
}
dta <- POST(url = url,
add_headers("Accept" = "application/json",
"Content-Type" = "application/json",
"Authorization" = token),
body = mod_json(detail, type = "toJSON"))
dta <- mod_json(dta, "fromJSON")
dta <- as.list(dta)
dta$updated_at <- lubridate::ymd_hms(dta$updated_at)
dta$last_transaction_at <- lubridate::ymd_hms(dta$last_transaction_at)
dta$created_at <- lubridate::ymd_hms(dta$created_at)
dta$fees <- as.numeric(dta$fees)
dta$cumulative_quantity <- as.numeric(dta$cumulative_quantity)
dta$stop_price <- as.numeric(dta$stop_price)
dta$reject_reason <- as.numeric(dta$reject_reason)
dta$price <- as.numeric(dta$price)
dta$average_price <- as.numeric(dta$average_price)
dta$quantity <- as.numeric(dta$quantity)
return(dta)
}
if (action == "status") {
# URL and token
url <- order_url
token <- paste("Bearer", RH$tokens.access_token)
# GET call
dta <- GET(url,
add_headers("Accept" = "application/json",
"Content-Type" = "application/json",
"Authorization" = token))
# format return
dta <- mod_json(dta, "fromJSON")
dta <- as.list(dta)
return(dta)
}
if (action == "cancel") {
# URL and token
url <- order_url
token <- paste("Bearer", RH$tokens.access_token)
# GET call
dta <- POST(url,
add_headers("Accept" = "application/json",
"Content-Type" = "application/json",
"Authorization" = token))
# format return
dta <- mod_json(dta, "fromJSON")
return(dta)
}
if (action == "history") {
url <- api_endpoints("orders")
token <- paste("Bearer", RH$tokens.access_token)
# GET call
dta <- GET(url,
add_headers("Accept" = "application/json",
"Content-Type" = "application/json",
"Authorization" = token))
# format return
dta <- mod_json(dta, "fromJSON")
dta <- as.data.frame(dta$results)
return(dta)
}
}
1) At no point is this token encrypted, is it dangerous to send this information unencrypted locally from my device?
2) Using a professional R Studio product such as Shiny Server, R Studio Connect, or Shinyapps.io. Would allowing users to sign into their Robinhood accounts via an R Shiny App be a potential vulnerability to user tokens on one of these hosting platforms?