install.packages(“dplyr”) # Để dễ dàng thao tác dữ liệu
library(quantmod)
## Warning: package 'quantmod' was built under R version 4.4.3
## Loading required package: xts
## Warning: package 'xts' was built under R version 4.4.3
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Loading required package: TTR
## Warning: package 'TTR' was built under R version 4.4.3
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
library(forecast)
## Warning: package 'forecast' was built under R version 4.4.3
library(tseries)
## Warning: package 'tseries' was built under R version 4.4.3
library(ggplot2)
## Warning: package 'ggplot2' was built under R version 4.4.3
library(dplyr)
## Warning: package 'dplyr' was built under R version 4.4.3
##
## ######################### 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: 'dplyr'
## The following objects are masked from 'package:xts':
##
## first, last
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
library(lubridate) # Để làm việc với ngày tháng
## Warning: package 'lubridate' was built under R version 4.4.3
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
# 2. Tải dữ liệu chứng khoán
# Thay "AAPL" bằng mã cổ phiếu bạn muốn (ví dụ: "FPT.VN", "VIC.VN" cho cổ phiếu Việt Nam trên Yahoo Finance)
# Hoặc "VNINDEX.VN" cho chỉ số VN-Index
ticker <- "AAPL" # Apple Inc.
start_date <- "2022-01-01"
end_date <- Sys.Date() # Lấy đến ngày hiện tại
# Sử dụng tryCatch để xử lý lỗi nếu không tải được dữ liệu
tryCatch({
getSymbols(ticker, src = "yahoo", from = start_date, to = end_date, auto.assign = TRUE)
}, error = function(e) {
stop(paste("Không thể tải dữ liệu cho mã:", ticker, ". Lỗi:", e$message))
})
## [1] "AAPL"
# Lấy giá đóng cửa đã điều chỉnh (Adjusted Close)
# Đối tượng trả về từ getSymbols sẽ có tên là ticker (ví dụ: AAPL)
stock_data <- Ad(get(ticker)) # Lấy cột Adjusted Close
colnames(stock_data) <- "Adjusted_Price"
# Kiểm tra dữ liệu
head(stock_data)
## Adjusted_Price
## 2022-01-03 178.6456
## 2022-01-04 176.3783
## 2022-01-05 171.6867
## 2022-01-06 168.8207
## 2022-01-07 168.9875
## 2022-01-10 169.0072
tail(stock_data)
## Adjusted_Price
## 2025-05-23 195.270
## 2025-05-27 200.210
## 2025-05-28 200.420
## 2025-05-29 199.950
## 2025-05-30 200.850
## 2025-06-02 201.025
class(stock_data) # Phải là 'xts' hoặc 'zoo'
## [1] "xts" "zoo"
# Chuyển thành đối tượng ts để dễ làm việc với forecast
# Lưu ý: Đối với dữ liệu hàng ngày, tần suất (frequency) có thể phức tạp.
# auto.arima thường xử lý tốt mà không cần frequency rõ ràng cho dữ liệu phi mùa vụ.
# Nếu bạn muốn xử lý các hiệu ứng ngày trong tuần/tháng, frequency có thể là 5 (ngày giao dịch) hoặc 252 (ước tính số ngày giao dịch/năm)
# Tuy nhiên, đơn giản nhất là để auto.arima tự quyết định hoặc không đặt frequency cụ thể.
ts_stock_prices <- ts(stock_data$Adjusted_Price, frequency = 1) # frequency=1 cho dữ liệu không có mùa vụ rõ ràng theo nghĩa truyền thống
# 3. Trực quan hóa và kiểm tra tính dừng
autoplot(ts_stock_prices) +
ggtitle(paste("Giá cổ phiếu", ticker, "(Đã điều chỉnh)")) +
ylab("Giá") +
xlab("Thời gian (quan sát)")

