Trong chương này, chúng ta xây dựng một bộ thước đo thanh khoản toàn diện cho thị trường chứng khoán Việt Nam, đối chiếu chúng với nhau và với các chuẩn mực đã biết, kiểm định xem thanh khoản có được định giá trong mặt cắt ngang của lợi nhuận cổ phiếu hay không, khảo sát tính đồng biến (commonality) của thanh khoản, và phân tích cách các điều kiện thanh khoản thay đổi theo thời gian và giữa các trạng thái thị trường.
Thanh khoản (tức là khả năng giao dịch nhanh với chi phí thấp mà không làm dịch chuyển giá) có lẽ là cân nhắc thực tiễn quan trọng nhất đối với bất kỳ ai làm việc với dữ liệu chứng khoán Việt Nam. Một phần bù nhân tố trông hấp dẫn trong kiểm định ngược (backtest) không có ma sát có thể hoàn toàn không thể triển khai nếu các vị thế mua và bán khống tập trung vào những cổ phiếu kém thanh khoản, vốn có giá dịch chuyển bất lợi cho bạn khi bạn giao dịch. Ngược lại, một phần bù thanh khoản thực sự (tức là khoản bù đắp cho rủi ro cổ phiếu khó bán khi bạn cần bán) là một trong những hiện tượng bất thường vững chắc nhất và có nền tảng lý thuyết tốt nhất trong định giá tài sản.
Thách thức nằm ở chỗ thanh khoản vốn đa chiều và khó đo lường. Ở các thị trường phát triển với sổ lệnh giới hạn liên tục và báo cáo giao dịch dưới một giây, các nhà nghiên cứu có thể quan sát trực tiếp chênh lệch giá mua–bán, độ sâu thị trường và tác động giá. Ở Việt Nam, dữ liệu vi cấu trúc ở mức chi tiết này còn hạn chế: HOSE vận hành khớp lệnh định kỳ lúc mở cửa và đóng cửa với khớp lệnh liên tục ở giữa, bước giá thô so với mặt bằng giá, và nhiều cổ phiếu giao dịch thưa đến mức khái niệm “chênh lệch giá chào” chỉ có ý nghĩa vào những ngày cổ phiếu thực sự có giao dịch. Điều này buộc các nhà nghiên cứu phải dựa vào các thước đo xấp xỉ tần suất thấp được tính từ dữ liệu giá và khối lượng theo ngày.
Chương này xây dựng các thước đo xấp xỉ thanh khoản chính được dùng trong tài liệu học thuật, kiểm chứng chúng trong bối cảnh Việt Nam, và minh họa việc sử dụng chúng trong định giá tài sản và xây dựng danh mục đầu tư.
14.1 Nền tảng Lý thuyết
14.1.1 Vì sao Thanh khoản Quan trọng
Thanh khoản tác động đến giá tài sản qua ít nhất ba kênh:
Hiệu ứng mức độ. Nhà đầu tư đòi hỏi được bù đắp cho chi phí giao dịch kỳ vọng. Amihud và Mendelson (1986) chỉ ra rằng các cổ phiếu có chênh lệch giá mua–bán cao hơn mang lại lợi nhuận kỳ vọng cao hơn, với phần bù là hàm tăng của thời gian nắm giữ của nhà đầu tư. Ở trạng thái cân bằng, các cổ phiếu kém thanh khoản phải đem lại lợi nhuận kỳ vọng cao hơn để bù đắp chi phí giao dịch khứ hồi cao hơn.
Hiệu ứng rủi ro. Thanh khoản biến đổi theo thời gian và đồng biến giữa các cổ phiếu. Pástor và Stambaugh (2003) chỉ ra rằng các cổ phiếu có lợi nhuận nhạy hơn với các cú sốc thanh khoản tổng hợp mang lại lợi nhuận kỳ vọng cao hơn. Acharya và Pedersen (2005) chính thức hóa điều này trong một CAPM điều chỉnh theo thanh khoản, trong đó lợi nhuận đòi hỏi bao gồm phần bù cho rủi ro thanh khoản (tức là rủi ro cổ phiếu trở nên kém thanh khoản đúng vào lúc nhà đầu tư cần bán).
Hiệu ứng đồng biến.Chordia, Roll, và Subrahmanyam (2000) ghi nhận rằng thanh khoản của từng cổ phiếu đồng biến mạnh với thanh khoản toàn thị trường. Brunnermeier và Pedersen (2009) giải thích điều này qua một “vòng xoáy thanh khoản”: khi giá trị tài sản giảm, các ràng buộc ký quỹ siết chặt, buộc các nhà đầu tư dùng đòn bẩy phải bán, làm giảm thanh khoản thị trường, từ đó đẩy giá xuống thấp hơn nữa. Cơ chế này đặc biệt phù hợp với Việt Nam, nơi nhà đầu tư cá nhân có tài khoản ký quỹ là nhóm giao dịch chiếm ưu thế.
Độ chặt (tightness). Chi phí để đảo vị thế một cách nhanh chóng, được đo bằng chênh lệch giá mua–bán.
Độ sâu (depth). Khối lượng có thể giao dịch mà không làm dịch chuyển giá, liên quan đến tác động giá.
Khả năng phục hồi (resiliency). Tốc độ giá phục hồi sau các cú sốc dòng lệnh không mang thông tin.
Không có thước đo đơn lẻ nào nắm bắt được cả ba chiều. Goyenko, Holden, và Trzcinka (2009) và Fong, Holden, và Trzcinka (2017) đánh giá một cách hệ thống thước đo xấp xỉ tần suất thấp nào nắm bắt tốt nhất từng chiều bằng cách đối chiếu với các thước đo tần suất cao. Phát hiện chính của họ: thước đo Amihud (2002) nắm bắt tốt nhất chiều tác động giá, ước lượng Roll (1984) và chênh lệch Corwin và Schultz (2012) nắm bắt tốt nhất độ chặt, còn thước đo lợi nhuận bằng không của Lesmond, Ogden, và Trzcinka (1999) nắm bắt sự kết hợp giữa chi phí giao dịch và bất cân xứng thông tin. Riêng đối với các thị trường mới nổi, Fong, Holden, và Trzcinka (2017) khuyến nghị thước đo Amihud và Chênh lệch giá chào theo phần trăm lúc đóng cửa (Closing Percent Quoted Spread) là các thước đo xấp xỉ đáng tin cậy nhất.
14.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 statsfrom linearmodels.panel import PanelOLSfrom linearmodels.asset_pricing import LinearFactorModelimport warningswarnings.filterwarnings('ignore')plt.rcParams.update({'figure.figsize': (12, 6),'figure.dpi': 150,'font.size': 11,'axes.spines.top': False,'axes.spines.right': False})
Chúng ta xây dựng bảy thước đo xấp xỉ thanh khoản bao trùm các chiều độ chặt, độ sâu và khả năng phục hồi. Mỗi thước đo được tính ở cấp công ty–tháng, tạo ra một bảng dữ liệu có thể ghép với dữ liệu lợi nhuận theo tháng cho các kiểm định mặt cắt ngang.
14.3.1 Tỷ số Phi thanh khoản Amihud
Thước đo phi thanh khoản Amihud (2002) là tỷ số giữa giá trị tuyệt đối của lợi nhuận theo ngày và khối lượng giao dịch theo ngày (tính bằng VND):
trong đó \(|R_{i,d}|\) là giá trị tuyệt đối của lợi nhuận theo ngày, \(\text{DVOL}_{i,d}\) là khối lượng giao dịch tính bằng VND trong ngày \(d\), và \(D_{i,m}\) là số ngày giao dịch có khối lượng dương trong tháng \(m\). Giá trị càng cao cho thấy mức phi thanh khoản càng lớn.
Thước đo Amihud nắm bắt chiều tác động giá của thanh khoản. Nó dựa trên mô hình Kyle (1985), trong đó tham số \(\lambda\) (lambda của Kyle) đo tác động giá của dòng lệnh: \(\Delta p = \lambda \cdot Q\). Tỷ số Amihud là phiên bản tương tự của \(\lambda\) ở tần suất theo ngày.
Compute monthly Amihud illiquidity ratio
def compute_amihud(daily_df, min_days=10):""" Compute the Amihud (2002) illiquidity ratio at the firm-month level. Excludes zero-volume days. Requires at least min_days observations with positive volume per firm-month. """ df = daily_df[daily_df['volume'] >0].copy() df['month'] = df['date'].dt.to_period('M')# |Return| / VND Volume df['illiq_daily'] = df['abs_return'] / df['turnover_value']# Remove extreme outliers (top 0.1% within each month) df['illiq_daily'] = df.groupby('month')['illiq_daily'].transform(lambda x: x.clip(upper=x.quantile(0.999)) )# Aggregate to firm-month amihud = ( df.groupby(['ticker', 'month']) .agg( amihud_raw=('illiq_daily', 'mean'), n_positive_vol_days=('illiq_daily', 'count') ) .reset_index() )# Filter: require minimum trading days amihud = amihud[amihud['n_positive_vol_days'] >= min_days]# Log transform (raw Amihud is heavily right-skewed) amihud['amihud'] = np.log(1+ amihud['amihud_raw'] *1e6)# Convert period to timestamp for merging amihud['month_end'] = amihud['month'].dt.to_timestamp('M')return amihud[['ticker', 'month_end', 'amihud', 'amihud_raw','n_positive_vol_days']]amihud_monthly = compute_amihud(daily)print(f"Amihud observations: {len(amihud_monthly):,}")print(f"\nLog Amihud distribution:")print(amihud_monthly['amihud'].describe().round(3))
14.3.1.1 Định nghĩa Gốc năm 2002
Phương trình 37.2 là dạng hiện đại của thước đo, không phải dạng mà Amihud (2002) thực sự ước lượng. Dạng gốc tính theo năm chứ không phải theo tháng:
trong đó \(\text{VOLD}_{i,y,d}\) là giá trị giao dịch bằng đô la trong ngày \(d\) của năm \(y\) và \(D_{i,y}\) là số ngày trong năm \(y\) có đủ cả dữ liệu lợi nhuận lẫn khối lượng. Những khác biệt so với Phương trình 37.2 không chỉ là hình thức, và mỗi khác biệt là một quyết định mô hình hóa mà các tác giả sau này đã lặng lẽ thay đổi:
Theo năm, không theo tháng. Đối tượng quan tâm của Amihud là mức phi thanh khoản kỳ vọng, mà ông mô hình hóa như một đại lượng biến đổi chậm. Ông hồi quy lợi nhuận theo tháng trong năm \(y\) trên \(\text{ILLIQ}\) đo trong năm \(y-1\), nên thước đo là một đặc điểm công ty có tính dai dẳng chứ không phải một biến trạng thái theo tháng. Quy ước theo tháng hiện đang thống trị trong tài liệu đánh đổi tính dai dẳng để lấy độ nhạy, và chịu nhiều nhiễu đo lường hơn đáng kể trên mỗi quan sát: lấy trung bình khoảng 21 tỷ số theo ngày thay vì 250 làm sai số lấy mẫu của giá trị trung bình tăng khoảng \(\sqrt{250/21} \approx 3.5\) lần.
Chỉ NYSE. Amihud cố ý loại NASDAQ, vì các giao dịch giữa các nhà tạo lập trên thị trường qua nhà tạo lập (dealer market) tính trùng khối lượng và sẽ làm nhiễm bẩn mẫu số. Điểm tương ứng ở Việt Nam không phải là thị trường qua nhà tạo lập mà là phân khúc giao dịch thỏa thuận (put-through, giao dịch khối lượng lớn thương lượng) và, trên UPCoM, hoạt động giao dịch cực kỳ rời rạc; cả hai đều làm méo \(\text{DVOL}\) theo những cách mà thiết kế gốc được xây dựng để tránh.
Các bộ lọc. Ông yêu cầu có hơn 200 ngày dữ liệu trong năm, giá cuối năm trên $5, và có số cổ phiếu đang lưu hành, đồng thời cắt bỏ 1% cao nhất và thấp nhất của mặt cắt ngang \(\text{ILLIQ}\) theo năm như các giá trị ngoại lai. Bộ lọc giá loại bỏ các cổ phiếu mà tính rời rạc của bước giá chi phối tử số, một bộ lọc có tác động thực sự ở Việt Nam, nơi một phần lớn các mã niêm yết giao dịch dưới 10,000 VND với bước giá 10 VND.
Hệ số quy đổi. Amihud nhân với \(10^6\), đây là một quy ước đơn vị gắn với đô la, không phải thuộc tính của ước lượng. Chúng ta sẽ quay lại điểm này bên dưới, vì đây là nguồn phổ biến nhất khiến các con số Amihud trong tài liệu không thể so sánh với nhau.
Điều chỉnh theo trung bình. Đây là thành phần của bài báo gốc bị bỏ qua thường xuyên nhất và là thành phần quan trọng nhất đối với Việt Nam. Vì giá trị giao dịch bằng đô la tăng theo xu hướng dài hạn cùng với lạm phát, tăng trưởng thị trường và tỷ lệ vòng quay tăng, \(\text{ILLIQ}\) thô mang một xu hướng giảm cơ học không liên quan gì đến việc thanh khoản được cải thiện. Do đó Amihud cũng làm việc với thước đo đã điều chỉnh theo trung bình
vốn không có đơn vị và đã được khử xu hướng theo cấu trúc. Trong một thị trường mà giá trị giao dịch bằng VND tăng gấp cả một bậc độ lớn trong giai đoạn mẫu, việc dùng \(\text{ILLIQ}\) thô trong bất kỳ đặc tả gộp (pooled) hay chuỗi thời gian nào mà không có hiệu ứng cố định theo thời kỳ sẽ cho thấy một “sự cải thiện thanh khoản” thực chất là sản phẩm giả tạo của mẫu số.
Hai kết quả chính của ông đáng được tách bạch, vì chúng thường bị nhập làm một. Trong mặt cắt ngang, mức phi thanh khoản kỳ vọng được định giá dương: các cổ phiếu có \(\text{ILLIQMA}\) năm trước cao hơn mang lại lợi nhuận sau đó cao hơn. Trong chuỗi thời gian, mức phi thanh khoản không được kỳ vọng — cú sốc (innovation) từ một AR(1) khớp với \(\text{AILLIQ}_y\) — gắn với lợi nhuận đồng thời âm, vì một cú sốc thanh khoản được tính ngay vào giá dưới dạng giá giảm dù nó làm tăng lợi nhuận kỳ vọng trong tương lai. Một kiểm định trộn lẫn hai điều này sẽ thấy dấu của kết quả gây nhầm lẫn.
14.3.1.2 Tiền thân và Lý thuyết: Vì sao là Tỷ số Lợi nhuận trên Khối lượng
Thước đo này không xuất hiện từ hư không. Amihud và Mendelson (1986) đã chỉ ra, cả về lý thuyết lẫn thực nghiệm, rằng lợi nhuận kỳ vọng tăng theo chênh lệch giá mua–bán chào, vì nhà đầu tư phải được bù đắp cho chi phí của một vòng khứ hồi. Vấn đề của kết quả đó là dữ liệu: chênh lệch giá chào đòi hỏi lịch sử các báo giá, vốn không tồn tại ở hầu hết các thị trường và trong phần lớn lịch sử. Thước đo năm 2002 được hiểu tốt nhất như một câu trả lời cho câu hỏi “chỉ từ lợi nhuận và khối lượng theo ngày, ta có thể tính được đại lượng nào hành xử như một chi phí giao dịch?” — cũng chính là câu hỏi thúc đẩy các ước lượng Roll, Corwin–Schultz và Lesmond được phát triển ở những phần khác của chương này. Amihud, Mendelson, và Pedersen (2005) tổng quan tài liệu phát sinh từ đó.
Cơ sở lý thuyết đến từ Kyle (1985). Trong mô hình của Kyle, tác động giá của một dòng lệnh có dấu \(Q\) là \(\Delta p = \lambda Q\), nên \(\lambda = |\Delta p| / |Q|\) đúng là một tỷ số lợi nhuận trên khối lượng. Phương trình 37.2 là tỷ số đó được tính với dòng lệnh không dấu, là tất cả những gì dữ liệu theo ngày cung cấp. Brennan và Subrahmanyam (1996) chỉ ra trực tiếp rằng các ước lượng tác động giá xây dựng từ dòng lệnh có dấu trong phiên mang theo một phần bù lợi nhuận, còn Goyenko, Holden, và Trzcinka (2009) và Goyenko và Ukhov (2009) về sau xác lập rằng tỷ số Amihud là một thước đo xấp xỉ tần suất thấp tốt cho các chuẩn tác động giá tần suất cao đó. Fong, Holden, và Trzcinka (2017) mở rộng cuộc đua này sang dữ liệu toàn cầu và thấy nó hoạt động tốt bên ngoài Hoa Kỳ, đó là lý do nó là công cụ chủ lực trong nghiên cứu thị trường mới nổi và cũng là lý do chương này dựa nhiều vào nó.
14.3.1.3 Dòng dõi các Biến thể
Bảng 14.1 tập hợp các phép biến đổi đang được dùng phổ biến. Tất cả đều đơn điệu theo \(\text{ILLIQ}\) thô trong cùng một thời kỳ, điều này có một hệ quả quan trọng sẽ được phát triển bên dưới.
Bảng 14.1: Các phép biến đổi của tỷ số phi thanh khoản Amihud đang được dùng phổ biến. Tất cả đều tăng nghiêm ngặt theo \(\text{ILLIQ}\) thô trong cùng một thời kỳ.
Biến thể
Định nghĩa
Nguồn gốc / cách dùng điển hình
Thô
Trung bình của \(\lvert R_d\rvert / \text{DVOL}_d\)
Chính ước lượng; hầu như không bao giờ dùng trực tiếp
Acharya và Pedersen (2005); ánh xạ sang chi phí theo phần trăm, có chặn trên
Thứ hạng trong thời kỳ
Phân vị của \(\text{ILLIQ}\) trong thời kỳ \(t\)
Karolyi, Lee, và Dijk (2012), Amihud và c.s. (2015); so sánh được giữa các thị trường
Hai trong số này đáng được bàn kỹ hơn. Phép chuẩn hóa của Acharya và Pedersen (2005) tồn tại vì mô hình của họ đòi hỏi mức phi thanh khoản được biểu thị dưới dạng chi phí giao dịch theo phần trăm, để có thể trừ khỏi lợi nhuận. Hiệu chỉnh của họ \(\min(0.25 + 0.30\,\text{ILLIQ}\,P^M_{t-1},\,30)\) co giãn theo mức tăng của danh mục thị trường, hấp thụ đúng xu hướng khối lượng mà \(\text{ILLIQMA}\) cũng hấp thụ, và chặn kết quả ở mức 30% để đuôi cực đoan không chi phối. Các hằng số được khớp với dữ liệu giá trị giao dịch bằng đô la Mỹ trong một giai đoạn cụ thể và không thể chuyển sang VND. Mã nguồn bên dưới cho thấy công thức thất bại theo cả hai hướng chỉ tùy thuộc vào đơn vị khối lượng: với khối lượng tính bằng VND, mọi cổ phiếu đều được gán chi phí hệ số chặn 0.25% với độ phân tán mặt cắt ngang bằng không, và với khối lượng tính bằng tỷ VND, mọi cổ phiếu đều bị ghim ở mức chặn 30%, lại với độ phân tán bằng không. Chỉ có một đơn vị trung gian tình cờ tạo ra biến thiên, và việc chọn đơn vị đó không có gì thuộc về kinh tế. Phép biến đổi thứ hạng trong thời kỳ được dùng trong tài liệu thanh khoản quốc tế (Karolyi, Lee, và Dijk 2012; Amihud và c.s. 2015) tồn tại chính vì lý do này. Đây là phép biến đổi duy nhất trong bảng bất biến đối với đơn vị tiền tệ, đơn vị khối lượng và giai đoạn.
Một cải tiến nữa đáng biết là nghiên cứu của Amihud và Noh (2021), trong đó các tác giả xem xét lại các kết quả gốc bằng một nhân tố kém thanh khoản trừ thanh khoản có thể giao dịch được và một đặc tả có điều kiện. Cách xây dựng của họ là công trình đã công bố gần nhất với nhân tố thanh khoản được xây dựng ở cuối chương này.
14.3.1.4 Việc Quy đổi Không Vô hại
Đoạn mã sau đây độc lập và có thể chạy được. Nó xây dựng một bảng dữ liệu theo ngày tổng hợp với mức phi thanh khoản thật đã biết và không đổi rồi cho thấy việc chọn biến thể tác động thế nào đến các con số.
All Amihud variants, and what scaling does to them
def amihud_variants(daily, ret="ret", dvol="dvol", scale=1e6, min_days=15):""" Compute the Amihud (2002) ratio and its common transformations from a daily panel with columns [ticker, date, ret, dvol]. `dvol` is currency volume in whatever unit the caller supplies; `scale` is Amihud's units convention, which is *not* unit-free (see below). """ df = daily.loc[daily[dvol] >0].copy() df["period"] = df["date"].dt.to_period("M") df["illiq_d"] = df[ret].abs() / df[dvol] p = (df.groupby(["ticker", "period"]) .agg(illiq=("illiq_d", "mean"), n_days=("illiq_d", "size")) .reset_index()) p = p.loc[p["n_days"] >= min_days].copy() p["illiq_scaled"] = p["illiq"] * scale # Amihud's x10^6 p["illiq_ma"] = p["illiq"] / p.groupby("period")["illiq"].transform("mean") p["log_illiq"] = np.log(p["illiq_scaled"]) # variance stabilizing p["sqrt_illiq"] = np.sqrt(p["illiq_scaled"]) # Hasbrouck (2009) p["illiq_rank"] = p.groupby("period")["illiq"].rank(pct=True)return p# A synthetic panel: 40 stocks, two years, TRUE illiquidity constant over time,# but market-wide VND volume growing at 35% per year.rng_v = np.random.default_rng(11)dates_v = pd.bdate_range("2023-01-02", "2024-12-31")tickers_v = [f"S{i:02d}"for i inrange(40)]level_v = rng_v.lognormal(np.log(3e9), 1.1, size=len(tickers_v)) # VND/dayyears_v = (dates_v - dates_v[0]).days /365.25demo = pd.concat([ pd.DataFrame({"ticker": t,"date": dates_v,"ret": rng_v.normal(0, 0.022, len(dates_v)),"dvol": L * np.exp(rng_v.normal(0, 0.6, len(dates_v))) * np.exp(0.35* years_v), })for t, L inzip(tickers_v, level_v)], ignore_index=True)# (1) The x10^6 convention is a currency convention, not a normalization.print("Median ILLIQ x 10^6, identical economics, three volume units:")for unit, divisor in [("VND", 1.0), ("million VND", 1e6), ("USD @ 25,400", 25_400.0)]: med = amihud_variants(demo.assign(dvol=demo["dvol"] / divisor))["illiq_scaled"].median()print(f" DVOL in {unit:<14s}{med:>12.6g}")# (2) Mean adjustment removes the volume trend; raw scaling does not.v = amihud_variants(demo)ts = v.groupby("period")[["illiq_scaled", "illiq_ma"]].median()first, last = ts.iloc[0], ts.iloc[-1]print("\nTrue illiquidity is constant; volume grows 35%/yr. Median over the panel:")for col, name in [("illiq_scaled", "ILLIQ x 10^6"), ("illiq_ma", "ILLIQMA ")]:print(f" {name}: {first[col]:>10.4g} -> {last[col]:>10.4g} "f"({100* (last[col] / first[col] -1):+.1f}%)")# (3) Every variant is monotone within a period, so ranks are identical.cols_v = ["illiq_scaled", "illiq_ma", "log_illiq", "sqrt_illiq", "illiq_rank"]within = (v.groupby("period")[["illiq"] + cols_v] .corr(method="spearman").xs("illiq", level=1)[cols_v].mean())print("\nMean within-period Spearman rank correlation with raw ILLIQ:")print(within.round(4).to_string())# (4) But the distributions they induce are very different.print("\nPooled distribution shape:")print(v[["illiq_scaled", "log_illiq", "sqrt_illiq"]] .agg(["skew", "kurt"]).round(3).to_string())# (5) Acharya-Pedersen constants are calibrated to US dollar volume, so the# same formula degenerates in opposite directions depending on the unit.print("\nAcharya-Pedersen cost min(0.25 + 0.30*ILLIQ*1e6, 30) by volume unit:")for unit, divisor in [("VND", 1.0), ("million VND", 1e6), ("billion VND", 1e9)]: s = amihud_variants(demo.assign(dvol=demo["dvol"] / divisor))["illiq_scaled"] ap = np.minimum(0.25+0.30* s, 30.0)print(f" DVOL in {unit:<12s} mean={ap.mean():8.4f} sd={ap.std():8.4f} "f"at cap={100* (ap >=30.0).mean():5.1f}%")
Median ILLIQ x 10^6, identical economics, three volume units:
DVOL in VND 5.40237e-06
DVOL in million VND 5.40237
DVOL in USD @ 25,400 0.13722
True illiquidity is constant; volume grows 35%/yr. Median over the panel:
ILLIQ x 10^6: 7.324e-06 -> 4.068e-06 (-44.5%)
ILLIQMA : 0.6307 -> 0.641 (+1.6%)
Mean within-period Spearman rank correlation with raw ILLIQ:
illiq_scaled 1.0
illiq_ma 1.0
log_illiq 1.0
sqrt_illiq 1.0
illiq_rank 1.0
Pooled distribution shape:
illiq_scaled log_illiq sqrt_illiq
skew 2.986 0.147 1.447
kurt 11.810 -0.360 2.519
Acharya-Pedersen cost min(0.25 + 0.30*ILLIQ*1e6, 30) by volume unit:
DVOL in VND mean= 0.2500 sd= 0.0000 at cap= 0.0%
DVOL in million VND mean= 2.8306 sd= 3.0728 at cap= 0.0%
DVOL in billion VND mean= 30.0000 sd= 0.0000 at cap=100.0%
Từ đó rút ra năm bài học, và chúng chính là lý do tiểu mục này tồn tại.
Hệ số \(\times 10^6\) không mang thông tin. Cùng một mức phi thanh khoản kinh tế được báo cáo ở khoảng \(5\times10^{-6}\), \(5\), hoặc \(0.13\) chỉ tùy thuộc vào việc khối lượng được ghi bằng VND, triệu VND hay USD. Do đó các con số “phi thanh khoản Amihud bằng 0.8” được công bố không thể so sánh giữa các bài báo trừ khi đơn vị khối lượng được nêu rõ, mà thường thì không. Hãy nêu rõ đơn vị; tốt hơn nữa, hãy báo cáo một phép biến đổi không phụ thuộc vào nó.
Mức thô di chuyển theo xu hướng của mẫu số. Trong mô phỏng, mức phi thanh khoản thật không đổi theo cấu trúc, vậy mà trung vị của thước đo thô giảm khoảng 45% trong hai năm chỉ vì khối lượng tăng. \(\text{ILLIQMA}\) thay đổi vài phần trăm, là nhiễu lấy mẫu. Giá trị giao dịch của Việt Nam tăng nhanh hơn nhiều so với 35% mỗi năm trong nhiều giai đoạn sau năm 2015, nên đây là mối quan tâm bậc nhất đối với mọi nhận định dạng “thanh khoản ở Việt Nam đã cải thiện trong giai đoạn mẫu”.
Đối với các phân loại trong cùng thời kỳ, việc chọn biến thể hoàn toàn không quan trọng. Mọi phép biến đổi trong Bảng 14.1 đều tăng nghiêm ngặt trong cùng một thời kỳ, nên tương quan hạng trung bình trong thời kỳ đúng bằng 1.0 và các phân loại danh mục ở phần sau của chương sẽ giống hệt nhau về mặt số học dù dùng biến thể nào. Điều này đáng biết vì nó giới hạn mức độ mà việc chọn lọc biến thể trong tài liệu có thể thực sự quan trọng: nó chỉ có thể quan trọng thông qua các đặc tả dùng mức — Fama-MacBeth gộp không có hiệu ứng cố định theo thời kỳ, hồi quy chuỗi thời gian, và mọi thứ so sánh độ lớn giữa các thời kỳ hoặc thị trường.
Phép biến đổi quan trọng vì lý do về phân phối. Độ lệch (skewness) gộp giảm từ khoảng 3.0 xuống 0.1 và độ nhọn vượt mức (excess kurtosis) từ khoảng 11.8 xuống \(-0.4\) dưới phép biến đổi logarit. Vì các hệ số góc Fama-MacBeth là hệ số OLS và OLS không vững trước một đuôi phải nặng như vậy, phép biến đổi logarit đang làm một việc thực sự trong các hồi quy mặt cắt ngang: không phải sửa các thứ hạng, mà là ngăn một số ít quan sát công ty–tháng quyết định hệ số góc.
Các hiệu chỉnh vay mượn không giữ được khi chuyển sang bối cảnh khác. Phép ánh xạ chi phí của Acharya và Pedersen (2005) là trường hợp rõ nhất. Với khối lượng tính bằng VND, nó gán cho mọi quan sát công ty–tháng giá trị hệ số chặn 0.25%, với độ lệch chuẩn mặt cắt ngang đúng bằng không; với khối lượng tính bằng tỷ VND, nó ghim tất cả ở mức chặn 30%, lại với độ phân tán bằng không. Trong cả hai trường hợp, nó đều không thất bại một cách ồn ào — nó trả về một chi phí theo phần trăm trông hợp lý nhưng hoàn toàn không mang thông tin nào về cổ phiếu nào kém thanh khoản. Mọi hằng số đã công bố dạng \(a + b\,\text{ILLIQ}\) đều kế thừa đơn vị của dữ liệu mà nó được khớp, và phải được ước lượng lại chứ không được sao chép.
Để áp dụng cùng hàm đó cho bảng dữ liệu thực của chương:
Apply the variant constructor to the Vietnamese daily panel
# `daily` carries turnover_value in VND; express it in billions so that the# resulting ratio reads as "return per billion VND traded".amihud_all = amihud_variants( daily.rename(columns={"turnover_value": "dvol"}).assign( dvol=lambda d: d["dvol"] /1e9 ), scale=100.0, # report as PERCENT price move per billion VND min_days=10,)print(amihud_all[["illiq_scaled", "illiq_ma", "log_illiq"]].describe().round(4))
Đặt scale=100.0 với khối lượng tính bằng tỷ VND cho thước đo một cách đọc có thể giữ nguyên khi chuyển đổi: mức dịch chuyển giá theo phần trăm gắn với một tỷ VND giao dịch. Đây là quy ước chúng tôi khuyến nghị cho các nghiên cứu về Việt Nam, và nó luôn phải được nêu cùng với con số.
14.3.1.5 Điều Thực sự được Định giá: Phê bình của Lou–Shu
Có một phản biện sâu hơn đối với Phương trình 37.2 so với bất kỳ câu hỏi nào về việc quy đổi, và nó liên quan trực tiếp đến cải tiến tần suất cao được phát triển trong Mục 14.4.
Lou và Shu (2017) phân rã tỷ số Amihud thành phần quy cho mẫu số và phần quy cho tác động giá thực sự theo lợi nhuận trên mỗi đơn vị khối lượng. Phát hiện của họ gây khó chịu đối với cách diễn giải chuẩn: phần bù lợi nhuận gắn với \(\text{ILLIQ}\) được dẫn dắt bởi thành phần khối lượng giao dịch chứ không phải thành phần tác động giá. Nói thẳng ra, phân loại theo \(\text{ILLIQ}\) gần như là phân loại theo nghịch đảo của giá trị giao dịch bằng đô la, và phần bù mà nó đem lại có thể phản ánh bất cứ điều gì mà nghịch đảo giá trị giao dịch đó đại diện — sự nhận biết của nhà đầu tư, sự bị lãng quên, sự chú ý của nhà đầu tư cá nhân — chứ không phải khoản bù đắp cho tác động giá.
Điều này có hai hàm ý đối với cuốn sách.
Hàm ý thứ nhất là một cảnh báo. Ở những phần sau của chương này báo cáo phần bù thanh khoản từ các phân loại theo \(\text{ILLIQ}\), cách diễn giải đúng là “một phần bù gắn với đặc điểm Amihud”, chứ không phải “khoản bù đắp cho tác động giá”. Phân biệt hai điều này đòi hỏi phép phân rã, và đòi hỏi kiểm soát trực tiếp giá trị giao dịch bằng đô la.
Hàm ý thứ hai là một câu hỏi thực sự còn bỏ ngỏ mà ước lượng thực hiện (realized) có khả năng trả lời. Nội dung tác động giá của \(\text{ILLIQ}\) nằm ở tử số, và Mục 14.4 cho thấy tử số theo ngày \(|R_d|\) mang khoảng 76% sai số đo lường tương đối trong khi mẫu số \(\text{DVOL}_d\) được quan sát gần như không có sai số. Một cuộc đua giữa tử số đo với 76% nhiễu và mẫu số đo không nhiễu không phải là một phép thử công bằng xem thành phần nào quan trọng: sai số trong biến cổ điển (errors-in-variables) làm đóng góp ước lượng của tử số suy giảm về không bất kể vai trò kinh tế thật của nó. Liệu kết quả của Lou–Shu có còn đứng vững khi tử số được đo với độ chính xác cao hơn khoảng bảy lần của Lacava, Ranaldo, và Santucci de Magistris (2026) hay không, theo hiểu biết của chúng tôi, vẫn chưa được kiểm định. Chúng tôi đánh dấu đó là một cơ hội nghiên cứu chứ không phải một kết quả, và lưu ý rằng Việt Nam là nơi không thuận lợi để thử nghiệm, vì những lý do về dữ liệu trong phiên được nêu trong Mục 14.4.
14.3.1.6 Cuốn sách này Dùng Biến thể nào
Để cụ thể, các quy ước được áp dụng trong phần còn lại của chương này và của cuốn sách:
Tần suất theo tháng, để nhất quán với bảng dữ liệu lợi nhuận theo tháng, chấp nhận sai số lấy mẫu tăng khoảng 3.5 lần so với cách xây dựng theo năm của Amihud.
Khối lượng tính bằng tỷ VND, chỉ các ngày có khối lượng dương, tối thiểu mười ngày hợp lệ cho mỗi quan sát công ty–tháng.
Biến đổi logarit cho công việc hồi quy, theo mã nguồn đi kèm Phương trình 37.2 ở trên, vì phân phối gộp quá lệch đối với OLS ở dạng mức.
Thứ hạng hoặc nhóm ngũ phân vị trong tháng cho các phân loại danh mục, vốn như đã chỉ ra ở trên, bất biến đối với mọi lựa chọn khác trong danh sách này.
Điều chỉnh theo trung bình bất cứ khi nào các mức được so sánh giữa các thời điểm, trên thực tế nghĩa là mọi đặc tả không có hiệu ứng cố định theo tháng.
Thước đo cũng được báo cáo không qua biến đổi trong các thống kê mô tả, để độc giả có thể quy đổi sang bất kỳ quy ước nào khác.
14.3.2 Ngày Lợi nhuận bằng Không (Thước đo Lesmond)
Lesmond, Ogden, và Trzcinka (1999) đề xuất dùng tỷ lệ các ngày có lợi nhuận bằng không làm thước đo chi phí giao dịch. Trực giác là nếu thay đổi giá trị thực trong một ngày nhỏ hơn chi phí giao dịch khứ hồi, một nhà đầu tư cận biên hợp lý sẽ không giao dịch, và lợi nhuận quan sát được sẽ bằng không. Do đó, tỷ lệ lợi nhuận bằng không là hàm tăng của chi phí giao dịch hiệu dụng.
Lesmond (2005) kiểm chứng thước đo này cho các thị trường mới nổi và thấy nó tương quan mạnh với các thước đo chi phí tường minh. Ở Việt Nam, nơi các ngày lợi nhuận bằng không rất phổ biến (như đã ghi nhận ở chương trước), thước đo này có ý nghĩa đặc biệt.
\[
\text{ZeroRet}_{i,m} = \frac{\text{Number of days with } R_{i,d} = 0}{D_{i,m}}
\tag{14.4}\]
Tỷ lệ vòng quay cổ phiếu (tức là khối lượng theo ngày chia cho số cổ phiếu đang lưu hành) đo hoạt động giao dịch chứ không phải chi phí giao dịch. Datar, Naik, và Radcliffe (1998) dùng tỷ lệ vòng quay làm thước đo xấp xỉ thanh khoản và ghi nhận mối quan hệ mặt cắt ngang âm giữa tỷ lệ vòng quay và lợi nhuận kỳ vọng, phù hợp với giả thuyết phần bù thanh khoản.
Roll (1984) suy ra một chênh lệch giá mua–bán ngầm định từ hiệp phương sai chuỗi của các thay đổi giá. Với các giả định rằng giá trị thực tuân theo bước ngẫu nhiên và giá quan sát được dao động giữa giá mua và giá bán:
trong đó \(\Delta P_{i,d} = P_{i,d} - P_{i,d-1}\). Thước đo này mang tính trực giác: dao động giữa giá mua và giá bán tạo ra tương quan chuỗi âm trong giá giao dịch, và độ lớn của tương quan âm này phản ánh chênh lệch giá.
Compute monthly Roll (1984) effective spread estimator
def compute_roll_spread(daily_df, min_days=15):""" Compute the Roll (1984) effective spread from serial covariance of daily price changes. """ df = daily_df.copy() df['month'] = df['date'].dt.to_period('M') df['price_change'] = df.groupby('ticker')['adjusted_close'].diff() df['price_change_lag'] = df.groupby('ticker')['price_change'].shift(1)def roll_estimate(group):iflen(group) < min_days:return np.nan cov = group['price_change'].cov(group['price_change_lag'])if cov <0: spread =2* np.sqrt(-cov)# Normalize by average price avg_price = group['adjusted_close'].mean()return spread / avg_price if avg_price >0else np.nanelse:return0.0 roll = ( df.dropna(subset=['price_change', 'price_change_lag']) .groupby(['ticker', 'month']) .apply(roll_estimate) .reset_index(name='roll_spread') ) roll['month_end'] = roll['month'].dt.to_timestamp('M')return roll[['ticker', 'month_end', 'roll_spread']]roll_monthly = compute_roll_spread(daily)print(f"Roll spread observations: {len(roll_monthly):,}")print(f"\nRoll spread distribution:")print(roll_monthly['roll_spread'].describe().round(4))
14.3.5 Chênh lệch Cao–Thấp Corwin-Schultz
Corwin và Schultz (2012) ước lượng chênh lệch giá hiệu dụng từ giá cao nhất và thấp nhất theo ngày. Điểm then chốt là giá cao nhất và thấp nhất theo ngày chứa thông tin về cả biến động lẫn chênh lệch giá—giá cao nhất thường là một lệnh mua và giá thấp nhất là một lệnh bán, nên biên độ cao–thấp phản ánh cả biến động thực lẫn chênh lệch giá mua–bán. Bằng cách so sánh biên độ cao–thấp một ngày và hai ngày, phương pháp tách hai thành phần này:
Đây là một sự điều chỉnh của thước đo chi phí hiệu dụng Hasbrouck (2009). Hệ số \(\lambda_i\) đo giá dịch chuyển bao nhiêu trên mỗi đơn vị khối lượng (không dấu, lấy căn bậc hai).
Estimate Kyle’s lambda from daily price-volume regressions
def compute_kyle_lambda(daily_df, min_days=15):""" Estimate Kyle's lambda (price impact per unit order flow) from daily return-on-signed-volume regressions. """ df = daily_df[daily_df['volume'] >0].copy() df['month'] = df['date'].dt.to_period('M')# Signed square-root volume (sign inferred from return) df['signed_sqrt_vol'] = ( np.sign(df['daily_return']) * np.sqrt(df['volume']) )def estimate_lambda(group):iflen(group) < min_days:return np.nan y = group['daily_return'].values x = group['signed_sqrt_vol'].values x = sm.add_constant(x)try: model = sm.OLS(y, x).fit() lam = model.params[1]returnmax(lam, 0) # Lambda should be non-negativeexceptException:return np.nan kyle = ( df.groupby(['ticker', 'month']) .apply(estimate_lambda) .reset_index(name='kyle_lambda') ) kyle['log_kyle'] = np.log(kyle['kyle_lambda'].clip(lower=1e-10)) kyle['month_end'] = kyle['month'].dt.to_timestamp('M')return kyle[['ticker', 'month_end', 'kyle_lambda', 'log_kyle']]kyle_monthly = compute_kyle_lambda(daily)print(f"Kyle lambda observations: {len(kyle_monthly):,}")print(f"\nLog Kyle lambda distribution:")print(kyle_monthly['log_kyle'].describe().round(3))
14.4 Phi thanh khoản Thực hiện: Một Cải tiến Tần suất Cao cho Thước đo Amihud
14.4.1 Động cơ
Trong bảy thước đo xấp xỉ được xây dựng ở trên, tỷ số Amihud (2002) trong Phương trình 37.2 là thước đo mà cuốn sách này dựa vào nhiều nhất: nó là biến phân loại trong các phân loại danh mục và hồi quy Fama-MacBeth ở phần sau, là đầu vào cho các hồi quy về tính đồng biến, và là cơ sở của nhân tố thanh khoản có thể giao dịch được xây dựng ở cuối chương. Đây cũng chính là thước đo mà Fong, Holden, và Trzcinka (2017) khuyến nghị riêng cho các thị trường mới nổi. Vì vậy, rất đáng để đặt câu hỏi: nó đo lường chính xác đến mức nào điều mà nó tuyên bố đo lường?
Câu trả lời là: kém chính xác hơn nhiều so với mức độ phổ biến của nó gợi ý, và vì một lý do không hề liên quan đến mẫu số khối lượng. Tử số \(|R_{i,d}|\) là một quan sát đơn lẻ được dùng để tóm tắt toàn bộ biến động giá trong ngày. Xét như một thống kê, nó gần như không chệch đối với độ biến động của ngày nhưng lại nhiễu một cách phi thường — chúng ta sẽ định lượng điều này dưới đây ở mức khoảng 76% độ lệch chuẩn tương đối mỗi ngày. Mỗi đơn vị nhiễu đó lan truyền vào các phân loại danh mục dưới dạng suy giảm do sai số trong biến (errors-in-variables) cổ điển, làm phẳng các phần bù thanh khoản đo được về phía không.
Lacava, Ranaldo, và Santucci de Magistris (2026) giải quyết đúng vấn đề này. Họ định nghĩa phi thanh khoản thực hiện là tỷ số giữa độ biến động thực hiện và khối lượng giao dịch, và giới thiệu thước đo Amihud thực hiện, thay giá trị tuyệt đối của lợi nhuận theo ngày đơn lẻ bằng một ước lượng tần suất cao của cùng đại lượng đó. Họ chỉ ra rằng nó nắm bắt một cách vững chắc độ nhạy giá tích lũy trong phiên đối với giao dịch, ngay cả khi có độ biến động ngẫu nhiên, nhiễu vi cấu trúc và các bước nhảy thông tin; ghi nhận hiện tượng phân cụm không đồng nhất và hiệu ứng đòn bẩy trong chuỗi kết quả; và thấy rằng nó dự báo được lợi nhuận ngắn hạn. Phần này phát triển ước lượng theo ký hiệu của cuốn sách, dẫn giải vì sao mức cải thiện độ chính xác lại lớn đến vậy, và trình bày cái giá phải trả để triển khai nó trên HOSE.
14.4.2 Thiết lập và Ký hiệu
Let \(p_{i,t}\) denote the efficient log price of stock \(i\) at time \(t\), evolving over trading day \(d\) as an Itô semimartingale:
where \(\sigma_{i,t}\) is a càdlàg stochastic volatility process, \(W_{i,t}\) a standard Brownian motion, and \(J_{i,t}\) a finite-activity jump process capturing discrete information arrivals. The object that summarizes the day’s price variation is the integrated variance
Chia ngày giao dịch liên tục thành \(M\) khoảng và ký hiệu log lợi nhuận trong phiên là \(r_{i,d,j}\), \(j = 1,\dots,M\), sao cho lợi nhuận theo ngày thỏa mãn \(R_{i,d} = \sum_{j=1}^{M} r_{i,d,j}\). Như trước, \(\text{DVOL}_{i,d}\) là khối lượng giao dịch tính bằng VND trong ngày \(d\) và \(D_{i,m}\) là số ngày có khối lượng dương trong tháng \(m\).
Phương sai thực hiện và biến phân song lũy thừa (bipower variation) là
(A1) Giá hiệu quả là nửa martingale. Log giá hiệu quả tuân theo Phương trình 14.11 với \(\int_{d-1}^{d}\sigma_{i,s}^4\,ds < \infty\).
Diễn giải. Giá được dẫn dắt bởi một quá trình khuếch tán với độ biến động có thể ngẫu nhiên và dai dẳng, cộng với các bước nhảy không thường xuyên. Đây là giả định yếu và nhìn chung không gây tranh cãi. Nó sẽ bị vi phạm bởi một quá trình giá có bước nhảy hoạt động vô hạn với cường độ đủ lớn, hoặc bởi một thị trường mỏng đến mức không một xấp xỉ thời gian liên tục nào còn có ý nghĩa — trường hợp sau là mối lo thực tế đối với các cổ phiếu vốn hóa nhỏ trên HOSE.
(A2) Nhiễu vi cấu trúc không đáng kể tại tần suất lấy mẫu. Giá quan sát được thỏa mãn \(\tilde{p}_{i,t} = p_{i,t} + u_{i,t}\), và khoảng lấy mẫu đủ thô để \(2M\,\mathbb{V}[u_{i,t}]\) là nhỏ so với \(\text{IV}_{i,d}\).
Diễn giải. Dao động giữa giá mua và giá bán, tính rời rạc của bước giá, và giá cũ không được cập nhật đều bơm nhiễu vào giá quan sát được. Vì \(\text{RV}\) cộng dồn lợi nhuận bình phương, nhiễu này tích lũy theo \(M\) thay vì triệt tiêu — lấy mẫu quá dày làm ước lượng tệ đi chứ không tốt lên. Trên HOSE, bước giá thô so với mặt bằng giá khiến ràng buộc này phát huy tác dụng sớm hơn so với dữ liệu Mỹ. Đây là giả định dễ bị vi phạm nhất trong thực tế, và chúng ta sẽ chỉ ra điều gì xảy ra khi nó bị vi phạm.
(A3) Không có bước nhảy, đối với thước đo Amihud thực hiện dạng thuần.\(J_{i,t} \equiv 0\) trong ngày \(d\).
Diễn giải. Một bất ngờ lợi nhuận hay một phiên chạm biên độ là một thay đổi giá lớn không phải do tác động giá của khối lượng giao dịch, nhưng \(\text{RV}\) vẫn tính nó như thể đúng là như vậy. Với tần suất cổ phiếu Việt Nam chạm biên độ \(\pm7\%\) của HOSE (xem thảo luận về biên độ giá trong chương vi cấu trúc thị trường), (A3) không hề vô hại ở đây. Nới lỏng giả định này chính là lý do chúng ta duy trì song song \(\text{BV}_{i,d}\).
(A4) Khối lượng đồng thời.\(\text{DVOL}_{i,d}\) được đo trên cùng cửa sổ thời gian với các lợi nhuận trong phiên.
Diễn giải. Nếu lợi nhuận được tính trên các phiên khớp lệnh liên tục nhưng khối lượng lại bao gồm cả các giao dịch thỏa thuận sau giờ đóng cửa, thì tử số và mẫu số đang nói về hai hoạt động giao dịch khác nhau. Trên HOSE, phiên thỏa thuận 14:45–15:00 khiến đây là một vấn đề hạch toán thực sự chứ không phải chi tiết kỹ thuật.
14.4.4 Ước lượng
Thước đo Amihud thực hiện thay \(|R_{i,d}|\) bằng một ước lượng tần suất cao đã được quy đổi tỷ lệ cho chính biến động giá của ngày đó:
Hằng số \(\sqrt{2/\pi} \approx 0.7979\) không phải để cho đẹp: nó đưa Phương trình 14.14 về đúng cùng thang đo với Phương trình 37.2, nhờ đó hai thước đo có thể so sánh trực tiếp về mặt số học và phiên bản thực hiện có thể thay thế trực tiếp ở mọi nơi cuốn sách đang dùng \(\text{ILLIQ}\).
14.4.5 Dẫn giải
Bước 1: \(|R_{i,d}|\) thực chất ước lượng cái gì. Điều kiện theo quỹ đạo độ biến động và bỏ qua xu hướng cùng bước nhảy, ta có \(R_{i,d} \mid \text{IV}_{i,d} \sim N(0, \text{IV}_{i,d})\). Đối với một biến bán chuẩn (half-normal),
Vậy \(|R_{i,d}|\) là ước lượng không chệch cho \(\sqrt{2/\pi}\sqrt{\text{IV}_{i,d}}\) — tử số của Amihud nhắm đúng mục tiêu. Tuy nhiên, độ lệch chuẩn tương đối của nó là
Tử số Amihud của một ngày đơn lẻ mang theo khoảng 76% sai số đo lường tương đối. Đây là tính chất của phân phối bán chuẩn, không phải của một thị trường cụ thể nào — nó không thể giảm được nếu chỉ có một quan sát mỗi ngày.
Bước 2: \(\sqrt{\text{RV}_{i,d}}\) ước lượng cái gì. Dưới (A1)–(A3), phương sai thực hiện tuân theo định lý giới hạn trung tâm của Barndorff-Nielsen và Shephard (2002),
Bước 3: So sánh. Chia Phương trình 14.17 cho Phương trình 14.20, tử số thực hiện chính xác hơn theo hệ số \(\sqrt{2M\left(\pi/2 - 1\right)}\). Các phiên khớp lệnh liên tục của HOSE diễn ra từ 09:15–11:30 và 13:00–14:30, tức 225 phút, nên lấy mẫu năm phút cho \(M = 45\):
Bảng 14.2: Sai số đo lường giải tích của tử số Amihud theo tần suất lấy mẫu, từ Phương trình 14.17 và Phương trình 14.20. Dòng 1 phút không phải là khuyến nghị — xem giả định (A2) và phần mô phỏng bên dưới.
Tần suất lấy mẫu
\(M\)
Độ lệch chuẩn tương đối của tử số
Cải thiện so với \(\lvert R_d\rvert\)
Đóng cửa sang đóng cửa theo ngày (\(\lvert R_d \rvert\))
1
0.7555
—
15 phút
15
0.1826
4.1\(\times\)
5 phút
45
0.1054
7.2\(\times\)
1 phút
225
0.0471
16.0\(\times\)
Ở tần suất lấy mẫu năm phút, phương sai nhiễu trong tử số giảm khoảng 51 lần.
Bước 4: Vì sao điều này quan trọng đối với các kết quả của cuốn sách. Viết biến phân loại theo tháng dưới dạng giá trị thật cộng sai số, \(\widehat{x}_{i,m} = x_{i,m} + \eta_{i,m}\). Trong các phân loại thanh khoản đơn biến và các hồi quy Fama-MacBeth ở phần sau của chương, sai số trong biến cổ điển làm suy giảm hệ số góc ước lượng theo tỷ số tin cậy
Giảm \(\mathbb{V}[\eta]\) khoảng 51\(\times\) sẽ đẩy tỷ số tin cậy tiến về một, điều này làm tăng phần bù thanh khoản đo được cùng thống kê \(t\) của nó. Hệ quả thực tiễn khá khó chịu và đáng nói thẳng: phần bù thanh khoản ước lượng bằng tỷ số Amihud theo ngày là một cận dưới. Một phần bù yếu hoặc không có ý nghĩa thống kê trong dữ liệu Việt Nam có thể phản ánh một biến phân loại nhiều nhiễu chứ không phải sự vắng mặt của phần bù.
14.4.6 Trực giác
Tỷ số Amihud đặt câu hỏi “giá đã dịch chuyển bao nhiêu trên mỗi đồng VND được giao dịch?” và trả lời bằng đúng một con số mà ngày hôm đó tình cờ mang lại — mức dịch chuyển từ mở cửa đến đóng cửa. Nhưng một ngày mà giá đi lên 4%, quay đầu giảm 5%, rồi đóng cửa thấp hơn 1% là một ngày có độ nhạy giá đáng kể đối với giao dịch, trong khi \(|R_d| = 1\%\) hầu như không ghi nhận được điều đó. Độ biến động thực hiện đo cả quỹ đạo thay vì chỉ hai đầu mút, nhờ vậy nắm bắt được đúng độ nhạy giá tích lũy trong phiên mà Amihud đang cố gắng xấp xỉ. Lấy trung bình \(M\) bình phương lợi nhuận trong phiên thay vì lấy một giá trị tuyệt đối của lợi nhuận theo ngày, xét về mặt thống kê, chính là khác biệt giữa một quan sát và \(M\) quan sát.
Điểm vướng là quỹ đạo chỉ quan sát được thông qua lớp vi cấu trúc. Đẩy \(M\) lên đủ cao thì ta không còn đo quỹ đạo giá hiệu quả nữa mà bắt đầu đo dao động giữa giá mua và giá bán. Toàn bộ nghệ thuật của ước lượng này nằm ở việc chọn \(M\) đủ lớn để có thêm độ chính xác và đủ nhỏ để giữ (A2) còn hiệu lực.
14.4.7 Thiết kế Mô phỏng
Thí nghiệm sau đây đối chiếu Phương trình 14.14 với Phương trình 37.2 trên cùng một quá trình sinh dữ liệu (DGP), rồi lần lượt phá vỡ (A2) và (A3) để làm lộ các kiểu thất bại. Đại lượng mục tiêu là mức phi thanh khoản thật theo ngày đã quy đổi \(\sqrt{2/\pi}\sqrt{\text{IV}_{i,d}}/\text{DVOL}_{i,d}\), nhờ đó độ chệch và RMSE được đo so với một đại lượng đã biết.
Monte Carlo: realized vs. classic Amihud under noise and jumps
rng = np.random.default_rng(20260812)M_HOSE =45# 225 continuous minutes on HOSE / 5-minute samplingdef simulate_days(n_days, M, noise_sd=0.0, jump_prob=0.0, jump_sd=0.0):""" Simulate intraday paths with stochastic volatility, optional iid microstructure noise in observed log prices, and optional information jumps. Returns the ingredients of both illiquidity estimators. """# Integrated variance: gamma-distributed, mean daily vol about 2.5% IV = rng.gamma(shape=4.0, scale=(0.025**2) /4.0, size=n_days)# Efficient intraday returns eff = rng.normal(0.0, 1.0, size=(n_days, M)) * np.sqrt(IV / M)[:, None]# Information jumps (violates A3)if jump_prob >0: hit = rng.binomial(1, jump_prob / M, size=(n_days, M)) eff = eff + hit * rng.normal(0.0, jump_sd, size=(n_days, M))# Efficient log-price path, then contaminate with noise (violates A2) P = np.cumsum(eff, axis=1) P_obs = P + rng.normal(0.0, noise_sd, size=(n_days, M)) if noise_sd >0else P# Observed intraday returns and the daily open-to-close return r = np.diff(P_obs, axis=1, prepend=0.0) R_d = P_obs[:, -1] RV = np.sum(r **2, axis=1) BV = (np.pi /2) * np.sum(np.abs(r[:, 1:]) * np.abs(r[:, :-1]), axis=1)# VND turnover, lognormal around 5 billion VND DVOL = np.exp(rng.normal(np.log(5e9), 1.0, size=n_days))returndict(IV=IV, R_d=R_d, RV=RV, BV=BV, DVOL=DVOL)def score(sim, label):"""Bias, RMSE (both relative to mean truth) and rank correlation.""" k = np.sqrt(2.0/ np.pi) truth = k * np.sqrt(sim['IV']) / sim['DVOL'] estimators = {'Amihud |R|/DVOL': np.abs(sim['R_d']) / sim['DVOL'],'Realized Amihud': k * np.sqrt(sim['RV']) / sim['DVOL'],'Bipower Amihud': k * np.sqrt(np.maximum(sim['BV'], 0)) / sim['DVOL'], } rows = []for name, est in estimators.items(): rows.append({'estimator': name,'rel_bias': (est - truth).mean() / truth.mean(),'rel_rmse': np.sqrt(((est - truth) **2).mean()) / truth.mean(),'rank_corr': stats.spearmanr(est, truth).statistic, }) out = pd.DataFrame(rows).set_index('estimator')print(f"\n--- {label}")print(out.round(4))return out# Baseline: assumptions holdscore(simulate_days(30_000, M_HOSE), "Baseline (A1-A4 hold), M=45")# Failure case A: microstructure noise, and what oversampling does to itscore(simulate_days(30_000, M_HOSE, noise_sd=0.0015),"Noise sd=0.15%, 5-minute sampling (M=45)")score(simulate_days(30_000, 225, noise_sd=0.0015),"Noise sd=0.15%, 1-minute sampling (M=225) -- oversampled")# Failure case B: information jumpsscore(simulate_days(30_000, M_HOSE, jump_prob=0.2, jump_sd=0.03),"Information jumps (~1 per 5 days, 3% size)")
Ba kết quả xuất hiện, và mỗi kết quả có một đối ứng trực tiếp trong phần giải tích ở trên.
Thứ nhất, dưới (A1)–(A4), cả hai ước lượng đều gần như không chệch — tử số Amihud theo ngày, như Phương trình 14.16 đã nói, không chệch đối với mục tiêu đúng — nhưng độ chính xác của chúng khác nhau một trời một vực. RMSE tương đối giảm từ khoảng 1.28 xuống khoảng 0.18, một tỷ lệ khoảng 7.2, đúng bằng hệ số cải thiện mà Bảng 14.2 dự báo bằng giải tích cho \(M = 45\). Tương quan hạng với mức phi thanh khoản thật tăng từ khoảng 0.70 lên khoảng 0.99. Tương quan hạng là con số quan trọng nhất đối với cuốn sách này, vì nó quyết định việc một phân loại danh mục có xếp cổ phiếu vào đúng nhóm ngũ phân vị hay không: ở mức 0.70, một tỷ lệ đáng kể các cổ phiếu bị xếp sai chỉ vì biến phân loại.
Thứ hai, việc phá vỡ (A2) làm thước đo Amihud thực hiện chệch lên trên, và lấy mẫu ở một phút thay vì năm phút làm độ chệch tệ hơn khoảng bốn lần chứ không tốt hơn — khoảng +16% tại \(M = 45\) so với khoảng +66% tại \(M = 225\). Đây là bài học không hiển nhiên của toàn bộ phần này: khi có nhiễu, nhiều dữ liệu hơn được lấy mẫu dày hơn lại gây hại thực sự. Cũng lưu ý rằng thước đo Amihud theo ngày thuần túy gần như miễn nhiễm với vấn đề cụ thể này, vì một mức lợi nhuận từ mở cửa đến đóng cửa chỉ chứa một liều nhiễu. Độ vững trước nhiễu vi cấu trúc là chiều duy nhất mà thước đo cổ điển thắng.
Thứ ba, khi có bước nhảy, thước đo Amihud thực hiện dạng thuần kế thừa một độ chệch lên trên rõ rệt (khoảng +9%) trong khi phiên bản song lũy thừa trong Phương trình 14.15 vẫn gần như không chệch (khoảng +1%) và còn cho RMSE thấp nhất trong ba thước đo. Với việc cổ phiếu Việt Nam thường xuyên chạm biên độ giá, đây là cấu hình nên ưu tiên cho thị trường Việt Nam.
14.4.8 Tái lập trên Dữ liệu Việt Nam
Lacava, Ranaldo, và Santucci de Magistris (2026) sử dụng dữ liệu tần suất cao của Mỹ, nên không thể tái lập trực tiếp bằng bảng dữ liệu theo ngày đã lắp ráp ở phần trước của chương. Quan điểm trung thực cho cuốn sách này là thước đo Amihud thực hiện hiện nằm ngoài tầm với đối với phần lớn mặt cắt ngang HOSE, vì một lý do đáng nói rõ: độ chính xác của ước lượng được dẫn dắt bởi \(M\), tức số lượng lợi nhuận trong phiên thực sự khác không. Một cổ phiếu chỉ khớp lệnh vài lần mỗi phiên không có 45 lợi nhuận năm phút mang thông tin — nó có một lưới năm phút phần lớn được lấp đầy bằng giá cũ được điền tiếp (forward-fill), với các lợi nhuận bằng không mang tính cơ học không đóng góp gì cho \(\text{RV}\). Chương vi cấu trúc thị trường mô phỏng trực tiếp điều này (Mục 4.5.6) và đi đến một kết luận rõ ràng hơn người ta có thể đoán. Mức tăng độ chính xác được quyết định bởi số khoảng mà giá thực sự thay đổi chứ không phải số điểm trên lưới, nên nó suy giảm từ mức 7.2\(\times\) theo lý thuyết xuống dưới 2\(\times\) đối với một cổ phiếu giao dịch năm lần mỗi ngày. Độ chệch xuống đi kèm trong \(\text{RV}\) là có thật nhưng chỉ ở bậc hai so với điều đó, vì việc thô hóa lấy mẫu của một martingale không làm chệch phương sai thực hiện về kỳ vọng; điều mà tình trạng giá cũ loại bỏ là biến động trước giao dịch đầu tiên và sau giao dịch cuối cùng. Đây chính là vấn đề giao dịch mỏng đã thúc đẩy thước đo lợi nhuận bằng không trong Phương trình 14.4, và nó tác động lên thước đo Amihud thực hiện mạnh hơn so với phiên bản theo ngày.
Hệ quả thực tiễn là ước lượng này là một cải tiến dành cho nhóm thanh khoản cao của thị trường — đại khái là các cổ phiếu thuộc VN30 và các mã vốn hóa trung bình lớn hơn — chứ không phải vật thay thế cho Phương trình 37.2 trên toàn bộ bảng dữ liệu. Việc xây dựng nó cho Việt Nam đòi hỏi dữ liệu khớp lệnh (tick) trong phiên từ HOSE, vốn không nằm trong quy trình dữ liệu theo ngày được sử dụng ở những phần khác của chương.
14.4.9 Hướng dẫn Thực hành
Lấy mẫu ở năm phút, không dày hơn. Với 225 phút khớp lệnh liên tục của HOSE, điều này cho \(M = 45\) và mức tăng độ chính xác 7.2\(\times\). Nên cưỡng lại cám dỗ hạ xuống một phút: Bảng 14.2 cho thấy mức cải thiện lý thuyết, nhưng (A2) sẽ đổ vỡ từ lâu trước khi bạn thu được nó. Năm phút không phải là một quy ước tùy tiện — đó là tần suất mà sự đánh đổi chệch–phương sai giữa sai số lấy mẫu và sự tích lũy nhiễu thường được chọn (Andersen và c.s. 2003), các tính toán lấy mẫu tối ưu của Aït-Sahalia, Mykland, và Zhang (2005) và Bandi và Russell (2008) cho kết quả quanh vùng đó đối với cổ phiếu Mỹ có thanh khoản cao, và Liu, Patton, và Sheppard (2015) thấy trên nhiều loại tài sản rằng rất ít thước đo thực hiện nào luôn vượt được \(\text{RV}\) năm phút. Vì HOSE mỏng hơn và bước giá thô hơn các thị trường mà những nghiên cứu đó khảo sát, khoảng tối ưu ở đây nếu có khác thì là dài hơn năm phút, không bao giờ ngắn hơn.
Sàng lọc theo tần suất giao dịch trước khi tính. Yêu cầu một số lượng tối thiểu các khoảng có giao dịch thực sự xảy ra — chứ không đơn thuần là có giá — và quay về Phương trình 37.2 khi dưới ngưỡng đó. Hãy báo cáo những cổ phiếu nào phải dùng phương án dự phòng.
Xử lý giờ nghỉ trưa và các phiên khớp lệnh định kỳ một cách tường minh. Khoảng 11:30–13:00 không phải là một khoảng tính lợi nhuận, và giá ATO cùng ATC đến từ khớp lệnh định kỳ chứ không phải khớp lệnh liên tục. Hãy tính \(\text{RV}\) trong từng phiên liên tục rồi cộng lại, thay vì lấy sai phân xuyên qua giờ nghỉ hoặc xuyên qua các phiên định kỳ.
Loại giao dịch thỏa thuận khỏi mẫu số, theo (A4), để phiên thỏa thuận 14:45–15:00 không thổi phồng \(\text{DVOL}\) so với một tử số chỉ được đo trên phần khớp lệnh liên tục.
Ưu tiên phiên bản song lũy thừa trong các ngày chạm biên độ. Biên độ giá theo ngày của Việt Nam khiến các bước dịch chuyển rời rạc lớn trở nên phổ biến, và Phương trình 14.15 là phiên bản trụ được trước chúng.
Chỉ chuyển sang ước lượng vững với nhiễu khi buộc phải lấy mẫu dày. Các ước lượng trong Phương trình 14.13 là lựa chọn mặc định đúng ở năm phút. Nếu ứng dụng thực sự đòi hỏi lưới dày hơn, tài liệu đưa ra ba họ phương pháp, xếp theo chi phí triển khai tăng dần: ước lượng hai thang thời gian của Zhang, Mykland, và Aït-Sahalia (2005), lấy mẫu con ở hai tần suất và ngoại suy để loại bỏ nhiễu; cách tiếp cận tiền lấy trung bình (pre-averaging) của Jacod và c.s. (2009), lấy trung bình lợi nhuận trong từng khối trước khi bình phương; và các hạt nhân thực hiện (realized kernels) của Barndorff-Nielsen và c.s. (2008), gán trọng số cho các tự hiệp phương sai của lợi nhuận trong phiên. Hansen và Lunde (2006) là tài liệu tham chiếu chuẩn để chẩn đoán mức độ nhiễu hiện diện ngay từ đầu, và biểu đồ chữ ký (signature plot) của họ — \(\text{RV}\) theo tần suất lấy mẫu — nên được lập cho mặt cắt ngang Việt Nam trước khi thử bất kỳ điều nào ở trên.
Cân nhắc cắt cụt theo láng giềng gần nhất như một phương án thay cho song lũy thừa.Andersen, Dobrev, và Schaumburg (2012) đề xuất các ước lượng MinRV và MedRV, vững với bước nhảy như \(\text{BV}\) nhưng hoạt động tốt hơn khi các bước nhảy đến ở các khoảng liền kề — một kịch bản thực tế vào ngày chạm biên độ, khi biên độ bị tiếp cận qua nhiều khoảng liên tiếp chứ không phải trong một lần dịch chuyển.
Phần mềm. Không có gói đơn lẻ nào làm trọn vẹn việc này; \(\text{RV}\) và \(\text{BV}\) chỉ là vài dòng pandas, như trong phần mô phỏng ở trên. Các hàm realized trong hệ sinh thái arch bao phủ các ước lượng vững với nhiễu cho độc giả không muốn tự triển khai.
14.4.10 Những Sai lầm Thường gặp
Lấy mẫu quá dày. Đây là lỗi phổ biến nhất. Nhiều quan sát hơn trông có vẻ là nhiều thông tin hơn, nhưng Phương trình 14.18 giả định rằng thứ đang được lấy mẫu là giá hiệu quả. Quá vài phút trên HOSE thì điều đó không còn đúng nữa.
Coi lợi nhuận bằng không trong phiên là thông tin. Trên lưới năm phút, một mức giá cũ tạo ra lợi nhuận bằng không mà \(\text{RV}\) đọc thành độ biến động bằng không. Nếu không sàng lọc, điều này khiến những cổ phiếu kém thanh khoản nhất trông thanh khoản nhất — dấu của sai số ngược lại với phỏng đoán trực giác.
Nhầm lẫn thang đo. Bỏ đi hằng số \(\sqrt{2/\pi}\) sẽ làm Phương trình 14.14 lớn hơn Phương trình 37.2 khoảng 25%, và điều đó âm thầm phá vỡ mọi so sánh với các mức Amihud đã công bố trước đây hoặc với các phân loại ở những phần khác của chương.
Cho rằng mức cải thiện chuyển thành phần bù theo tỷ lệ một-đổi-một.Phương trình 14.21 nói rằng một biến phân loại sạch hơn sẽ nâng phần bù đo được tiến về phần bù thật. Nó không tạo ra một phần bù vốn không tồn tại, và nó cũng không khắc phục các vấn đề thiên kiến sống sót và hủy niêm yết vốn quyết định những quan sát công ty–tháng nào được đưa vào bảng dữ liệu ngay từ đầu.
Quên rằng mẫu số không thay đổi. Tất cả những điều trên chỉ cải thiện tử số. Khối lượng vẫn chịu ảnh hưởng của các quy ước báo cáo và các phức tạp liên quan đến giao dịch thỏa thuận đã thảo luận trong chương vi cấu trúc thị trường.
14.5 Lắp ráp Bảng dữ liệu Thanh khoản
Chúng ta ghép cả bảy thước đo vào một bảng dữ liệu công ty–tháng duy nhất để phân tích so sánh.
14.6.1 Thống kê Mô tả theo Nhóm Ngũ phân vị Quy mô
Thanh khoản biến thiên rất lớn trên phân phối quy mô. Các cổ phiếu vốn hóa nhỏ của Việt Nam có thể kém thanh khoản hơn các cổ phiếu vốn hóa lớn nhiều bậc độ lớn.
Compute liquidity statistics by market cap quintile
# Assign size quintiles within each monthpanel['size_quintile'] = ( panel.groupby('month_end')['market_cap'] .transform(lambda x: pd.qcut(x, 5, labels=['Q1 (Small)', 'Q2','Q3', 'Q4','Q5 (Large)'], duplicates='drop')))# Average liquidity by quintileliq_by_size = ( panel.groupby('size_quintile')[liquidity_cols] .mean() .round(4))print("Average Liquidity by Market Cap Quintile:")print(liq_by_size.to_string())
Các thước đo thanh khoản khác nhau tương quan mạnh đến mức nào? Nếu chúng nắm bắt cùng một chiều cơ bản, chúng ta kỳ vọng tương quan cao. Nếu chúng nắm bắt các chiều khác nhau (độ chặt so với độ sâu so với hoạt động giao dịch), tương quan sẽ ở mức vừa phải.
Compute and plot cross-sectional rank correlations
Do tính đa chiều của thanh khoản, chúng ta trích xuất một nhân tố thanh khoản tổng hợp bằng PCA:
Extract principal components from standardized liquidity measures
from sklearn.preprocessing import StandardScalerfrom sklearn.decomposition import PCA# Standardize each measure within each month (cross-sectional)liq_data = panel[liquidity_cols].copy()liq_data['neg_log_turnover'] =-liq_data['log_turnover']pca_cols = ['amihud', 'zero_return_pct', 'neg_log_turnover','roll_spread', 'cs_spread', 'log_kyle']# Drop rows with any missing liquidity measureliq_complete = panel.dropna(subset=pca_cols).copy()# Cross-sectional standardization by monthdef standardize_within_month(df, cols):for col in cols: df[col +'_z'] = ( df.groupby('month_end')[col] .transform(lambda x: (x - x.mean()) / x.std()) )return dfliq_complete = standardize_within_month(liq_complete, pca_cols)z_cols = [c +'_z'for c in pca_cols]# Pool all months for PCApca_input = liq_complete[z_cols].dropna()pca = PCA(n_components=3)pca.fit(pca_input)print("PCA Explained Variance Ratios:")for i, (var, cumvar) inenumerate(zip( pca.explained_variance_ratio_, np.cumsum(pca.explained_variance_ratio_))):print(f" PC{i+1}: {var:.3f} (cumulative: {cumvar:.3f})")print("\nPC1 Loadings:")for col, loading inzip(pca_cols, pca.components_[0]):print(f" {col:<20}: {loading:.3f}")# Assign PC1 as composite illiquidityliq_complete['illiq_pc1'] = pca.transform( liq_complete[z_cols].values)[:, 0]
14.7 Thanh khoản Tổng hợp và Điều kiện Thị trường
14.7.1 Chuỗi Thời gian của Thanh khoản Thị trường
Thanh khoản tổng hợp (tức là mức phi thanh khoản trung bình trên tất cả cổ phiếu) biến đổi đáng kể theo thời gian. Chordia, Roll, và Subrahmanyam (2001) ghi nhận rằng thanh khoản toàn thị trường suy giảm trong các giai đoạn biến động cao và lợi nhuận thị trường âm.
Chordia, Roll, và Subrahmanyam (2000) thấy rằng thanh khoản của từng cổ phiếu đồng biến với thanh khoản thị trường, ngay cả sau khi kiểm soát các nhân tố riêng của công ty. Chúng ta kiểm định tính đồng biến ở Việt Nam bằng cách hồi quy các thay đổi của thanh khoản cấp công ty trên các thay đổi của thanh khoản cấp thị trường:
trong đó \(\Delta L_{i,m}\) là thay đổi mức phi thanh khoản của công ty \(i\), \(\Delta L_{M,m}\) là thay đổi mức phi thanh khoản trung bình thị trường (không tính công ty \(i\)), và các số hạng dẫn/trễ nắm bắt sự điều chỉnh không đồng bộ. Hệ số \(\beta_i\) đo độ nhạy của thanh khoản công ty \(i\) đối với các cú sốc thanh khoản toàn thị trường.
Test for commonality in liquidity across Vietnamese stocks
# Compute monthly changes in Amihud for each firm and the marketpanel_common = panel[['ticker', 'month_end', 'amihud']].dropna().copy()panel_common = panel_common.sort_values(['ticker', 'month_end'])panel_common['d_amihud'] = ( panel_common.groupby('ticker')['amihud'].diff())# Market-level illiquidity change (equal-weighted, excluding firm i)mkt_liq = ( panel_common.groupby('month_end')['amihud'] .mean() .diff() .to_frame('d_amihud_mkt'))mkt_liq['d_amihud_mkt_lag'] = mkt_liq['d_amihud_mkt'].shift(1)mkt_liq['d_amihud_mkt_lead'] = mkt_liq['d_amihud_mkt'].shift(-1)panel_common = panel_common.merge(mkt_liq, on='month_end', how='left')# Estimate commonality for each firmdef estimate_commonality(group, min_obs=24): g = group.dropna(subset=['d_amihud', 'd_amihud_mkt'])iflen(g) < min_obs:returnNone y = g['d_amihud'] X = sm.add_constant(g[['d_amihud_mkt', 'd_amihud_mkt_lag','d_amihud_mkt_lead']])try: model = sm.OLS(y, X).fit()return pd.Series({'beta_mkt': model.params['d_amihud_mkt'],'beta_t': model.tvalues['d_amihud_mkt'],'r_squared': model.rsquared })exceptException:returnNonecommonality = ( panel_common .groupby('ticker') .apply(estimate_commonality) .dropna())print("Commonality in Liquidity (Amihud):")print(f" Mean beta_mkt: {commonality['beta_mkt'].mean():.3f}")print(f" Median beta_mkt: {commonality['beta_mkt'].median():.3f}")print(f" % significant at 5%: "f"{(commonality['beta_t'].abs() >1.96).mean():.1%}")print(f" Mean R-squared: {commonality['r_squared'].mean():.3f}")
14.8 Thanh khoản có được Định giá không?
14.8.1 Phân loại Danh mục
Chúng ta kiểm định xem mức phi thanh khoản có dự báo lợi nhuận tương lai hay không bằng cách phân loại cổ phiếu vào các danh mục ngũ phân vị dựa trên các thước đo thanh khoản có độ trễ và so sánh lợi nhuận trung bình giữa các nhóm ngũ phân vị.
Sort stocks into quintiles by lagged illiquidity and compute returns
def liquidity_portfolio_sort(panel_df, liq_col, n_groups=5):""" Compute quintile portfolio returns sorted on lagged liquidity. Lag the sorting variable by one month to avoid look-ahead bias. """ df = panel_df[['ticker', 'month_end', 'monthly_return','market_cap', liq_col]].dropna().copy() df = df.sort_values(['ticker', 'month_end'])# Lag the sorting variable df['liq_lag'] = df.groupby('ticker')[liq_col].shift(1) df = df.dropna(subset=['liq_lag', 'monthly_return'])# Assign quintiles within each month df['quintile'] = ( df.groupby('month_end')['liq_lag'] .transform(lambda x: pd.qcut(x, n_groups, labels=False, duplicates='drop')) )# EW portfolio returns by quintile-month port_returns = ( df.groupby(['month_end', 'quintile'])['monthly_return'] .mean() .unstack() )# Long-short (Q5 - Q1)if n_groups -1in port_returns.columns and0in port_returns.columns: port_returns['long_short'] = ( port_returns[n_groups -1] - port_returns[0] )return port_returns# Run sorts for each illiquidity measuresort_measures = {'Amihud': 'amihud','Zero-Return': 'zero_return_pct','Neg. Turnover': 'log_turnover', # Will reverse below'Roll Spread': 'roll_spread','Corwin-Schultz': 'cs_spread',}# For turnover, negate so higher = less liquidpanel_sorts = panel.copy()panel_sorts['neg_turnover'] =-panel_sorts['log_turnover']sort_measures_actual = {'Amihud': 'amihud','Zero-Return': 'zero_return_pct','Neg. Turnover': 'neg_turnover','Roll Spread': 'roll_spread','Corwin-Schultz': 'cs_spread',}print("Liquidity Premium (EW, Quintile Sorts):")print(f"{'Measure':<18}{'Q1 (Liquid)':>12}{'Q5 (Illiquid)':>14} "f"{'Q5-Q1':>10}{'t-stat':>8}")print("-"*62)sort_results = {}for name, col in sort_measures_actual.items(): ports = liquidity_portfolio_sort(panel_sorts, col) sort_results[name] = ports q1 = ports[0].mean() *12 q5 = ports[4].mean() *12if4in ports.columns else np.nan ls = ports['long_short'].mean() *12if'long_short'in ports else np.nan ls_se = ports['long_short'].std() / np.sqrt(len(ports)) * np.sqrt(12) if'long_short'in ports else np.nan t = ls / ls_se if ls_se and ls_se >0else np.nanprint(f"{name:<18}{q1:>12.4f}{q5:>14.4f}{ls:>10.4f}{t:>8.2f}")
Bar chart of quintile returns by liquidity measure
Các phân loại danh mục mang tính thông tin nhưng không thể kiểm soát đồng thời nhiều đặc điểm. Chúng ta dùng các hồi quy mặt cắt ngang theo kiểu Fama và French (1993) để kiểm định xem thanh khoản có dự báo lợi nhuận sau khi kiểm soát quy mô, giá trị và động lượng hay không:
Trung bình chuỗi thời gian của hệ số theo tháng \(\bar{\gamma}_1\) ước lượng phần bù phi thanh khoản, và thống kê t của nó dùng sai số chuẩn của Fama và French (1993).
Fama-MacBeth regressions of returns on lagged liquidity and controls
def fama_macbeth(panel_df, illiq_col, controls=['log_mcap', 'bm'], min_stocks=50):""" Run Fama-MacBeth cross-sectional regressions of next-month returns on lagged illiquidity and controls. """ df = panel_df.copy() df = df.sort_values(['ticker', 'month_end'])# Lag the illiquidity measure df['illiq_lag'] = df.groupby('ticker')[illiq_col].shift(1)# Lag controlsfor c in controls: df[c +'_lag'] = df.groupby('ticker')[c].shift(1) regressors = ['illiq_lag'] + [c +'_lag'for c in controls] df = df.dropna(subset=['monthly_return'] + regressors)# Month-by-month cross-sectional regressions months =sorted(df['month_end'].unique()) gamma_list = []for month in months: cross = df[df['month_end'] == month]iflen(cross) < min_stocks:continue y = cross['monthly_return'].values X = sm.add_constant(cross[regressors].values)try: model = sm.OLS(y, X).fit() gammas = {'month_end': month, 'intercept': model.params[0]}for j, reg inenumerate(regressors): gammas[reg] = model.params[j +1] gamma_list.append(gammas)exceptException:pass gamma_df = pd.DataFrame(gamma_list)# Time-series averages and t-statistics results = {}for col in ['intercept'] + regressors: mean = gamma_df[col].mean() se = gamma_df[col].std() / np.sqrt(len(gamma_df)) t = mean / se if se >0else np.nan results[col] = {'Coefficient': mean, 'SE': se, 't-stat': t}return pd.DataFrame(results).T, gamma_df# Run for each illiquidity measureprint("Fama-MacBeth Regressions: R_{i,m+1} on ILLIQ_{i,m} + controls")print("="*70)for name, col in [('Amihud', 'amihud'), ('Zero-Return', 'zero_return_pct'), ('Roll Spread', 'roll_spread'), ('Corwin-Schultz', 'cs_spread')]: results, gammas = fama_macbeth(panel, col)print(f"\n{name}:")print(results[['Coefficient', 't-stat']].round(4).to_string())
14.8.3 Phần bù Thanh khoản đã Điều chỉnh Nhân tố
Phần bù thanh khoản có thể được giải thích một phần hoặc toàn bộ bởi các nhân tố rủi ro hiện có (quy mô, giá trị, động lượng). Chúng ta kiểm định điều này bằng cách hồi quy lợi nhuận danh mục mua–bán khống thanh khoản trên các nhân tố Fama-French-Carhart:
Regress liquidity long-short returns on FF+Carhart factors
14.9.1 Chuyển các Thước đo thành Chi phí Giao dịch
Đối với các nhà thực hành, câu hỏi then chốt là: một giá trị Amihud hay chênh lệch giá nhất định có ý nghĩa gì về chi phí VND thực tế cho mỗi giao dịch? Chúng ta hiệu chỉnh mối quan hệ giữa các thước đo xấp xỉ tần suất thấp và chi phí giao dịch tường minh.
Calibrate liquidity proxies to estimated round-trip trading costs
def estimate_round_trip_cost(row):""" Estimate total round-trip trading cost (in %) from multiple liquidity proxies. Components: 1. Explicit: commission + tax (~0.35% round-trip) 2. Spread cost: half-spread each way 3. Price impact: function of trade size """ explicit =0.0035# 35 bps round-trip# Use Corwin-Schultz or quoted spread as spread estimate spread = row.get('cs_spread', row.get('quoted_spread', 0.005)) spread_cost = spread # Full spread = round-trip cost# Price impact (approximate from Amihud)# For a trade of 1% of daily volume amihud_raw = row.get('amihud_raw', 0) impact = amihud_raw *0.01# Rough approximationreturn explicit + spread_cost + impactpanel['estimated_rtc'] = panel.apply(estimate_round_trip_cost, axis=1)# Distribution by size quintilertc_by_size = ( panel.groupby('size_quintile')['estimated_rtc'] .agg(['mean', 'median', 'std']) .round(4))print("Estimated Round-Trip Cost by Size Quintile (%):")print((rtc_by_size *100).round(2).to_string())
Plot estimated trading costs by size quintile
fig, axes = plt.subplots(1, 2, figsize=(14, 5))# Panel A: Distributionfor q, color inzip(['Q1 (Small)', 'Q3', 'Q5 (Large)'], ['#C0392B', '#F1C40F', '#27AE60']): subset = panel[panel['size_quintile'] == q]['estimated_rtc'].dropna() subset = subset[subset <0.15] # Trim extreme axes[0].hist(subset *100, bins=50, density=True, alpha=0.5, color=color, label=q, edgecolor='white')axes[0].set_xlabel('Estimated Round-Trip Cost (%)')axes[0].set_ylabel('Density')axes[0].set_title('Panel A: Cost Distribution by Size')axes[0].legend()# Panel B: Time series of median costcost_ts = ( panel.groupby('month_end')['estimated_rtc'] .median() .reset_index())axes[1].plot(pd.to_datetime(cost_ts['month_end']), cost_ts['estimated_rtc'] *100, color='#2C5F8A', linewidth=1.5)axes[1].set_xlabel('Date')axes[1].set_ylabel('Median Round-Trip Cost (%)')axes[1].set_title('Panel B: Aggregate Trading Costs Over Time')plt.tight_layout()plt.show()
Hình 14.5
14.9.2 Tính Khả thi của Chiến lược
Một ứng dụng quan trọng của việc đo lường thanh khoản là kiểm định xem một chiến lược khai thác hiện tượng bất thường có còn sinh lời sau khi tính đến chi phí giao dịch thực tế hay không. Chúng ta tính lợi nhuận sau chi phí cho chính các danh mục phân loại theo thanh khoản. Đây vốn là một phép kiểm định thận trọng vì vị thế mua kém thanh khoản chịu chi phí cao nhất.
Compute net-of-cost returns for illiquidity-sorted portfolios
# For each quintile, estimate average monthly turnover and cost# and subtract from gross returnsfor name, ports in sort_results.items():if'long_short'notin ports.columns:continue gross_ann = ports['long_short'].mean() *12# Estimate costs: illiquid quintile has higher costs# Assume monthly turnover of ~15% for long-short with monthly rebalancing turnover =0.15 cost_q1 =0.003# 30 bps per trade for liquid stocks cost_q5 =0.015# 150 bps for illiquid stocks avg_cost = (cost_q1 + cost_q5) /2# Average across long and short monthly_tc = turnover * avg_cost net_ann = gross_ann - monthly_tc *12print(f"{name:<18}: Gross = {gross_ann*100:>6.2f}%, "f"TC = {monthly_tc*1200:>6.1f} bps/mo, "f"Net = {net_ann*100:>6.2f}%")
14.10 Thanh khoản trong Giai đoạn Căng thẳng Thị trường
14.10.1 Tháo chạy sang Thanh khoản
Trong giai đoạn căng thẳng thị trường, nhà đầu tư bán các tài sản kém thanh khoản và mua các tài sản thanh khoản, đó là hiện tượng “tháo chạy sang thanh khoản” (flight to liquidity) làm nới rộng chênh lệch lợi nhuận giữa các cổ phiếu thanh khoản và kém thanh khoản. Hameed, Kang, và Viswanathan (2010) chỉ ra rằng dạng thức này mạnh nhất khi lợi nhuận thị trường âm nhất.
14.10.2 Đồng biến của Thanh khoản với Rủi ro Toàn cầu
Thanh khoản thị trường Việt Nam có thể bị chi phối bởi các nhân tố rủi ro toàn cầu, đặc biệt đối với các cổ phiếu do nhà đầu tư nước ngoài nắm giữ. Chúng ta kiểm định xem các thước đo rủi ro toàn cầu (VIX, sức mạnh của USD) có dự báo thanh khoản tổng hợp của Việt Nam hay không:
Regress Vietnamese aggregate liquidity on global risk proxies
# Merge aggregate liquidity with global variablesglobal_vars = client.get_macro_data( variables=['vix_close', 'dxy_index'], start_date='2008-01-01', end_date='2024-12-31', frequency='monthly')agg_liq_global = agg_liquidity.merge( global_vars, on='month_end', how='inner')# Changes in all variablesfor col in ['amihud_median', 'vix_close', 'dxy_index']: agg_liq_global[f'd_{col}'] = agg_liq_global[col].diff()# Regressiony = agg_liq_global['d_amihud_median'].dropna()X = sm.add_constant( agg_liq_global.loc[y.index, ['d_vix_close', 'd_dxy_index']])model = sm.OLS(y, X).fit(cov_type='HAC', cov_kwds={'maxlags': 3})print("Aggregate Illiquidity ~ Global Risk:")print(model.summary().tables[1])
14.11 Xây dựng một Nhân tố Thanh khoản có thể Giao dịch
Theo Pástor và Stambaugh (2003), chúng ta xây dựng một nhân tố thanh khoản tổng hợp có thể dùng trong các kiểm định định giá tài sản. Nhân tố này nắm bắt các cú sốc (innovations) của thanh khoản tổng hợp (những thay đổi không được kỳ vọng của điều kiện giao dịch toàn thị trường).
Construct an aggregate liquidity innovation factor
# Step 1: Compute market-level Amihud as EW averagemkt_amihud = ( panel.groupby('month_end')['amihud'] .mean() .to_frame('mkt_amihud'))# Step 2: Estimate AR(2) model for aggregate illiquiditymkt_amihud['mkt_amihud_lag1'] = mkt_amihud['mkt_amihud'].shift(1)mkt_amihud['mkt_amihud_lag2'] = mkt_amihud['mkt_amihud'].shift(2)mkt_amihud = mkt_amihud.dropna()ar_model = sm.OLS( mkt_amihud['mkt_amihud'], sm.add_constant(mkt_amihud[['mkt_amihud_lag1', 'mkt_amihud_lag2']])).fit()# Step 3: Residuals = liquidity innovations# Negative innovation = liquidity improved (good)# Positive innovation = liquidity deteriorated (bad)mkt_amihud['liq_innovation'] = ar_model.resid# Step 4: Test whether liquidity innovations predict returns# Higher sensitivity to negative innovations = higher expected returnprint("AR(2) Model for Aggregate Amihud:")print(f" R-squared: {ar_model.rsquared:.3f}")print(f" AR(1) coef: {ar_model.params['mkt_amihud_lag1']:.3f} "f"(t={ar_model.tvalues['mkt_amihud_lag1']:.2f})")# Step 5: Estimate liquidity betas for each firmpanel_liq_beta = panel.merge( mkt_amihud[['liq_innovation']], left_on='month_end', right_index=True, how='inner')def estimate_liq_beta(group, min_obs=36): g = group.dropna(subset=['monthly_return', 'liq_innovation'])iflen(g) < min_obs:returnNone y = g['monthly_return'] X = sm.add_constant(g['liq_innovation'])try: model = sm.OLS(y, X).fit()return model.params['liq_innovation']exceptException:returnNoneliq_betas = ( panel_liq_beta .groupby('ticker') .apply(estimate_liq_beta) .dropna() .to_frame('liq_beta'))print(f"\nLiquidity Beta Distribution:")print(liq_betas['liq_beta'].describe().round(4))
14.12 Hướng dẫn Thực hành cho Việt Nam
Phân tích trong chương này đưa ra các khuyến nghị sau:
Dành cho nhà nghiên cứu: Tỷ số phi thanh khoản Amihud là thước đo xấp xỉ thanh khoản đa dụng tốt nhất cho chứng khoán Việt Nam. Nó có độ bao phủ cao nhất, khả năng dự báo lợi nhuận mặt cắt ngang mạnh nhất, và mối quan hệ vững chắc nhất với quy mô công ty. Khi cần một thước đo thứ hai để kiểm tra độ vững, tỷ lệ lợi nhuận bằng không là bổ sung tự nhiên—nó nắm bắt một chiều khác (ngưỡng chi phí giao dịch) và có độ bao phủ gần như đầy đủ.
Dành cho xây dựng danh mục đầu tư: Mọi kiểm định ngược của một chiến lược chứng khoán Việt Nam nên tính và báo cáo chi phí khứ hồi ước lượng theo nhóm ngũ phân vị. Các chiến lược tập trung vào hai nhóm ngũ phân vị quy mô thấp nhất chịu chi phí 2–5% mỗi vòng khứ hồi, khiến việc tái cân bằng hằng tháng không kinh tế. Tái cân bằng theo quý hoặc theo năm với ràng buộc về tỷ lệ vòng quay thì thực tế hơn.
Dành cho quản trị rủi ro: Theo dõi điều kiện thanh khoản tổng hợp bằng trung vị mặt cắt ngang của Amihud hoặc tỷ lệ lợi nhuận bằng không toàn thị trường. Sự suy giảm thanh khoản dự báo lợi nhuận thị trường âm và chênh lệch giá rộng hơn trong các tháng tiếp theo. Hãy siết chặt giới hạn rủi ro khi mức phi thanh khoản tổng hợp vượt phân vị 90 lịch sử.
Dành cho so sánh quốc tế: Khi so sánh các phần bù nhân tố của Việt Nam với bằng chứng của Mỹ hoặc các thị trường phát triển khác, hãy luôn báo cáo kết quả trên một tập con “nhóm cổ phiếu thanh khoản” (60% hàng đầu theo vốn hóa thị trường) bên cạnh toàn bộ mẫu. Nhiều hiện tượng bất thường trông lớn trong toàn bộ mẫu thu hẹp đáng kể khi giới hạn ở các cổ phiếu thực sự có thể giao dịch ở quy mô lớn.
14.13 Tóm tắt
Bảng Bảng 14.3 trình bày các thước đo thanh khoản khác nhau.
Bảng 14.3: Tóm tắt các thuộc tính của các thước đo thanh khoản trên thị trường Việt Nam.
Thước đo
Chiều
Độ bao phủ
Gradient theo Quy mô
Khả năng Dự báo Lợi nhuận
Cách dùng Khuyến nghị
Amihud
Tác động giá
Cao
Rất dốc
Mạnh
Thước đo chính; phân loại danh mục; Fama-MacBeth
Lợi nhuận bằng không
Chi phí giao dịch
Đầy đủ
Dốc
Mạnh
Kiểm tra độ vững; nghiên cứu thị trường mới nổi
Tỷ lệ vòng quay
Hoạt động giao dịch
Cao
Vừa phải
Vừa phải
Bộ lọc dựa trên khối lượng; phân tích dòng tiền
Chênh lệch Roll
Độ chặt
Vừa phải
Vừa phải
Vừa phải
Ước lượng chênh lệch giá khi không có dữ liệu mua–bán
Corwin-Schultz
Độ chặt
Vừa phải
Vừa phải
Vừa phải
Chênh lệch giá dựa trên cao–thấp; hiệu chỉnh
Chênh lệch giá chào
Độ chặt
Thay đổi
Dốc
Mạnh
Thước đo trực tiếp khi có sẵn
Lambda của Kyle
Tác động giá
Vừa phải
Dốc
Mạnh
Nghiên cứu vi cấu trúc thị trường
Thanh khoản không phải là một cân nhắc thứ yếu đối với nghiên cứu chứng khoán Việt Nam; nó là yếu tố quyết định bậc nhất việc chiến lược nào có thể triển khai, hiện tượng bất thường nào là thật, và kết quả nào là sản phẩm giả tạo của việc giao dịch những cổ phiếu thực tế không thể giao dịch. Mọi phát hiện thực nghiệm trong cuốn sách này nên được đánh giá qua lăng kính của phân tích thanh khoản được phát triển trong chương này.
Acharya, Viral V, và Lasse Heje Pedersen. 2005. “Asset pricing with liquidity risk”. Journal of financial Economics 77 (2): 375–410.
Aït-Sahalia, Yacine, Per A. Mykland, và Lan Zhang. 2005. “How often to sample a continuous-time process in the presence of market microstructure noise”. The Review of Financial Studies 18 (2): 351–416. https://doi.org/10.1093/rfs/hhi016.
Amihud, Yakov. 2002. “Illiquidity and stock returns: cross-section and time-series effects”. Journal of financial markets 5 (1): 31–56. https://doi.org/10.1016/S1386-4181(01)00024-6.
Amihud, Yakov, Allaudeen Hameed, Wenjin Kang, và Huiping Zhang. 2015. “The illiquidity premium: International evidence”. Journal of Financial Economics 117 (2): 350–68. https://doi.org/10.1016/j.jfineco.2015.04.005.
Amihud, Yakov, và Haim Mendelson. 1986. “Asset pricing and the bid-ask spread”. Journal of financial Economics 17 (2): 223–49.
Amihud, Yakov, Haim Mendelson, và Lasse Heje Pedersen. 2005. “Liquidity and asset prices”. Foundations and Trends in Finance 1 (4): 269–364. https://doi.org/10.1561/0500000003.
Amihud, Yakov, và Joonki Noh. 2021. “Illiquidity and stock returns II: Cross-section and time-series effects”. The Review of Financial Studies 34 (4): 2101–23. https://doi.org/10.1093/rfs/hhaa080.
Andersen, Torben G., Tim Bollerslev, Francis X. Diebold, và Paul Labys. 2003. “Modeling and forecasting realized volatility”. Econometrica 71 (2): 579–625. https://doi.org/10.1111/1468-0262.00418.
Andersen, Torben G., Dobrislav Dobrev, và Ernst Schaumburg. 2012. “Jump-robust volatility estimation using nearest neighbor truncation”. Journal of Econometrics 169 (1): 75–93. https://doi.org/10.1016/j.jeconom.2012.01.011.
Bandi, Federico M., và Jeffrey R. Russell. 2008. “Microstructure noise, realized variance, and optimal sampling”. The Review of Economic Studies 75 (2): 339–69. https://doi.org/10.1111/j.1467-937X.2008.00474.x.
Barndorff-Nielsen, Ole E., Peter Reinhard Hansen, Asger Lunde, và Neil Shephard. 2008. “Designing realized kernels to measure the ex post variation of equity prices in the presence of noise”. Econometrica 76 (6): 1481–1536. https://doi.org/10.3982/ECTA6495.
Barndorff-Nielsen, Ole E., và Neil Shephard. 2002. “Econometric analysis of realized volatility and its use in estimating stochastic volatility models”. Journal of the Royal Statistical Society Series B: Statistical Methodology 64 (2): 253–80. https://doi.org/10.1111/1467-9868.00336.
———. 2004. “Power and bipower variation with stochastic volatility and jumps”. Journal of Financial Econometrics 2 (1): 1–37. https://doi.org/10.1093/jjfinec/nbh001.
Brennan, Michael J., và Avanidhar Subrahmanyam. 1996. “Market microstructure and asset pricing: On the compensation for illiquidity in stock returns”. Journal of Financial Economics 41 (3): 441–64. https://doi.org/10.1016/0304-405X(95)00870-K.
Brunnermeier, Markus K, và Lasse Heje Pedersen. 2009. “Market liquidity and funding liquidity”. The review of financial studies 22 (6): 2201–38.
Chordia, Tarun, Richard Roll, và Avanidhar Subrahmanyam. 2000. “Commonality in liquidity”. Journal of financial economics 56 (1): 3–28.
———. 2001. “Market liquidity and trading activity”. The journal of finance 56 (2): 501–30.
Corwin, Shane A, và Paul Schultz. 2012. “A simple way to estimate bid-ask spreads from daily high and low prices”. The journal of finance 67 (2): 719–60.
Datar, Vinay T, Narayan Y Naik, và Robert Radcliffe. 1998. “Liquidity and stock returns: An alternative test”. Journal of financial markets 1 (2): 203–19.
Fama, Eugene F., và Kenneth R. French. 1993. “Common risk factors in the returns on stocks and bonds”. Journal of Financial Economics 33 (1): 3–56. https://doi.org/10.1016/0304-405X(93)90023-5.
Fong, Kingsley YL, Craig W Holden, và Charles A Trzcinka. 2017. “What are the best liquidity proxies for global research?”Review of Finance 21 (4): 1355–1401.
Goyenko, Ruslan Y, Craig W Holden, và Charles A Trzcinka. 2009. “Do liquidity measures measure liquidity?”Journal of financial Economics 92 (2): 153–81.
Goyenko, Ruslan Y, và Andrey D Ukhov. 2009. “Stock and bond market liquidity: A long-run empirical analysis”. Journal of Financial and Quantitative Analysis 44 (1): 189–212.
Hameed, Allaudeen, Wenjin Kang, và Shivesh Viswanathan. 2010. “Stock market declines and liquidity”. The Journal of finance 65 (1): 257–93.
Hansen, Peter R., và Asger Lunde. 2006. “Realized variance and market microstructure noise”. Journal of Business & Economic Statistics 24 (2): 127–61. https://doi.org/10.1198/073500106000000071.
Hasbrouck, Joel. 2009. “Trading costs and returns for US equities: Estimating effective costs from daily data”. The Journal of Finance 64 (3): 1445–77. https://doi.org/10.1111/j.1540-6261.2009.01469.x.
Jacod, Jean, Yingying Li, Per A. Mykland, Mark Podolskij, và Mathias Vetter. 2009. “Microstructure noise in the continuous case: The pre-averaging approach”. Stochastic Processes and their Applications 119 (7): 2249–76. https://doi.org/10.1016/j.spa.2008.11.004.
Karolyi, G. Andrew, Kuan-Hui Lee, và Mathijs A. van Dijk. 2012. “Understanding commonality in liquidity around the world”. Journal of Financial Economics 105 (1): 82–112. https://doi.org/10.1016/j.jfineco.2011.12.008.
Kyle, Albert S. 1985. “Continuous auctions and insider trading”. Econometrica: Journal of the Econometric Society 53 (6): 1315–35. https://doi.org/10.2307/1913210.
Lesmond, David A. 2005. “Liquidity of emerging markets”. Journal of financial economics 77 (2): 411–52.
Lesmond, David A, Joseph P Ogden, và Charles A Trzcinka. 1999. “A new estimate of transaction costs”. The review of financial studies 12 (5): 1113–41.
Liu, Lily Y., Andrew J. Patton, và Kevin Sheppard. 2015. “Does anything beat 5-minute RV? A comparison of realized measures across multiple asset classes”. Journal of Econometrics 187 (1): 293–311. https://doi.org/10.1016/j.jeconom.2015.02.008.
Lou, Xiaoxia, và Tao Shu. 2017. “Price impact or trading volume: Why is the Amihud (2002) measure priced?”The Review of Financial Studies 30 (12): 4481–4520. https://doi.org/10.1093/rfs/hhx072.
Pástor, L’uboš, và Robert F Stambaugh. 2003. “Liquidity risk and expected stock returns”. Journal of Political economy 111 (3): 642–85. https://doi.org/10.1086/374184.
Zhang, Lan, Per A. Mykland, và Yacine Aït-Sahalia. 2005. “A tale of two time scales: Determining integrated volatility with noisy high-frequency data”. Journal of the American Statistical Association 100 (472): 1394–1411. https://doi.org/10.1198/016214505000000169.
# Các thước đo Thanh khoản và Vòng quay Giao dịch::: callout-noteTrong chương này, chúng ta xây dựng một bộ thước đo thanh khoản toàn diện cho thị trường chứng khoán Việt Nam, đối chiếu chúng với nhau và với các chuẩn mực đã biết, kiểm định xem thanh khoản có được định giá trong mặt cắt ngang của lợi nhuận cổ phiếu hay không, khảo sát tính đồng biến (commonality) của thanh khoản, và phân tích cách các điều kiện thanh khoản thay đổi theo thời gian và giữa các trạng thái thị trường.:::Thanh khoản (tức là khả năng giao dịch nhanh với chi phí thấp mà không làm dịch chuyển giá) có lẽ là cân nhắc thực tiễn quan trọng nhất đối với bất kỳ ai làm việc với dữ liệu chứng khoán Việt Nam. Một phần bù nhân tố trông hấp dẫn trong kiểm định ngược (backtest) không có ma sát có thể hoàn toàn không thể triển khai nếu các vị thế mua và bán khống tập trung vào những cổ phiếu kém thanh khoản, vốn có giá dịch chuyển bất lợi cho bạn khi bạn giao dịch. Ngược lại, một phần bù thanh khoản thực sự (tức là khoản bù đắp cho rủi ro cổ phiếu khó bán khi bạn cần bán) là một trong những hiện tượng bất thường vững chắc nhất và có nền tảng lý thuyết tốt nhất trong định giá tài sản.Thách thức nằm ở chỗ thanh khoản vốn đa chiều và khó đo lường. Ở các thị trường phát triển với sổ lệnh giới hạn liên tục và báo cáo giao dịch dưới một giây, các nhà nghiên cứu có thể quan sát trực tiếp chênh lệch giá mua–bán, độ sâu thị trường và tác động giá. Ở Việt Nam, dữ liệu vi cấu trúc ở mức chi tiết này còn hạn chế: HOSE vận hành khớp lệnh định kỳ lúc mở cửa và đóng cửa với khớp lệnh liên tục ở giữa, bước giá thô so với mặt bằng giá, và nhiều cổ phiếu giao dịch thưa đến mức khái niệm "chênh lệch giá chào" chỉ có ý nghĩa vào những ngày cổ phiếu thực sự có giao dịch. Điều này buộc các nhà nghiên cứu phải dựa vào các thước đo xấp xỉ tần suất thấp được tính từ dữ liệu giá và khối lượng theo ngày.Chương này xây dựng các thước đo xấp xỉ thanh khoản chính được dùng trong tài liệu học thuật, kiểm chứng chúng trong bối cảnh Việt Nam, và minh họa việc sử dụng chúng trong định giá tài sản và xây dựng danh mục đầu tư.## Nền tảng Lý thuyết {#sec-liquidity-theory}### Vì sao Thanh khoản Quan trọngThanh khoản tác động đến giá tài sản qua ít nhất ba kênh:1. **Hiệu ứng mức độ.** Nhà đầu tư đòi hỏi được bù đắp cho chi phí giao dịch kỳ vọng. @amihud1986asset chỉ ra rằng các cổ phiếu có chênh lệch giá mua–bán cao hơn mang lại lợi nhuận kỳ vọng cao hơn, với phần bù là hàm tăng của thời gian nắm giữ của nhà đầu tư. Ở trạng thái cân bằng, các cổ phiếu kém thanh khoản phải đem lại lợi nhuận kỳ vọng cao hơn để bù đắp chi phí giao dịch khứ hồi cao hơn.2. **Hiệu ứng rủi ro.** Thanh khoản biến đổi theo thời gian và đồng biến giữa các cổ phiếu. @pastor2003liquidity chỉ ra rằng các cổ phiếu có lợi nhuận nhạy hơn với các cú sốc thanh khoản tổng hợp mang lại lợi nhuận kỳ vọng cao hơn. @acharya2005asset chính thức hóa điều này trong một CAPM điều chỉnh theo thanh khoản, trong đó lợi nhuận đòi hỏi bao gồm phần bù cho rủi ro thanh khoản (tức là rủi ro cổ phiếu trở nên kém thanh khoản đúng vào lúc nhà đầu tư cần bán).3. **Hiệu ứng đồng biến.** @chordia2000commonality ghi nhận rằng thanh khoản của từng cổ phiếu đồng biến mạnh với thanh khoản toàn thị trường. @brunnermeier2009market giải thích điều này qua một "vòng xoáy thanh khoản": khi giá trị tài sản giảm, các ràng buộc ký quỹ siết chặt, buộc các nhà đầu tư dùng đòn bẩy phải bán, làm giảm thanh khoản thị trường, từ đó đẩy giá xuống thấp hơn nữa. Cơ chế này đặc biệt phù hợp với Việt Nam, nơi nhà đầu tư cá nhân có tài khoản ký quỹ là nhóm giao dịch chiếm ưu thế.### Các Chiều của Thanh khoản@kyle1985continuous xác định ba chiều của thanh khoản:1. **Độ chặt (tightness).** Chi phí để đảo vị thế một cách nhanh chóng, được đo bằng chênh lệch giá mua–bán.2. **Độ sâu (depth).** Khối lượng có thể giao dịch mà không làm dịch chuyển giá, liên quan đến tác động giá.3. **Khả năng phục hồi (resiliency).** Tốc độ giá phục hồi sau các cú sốc dòng lệnh không mang thông tin.Không có thước đo đơn lẻ nào nắm bắt được cả ba chiều. @goyenko2009liquidity và @fong2017best đánh giá một cách hệ thống thước đo xấp xỉ tần suất thấp nào nắm bắt tốt nhất từng chiều bằng cách đối chiếu với các thước đo tần suất cao. Phát hiện chính của họ: thước đo @amihud2002illiquidity nắm bắt tốt nhất chiều tác động giá, ước lượng @roll1984simple và chênh lệch @corwin2012simple nắm bắt tốt nhất độ chặt, còn thước đo lợi nhuận bằng không của @lesmond1999new nắm bắt sự kết hợp giữa chi phí giao dịch và bất cân xứng thông tin. Riêng đối với các thị trường mới nổi, @fong2017best khuyến nghị thước đo Amihud và Chênh lệch giá chào theo phần trăm lúc đóng cửa (Closing Percent Quoted Spread) là các thước đo xấp xỉ đáng tin cậy nhất.## Xây dựng Dữ liệu {#sec-liquidity-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 statsfrom linearmodels.panel import PanelOLSfrom linearmodels.asset_pricing import LinearFactorModelimport 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#| code-summary: "Load daily and monthly data"#| eval: falsefrom datacore import DataCoreClientclient = DataCoreClient()# Daily trading datadaily = client.get_daily_prices( exchanges=['HOSE', 'HNX'], start_date='2008-01-01', end_date='2024-12-31', include_delisted=True, fields=['ticker', 'date', 'open', 'high', 'low', 'close','adjusted_close', 'volume', 'turnover_value','market_cap', 'shares_outstanding', 'free_float_pct','bid', 'ask', 'foreign_buy_volume', 'foreign_sell_volume' ])# Monthly aggregatesmonthly = client.get_monthly_returns( exchanges=['HOSE', 'HNX'], start_date='2008-01-01', end_date='2024-12-31', include_delisted=True, fields=['ticker', 'month_end', 'monthly_return', 'market_cap','volume_avg_20d', 'turnover_value_avg_20d','n_trading_days', 'n_zero_volume_days' ])# Firm characteristics for cross-sectional testsfundamentals = client.get_fundamentals( exchanges=['HOSE', 'HNX'], start_date='2008-01-01', end_date='2024-12-31', include_delisted=True, frequency='annual', fields=['ticker', 'fiscal_year', 'total_assets', 'total_equity','net_income', 'revenue', 'book_equity' ])# Factor returns for asset pricing testsfactors = client.get_factor_returns( market='vietnam', start_date='2008-01-01', end_date='2024-12-31', factors=['mkt_excess', 'smb', 'hml', 'wml'])daily['date'] = pd.to_datetime(daily['date'])daily = daily.sort_values(['ticker', 'date'])print(f"Daily observations: {daily.shape[0]:,}")print(f"Monthly observations: {monthly.shape[0]:,}")print(f"Unique tickers: {daily['ticker'].nunique()}")``````{python}#| label: daily-returns#| code-summary: "Compute daily returns and basic trading statistics"#| eval: false# Daily returnsdaily['daily_return'] = ( daily.groupby('ticker')['adjusted_close'].pct_change())daily['abs_return'] = daily['daily_return'].abs()daily['log_return'] = np.log( daily['adjusted_close'] / daily.groupby('ticker')['adjusted_close'].shift(1))# Turnover ratio (shares traded / shares outstanding)daily['turnover_ratio'] = daily['volume'] / daily['shares_outstanding']# Zero indicatorsdaily['zero_return'] = (daily['daily_return'] ==0).astype(int)daily['zero_volume'] = (daily['volume'] ==0).astype(int)# VND turnover (in billions)daily['turnover_vnd_bn'] = daily['turnover_value'] /1e9print("Daily Return Summary:")print(daily['daily_return'].describe().round(6))print(f"\nZero-return days: {daily['zero_return'].mean():.1%}")print(f"Zero-volume days: {daily['zero_volume'].mean():.1%}")```## Xây dựng các Thước đo Thanh khoản {#sec-liquidity-measures}Chúng ta xây dựng bảy thước đo xấp xỉ thanh khoản bao trùm các chiều độ chặt, độ sâu và khả năng phục hồi. Mỗi thước đo được tính ở cấp công ty–tháng, tạo ra một bảng dữ liệu có thể ghép với dữ liệu lợi nhuận theo tháng cho các kiểm định mặt cắt ngang.### Tỷ số Phi thanh khoản AmihudThước đo phi thanh khoản @amihud2002illiquidity là tỷ số giữa giá trị tuyệt đối của lợi nhuận theo ngày và khối lượng giao dịch theo ngày (tính bằng VND):$$\text{ILLIQ}_{i,m} = \frac{1}{D_{i,m}} \sum_{d=1}^{D_{i,m}} \frac{|R_{i,d}|}{\text{DVOL}_{i,d}}$$ {#eq-amihud}trong đó $|R_{i,d}|$ là giá trị tuyệt đối của lợi nhuận theo ngày, $\text{DVOL}_{i,d}$ là khối lượng giao dịch tính bằng VND trong ngày $d$, và $D_{i,m}$ là số ngày giao dịch có khối lượng dương trong tháng $m$. Giá trị càng cao cho thấy mức phi thanh khoản càng lớn.Thước đo Amihud nắm bắt chiều tác động giá của thanh khoản. Nó dựa trên mô hình @kyle1985continuous, trong đó tham số $\lambda$ (lambda của Kyle) đo tác động giá của dòng lệnh: $\Delta p = \lambda \cdot Q$. Tỷ số Amihud là phiên bản tương tự của $\lambda$ ở tần suất theo ngày.```{python}#| label: amihud#| code-summary: "Compute monthly Amihud illiquidity ratio"#| eval: falsedef compute_amihud(daily_df, min_days=10):""" Compute the Amihud (2002) illiquidity ratio at the firm-month level. Excludes zero-volume days. Requires at least min_days observations with positive volume per firm-month. """ df = daily_df[daily_df['volume'] >0].copy() df['month'] = df['date'].dt.to_period('M')# |Return| / VND Volume df['illiq_daily'] = df['abs_return'] / df['turnover_value']# Remove extreme outliers (top 0.1% within each month) df['illiq_daily'] = df.groupby('month')['illiq_daily'].transform(lambda x: x.clip(upper=x.quantile(0.999)) )# Aggregate to firm-month amihud = ( df.groupby(['ticker', 'month']) .agg( amihud_raw=('illiq_daily', 'mean'), n_positive_vol_days=('illiq_daily', 'count') ) .reset_index() )# Filter: require minimum trading days amihud = amihud[amihud['n_positive_vol_days'] >= min_days]# Log transform (raw Amihud is heavily right-skewed) amihud['amihud'] = np.log(1+ amihud['amihud_raw'] *1e6)# Convert period to timestamp for merging amihud['month_end'] = amihud['month'].dt.to_timestamp('M')return amihud[['ticker', 'month_end', 'amihud', 'amihud_raw','n_positive_vol_days']]amihud_monthly = compute_amihud(daily)print(f"Amihud observations: {len(amihud_monthly):,}")print(f"\nLog Amihud distribution:")print(amihud_monthly['amihud'].describe().round(3))```#### Định nghĩa Gốc năm 2002 {#sec-liquidity-amihud-original}@eq-amihud là dạng *hiện đại* của thước đo, không phải dạng mà @amihud2002illiquidity thực sự ước lượng. Dạng gốc tính theo năm chứ không phải theo tháng:$$\text{ILLIQ}_{i,y} = \frac{1}{D_{i,y}}\sum_{d=1}^{D_{i,y}} \frac{|R_{i,y,d}|}{\text{VOLD}_{i,y,d}}$$ {#eq-amihud-original}trong đó $\text{VOLD}_{i,y,d}$ là giá trị giao dịch bằng đô la trong ngày $d$ của năm $y$ và $D_{i,y}$ là số ngày trong năm $y$ có đủ cả dữ liệu lợi nhuận lẫn khối lượng. Những khác biệt so với @eq-amihud không chỉ là hình thức, và mỗi khác biệt là một quyết định mô hình hóa mà các tác giả sau này đã lặng lẽ thay đổi:1. **Theo năm, không theo tháng.** Đối tượng quan tâm của Amihud là mức phi thanh khoản *kỳ vọng*, mà ông mô hình hóa như một đại lượng biến đổi chậm. Ông hồi quy lợi nhuận theo tháng trong năm $y$ trên $\text{ILLIQ}$ đo trong năm $y-1$, nên thước đo là một đặc điểm công ty có tính dai dẳng chứ không phải một biến trạng thái theo tháng. Quy ước theo tháng hiện đang thống trị trong tài liệu đánh đổi tính dai dẳng để lấy độ nhạy, và chịu nhiều nhiễu đo lường hơn đáng kể trên mỗi quan sát: lấy trung bình khoảng 21 tỷ số theo ngày thay vì 250 làm sai số lấy mẫu của giá trị trung bình tăng khoảng $\sqrt{250/21} \approx 3.5$ lần.2. **Chỉ NYSE.** Amihud cố ý loại NASDAQ, vì các giao dịch giữa các nhà tạo lập trên thị trường qua nhà tạo lập (dealer market) tính trùng khối lượng và sẽ làm nhiễm bẩn mẫu số. Điểm tương ứng ở Việt Nam không phải là thị trường qua nhà tạo lập mà là phân khúc giao dịch thỏa thuận (put-through, giao dịch khối lượng lớn thương lượng) và, trên UPCoM, hoạt động giao dịch cực kỳ rời rạc; cả hai đều làm méo $\text{DVOL}$ theo những cách mà thiết kế gốc được xây dựng để tránh.3. **Các bộ lọc.** Ông yêu cầu có hơn 200 ngày dữ liệu trong năm, giá cuối năm trên \$5, và có số cổ phiếu đang lưu hành, đồng thời cắt bỏ 1% cao nhất và thấp nhất của mặt cắt ngang $\text{ILLIQ}$ theo năm như các giá trị ngoại lai. Bộ lọc giá loại bỏ các cổ phiếu mà tính rời rạc của bước giá chi phối tử số, một bộ lọc có tác động thực sự ở Việt Nam, nơi một phần lớn các mã niêm yết giao dịch dưới 10,000 VND với bước giá 10 VND.4. **Hệ số quy đổi.** Amihud nhân với $10^6$, đây là một *quy ước đơn vị gắn với đô la*, không phải thuộc tính của ước lượng. Chúng ta sẽ quay lại điểm này bên dưới, vì đây là nguồn phổ biến nhất khiến các con số Amihud trong tài liệu không thể so sánh với nhau.5. **Điều chỉnh theo trung bình.** Đây là thành phần của bài báo gốc bị bỏ qua thường xuyên nhất và là thành phần quan trọng nhất đối với Việt Nam. Vì giá trị giao dịch bằng đô la tăng theo xu hướng dài hạn cùng với lạm phát, tăng trưởng thị trường và tỷ lệ vòng quay tăng, $\text{ILLIQ}$ thô mang một xu hướng giảm cơ học không liên quan gì đến việc thanh khoản được cải thiện. Do đó Amihud cũng làm việc với thước đo *đã điều chỉnh theo trung bình* $$ \text{ILLIQMA}_{i,y} = \frac{\text{ILLIQ}_{i,y}}{\text{AILLIQ}_{y}}, \qquad \text{AILLIQ}_{y} = \frac{1}{N_y}\sum_{i=1}^{N_y}\text{ILLIQ}_{i,y} $$ {#eq-amihud-ma} vốn không có đơn vị và đã được khử xu hướng theo cấu trúc. Trong một thị trường mà giá trị giao dịch bằng VND tăng gấp cả một bậc độ lớn trong giai đoạn mẫu, việc dùng $\text{ILLIQ}$ thô trong bất kỳ đặc tả gộp (pooled) hay chuỗi thời gian nào mà không có hiệu ứng cố định theo thời kỳ sẽ cho thấy một "sự cải thiện thanh khoản" thực chất là sản phẩm giả tạo của mẫu số.Hai kết quả chính của ông đáng được tách bạch, vì chúng thường bị nhập làm một. Trong mặt cắt ngang, mức phi thanh khoản kỳ vọng được định giá dương: các cổ phiếu có $\text{ILLIQMA}$ năm trước cao hơn mang lại lợi nhuận sau đó cao hơn. Trong chuỗi thời gian, mức phi thanh khoản *không được kỳ vọng* — cú sốc (innovation) từ một AR(1) khớp với $\text{AILLIQ}_y$ — gắn với lợi nhuận đồng thời *âm*, vì một cú sốc thanh khoản được tính ngay vào giá dưới dạng giá giảm dù nó làm tăng lợi nhuận kỳ vọng trong tương lai. Một kiểm định trộn lẫn hai điều này sẽ thấy dấu của kết quả gây nhầm lẫn.#### Tiền thân và Lý thuyết: Vì sao là Tỷ số Lợi nhuận trên Khối lượngThước đo này không xuất hiện từ hư không. @amihud1986asset đã chỉ ra, cả về lý thuyết lẫn thực nghiệm, rằng lợi nhuận kỳ vọng tăng theo chênh lệch giá mua–bán chào, vì nhà đầu tư phải được bù đắp cho chi phí của một vòng khứ hồi. Vấn đề của kết quả đó là dữ liệu: chênh lệch giá chào đòi hỏi lịch sử các báo giá, vốn không tồn tại ở hầu hết các thị trường và trong phần lớn lịch sử. Thước đo năm 2002 được hiểu tốt nhất như một câu trả lời cho câu hỏi "chỉ từ lợi nhuận và khối lượng theo ngày, ta có thể tính được đại lượng nào hành xử như một chi phí giao dịch?" — cũng chính là câu hỏi thúc đẩy các ước lượng Roll, Corwin–Schultz và Lesmond được phát triển ở những phần khác của chương này. @amihud2005liquidity tổng quan tài liệu phát sinh từ đó.Cơ sở lý thuyết đến từ @kyle1985continuous. Trong mô hình của Kyle, tác động giá của một dòng lệnh có dấu $Q$ là $\Delta p = \lambda Q$, nên $\lambda = |\Delta p| / |Q|$ đúng là một tỷ số lợi nhuận trên khối lượng. @eq-amihud là tỷ số đó được tính với dòng lệnh *không dấu*, là tất cả những gì dữ liệu theo ngày cung cấp. @brennan1996market chỉ ra trực tiếp rằng các ước lượng tác động giá xây dựng từ dòng lệnh có dấu trong phiên mang theo một phần bù lợi nhuận, còn @goyenko2009liquidity và @goyenko2009stock về sau xác lập rằng tỷ số Amihud là một thước đo xấp xỉ tần suất thấp tốt cho các chuẩn tác động giá tần suất cao đó. @fong2017best mở rộng cuộc đua này sang dữ liệu toàn cầu và thấy nó hoạt động tốt bên ngoài Hoa Kỳ, đó là lý do nó là công cụ chủ lực trong nghiên cứu thị trường mới nổi và cũng là lý do chương này dựa nhiều vào nó.#### Dòng dõi các Biến thể@tbl-amihud-variants tập hợp các phép biến đổi đang được dùng phổ biến. Tất cả đều đơn điệu theo $\text{ILLIQ}$ thô trong cùng một thời kỳ, điều này có một hệ quả quan trọng sẽ được phát triển bên dưới.| Biến thể | Định nghĩa | Nguồn gốc / cách dùng điển hình ||:---|:---|:---|| Thô | Trung bình của $\lvert R_d\rvert / \text{DVOL}_d$ | Chính ước lượng; hầu như không bao giờ dùng trực tiếp || Amihud đã quy đổi | $\text{ILLIQ}\times 10^6$ | @amihud2002illiquidity, khối lượng bằng đô la || Điều chỉnh theo trung bình | $\text{ILLIQ}_{i}/\overline{\text{ILLIQ}}$ | @amihud2002illiquidity; không có đơn vị, đã khử xu hướng (@eq-amihud-ma) || Logarit | $\log(\text{ILLIQ})$ hoặc $\log(1+\text{ILLIQ})$ | Ổn định phương sai; quy ước dùng trong mã nguồn ở trên || Căn bậc hai | $\sqrt{\text{ILLIQ}}$ | @hasbrouck2009trading; đơn vị gần hơn với chi phí hiệu dụng || Chi phí chuẩn hóa | $\min(a + b\,\text{ILLIQ}\,P^M_{t-1},\,\bar{c})$ | @acharya2005asset; ánh xạ sang chi phí theo phần trăm, có chặn trên || Thứ hạng trong thời kỳ | Phân vị của $\text{ILLIQ}$ trong thời kỳ $t$ | @karolyi2012understanding, @amihud2015illiquidity; so sánh được giữa các thị trường |: Các phép biến đổi của tỷ số phi thanh khoản Amihud đang được dùng phổ biến. Tất cả đều tăng nghiêm ngặt theo $\text{ILLIQ}$ thô trong cùng một thời kỳ. {#tbl-amihud-variants}Hai trong số này đáng được bàn kỹ hơn. Phép chuẩn hóa của @acharya2005asset tồn tại vì mô hình của họ đòi hỏi mức phi thanh khoản được biểu thị dưới dạng *chi phí giao dịch theo phần trăm*, để có thể trừ khỏi lợi nhuận. Hiệu chỉnh của họ $\min(0.25 + 0.30\,\text{ILLIQ}\,P^M_{t-1},\,30)$ co giãn theo mức tăng của danh mục thị trường, hấp thụ đúng xu hướng khối lượng mà $\text{ILLIQMA}$ cũng hấp thụ, và chặn kết quả ở mức 30% để đuôi cực đoan không chi phối. Các hằng số được khớp với dữ liệu giá trị giao dịch bằng đô la Mỹ trong một giai đoạn cụ thể và **không thể chuyển sang VND**. Mã nguồn bên dưới cho thấy công thức thất bại theo cả hai hướng chỉ tùy thuộc vào đơn vị khối lượng: với khối lượng tính bằng VND, mọi cổ phiếu đều được gán chi phí hệ số chặn 0.25% với độ phân tán mặt cắt ngang bằng không, và với khối lượng tính bằng tỷ VND, mọi cổ phiếu đều bị ghim ở mức chặn 30%, lại với độ phân tán bằng không. Chỉ có một đơn vị trung gian tình cờ tạo ra biến thiên, và việc chọn đơn vị đó không có gì thuộc về kinh tế. Phép biến đổi thứ hạng trong thời kỳ được dùng trong tài liệu thanh khoản quốc tế [@karolyi2012understanding; @amihud2015illiquidity] tồn tại chính vì lý do này. Đây là phép biến đổi duy nhất trong bảng bất biến đối với đơn vị tiền tệ, đơn vị khối lượng và giai đoạn.Một cải tiến nữa đáng biết là nghiên cứu của @amihud2021illiquidity, trong đó các tác giả xem xét lại các kết quả gốc bằng một nhân tố kém thanh khoản trừ thanh khoản có thể giao dịch được và một đặc tả có điều kiện. Cách xây dựng của họ là công trình đã công bố gần nhất với nhân tố thanh khoản được xây dựng ở cuối chương này.#### Việc Quy đổi Không Vô hạiĐoạn mã sau đây độc lập và có thể chạy được. Nó xây dựng một bảng dữ liệu theo ngày tổng hợp với mức phi thanh khoản thật *đã biết và không đổi* rồi cho thấy việc chọn biến thể tác động thế nào đến các con số.```{python}#| label: amihud-variants#| code-summary: "All Amihud variants, and what scaling does to them"def amihud_variants(daily, ret="ret", dvol="dvol", scale=1e6, min_days=15):""" Compute the Amihud (2002) ratio and its common transformations from a daily panel with columns [ticker, date, ret, dvol]. `dvol` is currency volume in whatever unit the caller supplies; `scale` is Amihud's units convention, which is *not* unit-free (see below). """ df = daily.loc[daily[dvol] >0].copy() df["period"] = df["date"].dt.to_period("M") df["illiq_d"] = df[ret].abs() / df[dvol] p = (df.groupby(["ticker", "period"]) .agg(illiq=("illiq_d", "mean"), n_days=("illiq_d", "size")) .reset_index()) p = p.loc[p["n_days"] >= min_days].copy() p["illiq_scaled"] = p["illiq"] * scale # Amihud's x10^6 p["illiq_ma"] = p["illiq"] / p.groupby("period")["illiq"].transform("mean") p["log_illiq"] = np.log(p["illiq_scaled"]) # variance stabilizing p["sqrt_illiq"] = np.sqrt(p["illiq_scaled"]) # Hasbrouck (2009) p["illiq_rank"] = p.groupby("period")["illiq"].rank(pct=True)return p# A synthetic panel: 40 stocks, two years, TRUE illiquidity constant over time,# but market-wide VND volume growing at 35% per year.rng_v = np.random.default_rng(11)dates_v = pd.bdate_range("2023-01-02", "2024-12-31")tickers_v = [f"S{i:02d}"for i inrange(40)]level_v = rng_v.lognormal(np.log(3e9), 1.1, size=len(tickers_v)) # VND/dayyears_v = (dates_v - dates_v[0]).days /365.25demo = pd.concat([ pd.DataFrame({"ticker": t,"date": dates_v,"ret": rng_v.normal(0, 0.022, len(dates_v)),"dvol": L * np.exp(rng_v.normal(0, 0.6, len(dates_v))) * np.exp(0.35* years_v), })for t, L inzip(tickers_v, level_v)], ignore_index=True)# (1) The x10^6 convention is a currency convention, not a normalization.print("Median ILLIQ x 10^6, identical economics, three volume units:")for unit, divisor in [("VND", 1.0), ("million VND", 1e6), ("USD @ 25,400", 25_400.0)]: med = amihud_variants(demo.assign(dvol=demo["dvol"] / divisor))["illiq_scaled"].median()print(f" DVOL in {unit:<14s}{med:>12.6g}")# (2) Mean adjustment removes the volume trend; raw scaling does not.v = amihud_variants(demo)ts = v.groupby("period")[["illiq_scaled", "illiq_ma"]].median()first, last = ts.iloc[0], ts.iloc[-1]print("\nTrue illiquidity is constant; volume grows 35%/yr. Median over the panel:")for col, name in [("illiq_scaled", "ILLIQ x 10^6"), ("illiq_ma", "ILLIQMA ")]:print(f" {name}: {first[col]:>10.4g} -> {last[col]:>10.4g} "f"({100* (last[col] / first[col] -1):+.1f}%)")# (3) Every variant is monotone within a period, so ranks are identical.cols_v = ["illiq_scaled", "illiq_ma", "log_illiq", "sqrt_illiq", "illiq_rank"]within = (v.groupby("period")[["illiq"] + cols_v] .corr(method="spearman").xs("illiq", level=1)[cols_v].mean())print("\nMean within-period Spearman rank correlation with raw ILLIQ:")print(within.round(4).to_string())# (4) But the distributions they induce are very different.print("\nPooled distribution shape:")print(v[["illiq_scaled", "log_illiq", "sqrt_illiq"]] .agg(["skew", "kurt"]).round(3).to_string())# (5) Acharya-Pedersen constants are calibrated to US dollar volume, so the# same formula degenerates in opposite directions depending on the unit.print("\nAcharya-Pedersen cost min(0.25 + 0.30*ILLIQ*1e6, 30) by volume unit:")for unit, divisor in [("VND", 1.0), ("million VND", 1e6), ("billion VND", 1e9)]: s = amihud_variants(demo.assign(dvol=demo["dvol"] / divisor))["illiq_scaled"] ap = np.minimum(0.25+0.30* s, 30.0)print(f" DVOL in {unit:<12s} mean={ap.mean():8.4f} sd={ap.std():8.4f} "f"at cap={100* (ap >=30.0).mean():5.1f}%")```Từ đó rút ra năm bài học, và chúng chính là lý do tiểu mục này tồn tại.**Hệ số $\times 10^6$ không mang thông tin.** Cùng một mức phi thanh khoản kinh tế được báo cáo ở khoảng $5\times10^{-6}$, $5$, hoặc $0.13$ chỉ tùy thuộc vào việc khối lượng được ghi bằng VND, triệu VND hay USD. Do đó các con số "phi thanh khoản Amihud bằng 0.8" được công bố không thể so sánh giữa các bài báo trừ khi đơn vị khối lượng được nêu rõ, mà thường thì không. Hãy nêu rõ đơn vị; tốt hơn nữa, hãy báo cáo một phép biến đổi không phụ thuộc vào nó.**Mức thô di chuyển theo xu hướng của mẫu số.** Trong mô phỏng, mức phi thanh khoản thật không đổi theo cấu trúc, vậy mà trung vị của thước đo thô giảm khoảng 45% trong hai năm chỉ vì khối lượng tăng. $\text{ILLIQMA}$ thay đổi vài phần trăm, là nhiễu lấy mẫu. Giá trị giao dịch của Việt Nam tăng nhanh hơn nhiều so với 35% mỗi năm trong nhiều giai đoạn sau năm 2015, nên đây là mối quan tâm bậc nhất đối với mọi nhận định dạng "thanh khoản ở Việt Nam đã cải thiện trong giai đoạn mẫu".**Đối với các phân loại trong cùng thời kỳ, việc chọn biến thể hoàn toàn không quan trọng.** Mọi phép biến đổi trong @tbl-amihud-variants đều tăng nghiêm ngặt trong cùng một thời kỳ, nên tương quan hạng trung bình trong thời kỳ đúng bằng 1.0 và các phân loại danh mục ở phần sau của chương sẽ giống hệt nhau về mặt số học dù dùng biến thể nào. Điều này đáng biết vì nó giới hạn mức độ mà việc chọn lọc biến thể trong tài liệu có thể thực sự quan trọng: nó chỉ có thể quan trọng thông qua các đặc tả dùng *mức* — Fama-MacBeth gộp không có hiệu ứng cố định theo thời kỳ, hồi quy chuỗi thời gian, và mọi thứ so sánh độ lớn giữa các thời kỳ hoặc thị trường.**Phép biến đổi quan trọng vì lý do về phân phối.** Độ lệch (skewness) gộp giảm từ khoảng 3.0 xuống 0.1 và độ nhọn vượt mức (excess kurtosis) từ khoảng 11.8 xuống $-0.4$ dưới phép biến đổi logarit. Vì các hệ số góc Fama-MacBeth là hệ số OLS và OLS không vững trước một đuôi phải nặng như vậy, phép biến đổi logarit đang làm một việc thực sự trong các hồi quy mặt cắt ngang: không phải sửa các thứ hạng, mà là ngăn một số ít quan sát công ty–tháng quyết định hệ số góc.**Các hiệu chỉnh vay mượn không giữ được khi chuyển sang bối cảnh khác.** Phép ánh xạ chi phí của @acharya2005asset là trường hợp rõ nhất. Với khối lượng tính bằng VND, nó gán cho mọi quan sát công ty–tháng giá trị hệ số chặn 0.25%, với độ lệch chuẩn mặt cắt ngang đúng bằng không; với khối lượng tính bằng tỷ VND, nó ghim tất cả ở mức chặn 30%, lại với độ phân tán bằng không. Trong cả hai trường hợp, nó đều không thất bại một cách ồn ào — nó trả về một chi phí theo phần trăm trông hợp lý nhưng hoàn toàn không mang thông tin nào về cổ phiếu nào kém thanh khoản. Mọi hằng số đã công bố dạng $a + b\,\text{ILLIQ}$ đều kế thừa đơn vị của dữ liệu mà nó được khớp, và phải được ước lượng lại chứ không được sao chép.Để áp dụng cùng hàm đó cho bảng dữ liệu thực của chương:```{python}#| label: amihud-variants-real#| eval: false#| code-summary: "Apply the variant constructor to the Vietnamese daily panel"# `daily` carries turnover_value in VND; express it in billions so that the# resulting ratio reads as "return per billion VND traded".amihud_all = amihud_variants( daily.rename(columns={"turnover_value": "dvol"}).assign( dvol=lambda d: d["dvol"] /1e9 ), scale=100.0, # report as PERCENT price move per billion VND min_days=10,)print(amihud_all[["illiq_scaled", "illiq_ma", "log_illiq"]].describe().round(4))```Đặt `scale=100.0` với khối lượng tính bằng tỷ VND cho thước đo một cách đọc có thể giữ nguyên khi chuyển đổi: *mức dịch chuyển giá theo phần trăm gắn với một tỷ VND giao dịch*. Đây là quy ước chúng tôi khuyến nghị cho các nghiên cứu về Việt Nam, và nó luôn phải được nêu cùng với con số.#### Điều Thực sự được Định giá: Phê bình của Lou--ShuCó một phản biện sâu hơn đối với @eq-amihud so với bất kỳ câu hỏi nào về việc quy đổi, và nó liên quan trực tiếp đến cải tiến tần suất cao được phát triển trong @sec-liquidity-realized.@lou2017price phân rã tỷ số Amihud thành phần quy cho mẫu số và phần quy cho tác động giá thực sự theo lợi nhuận trên mỗi đơn vị khối lượng. Phát hiện của họ gây khó chịu đối với cách diễn giải chuẩn: phần bù lợi nhuận gắn với $\text{ILLIQ}$ được dẫn dắt bởi thành phần *khối lượng giao dịch* chứ không phải thành phần tác động giá. Nói thẳng ra, phân loại theo $\text{ILLIQ}$ gần như là phân loại theo nghịch đảo của giá trị giao dịch bằng đô la, và phần bù mà nó đem lại có thể phản ánh bất cứ điều gì mà nghịch đảo giá trị giao dịch đó đại diện — sự nhận biết của nhà đầu tư, sự bị lãng quên, sự chú ý của nhà đầu tư cá nhân — chứ không phải khoản bù đắp cho tác động giá.Điều này có hai hàm ý đối với cuốn sách.Hàm ý thứ nhất là một cảnh báo. Ở những phần sau của chương này báo cáo phần bù thanh khoản từ các phân loại theo $\text{ILLIQ}$, cách diễn giải đúng là "một phần bù gắn với đặc điểm Amihud", chứ không phải "khoản bù đắp cho tác động giá". Phân biệt hai điều này đòi hỏi phép phân rã, và đòi hỏi kiểm soát trực tiếp giá trị giao dịch bằng đô la.Hàm ý thứ hai là một câu hỏi thực sự còn bỏ ngỏ mà ước lượng thực hiện (realized) có khả năng trả lời. Nội dung tác động giá của $\text{ILLIQ}$ nằm ở tử số, và @sec-liquidity-realized cho thấy tử số theo ngày $|R_d|$ mang khoảng 76% sai số đo lường tương đối trong khi mẫu số $\text{DVOL}_d$ được quan sát gần như không có sai số. Một cuộc đua giữa tử số đo với 76% nhiễu và mẫu số đo không nhiễu không phải là một phép thử công bằng xem thành phần nào quan trọng: sai số trong biến cổ điển (errors-in-variables) làm đóng góp ước lượng của tử số suy giảm về không bất kể vai trò kinh tế thật của nó. Liệu kết quả của Lou–Shu có còn đứng vững khi tử số được đo với độ chính xác cao hơn khoảng bảy lần của @lacava2026realized hay không, theo hiểu biết của chúng tôi, vẫn chưa được kiểm định. Chúng tôi đánh dấu đó là một cơ hội nghiên cứu chứ không phải một kết quả, và lưu ý rằng Việt Nam là nơi không thuận lợi để thử nghiệm, vì những lý do về dữ liệu trong phiên được nêu trong @sec-liquidity-realized.#### Cuốn sách này Dùng Biến thể nàoĐể cụ thể, các quy ước được áp dụng trong phần còn lại của chương này và của cuốn sách:1. **Tần suất theo tháng**, để nhất quán với bảng dữ liệu lợi nhuận theo tháng, chấp nhận sai số lấy mẫu tăng khoảng 3.5 lần so với cách xây dựng theo năm của Amihud.2. **Khối lượng tính bằng tỷ VND**, chỉ các ngày có khối lượng dương, tối thiểu mười ngày hợp lệ cho mỗi quan sát công ty–tháng.3. **Biến đổi logarit** cho công việc hồi quy, theo mã nguồn đi kèm @eq-amihud ở trên, vì phân phối gộp quá lệch đối với OLS ở dạng mức.4. **Thứ hạng hoặc nhóm ngũ phân vị trong tháng** cho các phân loại danh mục, vốn như đã chỉ ra ở trên, bất biến đối với mọi lựa chọn khác trong danh sách này.5. **Điều chỉnh theo trung bình** bất cứ khi nào các mức được so sánh giữa các thời điểm, trên thực tế nghĩa là mọi đặc tả không có hiệu ứng cố định theo tháng.Thước đo cũng được báo cáo không qua biến đổi trong các thống kê mô tả, để độc giả có thể quy đổi sang bất kỳ quy ước nào khác.### Ngày Lợi nhuận bằng Không (Thước đo Lesmond)@lesmond1999new đề xuất dùng tỷ lệ các ngày có lợi nhuận bằng không làm thước đo chi phí giao dịch. Trực giác là nếu thay đổi giá trị thực trong một ngày nhỏ hơn chi phí giao dịch khứ hồi, một nhà đầu tư cận biên hợp lý sẽ không giao dịch, và lợi nhuận quan sát được sẽ bằng không. Do đó, tỷ lệ lợi nhuận bằng không là hàm tăng của chi phí giao dịch hiệu dụng.@lesmond2005liquidity kiểm chứng thước đo này cho các thị trường mới nổi và thấy nó tương quan mạnh với các thước đo chi phí tường minh. Ở Việt Nam, nơi các ngày lợi nhuận bằng không rất phổ biến (như đã ghi nhận ở chương trước), thước đo này có ý nghĩa đặc biệt.$$\text{ZeroRet}_{i,m} = \frac{\text{Number of days with } R_{i,d} = 0}{D_{i,m}}$$ {#eq-zero-return}```{python}#| label: zero-return#| eval: false#| code-summary: "Compute monthly zero-return proportion"def compute_zero_return(daily_df):""" Compute the Lesmond et al. (1999) zero-return measure at the firm-month level. """ df = daily_df.copy() df['month'] = df['date'].dt.to_period('M') zero_ret = ( df.groupby(['ticker', 'month']) .agg( n_days=('daily_return', 'count'), n_zero_return=('zero_return', 'sum'), n_zero_volume=('zero_volume', 'sum') ) .reset_index() ) zero_ret['zero_return_pct'] = ( zero_ret['n_zero_return'] / zero_ret['n_days'] ) zero_ret['zero_volume_pct'] = ( zero_ret['n_zero_volume'] / zero_ret['n_days'] ) zero_ret['month_end'] = zero_ret['month'].dt.to_timestamp('M')return zero_ret[['ticker', 'month_end', 'zero_return_pct','zero_volume_pct', 'n_days']]zero_monthly = compute_zero_return(daily)print(f"Zero-return observations: {len(zero_monthly):,}")print(f"\nZero-return proportion distribution:")print(zero_monthly['zero_return_pct'].describe().round(3))```### Tỷ lệ Vòng quay Giao dịchTỷ lệ vòng quay cổ phiếu (tức là khối lượng theo ngày chia cho số cổ phiếu đang lưu hành) đo hoạt động giao dịch chứ không phải chi phí giao dịch. @datar1998liquidity dùng tỷ lệ vòng quay làm thước đo xấp xỉ thanh khoản và ghi nhận mối quan hệ mặt cắt ngang âm giữa tỷ lệ vòng quay và lợi nhuận kỳ vọng, phù hợp với giả thuyết phần bù thanh khoản.$$\text{Turn}_{i,m} = \frac{1}{D_{i,m}} \sum_{d=1}^{D_{i,m}} \frac{\text{Volume}_{i,d}}{\text{SharesOut}_{i,d}}$$ {#eq-turnover}```{python}#| label: turnover#| code-summary: "Compute monthly average turnover ratio"#| eval: falsedef compute_turnover(daily_df):"""Compute average daily turnover ratio at the firm-month level.""" df = daily_df.copy() df['month'] = df['date'].dt.to_period('M') turnover = ( df.groupby(['ticker', 'month']) .agg( turnover_mean=('turnover_ratio', 'mean'), turnover_sum=('turnover_ratio', 'sum'), volume_mean=('volume', 'mean'), dvol_mean=('turnover_value', 'mean') ) .reset_index() )# Log transform for cross-sectional normality turnover['log_turnover'] = np.log( turnover['turnover_mean'].clip(lower=1e-8) ) turnover['log_dvol'] = np.log( turnover['dvol_mean'].clip(lower=1) ) turnover['month_end'] = turnover['month'].dt.to_timestamp('M')return turnover[['ticker', 'month_end', 'turnover_mean','log_turnover', 'log_dvol']]turnover_monthly = compute_turnover(daily)print(f"Turnover observations: {len(turnover_monthly):,}")print(f"\nLog turnover distribution:")print(turnover_monthly['log_turnover'].describe().round(3))```### Ước lượng Chênh lệch Roll@roll1984simple suy ra một chênh lệch giá mua–bán ngầm định từ hiệp phương sai chuỗi của các thay đổi giá. Với các giả định rằng giá trị thực tuân theo bước ngẫu nhiên và giá quan sát được dao động giữa giá mua và giá bán:$$\text{Roll}_{i,m} = \begin{cases}2\sqrt{-\text{Cov}(\Delta P_{i,d}, \Delta P_{i,d-1})} & \text{if } \text{Cov} < 0 \\0 & \text{if } \text{Cov} \geq 0\end{cases}$$ {#eq-roll}trong đó $\Delta P_{i,d} = P_{i,d} - P_{i,d-1}$. Thước đo này mang tính trực giác: dao động giữa giá mua và giá bán tạo ra tương quan chuỗi âm trong giá giao dịch, và độ lớn của tương quan âm này phản ánh chênh lệch giá.```{python}#| label: roll-spread#| eval: false#| code-summary: "Compute monthly Roll (1984) effective spread estimator"def compute_roll_spread(daily_df, min_days=15):""" Compute the Roll (1984) effective spread from serial covariance of daily price changes. """ df = daily_df.copy() df['month'] = df['date'].dt.to_period('M') df['price_change'] = df.groupby('ticker')['adjusted_close'].diff() df['price_change_lag'] = df.groupby('ticker')['price_change'].shift(1)def roll_estimate(group):iflen(group) < min_days:return np.nan cov = group['price_change'].cov(group['price_change_lag'])if cov <0: spread =2* np.sqrt(-cov)# Normalize by average price avg_price = group['adjusted_close'].mean()return spread / avg_price if avg_price >0else np.nanelse:return0.0 roll = ( df.dropna(subset=['price_change', 'price_change_lag']) .groupby(['ticker', 'month']) .apply(roll_estimate) .reset_index(name='roll_spread') ) roll['month_end'] = roll['month'].dt.to_timestamp('M')return roll[['ticker', 'month_end', 'roll_spread']]roll_monthly = compute_roll_spread(daily)print(f"Roll spread observations: {len(roll_monthly):,}")print(f"\nRoll spread distribution:")print(roll_monthly['roll_spread'].describe().round(4))```### Chênh lệch Cao–Thấp Corwin-Schultz@corwin2012simple ước lượng chênh lệch giá hiệu dụng từ giá cao nhất và thấp nhất theo ngày. Điểm then chốt là giá cao nhất và thấp nhất theo ngày chứa thông tin về cả biến động lẫn chênh lệch giá—giá cao nhất thường là một lệnh mua và giá thấp nhất là một lệnh bán, nên biên độ cao–thấp phản ánh cả biến động thực lẫn chênh lệch giá mua–bán. Bằng cách so sánh biên độ cao–thấp một ngày và hai ngày, phương pháp tách hai thành phần này:$$\hat{S}_{i,m} = \frac{2(e^{\hat{\alpha}} - 1)}{1 + e^{\hat{\alpha}}}$$ {#eq-corwin-schultz}trong đó:$$\hat{\alpha} = \frac{\sqrt{2\hat{\beta}} - \sqrt{\hat{\beta}}}{3 - 2\sqrt{2}} - \sqrt{\frac{\hat{\gamma}}{3 - 2\sqrt{2}}}$$ {#eq-cs-alpha}với $\hat{\beta}$ và $\hat{\gamma}$ được tính từ các tỷ số cao–thấp lấy logarit trong một ngày và hai ngày.```{python}#| label: corwin-schultz#| eval: false#| code-summary: "Compute the Corwin-Schultz (2012) high-low spread estimator"def compute_corwin_schultz(daily_df, min_days=15):""" Compute the Corwin and Schultz (2012) bid-ask spread estimator from daily high and low prices. """ df = daily_df.copy() df['month'] = df['date'].dt.to_period('M')# Log high-low ratio df['log_hl'] = np.log(df['high'] / df['low']) df['log_hl_sq'] = df['log_hl'] **2# Two-day high and low df['high_2d'] = df.groupby('ticker')['high'].transform(lambda x: x.rolling(2).max() ) df['low_2d'] = df.groupby('ticker')['low'].transform(lambda x: x.rolling(2).min() ) df['log_hl_2d'] = np.log(df['high_2d'] / df['low_2d']) df['log_hl_2d_sq'] = df['log_hl_2d'] **2def cs_estimate(group):iflen(group) < min_days:return np.nan beta = group['log_hl_sq'].mean() + group['log_hl_sq'].shift(1).mean() beta = group[['log_hl_sq']].rolling(2).sum().mean().values[0] gamma = group['log_hl_2d_sq'].mean() k = np.sqrt(2) -1 denom =3-2* np.sqrt(2) term1 = np.sqrt(max(beta, 0)) / denomif beta >0: alpha_est = (np.sqrt(2* beta) - np.sqrt(beta)) / denom alpha_est -= np.sqrt(max(gamma / denom, 0))else: alpha_est =0# Spread estimateif alpha_est >0: spread =2* (np.exp(alpha_est) -1) / (1+ np.exp(alpha_est))else: spread =0returnmin(spread, 0.20) # Cap at 20% (sanity check) cs = ( df.dropna(subset=['log_hl', 'log_hl_2d']) .groupby(['ticker', 'month']) .apply(cs_estimate) .reset_index(name='cs_spread') ) cs['month_end'] = cs['month'].dt.to_timestamp('M')return cs[['ticker', 'month_end', 'cs_spread']]cs_monthly = compute_corwin_schultz(daily)print(f"Corwin-Schultz observations: {len(cs_monthly):,}")print(f"\nCS spread distribution:")print(cs_monthly['cs_spread'].describe().round(4))```### Chênh lệch Giá mua–Giá bán ChàoKhi có sẵn các báo giá mua và bán, chênh lệch theo phần trăm của giá chào cung cấp một thước đo trực tiếp về độ chặt:$$\text{PQSPR}_{i,m} = \frac{1}{D_{i,m}} \sum_{d=1}^{D_{i,m}} \frac{\text{Ask}_{i,d} - \text{Bid}_{i,d}}{(\text{Ask}_{i,d} + \text{Bid}_{i,d})/2}$$ {#eq-quoted-spread}```{python}#| label: quoted-spread#| eval: false#| code-summary: "Compute monthly average quoted percentage spread"def compute_quoted_spread(daily_df):"""Compute average quoted percentage spread at the firm-month level.""" df = daily_df[ (daily_df['bid'] >0) & (daily_df['ask'] >0) & (daily_df['ask'] >= daily_df['bid']) ].copy() df['month'] = df['date'].dt.to_period('M') df['pqspr'] = ( (df['ask'] - df['bid']) / ((df['ask'] + df['bid']) /2) )# Winsorize extreme values df['pqspr'] = df['pqspr'].clip(upper=df['pqspr'].quantile(0.999)) spread = ( df.groupby(['ticker', 'month']) .agg( quoted_spread=('pqspr', 'mean'), n_quotes=('pqspr', 'count') ) .reset_index() ) spread['month_end'] = spread['month'].dt.to_timestamp('M')return spread[['ticker', 'month_end', 'quoted_spread', 'n_quotes']]quoted_monthly = compute_quoted_spread(daily)print(f"Quoted spread observations: {len(quoted_monthly):,}")print(f"\nQuoted spread distribution:")print(quoted_monthly['quoted_spread'].describe().round(4))```### Lambda của Kyle (Hồi quy Tác động Giá)Chúng ta ước lượng lambda của Kyle (tức là tác động giá trên mỗi đơn vị dòng lệnh có dấu) bằng một hồi quy theo ngày:$$R_{i,d} = \alpha_i + \lambda_i \cdot \text{Sign}(R_{i,d}) \cdot \sqrt{\text{Volume}_{i,d}} + \varepsilon_{i,d}$$ {#eq-kyle-lambda}Đây là một sự điều chỉnh của thước đo chi phí hiệu dụng @hasbrouck2009trading. Hệ số $\lambda_i$ đo giá dịch chuyển bao nhiêu trên mỗi đơn vị khối lượng (không dấu, lấy căn bậc hai).```{python}#| label: kyle-lambda#| eval: false#| code-summary: "Estimate Kyle's lambda from daily price-volume regressions"def compute_kyle_lambda(daily_df, min_days=15):""" Estimate Kyle's lambda (price impact per unit order flow) from daily return-on-signed-volume regressions. """ df = daily_df[daily_df['volume'] >0].copy() df['month'] = df['date'].dt.to_period('M')# Signed square-root volume (sign inferred from return) df['signed_sqrt_vol'] = ( np.sign(df['daily_return']) * np.sqrt(df['volume']) )def estimate_lambda(group):iflen(group) < min_days:return np.nan y = group['daily_return'].values x = group['signed_sqrt_vol'].values x = sm.add_constant(x)try: model = sm.OLS(y, x).fit() lam = model.params[1]returnmax(lam, 0) # Lambda should be non-negativeexceptException:return np.nan kyle = ( df.groupby(['ticker', 'month']) .apply(estimate_lambda) .reset_index(name='kyle_lambda') ) kyle['log_kyle'] = np.log(kyle['kyle_lambda'].clip(lower=1e-10)) kyle['month_end'] = kyle['month'].dt.to_timestamp('M')return kyle[['ticker', 'month_end', 'kyle_lambda', 'log_kyle']]kyle_monthly = compute_kyle_lambda(daily)print(f"Kyle lambda observations: {len(kyle_monthly):,}")print(f"\nLog Kyle lambda distribution:")print(kyle_monthly['log_kyle'].describe().round(3))```## Phi thanh khoản Thực hiện: Một Cải tiến Tần suất Cao cho Thước đo Amihud {#sec-liquidity-realized}### Động cơTrong bảy thước đo xấp xỉ được xây dựng ở trên, tỷ số @amihud2002illiquidity trong @eq-amihud là thước đo mà cuốn sách này dựa vào nhiều nhất: nó là biến phân loại trong các phân loại danh mục và hồi quy Fama-MacBeth ở phần sau, là đầu vào cho các hồi quy về tính đồng biến, và là cơ sở của nhân tố thanh khoản có thể giao dịch được xây dựng ở cuối chương. Đây cũng chính là thước đo mà @fong2017best khuyến nghị riêng cho các thị trường mới nổi. Vì vậy, rất đáng để đặt câu hỏi: nó đo lường chính xác đến mức nào điều mà nó tuyên bố đo lường?Câu trả lời là: kém chính xác hơn nhiều so với mức độ phổ biến của nó gợi ý, và vì một lý do không hề liên quan đến mẫu số khối lượng. Tử số $|R_{i,d}|$ là một *quan sát đơn lẻ* được dùng để tóm tắt toàn bộ biến động giá trong ngày. Xét như một thống kê, nó gần như không chệch đối với độ biến động của ngày nhưng lại nhiễu một cách phi thường — chúng ta sẽ định lượng điều này dưới đây ở mức khoảng 76% độ lệch chuẩn tương đối *mỗi ngày*. Mỗi đơn vị nhiễu đó lan truyền vào các phân loại danh mục dưới dạng suy giảm do sai số trong biến (errors-in-variables) cổ điển, làm phẳng các phần bù thanh khoản đo được về phía không.@lacava2026realized giải quyết đúng vấn đề này. Họ định nghĩa *phi thanh khoản thực hiện* là tỷ số giữa độ biến động thực hiện và khối lượng giao dịch, và giới thiệu **thước đo Amihud thực hiện**, thay giá trị tuyệt đối của lợi nhuận theo ngày đơn lẻ bằng một ước lượng tần suất cao của cùng đại lượng đó. Họ chỉ ra rằng nó nắm bắt một cách vững chắc độ nhạy giá tích lũy trong phiên đối với giao dịch, ngay cả khi có độ biến động ngẫu nhiên, nhiễu vi cấu trúc và các bước nhảy thông tin; ghi nhận hiện tượng phân cụm không đồng nhất và hiệu ứng đòn bẩy trong chuỗi kết quả; và thấy rằng nó dự báo được lợi nhuận ngắn hạn. Phần này phát triển ước lượng theo ký hiệu của cuốn sách, dẫn giải vì sao mức cải thiện độ chính xác lại lớn đến vậy, và trình bày cái giá phải trả để triển khai nó trên HOSE.### Thiết lập và Ký hiệuLet $p_{i,t}$ denote the efficient log price of stock $i$ at time $t$, evolving over trading day $d$ as an Itô semimartingale:$$dp_{i,t} = \mu_{i,t}\,dt + \sigma_{i,t}\,dW_{i,t} + dJ_{i,t}$$ {#eq-semimartingale}where $\sigma_{i,t}$ is a càdlàg stochastic volatility process, $W_{i,t}$ a standard Brownian motion, and $J_{i,t}$ a finite-activity jump process capturing discrete information arrivals. The object that summarizes the day's price variation is the **integrated variance**$$\text{IV}_{i,d} = \int_{d-1}^{d} \sigma_{i,s}^2\,ds$$ {#eq-integrated-variance}Chia ngày giao dịch liên tục thành $M$ khoảng và ký hiệu log lợi nhuận trong phiên là $r_{i,d,j}$, $j = 1,\dots,M$, sao cho lợi nhuận theo ngày thỏa mãn $R_{i,d} = \sum_{j=1}^{M} r_{i,d,j}$. Như trước, $\text{DVOL}_{i,d}$ là khối lượng giao dịch tính bằng VND trong ngày $d$ và $D_{i,m}$ là số ngày có khối lượng dương trong tháng $m$.**Phương sai thực hiện** và **biến phân song lũy thừa (bipower variation)** là$$\text{RV}_{i,d} = \sum_{j=1}^{M} r_{i,d,j}^2,\qquad\text{BV}_{i,d} = \frac{\pi}{2}\sum_{j=2}^{M} \left| r_{i,d,j} \right|\left| r_{i,d,j-1} \right|$$ {#eq-rv-bv}Cả hai đều vững đối với $\text{IV}_{i,d}$ khi $M \to \infty$ trong trường hợp không có bước nhảy [@barndorff2002econometric; @andersen2003modeling]; $\text{BV}_{i,d}$ vẫn vững *ngay cả khi* có bước nhảy, và đó chính là điều khiến nó hữu ích ở phần dưới [@barndorff2004power].### Các Giả định**(A1) Giá hiệu quả là nửa martingale.** Log giá hiệu quả tuân theo @eq-semimartingale với $\int_{d-1}^{d}\sigma_{i,s}^4\,ds < \infty$.*Diễn giải.* Giá được dẫn dắt bởi một quá trình khuếch tán với độ biến động có thể ngẫu nhiên và dai dẳng, cộng với các bước nhảy không thường xuyên. Đây là giả định yếu và nhìn chung không gây tranh cãi. Nó sẽ bị vi phạm bởi một quá trình giá có bước nhảy hoạt động vô hạn với cường độ đủ lớn, hoặc bởi một thị trường mỏng đến mức không một xấp xỉ thời gian liên tục nào còn có ý nghĩa — trường hợp sau là mối lo thực tế đối với các cổ phiếu vốn hóa nhỏ trên HOSE.**(A2) Nhiễu vi cấu trúc không đáng kể tại tần suất lấy mẫu.** Giá quan sát được thỏa mãn $\tilde{p}_{i,t} = p_{i,t} + u_{i,t}$, và khoảng lấy mẫu đủ thô để $2M\,\mathbb{V}[u_{i,t}]$ là nhỏ so với $\text{IV}_{i,d}$.*Diễn giải.* Dao động giữa giá mua và giá bán, tính rời rạc của bước giá, và giá cũ không được cập nhật đều bơm nhiễu vào giá quan sát được. Vì $\text{RV}$ cộng dồn lợi nhuận *bình phương*, nhiễu này tích lũy theo $M$ thay vì triệt tiêu — lấy mẫu quá dày làm ước lượng tệ đi chứ không tốt lên. Trên HOSE, bước giá thô so với mặt bằng giá khiến ràng buộc này phát huy tác dụng sớm hơn so với dữ liệu Mỹ. Đây là giả định dễ bị vi phạm nhất trong thực tế, và chúng ta sẽ chỉ ra điều gì xảy ra khi nó bị vi phạm.**(A3) Không có bước nhảy, đối với thước đo Amihud thực hiện dạng thuần.** $J_{i,t} \equiv 0$ trong ngày $d$.*Diễn giải.* Một bất ngờ lợi nhuận hay một phiên chạm biên độ là một thay đổi giá lớn *không phải* do tác động giá của khối lượng giao dịch, nhưng $\text{RV}$ vẫn tính nó như thể đúng là như vậy. Với tần suất cổ phiếu Việt Nam chạm biên độ $\pm7\%$ của HOSE (xem thảo luận về biên độ giá trong chương vi cấu trúc thị trường), (A3) không hề vô hại ở đây. Nới lỏng giả định này chính là lý do chúng ta duy trì song song $\text{BV}_{i,d}$.**(A4) Khối lượng đồng thời.** $\text{DVOL}_{i,d}$ được đo trên cùng cửa sổ thời gian với các lợi nhuận trong phiên.*Diễn giải.* Nếu lợi nhuận được tính trên các phiên khớp lệnh liên tục nhưng khối lượng lại bao gồm cả các giao dịch thỏa thuận sau giờ đóng cửa, thì tử số và mẫu số đang nói về hai hoạt động giao dịch khác nhau. Trên HOSE, phiên thỏa thuận 14:45–15:00 khiến đây là một vấn đề hạch toán thực sự chứ không phải chi tiết kỹ thuật.### Ước lượngThước đo Amihud thực hiện thay $|R_{i,d}|$ bằng một ước lượng tần suất cao đã được quy đổi tỷ lệ cho chính biến động giá của ngày đó:$$\text{RILLIQ}_{i,m} = \frac{1}{D_{i,m}}\sum_{d=1}^{D_{i,m}} \sqrt{\frac{2}{\pi}}\,\frac{\sqrt{\text{RV}_{i,d}}}{\text{DVOL}_{i,d}}$$ {#eq-realized-amihud}và phiên bản vững với bước nhảy thay $\text{RV}_{i,d}$ bằng $\text{BV}_{i,d}$:$$\text{RILLIQ}^{\text{BV}}_{i,m} = \frac{1}{D_{i,m}}\sum_{d=1}^{D_{i,m}} \sqrt{\frac{2}{\pi}}\,\frac{\sqrt{\text{BV}_{i,d}}}{\text{DVOL}_{i,d}}$$ {#eq-realized-amihud-bv}Hằng số $\sqrt{2/\pi} \approx 0.7979$ không phải để cho đẹp: nó đưa @eq-realized-amihud về đúng cùng thang đo với @eq-amihud, nhờ đó hai thước đo có thể so sánh trực tiếp về mặt số học và phiên bản thực hiện có thể thay thế trực tiếp ở mọi nơi cuốn sách đang dùng $\text{ILLIQ}$.### Dẫn giải**Bước 1: $|R_{i,d}|$ thực chất ước lượng cái gì.** Điều kiện theo quỹ đạo độ biến động và bỏ qua xu hướng cùng bước nhảy, ta có $R_{i,d} \mid \text{IV}_{i,d} \sim N(0, \text{IV}_{i,d})$. Đối với một biến bán chuẩn (half-normal),$$\mathbb{E}\!\left[|R_{i,d}| \,\middle|\, \text{IV}_{i,d}\right] = \sqrt{\tfrac{2}{\pi}}\sqrt{\text{IV}_{i,d}},\qquad\mathbb{V}\!\left[|R_{i,d}| \,\middle|\, \text{IV}_{i,d}\right] = \left(1 - \tfrac{2}{\pi}\right)\text{IV}_{i,d}$$ {#eq-halfnormal}Vậy $|R_{i,d}|$ là ước lượng không chệch cho $\sqrt{2/\pi}\sqrt{\text{IV}_{i,d}}$ — tử số của Amihud nhắm đúng mục tiêu. Tuy nhiên, độ lệch chuẩn *tương đối* của nó là$$\frac{\text{sd}\!\left(|R_{i,d}|\right)}{\mathbb{E}\!\left[|R_{i,d}|\right]}= \sqrt{\frac{1 - 2/\pi}{2/\pi}}= \sqrt{\frac{\pi}{2} - 1} \approx 0.7555$$ {#eq-abs-relsd}Tử số Amihud của một ngày đơn lẻ mang theo khoảng **76% sai số đo lường tương đối**. Đây là tính chất của phân phối bán chuẩn, không phải của một thị trường cụ thể nào — nó không thể giảm được nếu chỉ có một quan sát mỗi ngày.**Bước 2: $\sqrt{\text{RV}_{i,d}}$ ước lượng cái gì.** Dưới (A1)–(A3), phương sai thực hiện tuân theo định lý giới hạn trung tâm của @barndorff2002econometric,$$\sqrt{M}\left(\text{RV}_{i,d} - \text{IV}_{i,d}\right) \;\xrightarrow{d}\; N\!\left(0,\; 2\,\text{IQ}_{i,d}\right),\qquad \text{IQ}_{i,d} = \int_{d-1}^{d}\sigma_{i,s}^4\,ds$$ {#eq-rv-clt}Áp dụng phương pháp delta cho $g(x) = \sqrt{x}$, với $g'(\text{IV}) = 1/(2\sqrt{\text{IV}})$,$$\sqrt{M}\left(\sqrt{\text{RV}_{i,d}} - \sqrt{\text{IV}_{i,d}}\right) \;\xrightarrow{d}\; N\!\left(0,\; \frac{\text{IQ}_{i,d}}{2\,\text{IV}_{i,d}}\right)$$ {#eq-sqrt-rv-clt}Xét trường hợp riêng độ biến động không đổi cục bộ trong ngày ($\text{IV} = \sigma^2$, $\text{IQ} = \sigma^4$), ta được độ lệch chuẩn tương đối$$\frac{\text{sd}\!\left(\sqrt{\text{RV}_{i,d}}\right)}{\mathbb{E}\!\left[\sqrt{\text{RV}_{i,d}}\right]} \approx \frac{1}{\sqrt{2M}}$$ {#eq-rv-relsd}**Bước 3: So sánh.** Chia @eq-abs-relsd cho @eq-rv-relsd, tử số thực hiện chính xác hơn theo hệ số $\sqrt{2M\left(\pi/2 - 1\right)}$. Các phiên khớp lệnh liên tục của HOSE diễn ra từ 09:15–11:30 và 13:00–14:30, tức **225 phút**, nên lấy mẫu năm phút cho $M = 45$:| Tần suất lấy mẫu | $M$ | Độ lệch chuẩn tương đối của tử số | Cải thiện so với $\lvert R_d\rvert$ ||:---|---:|---:|---:|| Đóng cửa sang đóng cửa theo ngày ($\lvert R_d \rvert$) | 1 | 0.7555 | — || 15 phút | 15 | 0.1826 | 4.1$\times$ || 5 phút | 45 | 0.1054 | 7.2$\times$ || 1 phút | 225 | 0.0471 | 16.0$\times$ |: Sai số đo lường giải tích của tử số Amihud theo tần suất lấy mẫu, từ @eq-abs-relsd và @eq-rv-relsd. Dòng 1 phút *không* phải là khuyến nghị — xem giả định (A2) và phần mô phỏng bên dưới. {#tbl-realized-precision}Ở tần suất lấy mẫu năm phút, *phương sai* nhiễu trong tử số giảm khoảng 51 lần.**Bước 4: Vì sao điều này quan trọng đối với các kết quả của cuốn sách.** Viết biến phân loại theo tháng dưới dạng giá trị thật cộng sai số, $\widehat{x}_{i,m} = x_{i,m} + \eta_{i,m}$. Trong các phân loại thanh khoản đơn biến và các hồi quy Fama-MacBeth ở phần sau của chương, sai số trong biến cổ điển làm suy giảm hệ số góc ước lượng theo tỷ số tin cậy$$\text{plim}\;\frac{\widehat{\gamma}}{\gamma} = \frac{\mathbb{V}[x]}{\mathbb{V}[x] + \mathbb{V}[\eta]}$$ {#eq-attenuation}Giảm $\mathbb{V}[\eta]$ khoảng 51$\times$ sẽ đẩy tỷ số tin cậy tiến về một, điều này *làm tăng* phần bù thanh khoản đo được cùng thống kê $t$ của nó. Hệ quả thực tiễn khá khó chịu và đáng nói thẳng: phần bù thanh khoản ước lượng bằng tỷ số Amihud theo ngày là một **cận dưới**. Một phần bù yếu hoặc không có ý nghĩa thống kê trong dữ liệu Việt Nam có thể phản ánh một biến phân loại nhiều nhiễu chứ không phải sự vắng mặt của phần bù.### Trực giácTỷ số Amihud đặt câu hỏi "giá đã dịch chuyển bao nhiêu trên mỗi đồng VND được giao dịch?" và trả lời bằng đúng một con số mà ngày hôm đó tình cờ mang lại — mức dịch chuyển từ mở cửa đến đóng cửa. Nhưng một ngày mà giá đi lên 4%, quay đầu giảm 5%, rồi đóng cửa thấp hơn 1% là một ngày có độ nhạy giá đáng kể đối với giao dịch, trong khi $|R_d| = 1\%$ hầu như không ghi nhận được điều đó. Độ biến động thực hiện đo *cả quỹ đạo* thay vì chỉ hai đầu mút, nhờ vậy nắm bắt được đúng độ nhạy giá tích lũy trong phiên mà Amihud đang cố gắng xấp xỉ. Lấy trung bình $M$ bình phương lợi nhuận trong phiên thay vì lấy một giá trị tuyệt đối của lợi nhuận theo ngày, xét về mặt thống kê, chính là khác biệt giữa một quan sát và $M$ quan sát.Điểm vướng là quỹ đạo chỉ quan sát được thông qua lớp vi cấu trúc. Đẩy $M$ lên đủ cao thì ta không còn đo quỹ đạo giá hiệu quả nữa mà bắt đầu đo dao động giữa giá mua và giá bán. Toàn bộ nghệ thuật của ước lượng này nằm ở việc chọn $M$ đủ lớn để có thêm độ chính xác và đủ nhỏ để giữ (A2) còn hiệu lực.### Thiết kế Mô phỏng {#sec-liquidity-realized-sim}Thí nghiệm sau đây đối chiếu @eq-realized-amihud với @eq-amihud trên cùng một quá trình sinh dữ liệu (DGP), rồi lần lượt phá vỡ (A2) và (A3) để làm lộ các kiểu thất bại. Đại lượng mục tiêu là mức phi thanh khoản thật theo ngày đã quy đổi $\sqrt{2/\pi}\sqrt{\text{IV}_{i,d}}/\text{DVOL}_{i,d}$, nhờ đó độ chệch và RMSE được đo so với một đại lượng đã biết.```{python}#| label: realized-amihud-simulation#| code-summary: "Monte Carlo: realized vs. classic Amihud under noise and jumps"rng = np.random.default_rng(20260812)M_HOSE =45# 225 continuous minutes on HOSE / 5-minute samplingdef simulate_days(n_days, M, noise_sd=0.0, jump_prob=0.0, jump_sd=0.0):""" Simulate intraday paths with stochastic volatility, optional iid microstructure noise in observed log prices, and optional information jumps. Returns the ingredients of both illiquidity estimators. """# Integrated variance: gamma-distributed, mean daily vol about 2.5% IV = rng.gamma(shape=4.0, scale=(0.025**2) /4.0, size=n_days)# Efficient intraday returns eff = rng.normal(0.0, 1.0, size=(n_days, M)) * np.sqrt(IV / M)[:, None]# Information jumps (violates A3)if jump_prob >0: hit = rng.binomial(1, jump_prob / M, size=(n_days, M)) eff = eff + hit * rng.normal(0.0, jump_sd, size=(n_days, M))# Efficient log-price path, then contaminate with noise (violates A2) P = np.cumsum(eff, axis=1) P_obs = P + rng.normal(0.0, noise_sd, size=(n_days, M)) if noise_sd >0else P# Observed intraday returns and the daily open-to-close return r = np.diff(P_obs, axis=1, prepend=0.0) R_d = P_obs[:, -1] RV = np.sum(r **2, axis=1) BV = (np.pi /2) * np.sum(np.abs(r[:, 1:]) * np.abs(r[:, :-1]), axis=1)# VND turnover, lognormal around 5 billion VND DVOL = np.exp(rng.normal(np.log(5e9), 1.0, size=n_days))returndict(IV=IV, R_d=R_d, RV=RV, BV=BV, DVOL=DVOL)def score(sim, label):"""Bias, RMSE (both relative to mean truth) and rank correlation.""" k = np.sqrt(2.0/ np.pi) truth = k * np.sqrt(sim['IV']) / sim['DVOL'] estimators = {'Amihud |R|/DVOL': np.abs(sim['R_d']) / sim['DVOL'],'Realized Amihud': k * np.sqrt(sim['RV']) / sim['DVOL'],'Bipower Amihud': k * np.sqrt(np.maximum(sim['BV'], 0)) / sim['DVOL'], } rows = []for name, est in estimators.items(): rows.append({'estimator': name,'rel_bias': (est - truth).mean() / truth.mean(),'rel_rmse': np.sqrt(((est - truth) **2).mean()) / truth.mean(),'rank_corr': stats.spearmanr(est, truth).statistic, }) out = pd.DataFrame(rows).set_index('estimator')print(f"\n--- {label}")print(out.round(4))return out# Baseline: assumptions holdscore(simulate_days(30_000, M_HOSE), "Baseline (A1-A4 hold), M=45")# Failure case A: microstructure noise, and what oversampling does to itscore(simulate_days(30_000, M_HOSE, noise_sd=0.0015),"Noise sd=0.15%, 5-minute sampling (M=45)")score(simulate_days(30_000, 225, noise_sd=0.0015),"Noise sd=0.15%, 1-minute sampling (M=225) -- oversampled")# Failure case B: information jumpsscore(simulate_days(30_000, M_HOSE, jump_prob=0.2, jump_sd=0.03),"Information jumps (~1 per 5 days, 3% size)")```Ba kết quả xuất hiện, và mỗi kết quả có một đối ứng trực tiếp trong phần giải tích ở trên.Thứ nhất, dưới (A1)–(A4), cả hai ước lượng đều gần như không chệch — tử số Amihud theo ngày, như @eq-halfnormal đã nói, không chệch đối với mục tiêu đúng — nhưng độ chính xác của chúng khác nhau một trời một vực. RMSE tương đối giảm từ khoảng 1.28 xuống khoảng 0.18, một tỷ lệ khoảng 7.2, đúng bằng hệ số cải thiện mà @tbl-realized-precision dự báo bằng giải tích cho $M = 45$. Tương quan hạng với mức phi thanh khoản thật tăng từ khoảng 0.70 lên khoảng 0.99. Tương quan hạng là con số quan trọng nhất đối với cuốn sách này, vì nó quyết định việc một phân loại danh mục có xếp cổ phiếu vào đúng nhóm ngũ phân vị hay không: ở mức 0.70, một tỷ lệ đáng kể các cổ phiếu bị xếp sai chỉ vì biến phân loại.Thứ hai, việc phá vỡ (A2) làm thước đo Amihud thực hiện chệch *lên trên*, và lấy mẫu ở một phút thay vì năm phút làm độ chệch tệ hơn khoảng bốn lần chứ không tốt hơn — khoảng +16% tại $M = 45$ so với khoảng +66% tại $M = 225$. Đây là bài học không hiển nhiên của toàn bộ phần này: khi có nhiễu, nhiều dữ liệu hơn được lấy mẫu dày hơn lại gây hại thực sự. Cũng lưu ý rằng thước đo Amihud theo ngày thuần túy gần như miễn nhiễm với vấn đề cụ thể này, vì một mức lợi nhuận từ mở cửa đến đóng cửa chỉ chứa một liều nhiễu. Độ vững trước nhiễu vi cấu trúc là chiều duy nhất mà thước đo cổ điển thắng.Thứ ba, khi có bước nhảy, thước đo Amihud thực hiện dạng thuần kế thừa một độ chệch lên trên rõ rệt (khoảng +9%) trong khi phiên bản song lũy thừa trong @eq-realized-amihud-bv vẫn gần như không chệch (khoảng +1%) và còn cho RMSE thấp nhất trong ba thước đo. Với việc cổ phiếu Việt Nam thường xuyên chạm biên độ giá, đây là cấu hình nên ưu tiên cho thị trường Việt Nam.### Tái lập trên Dữ liệu Việt Nam@lacava2026realized sử dụng dữ liệu tần suất cao của Mỹ, nên không thể tái lập trực tiếp bằng bảng dữ liệu theo ngày đã lắp ráp ở phần trước của chương. Quan điểm trung thực cho cuốn sách này là thước đo Amihud thực hiện hiện **nằm ngoài tầm với đối với phần lớn mặt cắt ngang HOSE**, vì một lý do đáng nói rõ: độ chính xác của ước lượng được dẫn dắt bởi $M$, tức số lượng lợi nhuận trong phiên *thực sự khác không*. Một cổ phiếu chỉ khớp lệnh vài lần mỗi phiên không có 45 lợi nhuận năm phút mang thông tin — nó có một lưới năm phút phần lớn được lấp đầy bằng giá cũ được điền tiếp (forward-fill), với các lợi nhuận bằng không mang tính cơ học không đóng góp gì cho $\text{RV}$. Chương vi cấu trúc thị trường mô phỏng trực tiếp điều này (@sec-micro-realized-illiq) và đi đến một kết luận rõ ràng hơn người ta có thể đoán. Mức tăng độ chính xác được quyết định bởi số khoảng mà giá thực sự thay đổi chứ không phải số điểm trên lưới, nên nó suy giảm từ mức 7.2$\times$ theo lý thuyết xuống dưới 2$\times$ đối với một cổ phiếu giao dịch năm lần mỗi ngày. Độ chệch xuống đi kèm trong $\text{RV}$ là có thật nhưng chỉ ở bậc hai so với điều đó, vì việc thô hóa lấy mẫu của một martingale không làm chệch phương sai thực hiện về kỳ vọng; điều mà tình trạng giá cũ loại bỏ là biến động trước giao dịch đầu tiên và sau giao dịch cuối cùng. Đây chính là vấn đề giao dịch mỏng đã thúc đẩy thước đo lợi nhuận bằng không trong @eq-zero-return, và nó tác động lên thước đo Amihud thực hiện mạnh hơn so với phiên bản theo ngày.Hệ quả thực tiễn là ước lượng này là một cải tiến dành cho nhóm thanh khoản cao của thị trường — đại khái là các cổ phiếu thuộc VN30 và các mã vốn hóa trung bình lớn hơn — chứ không phải vật thay thế cho @eq-amihud trên toàn bộ bảng dữ liệu. Việc xây dựng nó cho Việt Nam đòi hỏi dữ liệu khớp lệnh (tick) trong phiên từ HOSE, vốn không nằm trong quy trình dữ liệu theo ngày được sử dụng ở những phần khác của chương.### Hướng dẫn Thực hành1. **Lấy mẫu ở năm phút, không dày hơn.** Với 225 phút khớp lệnh liên tục của HOSE, điều này cho $M = 45$ và mức tăng độ chính xác 7.2$\times$. Nên cưỡng lại cám dỗ hạ xuống một phút: @tbl-realized-precision cho thấy mức cải thiện *lý thuyết*, nhưng (A2) sẽ đổ vỡ từ lâu trước khi bạn thu được nó. Năm phút không phải là một quy ước tùy tiện — đó là tần suất mà sự đánh đổi chệch–phương sai giữa sai số lấy mẫu và sự tích lũy nhiễu thường được chọn [@andersen2003modeling], các tính toán lấy mẫu tối ưu của @aitsahalia2005often và @bandi2008microstructure cho kết quả quanh vùng đó đối với cổ phiếu Mỹ có thanh khoản cao, và @liu2015does thấy trên nhiều loại tài sản rằng rất ít thước đo thực hiện nào luôn vượt được $\text{RV}$ năm phút. Vì HOSE mỏng hơn và bước giá thô hơn các thị trường mà những nghiên cứu đó khảo sát, khoảng tối ưu ở đây nếu có khác thì là *dài hơn* năm phút, không bao giờ ngắn hơn.2. **Sàng lọc theo tần suất giao dịch trước khi tính.** Yêu cầu một số lượng tối thiểu các khoảng có giao dịch thực sự xảy ra — chứ không đơn thuần là có giá — và quay về @eq-amihud khi dưới ngưỡng đó. Hãy báo cáo những cổ phiếu nào phải dùng phương án dự phòng.3. **Xử lý giờ nghỉ trưa và các phiên khớp lệnh định kỳ một cách tường minh.** Khoảng 11:30–13:00 không phải là một khoảng tính lợi nhuận, và giá ATO cùng ATC đến từ khớp lệnh định kỳ chứ không phải khớp lệnh liên tục. Hãy tính $\text{RV}$ trong từng phiên liên tục rồi cộng lại, thay vì lấy sai phân xuyên qua giờ nghỉ hoặc xuyên qua các phiên định kỳ.4. **Loại giao dịch thỏa thuận khỏi mẫu số**, theo (A4), để phiên thỏa thuận 14:45–15:00 không thổi phồng $\text{DVOL}$ so với một tử số chỉ được đo trên phần khớp lệnh liên tục.5. **Ưu tiên phiên bản song lũy thừa trong các ngày chạm biên độ.** Biên độ giá theo ngày của Việt Nam khiến các bước dịch chuyển rời rạc lớn trở nên phổ biến, và @eq-realized-amihud-bv là phiên bản trụ được trước chúng.6. **Chỉ chuyển sang ước lượng vững với nhiễu khi buộc phải lấy mẫu dày.** Các ước lượng trong @eq-rv-bv là lựa chọn mặc định đúng ở năm phút. Nếu ứng dụng thực sự đòi hỏi lưới dày hơn, tài liệu đưa ra ba họ phương pháp, xếp theo chi phí triển khai tăng dần: ước lượng hai thang thời gian của @zhang2005tale, lấy mẫu con ở hai tần suất và ngoại suy để loại bỏ nhiễu; cách tiếp cận tiền lấy trung bình (pre-averaging) của @jacod2009microstructure, lấy trung bình lợi nhuận trong từng khối trước khi bình phương; và các hạt nhân thực hiện (realized kernels) của @barndorff2008designing, gán trọng số cho các tự hiệp phương sai của lợi nhuận trong phiên. @hansen2006realized là tài liệu tham chiếu chuẩn để chẩn đoán mức độ nhiễu hiện diện ngay từ đầu, và biểu đồ chữ ký (signature plot) của họ — $\text{RV}$ theo tần suất lấy mẫu — nên được lập cho mặt cắt ngang Việt Nam trước khi thử bất kỳ điều nào ở trên.7. **Cân nhắc cắt cụt theo láng giềng gần nhất như một phương án thay cho song lũy thừa.** @andersen2012jump đề xuất các ước lượng MinRV và MedRV, vững với bước nhảy như $\text{BV}$ nhưng hoạt động tốt hơn khi các bước nhảy đến ở các khoảng liền kề — một kịch bản thực tế vào ngày chạm biên độ, khi biên độ bị tiếp cận qua nhiều khoảng liên tiếp chứ không phải trong một lần dịch chuyển.8. **Phần mềm.** Không có gói đơn lẻ nào làm trọn vẹn việc này; $\text{RV}$ và $\text{BV}$ chỉ là vài dòng `pandas`, như trong phần mô phỏng ở trên. Các hàm `realized` trong hệ sinh thái `arch` bao phủ các ước lượng vững với nhiễu cho độc giả không muốn tự triển khai.### Những Sai lầm Thường gặp- **Lấy mẫu quá dày.** Đây là lỗi phổ biến nhất. Nhiều quan sát hơn trông có vẻ là nhiều thông tin hơn, nhưng @eq-rv-clt giả định rằng thứ đang được lấy mẫu là giá hiệu quả. Quá vài phút trên HOSE thì điều đó không còn đúng nữa.- **Coi lợi nhuận bằng không trong phiên là thông tin.** Trên lưới năm phút, một mức giá cũ tạo ra lợi nhuận bằng không mà $\text{RV}$ đọc thành độ biến động bằng không. Nếu không sàng lọc, điều này khiến những cổ phiếu kém thanh khoản nhất trông thanh khoản nhất — dấu của sai số ngược lại với phỏng đoán trực giác.- **Nhầm lẫn thang đo.** Bỏ đi hằng số $\sqrt{2/\pi}$ sẽ làm @eq-realized-amihud lớn hơn @eq-amihud khoảng 25%, và điều đó âm thầm phá vỡ mọi so sánh với các mức Amihud đã công bố trước đây hoặc với các phân loại ở những phần khác của chương.- **Cho rằng mức cải thiện chuyển thành phần bù theo tỷ lệ một-đổi-một.** @eq-attenuation nói rằng một biến phân loại sạch hơn sẽ nâng phần bù *đo được* tiến về phần bù thật. Nó không tạo ra một phần bù vốn không tồn tại, và nó cũng không khắc phục các vấn đề thiên kiến sống sót và hủy niêm yết vốn quyết định những quan sát công ty–tháng nào được đưa vào bảng dữ liệu ngay từ đầu.- **Quên rằng mẫu số không thay đổi.** Tất cả những điều trên chỉ cải thiện tử số. Khối lượng vẫn chịu ảnh hưởng của các quy ước báo cáo và các phức tạp liên quan đến giao dịch thỏa thuận đã thảo luận trong chương vi cấu trúc thị trường.## Lắp ráp Bảng dữ liệu Thanh khoản {#sec-liquidity-panel}Chúng ta ghép cả bảy thước đo vào một bảng dữ liệu công ty–tháng duy nhất để phân tích so sánh.```{python}#| label: merge-panel#| eval: false#| code-summary: "Merge all liquidity measures into a unified panel"# Start with monthly returns as the basepanel = monthly[['ticker', 'month_end', 'monthly_return','market_cap']].copy()# Merge each liquidity measurefor name, df, key_col in [ ('Amihud', amihud_monthly, 'amihud'), ('Zero Return', zero_monthly, 'zero_return_pct'), ('Turnover', turnover_monthly, 'log_turnover'), ('Roll', roll_monthly, 'roll_spread'), ('Corwin-Schultz', cs_monthly, 'cs_spread'), ('Quoted Spread', quoted_monthly, 'quoted_spread'), ('Kyle Lambda', kyle_monthly, 'log_kyle'),]: panel = panel.merge( df[['ticker', 'month_end', key_col]], on=['ticker', 'month_end'], how='left' )# Add log market cappanel['log_mcap'] = np.log(panel['market_cap'].clip(lower=1))# Add fundamentals (lagged)fund_lagged = fundamentals.copy()fund_lagged['merge_year'] = fund_lagged['fiscal_year'] +1panel = panel.merge( fund_lagged[['ticker', 'merge_year', 'book_equity']].rename( columns={'merge_year': 'year'}), left_on=['ticker', panel['month_end'].dt.year], right_on=['ticker', 'year'], how='left')panel['bm'] = panel['book_equity'] / panel['market_cap']print(f"Unified panel: {len(panel):,} firm-months")print(f"\nCoverage by measure:")liquidity_cols = ['amihud', 'zero_return_pct', 'log_turnover','roll_spread', 'cs_spread', 'quoted_spread', 'log_kyle']for col in liquidity_cols: pct = panel[col].notna().mean()print(f" {col:<20}: {pct:.1%}")```## Đặc điểm Mặt cắt ngang của Thanh khoản {#sec-liquidity-cross-section}### Thống kê Mô tả theo Nhóm Ngũ phân vị Quy môThanh khoản biến thiên rất lớn trên phân phối quy mô. Các cổ phiếu vốn hóa nhỏ của Việt Nam có thể kém thanh khoản hơn các cổ phiếu vốn hóa lớn nhiều bậc độ lớn.```{python}#| label: liquidity-by-size#| eval: false#| code-summary: "Compute liquidity statistics by market cap quintile"# Assign size quintiles within each monthpanel['size_quintile'] = ( panel.groupby('month_end')['market_cap'] .transform(lambda x: pd.qcut(x, 5, labels=['Q1 (Small)', 'Q2','Q3', 'Q4','Q5 (Large)'], duplicates='drop')))# Average liquidity by quintileliq_by_size = ( panel.groupby('size_quintile')[liquidity_cols] .mean() .round(4))print("Average Liquidity by Market Cap Quintile:")print(liq_by_size.to_string())``````{python}#| label: fig-liquidity-by-size#| eval: false#| fig-cap: "Các thước đo thanh khoản theo nhóm ngũ phân vị vốn hóa thị trường. Mọi thước đo đều xác nhận mối quan hệ nghịch mạnh giữa quy mô và mức phi thanh khoản: nhóm nhỏ nhất kém thanh khoản hơn nhóm lớn nhất từ 10 đến 50 lần theo bất kỳ thước đo nào. Gradient này dốc hơn ở Việt Nam so với các thị trường phát triển, phản ánh sự không đồng nhất cực độ của tập các mã niêm yết."#| code-summary: "Bar charts of liquidity measures by size quintile"fig, axes = plt.subplots(2, 3, figsize=(16, 10))axes = axes.flatten()measures_to_plot = [ ('amihud', 'Amihud (log)', '#2C5F8A'), ('zero_return_pct', 'Zero-Return %', '#C0392B'), ('log_turnover', 'Log Turnover', '#27AE60'), ('roll_spread', 'Roll Spread', '#E67E22'), ('cs_spread', 'Corwin-Schultz Spread', '#8E44AD'), ('quoted_spread', 'Quoted Spread', '#1ABC9C')]for i, (col, label, color) inenumerate(measures_to_plot): data = panel.groupby('size_quintile')[col].mean() axes[i].bar(range(len(data)), data.values, color=color, alpha=0.85, edgecolor='white') axes[i].set_xticks(range(len(data))) axes[i].set_xticklabels(data.index, fontsize=8) axes[i].set_ylabel(label) axes[i].set_title(label)plt.suptitle('Liquidity Measures by Market Cap Quintile', fontsize=14)plt.tight_layout()plt.show()```### Cấu trúc Tương quanCác thước đo thanh khoản khác nhau tương quan mạnh đến mức nào? Nếu chúng nắm bắt cùng một chiều cơ bản, chúng ta kỳ vọng tương quan cao. Nếu chúng nắm bắt các chiều khác nhau (độ chặt so với độ sâu so với hoạt động giao dịch), tương quan sẽ ở mức vừa phải.```{python}#| label: fig-liquidity-correlations#| eval: false#| fig-cap: "Tương quan hạng từng cặp giữa các thước đo thanh khoản. Các thước đo tương quan dương (tất cả cùng chiều—giá trị càng cao nghĩa là thanh khoản càng thấp, ngoại trừ tỷ lệ vòng quay, nơi giá trị càng cao nghĩa là thanh khoản càng cao), nhưng các tương quan còn xa mức hoàn hảo. Điều này xác nhận rằng thanh khoản là đa chiều: mỗi thước đo nắm bắt một khía cạnh khác nhau của ma sát giao dịch."#| code-summary: "Compute and plot cross-sectional rank correlations"# Rank correlations (Spearman) among liquidity measures# Reverse turnover sign so higher = less liquid (consistent direction)panel_corr = panel[liquidity_cols].copy()panel_corr['neg_log_turnover'] =-panel_corr['log_turnover']corr_cols = ['amihud', 'zero_return_pct', 'neg_log_turnover','roll_spread', 'cs_spread', 'quoted_spread', 'log_kyle']corr_labels = ['Amihud', 'Zero-Return', 'Neg. Turnover', 'Roll','Corwin-Schultz', 'Quoted Spread', 'Kyle λ']rank_corr = panel_corr[corr_cols].corr(method='spearman')rank_corr.index = corr_labelsrank_corr.columns = corr_labelsfig, ax = plt.subplots(figsize=(9, 8))mask = np.triu(np.ones_like(rank_corr, dtype=bool), k=1)sns.heatmap( rank_corr, mask=mask, annot=True, fmt='.2f', cmap='YlOrRd', vmin=0, vmax=1, square=True, linewidths=0.5, ax=ax, cbar_kws={'label': 'Spearman Rank Correlation'})ax.set_title('Cross-Sectional Rank Correlations Among Liquidity Measures')plt.tight_layout()plt.show()```### Phân tích Thành phần Chính của Thanh khoảnDo tính đa chiều của thanh khoản, chúng ta trích xuất một nhân tố thanh khoản tổng hợp bằng PCA:```{python}#| label: pca-liquidity#| eval: false#| code-summary: "Extract principal components from standardized liquidity measures"from sklearn.preprocessing import StandardScalerfrom sklearn.decomposition import PCA# Standardize each measure within each month (cross-sectional)liq_data = panel[liquidity_cols].copy()liq_data['neg_log_turnover'] =-liq_data['log_turnover']pca_cols = ['amihud', 'zero_return_pct', 'neg_log_turnover','roll_spread', 'cs_spread', 'log_kyle']# Drop rows with any missing liquidity measureliq_complete = panel.dropna(subset=pca_cols).copy()# Cross-sectional standardization by monthdef standardize_within_month(df, cols):for col in cols: df[col +'_z'] = ( df.groupby('month_end')[col] .transform(lambda x: (x - x.mean()) / x.std()) )return dfliq_complete = standardize_within_month(liq_complete, pca_cols)z_cols = [c +'_z'for c in pca_cols]# Pool all months for PCApca_input = liq_complete[z_cols].dropna()pca = PCA(n_components=3)pca.fit(pca_input)print("PCA Explained Variance Ratios:")for i, (var, cumvar) inenumerate(zip( pca.explained_variance_ratio_, np.cumsum(pca.explained_variance_ratio_))):print(f" PC{i+1}: {var:.3f} (cumulative: {cumvar:.3f})")print("\nPC1 Loadings:")for col, loading inzip(pca_cols, pca.components_[0]):print(f" {col:<20}: {loading:.3f}")# Assign PC1 as composite illiquidityliq_complete['illiq_pc1'] = pca.transform( liq_complete[z_cols].values)[:, 0]```## Thanh khoản Tổng hợp và Điều kiện Thị trường {#sec-liquidity-aggregate}### Chuỗi Thời gian của Thanh khoản Thị trườngThanh khoản tổng hợp (tức là mức phi thanh khoản trung bình trên tất cả cổ phiếu) biến đổi đáng kể theo thời gian. @chordia2001market ghi nhận rằng thanh khoản toàn thị trường suy giảm trong các giai đoạn biến động cao và lợi nhuận thị trường âm.```{python}#| label: fig-aggregate-liquidity#| eval: false#| fig-cap: "Điều kiện thanh khoản tổng hợp trên thị trường chứng khoán Việt Nam. Panel A cho thấy trung vị mặt cắt ngang của ba thước đo phi thanh khoản theo thời gian (đã chuẩn hóa). Panel B cho thấy lợi nhuận VN-Index để làm bối cảnh. Thanh khoản suy giảm mạnh trong cuộc khủng hoảng tài chính toàn cầu 2008--2009, chu kỳ thắt chặt trong nước năm 2011, và đợt sụt giảm do COVID tháng 3 năm 2020. Mỗi giai đoạn được đánh dấu bằng sự tăng vọt đồng thời của mọi thước đo phi thanh khoản."#| code-summary: "Plot aggregate liquidity time series"# Compute monthly cross-sectional aggregatesagg_liquidity = ( panel.groupby('month_end') .agg( amihud_median=('amihud', 'median'), zero_ret_median=('zero_return_pct', 'median'), turnover_median=('log_turnover', 'median'), roll_median=('roll_spread', 'median'), cs_median=('cs_spread', 'median'), n_stocks=('ticker', 'nunique') ) .reset_index())# Standardize for plottingfor col in ['amihud_median', 'zero_ret_median', 'roll_median']: agg_liquidity[col +'_z'] = ( (agg_liquidity[col] - agg_liquidity[col].mean())/ agg_liquidity[col].std() )fig, axes = plt.subplots(2, 1, figsize=(14, 9), height_ratios=[2, 1])# Panel A: Aggregate illiquidityaxes[0].plot(agg_liquidity['month_end'], agg_liquidity['amihud_median_z'], color='#2C5F8A', linewidth=1.5, label='Amihud')axes[0].plot(agg_liquidity['month_end'], agg_liquidity['zero_ret_median_z'], color='#C0392B', linewidth=1.5, label='Zero-Return')axes[0].plot(agg_liquidity['month_end'], agg_liquidity['roll_median_z'], color='#27AE60', linewidth=1.5, label='Roll Spread')axes[0].axhline(y=0, color='gray', linewidth=0.5)axes[0].set_ylabel('Standardized Illiquidity')axes[0].set_title('Panel A: Aggregate Illiquidity Over Time')axes[0].legend(fontsize=9)# Shade crisis periodscrisis_periods = [ ('2008-06-01', '2009-03-31', 'GFC'), ('2011-01-01', '2011-12-31', 'Tightening'), ('2020-02-01', '2020-05-31', 'COVID')]for start, end, label in crisis_periods: axes[0].axvspan(pd.Timestamp(start), pd.Timestamp(end), alpha=0.15, color='gray') mid = pd.Timestamp(start) + (pd.Timestamp(end) - pd.Timestamp(start)) /2 axes[0].text(mid, axes[0].get_ylim()[1] *0.9, label, ha='center', fontsize=8, color='gray')# Panel B: Market returnmarket_monthly = factors[['month_end', 'mkt_excess']].copy()market_monthly['month_end'] = pd.to_datetime(market_monthly['month_end'])axes[1].bar(market_monthly['month_end'], market_monthly['mkt_excess'] *100, width=25, color=['#27AE60'if r >0else'#C0392B'for r in market_monthly['mkt_excess']], alpha=0.6)axes[1].set_ylabel('Market Excess Return (%)')axes[1].set_xlabel('Date')axes[1].set_title('Panel B: VN-Index Monthly Excess Return')plt.tight_layout()plt.show()```### Tính Đồng biến của Thanh khoản@chordia2000commonality thấy rằng thanh khoản của từng cổ phiếu đồng biến với thanh khoản thị trường, ngay cả sau khi kiểm soát các nhân tố riêng của công ty. Chúng ta kiểm định tính đồng biến ở Việt Nam bằng cách hồi quy các thay đổi của thanh khoản cấp công ty trên các thay đổi của thanh khoản cấp thị trường:$$\Delta L_{i,m} = \alpha_i + \beta_i \Delta L_{M,m} + \gamma_i \Delta L_{M,m-1} + \delta_i \Delta L_{M,m+1} + \varepsilon_{i,m}$$ {#eq-commonality}trong đó $\Delta L_{i,m}$ là thay đổi mức phi thanh khoản của công ty $i$, $\Delta L_{M,m}$ là thay đổi mức phi thanh khoản trung bình thị trường (không tính công ty $i$), và các số hạng dẫn/trễ nắm bắt sự điều chỉnh không đồng bộ. Hệ số $\beta_i$ đo độ nhạy của thanh khoản công ty $i$ đối với các cú sốc thanh khoản toàn thị trường.```{python}#| label: commonality#| eval: false#| code-summary: "Test for commonality in liquidity across Vietnamese stocks"# Compute monthly changes in Amihud for each firm and the marketpanel_common = panel[['ticker', 'month_end', 'amihud']].dropna().copy()panel_common = panel_common.sort_values(['ticker', 'month_end'])panel_common['d_amihud'] = ( panel_common.groupby('ticker')['amihud'].diff())# Market-level illiquidity change (equal-weighted, excluding firm i)mkt_liq = ( panel_common.groupby('month_end')['amihud'] .mean() .diff() .to_frame('d_amihud_mkt'))mkt_liq['d_amihud_mkt_lag'] = mkt_liq['d_amihud_mkt'].shift(1)mkt_liq['d_amihud_mkt_lead'] = mkt_liq['d_amihud_mkt'].shift(-1)panel_common = panel_common.merge(mkt_liq, on='month_end', how='left')# Estimate commonality for each firmdef estimate_commonality(group, min_obs=24): g = group.dropna(subset=['d_amihud', 'd_amihud_mkt'])iflen(g) < min_obs:returnNone y = g['d_amihud'] X = sm.add_constant(g[['d_amihud_mkt', 'd_amihud_mkt_lag','d_amihud_mkt_lead']])try: model = sm.OLS(y, X).fit()return pd.Series({'beta_mkt': model.params['d_amihud_mkt'],'beta_t': model.tvalues['d_amihud_mkt'],'r_squared': model.rsquared })exceptException:returnNonecommonality = ( panel_common .groupby('ticker') .apply(estimate_commonality) .dropna())print("Commonality in Liquidity (Amihud):")print(f" Mean beta_mkt: {commonality['beta_mkt'].mean():.3f}")print(f" Median beta_mkt: {commonality['beta_mkt'].median():.3f}")print(f" % significant at 5%: "f"{(commonality['beta_t'].abs() >1.96).mean():.1%}")print(f" Mean R-squared: {commonality['r_squared'].mean():.3f}")```## Thanh khoản có được Định giá không? {#sec-liquidity-pricing}### Phân loại Danh mụcChúng ta kiểm định xem mức phi thanh khoản có dự báo lợi nhuận tương lai hay không bằng cách phân loại cổ phiếu vào các danh mục ngũ phân vị dựa trên các thước đo thanh khoản có độ trễ và so sánh lợi nhuận trung bình giữa các nhóm ngũ phân vị.```{python}#| label: portfolio-sorts#| eval: false#| code-summary: "Sort stocks into quintiles by lagged illiquidity and compute returns"def liquidity_portfolio_sort(panel_df, liq_col, n_groups=5):""" Compute quintile portfolio returns sorted on lagged liquidity. Lag the sorting variable by one month to avoid look-ahead bias. """ df = panel_df[['ticker', 'month_end', 'monthly_return','market_cap', liq_col]].dropna().copy() df = df.sort_values(['ticker', 'month_end'])# Lag the sorting variable df['liq_lag'] = df.groupby('ticker')[liq_col].shift(1) df = df.dropna(subset=['liq_lag', 'monthly_return'])# Assign quintiles within each month df['quintile'] = ( df.groupby('month_end')['liq_lag'] .transform(lambda x: pd.qcut(x, n_groups, labels=False, duplicates='drop')) )# EW portfolio returns by quintile-month port_returns = ( df.groupby(['month_end', 'quintile'])['monthly_return'] .mean() .unstack() )# Long-short (Q5 - Q1)if n_groups -1in port_returns.columns and0in port_returns.columns: port_returns['long_short'] = ( port_returns[n_groups -1] - port_returns[0] )return port_returns# Run sorts for each illiquidity measuresort_measures = {'Amihud': 'amihud','Zero-Return': 'zero_return_pct','Neg. Turnover': 'log_turnover', # Will reverse below'Roll Spread': 'roll_spread','Corwin-Schultz': 'cs_spread',}# For turnover, negate so higher = less liquidpanel_sorts = panel.copy()panel_sorts['neg_turnover'] =-panel_sorts['log_turnover']sort_measures_actual = {'Amihud': 'amihud','Zero-Return': 'zero_return_pct','Neg. Turnover': 'neg_turnover','Roll Spread': 'roll_spread','Corwin-Schultz': 'cs_spread',}print("Liquidity Premium (EW, Quintile Sorts):")print(f"{'Measure':<18}{'Q1 (Liquid)':>12}{'Q5 (Illiquid)':>14} "f"{'Q5-Q1':>10}{'t-stat':>8}")print("-"*62)sort_results = {}for name, col in sort_measures_actual.items(): ports = liquidity_portfolio_sort(panel_sorts, col) sort_results[name] = ports q1 = ports[0].mean() *12 q5 = ports[4].mean() *12if4in ports.columns else np.nan ls = ports['long_short'].mean() *12if'long_short'in ports else np.nan ls_se = ports['long_short'].std() / np.sqrt(len(ports)) * np.sqrt(12) if'long_short'in ports else np.nan t = ls / ls_se if ls_se and ls_se >0else np.nanprint(f"{name:<18}{q1:>12.4f}{q5:>14.4f}{ls:>10.4f}{t:>8.2f}")``````{python}#| label: fig-liquidity-premium#| eval: false#| fig-cap: "Lợi nhuận trung bình quy năm theo nhóm ngũ phân vị phi thanh khoản cho từng thước đo thanh khoản. Một dạng thức tăng đơn điệu từ Q1 (thanh khoản nhất) đến Q5 (kém thanh khoản nhất) cho thấy có phần bù thanh khoản—khoản bù đắp cho việc nắm giữ các cổ phiếu khó giao dịch hơn."#| code-summary: "Bar chart of quintile returns by liquidity measure"fig, axes = plt.subplots(2, 3, figsize=(16, 10))axes = axes.flatten()colors_quintile = ['#27AE60', '#2ECC71', '#F1C40F', '#E67E22', '#C0392B']for i, (name, ports) inenumerate(sort_results.items()):if i >=5:break quintile_means = [ports[q].mean() *12*100for q inrange(5)if q in ports.columns] axes[i].bar(range(len(quintile_means)), quintile_means, color=colors_quintile[:len(quintile_means)], alpha=0.85, edgecolor='white') axes[i].set_xticks(range(len(quintile_means))) axes[i].set_xticklabels([f'Q{q+1}'for q inrange(len(quintile_means))]) axes[i].set_ylabel('Annualized Return (%)') axes[i].set_title(name) axes[i].axhline(y=0, color='gray', linewidth=0.5)# Hide unused subplotiflen(sort_results) <6: axes[5].set_visible(False)plt.suptitle('Average Returns by Illiquidity Quintile', fontsize=14)plt.tight_layout()plt.show()```### Hồi quy Mặt cắt ngang Fama-MacBethCác phân loại danh mục mang tính thông tin nhưng không thể kiểm soát đồng thời nhiều đặc điểm. Chúng ta dùng các hồi quy mặt cắt ngang theo kiểu @fama1993common để kiểm định xem thanh khoản có dự báo lợi nhuận sau khi kiểm soát quy mô, giá trị và động lượng hay không:$$R_{i,m+1} = \gamma_{0,m} + \gamma_{1,m} \text{ILLIQ}_{i,m} + \gamma_{2,m} \ln(\text{MCap}_{i,m}) + \gamma_{3,m} \text{BM}_{i,m} + \gamma_{4,m} R_{i,m-12:m-1} + \varepsilon_{i,m+1}$$ {#eq-fama-macbeth}Trung bình chuỗi thời gian của hệ số theo tháng $\bar{\gamma}_1$ ước lượng phần bù phi thanh khoản, và thống kê t của nó dùng sai số chuẩn của @fama1993common.```{python}#| label: fama-macbeth#| eval: false#| code-summary: "Fama-MacBeth regressions of returns on lagged liquidity and controls"def fama_macbeth(panel_df, illiq_col, controls=['log_mcap', 'bm'], min_stocks=50):""" Run Fama-MacBeth cross-sectional regressions of next-month returns on lagged illiquidity and controls. """ df = panel_df.copy() df = df.sort_values(['ticker', 'month_end'])# Lag the illiquidity measure df['illiq_lag'] = df.groupby('ticker')[illiq_col].shift(1)# Lag controlsfor c in controls: df[c +'_lag'] = df.groupby('ticker')[c].shift(1) regressors = ['illiq_lag'] + [c +'_lag'for c in controls] df = df.dropna(subset=['monthly_return'] + regressors)# Month-by-month cross-sectional regressions months =sorted(df['month_end'].unique()) gamma_list = []for month in months: cross = df[df['month_end'] == month]iflen(cross) < min_stocks:continue y = cross['monthly_return'].values X = sm.add_constant(cross[regressors].values)try: model = sm.OLS(y, X).fit() gammas = {'month_end': month, 'intercept': model.params[0]}for j, reg inenumerate(regressors): gammas[reg] = model.params[j +1] gamma_list.append(gammas)exceptException:pass gamma_df = pd.DataFrame(gamma_list)# Time-series averages and t-statistics results = {}for col in ['intercept'] + regressors: mean = gamma_df[col].mean() se = gamma_df[col].std() / np.sqrt(len(gamma_df)) t = mean / se if se >0else np.nan results[col] = {'Coefficient': mean, 'SE': se, 't-stat': t}return pd.DataFrame(results).T, gamma_df# Run for each illiquidity measureprint("Fama-MacBeth Regressions: R_{i,m+1} on ILLIQ_{i,m} + controls")print("="*70)for name, col in [('Amihud', 'amihud'), ('Zero-Return', 'zero_return_pct'), ('Roll Spread', 'roll_spread'), ('Corwin-Schultz', 'cs_spread')]: results, gammas = fama_macbeth(panel, col)print(f"\n{name}:")print(results[['Coefficient', 't-stat']].round(4).to_string())```### Phần bù Thanh khoản đã Điều chỉnh Nhân tốPhần bù thanh khoản có thể được giải thích một phần hoặc toàn bộ bởi các nhân tố rủi ro hiện có (quy mô, giá trị, động lượng). Chúng ta kiểm định điều này bằng cách hồi quy lợi nhuận danh mục mua–bán khống thanh khoản trên các nhân tố Fama-French-Carhart:```{python}#| label: factor-adjusted#| eval: false#| code-summary: "Regress liquidity long-short returns on FF+Carhart factors"print("Factor-Adjusted Liquidity Premium:")print(f"{'Measure':<18}{'Alpha (ann.)':>12}{'Alpha t':>10} "f"{'MKT':>8}{'SMB':>8}{'HML':>8}{'R2':>6}")print("-"*72)for name, ports in sort_results.items():if'long_short'notin ports.columns:continue ls_series = ports['long_short'].to_frame('ls') ls_series.index = pd.to_datetime(ls_series.index) merged = ls_series.merge(factors, left_index=True, right_on='month_end', how='inner')iflen(merged) <24:continue y = merged['ls'] X = sm.add_constant(merged[['mkt_excess', 'smb', 'hml', 'wml']]) model = sm.OLS(y, X).fit(cov_type='HAC', cov_kwds={'maxlags': 6}) alpha_ann = model.params['const'] *12 alpha_t = model.tvalues['const'] mkt_b = model.params['mkt_excess'] smb_b = model.params['smb'] hml_b = model.params['hml'] r2 = model.rsquaredprint(f"{name:<18}{alpha_ann:>12.4f}{alpha_t:>10.2f} "f"{mkt_b:>8.3f}{smb_b:>8.3f}{hml_b:>8.3f}{r2:>6.3f}")```## Thanh khoản và Ước lượng Chi phí Giao dịch {#sec-liquidity-implementation}### Chuyển các Thước đo thành Chi phí Giao dịchĐối với các nhà thực hành, câu hỏi then chốt là: một giá trị Amihud hay chênh lệch giá nhất định *có ý nghĩa* gì về chi phí VND thực tế cho mỗi giao dịch? Chúng ta hiệu chỉnh mối quan hệ giữa các thước đo xấp xỉ tần suất thấp và chi phí giao dịch tường minh.```{python}#| label: cost-calibration#| eval: false#| code-summary: "Calibrate liquidity proxies to estimated round-trip trading costs"def estimate_round_trip_cost(row):""" Estimate total round-trip trading cost (in %) from multiple liquidity proxies. Components: 1. Explicit: commission + tax (~0.35% round-trip) 2. Spread cost: half-spread each way 3. Price impact: function of trade size """ explicit =0.0035# 35 bps round-trip# Use Corwin-Schultz or quoted spread as spread estimate spread = row.get('cs_spread', row.get('quoted_spread', 0.005)) spread_cost = spread # Full spread = round-trip cost# Price impact (approximate from Amihud)# For a trade of 1% of daily volume amihud_raw = row.get('amihud_raw', 0) impact = amihud_raw *0.01# Rough approximationreturn explicit + spread_cost + impactpanel['estimated_rtc'] = panel.apply(estimate_round_trip_cost, axis=1)# Distribution by size quintilertc_by_size = ( panel.groupby('size_quintile')['estimated_rtc'] .agg(['mean', 'median', 'std']) .round(4))print("Estimated Round-Trip Cost by Size Quintile (%):")print((rtc_by_size *100).round(2).to_string())``````{python}#| label: fig-cost-distribution#| eval: false#| fig-cap: "Chi phí giao dịch khứ hồi ước lượng theo nhóm ngũ phân vị vốn hóa thị trường. Các cổ phiếu vốn hóa nhỏ của Việt Nam có chi phí ước lượng 3--8%, khiến các chiến lược tái cân bằng tần suất cao không kinh tế. Ngay cả các cổ phiếu vốn hóa lớn cũng chịu chi phí 0.5--1.5%, cao hơn đáng kể so với các thị trường phát triển."#| code-summary: "Plot estimated trading costs by size quintile"fig, axes = plt.subplots(1, 2, figsize=(14, 5))# Panel A: Distributionfor q, color inzip(['Q1 (Small)', 'Q3', 'Q5 (Large)'], ['#C0392B', '#F1C40F', '#27AE60']): subset = panel[panel['size_quintile'] == q]['estimated_rtc'].dropna() subset = subset[subset <0.15] # Trim extreme axes[0].hist(subset *100, bins=50, density=True, alpha=0.5, color=color, label=q, edgecolor='white')axes[0].set_xlabel('Estimated Round-Trip Cost (%)')axes[0].set_ylabel('Density')axes[0].set_title('Panel A: Cost Distribution by Size')axes[0].legend()# Panel B: Time series of median costcost_ts = ( panel.groupby('month_end')['estimated_rtc'] .median() .reset_index())axes[1].plot(pd.to_datetime(cost_ts['month_end']), cost_ts['estimated_rtc'] *100, color='#2C5F8A', linewidth=1.5)axes[1].set_xlabel('Date')axes[1].set_ylabel('Median Round-Trip Cost (%)')axes[1].set_title('Panel B: Aggregate Trading Costs Over Time')plt.tight_layout()plt.show()```### Tính Khả thi của Chiến lượcMột ứng dụng quan trọng của việc đo lường thanh khoản là kiểm định xem một chiến lược khai thác hiện tượng bất thường có còn sinh lời sau khi tính đến chi phí giao dịch thực tế hay không. Chúng ta tính lợi nhuận sau chi phí cho chính các danh mục phân loại theo thanh khoản. Đây vốn là một phép kiểm định thận trọng vì vị thế mua kém thanh khoản chịu chi phí cao nhất.```{python}#| label: net-of-cost#| eval: false#| code-summary: "Compute net-of-cost returns for illiquidity-sorted portfolios"# For each quintile, estimate average monthly turnover and cost# and subtract from gross returnsfor name, ports in sort_results.items():if'long_short'notin ports.columns:continue gross_ann = ports['long_short'].mean() *12# Estimate costs: illiquid quintile has higher costs# Assume monthly turnover of ~15% for long-short with monthly rebalancing turnover =0.15 cost_q1 =0.003# 30 bps per trade for liquid stocks cost_q5 =0.015# 150 bps for illiquid stocks avg_cost = (cost_q1 + cost_q5) /2# Average across long and short monthly_tc = turnover * avg_cost net_ann = gross_ann - monthly_tc *12print(f"{name:<18}: Gross = {gross_ann*100:>6.2f}%, "f"TC = {monthly_tc*1200:>6.1f} bps/mo, "f"Net = {net_ann*100:>6.2f}%")```## Thanh khoản trong Giai đoạn Căng thẳng Thị trường {#sec-liquidity-stress}### Tháo chạy sang Thanh khoảnTrong giai đoạn căng thẳng thị trường, nhà đầu tư bán các tài sản kém thanh khoản và mua các tài sản thanh khoản, đó là hiện tượng "tháo chạy sang thanh khoản" (flight to liquidity) làm nới rộng chênh lệch lợi nhuận giữa các cổ phiếu thanh khoản và kém thanh khoản. @hameed2010stock chỉ ra rằng dạng thức này mạnh nhất khi lợi nhuận thị trường âm nhất.```{python}#| label: flight-to-liquidity#| eval: false#| code-summary: "Test for flight-to-liquidity during market stress"# Merge Amihud-sorted portfolio returns with market returnsamihud_ports = sort_results.get('Amihud')if amihud_ports isnotNoneand'long_short'in amihud_ports.columns: ftl_data = pd.merge( amihud_ports['long_short'].to_frame('illiq_premium'), factors[['month_end', 'mkt_excess']].set_index('month_end'), left_index=True, right_index=True, how='inner' )# Classify market states ftl_data['mkt_state'] = pd.cut( ftl_data['mkt_excess'], bins=[-np.inf, ftl_data['mkt_excess'].quantile(0.20), ftl_data['mkt_excess'].quantile(0.80), np.inf], labels=['Bear (bottom 20%)', 'Normal', 'Bull (top 20%)'] )# Illiquidity premium by market state state_premium = ( ftl_data.groupby('mkt_state')['illiq_premium'] .agg(['mean', 'std', 'count']) ) state_premium['ann_premium'] = state_premium['mean'] *12 state_premium['t_stat'] = ( state_premium['mean']/ (state_premium['std'] / np.sqrt(state_premium['count'])) )print("Illiquidity Premium by Market State:")print(state_premium[['ann_premium', 't_stat', 'count']].round(3))``````{python}#| label: fig-flight-to-liquidity#| eval: false#| fig-cap: "Phần bù phi thanh khoản (Amihud Q5 trừ Q1) có điều kiện theo lợi nhuận thị trường. Trong thị trường giá xuống, các cổ phiếu kém thanh khoản có lợi nhuận thấp hơn các cổ phiếu thanh khoản---phần bù phi thanh khoản đảo chiều. Trong thị trường bình thường và giá lên, phần bù là dương. Sự bất đối xứng này phản ánh hành vi tháo chạy sang thanh khoản: nhà đầu tư bỏ chạy khỏi các cổ phiếu kém thanh khoản trong giai đoạn căng thẳng, làm giá của chúng giảm và tạo ra lợi nhuận đồng thời âm."#| code-summary: "Bar chart of conditional illiquidity premium"fig, ax = plt.subplots(figsize=(8, 5))if'state_premium'indir(): colors_state = ['#C0392B', '#F1C40F', '#27AE60'] bars = ax.bar(range(len(state_premium)), state_premium['ann_premium'] *100, color=colors_state, alpha=0.85, edgecolor='white') ax.set_xticks(range(len(state_premium))) ax.set_xticklabels(state_premium.index) ax.set_ylabel('Annualized Q5-Q1 Return (%)') ax.set_title('Illiquidity Premium by Market State') ax.axhline(y=0, color='gray', linewidth=0.8)for i, (_, row) inenumerate(state_premium.iterrows()): ax.text(i, row['ann_premium'] *100+0.3,f"t={row['t_stat']:.1f}", ha='center', fontsize=10)plt.tight_layout()plt.show()```### Đồng biến của Thanh khoản với Rủi ro Toàn cầuThanh khoản thị trường Việt Nam có thể bị chi phối bởi các nhân tố rủi ro toàn cầu, đặc biệt đối với các cổ phiếu do nhà đầu tư nước ngoài nắm giữ. Chúng ta kiểm định xem các thước đo rủi ro toàn cầu (VIX, sức mạnh của USD) có dự báo thanh khoản tổng hợp của Việt Nam hay không:```{python}#| label: global-risk#| eval: false#| code-summary: "Regress Vietnamese aggregate liquidity on global risk proxies"# Merge aggregate liquidity with global variablesglobal_vars = client.get_macro_data( variables=['vix_close', 'dxy_index'], start_date='2008-01-01', end_date='2024-12-31', frequency='monthly')agg_liq_global = agg_liquidity.merge( global_vars, on='month_end', how='inner')# Changes in all variablesfor col in ['amihud_median', 'vix_close', 'dxy_index']: agg_liq_global[f'd_{col}'] = agg_liq_global[col].diff()# Regressiony = agg_liq_global['d_amihud_median'].dropna()X = sm.add_constant( agg_liq_global.loc[y.index, ['d_vix_close', 'd_dxy_index']])model = sm.OLS(y, X).fit(cov_type='HAC', cov_kwds={'maxlags': 3})print("Aggregate Illiquidity ~ Global Risk:")print(model.summary().tables[1])```## Xây dựng một Nhân tố Thanh khoản có thể Giao dịch {#sec-liquidity-factor}Theo @pastor2003liquidity, chúng ta xây dựng một nhân tố thanh khoản tổng hợp có thể dùng trong các kiểm định định giá tài sản. Nhân tố này nắm bắt các cú sốc (innovations) của thanh khoản tổng hợp (những thay đổi không được kỳ vọng của điều kiện giao dịch toàn thị trường).```{python}#| label: liquidity-factor#| eval: false#| code-summary: "Construct an aggregate liquidity innovation factor"# Step 1: Compute market-level Amihud as EW averagemkt_amihud = ( panel.groupby('month_end')['amihud'] .mean() .to_frame('mkt_amihud'))# Step 2: Estimate AR(2) model for aggregate illiquiditymkt_amihud['mkt_amihud_lag1'] = mkt_amihud['mkt_amihud'].shift(1)mkt_amihud['mkt_amihud_lag2'] = mkt_amihud['mkt_amihud'].shift(2)mkt_amihud = mkt_amihud.dropna()ar_model = sm.OLS( mkt_amihud['mkt_amihud'], sm.add_constant(mkt_amihud[['mkt_amihud_lag1', 'mkt_amihud_lag2']])).fit()# Step 3: Residuals = liquidity innovations# Negative innovation = liquidity improved (good)# Positive innovation = liquidity deteriorated (bad)mkt_amihud['liq_innovation'] = ar_model.resid# Step 4: Test whether liquidity innovations predict returns# Higher sensitivity to negative innovations = higher expected returnprint("AR(2) Model for Aggregate Amihud:")print(f" R-squared: {ar_model.rsquared:.3f}")print(f" AR(1) coef: {ar_model.params['mkt_amihud_lag1']:.3f} "f"(t={ar_model.tvalues['mkt_amihud_lag1']:.2f})")# Step 5: Estimate liquidity betas for each firmpanel_liq_beta = panel.merge( mkt_amihud[['liq_innovation']], left_on='month_end', right_index=True, how='inner')def estimate_liq_beta(group, min_obs=36): g = group.dropna(subset=['monthly_return', 'liq_innovation'])iflen(g) < min_obs:returnNone y = g['monthly_return'] X = sm.add_constant(g['liq_innovation'])try: model = sm.OLS(y, X).fit()return model.params['liq_innovation']exceptException:returnNoneliq_betas = ( panel_liq_beta .groupby('ticker') .apply(estimate_liq_beta) .dropna() .to_frame('liq_beta'))print(f"\nLiquidity Beta Distribution:")print(liq_betas['liq_beta'].describe().round(4))```## Hướng dẫn Thực hành cho Việt Nam {#sec-liquidity-practical}Phân tích trong chương này đưa ra các khuyến nghị sau:**Dành cho nhà nghiên cứu:** Tỷ số phi thanh khoản Amihud là thước đo xấp xỉ thanh khoản đa dụng tốt nhất cho chứng khoán Việt Nam. Nó có độ bao phủ cao nhất, khả năng dự báo lợi nhuận mặt cắt ngang mạnh nhất, và mối quan hệ vững chắc nhất với quy mô công ty. Khi cần một thước đo thứ hai để kiểm tra độ vững, tỷ lệ lợi nhuận bằng không là bổ sung tự nhiên—nó nắm bắt một chiều khác (ngưỡng chi phí giao dịch) và có độ bao phủ gần như đầy đủ.**Dành cho xây dựng danh mục đầu tư:** Mọi kiểm định ngược của một chiến lược chứng khoán Việt Nam nên tính và báo cáo chi phí khứ hồi ước lượng theo nhóm ngũ phân vị. Các chiến lược tập trung vào hai nhóm ngũ phân vị quy mô thấp nhất chịu chi phí 2--5% mỗi vòng khứ hồi, khiến việc tái cân bằng hằng tháng không kinh tế. Tái cân bằng theo quý hoặc theo năm với ràng buộc về tỷ lệ vòng quay thì thực tế hơn.**Dành cho quản trị rủi ro:** Theo dõi điều kiện thanh khoản tổng hợp bằng trung vị mặt cắt ngang của Amihud hoặc tỷ lệ lợi nhuận bằng không toàn thị trường. Sự suy giảm thanh khoản dự báo lợi nhuận thị trường âm và chênh lệch giá rộng hơn trong các tháng tiếp theo. Hãy siết chặt giới hạn rủi ro khi mức phi thanh khoản tổng hợp vượt phân vị 90 lịch sử.**Dành cho so sánh quốc tế:** Khi so sánh các phần bù nhân tố của Việt Nam với bằng chứng của Mỹ hoặc các thị trường phát triển khác, hãy luôn báo cáo kết quả trên một tập con "nhóm cổ phiếu thanh khoản" (60% hàng đầu theo vốn hóa thị trường) bên cạnh toàn bộ mẫu. Nhiều hiện tượng bất thường trông lớn trong toàn bộ mẫu thu hẹp đáng kể khi giới hạn ở các cổ phiếu thực sự có thể giao dịch ở quy mô lớn.## Tóm tắt {#sec-liquidity-summary}Bảng @tbl-liquidity-summary trình bày các thước đo thanh khoản khác nhau.| Thước đo | Chiều | Độ bao phủ | Gradient theo Quy mô | Khả năng Dự báo Lợi nhuận | Cách dùng Khuyến nghị ||------------|------------|------------|------------|------------|------------|| Amihud | Tác động giá | Cao | Rất dốc | Mạnh | Thước đo chính; phân loại danh mục; Fama-MacBeth || Lợi nhuận bằng không | Chi phí giao dịch | Đầy đủ | Dốc | Mạnh | Kiểm tra độ vững; nghiên cứu thị trường mới nổi || Tỷ lệ vòng quay | Hoạt động giao dịch | Cao | Vừa phải | Vừa phải | Bộ lọc dựa trên khối lượng; phân tích dòng tiền || Chênh lệch Roll | Độ chặt | Vừa phải | Vừa phải | Vừa phải | Ước lượng chênh lệch giá khi không có dữ liệu mua–bán || Corwin-Schultz | Độ chặt | Vừa phải | Vừa phải | Vừa phải | Chênh lệch giá dựa trên cao–thấp; hiệu chỉnh || Chênh lệch giá chào | Độ chặt | Thay đổi | Dốc | Mạnh | Thước đo trực tiếp khi có sẵn || Lambda của Kyle | Tác động giá | Vừa phải | Dốc | Mạnh | Nghiên cứu vi cấu trúc thị trường |: Tóm tắt các thuộc tính của các thước đo thanh khoản trên thị trường Việt Nam. {#tbl-liquidity-summary}Thanh khoản không phải là một cân nhắc thứ yếu đối với nghiên cứu chứng khoán Việt Nam; nó là yếu tố quyết định bậc nhất việc chiến lược nào có thể triển khai, hiện tượng bất thường nào là thật, và kết quả nào là sản phẩm giả tạo của việc giao dịch những cổ phiếu thực tế không thể giao dịch. Mọi phát hiện thực nghiệm trong cuốn sách này nên được đánh giá qua lăng kính của phân tích thanh khoản được phát triển trong chương này.<!-- ## Bài tập {#sec-liquidity-exercises}1. **Chênh lệch giá chào theo phần trăm lúc đóng cửa.** @fong2017best khuyến nghị chênh lệch giá chào theo phần trăm lúc đóng cửa là thước đo xấp xỉ thanh khoản toàn cầu tốt nhất. Hãy tính nó bằng chênh lệch giá mua–bán lúc đóng cửa chia cho giá trung điểm, lấy trung bình trong tháng. So sánh tương quan của nó với thước đo Amihud và chênh lệch Corwin-Schultz. Khuyến nghị này có đúng với Việt Nam không?2. **Động lượng điều chỉnh theo thanh khoản.** Tính lợi nhuận danh mục động lượng (12-1 tháng) riêng trong từng nhóm tam phân vị phi thanh khoản Amihud. Phần bù động lượng có tập trung ở các cổ phiếu kém thanh khoản không? Điều này hàm ý gì về tính khả thi của các chiến lược động lượng ở Việt Nam? Hãy liên hệ các phát hiện của bạn với @novy2016taxonomy.3. **Nhu cầu thanh khoản của nhà đầu tư nước ngoài.** Dùng dữ liệu khối lượng mua và bán của nhà đầu tư nước ngoài, hãy tính một thước đo dòng vốn ngoại ròng cho từng cổ phiếu. Kiểm định xem việc nước ngoài mua ròng có dự báo sự cải thiện thanh khoản tháng sau (Amihud thấp hơn, chênh lệch giá thấp hơn) hay không. Sở hữu nước ngoài có tạo ra ngoại tác thanh khoản không?4. **Các dạng thức thanh khoản trong phiên.** Nếu có dữ liệu trong phiên, hãy tính tỷ số Amihud riêng cho phiên khớp lệnh định kỳ mở cửa, phiên khớp lệnh liên tục buổi sáng và phiên khớp lệnh liên tục buổi chiều. Biến thiên thanh khoản trong phiên so với biến thiên mặt cắt ngang như thế nào?5. **Thanh khoản và công bố lợi nhuận.** Kiểm định xem thanh khoản có xấu đi trước và cải thiện sau các công bố lợi nhuận hằng quý hay không. Dùng khung nghiên cứu sự kiện với tỷ số Amihud làm biến phụ thuộc. Việc mức phi thanh khoản tăng trước công bố có phù hợp với mô hình lựa chọn bất lợi của @glosten1985bid không?6. **Khả năng sinh lời của tạo lập thị trường.** Dùng chênh lệch Roll làm thước đo xấp xỉ cho dao động giữa giá mua và giá bán, hãy ước lượng lợi nhuận gộp mà một nhà tạo lập thị trường giả định sẽ kiếm được từ việc cung cấp thanh khoản cho các cổ phiếu Việt Nam. So sánh con số này với chi phí vốn tường minh của hoạt động tạo lập thị trường như thế nào?7. **Danh mục mô phỏng nhân tố thanh khoản.** Xây dựng một nhân tố thanh khoản có thể giao dịch bằng cách phân loại cổ phiếu vào các nhóm thập phân vị dựa trên beta thanh khoản của chúng (độ nhạy với các cú sốc thanh khoản tổng hợp). Lợi nhuận mua–bán khống của nhân tố này nên tương quan với các thay đổi thanh khoản tổng hợp. Kiểm định xem nhân tố này có đem lại phần bù đáng kể không và có giải thích được lợi nhuận vượt ngoài các nhân tố Fama-French không.8. **Cú sốc thanh khoản COVID-19.** Phân tích động thái thanh khoản quanh đợt sụt giảm thị trường do COVID-19 tháng 3 năm 2020. Thanh khoản tổng hợp suy giảm nhanh đến mức nào? Những công ty nào trải qua mức sụt giảm thanh khoản lớn nhất? Các phản ứng chính sách của NHNN (cắt giảm lãi suất, giảm dự trữ bắt buộc) có cải thiện thanh khoản không, và với độ trễ bao lâu? -->