4  Cấu trúc thị trường vi mô tại Việt Nam

Note

Trong chương này, chúng ta sẽ xem xét cách thức thiết kế thể chế của thị trường chứng khoán Việt Nam, chẳng hạn như phiên giao dịch, giới hạn giá, loại lệnh và thành phần nhà đầu tư, định hình giá cả, lợi nhuận và tính thanh khoản quan sát được. Chúng ta sẽ định lượng các ma sát vi mô và chứng minh tại sao việc bỏ qua các ma sát này dẫn đến suy luận sai lệch trong các bài kiểm tra định giá tài sản.

Cấu trúc vi mô thị trường là nghiên cứu về cách các quy tắc giao dịch, cơ chế xử lý lệnh và thiết kế thị trường ảnh hưởng đến sự hình thành giá, chi phí giao dịch và tính thanh khoản. Lĩnh vực này, được tiên phong bởi Kyle (1985), Glosten and Milgrom (1985)Hasbrouck (2007), cung cấp bộ công cụ phân tích để hiểu lý do tại sao giá quan sát được có thể sai lệch so với giá trị cơ bản và trong bao lâu.

Ở các thị trường phát triển với giao dịch điện tử liên tục, các nhà tạo lập thị trường được chỉ định và các ràng buộc pháp lý tối thiểu đối với biến động giá, ma sát cấu trúc vi mô thường là mối quan tâm thứ hai đối với các nhà nghiên cứu làm việc với tần suất hàng tháng hoặc thấp hơn. Trên thị trường chứng khoán Việt Nam, điều này rõ ràng là không đúng. Giới hạn giá hàng ngày, giao dịch mỏng, cơ sở chủ yếu là nhà đầu tư bán lẻ, kích thước đánh dấu rời rạc và không có các thỏa thuận tạo lập thị trường chính thức tạo ra ma sát lan truyền thành lợi nhuận hàng tháng, bóp méo tải yếu tố và suy luận cấp danh mục đầu tư thiên vị. Do đó, bất kỳ phân tích thực nghiệm nghiêm túc nào về chứng khoán Việt Nam đều phải bắt đầu bằng việc đánh giá cẩn thận cấu trúc vi mô thị trường.

Chương này cung cấp đánh giá đó. Trước tiên, chúng tôi mô tả cấu trúc thể chế của giao dịch chứng khoán Việt Nam. Sau đó, chúng tôi phát triển chẩn đoán cho các ma sát gây hậu quả nhất, chẳng hạn như mức giới hạn giá, ngày không lợi nhuận, kém thanh khoản và giao dịch không đồng bộ, đồng thời định lượng mức độ nghiêm trọng của chúng trong phần của các công ty niêm yết. Cuối cùng, chúng tôi rút ra hướng dẫn thực tế để điều chỉnh xây dựng danh mục đầu tư và kiểm tra định giá tài sản.

4.1 Cấu trúc vi mô thị trường là gì?

Giả định trong sách giáo khoa về thị trường không ma sát ngụ ý rằng giá cả liên tục và không tốn kém kết hợp thông tin. Theo giả định này, lợi nhuận quan sát được trên bất kỳ tài sản nào ở bất kỳ tần suất nào bằng lợi nhuận “thực” được quy định bởi các nguyên tắc cơ bản. Cấu trúc vi mô thị trường nới lỏng giả định này bằng cách nhận ra rằng giá được tạo ra bởi một quy trình giao dịch cụ thể với chi phí, ràng buộc và sự không hoàn hảo thực tế.

Khuôn khổ kinh điển của Kyle (1985) mô hình hóa một thị trường với ba loại người tham gia:

  1. một nhà giao dịch hiểu biết về giá trị cơ bản của tài sản,
  2. Nhà giao dịch tiếng ồn giao dịch vì lý do thanh khoản, và
  3. Một nhà tạo lập thị trường đặt giá hòa vốn trong kỳ vọng.

Thông tin chi tiết quan trọng là nhà tạo lập thị trường không thể phân biệt luồng lệnh được thông báo với luồng lệnh không được thông báo, vì vậy giá điều chỉnh dần dần theo thông tin, tạo ra một khoảng cách giữa giá giao dịch và giá trị cơ bản. Kích thước của nêm này (chênh lệch giá mua-bán) và tốc độ điều chỉnh giá (độ sâu thị trường) là những đối tượng cốt lõi của lý thuyết cấu trúc vi mô.

Glosten and Milgrom (1985) mở rộng khuôn khổ này thành một thiết lập giao dịch tuần tự và cho thấy rằng chênh lệch giá mua-bán có hai thành phần: thành phần lựa chọn bất lợi (bù đắp cho giao dịch chống lại các nhà giao dịch có thông tin) và thành phần xử lý lệnh (bù đắp cho chi phí cơ học của giao dịch). Huang and Stoll (1997) phân tích thêm chênh lệch thành các thành phần chênh lệch thực tế và tác động giá. Những phân hủy này rất quan trọng vì chúng tiết lộ các nguồn chi phí giao dịch khác nhau và có ý nghĩa khác nhau đối với chất lượng thị trường.

Đối với định giá tài sản thực nghiệm, câu hỏi quan trọng là: ở tần suất nào và trong điều kiện nào thì các hiệu ứng cấu trúc vi mô trở nên không đáng kể? Trong các thị trường có tính thanh khoản cao, Bali, Engle, and Murray (2016) lập luận rằng lợi nhuận hàng tháng phần lớn không bị ô nhiễm cấu trúc vi mô. Ở Việt Nam, như chúng tôi trình bày dưới đây, đây không phải là trường hợp. Hiệu ứng cấu trúc vi mô tồn tại ở tần suất hàng tháng và thậm chí hàng quý đối với một phần đáng kể các công ty niêm yết.

4.1.1 Giao diện giữa cấu trúc vi mô và định giá tài sản

Giao diện giữa cấu trúc vi mô và định giá tài sản hoạt động thông qua một số kênh. Thứ nhất, bản thân tính thanh khoản thấp có thể là một yếu tố rủi ro được định giá. Amihud (2002) chỉ ra rằng tính thanh khoản thấp dự kiến ​​có mối quan hệ tích cực với lợi nhuận cổ phiếu dự kiến, ngụ ý một khoản phí bảo hiểm thanh khoản. Pástor and Stambaugh (2003) phát triển một mô hình cân bằng trong đó rủi ro thanh khoản (tức là hiệp phương sai giữa tính thanh khoản của cổ phiếu với tính thanh khoản của thị trường) quyết định một khoản phí bảo hiểm rủi ro. Thứ hai, nhiễu cấu trúc vi mô trong giá cả làm sai lệch các hệ số beta ước tính, hệ số tải nhân tố và thống kê kiểm định. Scholes and Williams (1977) lần đầu tiên xác định sự sai lệch này trong bối cảnh giao dịch không đồng bộ, và Dimson (1979) đề xuất một ước lượng hệ số tổng hợp để hiệu chỉnh nó. Thứ ba, giới hạn giá và các ràng buộc quy định khác kiểm duyệt phân phối lợi nhuận, tạo ra sai lệch cắt cụt trong ước tính biến động, mômen lợi nhuận và thống kê giá trị cực đoan (Kim and Rhee 1997).

Table 4.1 tóm tắt các kênh này và những hệ quả thực nghiệm của chúng.

Table 4.1: Các kênh mà qua đó cấu trúc vi mô ảnh hưởng đến định giá tài sản
Kênh Cơ chế Hậu quả thực nghiệm
Phần bù rủi ro thanh khoản thấp Bồi thường cho việc gánh chịu chi phí giao dịch và rủi ro tồn kho Khả năng dự báo lợi nhuận theo phương pháp đo lường thanh khoản
Giao dịch không đồng bộ Giao dịch không thường xuyên tạo ra giá cả lỗi thời Hệ số beta lệch xuống, tương quan suy yếu và độ trễ-dẫn giả tạo
Giới hạn giá Kiểm duyệt quy định đối với lợi nhuận hàng ngày Phân phối lợi nhuận bị cắt cụt, sự lan truyền biến động và tương quan tự động nhân tạo
Bước giá rời rạc Giá bị giới hạn trong một lưới Biến động giá mua-bán, lợi nhuận rời rạc, biến động có thiên lệch
Thành phần nhà đầu tư Dòng lệnh chủ yếu đến từ nhà đầu tư cá nhân Giao dịch theo cảm tính, hành vi bầy đàn, định giá dựa trên tâm lý thị trường