# Kiểm tra tính dừng bằng ADF test trên giá gốc
# H0: Chuỗi không dừng
adf.test(na.omit(ts_stock_prices))
##
## Augmented Dickey-Fuller Test
##
## data: na.omit(ts_stock_prices)
## Dickey-Fuller = -2.8, Lag order = 9, p-value = 0.2397
## alternative hypothesis: stationary
# P-value lớn -> không bác bỏ H0 -> chuỗi giá có khả năng không dừng
# Thử với log returns để có chuỗi dừng hơn
# Log returns = log(P_t) - log(P_{t-1}) = diff(log(P_t))
log_returns <- diff(log(ts_stock_prices))
log_returns <- na.omit(log_returns) # Bỏ giá trị NA đầu tiên do phép diff
autoplot(log_returns) +
ggtitle(paste("Log Returns của", ticker)) +
ylab("Log Return") +
xlab("Thời gian (quan sát)")

# Kiểm tra tính dừng của log returns
adf.test(log_returns)
## Warning in adf.test(log_returns): p-value smaller than printed p-value
##
## Augmented Dickey-Fuller Test
##
## data: log_returns
## Dickey-Fuller = -9.919, Lag order = 9, p-value = 0.01
## alternative hypothesis: stationary
# P-value thường sẽ nhỏ hơn 0.05 -> bác bỏ H0 -> chuỗi log returns có khả năng dừng
# 4. Chia dữ liệu thành tập huấn luyện (train) và tập kiểm tra (test)
# Ví dụ: 80% cho train, 20% cho test
split_ratio <- 0.8
split_point <- floor(length(ts_stock_prices) * split_ratio)
train_prices <- window(ts_stock_prices, end = time(ts_stock_prices)[split_point])
test_prices <- window(ts_stock_prices, start = time(ts_stock_prices)[split_point + 1])
# Hoặc nếu bạn muốn mô hình hóa log_returns:
#split_point_returns <- floor(length(log_returns) * split_ratio)
#train_log_returns <- window(log_returns, end = time(log_returns)[split_point_returns])
#test_log_returns <- window(log_returns, start = time(log_returns)[split_point_returns + 1])
# 5. Xây dựng mô hình ARIMA
# Sử dụng auto.arima trên dữ liệu giá gốc (train_prices).
# auto.arima sẽ tự động tìm số lần sai phân (d) cần thiết.
# Đối với dữ liệu chứng khoán, thường seasonal=FALSE.
# stepwise=FALSE và approximation=FALSE để tìm kiếm kỹ hơn (có thể tốn thời gian hơn).
cat("\nĐang tìm mô hình ARIMA tối ưu cho giá cổ phiếu...\n")
##
## Đang tìm mô hình ARIMA tối ưu cho giá cổ phiếu...
arima_model <- auto.arima(
train_prices,
seasonal = FALSE, # Dữ liệu giá cổ phiếu hàng ngày thường không có tính mùa vụ rõ ràng
stepwise = TRUE, # Tìm kiếm nhanh hơn, đặt FALSE để tìm kỹ hơn
approximation = FALSE,# Không dùng xấp xỉ để có kết quả chính xác hơn
trace = TRUE, # Hiển thị các mô hình đã thử
allowdrift = TRUE # Cho phép có thành phần trôi dạt (drift)
)
##
## ARIMA(2,1,2) with drift : 3383.76
## ARIMA(0,1,0) with drift : 3379.357
## ARIMA(1,1,0) with drift : 3380.571
## ARIMA(0,1,1) with drift : 3380.486
## ARIMA(0,1,0) : 3377.74
## ARIMA(1,1,1) with drift : 3383.345
##
## Best model: ARIMA(0,1,0)
# In thông tin mô hình
print(summary(arima_model))
## Series: train_prices
## ARIMA(0,1,0)
##
## sigma^2 = 8.204: log likelihood = -1687.87
## AIC=3377.73 AICc=3377.74 BIC=3382.26
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.06902007 2.862191 2.134654 0.0190058 1.288716 0.9986602
## ACF1
## Training set 0.03423953
# Chú ý đến các hệ số và giá trị AIC, BIC.
# ARIMA(p,d,q). d > 0 nghĩa là auto.arima đã thực hiện sai phân.
# 6. Kiểm tra mô hình (Model Diagnostics)
checkresiduals(arima_model)

