Trong chương này, chúng ta đo lường mức độ thị trường cổ phiếu Việt Nam hội nhập với, hoặc bị phân khúc khỏi, các thị trường vốn toàn cầu. Chúng ta xây dựng nhiều thước đo hội nhập, gồm các thước đo dựa trên tương quan, dựa trên nhân tố và dựa trên sai số định giá, theo dõi diễn biến của chúng qua tiến trình tự do hóa của Việt Nam, và định lượng chi phí của phân khúc thị trường đối với các doanh nghiệp Việt Nam.
Một thị trường hội nhập hoàn toàn khi tài sản của nó được định giá bởi một hệ số chiết khấu ngẫu nhiên toàn cầu: phần bù rủi ro chỉ phản ánh mức độ phơi nhiễm với các nhân tố rủi ro toàn cầu, và các dòng tiền giống hệt nhau đòi hỏi cùng một mức lợi nhuận kỳ vọng bất kể nơi đặt trụ sở của tổ chức phát hành. Một thị trường phân khúc hoàn toàn khi chỉ các nhân tố rủi ro trong nước quyết định giá cả, và sự đánh đổi giữa rủi ro và lợi nhuận của quốc gia đó độc lập với phần còn lại của thế giới. Thực tế nằm đâu đó giữa hai cực này, và vị trí ấy thay đổi theo thời gian.
Việt Nam là một trường hợp đặc biệt thú vị. Sàn giao dịch chứng khoán của Việt Nam mở cửa vào tháng 7 năm 2000 với những hạn chế nặng nề đối với sự tham gia của nhà đầu tư nước ngoài. Giới hạn sở hữu nước ngoài (ban đầu là 20%, nâng lên 30% năm 2003, 49% năm 2015, và được bãi bỏ có chọn lọc đối với một số doanh nghiệp) đã dần được nới lỏng. Việt Nam gia nhập WTO năm 2007. FTSE Russell đã nâng hạng Việt Nam từ “unclassified” lên “secondary emerging” trong chỉ số frontier của mình vào năm 2018 và vẫn đang xem xét các đợt nâng hạng tiếp theo. Mỗi sự kiện này đều có thể đã làm dịch chuyển mức độ hội nhập.
Bekaert và Harvey (1995) và Bekaert và Harvey (2002) thiết lập khung phân tích hiện đại để đo lường hội nhập biến đổi theo thời gian. Errunza và Losq (1985) phát triển mô hình “phân khúc nhẹ” (mild segmentation), trong đó nhà đầu tư nước ngoài đối mặt với các rào cản nhưng vẫn có thể sao chép một phần lợi nhuận của thị trường mới nổi thông qua các chứng khoán toàn cầu. Pukthuanthong và Roll (2009) đề xuất một thước đo dựa trên mô hình nhân tố, tránh được những cạm bẫy của phân tích tương quan đơn giản. Chương này triển khai cả ba cách tiếp cận và áp dụng chúng vào quỹ đạo hội nhập của Việt Nam.
38.1 Hội nhập về mặt lý thuyết
38.1.1 Các chuẩn mực hội nhập và phân khúc
Trong điều kiện hội nhập hoàn toàn, lợi nhuận vượt mức kỳ vọng của cổ phiếu Việt Nam \(i\) là:
trong đó \(\beta_{i,\text{world}}\) là hệ số tải của cổ phiếu trên nhân tố thị trường toàn cầu và \(\lambda_{\text{world},t}\) là phần bù rủi ro toàn cầu. Chỉ rủi ro hệ thống toàn cầu được định giá; rủi ro đặc thù quốc gia có thể đa dạng hóa được và không đòi hỏi phần bù.
trong đó \(\beta_{i,\text{local}}\) là hệ số tải của cổ phiếu trên thị trường Việt Nam và \(\lambda_{\text{local},t}\) là phần bù rủi ro trong nước. Về mặt định giá, thị trường trong nước thực chất là một nền kinh tế đóng.
Bekaert và Harvey (1995) mô hình hóa sự chuyển đổi giữa hai trạng thái này như một quá trình chuyển đổi chế độ, trong đó trọng số trộn \(\omega_t \in [0, 1]\) biến động theo thời gian:
Trọng số \(\omega_t\) là mức độ hội nhập: \(\omega_t = 1\) là hội nhập hoàn toàn, \(\omega_t = 0\) là phân khúc hoàn toàn.
38.1.2 Phần bù phân khúc
Khi một thị trường chuyển từ phân khúc sang hội nhập, chi phí vốn của thị trường đó giảm vì rủi ro liên quan đến định giá thu hẹp từ tổng rủi ro trong nước xuống chỉ còn phần tương quan với thị trường toàn cầu (Henry 2000; Bekaert, Harvey, và Lundblad 2005). Phần bù phân khúc là lợi nhuận kỳ vọng vượt mức mà nhà đầu tư trong một thị trường phân khúc đòi hỏi:
Phần bù này đại diện cho một tổn thất vô ích (deadweight cost): nó làm tăng chi phí vốn của các doanh nghiệp Việt Nam, giảm đầu tư và làm giảm phúc lợi so với chuẩn mực hội nhập.
38.2 Xây dựng dữ liệu
Import libraries and configure environment
import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsimport statsmodels.api as smfrom scipy import stats, optimizefrom arch import arch_modelfrom arch.univariate import ConstantMean, GARCHimport warningswarnings.filterwarnings('ignore')plt.rcParams.update({'figure.figsize': (12, 6),'figure.dpi': 150,'font.size': 11,'axes.spines.top': False,'axes.spines.right': False})
Thước đo hội nhập đơn giản nhất là tương quan giữa lợi nhuận của thị trường Việt Nam và thị trường toàn cầu. Tương quan càng cao hàm ý hội nhập càng lớn (lợi nhuận được dẫn dắt bởi cùng các nhân tố toàn cầu). Tuy nhiên, tương quan đơn giản bị nhiễu bởi các thay đổi về biến động (tức là tương quan có xu hướng tăng lên một cách cơ học trong các giai đoạn biến động cao (Longin và Solnik 2001)).
Compute rolling correlations between Vietnam and global markets
# Align all seriesindices_aligned = global_indices.dropna(subset=['VIETNAM', 'MSCI_WORLD']).copy()# Rolling 36-month correlationsrolling_window =36corr_series = {}for idx in ['MSCI_WORLD', 'MSCI_EM', 'MSCI_ASIA_PAC_EX_JP','SP500', 'MSCI_CHINA', 'MSCI_THAILAND']:if idx in indices_aligned.columns: corr = ( indices_aligned[['VIETNAM', idx]] .rolling(rolling_window) .corr() .unstack()['VIETNAM'][idx] ) corr_series[idx] = corrcorr_df = pd.DataFrame(corr_series)
Plot rolling correlations with event timeline
fig, ax = plt.subplots(figsize=(14, 6))colors_idx = {'MSCI_WORLD': '#2C5F8A', 'MSCI_EM': '#C0392B','MSCI_ASIA_PAC_EX_JP': '#27AE60', 'SP500': '#8E44AD','MSCI_CHINA': '#E67E22', 'MSCI_THAILAND': '#1ABC9C'}for idx, color in colors_idx.items():if idx in corr_df.columns: ax.plot(corr_df.index, corr_df[idx], color=color, linewidth=1.5, label=idx.replace('MSCI_', '').replace('_', ' '), alpha=0.85)# Add liberalization eventsfor _, event in liberalization_events.iterrows():if event['date'] >= corr_df.index.min(): ax.axvline(x=event['date'], color='gray', linewidth=0.5, linestyle=':', alpha=0.6)ax.axhline(y=0, color='black', linewidth=0.5)ax.set_ylabel('Correlation with Vietnam')ax.set_title('Rolling 36-Month Correlation: Vietnam vs Global Markets')ax.legend(fontsize=8, ncol=3)ax.set_ylim([-0.3, 0.8])plt.tight_layout()plt.show()
Hình 38.1
38.3.2 Tương quan động DCC-GARCH
Để tách các thay đổi về tương quan khỏi các thay đổi về biến động, chúng ta ước lượng mô hình tương quan có điều kiện động (Dynamic Conditional Correlation, DCC) (Engle 2002). DCC phân rã ma trận hiệp phương sai biến đổi theo thời gian thành các độ biến động biến đổi theo thời gian và một ma trận tương quan biến đổi theo thời gian:
\[
H_t = D_t R_t D_t
\tag{38.5}\]
trong đó \(D_t = \text{diag}(\sigma_{1,t}, \ldots, \sigma_{n,t})\) và \(R_t\) là ma trận tương quan có điều kiện biến đổi theo:
\[
Q_t = (1 - a - b) \bar{Q} + a \epsilon_{t-1} \epsilon_{t-1}' + b Q_{t-1}
\tag{38.6}\]
Hội nhập có thể phụ thuộc vào trạng thái: đồng biến động thường tăng trong các cuộc khủng hoảng toàn cầu (lây lan) nhưng không tăng trong các giai đoạn bùng nổ cục bộ trong nước. Longin và Solnik (2001) và Ang và Chen (2002) cho thấy tương quan giữa các thị trường cổ phiếu cao hơn khi thị trường suy giảm. Chúng ta kiểm định tính bất đối xứng:
Test for asymmetric integration (bear vs bull markets)
# Classify global market regimesworld_ret_aligned = world_ret[common_dates]vn_ret_aligned = vn_ret[common_dates]# Bear: world return in bottom 25th percentile# Bull: world return in top 25th percentileq25 = world_ret_aligned.quantile(0.25)q75 = world_ret_aligned.quantile(0.75)bear = world_ret_aligned <= q25bull = world_ret_aligned >= q75normal =~bear &~bullregimes = {'Bear (bottom 25%)': bear,'Normal (middle 50%)': normal,'Bull (top 25%)': bull,'All': pd.Series(True, index=world_ret_aligned.index)}print("Asymmetric Correlation:")print(f"{'Regime':<25}{'Correlation':>12}{'N months':>10}")print("-"*47)for name, mask in regimes.items(): r_vn = vn_ret_aligned[mask] r_w = world_ret_aligned[mask] corr = r_vn.corr(r_w)print(f"{name:<25}{corr:>12.3f}{mask.sum():>10}")# Test: is bear correlation > bull correlation?r_bear_vn = vn_ret_aligned[bear]r_bear_w = world_ret_aligned[bear]r_bull_vn = vn_ret_aligned[bull]r_bull_w = world_ret_aligned[bull]# Fisher z-transformation testdef fisher_z_test(r1, n1, r2, n2): z1 = np.arctanh(r1) z2 = np.arctanh(r2) se = np.sqrt(1/ (n1 -3) +1/ (n2 -3)) z_stat = (z1 - z2) / se p_val =2* (1- stats.norm.cdf(abs(z_stat)))return z_stat, p_valz, p = fisher_z_test( r_bear_vn.corr(r_bear_w), len(r_bear_vn), r_bull_vn.corr(r_bull_w), len(r_bull_vn))print(f"\nFisher z-test (bear vs bull): z = {z:.2f}, p = {p:.4f}")
38.4 Các thước đo hội nhập dựa trên nhân tố
38.4.1 Thước đo R² của Pukthuanthong-Roll
Pukthuanthong và Roll (2009) đề xuất đo lường hội nhập bằng \(R^2\) từ hồi quy lợi nhuận của một quốc gia trên một tập các thành phần chính toàn cầu. Trực giác như sau: nếu một thị trường hội nhập hoàn toàn, các nhân tố toàn cầu phải giải thích toàn bộ biến thiên hệ thống của lợi nhuận thị trường đó.
Implement the Pukthuanthong-Roll integration measure
def pukthuanthong_roll_integration(country_returns, global_returns_matrix, n_components=10, rolling_window=36):""" Pukthuanthong-Roll (2009) R²-based integration measure. 1. Extract principal components from global returns. 2. Regress country returns on these PCs. 3. R² = degree of integration. """ common = country_returns.dropna().index.intersection( global_returns_matrix.dropna().index ) dates =sorted(common) T =len(dates) integration = []for t inrange(rolling_window, T): window = dates[t - rolling_window:t]# Global returns in window G = global_returns_matrix.loc[window].dropna(axis=1)if G.shape[1] < n_components:continue# Standardize G_std = (G - G.mean()) / G.std()# PCA cov = G_std.T @ G_std /len(G_std) eigenvalues, eigenvectors = np.linalg.eigh(cov.values)# Sort descending idx = np.argsort(-eigenvalues) eigenvalues = eigenvalues[idx] eigenvectors = eigenvectors[:, idx]# Project onto top K PCs PCs = G_std.values @ eigenvectors[:, :n_components]# Regress country returns on PCs y = country_returns.loc[window].values X = sm.add_constant(PCs)try: model = sm.OLS(y, X).fit() integration.append({'date': dates[t],'r_squared': model.rsquared,'adj_r_squared': model.rsquared_adj,'var_explained_pc1': eigenvalues[0] / eigenvalues.sum(),'n_countries': G.shape[1] })exceptException:passreturn pd.DataFrame(integration)# Build global returns matrix from multiple country indicesglobal_matrix = global_indices.drop(columns=['VIETNAM'], errors='ignore')pr_result = pukthuanthong_roll_integration( indices_aligned['VIETNAM'], global_matrix, n_components=5, rolling_window=36)iflen(pr_result) >0: pr_result['date'] = pd.to_datetime(pr_result['date'])print(f"Pukthuanthong-Roll Integration (R²):")print(f" Mean: {pr_result['r_squared'].mean():.3f}")print(f" 2008-2012: {pr_result[(pr_result['date'] >='2008') & (pr_result['date'] <'2013')]['r_squared'].mean():.3f}")print(f" 2013-2018: {pr_result[(pr_result['date'] >='2013') & (pr_result['date'] <'2019')]['r_squared'].mean():.3f}")print(f" 2019-2024: {pr_result[pr_result['date'] >='2019']['r_squared'].mean():.3f}")
38.4.2 Định giá theo nhân tố toàn cầu và nhân tố nội địa
Griffin (2002) kiểm định xem phiên bản toàn cầu hay phiên bản nội địa của các nhân tố Fama-French giải thích tốt hơn lợi nhuận cấp quốc gia. Chúng ta triển khai cuộc “đua ngựa” này cho Việt Nam:
Compare explanatory power of global vs local factor models
# Align global and local factorscommon_factor_dates = ( global_factors.index .intersection(local_factors.index) .intersection(vn_index.index))vn_excess = vn_index.loc[common_factor_dates, 'return']# Model 1: Global FF5X_global = sm.add_constant( global_factors.loc[common_factor_dates, ['mkt_excess_world', 'smb_world', 'hml_world','rmw_world', 'cma_world']])model_global = sm.OLS(vn_excess, X_global).fit( cov_type='HAC', cov_kwds={'maxlags': 6})# Model 2: Local FF5X_local = sm.add_constant( local_factors.loc[common_factor_dates, ['mkt_excess', 'smb', 'hml', 'rmw', 'cma']])model_local = sm.OLS(vn_excess, X_local).fit( cov_type='HAC', cov_kwds={'maxlags': 6})# Model 3: Both global and localX_both = sm.add_constant(pd.concat([ global_factors.loc[common_factor_dates, ['mkt_excess_world', 'smb_world', 'hml_world']], local_factors.loc[common_factor_dates, ['mkt_excess', 'smb', 'hml']]], axis=1))model_both = sm.OLS(vn_excess, X_both).fit( cov_type='HAC', cov_kwds={'maxlags': 6})print("Global vs Local Factor Models for VN-Index:")print(f"{'Model':<20}{'R²':>8}{'Adj R²':>8}{'α (ann)':>10}{'α t-stat':>10}")print("-"*56)for name, mod in [('Global FF5', model_global), ('Local FF5', model_local), ('Global + Local', model_both)]:print(f"{name:<20}{mod.rsquared:>8.3f}{mod.rsquared_adj:>8.3f} "f"{mod.params['const']*12:>10.4f}{mod.tvalues['const']:>10.2f}")
Bekaert và Harvey (1995) đo lường hội nhập bằng khả năng của một CAPM toàn cầu trong việc định giá các tài sản nội địa. Trong điều kiện hội nhập, alpha (hệ số chặn) của thị trường nội địa trong hồi quy trên thị trường toàn cầu phải bằng không, và phần bù rủi ro toàn cầu phải giải thích lợi nhuận kỳ vọng nội địa. Trong điều kiện phân khúc, alpha nội địa phản ánh phần bù phân khúc.
Bekaert-Harvey integration measure: rolling alpha from global CAPM
def bekaert_harvey_integration(local_return, global_return, rolling_window=36):""" Rolling alpha from regressing local on global market. Under integration, alpha -> 0. """ common = local_return.dropna().index.intersection(global_return.dropna().index) results = []for t inrange(rolling_window, len(common)): window = common[t - rolling_window:t] y = local_return[window] X = sm.add_constant(global_return[window]) model = sm.OLS(y, X).fit() results.append({'date': common[t],'alpha': model.params['const'],'alpha_t': model.tvalues['const'],'beta_global': model.params.iloc[1],'r_squared': model.rsquared })return pd.DataFrame(results)bh_result = bekaert_harvey_integration( indices_aligned['VIETNAM'], indices_aligned['MSCI_WORLD'], rolling_window=36)# The absolute alpha is the segmentation premiumbh_result['abs_alpha_ann'] = bh_result['alpha'].abs() *12print("Bekaert-Harvey Integration Diagnostic:")print(f" Mean |α| (ann.): {bh_result['abs_alpha_ann'].mean():.4f}")print(f" Mean β_world: {bh_result['beta_global'].mean():.3f}")print(f" Mean R²: {bh_result['r_squared'].mean():.3f}")
38.5.2 Chỉ số hội nhập tổng hợp
Chúng ta kết hợp tất cả các thước đo thành một chỉ số tổng hợp duy nhất về hội nhập của thị trường Việt Nam:
Construct a composite integration index
# Standardize each measure to [0, 1] using historical percentile ranksmeasures = pd.DataFrame(index=bh_result['date'])# 1. DCC correlation (higher = more integrated)dcc_aligned = dcc_result['dcc_corr'].reindex(measures.index).interpolate()measures['dcc_corr'] = dcc_aligned# 2. PR R² (higher = more integrated)pr_aligned = pr_result.set_index('date')['r_squared'].reindex(measures.index).interpolate()measures['pr_r2'] = pr_aligned# 3. Global/Local R² ratio (higher = more integrated)r2_aligned = r2_df.set_index('date')['ratio'].reindex(measures.index).interpolate()measures['gl_ratio'] = r2_aligned# 4. |Alpha| from global CAPM (lower = more integrated)# Invert: 1 - percentile rank of |alpha|measures['inv_alpha'] = bh_result.set_index('date')['abs_alpha_ann']measures['inv_alpha'] =1- measures['inv_alpha'].rank(pct=True)# 5. Global beta (higher = more integrated, up to a point)measures['global_beta'] = bh_result.set_index('date')['beta_global']# Standardize to percentile ranksfor col in ['dcc_corr', 'pr_r2', 'gl_ratio', 'inv_alpha', 'global_beta']: measures[f'{col}_rank'] = measures[col].rank(pct=True)# Composite = equal-weighted average of ranksrank_cols = [c for c in measures.columns if c.endswith('_rank')]measures['composite'] = measures[rank_cols].mean(axis=1)# Smooth with 6-month moving averagemeasures['composite_smooth'] = measures['composite'].rolling(6).mean()
Plot the composite integration index with milestones
fig, ax = plt.subplots(figsize=(14, 6))ax.fill_between(measures.index, measures['composite_smooth'], alpha=0.3, color='#2C5F8A')ax.plot(measures.index, measures['composite_smooth'], color='#2C5F8A', linewidth=2)# Add event markersfor _, event in liberalization_events.iterrows():if event['date'] >= measures.index.min(): color = event_colors.get(event['category'], 'gray') ax.axvline(x=event['date'], color=color, linewidth=1.5, linestyle='--', alpha=0.6)ax.set_ylabel('Integration Index (0 = segmented, 1 = integrated)')ax.set_title('Vietnam Equity Market Integration: Composite Index')ax.set_ylim([0, 1])# Legend for event categoriesfrom matplotlib.patches import Patchlegend_patches = [Patch(facecolor=c, label=cat)for cat, c in event_colors.items() if cat in liberalization_events['category'].values]ax.legend(handles=legend_patches, fontsize=7, loc='lower right', ncol=2)plt.tight_layout()plt.show()
Hình 38.4
38.6 Phát hiện điểm gãy cấu trúc
38.6.1 Kiểm định Bai-Perron đối với các thay đổi chế độ hội nhập
Chúng ta kiểm định xem quỹ đạo hội nhập của Việt Nam có chứa các điểm gãy cấu trúc rời rạc (tức là những dịch chuyển đột ngột về mức hội nhập) hay chỉ là một xu hướng trơn tru:
Detect structural breaks in the integration time series
def detect_breaks_cusum(series, significance=0.05):""" CUSUM-based structural break detection. """ y = series.dropna().values T =len(y)# Recursive residuals from rolling mean cumsum = np.cumsum(y - y.mean()) / (y.std() * np.sqrt(T))# Brown-Durbin-Evans critical values (approximate)# At 5%: ±0.948 critical =0.948 breaks = []for t inrange(1, T -1):ifabs(cumsum[t]) > critical: breaks.append(t)return cumsum, breaks# Apply to composite indexcomposite_clean = measures['composite_smooth'].dropna()cusum, break_points = detect_breaks_cusum(composite_clean)# Alternative: Chow test at key liberalization datesdef chow_test(y, breakpoint_idx):"""Simple Chow test for structural break.""" T =len(y) y1 = y[:breakpoint_idx] y2 = y[breakpoint_idx:]# Full sample regression (on constant) rss_full = np.sum((y - y.mean()) **2)# Split samples rss1 = np.sum((y1 - y1.mean()) **2) rss2 = np.sum((y2 - y2.mean()) **2) rss_split = rss1 + rss2 k =1# Number of parameters f_stat = ((rss_full - rss_split) / k) / (rss_split / (T -2* k)) p_val =1- stats.f.cdf(f_stat, k, T -2* k)return f_stat, p_valprint("Chow Tests for Structural Breaks at Key Dates:")print(f"{'Event':<30}{'Date':>12}{'F-stat':>10}{'p-value':>10}")print("-"*62)for _, event in liberalization_events.iterrows():if event['date'] < composite_clean.index.min():continue# Find nearest date nearest = composite_clean.index.searchsorted(event['date'])if nearest <12or nearest >len(composite_clean) -12:continue f_stat, p_val = chow_test(composite_clean.values, nearest) sig ='***'if p_val <0.01else'**'if p_val <0.05else'*'if p_val <0.1else''print(f"{event['event']:<30}{event['date'].strftime('%Y-%m'):>12} "f"{f_stat:>10.2f}{p_val:>10.4f}{sig}")
38.7 Phần bù phân khúc của Việt Nam
38.7.1 Bằng chứng chéo (cross-sectional)
Trong điều kiện phân khúc một phần, các cổ phiếu có tỷ lệ sở hữu nước ngoài cao hơn phải có lợi nhuận kỳ vọng thấp hơn (vì nhà đầu tư nước ngoài có thể đa dạng hóa bớt rủi ro nội địa). Điều này đưa ra một dự đoán kiểm định được: sở hữu nước ngoài phải có quan hệ nghịch chiều với lợi nhuận kỳ vọng, sau khi kiểm soát mức phơi nhiễm rủi ro toàn cầu.
Estimate the cross-sectional segmentation premium
# Get monthly stock returns with foreign ownershipstock_data = client.get_monthly_returns( exchanges=['HOSE', 'HNX'], start_date='2008-01-01', end_date='2024-12-31', fields=['ticker', 'month_end', 'monthly_return', 'market_cap','foreign_ownership_pct'])stock_data['month_end'] = pd.to_datetime(stock_data['month_end'])# Fama-MacBeth: regress returns on lagged foreign ownershipstock_data = stock_data.sort_values(['ticker', 'month_end'])stock_data['fol_lag'] = ( stock_data.groupby('ticker')['foreign_ownership_pct'].shift(1))stock_data['log_mcap'] = np.log(stock_data['market_cap'].clip(lower=1))# Monthly cross-sectional regressionsgamma_fol = []for month, group in stock_data.dropna(subset=['fol_lag', 'monthly_return']).groupby('month_end'):iflen(group) <100:continue y = group['monthly_return'].values X = sm.add_constant(group[['fol_lag', 'log_mcap']].values)try: model = sm.OLS(y, X).fit() gamma_fol.append({'month': month,'gamma_fol': model.params[1],'gamma_size': model.params[2],'n': len(group) })except:passgamma_fol_df = pd.DataFrame(gamma_fol)mean_gamma = gamma_fol_df['gamma_fol'].mean()se_gamma = gamma_fol_df['gamma_fol'].std() / np.sqrt(len(gamma_fol_df))t_gamma = mean_gamma / se_gammaprint(f"Fama-MacBeth: Foreign Ownership and Expected Returns")print(f" γ_FOL (monthly): {mean_gamma:.6f}")print(f" γ_FOL (ann.): {mean_gamma *12:.4f}")print(f" t-statistic: {t_gamma:.2f}")print(f" Interpretation: A 10pp increase in foreign ownership is "f"associated with a {mean_gamma *12*10:.2f}% change in "f"annual expected returns")
Trong một thị trường hội nhập một phần, rủi ro tỷ giá hối đoái có thể mang một phần bù riêng. Jorion (1991) và Dumas và Solnik (1995) kiểm định xem mức phơi nhiễm tiền tệ có được định giá ngoài rủi ro cổ phiếu toàn cầu hay không. Với Việt Nam, tỷ giá VND/USD được quản lý (neo trượt, thỉnh thoảng có những đợt phá giá theo bậc), tạo ra một rủi ro đặc thù mà không thể đa dạng hóa hoàn toàn cũng không được các nhân tố cổ phiếu toàn cầu định giá đầy đủ.
Test whether VND/USD exchange rate risk is priced
# VND depreciationfx_return = fx['rate'].pct_change()fx_return.name ='fx_return'# Merge with stock datastock_fx = stock_data.merge( fx_return.to_frame().reset_index().rename(columns={'date': 'month_end'}), on='month_end', how='left')# Estimate FX beta for each stock (rolling 60-month)# Then test in Fama-MacBeth whether FX beta is pricedfx_betas = {}for ticker, group in stock_fx.groupby('ticker'):iflen(group) <60:continue group = group.sort_values('month_end') y = group['monthly_return'] x = group[['fx_return']].dropna() common = y.dropna().index.intersection(x.index)iflen(common) <48:continue model = sm.OLS(y[common], sm.add_constant(x.loc[common])).fit() fx_betas[ticker] = model.params.get('fx_return', np.nan)fx_beta_series = pd.Series(fx_betas, name='fx_beta')# Cross-sectional test: do stocks with higher FX beta earn different returns?stock_fx_beta = stock_fx.merge( fx_beta_series.to_frame().reset_index().rename(columns={'index': 'ticker'}), on='ticker', how='left')# Quintile sort on FX betafx_q = stock_fx_beta.dropna(subset=['fx_beta', 'monthly_return'])fx_q['fx_quintile'] = pd.qcut(fx_q['fx_beta'].rank(method='first'),5, labels=False)fx_premium = fx_q.groupby('fx_quintile')['monthly_return'].mean() *12print("Returns by FX Beta Quintile:")for q, ret in fx_premium.items():print(f" Q{q+1}: {ret*100:.2f}% ann.")print(f" Q5-Q1: {(fx_premium.iloc[-1] - fx_premium.iloc[0])*100:.2f}% ann.")
38.9 Lây lan và phụ thuộc lẫn nhau
Trong các cuộc khủng hoảng toàn cầu, tương quan giữa Việt Nam và các thị trường thế giới tăng vọt. Câu hỏi là điều này thể hiện lây lan (contagion, một thay đổi cấu trúc trong cơ chế truyền dẫn) hay chỉ là phụ thuộc lẫn nhau (interdependence, đồng biến động bình thường được khuếch đại bởi biến động cao hơn). Longin và Solnik (2001) cho thấy tương quan tăng một cách cơ học cùng với biến động, ngay cả khi cấu trúc phụ thuộc cơ bản không hề thay đổi.
Quỹ đạo hội nhập của Việt Nam được hiểu rõ nhất trong bối cảnh các nước ASEAN, vốn có điều kiện xuất phát tương tự nhưng đã đi theo các con đường tự do hóa khác nhau:
Compare integration trajectories across ASEAN markets
Mức độ hội nhập quyết định mô hình định giá tài sản nào phù hợp cho cổ phiếu Việt Nam. Bằng chứng trong chương này ủng hộ một số kết luận thực tiễn:
Việt Nam hội nhập một phần và đang có xu hướng hội nhập thêm. Chỉ số tổng hợp cho thấy quỹ đạo đi lên rõ ràng, trong đó giai đoạn sau năm 2015 là giai đoạn hội nhập bền vững cao nhất trong lịch sử thị trường. Tuy nhiên, Việt Nam vẫn kém hội nhập hơn Thái Lan hoặc Malaysia, và còn xa mới hội nhập hoàn toàn với các thị trường toàn cầu.
Các nhân tố nội địa chiếm ưu thế so với các nhân tố toàn cầu trong định giá cổ phiếu Việt Nam. So sánh \(R^2\) trượt cho thấy các nhân tố nội địa của Việt Nam luôn giải thích được nhiều biến thiên lợi nhuận hơn các nhân tố toàn cầu. Điều này có nghĩa là các nhà nghiên cứu về lợi nhuận cắt ngang (cross-section) của cổ phiếu Việt Nam nên dùng các mô hình nhân tố nội địa (FF5 Việt Nam) thay vì các nhân tố toàn cầu. Các nhân tố toàn cầu chủ yếu hữu ích cho nhà đầu tư quốc tế khi đánh giá rủi ro đồng biến động.
Phần bù phân khúc đang thu hẹp nhưng chưa bằng không. Bằng chứng Fama-MacBeth cho thấy các cổ phiếu có sở hữu nước ngoài cao hơn có lợi nhuận thấp hơn, phù hợp với phân khúc một phần. Mức độ này đã giảm theo thời gian khi các giới hạn sở hữu nước ngoài được nới lỏng, nhưng một phần bù còn lại vẫn tồn tại, nhiều khả năng do các mức trần sở hữu còn áp dụng trong ngành ngân hàng và các lĩnh vực chiến lược.
Đồng biến động trong thời kỳ khủng hoảng chủ yếu là phụ thuộc lẫn nhau, không phải lây lan. Các tương quan điều chỉnh Forbes-Rigobon cho thấy việc tương quan Việt Nam-thế giới tăng vọt trong khủng hoảng phần lớn được giải thích bởi biến động toàn cầu gia tăng, chứ không phải do thay đổi cấu trúc trong cơ chế truyền dẫn. Điều này đáng yên tâm đối với đa dạng hóa: Việt Nam vẫn mang lại lợi ích đa dạng hóa đáng kể ngay cả trong thời kỳ căng thẳng toàn cầu.
Lộ trình nâng hạng của FTSE/MSCI rất quan trọng. Việc Việt Nam có thể được nâng hạng từ thị trường cận biên (frontier) lên thị trường mới nổi sẽ kích hoạt việc tái cân bằng danh mục bắt buộc theo chỉ số của các quỹ thụ động, làm tăng dòng vốn nước ngoài và nhiều khả năng đẩy nhanh hội nhập. Các nhà nghiên cứu và nhà đầu tư nên theo dõi các tiêu chí nâng hạng và tác động của chúng đến chi phí vốn.
38.12 Tóm tắt
Bảng 38.1: Tóm tắt các thước đo hội nhập của thị trường cổ phiếu Việt Nam.
Thước đo
Nội dung phản ánh
Khoảng giá trị của Việt Nam
Mức hiện tại
Xu hướng
Tương quan DCC (thế giới)
Đồng biến động
0.0–0.5
~0.35–0.45
Tăng
PR R² (PC toàn cầu)
Mức phơi nhiễm nhân tố toàn cầu
0.05–0.50
~0.30–0.40
Tăng
Tỷ số R² toàn cầu/nội địa
Sức mạnh định giá tương đối
0.1–0.8
~0.5–0.6
Tăng
α CAPM toàn cầu
Sai số định giá
0–15%/năm
~3–5%/năm
Giảm
Phần bù FOL (γ)
Chi phí phân khúc
-5% đến +2%
~-1% đến 0%
Thu hẹp
Ang, Andrew, và Joseph Chen. 2002. “Asymmetric correlations of equity portfolios”. Journal of financial Economics 63 (3): 443–94.
Bekaert, Geert, và Campbell R Harvey. 1995. “Time-varying world market integration”. the Journal of Finance 50 (2): 403–44.
———. 2002. “Research in emerging markets finance: looking to the future”. Emerging markets review 3 (4): 429–48.
Bekaert, Geert, Campbell R Harvey, và Christian Lundblad. 2005. “Does financial liberalization spur growth?”Journal of Financial economics 77 (1): 3–55.
Dumas, Bernard, và Bruno Solnik. 1995. “The world price of foreign exchange risk”. The journal of finance 50 (2): 445–79.
Engle, Robert. 2002. “Dynamic conditional correlation: A simple class of multivariate generalized autoregressive conditional heteroskedasticity models”. Journal of business & economic statistics 20 (3): 339–50.
Errunza, Vihang, và Etienne Losq. 1985. “International asset pricing under mild segmentation: Theory and test”. The Journal of Finance 40 (1): 105–24.
Griffin, John M. 2002. “Are the Fama and French factors global or country specific?”The Review of Financial Studies 15 (3): 783–803.
Henry, Peter Blair. 2000. “Stock market liberalization, economic reform, and emerging market equity prices”. The Journal of Finance 55 (2): 529–64.
Jorion, Philippe. 1991. “The pricing of exchange rate risk in the stock market”. Journal of financial and quantitative analysis 26 (3): 363–76. https://doi.org/10.2307/2331212.
Longin, Francois, và Bruno Solnik. 2001. “Extreme correlation of international equity markets”. The journal of finance 56 (2): 649–76. https://doi.org/10.1111/0022-1082.00340.
Pukthuanthong, Kuntara, và Richard Roll. 2009. “Global market integration: An alternative measure and its application”. Journal of Financial Economics 94 (2): 214–32.
# Hội nhập và phân khúc thị trường::: callout-noteTrong chương này, chúng ta đo lường mức độ thị trường cổ phiếu Việt Nam hội nhập với, hoặc bị phân khúc khỏi, các thị trường vốn toàn cầu. Chúng ta xây dựng nhiều thước đo hội nhập, gồm các thước đo dựa trên tương quan, dựa trên nhân tố và dựa trên sai số định giá, theo dõi diễn biến của chúng qua tiến trình tự do hóa của Việt Nam, và định lượng chi phí của phân khúc thị trường đối với các doanh nghiệp Việt Nam.:::Một thị trường *hội nhập hoàn toàn* khi tài sản của nó được định giá bởi một hệ số chiết khấu ngẫu nhiên toàn cầu: phần bù rủi ro chỉ phản ánh mức độ phơi nhiễm với các nhân tố rủi ro toàn cầu, và các dòng tiền giống hệt nhau đòi hỏi cùng một mức lợi nhuận kỳ vọng bất kể nơi đặt trụ sở của tổ chức phát hành. Một thị trường *phân khúc hoàn toàn* khi chỉ các nhân tố rủi ro trong nước quyết định giá cả, và sự đánh đổi giữa rủi ro và lợi nhuận của quốc gia đó độc lập với phần còn lại của thế giới. Thực tế nằm đâu đó giữa hai cực này, và vị trí ấy thay đổi theo thời gian.Việt Nam là một trường hợp đặc biệt thú vị. Sàn giao dịch chứng khoán của Việt Nam mở cửa vào tháng 7 năm 2000 với những hạn chế nặng nề đối với sự tham gia của nhà đầu tư nước ngoài. Giới hạn sở hữu nước ngoài (ban đầu là 20%, nâng lên 30% năm 2003, 49% năm 2015, và được bãi bỏ có chọn lọc đối với một số doanh nghiệp) đã dần được nới lỏng. Việt Nam gia nhập WTO năm 2007. FTSE Russell đã nâng hạng Việt Nam từ "unclassified" lên "secondary emerging" trong chỉ số frontier của mình vào năm 2018 và vẫn đang xem xét các đợt nâng hạng tiếp theo. Mỗi sự kiện này đều có thể đã làm dịch chuyển mức độ hội nhập.@bekaert1995time và @bekaert2002research thiết lập khung phân tích hiện đại để đo lường hội nhập biến đổi theo thời gian. @errunza1985international phát triển mô hình "phân khúc nhẹ" (mild segmentation), trong đó nhà đầu tư nước ngoài đối mặt với các rào cản nhưng vẫn có thể sao chép một phần lợi nhuận của thị trường mới nổi thông qua các chứng khoán toàn cầu. @pukthuanthong2009global đề xuất một thước đo dựa trên mô hình nhân tố, tránh được những cạm bẫy của phân tích tương quan đơn giản. Chương này triển khai cả ba cách tiếp cận và áp dụng chúng vào quỹ đạo hội nhập của Việt Nam.## Hội nhập về mặt lý thuyết {#sec-market-integration-theory}### Các chuẩn mực hội nhập và phân khúcTrong điều kiện hội nhập hoàn toàn, lợi nhuận vượt mức kỳ vọng của cổ phiếu Việt Nam $i$ là:$$E[R_{i,t} - R_{f,t}] = \beta_{i,\text{world}} \cdot \lambda_{\text{world},t}$$ {#eq-integrated}trong đó $\beta_{i,\text{world}}$ là hệ số tải của cổ phiếu trên nhân tố thị trường toàn cầu và $\lambda_{\text{world},t}$ là phần bù rủi ro toàn cầu. Chỉ rủi ro hệ thống toàn cầu được định giá; rủi ro đặc thù quốc gia có thể đa dạng hóa được và không đòi hỏi phần bù.Trong điều kiện phân khúc hoàn toàn:$$E[R_{i,t} - R_{f,t}] = \beta_{i,\text{local}} \cdot \lambda_{\text{local},t}$$ {#eq-segmented}trong đó $\beta_{i,\text{local}}$ là hệ số tải của cổ phiếu trên thị trường Việt Nam và $\lambda_{\text{local},t}$ là phần bù rủi ro trong nước. Về mặt định giá, thị trường trong nước thực chất là một nền kinh tế đóng.@bekaert1995time mô hình hóa sự chuyển đổi giữa hai trạng thái này như một quá trình chuyển đổi chế độ, trong đó trọng số trộn $\omega_t \in [0, 1]$ biến động theo thời gian:$$E[R_{i,t} - R_{f,t}] = \omega_t \cdot \beta_{i,\text{world}} \lambda_{\text{world},t} + (1 - \omega_t) \cdot \beta_{i,\text{local}} \lambda_{\text{local},t}$$ {#eq-partial}Trọng số $\omega_t$ là *mức độ hội nhập*: $\omega_t = 1$ là hội nhập hoàn toàn, $\omega_t = 0$ là phân khúc hoàn toàn.### Phần bù phân khúcKhi một thị trường chuyển từ phân khúc sang hội nhập, chi phí vốn của thị trường đó giảm vì rủi ro liên quan đến định giá thu hẹp từ tổng rủi ro trong nước xuống chỉ còn phần tương quan với thị trường toàn cầu [@henry2000stock; @bekaert2005does]. *Phần bù phân khúc* là lợi nhuận kỳ vọng vượt mức mà nhà đầu tư trong một thị trường phân khúc đòi hỏi:$$\text{Segmentation premium} = (1 - \omega_t) \cdot (\lambda_{\text{local}} - \beta_{\text{local,world}} \cdot \lambda_{\text{world}})$$ {#eq-segmentation-premium}Phần bù này đại diện cho một tổn thất vô ích (deadweight cost): nó làm tăng chi phí vốn của các doanh nghiệp Việt Nam, giảm đầu tư và làm giảm phúc lợi so với chuẩn mực hội nhập.## Xây dựng dữ liệu {#sec-market-integration-data}```{python}#| label: setup#| code-summary: "Import libraries and configure environment"import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsimport statsmodels.api as smfrom scipy import stats, optimizefrom arch import arch_modelfrom arch.univariate import ConstantMean, GARCHimport warningswarnings.filterwarnings('ignore')plt.rcParams.update({'figure.figsize': (12, 6),'figure.dpi': 150,'font.size': 11,'axes.spines.top': False,'axes.spines.right': False})``````{python}#| label: data-load#| eval: false#| code-summary: "Load Vietnamese and global market data"from datacore import DataCoreClientclient = DataCoreClient()# Vietnamese market returnsvn_index = client.get_index_returns( index='VNINDEX', start_date='2000-07-01', end_date='2024-12-31', frequency='monthly', fields=['date', 'return', 'total_return_index'])vn_index['date'] = pd.to_datetime(vn_index['date'])vn_index = vn_index.set_index('date')# Global and regional indices (USD-denominated for comparability)global_indices = client.get_global_indices( indices=['MSCI_WORLD', 'MSCI_EM', 'MSCI_ASIA_PAC_EX_JP','MSCI_FM', # Frontier markets'SP500', 'STOXX600','MSCI_CHINA', 'MSCI_THAILAND', 'MSCI_INDONESIA','MSCI_PHILIPPINES', 'MSCI_MALAYSIA' ], start_date='2000-07-01', end_date='2024-12-31', frequency='monthly', currency='USD')global_indices['date'] = pd.to_datetime(global_indices['date'])global_indices = global_indices.pivot(index='date', columns='index', values='return')# Vietnam returns in USD for apples-to-apples comparisonvn_usd = client.get_index_returns( index='VNINDEX', start_date='2000-07-01', end_date='2024-12-31', frequency='monthly', currency='USD')vn_usd['date'] = pd.to_datetime(vn_usd['date'])global_indices['VIETNAM'] = vn_usd.set_index('date')['return']# VND/USD exchange ratefx = client.get_exchange_rates( pair='USD_VND', start_date='2000-07-01', end_date='2024-12-31', frequency='monthly')fx['date'] = pd.to_datetime(fx['date'])fx = fx.set_index('date')# Global factor returns (Fama-French global)global_factors = client.get_global_factor_returns( start_date='2000-07-01', end_date='2024-12-31', frequency='monthly', factors=['mkt_excess_world', 'smb_world', 'hml_world','rmw_world', 'cma_world', 'wml_world'])global_factors['date'] = pd.to_datetime(global_factors['date'])global_factors = global_factors.set_index('date')# Vietnamese factor returns (local)local_factors = client.get_factor_returns( market='vietnam', start_date='2008-01-01', end_date='2024-12-31', factors=['mkt_excess', 'smb', 'hml', 'rmw', 'cma', 'wml'])local_factors['date'] = pd.to_datetime(local_factors['date'])local_factors = local_factors.set_index('date')print(f"Vietnam index: {len(vn_index)} months")print(f"Global indices: {global_indices.shape}")print(f"Global factors: {len(global_factors)} months")```### Tiến trình tự do hóa của Việt Nam```{python}#| label: timeline#| eval: false#| code-summary: "Define key liberalization events for Vietnam"liberalization_events = pd.DataFrame([ ('2000-07-28', 'HOSE opens', 'Institutional'), ('2002-03-01', 'FOL raised to 30%', 'Ownership'), ('2005-03-01', 'HNX opens', 'Institutional'), ('2006-01-01', 'Securities Law enacted', 'Legal'), ('2007-01-11', 'WTO accession', 'Trade'), ('2007-06-01', 'FOL raised to 49%', 'Ownership'), ('2009-06-24', 'UPCoM opens', 'Institutional'), ('2012-01-01', 'SSC restructuring', 'Regulatory'), ('2015-09-01', 'FOL selectively removed', 'Ownership'), ('2017-08-01', 'Derivatives market opens', 'Institutional'), ('2018-09-01', 'FTSE Frontier Secondary', 'Index'), ('2021-01-01', 'New Securities Law', 'Legal'), ('2023-06-01', 'KRX trading system', 'Infrastructure'),], columns=['date', 'event', 'category'])liberalization_events['date'] = pd.to_datetime(liberalization_events['date'])print("Vietnam Liberalization Timeline:")for _, row in liberalization_events.iterrows():print(f" {row['date'].strftime('%Y-%m')}: {row['event']} [{row['category']}]")```## Các thước đo hội nhập dựa trên tương quan {#sec-market-integration-correlation}### Tương quan trượtThước đo hội nhập đơn giản nhất là tương quan giữa lợi nhuận của thị trường Việt Nam và thị trường toàn cầu. Tương quan càng cao hàm ý hội nhập càng lớn (lợi nhuận được dẫn dắt bởi cùng các nhân tố toàn cầu). Tuy nhiên, tương quan đơn giản bị nhiễu bởi các thay đổi về biến động (tức là tương quan có xu hướng tăng lên một cách cơ học trong các giai đoạn biến động cao [@longin2001extreme]).```{python}#| label: rolling-correlations#| eval: false#| code-summary: "Compute rolling correlations between Vietnam and global markets"# Align all seriesindices_aligned = global_indices.dropna(subset=['VIETNAM', 'MSCI_WORLD']).copy()# Rolling 36-month correlationsrolling_window =36corr_series = {}for idx in ['MSCI_WORLD', 'MSCI_EM', 'MSCI_ASIA_PAC_EX_JP','SP500', 'MSCI_CHINA', 'MSCI_THAILAND']:if idx in indices_aligned.columns: corr = ( indices_aligned[['VIETNAM', idx]] .rolling(rolling_window) .corr() .unstack()['VIETNAM'][idx] ) corr_series[idx] = corrcorr_df = pd.DataFrame(corr_series)``````{python}#| label: fig-rolling-corr#| eval: false#| fig-cap: "Tương quan trượt 36 tháng giữa lợi nhuận cổ phiếu Việt Nam (USD) và các chỉ số toàn cầu/khu vực chính. Tương quan của Việt Nam với MSCI World tăng từ gần bằng không vào đầu những năm 2000 lên 0,3–0,5 vào giữa thập niên 2010, phản ánh quá trình hội nhập dần dần. Tương quan cao nhất với các nước ASEAN (Thái Lan, Indonesia) và MSCI Emerging Markets, và thấp nhất với S&P 500. Các sự kiện tự do hóa (đường thẳng đứng) đi trước các giai đoạn tương quan gia tăng."#| code-summary: "Plot rolling correlations with event timeline"fig, ax = plt.subplots(figsize=(14, 6))colors_idx = {'MSCI_WORLD': '#2C5F8A', 'MSCI_EM': '#C0392B','MSCI_ASIA_PAC_EX_JP': '#27AE60', 'SP500': '#8E44AD','MSCI_CHINA': '#E67E22', 'MSCI_THAILAND': '#1ABC9C'}for idx, color in colors_idx.items():if idx in corr_df.columns: ax.plot(corr_df.index, corr_df[idx], color=color, linewidth=1.5, label=idx.replace('MSCI_', '').replace('_', ' '), alpha=0.85)# Add liberalization eventsfor _, event in liberalization_events.iterrows():if event['date'] >= corr_df.index.min(): ax.axvline(x=event['date'], color='gray', linewidth=0.5, linestyle=':', alpha=0.6)ax.axhline(y=0, color='black', linewidth=0.5)ax.set_ylabel('Correlation with Vietnam')ax.set_title('Rolling 36-Month Correlation: Vietnam vs Global Markets')ax.legend(fontsize=8, ncol=3)ax.set_ylim([-0.3, 0.8])plt.tight_layout()plt.show()```### Tương quan động DCC-GARCHĐể tách các thay đổi về tương quan khỏi các thay đổi về biến động, chúng ta ước lượng mô hình tương quan có điều kiện động (Dynamic Conditional Correlation, DCC) [@engle2002dynamic]. DCC phân rã ma trận hiệp phương sai biến đổi theo thời gian thành các độ biến động biến đổi theo thời gian và một ma trận tương quan biến đổi theo thời gian:$$H_t = D_t R_t D_t$$ {#eq-dcc-decomp}trong đó $D_t = \text{diag}(\sigma_{1,t}, \ldots, \sigma_{n,t})$ và $R_t$ là ma trận tương quan có điều kiện biến đổi theo:$$Q_t = (1 - a - b) \bar{Q} + a \epsilon_{t-1} \epsilon_{t-1}' + b Q_{t-1}$$ {#eq-dcc-evolution}$$R_t = \text{diag}(Q_t)^{-1/2} Q_t \text{diag}(Q_t)^{-1/2}$$ {#eq-dcc-normalize}```{python}#| label: dcc-garch#| eval: false#| code-summary: "Estimate DCC-GARCH model for Vietnam and global market"def estimate_dcc(y1, y2, p=1, q=1):""" Two-step DCC-GARCH estimation. Step 1: Univariate GARCH for each series. Step 2: DCC parameters from standardized residuals. """# Step 1: Univariate GARCH(1,1) for each series models = [] std_resids = [] cond_vols = []for y in [y1, y2]: am = arch_model(y *100, vol='GARCH', p=p, q=q, mean='Constant', dist='normal') res = am.fit(disp='off') models.append(res) std_resids.append(res.std_resid) cond_vols.append(res.conditional_volatility /100)# Align residuals e1 = std_resids[0] e2 = std_resids[1] common = e1.dropna().index.intersection(e2.dropna().index) e1 = e1[common].values e2 = e2[common].values T =len(e1)# Step 2: DCC estimation# Q_bar = unconditional correlation of standardized residuals Q_bar = np.corrcoef(e1, e2)def dcc_loglik(params): a, b = paramsif a <0or b <0or a + b >=1:return1e10 Q = np.zeros((T, 2, 2)) R = np.zeros((T, 2, 2)) Q[0] = Q_bar.copy() ll =0for t inrange(T):if t >0: et = np.array([[e1[t-1]], [e2[t-1]]]) Q[t] = (1- a - b) * Q_bar + a * (et @ et.T) + b * Q[t-1]# Normalize d = np.sqrt(np.diag(Q[t]))if d[0] >0and d[1] >0: R[t] = Q[t] / np.outer(d, d)else: R[t] = np.eye(2)# Clip correlation R[t, 0, 1] = np.clip(R[t, 0, 1], -0.999, 0.999) R[t, 1, 0] = R[t, 0, 1]# Log-likelihood contribution det_R =1- R[t, 0, 1] **2if det_R >0: et_vec = np.array([e1[t], e2[t]]) ll +=-0.5* (np.log(det_R) + et_vec @ np.linalg.inv(R[t]) @ et_vec - et_vec @ et_vec)return-ll result = optimize.minimize(dcc_loglik, [0.05, 0.90], method='Nelder-Mead', options={'maxiter': 5000}) a_hat, b_hat = result.x# Reconstruct dynamic correlations Q = np.zeros((T, 2, 2)) dcc_corr = np.zeros(T) Q[0] = Q_bar.copy()for t inrange(T):if t >0: et = np.array([[e1[t-1]], [e2[t-1]]]) Q[t] = (1- a_hat - b_hat) * Q_bar + a_hat * (et @ et.T) + b_hat * Q[t-1] d = np.sqrt(np.diag(Q[t]))if d[0] >0and d[1] >0: dcc_corr[t] = Q[t, 0, 1] / (d[0] * d[1])else: dcc_corr[t] =0return {'a': a_hat, 'b': b_hat,'persistence': a_hat + b_hat,'dcc_corr': pd.Series(dcc_corr, index=common),'cond_vol_1': cond_vols[0],'cond_vol_2': cond_vols[1] }# Estimate DCC: Vietnam vs MSCI Worldvn_ret = indices_aligned['VIETNAM'].dropna()world_ret = indices_aligned['MSCI_WORLD'].dropna()common_dates = vn_ret.index.intersection(world_ret.index)dcc_result = estimate_dcc(vn_ret[common_dates], world_ret[common_dates])print(f"DCC Parameters:")print(f" a (news): {dcc_result['a']:.4f}")print(f" b (persistence): {dcc_result['b']:.4f}")print(f" a + b: {dcc_result['persistence']:.4f}")print(f"\nDCC Correlation with MSCI World:")print(f" Mean: {dcc_result['dcc_corr'].mean():.3f}")print(f" Min: {dcc_result['dcc_corr'].min():.3f}")print(f" Max: {dcc_result['dcc_corr'].max():.3f}")``````{python}#| label: fig-dcc#| eval: false#| fig-cap: "Tương quan động DCC-GARCH giữa Việt Nam và chỉ số MSCI World. Khác với tương quan trượt đơn giản, DCC tách biệt những thay đổi thực sự về mức đồng biến động khỏi các hiệu ứng biến động. Xu hướng rõ ràng là đi lên, từ gần bằng không vào đầu những năm 2000 lên 0,3–0,5 trong những năm gần đây. Các sự kiện tự do hóa quan trọng (đường màu) gắn với sự gia tăng bền vững của tương quan có điều kiện."#| code-summary: "Plot DCC dynamic correlation with event annotations"fig, axes = plt.subplots(2, 1, figsize=(14, 8), sharex=True, gridspec_kw={'height_ratios': [2, 1]})# Panel A: DCC correlationaxes[0].plot(dcc_result['dcc_corr'].index, dcc_result['dcc_corr'].values, color='#2C5F8A', linewidth=1.5)axes[0].fill_between(dcc_result['dcc_corr'].index, dcc_result['dcc_corr'].values, 0, alpha=0.2, color='#2C5F8A')# Liberalization eventsevent_colors = {'Ownership': '#C0392B', 'Trade': '#27AE60','Institutional': '#E67E22', 'Legal': '#8E44AD','Index': '#1ABC9C', 'Regulatory': '#F1C40F','Infrastructure': '#3498DB'}for _, event in liberalization_events.iterrows():if event['date'] in dcc_result['dcc_corr'].index orTrue: color = event_colors.get(event['category'], 'gray') axes[0].axvline(x=event['date'], color=color, linewidth=1.5, linestyle='--', alpha=0.7) axes[0].text(event['date'], axes[0].get_ylim()[1] *0.95, event['event'][:15], rotation=90, fontsize=6, va='top', color=color)axes[0].set_ylabel('Dynamic Conditional Correlation')axes[0].set_title('Panel A: DCC-GARCH Correlation (Vietnam–World)')axes[0].axhline(y=0, color='black', linewidth=0.5)# Panel B: Conditional volatilitiesvol1 = dcc_result['cond_vol_1'] * np.sqrt(12) # Annualizedvol2 = dcc_result['cond_vol_2'] * np.sqrt(12)common_vol = vol1.index.intersection(vol2.index)axes[1].plot(common_vol, vol1[common_vol], color='#C0392B', linewidth=1, label='Vietnam', alpha=0.8)axes[1].plot(common_vol, vol2[common_vol], color='#2C5F8A', linewidth=1, label='World', alpha=0.8)axes[1].set_ylabel('Annualized Cond. Vol')axes[1].set_title('Panel B: Conditional Volatility')axes[1].legend(fontsize=9)plt.tight_layout()plt.show()```### Hội nhập bất đối xứngHội nhập có thể phụ thuộc vào trạng thái: đồng biến động thường tăng trong các cuộc khủng hoảng toàn cầu (lây lan) nhưng không tăng trong các giai đoạn bùng nổ cục bộ trong nước. @longin2001extreme và @ang2002asymmetric cho thấy tương quan giữa các thị trường cổ phiếu cao hơn khi thị trường suy giảm. Chúng ta kiểm định tính bất đối xứng:```{python}#| label: asymmetric-corr#| eval: false#| code-summary: "Test for asymmetric integration (bear vs bull markets)"# Classify global market regimesworld_ret_aligned = world_ret[common_dates]vn_ret_aligned = vn_ret[common_dates]# Bear: world return in bottom 25th percentile# Bull: world return in top 25th percentileq25 = world_ret_aligned.quantile(0.25)q75 = world_ret_aligned.quantile(0.75)bear = world_ret_aligned <= q25bull = world_ret_aligned >= q75normal =~bear &~bullregimes = {'Bear (bottom 25%)': bear,'Normal (middle 50%)': normal,'Bull (top 25%)': bull,'All': pd.Series(True, index=world_ret_aligned.index)}print("Asymmetric Correlation:")print(f"{'Regime':<25}{'Correlation':>12}{'N months':>10}")print("-"*47)for name, mask in regimes.items(): r_vn = vn_ret_aligned[mask] r_w = world_ret_aligned[mask] corr = r_vn.corr(r_w)print(f"{name:<25}{corr:>12.3f}{mask.sum():>10}")# Test: is bear correlation > bull correlation?r_bear_vn = vn_ret_aligned[bear]r_bear_w = world_ret_aligned[bear]r_bull_vn = vn_ret_aligned[bull]r_bull_w = world_ret_aligned[bull]# Fisher z-transformation testdef fisher_z_test(r1, n1, r2, n2): z1 = np.arctanh(r1) z2 = np.arctanh(r2) se = np.sqrt(1/ (n1 -3) +1/ (n2 -3)) z_stat = (z1 - z2) / se p_val =2* (1- stats.norm.cdf(abs(z_stat)))return z_stat, p_valz, p = fisher_z_test( r_bear_vn.corr(r_bear_w), len(r_bear_vn), r_bull_vn.corr(r_bull_w), len(r_bull_vn))print(f"\nFisher z-test (bear vs bull): z = {z:.2f}, p = {p:.4f}")```## Các thước đo hội nhập dựa trên nhân tố {#sec-market-integration-factor}### Thước đo R² của Pukthuanthong-Roll@pukthuanthong2009global đề xuất đo lường hội nhập bằng $R^2$ từ hồi quy lợi nhuận của một quốc gia trên một tập các thành phần chính toàn cầu. Trực giác như sau: nếu một thị trường hội nhập hoàn toàn, các nhân tố toàn cầu phải giải thích toàn bộ biến thiên hệ thống của lợi nhuận thị trường đó.```{python}#| label: pr-measure#| eval: false#| code-summary: "Implement the Pukthuanthong-Roll integration measure"def pukthuanthong_roll_integration(country_returns, global_returns_matrix, n_components=10, rolling_window=36):""" Pukthuanthong-Roll (2009) R²-based integration measure. 1. Extract principal components from global returns. 2. Regress country returns on these PCs. 3. R² = degree of integration. """ common = country_returns.dropna().index.intersection( global_returns_matrix.dropna().index ) dates =sorted(common) T =len(dates) integration = []for t inrange(rolling_window, T): window = dates[t - rolling_window:t]# Global returns in window G = global_returns_matrix.loc[window].dropna(axis=1)if G.shape[1] < n_components:continue# Standardize G_std = (G - G.mean()) / G.std()# PCA cov = G_std.T @ G_std /len(G_std) eigenvalues, eigenvectors = np.linalg.eigh(cov.values)# Sort descending idx = np.argsort(-eigenvalues) eigenvalues = eigenvalues[idx] eigenvectors = eigenvectors[:, idx]# Project onto top K PCs PCs = G_std.values @ eigenvectors[:, :n_components]# Regress country returns on PCs y = country_returns.loc[window].values X = sm.add_constant(PCs)try: model = sm.OLS(y, X).fit() integration.append({'date': dates[t],'r_squared': model.rsquared,'adj_r_squared': model.rsquared_adj,'var_explained_pc1': eigenvalues[0] / eigenvalues.sum(),'n_countries': G.shape[1] })exceptException:passreturn pd.DataFrame(integration)# Build global returns matrix from multiple country indicesglobal_matrix = global_indices.drop(columns=['VIETNAM'], errors='ignore')pr_result = pukthuanthong_roll_integration( indices_aligned['VIETNAM'], global_matrix, n_components=5, rolling_window=36)iflen(pr_result) >0: pr_result['date'] = pd.to_datetime(pr_result['date'])print(f"Pukthuanthong-Roll Integration (R²):")print(f" Mean: {pr_result['r_squared'].mean():.3f}")print(f" 2008-2012: {pr_result[(pr_result['date'] >='2008') & (pr_result['date'] <'2013')]['r_squared'].mean():.3f}")print(f" 2013-2018: {pr_result[(pr_result['date'] >='2013') & (pr_result['date'] <'2019')]['r_squared'].mean():.3f}")print(f" 2019-2024: {pr_result[pr_result['date'] >='2019']['r_squared'].mean():.3f}")```### Định giá theo nhân tố toàn cầu và nhân tố nội địa@griffin2002fama kiểm định xem phiên bản toàn cầu hay phiên bản nội địa của các nhân tố Fama-French giải thích tốt hơn lợi nhuận cấp quốc gia. Chúng ta triển khai cuộc "đua ngựa" này cho Việt Nam:```{python}#| label: global-vs-local#| eval: false#| code-summary: "Compare explanatory power of global vs local factor models"# Align global and local factorscommon_factor_dates = ( global_factors.index .intersection(local_factors.index) .intersection(vn_index.index))vn_excess = vn_index.loc[common_factor_dates, 'return']# Model 1: Global FF5X_global = sm.add_constant( global_factors.loc[common_factor_dates, ['mkt_excess_world', 'smb_world', 'hml_world','rmw_world', 'cma_world']])model_global = sm.OLS(vn_excess, X_global).fit( cov_type='HAC', cov_kwds={'maxlags': 6})# Model 2: Local FF5X_local = sm.add_constant( local_factors.loc[common_factor_dates, ['mkt_excess', 'smb', 'hml', 'rmw', 'cma']])model_local = sm.OLS(vn_excess, X_local).fit( cov_type='HAC', cov_kwds={'maxlags': 6})# Model 3: Both global and localX_both = sm.add_constant(pd.concat([ global_factors.loc[common_factor_dates, ['mkt_excess_world', 'smb_world', 'hml_world']], local_factors.loc[common_factor_dates, ['mkt_excess', 'smb', 'hml']]], axis=1))model_both = sm.OLS(vn_excess, X_both).fit( cov_type='HAC', cov_kwds={'maxlags': 6})print("Global vs Local Factor Models for VN-Index:")print(f"{'Model':<20}{'R²':>8}{'Adj R²':>8}{'α (ann)':>10}{'α t-stat':>10}")print("-"*56)for name, mod in [('Global FF5', model_global), ('Local FF5', model_local), ('Global + Local', model_both)]:print(f"{name:<20}{mod.rsquared:>8.3f}{mod.rsquared_adj:>8.3f} "f"{mod.params['const']*12:>10.4f}{mod.tvalues['const']:>10.2f}")``````{python}#| label: fig-global-local-r2#| eval: false#| fig-cap: "R² trượt 36 tháng từ hồi quy lợi nhuận vượt mức của thị trường Việt Nam trên các nhân tố toàn cầu (MSCI World FF5) so với các nhân tố nội địa (FF5 Việt Nam). Panel A thể hiện chuỗi thời gian R² của từng mô hình. Khi mô hình nội địa chiếm ưu thế (R² cao hơn), thị trường phân khúc hơn; khi R² của mô hình toàn cầu tiến gần R² của mô hình nội địa, thị trường hội nhập hơn. Panel B thể hiện tỷ số giữa R² toàn cầu và R² nội địa, đóng vai trò một thước đo hội nhập thay thế."#| code-summary: "Rolling R² comparison of global and local models"rolling_r2 = []rw =36for t inrange(rw, len(common_factor_dates)): window = common_factor_dates[t - rw:t] y = vn_excess[window]# Global X_g = sm.add_constant(global_factors.loc[window, ['mkt_excess_world', 'smb_world', 'hml_world','rmw_world', 'cma_world']])try: r2_g = sm.OLS(y, X_g).fit().rsquaredexcept: r2_g = np.nan# Local X_l = sm.add_constant(local_factors.loc[window, ['mkt_excess', 'smb', 'hml', 'rmw', 'cma']])try: r2_l = sm.OLS(y, X_l).fit().rsquaredexcept: r2_l = np.nan rolling_r2.append({'date': common_factor_dates[t],'r2_global': r2_g,'r2_local': r2_l,'ratio': r2_g / r2_l if r2_l >0else np.nan })r2_df = pd.DataFrame(rolling_r2)fig, axes = plt.subplots(2, 1, figsize=(14, 8), sharex=True)axes[0].plot(r2_df['date'], r2_df['r2_global'], color='#2C5F8A', linewidth=1.5, label='Global FF5')axes[0].plot(r2_df['date'], r2_df['r2_local'], color='#C0392B', linewidth=1.5, label='Local FF5')axes[0].set_ylabel('R²')axes[0].set_title('Panel A: Global vs Local Factor R²')axes[0].legend()axes[1].plot(r2_df['date'], r2_df['ratio'], color='#27AE60', linewidth=1.5)axes[1].axhline(y=1, color='gray', linewidth=1, linestyle='--', label='Full integration (ratio = 1)')axes[1].set_ylabel('Global R² / Local R²')axes[1].set_title('Panel B: Integration Ratio')axes[1].legend()axes[1].set_ylim([0, 1.5])plt.tight_layout()plt.show()```## Hội nhập dựa trên sai số định giá {#sec-market-integration-pricing-error}### Cách tiếp cận của Bekaert-Harvey@bekaert1995time đo lường hội nhập bằng khả năng của một CAPM toàn cầu trong việc định giá các tài sản nội địa. Trong điều kiện hội nhập, alpha (hệ số chặn) của thị trường nội địa trong hồi quy trên thị trường toàn cầu phải bằng không, và phần bù rủi ro toàn cầu phải giải thích lợi nhuận kỳ vọng nội địa. Trong điều kiện phân khúc, alpha nội địa phản ánh phần bù phân khúc.```{python}#| label: bh-integration#| eval: false#| code-summary: "Bekaert-Harvey integration measure: rolling alpha from global CAPM"def bekaert_harvey_integration(local_return, global_return, rolling_window=36):""" Rolling alpha from regressing local on global market. Under integration, alpha -> 0. """ common = local_return.dropna().index.intersection(global_return.dropna().index) results = []for t inrange(rolling_window, len(common)): window = common[t - rolling_window:t] y = local_return[window] X = sm.add_constant(global_return[window]) model = sm.OLS(y, X).fit() results.append({'date': common[t],'alpha': model.params['const'],'alpha_t': model.tvalues['const'],'beta_global': model.params.iloc[1],'r_squared': model.rsquared })return pd.DataFrame(results)bh_result = bekaert_harvey_integration( indices_aligned['VIETNAM'], indices_aligned['MSCI_WORLD'], rolling_window=36)# The absolute alpha is the segmentation premiumbh_result['abs_alpha_ann'] = bh_result['alpha'].abs() *12print("Bekaert-Harvey Integration Diagnostic:")print(f" Mean |α| (ann.): {bh_result['abs_alpha_ann'].mean():.4f}")print(f" Mean β_world: {bh_result['beta_global'].mean():.3f}")print(f" Mean R²: {bh_result['r_squared'].mean():.3f}")```### Chỉ số hội nhập tổng hợpChúng ta kết hợp tất cả các thước đo thành một chỉ số tổng hợp duy nhất về hội nhập của thị trường Việt Nam:```{python}#| label: composite-index#| eval: false#| code-summary: "Construct a composite integration index"# Standardize each measure to [0, 1] using historical percentile ranksmeasures = pd.DataFrame(index=bh_result['date'])# 1. DCC correlation (higher = more integrated)dcc_aligned = dcc_result['dcc_corr'].reindex(measures.index).interpolate()measures['dcc_corr'] = dcc_aligned# 2. PR R² (higher = more integrated)pr_aligned = pr_result.set_index('date')['r_squared'].reindex(measures.index).interpolate()measures['pr_r2'] = pr_aligned# 3. Global/Local R² ratio (higher = more integrated)r2_aligned = r2_df.set_index('date')['ratio'].reindex(measures.index).interpolate()measures['gl_ratio'] = r2_aligned# 4. |Alpha| from global CAPM (lower = more integrated)# Invert: 1 - percentile rank of |alpha|measures['inv_alpha'] = bh_result.set_index('date')['abs_alpha_ann']measures['inv_alpha'] =1- measures['inv_alpha'].rank(pct=True)# 5. Global beta (higher = more integrated, up to a point)measures['global_beta'] = bh_result.set_index('date')['beta_global']# Standardize to percentile ranksfor col in ['dcc_corr', 'pr_r2', 'gl_ratio', 'inv_alpha', 'global_beta']: measures[f'{col}_rank'] = measures[col].rank(pct=True)# Composite = equal-weighted average of ranksrank_cols = [c for c in measures.columns if c.endswith('_rank')]measures['composite'] = measures[rank_cols].mean(axis=1)# Smooth with 6-month moving averagemeasures['composite_smooth'] = measures['composite'].rolling(6).mean()``````{python}#| label: fig-composite#| eval: false#| fig-cap: "Chỉ số hội nhập tổng hợp của thị trường cổ phiếu Việt Nam, kết hợp tương quan DCC, R² Pukthuanthong-Roll, tỷ số R² nhân tố toàn cầu/nội địa và alpha của CAPM toàn cầu. Chỉ số được chuẩn hóa về khoảng [0, 1], trong đó 1 là mức hội nhập tối đa quan sát được. Việt Nam theo xu hướng đi lên chung với những lần thụt lùi đáng kể trong khủng hoảng tài chính toàn cầu (2008–2009) và COVID (2020). Giai đoạn sau 2018 cho thấy mức hội nhập bền vững cao nhất, trùng với việc được đưa vào FTSE và nới lỏng giới hạn sở hữu nước ngoài."#| code-summary: "Plot the composite integration index with milestones"fig, ax = plt.subplots(figsize=(14, 6))ax.fill_between(measures.index, measures['composite_smooth'], alpha=0.3, color='#2C5F8A')ax.plot(measures.index, measures['composite_smooth'], color='#2C5F8A', linewidth=2)# Add event markersfor _, event in liberalization_events.iterrows():if event['date'] >= measures.index.min(): color = event_colors.get(event['category'], 'gray') ax.axvline(x=event['date'], color=color, linewidth=1.5, linestyle='--', alpha=0.6)ax.set_ylabel('Integration Index (0 = segmented, 1 = integrated)')ax.set_title('Vietnam Equity Market Integration: Composite Index')ax.set_ylim([0, 1])# Legend for event categoriesfrom matplotlib.patches import Patchlegend_patches = [Patch(facecolor=c, label=cat)for cat, c in event_colors.items() if cat in liberalization_events['category'].values]ax.legend(handles=legend_patches, fontsize=7, loc='lower right', ncol=2)plt.tight_layout()plt.show()```## Phát hiện điểm gãy cấu trúc {#sec-market-integration-breaks}### Kiểm định Bai-Perron đối với các thay đổi chế độ hội nhậpChúng ta kiểm định xem quỹ đạo hội nhập của Việt Nam có chứa các điểm gãy cấu trúc rời rạc (tức là những dịch chuyển đột ngột về mức hội nhập) hay chỉ là một xu hướng trơn tru:```{python}#| label: structural-breaks#| eval: false#| code-summary: "Detect structural breaks in the integration time series"def detect_breaks_cusum(series, significance=0.05):""" CUSUM-based structural break detection. """ y = series.dropna().values T =len(y)# Recursive residuals from rolling mean cumsum = np.cumsum(y - y.mean()) / (y.std() * np.sqrt(T))# Brown-Durbin-Evans critical values (approximate)# At 5%: ±0.948 critical =0.948 breaks = []for t inrange(1, T -1):ifabs(cumsum[t]) > critical: breaks.append(t)return cumsum, breaks# Apply to composite indexcomposite_clean = measures['composite_smooth'].dropna()cusum, break_points = detect_breaks_cusum(composite_clean)# Alternative: Chow test at key liberalization datesdef chow_test(y, breakpoint_idx):"""Simple Chow test for structural break.""" T =len(y) y1 = y[:breakpoint_idx] y2 = y[breakpoint_idx:]# Full sample regression (on constant) rss_full = np.sum((y - y.mean()) **2)# Split samples rss1 = np.sum((y1 - y1.mean()) **2) rss2 = np.sum((y2 - y2.mean()) **2) rss_split = rss1 + rss2 k =1# Number of parameters f_stat = ((rss_full - rss_split) / k) / (rss_split / (T -2* k)) p_val =1- stats.f.cdf(f_stat, k, T -2* k)return f_stat, p_valprint("Chow Tests for Structural Breaks at Key Dates:")print(f"{'Event':<30}{'Date':>12}{'F-stat':>10}{'p-value':>10}")print("-"*62)for _, event in liberalization_events.iterrows():if event['date'] < composite_clean.index.min():continue# Find nearest date nearest = composite_clean.index.searchsorted(event['date'])if nearest <12or nearest >len(composite_clean) -12:continue f_stat, p_val = chow_test(composite_clean.values, nearest) sig ='***'if p_val <0.01else'**'if p_val <0.05else'*'if p_val <0.1else''print(f"{event['event']:<30}{event['date'].strftime('%Y-%m'):>12} "f"{f_stat:>10.2f}{p_val:>10.4f}{sig}")```## Phần bù phân khúc của Việt Nam {#sec-market-integration-premium}### Bằng chứng chéo (cross-sectional)Trong điều kiện phân khúc một phần, các cổ phiếu có tỷ lệ sở hữu nước ngoài cao hơn phải có lợi nhuận kỳ vọng thấp hơn (vì nhà đầu tư nước ngoài có thể đa dạng hóa bớt rủi ro nội địa). Điều này đưa ra một dự đoán kiểm định được: sở hữu nước ngoài phải có quan hệ nghịch chiều với lợi nhuận kỳ vọng, sau khi kiểm soát mức phơi nhiễm rủi ro toàn cầu.```{python}#| label: segmentation-premium#| eval: false#| code-summary: "Estimate the cross-sectional segmentation premium"# Get monthly stock returns with foreign ownershipstock_data = client.get_monthly_returns( exchanges=['HOSE', 'HNX'], start_date='2008-01-01', end_date='2024-12-31', fields=['ticker', 'month_end', 'monthly_return', 'market_cap','foreign_ownership_pct'])stock_data['month_end'] = pd.to_datetime(stock_data['month_end'])# Fama-MacBeth: regress returns on lagged foreign ownershipstock_data = stock_data.sort_values(['ticker', 'month_end'])stock_data['fol_lag'] = ( stock_data.groupby('ticker')['foreign_ownership_pct'].shift(1))stock_data['log_mcap'] = np.log(stock_data['market_cap'].clip(lower=1))# Monthly cross-sectional regressionsgamma_fol = []for month, group in stock_data.dropna(subset=['fol_lag', 'monthly_return']).groupby('month_end'):iflen(group) <100:continue y = group['monthly_return'].values X = sm.add_constant(group[['fol_lag', 'log_mcap']].values)try: model = sm.OLS(y, X).fit() gamma_fol.append({'month': month,'gamma_fol': model.params[1],'gamma_size': model.params[2],'n': len(group) })except:passgamma_fol_df = pd.DataFrame(gamma_fol)mean_gamma = gamma_fol_df['gamma_fol'].mean()se_gamma = gamma_fol_df['gamma_fol'].std() / np.sqrt(len(gamma_fol_df))t_gamma = mean_gamma / se_gammaprint(f"Fama-MacBeth: Foreign Ownership and Expected Returns")print(f" γ_FOL (monthly): {mean_gamma:.6f}")print(f" γ_FOL (ann.): {mean_gamma *12:.4f}")print(f" t-statistic: {t_gamma:.2f}")print(f" Interpretation: A 10pp increase in foreign ownership is "f"associated with a {mean_gamma *12*10:.2f}% change in "f"annual expected returns")``````{python}#| label: fig-segmentation-premium#| eval: false#| fig-cap: "Phần bù phân khúc ở Việt Nam. Panel A thể hiện lợi nhuận của các danh mục ngũ phân vị sắp xếp theo sở hữu nước ngoài có độ trễ: độ dốc âm (FOL cao → lợi nhuận thấp hơn) phù hợp với lý thuyết phân khúc. Panel B thể hiện hệ số Fama-MacBeth trượt trên sở hữu nước ngoài, tức phần bù phân khúc biến đổi theo thời gian. Phần bù này đã thu hẹp dần theo thời gian khi thị trường hội nhập."#| code-summary: "Visualize the segmentation premium"fig, axes = plt.subplots(1, 2, figsize=(14, 5))# Panel A: Quintile returns by foreign ownershipfol_quintiles = stock_data.dropna(subset=['fol_lag', 'monthly_return']).copy()fol_quintiles['fol_q'] = ( fol_quintiles.groupby('month_end')['fol_lag'] .transform(lambda x: pd.qcut(x.rank(method='first'), 5, labels=['Q1\n(Low FOL)', 'Q2', 'Q3', 'Q4','Q5\n(High FOL)'])))q_returns = ( fol_quintiles.groupby('fol_q')['monthly_return'] .mean() *12*100)colors_q = plt.cm.RdYlGn_r(np.linspace(0.2, 0.8, 5))axes[0].bar(range(5), q_returns.values, color=colors_q, edgecolor='white', alpha=0.85)axes[0].set_xticks(range(5))axes[0].set_xticklabels(q_returns.index)axes[0].set_ylabel('Ann. Return (%)')axes[0].set_title('Panel A: Returns by Foreign Ownership Quintile')axes[0].axhline(y=0, color='black', linewidth=0.5)# Panel B: Rolling FM coefficientgamma_fol_df['date'] = pd.to_datetime(gamma_fol_df['month'])rolling_gamma = gamma_fol_df.set_index('date')['gamma_fol'].rolling(24).mean() *12axes[1].plot(rolling_gamma.index, rolling_gamma.values, color='#2C5F8A', linewidth=1.5)axes[1].axhline(y=0, color='black', linewidth=0.5)axes[1].set_ylabel('γ_FOL (annualized)')axes[1].set_title('Panel B: Rolling Segmentation Premium')axes[1].fill_between(rolling_gamma.index, rolling_gamma.values, 0, where=rolling_gamma.values <0, alpha=0.3, color='#27AE60', label='Negative (integration)')axes[1].fill_between(rolling_gamma.index, rolling_gamma.values, 0, where=rolling_gamma.values >=0, alpha=0.3, color='#C0392B', label='Positive (segmentation)')axes[1].legend(fontsize=8)plt.tight_layout()plt.show()```## Rủi ro tỷ giá hối đoái và hội nhập {#sec-market-integration-fx}### Rủi ro tiền tệ có được định giá không?Trong một thị trường hội nhập một phần, rủi ro tỷ giá hối đoái có thể mang một phần bù riêng. @jorion1991pricing và @dumas1995world kiểm định xem mức phơi nhiễm tiền tệ có được định giá ngoài rủi ro cổ phiếu toàn cầu hay không. Với Việt Nam, tỷ giá VND/USD được quản lý (neo trượt, thỉnh thoảng có những đợt phá giá theo bậc), tạo ra một rủi ro đặc thù mà không thể đa dạng hóa hoàn toàn cũng không được các nhân tố cổ phiếu toàn cầu định giá đầy đủ.```{python}#| label: fx-risk#| eval: false#| code-summary: "Test whether VND/USD exchange rate risk is priced"# VND depreciationfx_return = fx['rate'].pct_change()fx_return.name ='fx_return'# Merge with stock datastock_fx = stock_data.merge( fx_return.to_frame().reset_index().rename(columns={'date': 'month_end'}), on='month_end', how='left')# Estimate FX beta for each stock (rolling 60-month)# Then test in Fama-MacBeth whether FX beta is pricedfx_betas = {}for ticker, group in stock_fx.groupby('ticker'):iflen(group) <60:continue group = group.sort_values('month_end') y = group['monthly_return'] x = group[['fx_return']].dropna() common = y.dropna().index.intersection(x.index)iflen(common) <48:continue model = sm.OLS(y[common], sm.add_constant(x.loc[common])).fit() fx_betas[ticker] = model.params.get('fx_return', np.nan)fx_beta_series = pd.Series(fx_betas, name='fx_beta')# Cross-sectional test: do stocks with higher FX beta earn different returns?stock_fx_beta = stock_fx.merge( fx_beta_series.to_frame().reset_index().rename(columns={'index': 'ticker'}), on='ticker', how='left')# Quintile sort on FX betafx_q = stock_fx_beta.dropna(subset=['fx_beta', 'monthly_return'])fx_q['fx_quintile'] = pd.qcut(fx_q['fx_beta'].rank(method='first'),5, labels=False)fx_premium = fx_q.groupby('fx_quintile')['monthly_return'].mean() *12print("Returns by FX Beta Quintile:")for q, ret in fx_premium.items():print(f" Q{q+1}: {ret*100:.2f}% ann.")print(f" Q5-Q1: {(fx_premium.iloc[-1] - fx_premium.iloc[0])*100:.2f}% ann.")```## Lây lan và phụ thuộc lẫn nhau {#sec-market-integration-contagion}Trong các cuộc khủng hoảng toàn cầu, tương quan giữa Việt Nam và các thị trường thế giới tăng vọt. Câu hỏi là điều này thể hiện *lây lan* (contagion, một thay đổi cấu trúc trong cơ chế truyền dẫn) hay chỉ là *phụ thuộc lẫn nhau* (interdependence, đồng biến động bình thường được khuếch đại bởi biến động cao hơn). @longin2001extreme cho thấy tương quan tăng một cách cơ học cùng với biến động, ngay cả khi cấu trúc phụ thuộc cơ bản không hề thay đổi.```{python}#| label: contagion-test#| eval: false#| code-summary: "Test for contagion during major crisis episodes"def forbes_rigobon_test(r_local, r_global, crisis_dates, tranquil_dates):""" Forbes-Rigobon (2002) contagion test. Adjusts for heteroskedasticity-induced bias in correlation. H0: No contagion (correlation increase is explained by volatility) H1: Contagion (correlation increase exceeds what volatility explains) """ r_l_crisis = r_local[crisis_dates] r_g_crisis = r_global[crisis_dates] r_l_tranquil = r_local[tranquil_dates] r_g_tranquil = r_global[tranquil_dates]# Unadjusted correlations rho_crisis = r_l_crisis.corr(r_g_crisis) rho_tranquil = r_l_tranquil.corr(r_g_tranquil)# Volatility ratio delta = r_g_crisis.var() / r_g_tranquil.var() -1# Adjusted correlation rho_adj = rho_crisis / np.sqrt(1+ delta * (1- rho_crisis **2))# Fisher z-test on adjusted vs tranquil z_adj = np.arctanh(rho_adj) z_tranquil = np.arctanh(rho_tranquil) se = np.sqrt(1/ (len(r_l_crisis) -3) +1/ (len(r_l_tranquil) -3)) z_stat = (z_adj - z_tranquil) / se p_val =2* (1- stats.norm.cdf(abs(z_stat)))return {'rho_crisis_raw': rho_crisis,'rho_crisis_adj': rho_adj,'rho_tranquil': rho_tranquil,'delta': delta,'z_stat': z_stat,'p_value': p_val,'contagion': p_val <0.05 }# Define crisis and tranquil periodscrises = {'GFC (2008-09)': (pd.Timestamp('2008-09-01'), pd.Timestamp('2009-03-31')),'European Debt (2011-12)': (pd.Timestamp('2011-06-01'), pd.Timestamp('2012-06-30')),'COVID (2020)': (pd.Timestamp('2020-02-01'), pd.Timestamp('2020-06-30')),'Fed Tightening (2022)': (pd.Timestamp('2022-01-01'), pd.Timestamp('2022-12-31')),}# Tranquil = 24 months before each crisisvn_aligned = indices_aligned['VIETNAM']world_aligned = indices_aligned['MSCI_WORLD']print("Contagion Tests (Forbes-Rigobon):")print(f"{'Crisis':<28}{'ρ(raw)':>8}{'ρ(adj)':>8}{'ρ(calm)':>8} "f"{'z-stat':>8}{'p-val':>8}{'Result':>12}")print("-"*80)for name, (start, end) in crises.items(): crisis_mask = (vn_aligned.index >= start) & (vn_aligned.index <= end) tranquil_start = start - pd.DateOffset(months=24) tranquil_mask = ((vn_aligned.index >= tranquil_start) & (vn_aligned.index < start)) crisis_dates = vn_aligned.index[crisis_mask] tranquil_dates = vn_aligned.index[tranquil_mask]iflen(crisis_dates) <3orlen(tranquil_dates) <12:continue result = forbes_rigobon_test(vn_aligned, world_aligned, crisis_dates, tranquil_dates) verdict ='CONTAGION'if result['contagion'] else'Interdependence'print(f"{name:<28}{result['rho_crisis_raw']:>8.3f} "f"{result['rho_crisis_adj']:>8.3f}{result['rho_tranquil']:>8.3f} "f"{result['z_stat']:>8.2f}{result['p_value']:>8.3f} "f"{verdict:>12}")```## So sánh với các nước ASEAN {#sec-market-integration-asean}Quỹ đạo hội nhập của Việt Nam được hiểu rõ nhất trong bối cảnh các nước ASEAN, vốn có điều kiện xuất phát tương tự nhưng đã đi theo các con đường tự do hóa khác nhau:```{python}#| label: fig-asean-comparison#| eval: false#| fig-cap: "Mức độ hội nhập với MSCI World giữa các thị trường ASEAN, đo bằng tương quan trượt 36 tháng. Thái Lan và Malaysia, những nước tự do hóa sớm hơn, có tương quan cao hơn và ổn định hơn. Hội nhập của Việt Nam tăng tốc từ năm 2015, hội tụ dần về phía các nước trong khu vực nhưng vẫn thấp hơn Thái Lan và Malaysia. Indonesia và Philippines ở vị trí trung gian."#| code-summary: "Compare integration trajectories across ASEAN markets"fig, ax = plt.subplots(figsize=(14, 6))asean_markets = {'VIETNAM': '#C0392B','MSCI_THAILAND': '#2C5F8A','MSCI_INDONESIA': '#27AE60','MSCI_PHILIPPINES': '#E67E22','MSCI_MALAYSIA': '#8E44AD'}for market, color in asean_markets.items():if market notin global_indices.columns:continue corr = ( global_indices[['MSCI_WORLD', market]] .rolling(36) .corr() .unstack()['MSCI_WORLD'][market] ) label = market.replace('MSCI_', '').replace('_', ' ').title()if market =='VIETNAM': ax.plot(corr.index, corr.values, color=color, linewidth=2.5, label=label, zorder=5)else: ax.plot(corr.index, corr.values, color=color, linewidth=1.5, label=label, alpha=0.7)ax.set_ylabel('Correlation with MSCI World')ax.set_title('ASEAN Market Integration: Rolling 36-Month Correlation')ax.legend(fontsize=9)ax.set_ylim([-0.2, 0.9])ax.axhline(y=0, color='black', linewidth=0.5)plt.tight_layout()plt.show()```## Hàm ý thực tiễn {#sec-market-integration-implications}Mức độ hội nhập quyết định mô hình định giá tài sản nào phù hợp cho cổ phiếu Việt Nam. Bằng chứng trong chương này ủng hộ một số kết luận thực tiễn:**Việt Nam hội nhập một phần và đang có xu hướng hội nhập thêm.** Chỉ số tổng hợp cho thấy quỹ đạo đi lên rõ ràng, trong đó giai đoạn sau năm 2015 là giai đoạn hội nhập bền vững cao nhất trong lịch sử thị trường. Tuy nhiên, Việt Nam vẫn kém hội nhập hơn Thái Lan hoặc Malaysia, và còn xa mới hội nhập hoàn toàn với các thị trường toàn cầu.**Các nhân tố nội địa chiếm ưu thế so với các nhân tố toàn cầu trong định giá cổ phiếu Việt Nam.** So sánh $R^2$ trượt cho thấy các nhân tố nội địa của Việt Nam luôn giải thích được nhiều biến thiên lợi nhuận hơn các nhân tố toàn cầu. Điều này có nghĩa là các nhà nghiên cứu về lợi nhuận cắt ngang (cross-section) của cổ phiếu Việt Nam nên dùng các mô hình nhân tố nội địa (FF5 Việt Nam) thay vì các nhân tố toàn cầu. Các nhân tố toàn cầu chủ yếu hữu ích cho nhà đầu tư quốc tế khi đánh giá rủi ro đồng biến động.**Phần bù phân khúc đang thu hẹp nhưng chưa bằng không.** Bằng chứng Fama-MacBeth cho thấy các cổ phiếu có sở hữu nước ngoài cao hơn có lợi nhuận thấp hơn, phù hợp với phân khúc một phần. Mức độ này đã giảm theo thời gian khi các giới hạn sở hữu nước ngoài được nới lỏng, nhưng một phần bù còn lại vẫn tồn tại, nhiều khả năng do các mức trần sở hữu còn áp dụng trong ngành ngân hàng và các lĩnh vực chiến lược.**Đồng biến động trong thời kỳ khủng hoảng chủ yếu là phụ thuộc lẫn nhau, không phải lây lan.** Các tương quan điều chỉnh Forbes-Rigobon cho thấy việc tương quan Việt Nam-thế giới tăng vọt trong khủng hoảng phần lớn được giải thích bởi biến động toàn cầu gia tăng, chứ không phải do thay đổi cấu trúc trong cơ chế truyền dẫn. Điều này đáng yên tâm đối với đa dạng hóa: Việt Nam vẫn mang lại lợi ích đa dạng hóa đáng kể ngay cả trong thời kỳ căng thẳng toàn cầu.**Lộ trình nâng hạng của FTSE/MSCI rất quan trọng.** Việc Việt Nam có thể được nâng hạng từ thị trường cận biên (frontier) lên thị trường mới nổi sẽ kích hoạt việc tái cân bằng danh mục bắt buộc theo chỉ số của các quỹ thụ động, làm tăng dòng vốn nước ngoài và nhiều khả năng đẩy nhanh hội nhập. Các nhà nghiên cứu và nhà đầu tư nên theo dõi các tiêu chí nâng hạng và tác động của chúng đến chi phí vốn.## Tóm tắt {#sec-market-integration-summary}| Thước đo | Nội dung phản ánh | Khoảng giá trị của Việt Nam | Mức hiện tại | Xu hướng ||---------------|---------------|---------------|---------------|---------------|| Tương quan DCC (thế giới) | Đồng biến động | 0.0–0.5 | \~0.35–0.45 | Tăng || PR R² (PC toàn cầu) | Mức phơi nhiễm nhân tố toàn cầu | 0.05–0.50 | \~0.30–0.40 | Tăng || Tỷ số R² toàn cầu/nội địa | Sức mạnh định giá tương đối | 0.1–0.8 | \~0.5–0.6 | Tăng || α CAPM toàn cầu | Sai số định giá | 0–15%/năm | \~3–5%/năm | Giảm || Phần bù FOL (γ) | Chi phí phân khúc | -5% đến +2% | \~-1% đến 0% | Thu hẹp |: Tóm tắt các thước đo hội nhập của thị trường cổ phiếu Việt Nam. {#tbl-market-integration-summary}<!-- ## Bài tập {#sec-market-integration-exercises}1. **Mô hình chuyển đổi chế độ.** Ước lượng mô hình chuyển đổi chế độ của @bekaert1995time cho Việt Nam với hai trạng thái (hội nhập, phân khúc). Dùng bộ lọc Hamilton để trích xuất xác suất được làm mượt của việc ở trạng thái hội nhập tại mỗi thời điểm. Xác suất được lọc có khớp với chỉ số tổng hợp trong chương này không?2. **Kiểm định bao phủ (spanning).** Một nhà đầu tư Mỹ có thể sao chép lợi nhuận thị trường Việt Nam bằng một danh mục các ETF niêm yết tại Mỹ (ví dụ VNM ETF, EEM, FXI, EWY) không? Hồi quy VN-Index trên các ETF này và xem xét alpha. Alpha bằng không nghĩa là thị trường Việt Nam được "bao phủ" và không mang lại lợi ích đa dạng hóa nào ngoài những gì có thể đạt được qua các công cụ niêm yết tại Mỹ [@errunza1985international].3. **Dòng vốn và hội nhập.** Thu thập dữ liệu dòng vốn nước ngoài ròng hằng tháng của Việt Nam (từ UBCKNN hoặc ngân hàng trung ương). Kiểm định xem các tháng có dòng vốn vào cao có được theo sau bởi tương quan Việt Nam-thế giới cao hơn hay không. Điều này kiểm định xem hội nhập được thúc đẩy bởi dòng vốn hay bởi các kênh khác (thương mại, thông tin).4. **Hội nhập cấp ngành.** Ước lượng tương quan DCC riêng cho các ngành ngân hàng, bất động sản, hàng tiêu dùng và công nghệ của Việt Nam so với các ngành tương ứng trên thế giới. Ngành nào hội nhập nhất? Các ngành có sở hữu nhà nước lớn có thể bị phân khúc nhiều hơn.5. **Hội nhập và chi phí vốn.** Dùng khung phân tích của @bekaert2005does, ước lượng mức giảm chi phí vốn chủ sở hữu hàm ý của Việt Nam gắn với mức tăng hội nhập đo được từ năm 2000. Dùng các phần bù rủi ro hàm ý từ CAPM nội địa so với CAPM toàn cầu để định lượng "cổ tức hội nhập".6. **Chỉ số Chinn-Ito.** Tải chỉ số mức độ mở cửa tài khoản vốn của @chinn2006what (KAOPEN) cho Việt Nam và các nước ASEAN. Vẽ KAOPEN cùng với các thước đo hội nhập dựa trên thị trường trong chương này. Mức độ mở cửa de jure (tự do hóa tài khoản vốn theo pháp lý) có phải là yếu tố dự báo tốt cho hội nhập de facto (đồng biến động thị trường) không?7. **Hội nhập trong miền tần số.** Phân rã đồng biến động Việt Nam-thế giới theo dải tần số (tuần, tháng, quý, năm) bằng wavelet coherence. Hội nhập có thể khác nhau ở các tần số khác nhau: đồng biến động tần số cao phản ánh chênh lệch giá (arbitrage) và truyền tải thông tin, còn đồng biến động tần số thấp phản ánh các chu kỳ kinh doanh chung.8. **Kiểm tra sức chịu đựng với COVID.** Cú sụp đổ do COVID vào tháng 3 năm 2020 là đợt bán tháo đồng loạt toàn cầu nghiêm trọng nhất trong một thập kỷ. Dùng dữ liệu hằng ngày, ước lượng tương quan theo giờ hoặc theo ngày giữa Việt Nam và các thị trường toàn cầu trong cuộc khủng hoảng. Biên độ giá của Việt Nam (chương về biên độ giá) có ngăn chặn hoàn toàn sự lây lan, hay chỉ làm chậm lại? -->