Luận án Tiếp cận machine learning trong quản trị danh mục trên thị trường chứng khoán Việt Nam

Trong phương pháp này, các cửa sổ dữ liệu gồm 2 năm giao dịch lịch sử sẽ được sử dụng như các tập huấn luyện để xây dựng danh mục tối ưu. Trên mỗi tập huấn luyện, trước tiên, giá trị tham số phạt được xác định bằng phương pháp xác thực chéo. Giá trị này được đưa vào bài toán tối ưu Lasso và danh mục được xác định thông qua giải bài toán tối ưu này. Danh mục tối ưu được giữ cố định trong 21 ngày giao dịch tiếp theo để ghi nhận lợi suất ngoài mẫu. Sau đó, phương pháp cửa sổ cuộn được sử dụng để dịch các tập huấn luyện tiến lên phía trước, thêm vào giao dịch của 21 ngày vừa được tính lợi suất ngoài mẫu và bỏ đi 21 ngày giao dịch cũ nhất. Quá trình như vậy được thực hiện cho đến khi kết thúc giai đoạn xem xét. Dữ liệu được chia làm 2 phần: dữ liệu huấn luyện và dữ liệu kiểm tra. Dữ liệu huấn luyện sẽ là một cửa sổ gồm hai năm giao dịch trên thị trường (498 quan sát) và dữ liệu kiểm tra là một tháng giao dịch trên thị trường (21 quan sát) kế tiếp. Việc lựa chọn tập huấn luyện và tập kiểm tra được dựa trên các nghiên cứu của Husmann và cộng sự (2022), Fastrich và cộng sự (2015). Với mỗi giai đoạn, quá trình đầu tư được thực hiện như sau: Bước 1: giải bài toán tối ưu Lasso cho tập huấn luyện (498 ngày), thu được trọng số tương ứng, ký hiệu là w1. Sử dụng trọng số cho các chuỗi cổ phiếu cho 21 ngày tiếp theo, từ đó tính được lợi suất theo ngày cho danh mục cho 21 ngày này, ký hiệu chuỗi lợi suất cho danh mục là L1. Bước 2: dịch chuyển cửa sổ tiến thêm 21 ngày (loại bỏ 21 quan sát ban đầu, bổ sung 21 quan sát tiếp theo). Giải bài toán tối ưu Lasso trên cửa sổ mới này, thu được trọng số w2. Sử dụng trọng số này tính lợi suất theo ngày của danh mục mới cho 21 ngày tiếp theo, ký hiệu chuỗi lợi suất này là L2. Bước N: cứ tiếp tục cho đến hết thời kỳ nghiên cứu, thu được chuỗi lợi suất LN Kết hợp các chuỗi L1, L2, , LN để thu được chuỗi lợi suất của phương pháp từ ngày bắt đầu kiểm tra tới ngày cuối cùng của giai đoạn xem xét. Xác định tham số