4.2 Kiến trúc giao dịch tại Việt Nam

Việt Nam có hai sàn giao dịch chứng khoán: Sở Giao dịch Chứng khoán Thành phố Hồ Chí Minh (HOSE), thành lập năm 2000 và Sở Giao dịch Chứng khoán Hà Nội (HNX), thành lập năm 2005. HOSE niêm yết các công ty lớn hơn và chiếm phần lớn vốn hóa thị trường và khối lượng giao dịch. HNX niêm yết các công ty nhỏ hơn và cũng điều hành Thị trường công ty đại chúng chưa niêm yết (UPCoM) cho các doanh nghiệp chưa đáp ứng đầy đủ các yêu cầu niêm yết. Cả ba địa điểm đều vận hành hệ thống sổ lệnh giới hạn điện tử mà không có nhà tạo lập thị trường được chỉ định.

4.2.1 Đặc điểm của Sàn giao dịch

Table 4.2 trình bày sự khác biệt cấu trúc giữa HOSE, HNX và UPCoM. Những khác biệt này có ý nghĩa trực tiếp đến tính thanh khoản, phát hiện giá và mức độ nghiêm trọng của ma sát cấu trúc vi mô.

Table 4.2: So sánh sàn giao dịch: HOSE, HNX và UPCoM
Tính năng VÒI HNX UPCoM
Thành lập 2000 2005 2009
Hạng niêm yết Vốn hóa lớn Vốn hóa vừa/nhỏ Niêm yết trước
Giới hạn giá hàng ngày \(\pm\) 7% \(\pm\) 10% \(\pm\) 15%
Chế độ kích thước tick Phân cấp theo giá Phân cấp theo giá 100 VNĐ
Lô giao dịch 100 cổ phiếu 100 cổ phiếu 100 cổ phiếu
Bán khống Giới hạn Không có sẵn Không có sẵn
Giới hạn sở hữu nước ngoài Ngành cụ thể Ngành cụ thể Ngành cụ thể

Các dải giới hạn giá không đồng nhất trên các sàn giao dịch tạo ra một thí nghiệm tự nhiên để nghiên cứu các hiệu ứng giới hạn. Dải \(\pm\) 7% chặt chẽ hơn của HOSE có nghĩa là cổ phiếu vốn hóa lớn thường xuyên bị hạn chế hơn so với cổ phiếu vốn hóa trung bình trên HNX, với điều kiện cùng một cú sốc thông tin. Dải \(\pm\) 15% rộng hơn của UPCoM cung cấp môi trường ít bị hạn chế nhất, mặc dù cổ phiếu của nó cũng có tính thanh khoản thấp nhất.

4.2.2 Phiên giao dịch

Mỗi sàn giao dịch đều hoạt động theo một ngày giao dịch có cấu trúc với các phiên giao dịch riêng biệt. Hiểu rõ cấu trúc phiên giao dịch là điều cần thiết vì cơ chế hình thành giá khác nhau giữa các phiên, và một số phiên có tầm quan trọng đặc biệt đối với việc định giá chuẩn (Table 4.3).

Table 4.3: Cấu trúc phiên giao dịch trên HOSE
Phiên Thời gian Cơ chế Vai trò khám phá giá
Trước khi khai trương 08:30–09:00 Chỉ nhập lệnh, không khớp Tiết lộ cung / cầu trước khi mở
Mở phiên đấu giá (ATO) 09:00–09:15 Đấu giá hàng loạt, giá đơn Đặt giá mở cửa từ các lệnh tích lũy
Giao dịch liên tục (Buổi sáng) 09:15–11:30 Khớp lệnh giới hạn liên tục Khám phá giá chính
Nghỉ trưa 11:30–13:00 Không giao dịch
Giao dịch liên tục (Buổi chiều) 13:00–14:30 Khớp lệnh giới hạn liên tục Khám phá giá chính
Kết thúc phiên đấu giá (ATC) 14:30–14:45 Đấu giá hàng loạt, giá đơn Đặt giá đóng cửa (điểm chuẩn)
Sau khi kết thúc 14:45–15:00 Giao dịch thỏa thuận (thương lượng) Khối và thương lượng giao dịch

Phiên đấu giá đóng cửa (ATC) cần được đặc biệt chú ý. Giá ATC là giá đóng cửa chính thức được sử dụng để tính toán chỉ số, tính toán giá trị tài sản ròng (NAV) và yêu cầu ký quỹ. Vì nó được xác định bởi một phiên đấu giá duy nhất, nên nó có thể bị thao túng bởi các lệnh được đặt vào thời điểm chiến lược, một hiện tượng đã được ghi nhận ở nhiều thị trường mới nổi (Comerton-Forde and Tang 2009; Hillion and Suominen 2004). Các nhà nghiên cứu sử dụng giá đóng cửa hàng ngày nên lưu ý rằng giá ATC có thể không phản ánh trạng thái cân bằng của phiên giao dịch liên tục, đặc biệt đối với các cổ phiếu kém thanh khoản, nơi một lệnh lớn duy nhất có thể làm thay đổi giá đóng cửa.

4.2.3 Các loại lệnh và quy tắc khớp lệnh

Các sàn giao dịch Việt Nam hỗ trợ một số loại lệnh hạn chế so với các thị trường phát triển (Table 4.4).

Table 4.4: Các loại lệnh có sẵn
Loại lệnh Mô tả Tính khả dụng
Lệnh giới hạn (LO) Chỉ định giá và số lượng Tất cả các phiên
Lệnh thị trường (ATO/ATC) Các trận đấu với giá đấu giá Chỉ phiên đấu giá
Giới hạn thị trường (MTL) Chuyển đổi thành giới hạn ở mức tốt nhất hiện có Chỉ HNX

Việc không có các lệnh iceberg, lệnh dừng và lệnh ẩn đồng nghĩa với việc toàn bộ sổ lệnh giới hạn đều hiển thị cho tất cả người tham gia. Mặc dù điều này tăng cường tính minh bạch trước giao dịch, nhưng nó cũng có nghĩa là các lệnh lớn của tổ chức phải đối mặt với rủi ro rò rỉ thông tin đáng kể, điều này có thể làm giảm sự tham gia của các tổ chức và làm giảm độ sâu của thị trường.

Các lệnh được khớp dựa trên nguyên tắc ưu tiên giá-thời gian nghiêm ngặt trong các phiên giao dịch liên tục. Trong các phiên đấu giá, một giá thanh toán duy nhất được xác định nhằm tối đa hóa khối lượng giao dịch được thực hiện. Nếu nhiều giá thỏa mãn tiêu chí này, giá gần nhất với giá đóng cửa trước đó sẽ được chọn.

4.2.4 Cấu trúc kích thước dấu tích

Bước giá trên HOSE được phân cấp theo mức giá, điều này tạo ra sự gián đoạn trong chênh lệch giá mua-bán tính theo tỷ lệ phần trăm của giá (Table 4.5).

Table 4.5: Tick Size Schedule on HOSE
Price Range (VND) Tick Size (VND) Minimum Spread as % of Midpoint
< 10,000 10 0.10% at 10,000
10,000–49,900 50 0.10% at 50,000
≥ 50,000 100 0.20% at 50,000

