library(rvest) library(dplyr) ## ## Attaching package: ‘dplyr’ ## The following objects are masked from ‘package:stats’: ## ## filter, lag ## The following objects are masked from ‘package:base’: ## ## intersect, setdiff, setequal, union library(stringr)
url <- “https://isin.twse.com.tw/isin/C_public.jsp?strMode=2”
library(httr) library(rvest)
response <- GET(url) stop_for_status(response)
raw_html <- content(response, “raw”)
html_utf8 <- iconv( rawToChar(raw_html), from = “CP950”, to = “UTF-8”, sub = “byte” )
page <- read_html(html_utf8, encoding = “UTF-8”)
tables <- html_elements(page, “table”) |> html_table(fill = TRUE)
raw_twse <- tables[[which.max(sapply(tables, nrow))]]
head(raw_twse) ## # A tibble: 6 × 7 ## X1 X2 X3 X4 X5 X6 X7
##
## 4 1102 亞泥 TW0001102002 1962/0… 上市 水泥工業… ESVU… “”
## 5 1103 嘉泥 TW0001103000 1969/1… 上市 水泥工業… ESVU… “”
## 6 1104 環泥 TW0001104008 1971/0… 上市 水泥工業… ESVU… “”
library(dplyr) library(stringr)
twse_clean <- raw_twse %>% mutate( stock_id = str_extract(X1, “1{4}”), name = str_trim(str_remove(X1, “2{4}”)), listing_date = X3, industry = X5, cfi_code = X6 ) %>% filter( !is.na(stock_id), cfi_code == “ESVUFR”, X4 == “上市” ) %>% select(stock_id, name, listing_date, industry) %>% distinct(stock_id, .keep_all = TRUE)
head(twse_clean) ## # A tibble: 6 × 4 ## stock_id name listing_date
industry ##
## 1 1101 台泥 1962/02/09 水泥工業 ## 2 1102 亞泥 1962/06/08 水泥工業 ##
3 1103 嘉泥 1969/11/14 水泥工業 ## 4 1104 環泥 1971/02/01 水泥工業 ## 5
1108 幸福 1990/06/06 水泥工業 ## 6 1109 信大 1991/12/05 水泥工業
nrow(twse_clean) ## [1] 1053 library(httr) library(jsonlite)
library(dplyr) library(stringr)
api_url <- “https://api.finmindtrade.com/api/v4/data”
response_fm <- GET( api_url, query = list(dataset = “TaiwanStockInfo”) )
stop_for_status(response_fm)
result_fm <- fromJSON( content(response_fm, “text”, encoding = “UTF-8”) )
if (result_fm\(status != 200) { stop(result_fm\)msg) }
finmind_raw <- as.data.frame(result_fm$data)
head(finmind_raw) ## industry_category stock_id stock_name type date ## 1 電子零組件業 5481 新華 tpex 2020-06-03 ## 2 電腦及週邊設備業 5450 寶聯通 tpex 2020-06-03 ## 3 光電業 3629 地心引力 tpex 2020-06-03 ## 4 文化創意業 3687 歐買尬 tpex 2020-06-03 ## 5 其他電子類 6238 勝麗 tpex 2020-06-13 ## 6 上櫃指數股票型基金(ETF) 00835B 第一金科技債10+ tpex 2020-06-24 library(dplyr) library(stringr)
finmind_clean <- finmind_raw %>% filter( type == “twse”, str_detect(stock_id, “3{4}$”), !str_detect( industry_category, regex(“ETF|ETN|fund|preferred|特別股|受益證券”, ignore_case = TRUE) ) ) %>% transmute( stock_id = as.character(stock_id), name = stock_name, industry = industry_category ) %>% distinct(stock_id, .keep_all = TRUE)
finmind_clean <- finmind_clean %>% left_join( twse_clean %>% select(stock_id, listing_date), by = “stock_id” ) %>% select(stock_id, name, listing_date, industry)
head(finmind_clean) ## stock_id name listing_date industry ## 1 2241 艾姆勒 2020/08/26 電機機械 ## 2 1443 立益 1990/04/21 紡織纖維 ## 3 3669 圓展 2011/08/25 光電業 ## 4 6165 浪凡 2003/08/04 電子工業 ## 5 2429 銘旺科 2000/09/11 電子零組件業 ## 6 2459 敦吉 2001/09/17 電子通路業 nrow(finmind_clean) ## [1] 1226 library(dplyr)
only_twse <- anti_join( twse_clean, finmind_clean, by = “stock_id” )
only_finmind <- anti_join( finmind_clean, twse_clean, by = “stock_id” )
common_stocks <- inner_join( twse_clean, finmind_clean, by = “stock_id”, suffix = c(“_twse”, “_finmind”) )
comparison <- data.frame( Category = c( “TWSE total”, “FinMind total”, “In both datasets”, “Only in TWSE”, “Only in FinMind” ), Count = c( nrow(twse_clean), nrow(finmind_clean), nrow(common_stocks), nrow(only_twse), nrow(only_finmind) ) )
knitr::kable(comparison) Category Count TWSE total 1053 FinMind total
1226 In both datasets 1053 Only in TWSE 0 Only in FinMind 173 # Inspect
unmatched FinMind securities head(only_finmind, 20) ## stock_id name
listing_date industry ## 1 2432 倚天
finmind_final <- finmind_clean %>% semi_join(twse_clean, by = “stock_id”)
nrow(finmind_final) ## [1] 1053 head(finmind_final) ## stock_id name listing_date industry ## 1 2241 艾姆勒 2020/08/26 電機機械 ## 2 1443 立益 1990/04/21 紡織纖維 ## 3 3669 圓展 2011/08/25 光電業 ## 4 6165 浪凡 2003/08/04 電子工業 ## 5 2429 銘旺科 2000/09/11 電子零組件業 ## 6 2459 敦吉 2001/09/17 電子通路業 # Save verified FinMind data write.csv( finmind_final, “FinMind_Common_Stocks.csv”, row.names = FALSE, fileEncoding = “UTF-8” ) final_comparison <- data.frame( Source = c( “TWSE common stocks”, “FinMind initially filtered”, “FinMind matched with TWSE”, “FinMind unmatched records” ), Number_of_stocks = c( nrow(twse_clean), nrow(finmind_clean), nrow(finmind_final), nrow(only_finmind) ) )
knitr::kable(final_comparison) Source Number_of_stocks TWSE common stocks 1053 FinMind initially filtered 1226 FinMind matched with TWSE 1053 FinMind unmatched records 173 Results and Discussion The official TWSE ISIN database returned 1,053 listed common stocks after applying the common stock classification and market filters.
The FinMind TaiwanStockInfo dataset initially contained 4,333 securities. After filtering, 1,226 records remained.
A comparison of stock IDs showed that 1,053 securities appeared in both datasets, while 173 appeared only in the filtered FinMind dataset.
The 173 unmatched securities require further verification. Therefore, a TWSE-validated FinMind subset containing 1,053 stocks was also created. Original listing dates were obtained from TWSE because the FinMind dataset does not directly provide this information.
Conclusion The TWSE and FinMind datasets were successfully downloaded, processed, and compared. All 1,053 securities in the filtered TWSE dataset were matched in FinMind. The remaining 173 FinMind records were documented separately for further investigation.