pdf148 trang | Chia sẻ: Kim Linh 2 | Ngày: 09/11/2024 | Lượt xem: 45 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Luận án Tiếp cận machine learning trong quản trị danh mục trên thị trường chứng khoán Việt Nam, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
83. Scott-Ram (2000), 'Managing Portfolio Risk for Periods of Stress', World Gold Council, Gold portfolio letter 11. 84. Sen J., S. Mondal, and G. Nath (2021), 'Robust Portfolio Design and Stock Price Prediction Using an Optimized LSTM Model', 2021 IEEE 18th India Council International Conference (INDICON), 1-6. 85. Serraino Gaia and Stanislav Uryasev (2013), 'Conditional Value-at-Risk (CVaR)', Encyclopedia of Operations Research and Management Science, Saul I. Gass và Michael C. Fu, Springer US, Boston, MA, 258-266. 86. Sharpe William F. (1963), 'A Simplified Model for Portfolio Analysis', Management Science, 9(2), 277-293. 87. Strong R.A. (2009), Portfolio construction, management and protection, 5th, South-Western, Cengage Learning. 88. Szczepocki Piotr (2019), 'Clustering companies listed on the Warsaw Stock Exchange according to time-varying beta', Econometrics, 23, 63-79. 89. Ta Bao Q., Vu T. Huynh, Khai Q. H. Nguyen, Phung N. Nguyen, and Binh H. Ho (2022), 'Maximal Predictability Portfolio Optimization Model and Applications to Vietnam Stock Market', Credible Asset Allocation, Optimal Transport Methods, and Related Topics, Cham, 559-578. 90. Ta Van-Dai, CHUAN-MING Liu, and Direselign Addis Tadesse (2020), 'Portfolio Optimization-Based Stock Prediction Using Long-Short Term Memory Network in Quantitative Trading', Applied Sciences, 10(2), 437. 91. Tibshirani Robert (1996), 'Regression Shrinkage and Selection via the Lasso', Journal of the Royal Statistical Society. Series B (Methodological), 58(1), 267- 288. 92. Tibshirani Robert, Guenther Walther, and Trevor Hastie (2001), 'Estimating the Number of Clusters in a Data Set Via the Gap Statistic', Journal of the Royal Statistical Society Series B, 63, 411-423. 124 93. Tibshirani Ryan and Larry Wasserman (2017), 'Sparsity, the lasso, and friends', Lecture notes from “Statistical Machine Learning,” Carnegie Mellon University, Spring. 94. Tikhonov Andrei Nikolaevich (1963), 'On the regularization of ill-posed problems', Doklady Akademii Nauk, 49-52. 95. Tobin J. (1958), 'Liquidity Preference as Behavior Towards Risk', The Review of Economic Studies, 25(2), 65-86. 96. Tom Mitchell (1997), 'Machine Learning', Publisher: McGraw Hill. 97. Treynor Jack (1965), 'How to rate management of investment funds'. 98. Truong Loc Dong, H. Swint Friday, and Tran My Ngo (2023), 'Market Reaction to Delisting Announcements in Frontier Markets: Evidence from the Vietnam Stock Market', Risks, 11(11), 201. 99. Võ Thị Quý and Nguyễn Minh Cao Hoàng (2011), 'Measuring Risk Tolerance And Establishing An Optimal Portfolio For Individual Investors In HOSE', Journal of Economic Development, June 2011, 33-38. 100. Wang Wuyu, Weizi Li, Ning Zhang, and Kecheng Liu (2020), 'Portfolio formation with preselection using deep learning from long-term financial data', Expert Systems with Applications, 143, 113042. 101. Ward Joe H. (1963), 'Hierarchical Grouping to Optimize an Objective Function', Journal of the American Statistical Association, 58(301), 236-244. 102. Warren Liao T. (2005), 'Clustering of time series data—a survey', Pattern Recognition, 38(11), 1857-1874. 103. Wu Fanyou, Rado Gazo, Eva Haviarova, and Bedrich Benes (2021), 'Wood identification based on longitudinal section images by using deep learning', Wood Science and Technology, 55, 1-11. 104. Yue Shihong, Xiuxiu Wang, and Miaomiao Wei (2008), 'Application of two- order difference to gap statistic', Transactions of Tianjin University, 14(3), 217- 221. 105. Zhang Yong, Yongbin Su, Weilong Liu, and Xingyu Yang (2022), 'Portfolio Optimization with Lstm-Based Return and Risk Information', Available at SSRN 4215299, Preprint. 106. Zhang Yuanyuan, Xiang Li, and Sini Guo (2018), 'Portfolio selection problems with Markowitz’s mean–variance framework: a review of literature', Fuzzy Optimization and Decision Making, 17(2), 125-158. 125 PHỤ LỤC Phụ lục 1. Code Python chạy kết quả thực nghiệm cho phương pháp Phân cụm chuỗi thời gian và Chính quy hóa Lasso # %% import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt import matplotlib.dates as mdates from datetime import date ### import riskfolio as rp ### from scipy.cluster.hierarchy import fcluster import scipy.cluster.hierarchy as hierarchy from scipy.spatial.distance import pdist ### from arch import arch_model ### import os import warnings warnings.filterwarnings("ignore") pd.options.display.float_format = '{:.5}'.format ### from cycler import cycler ### ## Hàm tính lợi suất def calculate_returns(df): return np.log1p(df.pct_change()).iloc[1:] # Hàm tính lợi suất tích lũy theo thời gian def calculate_portfolio_return(returns): portfolio_return = [0] for r in returns: value = portfolio_return[-1] + r portfolio_return.append(value) return portfolio_return[:-1] # Đặt thư mục mặc định os.chdir('D:\\ NCS\\Code_Final') # %% # Ngày bắt đầu và kết thúc giai đoạn first_day = '2013-01-02' last_day = '2019-12-31' # Các ngày không giao dịch dropdays = ['2018-01-23', '2018-01-24'] # Số ngày lịch sử - train N_hist = 496 # Số ngày kiểm tra - test N_test = 21 # Nhập dữ liệu dạng thô df_raw = pd.read_csv("Data\\HOSE_2008_2022_vnstock.csv") 126 df_raw["TradingDate"] = pd.to_datetime(df_raw["TradingDate"], format="%m/%d/%Y") ### df_raw_period = df_raw[(df_raw['TradingDate'] >= first_day) & (df_raw['TradingDate'] <= last_day)] df_raw_period = df_raw_period.set_index('TradingDate', drop=True) days_max = df_raw_period.groupby('Stock')['Close'].count().value_counts().idxmax() # %% df_raw_frame = df_raw_period.groupby('Stock')['Close'].apply(calculate_returns).to_frame() # Tính lợi suất các mã trong giai đoạn theo giá đóng cửa df_Returns = pd.pivot_table(df_raw_frame, values = 'Close', columns = 'TradingDate', index = 'Stock') df = df_Returns.T df = df.drop(dropdays, axis = 0) # %% # Chia các tập train - test def partition_data(df, n_train, n_test): train_set = [] test_set = [] i = 0 while(i <= df.shape[0] - n_train - 1): df_temp = df.iloc[i: i + min(n_train + n_test, df.shape[0] - i)] df_days_nan = df_temp.isna().sum(axis = 0) lst = np.where(df_days_nan == 0) df_temp2 = df_temp.iloc[:, np.array(lst)[0]] print(df_temp2.head()) column_list = [] for column in df_temp2.columns: value_counts = df_temp2[column].value_counts() column_list.append(column) df_temp3 = df_temp2[column_list] df_train_temp = df_temp3.iloc[: n_train] df_test_temp = df_temp3.iloc[n_train:] train_set.append(df_train_temp) test_set.append(df_test_temp) i += n_test return train_set, test_set # %% df_data = partition_data(df, N_hist, N_test) # %% # Nhập dữ liệu VN-Index df_VNIndex_raw = pd.read_csv("Data\VNIndex_2008_2022.csv") df_VNIndex_raw['TradingDate'] = pd.to_datetime(df_VNIndex_raw['TradingDate']) df_VNIndex_raw = df_VNIndex_raw.set_index("TradingDate") df_Returns_VNIndex = calculate_returns(df_VNIndex_raw) df_VNIndex_raw = df_VNIndex_raw.loc[first_day:last_day] # %% # Lãi suất phi rủi ro df_rf = pd.read_csv("Data/TraiPhieu10Nam.csv") df_rf["Date"] = pd.to_datetime(df_rf["Date"],format= "%d/%m/%Y") df_rf["Date"] = pd.to_datetime(df_rf["Date"],format= "%Y-%m-%d") 127 df_rf = df_rf.set_index("Date") df_rf = df_rf.sort_index() df_rf_last = df_rf['Last'] # %% # Tính lãi suất phi rủi ro đại diện trong giai đoạn def get_rf(returns): return df_rf_last.loc[returns.index[0]:returns.index[- 1]].mean()/(365*100) # def sharpe_ratio(returns): rf = get_rf(returns) return ((returns - rf).mean()) / ((returns - rf).std()) # %% def get_lowerret(returns): return df_Returns_VNIndex.loc[returns.index[0]:returns.index[-1]].mean() # %% def get_clusters(returns, label): stocks = returns.columns return [stocks[label == name] for name in np.unique(label)] %% def get_portfolio(cluster, returns, k = 2): # mỗi cụm lấy k asset có sharpe lớn nhất portflolio = [] for clus in cluster: portflolio += list((sharpe_ratio(returns[clus])).nlargest(k).index) return portflolio %% def optimize(df_returns): print(df_returns.index[-1]) # Building the portfolio object method_mu='hist' # Method to estimate expected returns method_cov='hist' # Method to estimate covariance matrix model='Classic' rm = 'MV' # Risk measure used, this time will be variance obj = 'MinRisk' # Objective function hist = True # Use historical scenarios for risk measures rf = get_rf(df_returns) # Risk free rate l = 0 # Risk aversion factor port = rp.Portfolio(returns = df_returns) lower_ret = df_Returns_VNIndex.loc[df_returns.index].mean().values[0] port.lowerret = lower_ret port.assets_stats(method_mu = method_mu, method_cov = method_cov, d = 0.94) # Estimate optimal portfolio: w = port.optimization(model = model, rm = rm, obj = obj, rf = rf, l = l, hist = hist) return w # %% def hierarchical_clustering(corr, linkage, max_k): # hierarchcial clustering # corr is matrix correlation dist = np.sqrt((1 - corr).round(5) / 2) dist = pd.DataFrame(dist, columns=corr.columns, index=corr.index) 128 p_dist = pdist(dist) clustering = hierarchy.linkage(p_dist, method=linkage) # cluster levels over from 1 to N-1 clusters cluster_lvls = pd.DataFrame(hierarchy.cut_tree(clustering), index=corr.columns) num_k = cluster_lvls.columns # save column with number of clusters cluster_lvls = cluster_lvls.iloc[:, ::-1] # reverse order to start with 1 cluster cluster_lvls.columns = num_k # set columns to number of cluster W_list = [] # get within-cluster dissimilarity for each k for k in range(min(len(cluster_lvls.columns), max_k)): level = cluster_lvls.iloc[:,k] # get k clusters D_list = [] # within-cluster distance list for i in range(np.max(level.unique())+1): cluster = level.loc[level == i] # cluster_dist = dist.loc[cluster.index, cluster.index] # get distance cluster_pdist = pdist(cluster_dist) # flatten and transform D = np.nan_to_num(cluster_pdist.std()) D_list.append(D) # append to list W_k = np.sum(D_list) W_list.append(W_k) W_list = pd.Series(W_list) n = corr.shape[0] limit_k = int(min(max_k, np.sqrt(n))) gaps = W_list.shift(-2) + W_list - 2*W_list.shift(-1) gaps = gaps[:limit_k] k = gaps.idxmax() + 2 k = max(k, 3) return clustering, k ### def fit_hierarchical(returns_set): N = len(returns_set) labels =[] corr_returns = [returns_set[i].corr() for i in range(N)] for i in range(N): Z, t = hierarchical_clustering(corr_returns[i], linkage = 'complete', max_k = 10) labels_h = fcluster(Z, t = t, criterion = 'maxclust') labels.append(labels_h) return labels # %% def plot_df(df_input, y_label = ''): ## Set the rcParams with color or linestyle plt.rc('axes', prop_cycle=(cycler('color', ['r', 'y', 'b', 'g', 'c', 'm']) + cycler('linestyle', ['-', '--', ':', '-.', '-', '--']))) fig, ax = plt.subplots(figsize=(16,8)) params = {'axes.labelsize': 20, 'axes.titlesize': 20, 'legend.fontsize': 18} ax.xaxis.set_major_locator(mdates.MonthLocator(bymonthday = 1, interval=3)) 129 ax.xaxis.set_major_formatter(mdates.DateFormatter('%b\n%Y')) plt.plot(df_input.index, df_input) plt.xlabel(xlabel='Trading Date') plt.ylabel(ylabel = y_label) plt.legend(df_input.columns) plt.tight_layout() plt.show() # %% def set_Full_Weights(df, w0): weights_Full = pd.DataFrame() weights_Full.index = df.columns weights_Full = weights_Full.join(w0) weights_Full = weights_Full.fillna(0) return weights_Full # %% def get_result_MV_NSS(df_returns): returns_train = df_returns[0] returns_test = df_returns[1] N = len(returns_train) weights_MV = [optimize(returns_train[i]) for i in range(N)] returns_port_MV = pd.concat([returns_test[i] @ weights_MV[i] for i in range(N)]) return returns_port_MV, weights_MV # %% df_returns_port_MV, df_w_MV = get_result_MV_NSS(df_data) # %% def get_result(df_returns, labels): returns_train = df_returns[0] returns_test = df_returns[1] N = len(returns_train) clusters = [get_clusters(returns_train[i], labels[i]) for i in range(N)] portfolios = [get_portfolio(clusters[i], returns_train[i]) for i in range(N)] weights = [optimize(returns_train[i][portfolios[i]]) for i in range(N)] df_returns_port = pd.concat([returns_test[i][portfolios[i]]@weights[i] for i in range(N)]) return df_returns_port, weights # %% df_returns_port_1TS , df_w_1TS = get_result(df_data, labels_1TS) # Các hàm với 2 chuỗi thời gian # %% def d_corr(x,y): return np.sqrt(2*(1-np.corrcoef(x, y)[0, 1])) %% def get_volatility_matrix(returns): vols = [] for col in returns.columns: model = arch_model(returns[[col]], mean='Zero', vol='GARCH', p=1, q=1) model_fitted = model.fit(disp = 'off') vols.append(model_fitted.conditional_volatility) return np.array(vols) %% 130 def distance_matrix(returns, vol_arr): returns_val = returns.values.T n_samples = returns_val.shape[0] X_distance = np.zeros((n_samples, n_samples)) for i in range(n_samples): for j in range(i+1, n_samples): d1 = d_corr(returns_val[i], returns_val[j]) d2 = d_corr(vol_arr[i], vol_arr[j]) if np.isnan(d2): print("there is a nan in d_corr of volalities") d2=0 d = 0.5*d1 + 0.5*d2 X_distance[i][j] = d X_distance[j][i] = d return X_distance # %% def hierarchical_clustering_MTS(returns, linkage, max_k): # corr is matrix correlation corr = returns.corr() volatility_matrices = get_volatility_matrix(returns) # tính distance_matrices distance_matrices = distance_matrix(returns, volatility_matrices) dist = pd.DataFrame(distance_matrices, columns=corr.columns, index=corr.index) p_dist = pdist(dist) clustering = hierarchy.linkage(p_dist, method=linkage) # cluster levels over from 1 to N-1 clusters cluster_lvls = pd.DataFrame(hierarchy.cut_tree(clustering), index=corr.columns) num_k = cluster_lvls.columns # save column with number of clusters cluster_lvls = cluster_lvls.iloc[:, ::-1] cluster_lvls.columns = num_k # set columns to number of cluster W_list = [] # get within-cluster dissimilarity for each k for k in range(min(len(cluster_lvls.columns), max_k)): level = cluster_lvls.iloc[:,k] # get k clusters D_list = [] # within-cluster distance list for i in range(np.max(level.unique())+1): cluster = level.loc[level == i] # cluster_dist = dist.loc[cluster.index, cluster.index] cluster_pdist = pdist(cluster_dist) D = np.nan_to_num(cluster_pdist.std()) D_list.append(D) # append to list W_k = np.sum(D_list) W_list.append(W_k) W_list = pd.Series(W_list) n = corr.shape[0] limit_k = int(min(max_k, np.sqrt(n))) gaps = W_list.shift(-2) + W_list - 2*W_list.shift(-1) gaps = gaps[:limit_k] k = gaps.idxmax() + 2 k = max(k, 3) return clustering, k # %% 131 def fit_hierarchical_MTS(returns_train): N = len(returns_train) labels =[] for i in range(N): print('Tap thu', i) Z, t = hierarchical_clustering_MTS(returns_train[i], linkage = 'complete', max_k=10) labels_h=fcluster(Z, t=t, criterion='maxclust') labels.append(labels_h) return labels labels_2TS = fit_hierarchical_MTS(df_data[0]) # %% df_returns_port_2TS, df_w_2TS = get_result(df_data, labels_2TS) # Sharpe ratio def sharpe(returns): rf = get_rf(returns) return ((returns - rf).mean() / (returns - rf).std()) # Value-at-risk def var(returns, alpha): sorted_returns = np.sort(returns) index = int(alpha * len(sorted_returns)) return abs(sorted_returns[index]) # Expected Shortfall (cVaR) def cvar(returns, alpha): sorted_returns = np.sort(returns) index = int(alpha * len(sorted_returns)) sum_var = sorted_returns[0] # Calculate the total VaR beyond alpha for i in range(1, index): sum_var += sorted_returns[i] return abs(sum_var / index) # Return the average VaR # MV cho phép bán khống def port_MV(df): rho_MV = float(df_Returns_VNIndex.loc[df.index[0]:df.index[- 1]].mean(0).iloc[0]) w_MV = cp.Variable(df.shape[1]) ACov = np.cov(df.T) p_cov = cp.psd_wrap(ACov) risk_MV = cp.quad_form(w_MV, p_cov) cons_MV = [cp.sum(w_MV) == 1, df.mean(0).to_numpy() @ w_MV == rho_MV] prob_MV = cp.Problem(cp.Minimize(risk_MV), cons_MV) prob_MV.solve() return w_MV.value ### def get_result_MV(df_returns): returns_train = df_returns[0] returns_test = df_returns[1] N = len(returns_train) weights = [port_MV(returns_train[i]) for i in range(N)] df_returns_port = pd.concat([returns_test[i] @ weights[i] for i in range(N)]) return df_returns_port, weights # %% df_returns_MV = get_result_MV(df_data)[0] # Các giá trị lựa chọn cho tham số phạt 132 gamma_vals = [10**(n) for n in np.arange(2, -5, -0.5)] #### LASSO N_Periods = 12 # Số lần thực hiện trong tập train ### def loss_fn(A, b, w): return cp.norm2(b - A @ w)**2 def regularizer(w): return cp.norm1(w) def objective_Lasso_fn(A, b, w, lambd): return loss_fn(A, b, w) + lambd * regularizer(w) ### Cross Validation def cross_val(df): N_train = df.shape[0] k_start = 0 k_end = N_train - N_Periods*N_test - 1 # df_std_port_AllSample = pd.DataFrame() while k_end < N_train - 1: A = df.iloc[k_start : k_end] n_A = A.shape[0] p = A.shape[1] A_test = df.iloc[k_end : k_end + N_test] # Tập test trong mẫu ## RVNIndex_Lasso = df_Returns_VNIndex.iloc[k_start : k_end] AveReturn_Lasso = RVNIndex_Lasso.mean() rho_insample = float(AveReturn_Lasso) b_insample = [rho_insample]*n_A w_insample = cp.Variable(p) gamma_insample = cp.Parameter(nonneg=True) objective_insample = objective_Lasso_fn(A.to_numpy(), np.transpose(b_insample), w_insample, gamma_insample) constraints_insample = [A.to_numpy().mean(0) @ w_insample == rho_insample, cp.sum(w_insample) == 1] lst_std_port = [] df_std_port_InSample = pd.DataFrame() for val in gamma_vals: prob_insample = cp.Problem(cp.Minimize(objective_insample), constraints_insample) gamma_insample.value = val prob_insample.solve(solver=cp.MOSEK) Return_test = A_test @ w_insample.value lst_std_port.append(Return_test.std()) df_std_port_InSample = pd.DataFrame(lst_std_port) df_std_port_AllSample = pd.concat([df_std_port_AllSample, df_std_port_InSample], axis=1, ignore_index=True) k_start += N_test k_end += N_test id_ld_std = df_std_port_AllSample.mean(1) id_ld = id_ld_std.argmin() return id_ld # %% def get_port_Lasso(df): N_train_sample, p = df.shape rho_sample = df_Returns_VNIndex.loc[df.index].mean(0) 133 b_sample = [rho_sample]*N_train_sample w_sample = cp.Variable(p) gamma_sample = cp.Parameter(nonneg=True) id_ld = cross_val(df) gamma_sample.value = gamma_vals[id_ld] objective_sample = objective_Lasso_fn(df.to_numpy(), np.array(b_sample).reshape(N_train_sample, ), w_sample, gamma_sample) constraints_sample = [df.to_numpy().mean(0) @ w_sample == rho_sample, cp.sum(w_sample) == 1] prob_sample = cp.Problem(cp.Minimize(objective_sample), constraints_sample) prob_sample.solve(solver=cp.MOSEK) return w_sample.value # %% def get_result_Lasso(df_returns): returns_train = df_returns[0] returns_test = df_returns[1] N = len(returns_train) weights = [get_port_Lasso(returns_train[i]) for i in range(N)] df_returns_port = pd.concat([returns_test[i] @ weights[i] for i in range(N)]) return df_returns_port, weights df_return_Lasso = get_result_Lasso(df_data)[0] 134 Phụ lục 2. Code Python chạy kết quả thực nghiệm cho phương pháp LSTM # %% LSTM days_max = df_raw_period.groupby('Stock')['Open'].count().value_counts().idxmax() lst_days = (df_raw_period.groupby('Stock')['Open'].count() == days_max) lst_stocks_fulldays = lst_days.index[np.where(lst_days == True)] dta_period = df_raw_period[df_raw_period['Stock'].isin(lst_stocks_fulldays)] # %% def SMA(df, k = 10): sma = df.rolling(k).mean() return sma ## def WMA(df, k=10): weights = np.arange(k, 0, step=-1) wma = df.rolling(k).apply(lambda x: np.dot(x, weights)/weights.sum(), raw=True) return wma # def computeRSI (data, time_window): diff = data.diff(1).dropna() up_chg = 0 * diff down_chg = 0 * diff up_chg[diff > 0] = diff[ diff>0 ] down_chg[diff < 0] = diff[ diff < 0 ] up_chg_avg = up_chg.ewm(com=time_window-1, min_periods=time_window).mean() down_chg_avg = down_chg.ewm(com=time_window-1 , min_periods=time_window).mean() rs = np.abs(up_chg_avg/down_chg_avg) rsi = 100 - 100/(1+rs) return rsi ### def stochastic(data, k_window, d_window, window): min_val = data.rolling(window=window, center=False).min() max_val = data.rolling(window=window, center=False).max() stoch = ( (data - min_val) / (max_val - min_val) ) * 100 K = stoch.rolling(window=k_window, center=False).mean() D = K.rolling(window=d_window, center=False).mean() return K, D ### from sklearn.preprocessing import StandardScaler, MinMaxScaler scaler = StandardScaler() scaler_target = MinMaxScaler() ######## def df_to_windowed_df(df_input): ### Tạo dữ liệu đầu vào cho LSTM Y = df_input['Close'] Y_returns = calculate_returns(Y) X_history = df_input.copy() df_SMA = SMA(df_input['Close'], k = 10) df_WMA = WMA(df_input['Close'], k = 10) df_RSI = computeRSI(df_input['Close'], 14) 135 df_K, df_D = stochastic(df_input['Close'], 3, 3, 14) ### df_K = df_K.dropna() df_D = df_D.dropna() dates = df_D.index Y_returns = Y_returns[dates] df_K = df_K[dates] X_history = X_history.loc[dates] df_SMA = df_SMA[dates] df_WMA = df_WMA[dates] df_RSI = df_RSI[dates] df_data = pd.concat([X_history, df_SMA, df_WMA, df_RSI, df_K, df_D, Y_returns], axis=1, ignore_index=True) df_data.dropna(inplace=True) return df_data # %% # Chuyển đổi dữ liệu thành dạng phù hợp cho mô hình: def create_dataset(dataset, lead=1): n_rows = dataset.shape[0] dates = dataset.index df_as_np = dataset.to_numpy() q_60 = int(len(dates) * .6) q_80 = int(len(dates) * .8) middle_matrix = df_as_np[:, 0:-1] middle_matrix_train, middle_matrix_val, middle_matrix_test = middle_matrix[:q_60], middle_matrix[q_60:q_80], middle_matrix[q_80:] # middle_matrix_train_scaled = scaler.fit_transform(middle_matrix_train) middle_matrix_val_scaled, middle_matrix_test_scaled = scaler.transform(middle_matrix_val), scaler.transform(middle_matrix_test) # target = df_as_np[:, -1] target_train, target_val, target_test = target[:q_60], target[q_60:q_80], target[q_80:] target_train_scaled = scaler_target.fit_transform(target_train.reshape(-1, 1)) target_val_scaled, target_test_scaled = scaler_target.transform(target_val.reshape(-1, 1)), scaler_target.transform(target_test.reshape(-1, 1)) # data_scaled = np.concatenate((middle_matrix_train_scaled, middle_matrix_val_scaled, middle_matrix_test_scaled), axis=0) target_scaled = np.concatenate((target_train_scaled, target_val_scaled, target_test_scaled), axis=0) X, y = [], [] for i in range(n_rows - lead): a = data_scaled[i:(i+lead), :] X.append(a) y.append(target_scaled[i+lead]) return dates, np.array(X), np.array(y) ### # %% from tensorflow.keras.models import Sequential from tensorflow.keras.optimizers import Adam 136 from tensorflow.keras import layers from keras.layers import Dense, LSTM from sklearn.model_selection import GridSearchCV from scikeras.wrappers import KerasClassifier, KerasRegressor from keras.callbacks import EarlyStopping from sklearn.metrics import mean_squared_error # %% def create_model(optimizer='adam', neurons=50): model = Sequential() model.add(LSTM(neurons, input_shape=(X_train.shape[1], X_train.shape[2]))) model.add(Dense(1)) model.compile(loss='mse', optimizer=optimizer) return model # %% # Tính lợi suất các mã trong giai đoạn theo giá đóng cửa df_returns_period = dta_period.groupby('Stock')['Close'].apply(calculate_returns) # Chuyển về dạng wide df_returns_period = df_returns_period.unstack().T # %% from sklearn import metrics from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score, explained_variance_score from sklearn.metrics import mean_absolute_percentage_error # %% Score_all = dict() df_all = pd.DataFrame() run_step = 1 ##### for stock_name in lst_stocks_fulldays: df_Stock = dta_period[dta_period['Stock'] == stock_name].copy() df_Stock = df_Stock.drop(['Stock'], axis=1) windowed_df = df_to_windowed_df(df_Stock) dates, X, y = create_dataset(windowed_df, lead = 10) # q_60 = int(len(dates) * .6) q_80 = int(len(dates) * .8) ### dates_train, X_train, y_train = dates[:q_60], X[:q_60], y[:q_60] dates_val, X_val, y_val = dates[q_60:q_80], X[q_60:q_80], y[q_60:q_80] dates_test, X_test, y_test = dates[q_80:], X[q_80:], y[q_80:] model = create_model() model.compile(loss='mse', optimizer=Adam(learning_rate=0.001), metrics=['mean_absolute_error']) es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=10) history = model.fit(X_train, y_train, callbacks=[es], validation_data=(X_val, y_val), batch_size=32, epochs=100, verbose = 0) ### y_pred_norm = model.predict(X_test, batch_size = 4, verbose=0) y_pred_val = model.predict(X_val, batch_size = 4, verbose = 0) # Chuyển đổi ngược các giá trị dự đoán và giá trị thực tế về dạng gốc y_pred_orig = scaler_target.inverse_transform(y_pred_norm.reshape(-1, 1)).flatten() 137 y_test_orig = scaler_target.inverse_transform(y_test.reshape(-1, 1)).flatten() y_val_orig = scaler_target.inverse_transform(y_pred_val.reshape(-1, 1)).flatten() # Tính toán Mean Squared Error (MSE) mse = mean_squared_error(y_test, y_pred_norm) y_pred_valtest = y_val_orig.tolist() + y_pred_orig.tolist() df_all = pd.concat([df_all, pd.DataFrame({str(stock_name) : y_pred_valtest})], axis=1) # Tính toán độ lỗi trung bình tuyệt đối phần trăm (MAPE) mape = mean_absolute_percentage_error(y_test_orig, y_pred_orig) # Tính toán Mean Absolute Error (MAE) mae = mean_absolute_error(y_test_orig, y_pred_orig) # Tính toán Coefficient of determination (R2) r2 = r2_score(y_test_orig, y_pred_orig) # Tính toán Explained Variance Score (EVS) evs = explained_variance_score(y_test_orig, y_pred_orig) Score_all[stock_name] = [mape, mse, mae, r2, evs] # %% df_all.set_index(dates[q_60 + 10:], inplace = True) dates_train.shape, dates_val.shape, dates_test.shape # %% na_columns = np.where(df_all.isna().apply(sum, axis = 0) > 0) df_all.drop(columns = df_all.columns[na_columns], inplace = True) # %% df_pred = df_all.copy() df_real = df_returns_period.loc[df_all.index] df_real = df_real[df_pred.keys()] df_real_pred = df_real - df_pred df_real_test = df_real.loc[dates_test[0]:] df_pred_val = df_pred.loc[:dates_val[-1]] df_pred_test = df_pred.loc[dates_test[0]:] # %% df_rp_val = df_real_pred.loc[:dates_val[-1]] df_rp_test = df_real_pred.loc[dates_test[0]:] # %% def create_Vt(df_input): Tv = df_input.shape[0] Vt = np.zeros((df_input.shape[1], df_input.shape[1])) for i in range(Tv): Vt += np.outer(df_input.iloc[i], df_input.iloc[i].T) return (1/Tv)*Vt # %% import cvxpy as cp # %% ### Các tham số chung cho bài toán tối ưu method_mu='hist' # Method to estimate expected returns method_cov='hist' # Method to estimate covariance matrix model='Classic' rm = 'MV' # Risk measure used obj = 'MinRisk' # Objective function hist = True # Use historical scenarios for risk measures # %% 138 Returns_LSTM = [] Returns_MV = [] # weights_LSTM = [] weights_MV = {} N = df_pred_test.shape[1] Vt = create_Vt(df_rp_val) N_val = df_pred_val.shape[0] # for i in range(df_pred_test.shape[0]): rmax, rmin = max(df_pred_test.iloc[i]), min(df_pred_test.iloc[i]) r0 = 0.9*(rmax - rmin) + rmin lower_ret = df_RVNIndex.loc[:df_real_test.index[i]].tail(N_val).mean().values[0] w_LSTM = cp.Variable(N) Vt_edited = cp.psd_wrap(Vt) risk_LSTM = cp.quad_form(w_LSTM, Vt_edited) cons_LSTM = [w_LSTM >= 0, cp.sum(w_LSTM) == 1, w_LSTM @ cp.vec(df_pred_test.iloc[i]) >= r0] prob_LSTM = cp.Problem(cp.Minimize(risk_LSTM), cons_LSTM) prob_LSTM.solve(solver=cp.ECOS, verbose = False) Returns_LSTM.append(df_real_test.iloc[i] @ w_LSTM.value) print(df_pred_test.index[i]) Vt = (1/2)*(Vt + np.outer(df_rp_test.iloc[i], df_rp_test.iloc[i].T)) weights_LSTM.append(w_LSTM.value) #### MV dữ liệu lịch sử risk_free_rate = get_rf(df_pred_test.loc[:df_real_test.index[i]]) rf =risk_free_rate # Risk free rate print(rf) l = 0 # Risk aversion factor port_MV_model = rp.Portfolio(returns= df_real.loc[:df_real_test.index[i]].tail(N_val)) port_MV_model.lowerret = lower_ret port_MV_model.assets_stats(method_mu=method_mu, method_cov=method_cov, d=0.94) # Estimate optimal portfolio: w_MV = port_MV_model.optimization(model=model, rm=rm, obj=obj, rf=rf, l=l, hist=hist) Returns_MV.append(df_real_test.iloc[i] @ w_MV.values) weights_MV[i] = w_MV.values.flatten() # %% df_benchmark = pd.DataFrame() df_benchmark['MV_LSTM'] = Returns_LSTM df_benchmark['MV'] = np.array(Returns_MV).reshape(1, -1).flatten() df_benchmark['VNIndex'] = df_RVNIndex.loc[df_real_test.index].values df_benchmark.set_index(df_real_test.index, inplace = True)

Các file đính kèm theo tài liệu này:

  • pdfluan_an_tiep_can_machine_learning_trong_quan_tri_danh_muc_tr.pdf
  • pdfCV dang bo ngay 19 thang 8.pdf
  • docxLA_BuiQuocHoan_E.docx
  • pdfLA_BuiQuocHoan_Sum.pdf
  • pdfLA_BuiQuocHoan_TT.pdf
  • docxLA_BuiQuocHoan_V.docx
  • pdfQD CS Quoc Hoan.pdf
Luận văn liên quan