Việc nhảy vọt từ mức giá 50 VND lên mức giá 100 VND tại ranh giới 50.000 VND có nghĩa là chênh lệch giá tối thiểu tăng gấp đôi một cách không liên tục. Điều này tạo ra một “vách đá kích thước bước giá” có thể ảnh hưởng đến phân bố giá mua-bán theo mặt cắt ngang và do đó, ảnh hưởng đến việc đo lường tính thanh khoản thấp (Vo and Doan 2023). Bessembinder (2003) đã ghi nhận những hiệu ứng tương tự ở các thị trường khác có cấu trúc bước giá phân cấp.

4.2.5 Investor Composition

The Vietnamese equity market is predominantly driven by retail investors. While foreign institutional investors account for a meaningful share of market capitalization (particularly in blue-chip stocks subject to foreign ownership limits), daily trading volume is overwhelmingly generated by domestic retail accounts.

Sự thống trị của nhà đầu tư cá nhân có một số hệ quả đối với cấu trúc thị trường vi mô. Thứ nhất, các nhà đầu tư cá nhân có xu hướng đặt lệnh nhỏ hơn và giao dịch thường xuyên hơn, tạo ra tỷ lệ lệnh trên giao dịch cao nhưng độ sâu thị trường hạn chế ở mỗi mức giá. Thứ hai, dòng lệnh của nhà đầu tư cá nhân dễ bị ảnh hưởng bởi hiệu ứng đám đông và tâm lý thị trường, điều này có thể khuếch đại động lượng và tạo ra sự biến động quá mức (Barber et al. 2009; Kaniel et al. 2012). Thứ ba, sự hiện diện hạn chế của các tổ chức đồng nghĩa với việc cung cấp thanh khoản tinh vi rất khan hiếm, đặc biệt là đối với các cổ phiếu vốn hóa trung bình và nhỏ.

4.3 Giới hạn giá và hậu quả của chúng

Việt Nam thực thi giới hạn giá hàng ngày đối với tất cả các cổ phiếu niêm yết. Giá cổ phiếu không thể vượt quá tỷ lệ phần trăm cố định của giá đóng cửa ngày hôm trước trong một ngày giao dịch. Các dải giới hạn là \(\pm\) 7% trên HOSE, \(\pm\) 10% trên HNX và \(\pm\) 15% trên UPCoM.

4.3.1 Khung lý thuyết

Giới hạn giá đã được đưa ra với mục tiêu đã nêu là giảm biến động và ngăn chặn sự xáo trộn giá do hoảng loạn. Tuy nhiên, tài liệu học thuật trình bày một bức tranh sắc thái hơn. Giả thuyết “hiệu ứng nam châm” (Subrahmanyam 1994) dự đoán rằng giới hạn giá thực sự đẩy nhanh chuyển động giá về giới hạn khi các nhà giao dịch vội vã thực hiện trước khi đạt đến giới hạn. Giả thuyết “phát hiện giá bị trì hoãn” (Fama and French 1989) lập luận rằng các giới hạn chỉ đơn thuần trì hoãn các điều chỉnh giá không thể tránh khỏi, tạo ra sự biến động lan tỏa vào những ngày tiếp theo.

Về mặt chính thức, giả sử \(P_t^*\) biểu thị giá cân bằng vào ngày \(t\)\(P_{t-1}^c\) giá đóng cửa trước đó. Lợi nhuận quan sát được là:

\[ r_t^{obs} = \begin{cases} \bar{L} & \text{if } r_t^* \geq \bar{L} \\ r_t^* & \text{if } \underline{L} < r_t^* < \bar{L} \\ \underline{L} & \text{if } r_t^* \leq \underline{L} \end{cases} \tag{4.1}\]

trong đó \(r_t^* = \ln(P_t^* / P_{t-1}^c)\) là lợi nhuận tiềm ẩn (không bị ràng buộc), \(\bar{L}\) là giới hạn trên và \(\underline{L}\) là giới hạn dưới. Lợi nhuận quan sát được \(r_t^{obs}\) là phiên bản bị kiểm duyệt của lợi nhuận thực. Việc kiểm duyệt này có một số hệ quả:

  1. Các momen bị cắt cụt: Phương sai quan sát được \(\text{Var}(r_t^{obs}) < \text{Var}(r_t^*)\) vì lợi nhuận cực đoan bị cắt bớt. Điều này làm sai lệch xuống dưới bất kỳ thước đo rủi ro nào dựa trên biến động.

  2. Tự tương quan nhân tạo: Khi \(r_t^{obs} = \bar{L}\)\(r_{t+1}^{obs} > 0\) (tiếp tục điều chỉnh vào ngày hôm sau), chuỗi lợi nhuận thể hiện tự tương quan dương hoàn toàn mang tính cơ học, không mang tính thông tin.

  3. Sự lan tỏa biến động: Định nghĩa biến động vượt mức vào ngày \(t+1\)\(\sigma_{t+1}^2 - E[\sigma_{t+1}^2 | \text{không chạm giới hạn vào ngày } t]\). Kim and Rhee (1997)Chu and Qiu (2019) đã ghi nhận sự lan tỏa tích cực đáng kể, trong đó những ngày sau khi chạm giới hạn cho thấy biến động cao bất thường.

  4. Thống kê giá trị cực đoan bị sai lệch: Các chỉ số như Giá trị rủi ro (Value-at-Risk), Thiếu hụt dự kiến ​​(Expected Shortfall) và mức giảm tối đa (maximum drawdown) bị giới hạn một cách máy móc, đánh giá thấp rủi ro đuôi thực sự.

4.3.2 Phát hiện khi giá chạm giới hạn

Hiện tại, chúng tôi đang triển khai một công cụ chẩn đoán để phát hiện các lần chạm ngưỡng giá trong dữ liệu hàng ngày.

import pandas as pd
import numpy as np
import sqlite3

# Load daily price data
tidy_finance = sqlite3.connect(database="data/tidy_finance_python.sqlite")

# Assume prices_daily contains: symbol, date, close, exchange
prices_daily = pd.read_sql_query(
    # , exchange
    sql="""
        SELECT symbol, date, close
        FROM prices_daily
    """,
    con=tidy_finance,
    parse_dates=["date"]
).dropna()
# Define limit bands by exchange
limit_bands = {"HOSE": 0.07, "HNX": 0.10, "UPCoM": 0.15}

prices_daily = prices_daily.sort_values(["symbol", "date"])
prices_daily["prev_close"] = prices_daily.groupby("symbol")["close"].shift(1)
prices_daily["ret"] = prices_daily["close"] / prices_daily["prev_close"] - 1
prices_daily["limit_band"] = prices_daily["exchange"].map(limit_bands)

# A limit hit occurs when the return is within 0.1% of the theoretical limit
tolerance = 0.001
prices_daily["upper_hit"] = (
    prices_daily["ret"] >= prices_daily["limit_band"] - tolerance
)
prices_daily["lower_hit"] = (
    prices_daily["ret"] <= -prices_daily["limit_band"] + tolerance
)
prices_daily["limit_hit"] = (
    prices_daily["upper_hit"] | prices_daily["lower_hit"]
)

4.3.3 Tần suất đạt giới hạn

prices_daily["year_month"] = prices_daily["date"].dt.to_period("M")

limit_hit_monthly = (
    prices_daily
    .groupby(["year_month", "exchange"])
    .agg(
        total_obs=("limit_hit", "count"),
        limit_hits=("limit_hit", "sum")
    )
    .reset_index()
)
limit_hit_monthly["hit_rate"] = (
    limit_hit_monthly["limit_hits"] / limit_hit_monthly["total_obs"]
)
limit_hit_monthly["date"] = limit_hit_monthly["year_month"].dt.to_timestamp()

fig, ax = plt.subplots(figsize=(8, 4))

for exchange, color in zip(
    ["HOSE", "HNX", "UPCoM"], ["#2C73D2", "#FF6B6B", "#5DCEAF"]
):
    subset = limit_hit_monthly[limit_hit_monthly["exchange"] == exchange]
    ax.plot(
        subset["date"], subset["hit_rate"] * 100,
        label=exchange, color=color, linewidth=1.2
    )