##
## Ljung-Box test
##
## data: Residuals from ARIMA(0,1,0)
## Q* = 6.7212, df = 10, p-value = 0.7515
##
## Model df: 0. Total lags used: 10
# - Phần dư nên trông giống nhiễu trắng (white noise).
# - ACF của phần dư: không có tự tương quan đáng kể.
# - Ljung-Box test: p-value lớn (>0.05) cho thấy phần dư không có tự tương quan.
# Tuy nhiên, với dữ liệu tài chính, phần dư có thể vẫn còn hiện tượng volatility clustering.
# 7. Dự báo trên tập kiểm tra (test set)
num_forecast_periods <- length(test_prices)
forecast_values <- forecast(arima_model, h = num_forecast_periods)
# In kết quả dự báo
print(forecast_values)
## Point Forecast Lo 80 Hi 80 Lo 95 Hi 95
## 685 225.6767 222.0060 229.3475 220.0628 231.2906
## 686 225.6767 220.4855 230.8679 217.7375 233.6160
## 687 225.6767 219.3188 232.0346 215.9532 235.4003
## 688 225.6767 218.3353 233.0182 214.4489 236.9045
## 689 225.6767 217.4687 233.8847 213.1237 238.2298
## 690 225.6767 216.6853 234.6681 211.9255 239.4279
## 691 225.6767 215.9649 235.3886 210.8238 240.5297
## 692 225.6767 215.2943 236.0591 209.7982 241.5552
## 693 225.6767 214.6645 236.6889 208.8350 242.5184
## 694 225.6767 214.0689 237.2846 207.9240 243.4294
## 695 225.6767 213.5023 237.8512 207.0575 244.2959
## 696 225.6767 212.9609 238.3925 206.2296 245.1238
## 697 225.6767 212.4417 238.9117 205.4355 245.9179
## 698 225.6767 211.9421 239.4113 204.6714 246.6820
## 699 225.6767 211.4601 239.8934 203.9342 247.4193
## 700 225.6767 210.9938 240.3596 203.2211 248.1323
## 701 225.6767 210.5419 240.8115 202.5300 248.8234
## 702 225.6767 210.1031 241.2503 201.8590 249.4945
## 703 225.6767 209.6764 241.6771 201.2063 250.1471
## 704 225.6767 209.2607 242.0927 200.5706 250.7828
## 705 225.6767 208.8553 242.4981 199.9506 251.4028
## 706 225.6767 208.4595 242.8940 199.3452 252.0082
## 707 225.6767 208.0725 243.2809 198.7534 252.6000
## 708 225.6767 207.6939 243.6596 198.1744 253.1791
## 709 225.6767 207.3231 244.0304 197.6072 253.7462
## 710 225.6767 206.9596 244.3938 197.0514 254.3021
## 711 225.6767 206.6031 244.7504 196.5061 254.8474
## 712 225.6767 206.2531 245.1004 195.9708 255.3827
## 713 225.6767 205.9092 245.4442 195.4450 255.9085
## 714 225.6767 205.5713 245.7821 194.9281 256.4253
## 715 225.6767 205.2390 246.1145 194.4199 256.9336
## 716 225.6767 204.9119 246.4415 193.9197 257.4337
## 717 225.6767 204.5900 246.7635 193.4273 257.9261
## 718 225.6767 204.2729 247.0806 192.9424 258.4111
## 719 225.6767 203.9604 247.3931 192.4645 258.8890
## 720 225.6767 203.6523 247.7011 191.9933 259.3601
## 721 225.6767 203.3486 248.0049 191.5287 259.8247
## 722 225.6767 203.0488 248.3046 191.0703 260.2831
## 723 225.6767 202.7530 248.6004 190.6180 260.7355
## 724 225.6767 202.4610 248.8925 190.1713 261.1821
## 725 225.6767 202.1726 249.1809 189.7302 261.6232
## 726 225.6767 201.8877 249.4658 189.2945 262.0589
## 727 225.6767 201.6061 249.7473 188.8639 262.4895
## 728 225.6767 201.3279 250.0256 188.4383 262.9151
## 729 225.6767 201.0527 250.3007 188.0176 263.3359
## 730 225.6767 200.7806 250.5728 187.6014 263.7520
## 731 225.6767 200.5115 250.8420 187.1898 264.1637
## 732 225.6767 200.2452 251.1083 186.7825 264.5709
## 733 225.6767 199.9816 251.3718 186.3795 264.9740
## 734 225.6767 199.7207 251.6327 185.9805 265.3730
## 735 225.6767 199.4625 251.8910 185.5855 265.7680
## 736 225.6767 199.2067 252.1467 185.1943 266.1591
## 737 225.6767 198.9534 252.4000 184.8069 266.5465
## 738 225.6767 198.7025 252.6510 184.4232 266.9303
## 739 225.6767 198.4539 252.8996 184.0430 267.3105
## 740 225.6767 198.2075 253.1460 183.6662 267.6873
## 741 225.6767 197.9633 253.3901 183.2927 268.0607
## 742 225.6767 197.7213 253.6322 182.9226 268.4309
## 743 225.6767 197.4813 253.8721 182.5556 268.7979
## 744 225.6767 197.2434 254.1101 182.1917 269.1618
## 745 225.6767 197.0074 254.3460 181.8308 269.5227
## 746 225.6767 196.7734 254.5801 181.4729 269.8806
## 747 225.6767 196.5412 254.8122 181.1178 270.2357
## 748 225.6767 196.3109 255.0426 180.7656 270.5879
## 749 225.6767 196.0824 255.2711 180.4160 270.9374
## 750 225.6767 195.8556 255.4979 180.0692 271.2842
## 751 225.6767 195.6305 255.7229 179.7250 271.6285
## 752 225.6767 195.4071 255.9463 179.3833 271.9701
## 753 225.6767 195.1854 256.1681 179.0442 272.3093
## 754 225.6767 194.9652 256.3883 178.7075 272.6460
## 755 225.6767 194.7466 256.6068 178.3732 272.9803
## 756 225.6767 194.5296 256.8239 178.0412 273.3122
## 757 225.6767 194.3140 257.0395 177.7116 273.6419
## 758 225.6767 194.0999 257.2535 177.3842 273.9693
## 759 225.6767 193.8873 257.4662 177.0590 274.2945
## 760 225.6767 193.6760 257.6774 176.7359 274.6175
## 761 225.6767 193.4662 257.8872 176.4150 274.9385
## 762 225.6767 193.2577 258.0957 176.0961 275.2573
## 763 225.6767 193.0506 258.3029 175.7793 275.5741
## 764 225.6767 192.8447 258.5087 175.4645 275.8889
## 765 225.6767 192.6402 258.7133 175.1517 276.2018
## 766 225.6767 192.4369 258.9166 174.8407 276.5127
## 767 225.6767 192.2348 259.1187 174.5317 276.8218
## 768 225.6767 192.0339 259.3195 174.2245 277.1289
## 769 225.6767 191.8343 259.5192 173.9192 277.4343
## 770 225.6767 191.6358 259.7177 173.6156 277.7379
## 771 225.6767 191.4384 259.9150 173.3138 278.0397
## 772 225.6767 191.2422 260.1112 173.0137 278.3397
## 773 225.6767 191.0471 260.3063 172.7153 278.6381
## 774 225.6767 190.8531 260.5003 172.4186 278.9348
## 775 225.6767 190.6602 260.6933 172.1236 279.2299
## 776 225.6767 190.4683 260.8851 171.8301 279.5233
## 777 225.6767 190.2775 261.0760 171.5383 279.8152
## 778 225.6767 190.0877 261.2658 171.2480 280.1055
## 779 225.6767 189.8989 261.4546 170.9592 280.3942
## 780 225.6767 189.7111 261.6424 170.6720 280.6815
## 781 225.6767 189.5242 261.8292 170.3863 280.9672
## 782 225.6767 189.3384 262.0151 170.1020 281.2515
## 783 225.6767 189.1534 262.2000 169.8192 281.5343
## 784 225.6767 188.9694 262.3840 169.5378 281.8157
## 785 225.6767 188.7864 262.5671 169.2578 282.0957
## 786 225.6767 188.6042 262.7493 168.9792 282.3743
## 787 225.6767 188.4229 262.9306 168.7019 282.6516
## 788 225.6767 188.2425 263.1110 168.4260 282.9275
## 789 225.6767 188.0629 263.2905 168.1514 283.2020
## 790 225.6767 187.8843 263.4692 167.8781 283.4753
## 791 225.6767 187.7064 263.6471 167.6061 283.7473
## 792 225.6767 187.5294 263.8241 167.3354 284.0181
## 793 225.6767 187.3532 264.0003 167.0659 284.2875
## 794 225.6767 187.1778 264.1757 166.7977 284.5558
## 795 225.6767 187.0032 264.3503 166.5307 284.8228
## 796 225.6767 186.8294 264.5241 166.2648 285.0886
## 797 225.6767 186.6563 264.6971 166.0002 285.3533
## 798 225.6767 186.4841 264.8694 165.7367 285.6167
## 799 225.6767 186.3125 265.0409 165.4744 285.8791
## 800 225.6767 186.1418 265.2117 165.2132 286.1402
## 801 225.6767 185.9717 265.3817 164.9532 286.4003
## 802 225.6767 185.8024 265.5511 164.6942 286.6593
## 803 225.6767 185.6338 265.7197 164.4363 286.9171
## 804 225.6767 185.4659 265.8876 164.1796 287.1739
## 805 225.6767 185.2987 266.0548 163.9239 287.4296
## 806 225.6767 185.1322 266.2213 163.6692 287.6842
## 807 225.6767 184.9664 266.3871 163.4156 287.9379
## 808 225.6767 184.8012 266.5522 163.1630 288.1904
## 809 225.6767 184.6367 266.7167 162.9115 288.4420
## 810 225.6767 184.4729 266.8806 162.6609 288.6926
## 811 225.6767 184.3097 267.0437 162.4113 288.9421
## 812 225.6767 184.1472 267.2063 162.1627 289.1907
## 813 225.6767 183.9853 267.3682 161.9151 289.4383
## 814 225.6767 183.8240 267.5295 161.6685 289.6850
## 815 225.6767 183.6633 267.6901 161.4227 289.9307
## 816 225.6767 183.5033 267.8502 161.1780 290.1755
## 817 225.6767 183.3438 268.0096 160.9341 290.4193
## 818 225.6767 183.1850 268.1685 160.6912 290.6623
## 819 225.6767 183.0267 268.3268 160.4491 290.9043
## 820 225.6767 182.8690 268.4844 160.2080 291.1454
## 821 225.6767 182.7119 268.6415 159.9678 291.3857
## 822 225.6767 182.5554 268.7980 159.7284 291.6251
## 823 225.6767 182.3995 268.9540 159.4899 291.8636
## 824 225.6767 182.2441 269.1094 159.2522 292.1012
## 825 225.6767 182.0892 269.2642 159.0154 292.3381
## 826 225.6767 181.9349 269.4185 158.7794 292.5740
## 827 225.6767 181.7812 269.5723 158.5443 292.8092
## 828 225.6767 181.6280 269.7255 158.3100 293.0435
## 829 225.6767 181.4753 269.8782 158.0765 293.2770
## 830 225.6767 181.3231 270.0303 157.8438 293.5097
## 831 225.6767 181.1715 270.1820 157.6118 293.7416
## 832 225.6767 181.0204 270.3331 157.3807 293.9727
## 833 225.6767 180.8698 270.4837 157.1504 294.2031
## 834 225.6767 180.7197 270.6338 156.9208 294.4326
## 835 225.6767 180.5700 270.7834 156.6920 294.6614
## 836 225.6767 180.4209 270.9325 156.4640 294.8895
## 837 225.6767 180.2723 271.0811 156.2367 295.1168
## 838 225.6767 180.1242 271.2293 156.0101 295.3434
## 839 225.6767 179.9765 271.3769 155.7843 295.5692
## 840 225.6767 179.8293 271.5241 155.5592 295.7943
## 841 225.6767 179.6826 271.6708 155.3348 296.0187
## 842 225.6767 179.5364 271.8171 155.1111 296.2423
## 843 225.6767 179.3906 271.9629 154.8882 296.4653
## 844 225.6767 179.2453 272.1082 154.6659 296.6875
## 845 225.6767 179.1004 272.2531 154.4444 296.9091
## 846 225.6767 178.9560 272.3975 154.2235 297.1300
## 847 225.6767 178.8120 272.5415 154.0033 297.3502
## 848 225.6767 178.6685 272.6850 153.7838 297.5697
## 849 225.6767 178.5254 272.8281 153.5649 297.7885
## 850 225.6767 178.3827 272.9708 153.3467 298.0067
## 851 225.6767 178.2404 273.1130 153.1292 298.2243
## 852 225.6767 178.0986 273.2548 152.9123 298.4411
## 853 225.6767 177.9572 273.3962 152.6961 298.6574
## 854 225.6767 177.8163 273.5372 152.4805 298.8730
## 855 225.6767 177.6757 273.6777 152.2655 299.0880
## 856 225.6767 177.5356 273.8179 152.0512 299.3023
# 8. Trực quan hóa kết quả dự báo so với dữ liệu thực tế
autoplot(forecast_values) +
autolayer(test_prices, series = "Giá thực tế") +
ggtitle(paste("Dự báo giá cổ phiếu", ticker, "sử dụng ARIMA")) +
ylab("Giá") +
xlab("Thời gian (quan sát)") +
guides(colour = guide_legend(title = "Dữ liệu"))

