Course: YOUR COURSE NAME Instructor: INSTRUCTOR NAME

Objective

Download the daily adjusted prices of three Taiwan ETFs (0050, 0052, 0056) from the TEJ database, import them into R, and convert them into a time-series object with one column per ETF.

Data source

The data were downloaded from the TEJ database using TEJ Pro. The variable used is the daily adjusted closing price (Close(NTD)) of:

Code ETF
0050 元大台灣50 (Yuanta Taiwan Top50)
0052 富邦科技 (FB Technology)
0056 元大高股息 (Yuanta High Dividend / PTD)

Data period

Requested range: 2010-01-01 to 2026-09-22. The first trading day in the range is 2010-01-04.

Data preparation

The TEJ Pro export is a tab-delimited, UTF-16 encoded text file in “long” format (one row per ETF per day).

candidates <- c("20260924061630.csv",
                "~/Downloads/20260924061630.csv",
                "~/Desktop/20260924061630.csv")
file_path <- candidates[file.exists(candidates)][1]
raw <- read.delim(file_path, fileEncoding = "UTF-16",
                  check.names = FALSE, stringsAsFactors = FALSE)
names(raw) <- sub("^\ufeff", "", names(raw))   # remove a hidden BOM character if present

names(raw)
## [1] "CO_ID"      "Date"       "Close(NTD)"
str(raw)
## 'data.frame':    12297 obs. of  3 variables:
##  $ CO_ID     : chr  "0050 Yuanta Taiwan Top50" "0052 FB Technology" "0056 PTD" "0050 Yuanta Taiwan Top50" ...
##  $ Date      : int  20100104 20100104 20100104 20100105 20100105 20100105 20100106 20100106 20100106 20100107 ...
##  $ Close(NTD): num  8.45 2.92 8.47 8.45 2.93 ...
etf_long <- raw %>%
  rename(security = `CO_ID`, date = `Date`, price = `Close(NTD)`) %>%
  mutate(
    code  = substr(security, 1, 4),
    date  = as.Date(as.character(date), format = "%Y%m%d"),
    price = as.numeric(price)
  ) %>%
  select(date, code, price) %>%
  arrange(code, date)

head(etf_long)
##         date code  price
## 1 2010-01-04 0050 8.4501
## 2 2010-01-05 0050 8.4501
## 3 2010-01-06 0050 8.6072
## 4 2010-01-07 0050 8.5847
## 5 2010-01-08 0050 8.6371
## 6 2010-01-11 0050 8.6595

Time-series conversion

The xts package (built on zoo) is the standard R structure for financial time series: the dates are the index and each ETF is a column.

etf_wide <- etf_long %>%
  pivot_wider(names_from = code, values_from = price) %>%
  arrange(date) %>%
  select(date, `0050`, `0052`, `0056`)

etf_xts <- xts(as.matrix(etf_wide[, c("0050", "0052", "0056")]),
               order.by = etf_wide$date)

colnames(etf_xts) <- c("0050 元大台灣50", "0052 富邦科技", "0056 元大高股息")

Final data output

head(etf_xts)                              # first six rows of the full sample
##            0050 元大台灣50 0052 富邦科技 0056 元大高股息
## 2010-01-04          8.4501        2.9216          8.4679
## 2010-01-05          8.4501        2.9256          8.4318
## 2010-01-06          8.6072        2.9936          8.5579
## 2010-01-07          8.5847        2.9616          8.4859
## 2010-01-08          8.6371        2.9528          8.5760
## 2010-01-11          8.6595        2.9696          8.6660
head(etf_xts["2020-01-02/2020-01-09"], 6)  # rows to compare with the example
##            0050 元大台灣50 0052 富邦科技 0056 元大高股息
## 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_xts)
##            0050 元大台灣50 0052 富邦科技 0056 元大高股息
## 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
dim(etf_xts)
## [1] 4099    3
class(etf_xts)
## [1] "xts" "zoo"

Data validation

checks <- data.frame(
  check = c("Number of observations (days)",
            "Minimum date",
            "Maximum date",
            "Missing values",
            "Duplicated dates (time series)",
            "Duplicated code/date pairs (long data)",
            "All three ETFs present",
            "Prices numeric",
            "Sorted by date"),
  result = c(nrow(etf_xts),
             as.character(min(index(etf_xts))),
             as.character(max(index(etf_xts))),
             sum(is.na(etf_xts)),
             sum(duplicated(index(etf_xts))),
             sum(duplicated(etf_long[, c("code", "date")])),
             all(c("0050", "0052", "0056") %in% etf_long$code),
             is.numeric(coredata(etf_xts)),
             !is.unsorted(index(etf_xts)))
)
checks
##                                    check     result
## 1          Number of observations (days)       4099
## 2                           Minimum date 2010-01-04
## 3                           Maximum date 2026-09-22
## 4                         Missing values          0
## 5         Duplicated dates (time series)          0
## 6 Duplicated code/date pairs (long data)          0
## 7                 All three ETFs present       TRUE
## 8                         Prices numeric       TRUE
## 9                         Sorted by date       TRUE
etf_long %>%
  group_by(code) %>%
  summarise(n = n(), first_date = min(date), last_date = max(date),
            missing = sum(is.na(price)))
## # A tibble: 3 × 5
##   code      n first_date last_date  missing
##   <chr> <int> <date>     <date>       <int>
## 1 0050   4099 2010-01-04 2026-09-22       0
## 2 0052   4099 2010-01-04 2026-09-22       0
## 3 0056   4099 2010-01-04 2026-09-22       0

Conclusion

The daily adjusted prices of ETFs 0050, 0052 and 0056 were imported from TEJ Pro, cleaned, and converted into a single xts time series with one column per ETF. The series covers 2010-01-04 to 2026-09-22, contains no missing values or duplicated dates, and is sorted chronologically. The rows for January 2020 match the format and values in the assignment example.