library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.2.1     ✔ readr     2.2.0
## ✔ forcats   1.0.1     ✔ stringr   1.6.0
## ✔ ggplot2   4.0.3     ✔ tibble    3.3.1
## ✔ lubridate 1.9.5     ✔ tidyr     1.3.2
## ✔ purrr     1.2.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(lubridate)
library(xts)
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## 
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## 
## ######################### Warning from 'xts' package ##########################
## #                                                                             #
## # The dplyr lag() function breaks how base R's lag() function is supposed to  #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
## # source() into this session won't work correctly.                            #
## #                                                                             #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
## # dplyr from breaking base R's lag() function.                                #
## #                                                                             #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## ###############################################################################
## 
## Attaching package: 'xts'
## 
## The following objects are masked from 'package:dplyr':
## 
##     first, last
raw <- read_tsv("20261002012515.txt",
                locale = locale(encoding = "UTF-16LE"),
                show_col_types = FALSE)

long <- data.frame(
  id    = raw[["CO_ID"]],
  date  = ymd(raw[["Date"]]),
  price = raw[["Close(NTD)"]],
  check.names = FALSE
)

wide <- long %>%
  pivot_wider(names_from = id, values_from = price) %>%
  arrange(date)

etf <- xts(as.matrix(wide[ , -1]), order.by = wide$date)

head(etf)
##            0050 Yuanta Taiwan Top50 0052 FB Technology 0056 PTD
## 2020-01-02                  19.8573             8.1422  16.9530
## 2020-01-03                  19.8573             8.0922  17.0054
## 2020-01-06                  19.6031             8.0033  16.8772
## 2020-01-07                  19.5421             7.9755  16.7199
## 2020-01-08                  19.4506             7.9144  16.6091
## 2020-01-09                  19.7149             8.0477  16.7257
tail(etf)
##            0050 Yuanta Taiwan Top50 0052 FB Technology 0056 PTD
## 2026-09-15                   106.25              61.60    55.00
## 2026-09-16                   106.90              61.95    55.55
## 2026-09-17                   108.05              62.70    56.30
## 2026-09-18                   109.85              63.80    56.85
## 2026-09-21                   111.35              64.55    57.15
## 2026-09-22                   111.85              64.75    56.85
class(etf)
## [1] "xts" "zoo"

Checks

dim(etf)
## [1] 1634    3
range(index(etf))
## [1] "2020-01-02" "2026-09-22"
sum(is.na(etf))
## [1] 0