# Một cách trực quan hóa khác, kết hợp cả train, test và forecast
# Tạo data frame để vẽ bằng ggplot
dates_all <- time(ts_stock_prices)
dates_train <- time(train_prices)
dates_test <- time(test_prices) # Cũng là ngày cho forecast_values$mean
plot_df <- data.frame(
Date = as.Date(date_decimal(as.numeric(dates_all))), # Chuyển đổi time object sang Date
Price = as.numeric(ts_stock_prices),
Type = "Dữ liệu lịch sử"
)
# Đánh dấu phần test
plot_df$Type[plot_df$Date %in% as.Date(date_decimal(as.numeric(dates_test)))] <- "Giá thực tế (Test)"
forecast_df <- data.frame(
Date = as.Date(date_decimal(as.numeric(time(forecast_values$mean)))),
Price = as.numeric(forecast_values$mean),
Lower_80 = as.numeric(forecast_values$lower[,1]), # 80% PI
Upper_80 = as.numeric(forecast_values$upper[,1]), # 80% PI
Lower_95 = as.numeric(forecast_values$lower[,2]), # 95% PI
Upper_95 = as.numeric(forecast_values$upper[,2]), # 95% PI
Type = "Dự báo ARIMA"
)
ggplot() +
geom_line(data = filter(plot_df, Type == "Dữ liệu lịch sử" & Date < min(forecast_df$Date)), aes(x = Date, y = Price, color = Type)) +
geom_line(data = filter(plot_df, Type == "Giá thực tế (Test)"), aes(x = Date, y = Price, color = Type)) +
geom_line(data = forecast_df, aes(x = Date, y = Price, color = Type), linetype = "dashed") +
geom_ribbon(data = forecast_df, aes(x = Date, ymin = Lower_95, ymax = Upper_95), fill = "skyblue", alpha = 0.3, inherit.aes = FALSE) +
geom_ribbon(data = forecast_df, aes(x = Date, ymin = Lower_80, ymax = Upper_80), fill = "steelblue", alpha = 0.4, inherit.aes = FALSE) +
scale_color_manual(values = c("Dữ liệu lịch sử" = "black", "Giá thực tế (Test)" = "blue", "Dự báo ARIMA" = "red")) +
labs(title = paste("Dự báo giá cổ phiếu", ticker, "và khoảng tin cậy"),
x = "Ngày", y = "Giá đã điều chỉnh", color = "Chú thích") +
theme_minimal()

