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
## <chr> <chr> <chr> <chr> <chr> <chr> <chr>
## 1 有價證券代號及名稱 國際證券辨識號碼(ISIN Code) 上市日 市場別…… 產業別…… CFIC… "備註"…
## 2 股票 股票 股票 股票 股票 股票 "股票"…
## 3 1101 台泥 TW0001101004 1962/0… 上市 水泥工業… ESVU… ""
## 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, "^[0-9]{4}"),
name = str_trim(str_remove(X1, "^[0-9]{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
## <chr> <chr> <chr> <chr>
## 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, "^[0-9]{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)
# Add official listing dates from TWSE
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)
# Stocks in TWSE but not in FinMind
only_twse <- anti_join(
twse_clean,
finmind_clean,
by = "stock_id"
)
# Stocks in FinMind but not in TWSE
only_finmind <- anti_join(
finmind_clean,
twse_clean,
by = "stock_id"
)
# Stocks appearing in both datasets
common_stocks <- inner_join(
twse_clean,
finmind_clean,
by = "stock_id",
suffix = c("_twse", "_finmind")
)
# Comparison summary
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 倚天 <NA> 通信網路業
## 2 6806 森崴能源 <NA> 其他
## 3 1230 聯成食 <NA> 電器電纜
## 4 4733 上緯 <NA> 化學生技醫療
## 5 1523 開億 <NA> 電機機械
## 6 1606 歌林 <NA> 電器電纜
## 7 1716 永信 <NA> 化學生技醫療
## 8 1601 台光 <NA> 電器電纜
## 9 1311 福聚 <NA> 塑膠工業
## 10 6951 青新-創 <NA> 創新版股票
## 11 8078 華寶 <NA> 通信網路業
## 12 6969 成信實業*-創 <NA> 創新版股票
## 13 2341 英群 <NA> 電子工業
## 14 2418 雅新 <NA> 電子工業
## 15 8008 建興電 <NA> 電腦及週邊設備業
## 16 2411 飛瑞 <NA> 電子工業
## 17 2350 環電 <NA> 其他電子業
## 18 2403 友尚 <NA> 電子通路業
## 19 2396 精碟 <NA> 電子工業
## 20 2391 合勤 <NA> 電子工業
# Check missing listing dates
sum(is.na(finmind_clean$listing_date))
## [1] 173
# Examine unmatched securities
head(only_finmind, 20)
## stock_id name listing_date industry
## 1 2432 倚天 <NA> 通信網路業
## 2 6806 森崴能源 <NA> 其他
## 3 1230 聯成食 <NA> 電器電纜
## 4 4733 上緯 <NA> 化學生技醫療
## 5 1523 開億 <NA> 電機機械
## 6 1606 歌林 <NA> 電器電纜
## 7 1716 永信 <NA> 化學生技醫療
## 8 1601 台光 <NA> 電器電纜
## 9 1311 福聚 <NA> 塑膠工業
## 10 6951 青新-創 <NA> 創新版股票
## 11 8078 華寶 <NA> 通信網路業
## 12 6969 成信實業*-創 <NA> 創新版股票
## 13 2341 英群 <NA> 電子工業
## 14 2418 雅新 <NA> 電子工業
## 15 8008 建興電 <NA> 電腦及週邊設備業
## 16 2411 飛瑞 <NA> 電子工業
## 17 2350 環電 <NA> 其他電子業
## 18 2403 友尚 <NA> 電子通路業
## 19 2396 精碟 <NA> 電子工業
## 20 2391 合勤 <NA> 電子工業
# Check industries of unmatched securities
sort(table(only_finmind$industry), decreasing = TRUE)
##
## 電子工業 光電業 創新版股票 金融保險
## 32 16 15 15
## 其他 半導體業 存託憑證 創新板股票
## 11 11 11 10
## 電腦及週邊設備業 生技醫療業 電子零組件業 通信網路業
## 7 6 5 4
## 電器電纜 化學工業 化學生技醫療 電子通路業
## 4 3 3 3
## 其他電子業 塑膠工業 建材營造 汽車工業
## 2 2 2 2
## 貿易百貨 電機機械 數位雲端 紡織纖維
## 2 2 1 1
## 綠能環保 航運業 造紙工業
## 1 1 1
# Verify stock IDs
length(unique(only_finmind$stock_id))
## [1] 173
library(dplyr)
# Keep stocks confirmed by official TWSE list
finmind_final <- finmind_clean %>%
semi_join(twse_clean, by = "stock_id")
# Check final results
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 |
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.
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.