ax.set_ylabel("Limit Hit Rate (%)")
ax.set_xlabel("")
ax.legend(frameon=False)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.tight_layout()
plt.show()
Figure 4.1

4.3.4 Thử nghiệm tràn biến động

Theo Kim and Rhee (1997), chúng tôi kiểm tra xem những ngày sau khi chạm mức trần có biểu hiện biến động cao bất thường hay không. Định nghĩa biến giả \(D_t = 1\) nếu chạm mức trần xảy ra vào ngày \(t\), và ước tính:

\[ \sigma_{t+1}^2 = \alpha + \beta D_t + \gamma \sigma_t^2 + \varepsilon_{t+1} \tag{4.2}\]

trong đó \(\sigma_t^2\) là bình phương lợi nhuận. Hệ số \(\beta\) dương và có ý nghĩa thống kê cho thấy sự lan truyền biến động do giới hạn giá gây ra.

import statsmodels.api as sm

# Panel-level volatility spillover test
prices_daily["sq_ret"] = prices_daily["ret"] ** 2
prices_daily["sq_ret_lead"] = prices_daily.groupby("symbol")["sq_ret"].shift(-1)
prices_daily["limit_hit_int"] = prices_daily["limit_hit"].astype(int)

spillover_data = prices_daily.dropna(subset=["sq_ret_lead", "sq_ret"])

X = sm.add_constant(spillover_data[["limit_hit_int", "sq_ret"]])
y = spillover_data["sq_ret_lead"]

model = sm.OLS(y, X).fit(cov_type="cluster", cov_kwds={"groups": spillover_data["symbol"]})

spillover_results = pd.DataFrame({
    "Coefficient": model.params,
    "Std. Error": model.bse,
    "t-stat": model.tvalues,
    "p-value": model.pvalues
}).round(6)

print(spillover_results)
Tip

Hệ số dương đáng kể của biến giả chạm giới hạn xác nhận rằng giới hạn giá ở Việt Nam không loại bỏ biến động, mà chỉ phân bổ lại biến động đó giữa các ngày. Điều này có tác động trực tiếp đến quản lý rủi ro: các chỉ số VaR hàng ngày được tính toán từ lợi nhuận bị kiểm duyệt đánh giá thấp mức độ rủi ro thực tế.

4.3.5 Hệ số tự tương quan lợi nhuận do giới hạn giá gây ra

Giới hạn giá tạo ra một cách máy móc hiện tượng tự tương quan dương trong lợi nhuận. Để định lượng điều này, chúng tôi tính toán hệ số tự tương quan bậc nhất riêng biệt cho các cổ phiếu thường xuyên chạm giới hạn giá so với các cổ phiếu không thường xuyên chạm giới hạn giá.

Table 4.6: Return Autocorrelation by Price Limit Hit Frequency
# Classify stocks by limit hit frequency
stock_limit_freq = (
    prices_daily
    .groupby("symbol")
    .agg(
        hit_rate=("limit_hit", "mean"),
        n_obs=("ret", "count")
    )
    .query("n_obs >= 250")  # At least 1 year of data
)

stock_limit_freq["limit_group"] = pd.qcut(
    stock_limit_freq["hit_rate"], q=3,
    labels=["Low", "Medium", "High"]
)

# Compute autocorrelation by group
def compute_autocorr(group_symbols):
    subset = prices_daily[prices_daily["symbol"].isin(group_symbols)].copy()
    subset["ret_lag"] = subset.groupby("symbol")["ret"].shift(1)
    return subset[["ret", "ret_lag"]].dropna().corr().iloc[0, 1]

autocorr_results = []
for group in ["Low", "Medium", "High"]:
    symbols = stock_limit_freq[stock_limit_freq["limit_group"] == group].index
    ac = compute_autocorr(symbols)
    n_stocks = len(symbols)
    avg_hit_rate = stock_limit_freq.loc[symbols, "hit_rate"].mean()
    autocorr_results.append({
        "Group": group,
        "N Stocks": n_stocks,
        "Avg Limit Hit Rate (%)": round(avg_hit_rate * 100, 2),
        "AR(1)": round(ac, 4)
    })

pd.DataFrame(autocorr_results).style.hide(axis="index")

Mô hình dự kiến ​​là hệ số tự tương quan tăng dần từ nhóm chạm giới hạn thấp đến nhóm chạm giới hạn cao, xác nhận rằng sự phụ thuộc nối tiếp quan sát được trong lợi nhuận ít nhất một phần là do việc kiểm duyệt giá chứ không phải là khả năng dự đoán lợi nhuận thực sự.

4.4 Thanh khoản thấp, giao dịch thưa thớt và lợi nhuận bằng không

Tính thanh khoản (tức là khả năng giao dịch nhanh chóng với chi phí thấp mà không làm biến động giá) là mối quan ngại hàng đầu trên thị trường chứng khoán Việt Nam. Một phần đáng kể các công ty niêm yết, đặc biệt là trên HNX và UPCoM, đang phải đối mặt với tình trạng thanh khoản thấp kinh niên, thể hiện qua khối lượng giao dịch ít, chênh lệch giá mua bán lớn và thường xuyên có những ngày không sinh lời.

4.4.1 Đo lường tính thanh khoản

Các tài liệu học thuật đã phát triển nhiều thước đo thanh khoản, mỗi thước đo phản ánh một khía cạnh khác nhau của chất lượng thị trường. @#tbl-liquidity-measures tóm tắt các thước đo phù hợp nhất với dữ liệu Việt Nam, dựa trên tính khả dụng của dữ liệu điển hình.