# 9. Đánh giá độ chính xác trên tập test
# accuracy() so sánh giá trị dự báo (forecast_values$mean) với giá trị thực tế (test_prices)
accuracy_metrics <- accuracy(forecast_values$mean, test_prices)
cat("\nĐộ chính xác của mô hình trên tập test:\n")
##
## Độ chính xác của mô hình trên tập test:
print(accuracy_metrics)
## ME RMSE MAE MPE MAPE ACF1 Theil's U
## Test set -0.3566918 16.75987 13.2142 -0.7415582 6.030067 0.9556313 3.49383
# Các chỉ số quan trọng: RMSE, MAE, MAPE.
# MAPE có thể rất lớn nếu giá gần 0, nhưng với giá cổ phiếu thì thường ổn.
# 10. Thảo luận về kết quả
cat("\n**Lưu ý quan trọng:**\n")
##
## **Lưu ý quan trọng:**
cat("- Mô hình ARIMA dựa trên giả định rằng các mẫu hình trong quá khứ sẽ tiếp tục trong tương lai.\n")
## - Mô hình ARIMA dựa trên giả định rằng các mẫu hình trong quá khứ sẽ tiếp tục trong tương lai.
cat("- Thị trường chứng khoán bị ảnh hưởng bởi nhiều yếu tố (tin tức, kinh tế vĩ mô, tâm lý nhà đầu tư) mà ARIMA không nắm bắt được.\n")
## - Thị trường chứng khoán bị ảnh hưởng bởi nhiều yếu tố (tin tức, kinh tế vĩ mô, tâm lý nhà đầu tư) mà ARIMA không nắm bắt được.
cat("- Dự báo giá cổ phiếu rất khó khăn. Kết quả từ ARIMA nên được xem xét cẩn trọng và không nên coi là lời khuyên đầu tư.\n")
## - Dự báo giá cổ phiếu rất khó khăn. Kết quả từ ARIMA nên được xem xét cẩn trọng và không nên coi là lời khuyên đầu tư.
cat("- Nếu phần dư vẫn còn tự tương quan hoặc có hiện tượng phương sai thay đổi (volatility clustering), các mô hình phức tạp hơn như ARMA-GARCH có thể phù hợp hơn.\n")
## - Nếu phần dư vẫn còn tự tương quan hoặc có hiện tượng phương sai thay đổi (volatility clustering), các mô hình phức tạp hơn như ARMA-GARCH có thể phù hợp hơn.
# Lựa chọn mô hình hóa log returns (thay thế cho bước 5-9 nếu muốn)
# cat("\nThử mô hình hóa Log Returns:\n")
# train_log_returns_ts <- ts(train_log_returns) # Đảm bảo là ts object
# arima_model_returns <- auto.arima(train_log_returns_ts, seasonal=FALSE, stepwise=TRUE, approximation=FALSE, trace=FALSE)
# print(summary(arima_model_returns))
# checkresiduals(arima_model_returns)
#
# num_forecast_periods_returns <- length(test_log_returns)
# forecast_log_returns <- forecast(arima_model_returns, h=num_forecast_periods_returns)
#
# # Để đánh giá, bạn cần chuyển đổi dự báo log returns về lại giá
# # Điều này phức tạp hơn vì bạn cần giá cuối cùng của tập train để bắt đầu.
# last_train_price = as.numeric(tail(train_prices, 1))
# predicted_prices_from_returns = rep(0, num_forecast_periods_returns)
# predicted_prices_from_returns[1] = last_train_price * exp(forecast_log_returns$mean[1])
# for(i in 2:num_forecast_periods_returns) {
# predicted_prices_from_returns[i] = predicted_prices_from_returns[i-1] * exp(forecast_log_returns$mean[i])
# }
#
# accuracy_returns_model <- accuracy(predicted_prices_from_returns, test_prices)
# cat("\nĐộ chính xác của mô hình trên Log Returns (chuyển về giá) trên tập test:\n")
# print(accuracy_returns_model)
#
# autoplot(forecast_log_returns) + ggtitle("Dự báo Log Returns")
# plot(test_prices, col="blue", type='l', main="Giá thực tế (Test) vs Dự báo từ Log Returns")
# lines(time(test_prices), predicted_prices_from_returns, col="red")
# legend("topleft", legend=c("Thực tế", "Dự báo"), col=c("blue", "red"), lty=1)