yogat3ch / alpacaforr Goto Github PK
View Code? Open in Web Editor NEWConnecting to Alpaca API and using it with R
Connecting to Alpaca API and using it with R
Hi @jagg19,
As Alpaca continues to roll out updates for solely the V2 API, especially the replace functions that will be super useful for what I'm currently working on, I'm wondering if you would be up for migrating the package to the V2 API? I'm happy to do most of the updates, I'll likely need some help with the syntax to get httr
to do the PATCH
request correctly though. See the post on the Alpaca forums for details
Additionally, I was wondering what your thoughts are on getting the package CRAN ready?
Note: I've already started this on my local repo and wondering if its worth continuing the effort?
I have just try:
bars <- AlpacaforR::get_bars(
ticker = 'SPY',
from = '2020-08-26',
to = '2020-08-28',
timeframe = '15min'
)
and I got bars for the current month:
$SPY
time open high low close volume
1 2020-09-17 21:15:00 334.525 334.610 333.420 333.650 310005
2 2020-09-17 21:30:00 333.590 335.670 333.560 335.030 262097
3 2020-09-17 21:45:00 335.070 336.040 334.620 335.860 419328
4 2020-09-17 22:00:00 335.870 336.550 335.810 336.260 55857
5 2020-09-17 22:15:00 336.179 336.179 336.179 336.179 118
6 2020-09-17 22:30:00 336.200 336.330 336.200 336.330 3476
7 2020-09-17 22:45:00 336.200 336.200 335.880 335.900 11158
8 2020-09-17 23:00:00 335.800 335.800 335.800 335.800 1000
It's been a while since I've used the API and spotted the version parameter.
What does version="v2"
stand for in the package functions?
Hi there.
I'm just getting starting with Alpaca and buying stocks. I like R and want to thank you for the effort that you've put into this AlpacaforR!
In addition to buying stocks to hold via AlpacaforR, I'm also interested in using it to short stocks.
Do you have the syntax for shorting a stock and then covering(closing) that position?
Kind regards
Hey there,
UPDATE 2019-07-14
I was getting errors in using get_bars related to the as.POSIXct conversion and I believe it was due to the fact that the sapply pulls out a list of dates when more than one ticker is passed and the as.POSIXct is only designed to vectorize over vectors rather than lists.
Trying to run through the code manually I found that the else statements aren't being interpreted due to the newlines preceding them.
I went ahead and rewrote some of the sections with these ifelse statements such that it executes properly when run from the source editor.
I also reworked the output from the GET call using dplyr to return POSIXct for the time column (and remove rather than cbind the superfluous integer date column), and to rename the variables to quantmod standard names as I know many in the R finance coder community use this package and it's functions require columns to be named as "open", "high", "low" ,"close" etc.
The code for the new function is as follows:
function (ticker, from = Sys.Date() - 6, to = Sys.Date(), timeframe = "1D",
limit = NULL)
{
url = "https://data.alpaca.markets"
headers = get_headers()
if (!is.null(limit)) {
if (limit > 1000) {
stop("Max limit is 1000!")
}
}
ticker = ifelse(length(ticker) > 1, paste0(ticker, collapse = ","),
ticker)
week_dates = get_calendar(from, to)$date
if (length(week_dates) > 1000) {
start <- length(week_dates) - 999
week_dates <- week_dates[start:length(week_dates)]
}
if ((timeframe == "1D" | timeframe == "day") & is.null(limit)) {
limit = length(week_dates)
} else if (timeframe == "15Min" & is.null(limit)) {
limit = 250
} else if (timeframe == "5Min" & is.null(limit)) {
limit = 500
} else if ((timeframe == "1Min" | timeframe == "minute") & is.null(limit)) {
limit = 1000
}
if (!(timeframe == "1D" | timeframe == "day")) {
from = paste0(from, stringr::str_extract(format(Sys.time(),
"%Y-%m-%dT%H:%M:%OS%z"), "T.*"))
to = paste0(to, stringr::str_extract(format(Sys.time(),
"%Y-%m-%dT%H:%M:%OS%z"), "T.*"))
} else {
from = paste0(from, "T20:30:00-04:00")
to = paste0(to, "T20:30:00-04:00")
}
bars = httr::GET(url = paste0(url, "/v1/bars/", timeframe,
"?symbols=", ticker, "&limit=", limit, "&start=", from,
"&end=", to), headers)
bars = response_text_clean(bars)
bars = lapply(bars, function(l){
nms <- c(time = "t", open = "o", high = "h", low = "l", close = "c", volume = "v")
out <- dplyr::mutate_at(l, dplyr::vars("t"), dplyr::funs(as.POSIXct, .args = list(origin = "1970-01-01"))) %>% dplyr::mutate_at(dplyr::vars(o,h,c,l,v),dplyr::funs(as.numeric)) %>%
dplyr::rename((!!nms))
})
return(bars)
}
On a related note, a suggestion for future development is to create a vector of GET requests executed in serial to accommodate from
values that are older than 1000 bars will cover.
My previous suggestion regarding the documentation has been updated to account for the changes to the column names as per the suggestion above.
Suggestions for the @return section of get_bars to indicate that the type of object returned is a list, v is in millions, and the addition of column d that is a POSIXct. I tested the knitting of this with Roxygen, it formats nicely with the first line as the return value and then an indented list for the data.frame columns.
#' @return \code{list} object for each ticker symbol containing a \code{data.frame} with the following columns:
#' \itemize{
#' \item{\code{time}}{ the time of the bar as \code{POSIXct} in yyyy-mm-dd for timeframe = day, and yyyy-mm-dd hh:mm:ss for timeframes < day}
#' \item{\code{open}}{ open price as a numeric object.}
#' \item{\code{high}}{ high price as a numeric object.}
#' \item{\code{low}}{ low price as a numeric object.}
#' \item{\code{close}}{ close price as a numeric object.}
#' \item{\code{volume}}{ volume (in millions) as a numeric object.}
#' }
An aside, I'm finding that running the example on get_bars:
get_bars(ticker = c("INTC","MSFT"))
leads to the following warning messages:
Warning messages:
1: In data.frame(..., check.names = FALSE) :
row names were found from a short variable and have been discarded
2: In data.frame(..., check.names = FALSE) :
row names were found from a short variable and have been discarded
I can view the list object, and then when I click to view an individual data.frame object within that list, the R session aborts entirely.
I'll see if I can troubleshoot the cause, otherwise I just wanted to let you know about it in case you want to try to replicate it or have an intuition as to what might be causing it.
Thanks!
Hi @jagg19,
I just finished up writing you an email, and hope to get a response but in case the email attempt is unsuccessful I'm also tagging you here to hopefully get your attention. There are additional details in the email, but here's a basic synopsis of the situation.
I've just pushed some of the final changes to the greatly expanded AlpacaforR
over at yogat3ch/AlpacaforR. I'd appreciate your input, if possible, on the CRAN release that I hope to submit by Friday once I add the recently released Alpaca Websockets. Please check your gmail if you get the chance!
The _order(s)
type requests return a data.frame with all character formatted objects, which makes the values therein not very conducive to manipulation R. I've started to use the same chunk of code in manipulating the output from all of the _order type functions over and over again so I thought I'd post it here if you want to just add it in to the end of each function to change the output data types to be readily manipulable in R.
AlpacatoR_order_mutate <- function(df) {
toNum <- function(x){
as.numeric(stringr::str_replace_all(x, "\\$|\\,", ""))
}
df <- dplyr::mutate_at(df, dplyr::vars(dplyr::ends_with("at")),dplyr::funs(lubridate::ymd_hms(., tz = Sys.timezone())))
out <- dplyr::mutate_at(df, dplyr::vars(qty, filled_qty, filled_avg_price, limit_price, stop_price), dplyr::funs(toNum))
return(out)
}
Depends:
Additionally, here's one for get_positions that changes all of those character numbers to actual numerics:
AlpacatoR_position_mutate <- function(df) {
out <- dplyr::mutate_at(df, tidyselect::vars_select(names(df), "qty", "market_value", "cost_basis", "change_today", dplyr::starts_with("unr"), dplyr::ends_with("price")), dplyr::funs(as.numeric))
return(out)
}
Depends are tidyselect - but this is a suggest of dplyr so it should already be installed if dplyr is already in the AlpacaforR depends.
Hi @jagg19,
I'm stoked to have just found this package! I've been in the same boat for about a year of debating whether I need to make the switch to Python for this quantitative analysis project in R in order to take it live though I've put so much work into it already! Thank you so much for creating this package! I'm happy to contribute what I can to develop the package and documentation as I use it!
Thus far I have a small suggestion for cancel_order:
I was just using cancel_order as follows:
cancel_order(orders$id[1])
Order ID for 7976525f-cac3-415e-9269-1273fd25a73d was successfully canceled.
However, when I checked the order with get_orders, and via the browser, the order was not actually canceled, I believe because the ticker was not specified, or because the order id submitted was interpreted as the ticker.
I also noticed that if two orders are placed for the same symbol, cancel_order will error:
submit_order(ticker = "AMZN", qty = as.character(1), side = "buy", type = "stop_limit", time_in_force = "day", limit_price = as.character(1896), stop_price = as.character(1896))
submit_order(ticker = "AMZN", qty = as.character(1), side = "buy", type = "stop_limit", time_in_force = "gtc", limit_price = as.character(1896), stop_price = as.character(1896))
cancel_order("AMZN")
# Error in is.url(url) : length(url) == 1 is not TRUE
Looking at the code it appears that because the first argument should be the ticker though what was supplied was the id and the function used this as the ticker and accidentally confirmed that the order was canceled though it actually wasn't.
This function could probably be simplified to accept as it's first argument ticker_id
as the ticker symbol or id with documentation as such:
#' @param ticker_id The ticker symbol or the order id.
and to the function could be added the following:
if (nchar(ticker_id) > 15) {
order_id <- ticker_id
ticker <- open_orders$symbol[open_orders$id == order_id]
} else {
ticker <- ticker_id
open_orders_sym <- grep(ticker, open_orders$symbol, ignore.case = T)
# If more than one order is open
if (length(open_orders_sym > 1)) message(paste0("More than one order open for ",ticker,", most recent order placed at ", lubridate::with_tz(as.POSIXlt(open_orders$submitted_at[open_orders_sym[1]], tz = "UTC", tryFormats = c("%Y-%m-%dT%H:%M:%OS")), Sys.timezone())," will be canceled"))
order_id <- open_orders[open_orders_sym[1], "id", drop = T]
}
lubridate will need to be added to the depends for the timezone coercion in the message, or a simplified but less informative version is possible:
if (nchar(ticker_id) > 15) {
order_id <- ticker_id
ticker <- open_orders$symbol[open_orders$id == order_id]
} else {
ticker <- ticker_id
open_orders_sym <- grep(ticker, open_orders$symbol, ignore.case = T)
# If more than one order is open
if (length(open_orders_sym > 1)) message(paste0("More than one order open for ",ticker,", most recent order will be canceled"))
order_id <- open_orders[open_orders_sym[1], "id", drop = T]
}
This will allow for magrittr piping %>%
of either ticker symbols or order ids into the function, and makes it such that the ticker symbol is no longer required to be upper case.
Happy to make these changes if you're busy.
Thank you again!
I'm trying to fire a stoplimit orders with the example code below:
submit_order(ticker = "ABT", qty = 1,side = "sell", type="stoplimit",limit_price = 86, stop_price = 87 )
but I get an error:
$code
[1] 40010001
$message
[1] "invalid order type"
From the documentation of submit_order
that should be available.
Is it because I'm submitting the order outside market opening hours?
Thank you! I was looking for a R solution! Do you know whether the API also works for the Broker API? I am trying to input my broker key and secret key but it does not seem to work :-(
Do you know whether the order_submit function handles notional orders?
#> [1] TRUE
if (.open) {
# if the market is open then place a market buy order for "BYND"
(bo <- order_submit("bynd", side = "b", q = 2, type = "m"))
}
In the order_submit
doc I only see qty(integer) Ordered quantity
Although the Alpaca documentation mentions it is possible to pass notional orders
notional
| string | Required
| dollar amount to trade - Cannot work with qty. Can only work for market order types and day for time in force.
Hey again!
In an attempt to get recent prices I passed the following to get_bars:
price <- AlpacaforR::get_bars(s, from = {lubridate::now() - lubridate::minutes(5)}, to = lubridate::now(), timeframe = "minute")
and received the following error:
Error: parse error: trailing garbage
400 Bad Request
(right here) ------^
I think this was due to the format of the from & to arguments, I suspected this because using strftime I was able to return a response that was more than I was looking for. It worked returning the previous 1000 minutes rather than 5, but I imagine that must be an API default?
Since we're now using lubridate in the package, it's possible to account for these exceptions that occur when POSIXct classes are provided for the from & to args with the following addition to the if statement regarding timeframes other than "day":
if(!(timeframe == "1D" | timeframe == "day") & (lubridate::is.POSIXct(from) | lubridate::is.POSIXct(to))){
from <- strftime(from, "%Y-%m-%dT%H:%M:%S%z", tz = Sys.timezone())
to <- strftime(to, "%Y-%m-%dT%H:%M:%S%z", tz = Sys.timezone())
} else if(!(timeframe == "1D" | timeframe == "day")){
from = paste0(from,stringr::str_extract(format(Sys.time(), "%Y-%m-%dT%H:%M:%OS%z"), "T.*"))
to = paste0(to,stringr::str_extract(format(Sys.time(), "%Y-%m-%dT%H:%M:%OS%z"), "T.*"))
} else {
from = paste0(from,"T20:30:00-04:00")
to = paste0(to,"T20:30:00-04:00")
}
I think this will account for that exception when using POSIXct that aren't ISO formatted as the API specifies.
In service,
Stephen
I'm querying the list of open orders in my paper account with the below code:
OpenOrders<-get_orders(status = "open",live=FALSE)
The code runs but it returns always 50 rows of orders, although I actually have 100+open orders
Hi firstly thanks very much for the great work!
I am running R 4.1.2 first thing was that I can't install it just by install.packages
I understand that the package may not be available just yet on 4.1.2; I installed the package with devtools::install_github()
.
I was trying to set up my credentials via firstrun
:
firstrun(paper_api=c(key=my_key, secret=my_secret), live_api=c(key=my_key, secret=my_secret), pro=TRUE, live=FALSE)
it didn't work and from the stack trace I can see that it got stuck after map2
. There was not error message whatsoever it simply jumped to a break point, halted.
setting these params in .Renviron
didn't work either. I got a request is not authorized
when I was trying to request market data.
@jagg19 I try to learn and test the OCO sell order type. How may I specify it within the submit_order()
function?
Perhaps you could help me with an example for the following desired order parameters:
Quick update,
It turns out that the V1 was retired last week and now the functions changed and there is a r environment key needed from polygon which can be obtained by creating an account.
id<-'your id'
secret_key<-'your secret'
POLYGON_K<- 'your polygon key'
Sys.setenv('APCA-LIVE-API-KEY-ID' = id)
Sys.setenv('APCA-LIVE-API-SECRET-KEY' = secret_key)
Sys.setenv('POLYGON-KEY' = POLYGON_K)
https://github.com/yogat3ch/AlpacaforR#arguments-to-market_data-for-the-v2-api
Hello,
Recently after trying to reinstall AlpacaforR after uninstalling and reinstalling R I get the notification below.
I tried uninstalling and reinstalling because of a recent inability to get stock prices from code which had previously been working for over a year. Any idea what could be causing this or how to fix? My issues seem to be getting worse. First it was only the get_bars() function not returning stock prices and now the functions arent being found.
####First error after returning from vacation from code that broke while running over vacation.
get_bars(ticker = "FB", time="minute", limit=5)
Error in UseMethod("tbl_vars") :
no applicable method for 'tbl_vars' applied to an object of class "character"
####Second error after uninstalling and reinstalling
library(AlpacaforR)
Attaching package: ‘AlpacaforR’
The following object is masked from ‘package:graphics’:
polygon
Thank You,
Brian
Hello, I've just opened an Alpaca account and trying to setup a paper trading connection in R with the below code:
Sys.setenv('APCA-API-KEY-ID' = 'PKCW8xxxxxxxxxI4U2SQJ')
Sys.setenv('APCA-API-SECRET-KEY' = 'CW1ZxxxxxxxxxxxxxxxxxxxxxK1QucMKI')
get_account()
However, I'm getting the following error in R console
$code
[1] 40110000
$message
[1] "access key verification failed : access key not found (Code = 40110000)
Hello,
I am trying to pull the latest price of a symbol. I just merged jagg19 with yogat3ch. To pull the latest price, i was using:
last_price <- get_poly_last_price("AMZN")$last$price
Now the documentation says that code is deprecated and throws a warning, though it still works. I noticed the following code in the order_submit documentation is meant to replace that:
(.lq <- polygon("lq", symbol = "AMZN"))
When running it, I get the error:
Error in list2env(.x, parent = .envir) : first argument must be a named list
Any help in resolving this error is much appreciated.
Expected behavior
Expected to successfully order a trail percent with the code for example:
bo <- AlpacaforR::order_submit(watchList[i,1], side = "buy", q = 3, type = "market",time_in_force = "ioc") ## This works great!
ts_0 <- AlpacaforR::order_submit(bo,trail_percent = 5) #This is where the error is thrown
Actual Behavior
When I try to order a trail percent shown above, I get the error:
Error in rlang::call_args()
:
! call
must be a defused call, not NULL.
The error is thrown inside the function AlpacaforR::order_submit(), specifically:
ca <- rlang::call_args(tail(rlang::trace_back(bottom = 1)$calls,
1)[[1]])
#Example
library(AlpacaforR)
Sys.setenv('APCA-PAPER-KEY' = "")
Sys.setenv('APCA-PAPER-SECRET' = "")
Sys.setenv("APCA-LIVE" = FALSE)
positions<-data.frame(try(AlpacaforR::positions()))
orders<-AlpacaforR::orders(status="closed")
bo19<-which(orders$order_type=="market" & orders$side == "buy")
boughtorders<-orders[bo19,]
bo20<-which(positions$symbol %in% boughtorders$symbol)
currentpositions<-boughtorders[bo20,]
symbolID<-currentpositions$symbol[1]
symbolID
[1] "APYX"
client_order_id<-currentpositions$client_order_id[1]
client_order_id
[1] "6fc87c20-251a-47a9-92f2-b59859abc39e"
ts_0<-AlpacaforR::order_submit(symbolID, client_order_id = client_order_id,trail_percent = 5)
Error in rlang::call_args()
:
! call
must be a defused call, not NULL.
Run rlang::last_error()
to see where the error occurred.
sessionInfo()
Session info ───────────────────────────────────────────────────────────────────────────────────────────────────────────────
setting value
version R version 4.2.2
os macOS
system x86_64
ui RStudio
language (EN)
rstudio 2022.12.0+353 Elsbeth Geranium
pandoc NA
Matrix products: default
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] AlpacaforR_1.0.1
THANK YOU X1,000,000! This package changed my life. What are the odds we could get an update for cypto integration?
Again,
THANK YOU THANK YOU THANK YOU!!!
I am having an issue connecting to a live account and am getting error code 40110000 "access key verification failure : verification failure" when calling get account. The paper account works great and I have ensured a new R session was started and keys were entered correctly and from the live account. I wrote to alpaca since this is a newer account a couple weeks old to see if they needed to do something on their end to allow access but wasnt sure what else I may be doing wrong or what I could try. Any help is appreciated.
library(devtools)
library(AlpacaforR)
id<-'my live id'
secret_key<-'my live secret key'
Sys.setenv('APCA-LIVE-API-KEY-ID' = id)
Sys.setenv('APCA-LIVE-SECRET-KEY' = secret_key)
get_account(live = TRUE)
as.matrix(get_account(live=TRUE))
Thanks,
Brian
First, thanks for the great package. I think this is the first package of this kind in R. The main reason I moved to python, from R, are great backtest / trading tools.
I saw on Alpaca site some examples with streaming: https://github.com/alpacahq/alpaca-trade-api-python/tree/master/examples
Is it even possible to set up the stream with this package, even theoretically?
I am trying to market buy and take profit at (ie place a limit sell above the purchased price) using jagg19/AlpacaforR. What is the easiest way to code this?
So far, I understand that the two solutions are to either:
-use yogat3ch/AlpacaforR (which may support OCO/bracket orders, though after glancing at the code, I only saw stop_limit orders, not take_profit), or
-refactor the submit_orders function
Is there any easier way currently?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.