1. Listed companies
library(jsonlite)
get_list <- function(api_url, suffix) {
resp <- GET(api_url, config(ssl_verifypeer = FALSE), user_agent("Mozilla/5.0"))
stop_for_status(resp)
df <- fromJSON(content(resp, "text", encoding = "UTF-8"))
# Find the column that holds 4-digit stock codes (works for both APIs)
is_code <- map_dbl(df, ~ mean(str_detect(as.character(.x), "^[1-9][0-9]{3}$")))
code_col <- names(which.max(is_code))
tibble(Ticker = as.character(df[[code_col]])) %>%
filter(str_detect(Ticker, "^[1-9][0-9]{3}$")) %>%
distinct() %>%
mutate(YahooTicker = paste0(Ticker, suffix))
}
listed_stocks <- bind_rows(
get_list("https://openapi.twse.com.tw/v1/opendata/t187ap03_L", ".TW"), # TWSE listed
get_list("https://www.tpex.org.tw/openapi/v1/mopsfin_t187ap03_O", ".TWO") # TPEx OTC
)
nrow(listed_stocks)
## [1] 1981
head(listed_stocks)
## # A tibble: 6 × 2
## Ticker YahooTicker
## <chr> <chr>
## 1 1101 1101.TW
## 2 1102 1102.TW
## 3 1103 1103.TW
## 4 1104 1104.TW
## 5 1108 1108.TW
## 6 1109 1109.TW
2. Download daily prices
end_date <- Sys.Date()
start_date <- end_date - (365 * 2)
get_stock_data <- function(symbol) {
Sys.sleep(0.3) # avoid Yahoo rate limit
tryCatch({
df <- getSymbols(symbol, src = "yahoo", from = start_date, to = end_date,
auto.assign = FALSE)
out <- data.frame(Date = index(df), coredata(df))
colnames(out) <- c("Date", "Open", "High", "Low", "Close", "Volume", "Adjusted")
out$Ticker <- sub("\\.TWO?$", "", symbol)
out
}, error = function(e) NULL)
}
# TRUE = first 20 stocks only (quick test); FALSE = all stocks
test_mode <- TRUE
targets <- if (test_mode) slice(listed_stocks, 1:20) else listed_stocks
cache <- if (test_mode) "tw_prices_test.rds" else "tw_prices_2y.rds"
if (file.exists(cache)) {
all_prices <- readRDS(cache)
} else {
all_prices <- map_dfr(targets$YahooTicker, get_stock_data)
saveRDS(all_prices, cache)
write_csv(all_prices, sub("\\.rds$", ".csv", cache))
}
3. Summary
all_prices %>%
summarise(companies = n_distinct(Ticker),
rows = n(),
first_day = min(Date),
last_day = max(Date))
## companies rows first_day last_day
## 1 20 9720 2024-10-04 2026-10-02
head(all_prices)
## Date Open High Low Close Volume Adjusted Ticker
## 1 2024-10-04 33.50 34.00 33.50 33.80 17262621 31.42613 1101
## 2 2024-10-07 33.80 33.85 33.30 33.50 13071136 31.14720 1101
## 3 2024-10-08 33.25 33.45 32.90 33.05 12628511 30.72880 1101
## 4 2024-10-09 32.90 32.95 32.45 32.45 11284246 30.17094 1101
## 5 2024-10-11 32.75 33.00 32.40 32.50 9354924 30.21743 1101
## 6 2024-10-14 32.55 32.70 32.45 32.55 5999260 30.26392 1101