-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathWatchlist.R
186 lines (162 loc) · 9.27 KB
/
Watchlist.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# watchlist ----
# Wed Apr 22 20:37:02 2020
#' @family Watchlist
#' @title Access all Watchlist Endpoints
#'
#' @description Access all [Watchlist](https://alpaca.markets/docs/api-documentation/api-v2/watchlist) endpoints. See link for details. \lifecycle{stable}
#' @param watchlist_id `(character)` **Watchlist Name or ID**. If `NULL` **Default**, the array of all saved `Watchlist` will be returned. All other actions to be performed on the provided `Watchlist` name or ID must be explicitly specified via `action`. See below for description of the behavior of this argument based on the value supplied to `action`.
#' @param symbols `(character)` Vector of symbols. Must be specified with a `watchlist_id`. If the `action` is:
#' \itemize{
#' \item{\code{"get" **Default**}}{ `action` assumed to be add and `symbols` will be added.}
#' \item{\code{"create"}}{ A `Watchlist` called `watchlist_id` will be created with `symbols`. If symbols are missing the new `Watchlist` will be empty.}
#' \item{\code{"add"}}{ The `symbols` will be added to the specified `watchlist_id`. If this argument is specified with `watchlist_id` and `new_name` the `Watchlist` will be renamed to `new_name` and the `symbols` will be added. Set `action = "update"` explicitly to replace existing symbols with `symbols` when renaming.}
#' \item{\code{"update"}}{ The `symbols` will replace those in `watchlist_id`.}
#' \item{\code{"delete"}}{ If this argument is specified with `watchlist_id`, the `symbols` will be deleted from the `Watchlist`. If `symbols` is missing the `Watchlist` will be deleted.}
#' }
#' @param action `(character)` The action to take. See other parameters for detailed descriptions of how this argument determines the action when combinations of parameters are specified. This argument can be abbreviated with a single non-case-sensitive letter. One of:
#' \itemize{
#' \item{\code{"get"/"g"}}{ **Default** If no `watchlist_id` is specified, an array of all `Watchlist` will be returned, otherwise a the watchlist specified by `watchlist_id` will be returned. If `symbols` are specified, the symbols will be added to the `watchlist_id`.}
#' \item{\code{"create"/"c"}}{ A `Watchlist` will be created named according to `watchlist_id` with any `symbols` provided.}
#' \item{\code{"add"/"a"}}{ the `watchlist_id` will have `symbols` added. If `new_name` is also specified, `symbols` will be added (unless `action = "update"`)}
#' \item{\code{"update"/"u"}}{ `watchlist_id` is *required* and will have it's symbols replaced by those provided to `symbols`. If `new_name` and `symbols` are provided the `Watchlist` will be renamed and the `symbols` will *replace* the existing ones. If the `symbols` are meant to be added, do not set action, or set as `"add"`.}
#' \item{\code{"delete"/"d"}}{ If `action = "delete"` with `watchlist_id`, the `symbols` will be deleted from the `Watchlist`. If `symbols` is missing the `Watchlist` with id/name `watchlist_id` will be deleted.}
#' }
#' @param new_name `(character)` arbitrary name string, up to 64 characters. If present, then `action = "update"` is assumed and the `Watchlist` specified by `watchlist_id` will be renamed to `new_name` preserving existing symbols, unless `symbols` is present, in which case they will replace the existing.
#' @inheritParams account
#' @return `Watchlist` `(tibble)` If `watchlist()` is called with no arguments, an `tibble` of [Watchlist](https://alpaca.markets/docs/api-documentation/api-v2/watchlist/#watchlist-entity) Object. For all others `action`s, a `tibble` of [Asset](https://alpaca.markets/docs/api-documentation/api-v2/assets/#asset-entity) Objects with the `Watchlist` stored as an attribute retrieved with `attr(Asset, "info")` and query metadata stored as `attr(Asset, "query")`. A `Watchlist` object has the following information:
#' \itemize{
#' \item{\code{name}}{`(character)` The name of the watchlist.}
#' \item{\code{updated_at}}{`(POSIXct)` The timestamp of the last update.}
#' \item{\code{id}}{`(character)` Watchlist ID}
#' \item{\code{account_id}}{`(character)` The account ID associated with the watchlist}
#' \item{\code{created_at}}{`(POSIXct)` The timestamp of the watchlist creation.}
#' }
#' @inherit assets return
#' @examples
#' \dontrun{
#' # get the existing watchlists
#' watchlist()
#' # create a watchlist named test with Microsoft
#' (wl <- watchlist("test", symbols = "AAPL", action = "c"))
#' # See it in the list of watchlists
#' watchlist()
#' # Get it
#' (test <- watchlist("test"))
#' all.equal(test,wl, check.attributes = FALSE)
#' # Get that Watchlist's info
#' attr(wl, "info")
#' # Add "FB", "AMZN", "NFLX", "GOOG" and update the watchlist name to "FAANG"
#' (wl <- watchlist("test", new_name = "FAANG", symbols = c("FB", "AMZN", "NFLX", "GOOG")))
#' # Add individual stocks by specifying symbols (action assumed to be add when symbols are present)
#' (wl <- watchlist("FAANG", symbol = "GOOGL"))
#' # Delete individual items (or multiple)
#' (wl <- watchlist("FAANG", action = "d", symbols = "GOOGL"))
#' # Rename it appropriately and replace the symbols to match the name by specifying action = "update" explicitly.
#' (wl <- watchlist("FAANG", new_name = "FANG", symbols = c("FB", "AAPL", "NFLX", "GOOG"), action = "u"))
#' # Delete it using a partial argument (may cause a warning)
#' watchlist("FANG", a = "d")
#' }
#' @export
watchlist <-
function(watchlist_id = NULL,
symbols = NULL,
action = "get",
new_name,
live = get_live()) {
action <- tolower(substr(action,0,1))
# Set URL & Headers
.url <- purrr::when(
watchlist_id,
is_id(.) ~ list(path = c("watchlists", watchlist_id)),
is.character(.) && action != "c" ~ list(path = "watchlists:by_name", query = list(name = watchlist_id)),
is.null(.) || action == "c" ~ list(path = "watchlists")
) %>%
{
rlang::exec(get_url,!!!., live = live)
}
headers = get_headers(live)
# logical indicating if new_name is present
.nn <- !missing(new_name)
## if new_name is present, and action = "add"
if (.nn && action != "u")
symbols <- c(watchlist(watchlist_id)$symbol, symbols)
# convenience action assumption
action <- purrr::when(action,
# If symbols are present, new_name is absent & action = get (default) assume action = "add"
. == "g" && !.nn && !is.null(symbols) ~ "a",
# If new_name is present, assume action = "update"
.nn ~ "u",
~ action)
# Select the appropriate action
fn <- switch(action,
g = httr::GET,
a = ,
c = httr::POST,
u = httr::PUT,
d = httr::DELETE)
# If add
# Make body
bodyl <- purrr::compact(list2(
name = purrr::when(action,
. == "c" ~ watchlist_id,
.nn ~ new_name),
symbols = if (length(symbols) == 1) list(symbols) else symbols
))
bodyl <- if (!rlang::is_empty(bodyl)) jsonlite::toJSON(bodyl, auto_unbox = TRUE, pretty = TRUE) else NULL
# iterative actions
if(action %in% c("a", "d") && !rlang::is_empty(symbols)) {
out <- purrr::map(symbols, ~{
if (action == "d") .args <- list(url = httr::build_url(purrr::list_merge(httr::parse_url(.url), path = .x)))
else # if action = add symbol goes in body
.args <- list(url = .url, body = jsonlite::toJSON(list(symbol = .x), auto_unbox = TRUE, pretty = TRUE))
resp <- rlang::exec(fn, !!!.args, headers)
wl_transform(resp, action = action)
}) %>% {.[[length(.)]]}
} else {
# single operations
out <- rlang::exec(fn, url = .url, headers, body = bodyl)
out <- wl_transform(out, action = action)
}
return(out)
}
# wl_transform ----
# Sun May 03 08:55:01 2020
#' @title Transform watchlist objects
#'
#' @description Takes the watchlist endpoints server response and transforms to R amenable output
#' @param resp The response
#' @return \code{(tibble)} with respective R compliant objects (POSIXct)
#' @keywords internal
wl_transform <- function(resp, action) {
stopifnot(inherits(resp, "response"))
.wl <- response_text_clean(resp)
.q <- get_query(.wl)
# Handle info
if (!rlang::is_empty(.wl)) {
.info <- purrr::map_at(.wl[1:5], c("created_at", "updated_at"), try_date, timeframe = "minute")
} else if (action == "d" && resp$status_code == 204) {
message(paste0("Watchlist deleted successfully"))
out <- .wl
}
if ("assets" %in% names(.wl)) {
# If it's a specific watchlist, make the watchlist info an attribute
out <- .wl$assets
attr(out, "info") <- .info
} else if (length(.wl) == 5) {
# if it's an array of watchlists
out <- .info
} else if (action == "d") {
# if empty, return empty tibble
out <- structure(data.frame(), query = get_query(.wl))
}
attr(out, "query") <- get_query(.wl)
as_watchlist(out)
}
as_watchlist <- function(x) {
.a <- attributes(x)
if ("symbol" %in% names(x))
x <- dplyr::select(x, symbol, everything())
else if ("name" %in% names(x))
x <- dplyr::select(tibble::as_tibble(x), name, updated_at, everything())
out <- rlang::exec(structure, .Data = tibble::as_tibble(x), !!!.a[!names(.a) %in% "names"])
}