Vi cấu trúc thị trường
Mục lục
Tính năng trên trang này đang trong giai đoạn phát triển thử nghiệm và chưa ổn định. Bạn dùng được để tìm hiểu và chạy thử, nhưng tên hàm hay công cụ, tham số và cấu trúc kết quả có thể còn đổi giữa các bản phát hành, cách tính và cách xử lý cũng có thể còn được hiệu chỉnh.
Hãy đối chiếu kết quả với một cách tính độc lập trước khi đưa vào quy trình nghiên cứu, và ghim phiên bản thư viện trong dự án để mã đang chạy không tự đổi theo bản mới.
vnstock_quant.micro tính trên dữ liệu khớp lệnh từng lệnh (tick): gom thành nến theo thời gian, số lệnh, khối lượng hoặc giá trị; xác định bên chủ động; khối lượng mua trừ bán cộng dồn (CVD); VPIN; biến động thực hiện. Cần cấp quyền sử dụng từ Bronze, cùng nhóm quyền risk (Quyền dùng và hạn mức).
Hàm không phụ thuộc nguồn. Cùng một cách gọi cho:
| Dữ liệu | Cột thường có | Bên chủ động |
|---|---|---|
Khớp lệnh trong phiên cổ phiếu Việt Nam (Market().equity(...).trades()) | time, price, volume, match_type, id | match_type là Buy hoặc Sell; ATO, ATC không có bên |
Hợp đồng tương lai (Market().futures("VN30F1M").trades()) | như trên | như trên |
Luồng giao dịch tài sản số lưu bằng vnstock_pipeline | time, price, quantity, buyer_is_maker | buyer_is_maker=True nghĩa là bên bán chủ động |
| Nguồn không có bên | time, price, volume | suy bằng tick rule |
Dữ liệu mẫu
Ví dụ dùng một phiên tổng hợp có hình dạng của dữ liệu khớp lệnh HOSE (bước giá 50 đồng, phiên ATO, nghỉ trưa, ATC), chạy được không cần mạng.
import numpy as np
import pandas as pd
from vnstock_quant import micro
rng = np.random.default_rng(7)
day = "2026-10-13"
tz = "Asia/Ho_Chi_Minh"
morning = pd.date_range(f"{day} 09:15:01", f"{day} 11:30", tz=tz, periods=12_000)
afternoon = pd.date_range(f"{day} 13:00", f"{day} 14:29:59", tz=tz, periods=8_000)
time = pd.DatetimeIndex([pd.Timestamp(f"{day} 09:15", tz=tz), *morning, *afternoon, pd.Timestamp(f"{day} 14:45", tz=tz)])
price = 25_000 + np.cumsum(rng.choice([-50, 0, 0, 50], size=len(time))).astype(float)
side = np.where(rng.random(len(time)) < 0.52, "Buy", "Sell").astype(object)
side[0], side[-1] = "ATO", "ATC"
volume = rng.integers(1, 200, size=len(time)) * 100.0
volume[0], volume[-1] = 250_000.0, 400_000.0
ticks = pd.DataFrame({"time": time, "price": price, "volume": volume, "match_type": side})
len(ticks)Với dữ liệu thật:
from vnstock import Market
ticks = Market().equity("FPT").trades(limit=1000) # tối đa 1.000 lệnh mỗi lần gọi
futures = Market().futures("VN30F1M").trades(limit=1000)Một phiên đầy đủ có vài chục nghìn lệnh. Lấy nhiều trang qua last_id, hoặc để vnstock_pipeline lưu dữ liệu khớp lệnh vào kho rồi đọc lại.
Bên chủ động
sign = micro.classify(ticks) # đọc match_type; ATO, ATC cho 0
sign.value_counts()
tick_rule = micro.classify(ticks, method="tick_rule")
(tick_rule == sign)[sign != 0].mean() # tỷ lệ tick rule khớp với nguồnKhi nguồn có cột bên chủ động thì nên dùng cột đó. Tick rule chỉ là ước lượng; trên dữ liệu ngẫu nhiên như ví dụ này, tỷ lệ khớp gần 50%, nghĩa là ví dụ không nói gì về độ chính xác của tick rule trên dữ liệu thật.
Nến theo thời gian, số lệnh, khối lượng, giá trị
m1 = micro.bars(ticks, by="time", size="1min") # tick → nến 1 phút
m5 = micro.bars(ticks, by="time", size="5min")
tb = micro.bars(ticks, by="tick", size=200) # mỗi nến 200 lệnh
vb = micro.bars(ticks, by="volume", size=100_000) # mỗi nến đúng 100.000 cổ phiếu
db = micro.bars(ticks, by="value", size=2e9) # mỗi nến đúng 2 tỷ đồng
m1[["open", "high", "low", "close", "volume", "vwap", "buy_volume", "sell_volume"]].head()vwapcủa từng nến là Σ(giá × khối lượng) / Σ khối lượng, giá bình quân thật của nến. Với nến khối lượng hay giá trị, đó là giá bình quân của đúng một lô.- Lệnh vắt qua ranh giới được tách để mỗi nến đủ đúng
size(split=True).split=Falseđể lệnh đó đóng nến nguyên vẹn, nến hơi vượtsize. - Một lệnh lớn hơn
max_splitnến (mặc định 1.000) không bị tách mà thành một nến gắn cờoversize. Ở ví dụ này, ATC 400.000 cổ phiếu vớisize=100là một nến chứ không phải 4.000 nến. - Nến cuối chưa đủ
sizecócomplete=False. - Tổng khối lượng các nến bằng tổng khối lượng khớp: không mất, không đếm trùng.
assert vb["volume"].sum() == ticks["volume"].sum()
vb[["volume", "complete", "oversize"]].tail(3)Nến dùng thẳng cho chỉ báo kỹ thuật:
from vnstock_quant.ta import Indicator
Indicator(m1).rsi(length=14).tail()Khối lượng mua trừ bán (CVD)
flow = micro.cvd(m1) # cộng dồn, bắt đầu lại mỗi ngày
flow.tail()CVD tăng trong khi giá đi ngang thường được đọc là lực mua chủ động đang bị hấp thụ. Hàm chỉ tính chuỗi số, không đưa tín hiệu.
VPIN
v = micro.vpin(vb, window=50)
v.describe()VPIN (Easley, López de Prado và O'Hara, 2012) là tỷ lệ mất cân bằng mua bán trên window nến khối lượng gần nhất, từ 0 đến 1. Bài gốc chọn kích thước nến khoảng 1/50 khối lượng trung bình ngày và window bằng 50. Chỉ số này còn tranh cãi: Andersen và Bondarenko (2014) cho rằng nó dự báo kém biến động ngắn hạn khi đã tính đến cường độ giao dịch. Dùng để nghiên cứu, không dùng làm tín hiệu.
Biến động thực hiện
rv = micro.realized(m5) # mỗi ngày: rv, bv, jump, jump_share, rvol, rvol_annual, n
rv[["rv", "bv", "jump_share", "rvol", "rvol_annual", "n"]]rvlà tổng bình phương log-return giữa các nến trong ngày (Andersen, Bollerslev, Diebold và Labys, 2003).bv, biến thiên bipower, ít bị ảnh hưởng bởi một cú nhảy giá (Barndorff-Nielsen và Shephard, 2004);jump = max(rv − bv, 0)là phần nhảy.- Lợi suất không nối qua đêm. Giờ nghỉ trưa tính là một lợi suất.
- Dùng nến khoảng 5 phút. Nến quá mịn dính nhiễu bước giá (giá nhảy qua lại giữa giá mua và giá bán), làm
rvcao hơn thật. Với cổ phiếu Việt Nam, bước giá 10 đến 100 đồng là một phần đáng kể của biến động trong phút. rvol_annualnhân vớiperiods=252. Muốn theo lịch HOSE thì truyềnperiods=250.
Hiệu năng
Phép gom chạy trong lõi Rust; phần còn lại là chuẩn hoá cột bằng pandas. Một lần gọi bars gồm cả xác định bên chủ động từ match_type, đo trên máy tính xách tay chip Apple Silicon:
| Số lệnh | Thời gian |
|---|---|
| 20.000 (một phiên như ví dụ) | khoảng 7 ms |
| 500.000 | khoảng 85 ms |
Đọc tiếp
- Chỉ báo kỹ thuật — Tính chỉ báo trên nến vừa gom.
- Rủi ro và hiệu suất — Thước đo độ kém thanh khoản Amihud trên dữ liệu ngày.
- Tải dữ liệu bằng vnstock — Lấy dữ liệu khớp lệnh và các loại dữ liệu khác.