Table 4.7: Các biện pháp đảm bảo thanh khoản cho thị trường chứng khoán Việt Nam
Đo lường Công thức Giải thích Dữ liệu cần thiết
Tỷ lệ vòng quay \(\text{TO}_{i,t} = \frac{\text{Khối lượng}_{i,t}}{\text{Số cổ phiếu đang lưu hành}_{i}}\) Cường độ giao dịch so với số cổ phiếu tự do lưu hành Khối lượng, số cổ phiếu đang lưu hành
Thanh khoản thấp của Amihud \(\text{ILLIQ}_{i,t} = \frac{1}{D} \sum_{d=1}^{D} \frac{|r_{i,d}|}{V_{i,d}}\) Tác động giá trên mỗi đơn vị khối lượng Lợi nhuận hàng ngày, khối lượng hàng ngày
Tỷ lệ lợi nhuận bằng không \(\text{ZR}_{i,t} = \frac{\#\{d : r_{i,d} = 0\}}{D}\) Tần suất giá không giao dịch hoặc lỗi thời Lợi nhuận hàng ngày
Chênh lệch giá mua-bán \(\hat{S}_i = 2\sqrt{-\text{Cov}(r_{i,d}, r_{i,d-1})}\) Ước tính chênh lệch giá mua-bán hiệu quả Lợi nhuận hàng ngày
Chênh lệch giá mua-bán \(\text{BA}_{i,d} = \frac{\text{Ask}_{i,d} - \text{Bid}_{i,d}}{(\text{Ask}_{i,d} + \text{Bid}_{i,d})/2}\) Chi phí giao dịch trực tiếp Dữ liệu báo giá

Chỉ số thanh khoản thấp Amihud (Amihud 2002) đặc biệt hữu ích vì nó chỉ yêu cầu dữ liệu lợi nhuận và khối lượng giao dịch hàng ngày. Nó nắm bắt được tác động của giao dịch lên giá (tức là lợi nhuận trên mỗi đơn vị khối lượng tiền tệ) và đã được chứng minh là có mối tương quan tốt với các biện pháp dựa trên cấu trúc vi mô phức tạp hơn như chênh lệch giá hiệu quả Goyenko and Ukhov (2009).

4.4.2 Xác định khả năng thanh khoản

# Compute standard liquidity measures at the stock-month level
prices_daily["abs_ret"] = prices_daily["ret"].abs()
prices_daily["zero_return"] = (prices_daily["ret"] == 0).astype(int)
prices_daily["year_month"] = prices_daily["date"].dt.to_period("M")

# Assume volume is in shares and value is in VND
# Amihud: average |ret| / value (in billions VND)
prices_daily["amihud_daily"] = np.where(
    prices_daily["value"] > 0,
    prices_daily["abs_ret"] / (prices_daily["value"] / 1e9),
    np.nan
)

liquidity_monthly = (
    prices_daily
    .groupby(["symbol", "year_month"])
    .agg(
        zero_return_share=("zero_return", "mean"),
        avg_turnover=("turnover", "mean"),
        amihud=("amihud_daily", "mean"),
        trading_days=("ret", "count"),
        avg_daily_value=("value", "mean")
    )
    .reset_index()
)

# Flag severely illiquid stock-months
liquidity_monthly["illiquid_flag"] = (
    (liquidity_monthly["zero_return_share"] > 0.5) |
    (liquidity_monthly["trading_days"] < 10) |
    (liquidity_monthly["avg_daily_value"] < 1e8)  # < 100M VND/day
)

4.4.3 Phân phối thanh khoản theo mặt cắt ngang

Table 4.8: Cross-Sectional Distribution of Liquidity Measures (Latest Full Year)
latest_year = liquidity_monthly["year_month"].dt.year.max()
annual_liq = (
    liquidity_monthly[liquidity_monthly["year_month"].dt.year == latest_year]
    .groupby("symbol")
    .agg(
        zero_return_share=("zero_return_share", "mean"),
        avg_turnover=("avg_turnover", "mean"),
        amihud=("amihud", "mean"),
        avg_daily_value_m=("avg_daily_value", lambda x: x.mean() / 1e6)
    )
)

summary_stats = annual_liq.describe(percentiles=[0.10, 0.25, 0.50, 0.75, 0.90]).T
summary_stats = summary_stats[
    ["mean", "std", "10%", "25%", "50%", "75%", "90%"]
].round(4)
summary_stats.columns = ["Mean", "Std", "P10", "P25", "Median", "P75", "P90"]
summary_stats.index = [
    "Zero-Return Share",
    "Avg Daily Turnover",
    "Amihud Illiquidity",
    "Avg Daily Value (M VND)"
]
summary_stats

4.4.4 Phân phối thanh khoản trên các sàn giao dịch

# Merge exchange info
stock_exchange = (
    prices_daily[["symbol", "exchange"]]
    .drop_duplicates("symbol")
)
annual_liq_exch = annual_liq.merge(
    stock_exchange, left_index=True, right_on="symbol"
)

fig, axes = plt.subplots(1, 2, figsize=(10, 4))

# Zero-return share
for exchange, color in zip(
    ["HOSE", "HNX", "UPCoM"], ["#2C73D2", "#FF6B6B", "#5DCEAF"]
):
    subset = annual_liq_exch[annual_liq_exch["exchange"] == exchange]
    axes[0].hist(
        subset["zero_return_share"], bins=30, alpha=0.6,
        color=color, label=exchange, density=True
    )
axes[0].set_xlabel("Zero-Return Share")
axes[0].set_ylabel("Density")
axes[0].legend(frameon=False)
axes[0].spines["top"].set_visible(False)
axes[0].spines["right"].set_visible(False)

# Amihud (log scale)
for exchange, color in zip(
    ["HOSE", "HNX", "UPCoM"], ["#2C73D2", "#FF6B6B", "#5DCEAF"]
):
    subset = annual_liq_exch[annual_liq_exch["exchange"] == exchange]
    amihud_log = np.log(subset["amihud"].clip(lower=1e-10))
    axes[1].hist(
        amihud_log, bins=30, alpha=0.6,
        color=color, label=exchange, density=True
    )
axes[1].set_xlabel("Log Amihud Illiquidity")
axes[1].set_ylabel("Density")
axes[1].legend(frameon=False)
axes[1].spines["top"].set_visible(False)
axes[1].spines["right"].set_visible(False)

plt.tight_layout()
plt.show()
Figure 4.2

Các phân bố thường cho thấy mô hình hai đỉnh: cổ phiếu HOSE tập trung ở các giá trị thanh khoản thấp, trong khi cổ phiếu HNX và đặc biệt là cổ phiếu UPCoM thể hiện phần đuôi bên phải dài với thanh khoản cực thấp. Sự không đồng nhất này ngụ ý rằng một bộ lọc hoặc phương pháp xử lý thanh khoản duy nhất là không đủ cho toàn bộ mặt cắt ngang.

4.4.5 Biến động theo thời gian của tổng thanh khoản

Tính thanh khoản trên toàn thị trường không ổn định. Nó xấu đi trong các cuộc khủng hoảng, sự bất ổn về chính sách và các giai đoạn dòng vốn chảy ra, và cải thiện trong các thị trường tăng giá và các giai đoạn dòng vốn nước ngoài chảy vào. Sự biến động theo thời gian của tổng thanh khoản tự nó là một yếu tố rủi ro (Pástor and Stambaugh 2003).

agg_liquidity = (
    liquidity_monthly
    .groupby("year_month")
    .agg(
        median_amihud=("amihud", "median"),
        median_zero_ret=("zero_return_share", "median"),
        total_value=("avg_daily_value", "sum")
    )
    .reset_index()
)
agg_liquidity["date"] = agg_liquidity["year_month"].dt.to_timestamp()

fig, ax1 = plt.subplots(figsize=(8, 4))

ax1.plot(
    agg_liquidity["date"],
    np.log(agg_liquidity["median_amihud"].clip(lower=1e-10)),
    color="#2C73D2", linewidth=1.2
)
ax1.set_ylabel("Log Median Amihud", color="#2C73D2")
ax1.tick_params(axis="y", labelcolor="#2C73D2")

ax2 = ax1.twinx()
ax2.fill_between(
    agg_liquidity["date"],
    agg_liquidity["median_zero_ret"] * 100,
    alpha=0.3, color="#FF6B6B"
)
ax2.set_ylabel("Median Zero-Return Share (%)", color="#FF6B6B")
ax2.tick_params(axis="y", labelcolor="#FF6B6B")

ax1.spines["top"].set_visible(False)
plt.tight_layout()
plt.show()
Figure 4.3
ImportantKhuyến nghị thực tế

Trước khi tiến hành bất kỳ phân tích định giá tài sản nào, hãy áp dụng bộ lọc thanh khoản sau: loại trừ các tháng giao dịch có tỷ lệ cổ phiếu không sinh lời vượt quá 50%, có ít hơn 15 ngày giao dịch, hoặc có giá trị giao dịch trung bình hàng ngày thấp hơn một ngưỡng nhất định (ví dụ: 100 triệu VND). Ghi chép rõ ràng bộ lọc này và báo cáo độ nhạy của kết quả đối với các ngưỡng thay thế.

4.5 Ước tính chênh lệch giá mua-bán

Trong trường hợp thiếu dữ liệu báo giá đầy đủ, chênh lệch giá mua-bán hiệu quả có thể được ước tính từ dữ liệu giao dịch bằng phương pháp của Roll (1984). Bộ ước tính Roll khai thác thực tế rằng nếu sự dao động giá mua-bán là nguồn duy nhất gây ra hiệp phương sai nối tiếp âm trong lợi nhuận, thì:

\[ \hat{S}_{\text{Roll}} = 2\sqrt{-\text{Cov}(\Delta p_t, \Delta p_{t-1})} \tag{4.3}\]

trong đó \(\Delta p_t = p_t - p_{t-1}\) là sự thay đổi giá. Khi hệ số tự tương quan dương (điều này xảy ra khi tương quan chuỗi dựa trên thông tin chi phối sự dao động giá mua-bán), ước lượng Roll không xác định. Hasbrouck (2009) đề xuất một biến thể Bayes xử lý trường hợp này bằng cách áp đặt một phân bố tiên nghiệm lên chênh lệch giá.

# Compute Roll spread estimate at the stock-month level
prices_daily["dprice"] = prices_daily.groupby("symbol")["close"].diff()
prices_daily["dprice_lag"] = prices_daily.groupby("symbol")["dprice"].shift(1)

roll_cov = (
    prices_daily
    .groupby(["symbol", "year_month"])
    .apply(
        lambda g: g[["dprice", "dprice_lag"]].dropna().cov().iloc[0, 1],
        include_groups=False
    )
    .reset_index(name="autocovariance")
)

# Roll spread is defined only when autocovariance is negative
roll_cov["roll_spread"] = np.where(
    roll_cov["autocovariance"] < 0,
    2 * np.sqrt(-roll_cov["autocovariance"]),
    np.nan
)

# As a percentage of price
roll_cov = roll_cov.merge(
    prices_daily.groupby(["symbol", "year_month"])["close"].mean()
    .reset_index(name="avg_price"),
    on=["symbol", "year_month"]
)
roll_cov["roll_spread_pct"] = roll_cov["roll_spread"] / roll_cov["avg_price"] * 100
Table 4.9: Distribution of Roll Spread Estimates (% of Price)
roll_summary = (
    roll_cov
    .dropna(subset=["roll_spread_pct"])
    .groupby("year_month")["roll_spread_pct"]
    .describe(percentiles=[0.25, 0.50, 0.75])
    .reset_index()
)

# Show latest year summary
latest_year_roll = roll_cov[
    roll_cov["year_month"].dt.year == roll_cov["year_month"].dt.year.max()
]
print(
    latest_year_roll["roll_spread_pct"]
    .dropna()
    .describe(percentiles=[0.10, 0.25, 0.50, 0.75, 0.90])
    .round(3)
)

4.6 Thiên kiến ​​giao dịch không đồng bộ

Khi cổ phiếu không được giao dịch với tần suất hoặc thời điểm giống nhau, lợi nhuận quan sát được sẽ không đồng bộ. Sai lệch do giao dịch không đồng bộ này, lần đầu tiên được Scholes and Williams (1977)Lo and MacKinlay (1990) chính thức hóa, là một trong những hiệu ứng vi mô quan trọng nhất đối với định giá tài sản trong các thị trường có nguồn cung ít.

4.6.1 Vấn đề

Giả sử quá trình lợi nhuận thực tế (không quan sát được) của cổ phiếu \(i\) tuân theo mô hình một yếu tố:

\[ r_{i,t}^* = \alpha_i + \beta_i r_{m,t}^* + \varepsilon_{i,t} \tag{4.4}\]

trong đó \(r_{m,t}^*\) là lợi suất thị trường thực và \(\beta_i\) là hệ số beta thực. Nếu cổ phiếu \(i\) được giao dịch lần cuối cách ngày \(t\) k ngày, thì lợi suất quan sát được chỉ bao gồm thông tin đến ngày \(t - k\). Scholes and Williams (1977) cho thấy rằng ước tính OLS của beta từ việc hồi quy lợi suất quan sát được trên lợi suất thị trường quan sát được là:

\[ \hat{\beta}_i^{OLS} = \beta_i \cdot \pi_i \tag{4.5}\]

trong đó \(\pi_i\) là xác suất mà cổ phiếu \(i\) giao dịch vào bất kỳ ngày nào. Đối với một cổ phiếu chỉ giao dịch trong 50% ngày, OLS beta bị nghiêng xuống 50%. Sự thiên vị này rất nghiêm trọng ở Việt Nam, nơi nhiều cổ phiếu vốn hóa nhỏ giao dịch ít hơn một nửa số ngày giao dịch.

4.6.2 Định lượng sai lệch

# Compute trading frequency: proportion of market days with nonzero volume
market_days = prices_daily.groupby("year_month")["date"].nunique()
trading_freq = (
    prices_daily[prices_daily["value"] > 0]
    .groupby(["symbol", "year_month"])["date"]
    .nunique()
    .reset_index(name="days_traded")
)
trading_freq = trading_freq.merge(
    market_days.reset_index().rename(columns={"date": "market_days"}),
    on="year_month"
)
trading_freq["trade_prob"] = trading_freq["days_traded"] / trading_freq["market_days"]

# Annual average
annual_trade_freq = (
    trading_freq
    .groupby("symbol")["trade_prob"]
    .mean()
    .reset_index(name="avg_trade_prob")
)

fig, ax = plt.subplots(figsize=(7, 4))
ax.hist(
    annual_trade_freq["avg_trade_prob"], bins=50,
    color="#2C73D2", edgecolor="white", alpha=0.8
)
ax.axvline(
    annual_trade_freq["avg_trade_prob"].median(),
    color="#FF6B6B", linestyle="--", linewidth=1.5,
    label=f"Median = {annual_trade_freq['avg_trade_prob'].median():.2f}"
)
ax.set_xlabel("Average Trading Probability (Fraction of Market Days)")
ax.set_ylabel("Number of Stocks")
ax.legend(frameon=False)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.tight_layout()
plt.show()
Figure 4.4

4.6.3 Hiệu chỉnh Dimson Beta

Dimson (1979) đề xuất một sự điều chỉnh đơn giản: bao gồm lợi nhuận thị trường trễ và dẫn đầu trong hồi quy beta:

\[ r_{i,t} = \alpha_i + \sum_{k=-K}^{K} \beta_{i,k} \, r_{m,t-k} + \varepsilon_{i,t} \tag{4.6}\]

Hệ số beta hiệu chỉnh Dimson là \(\hat{\beta}_i^{Dimson} = \sum_{k=-K}^{K} \hat{\beta}_{i,k}\). Thông thường, \(K = 1\) hoặc \(K = 2\) là đủ. Tổng các hệ số này thể hiện đầy đủ phản ứng của lợi nhuận quan sát được của cổ phiếu đối với thông tin thị trường, bất kể thời điểm giao dịch thực tế của cổ phiếu.

# Estimate Dimson betas with K=1 lag and lead
# Merge market return
market_ret = (
    prices_daily
    .groupby("date")
    .apply(
        lambda g: np.average(g["ret"].dropna(), weights=g["mktcap"].loc[g["ret"].dropna().index])
        if g["ret"].dropna().shape[0] > 0 else np.nan,
        include_groups=False
    )
    .reset_index(name="rm")
)

prices_daily = prices_daily.merge(market_ret, on="date", how="left")
prices_daily["rm_lag1"] = prices_daily.groupby("symbol")["rm"].shift(1)
prices_daily["rm_lead1"] = prices_daily.groupby("symbol")["rm"].shift(-1)

def estimate_dimson_beta(group):
    """Estimate OLS and Dimson(K=1) betas for a single stock."""
    g = group.dropna(subset=["ret", "rm", "rm_lag1", "rm_lead1"])
    if len(g) < 60:
        return pd.Series({"beta_ols": np.nan, "beta_dimson": np.nan, "n_obs": len(g)})

    # OLS beta
    X_ols = sm.add_constant(g["rm"])
    ols_model = sm.OLS(g["ret"], X_ols).fit()
    beta_ols = ols_model.params["rm"]

    # Dimson beta
    X_dim = sm.add_constant(g[["rm_lag1", "rm", "rm_lead1"]])
    dim_model = sm.OLS(g["ret"], X_dim).fit()
    beta_dimson = dim_model.params[["rm_lag1", "rm", "rm_lead1"]].sum()

    return pd.Series({
        "beta_ols": beta_ols,
        "beta_dimson": beta_dimson,
        "n_obs": len(g)
    })

beta_comparison = (
    prices_daily
    .groupby("symbol")
    .apply(estimate_dimson_beta, include_groups=False)
    .reset_index()
)
beta_valid = beta_comparison.dropna()

fig, ax = plt.subplots(figsize=(6, 6))
ax.scatter(
    beta_valid["beta_ols"], beta_valid["beta_dimson"],
    alpha=0.3, s=10, color="#2C73D2"
)
lims = [
    min(ax.get_xlim()[0], ax.get_ylim()[0]),
    max(ax.get_xlim()[1], ax.get_ylim()[1])
]
ax.plot(lims, lims, "--", color="gray", linewidth=1)
ax.set_xlabel("OLS Beta")
ax.set_ylabel("Dimson Beta (K=1)")
ax.set_aspect("equal")
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.tight_layout()
plt.show()
Figure 4.5

Biểu đồ phân tán sẽ tiết lộ một mô hình có hệ thống: Dimson beta vượt quá OLS beta đối với hầu hết các cổ phiếu, với sự khác biệt lớn nhất đối với các cổ phiếu được giao dịch mỏng. Các điểm trên đường 45 độ cho biết các cổ phiếu có OLS beta bị lệch xuống do giao dịch không đồng bộ.

Table 4.10: Beta Bias by Trading Frequency Tercile
beta_with_freq = beta_valid.merge(annual_trade_freq, on="symbol")
beta_with_freq["freq_tercile"] = pd.qcut(
    beta_with_freq["avg_trade_prob"], q=3,
    labels=["Low (Thin)", "Medium", "High (Liquid)"]
)

beta_bias_summary = (
    beta_with_freq
    .groupby("freq_tercile")
    .agg(
        n_stocks=("symbol", "count"),
        avg_trade_prob=("avg_trade_prob", "mean"),
        mean_beta_ols=("beta_ols", "mean"),
        mean_beta_dimson=("beta_dimson", "mean"),
        median_beta_ols=("beta_ols", "median"),
        median_beta_dimson=("beta_dimson", "median")
    )
    .round(3)
)

beta_bias_summary["bias_pct"] = (
    (beta_bias_summary["mean_beta_dimson"] - beta_bias_summary["mean_beta_ols"])
    / beta_bias_summary["mean_beta_dimson"] * 100
).round(1)

beta_bias_summary
Warning

Đối với nhóm cổ phiếu có khối lượng giao dịch thấp nhất (phân vị thứ ba), hệ số beta OLS ước tính thấp hơn rủi ro hệ thống thực tế trung bình từ 20-40%. Việc sử dụng hệ số beta chưa hiệu chỉnh để ước tính chi phí vốn chủ sở hữu hoặc kiểm định mô hình nhân tố sẽ dẫn đến kết quả không chính xác một cách có hệ thống đối với các cổ phiếu này.

4.6.4 Công thức ước lượng Scholes-Williams

Một phương pháp hiệu chỉnh khác, được đề xuất bởi Scholes and Williams (1977), ước tính hệ số beta như sau:

\[ \hat{\beta}_i^{SW} = \frac{\hat{\beta}_{i,-1} + \hat{\beta}_{i,0} + \hat{\beta}_{i,+1}}{1 + 2\hat{\rho}_m} \tag{4.7}\]

trong đó \(\hat{\beta}_{i,k}\) là độ dốc thu được từ việc hồi quy \(r_{i,t}\) chỉ dựa trên \(r_{m,tk}\), và \(\hat{\rho}_m\) là hệ số tự tương quan bậc nhất của lợi nhuận thị trường. Ước lượng Scholes-Williams nhất quán với giả định rằng phi giao dịch là nguồn duy nhất của tương quan chéo nối tiếp, trong khi ước lượng Dimson mạnh mẽ hơn đối với các nguồn cấu trúc dẫn-trễ bổ sung.

def estimate_sw_beta(group):
    """Estimate Scholes-Williams beta."""
    g = group.dropna(subset=["ret", "rm", "rm_lag1", "rm_lead1"])
    if len(g) < 60:
        return np.nan

    # Separate regressions
    beta_lag = sm.OLS(g["ret"], sm.add_constant(g["rm_lag1"])).fit().params.iloc[1]
    beta_0 = sm.OLS(g["ret"], sm.add_constant(g["rm"])).fit().params.iloc[1]
    beta_lead = sm.OLS(g["ret"], sm.add_constant(g["rm_lead1"])).fit().params.iloc[1]

    # Market autocorrelation
    rho_m = g["rm"].autocorr(lag=1)

    beta_sw = (beta_lag + beta_0 + beta_lead) / (1 + 2 * rho_m)
    return beta_sw

beta_comparison["beta_sw"] = (
    prices_daily
    .groupby("symbol")
    .apply(estimate_sw_beta, include_groups=False)
    .values
)

4.7 Ý nghĩa đối với việc xây dựng danh mục đầu tư

Những bất cập trong cấu trúc danh mục đầu tư được nêu trên có ảnh hưởng trực tiếp đến việc xây dựng danh mục đầu tư, đặc biệt là đối với các chiến lược liên quan đến việc tái cân bằng trên toàn bộ các công ty niêm yết.

4.7.1 Lợi nhuận tính theo trọng số bằng nhau so với lợi nhuận tính theo trọng số giá trị

Lợi nhuận danh mục đầu tư theo phương pháp trọng số bằng nhau gán trọng số như nhau cho mỗi cổ phiếu, bao gồm cả các cổ phiếu vốn hóa nhỏ kém thanh khoản có thể gây ra biến động giá hoặc lỗi thời. Lợi nhuận theo phương pháp trọng số giá trị nghiêng về các cổ phiếu lớn, có tính thanh khoản cao và ít bị ảnh hưởng bởi cấu trúc thị trường nhỏ.

monthly_returns = (
    prices_daily
    .groupby(["symbol", "year_month"])
    .agg(
        monthly_ret=("ret", lambda x: (1 + x).prod() - 1),
        last_mktcap=("mktcap", "last")
    )
    .reset_index()
)
monthly_returns["date"] = monthly_returns["year_month"].dt.to_timestamp()

# Equal-weighted
ew_ret = monthly_returns.groupby("date")["monthly_ret"].mean().reset_index(name="ew")

# Value-weighted
def vw_return(group):
    w = group["last_mktcap"] / group["last_mktcap"].sum()
    return (w * group["monthly_ret"]).sum()

vw_ret = (
    monthly_returns.groupby("date")
    .apply(vw_return, include_groups=False)
    .reset_index(name="vw")
)

port_comp = ew_ret.merge(vw_ret, on="date")

fig, ax = plt.subplots(figsize=(8, 4))
for col, label, color in [
    ("ew", "Equal-Weighted", "#FF6B6B"),
    ("vw", "Value-Weighted", "#2C73D2")
]:
    cum_ret = (1 + port_comp[col]).cumprod()
    ax.plot(port_comp["date"], cum_ret, label=label, color=color, linewidth=1.2)

ax.set_ylabel("Cumulative Return (Growth of 1 VND)")
ax.set_xlabel("")
ax.legend(frameon=False)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.tight_layout()
plt.show()
Figure 4.6

Sự khác biệt dai dẳng giữa lợi nhuận tích lũy được tính theo trọng số bằng nhau và lợi nhuận tích lũy được tính theo trọng số giá trị là đặc điểm nổi bật của các hiệu ứng cấu trúc vi mô: danh mục đầu tư được tính theo trọng số bằng nhau đánh giá quá cao lợi nhuận có thể đạt được vì nó ngầm giả định việc giao dịch không tốn chi phí đối với các cổ phiếu kém thanh khoản.

4.7.2 Các bộ lọc thanh khoản được đề xuất

Dựa trên các phương pháp chẩn đoán được phát triển trong chương này, chúng tôi đề xuất các bộ lọc tiền phân tích sau:

Tip

Luôn báo cáo kết quả có và không có bộ lọc thanh khoản. Nếu kết quả khác nhau về mặt định tính, thì các phát hiện cơ bản có thể bị ảnh hưởng bởi các yếu tố cấu trúc vi mô hơn là các tác động kinh tế thực sự.

4.7.3 Tần suất hàng tháng so với tần suất hàng ngày

Đối với hầu hết các ứng dụng định giá tài sản, việc tổng hợp lợi nhuận hàng tháng được ưu tiên hơn so với phân tích hàng ngày tại Việt Nam vì:

  1. Lợi nhuận hàng tháng giúp làm giảm bớt sự biến động trong ngày, sự dao động giá mua-bán và hiệu ứng giới hạn giá.
  2. Những cổ phiếu có khối lượng giao dịch không thường xuyên trong một tháng vẫn mang lại lợi nhuận hàng tháng đáng kể.
  3. Việc phân loại danh mục đầu tư theo yếu tố thường được thực hiện hàng tháng.
  4. Các phép thử thống kê có đặc tính kích thước tốt hơn khi nhiễu cấu trúc vi mô được giảm thiểu.

Tuy nhiên, việc tổng hợp dữ liệu hàng tháng không loại bỏ được tất cả các sai lệch. Các cổ phiếu có lợi nhuận bằng không trong cả một tháng vẫn đóng góp vào các quan sát lỗi thời. Các hiệu chỉnh Dimson và Scholes-Williams vẫn nên được áp dụng hàng tháng để ước tính hệ số beta.

4.8 Ý nghĩa đối với các bài kiểm tra định giá tài sản

4.8.1 Ước lượng mô hình nhân tố

Phương pháp ước lượng mô hình nhân tố tiêu chuẩn giả định rằng lợi nhuận được quan sát đồng bộ và không bị kiểm duyệt. Tại Việt Nam, cả hai giả định này đều bị vi phạm. Hậu quả thực tiễn được nêu trong Table 4.11

Table 4.11: Các giả định tiêu chuẩn và vi phạm của chúng
Giả định Vi phạm tại Việt Nam Hậu quả
Quan sát đồng bộ Giao dịch mỏng Beta thiên vị, R² suy giảm
Trả lại không kiểm duyệt Giới hạn giá Phân phối bị cắt bớt, thời điểm thiên vị
Giao dịch liên tục Bọ ve rời rạc Trả lại sự rời rạc, trả lại giá mua-bán
Không có chi phí giao dịch Chênh lệch rộng Lợi nhuận danh mục đầu tư bị phóng đại

4.8.2 Quy trình kiểm tra được điều chỉnh

Chúng tôi khuyến nghị điều chỉnh các tiêu chuẩn kiểm định giá tài sản thông thường khi áp dụng cho dữ liệu Việt Nam như sau:

  1. Ước lượng hệ số Beta: Sử dụng hệ số beta Dimson (\(K \ge 1\)) hoặc Scholes-Williams, không sử dụng hệ số beta OLS.

  2. Xây dựng yếu tố: Khi xây dựng danh mục đầu tư theo quy mô và giá trị, hãy áp dụng các bộ lọc thanh khoản trước khi sắp xếp. Cân nhắc loại trừ nhóm 20% cổ phiếu nhỏ nhất theo vốn hóa thị trường, nhóm này bị ảnh hưởng nhiều nhất bởi giao dịch thưa thớt.

  3. Tổng hợp lợi nhuận: Sử dụng tần suất hàng tháng. Nếu cần phân tích hàng ngày, hãy bao gồm lợi nhuận thị trường trễ trong hồi quy chuỗi thời gian.

  4. Suy luận mạnh mẽ: Gom nhóm sai số chuẩn theo cổ phiếu để tính đến tương quan chuỗi do cấu trúc vi mô gây ra. Sử dụng sai số chuẩn HAC của Newey-West với độ trễ đủ lớn.

  5. Điều chỉnh giới hạn giá: Để phân tích biến động hoặc đo lường rủi ro, hãy xem xét phương pháp dự báo của Chu and Qiu (2019) về việc mô hình hóa phân phối lợi nhuận tiềm ẩn (không bị kiểm duyệt) bằng cách sử dụng hồi quy bị cắt cụt:

\[ r_{i,t}^* \sim N(\mu_i, \sigma_i^2), \quad r_{i,t}^{obs} = \max(\underline{L}, \min(\bar{L}, r_{i,t}^*)) \tag{4.8}\]

Ước tính \(\mu_i\)\(\sigma_i^2\) bằng phương pháp hợp lý tối đa cho phân phối chuẩn bị cắt cụt.

from scipy.optimize import minimize
from scipy.stats import norm

def truncated_normal_nll(params, returns, lower, upper):
    """Negative log-likelihood of truncated normal."""
    mu, log_sigma = params
    sigma = np.exp(log_sigma)

    # Interior observations
    interior = (returns > lower) & (returns < upper)
    ll_interior = norm.logpdf(returns[interior], mu, sigma)

    # Lower censored
    ll_lower = norm.logcdf(lower, mu, sigma)
    n_lower = (returns <= lower).sum()

    # Upper censored
    ll_upper = np.log(1 - norm.cdf(upper, mu, sigma) + 1e-15)
    n_upper = (returns >= upper).sum()

    nll = -(ll_interior.sum() + n_lower * ll_lower + n_upper * ll_upper)
    return nll

def estimate_true_volatility(returns, limit_band):
    """Estimate latent volatility correcting for price limit censoring."""
    result = minimize(
        truncated_normal_nll,
        x0=[returns.mean(), np.log(returns.std())],
        args=(returns.values, -limit_band, limit_band),
        method="Nelder-Mead"
    )
    mu, log_sigma = result.x
    return np.exp(log_sigma)
  1. Báo cáo độ nhạy: Luôn báo cáo các kết quả chính theo các thông số kỹ thuật thay thế: có và không có bộ lọc thanh khoản, sử dụng hệ số beta OLS so với Dimson, ở tần suất hàng ngày so với hàng tháng và sử dụng độ biến động quan sát được so với độ biến động đã được hiệu chỉnh cắt cụt.

4.9 Tổng kết

Chương này đã chỉ ra rằng thị trường chứng khoán Việt Nam thể hiện những đặc điểm vi cấu trúc ảnh hưởng đáng kể đến giá cả, lợi nhuận và các chỉ số rủi ro. Những phát hiện chính là:

  1. Giới hạn giá hạn chế lợi nhuận hàng ngày, gây ra tương quan dương, lan truyền biến động và phân phối bị cắt cụt. Giới hạn ±7% trên HOSE đặc biệt hạn chế đối với các cổ phiếu có độ biến động cao.

  2. Giao dịch thưa thớt và lợi nhuận bằng không ảnh hưởng đến một phần đáng kể các công ty niêm yết. Xác suất giao dịch dưới 50% là phổ biến trên HNX và UPCoM, tạo ra sai lệch giao dịch không đồng bộ làm giảm ước tính hệ số beta OLS từ 20-40%.

  3. Tính thanh khoản thấp biến động mạnh mẽ giữa các danh mục đầu tư, với tỷ lệ Amihud trải rộng trên nhiều bậc độ lớn. Lợi nhuận danh mục đầu tư được tính theo giá trị ít bị ảnh hưởng bởi rủi ro hơn so với lợi nhuận được tính theo trọng số bằng nhau.

  4. Các phương pháp hiệu chỉnh beta Dimson và Scholes-Williams giải quyết hiệu quả sai lệch giao dịch không đồng bộ và nên được sử dụng làm phương pháp ước tính beta mặc định cho cổ phiếu Việt Nam.

  5. Các bộ lọc thanh khoản nên được áp dụng trước bất kỳ phân tích định giá tài sản nào, và kết quả nên được báo cáo cả khi có và không có các bộ lọc này như một bước kiểm tra độ tin cậy.

Việc bỏ qua những trở ngại này không chỉ làm tăng thêm nhiễu cho kết quả thực nghiệm, mà còn làm sai lệch các ước tính một cách có hệ thống theo những hướng có thể dự đoán được. Các chẩn đoán và hiệu chỉnh được trình bày trong chương này cung cấp nền tảng cho việc định giá tài sản thực nghiệm đáng tin cậy tại Việt Nam.