vectorbt giúp kiểm tra nhiều chiến lược nhanh như thế nào?
vectorbt
Trong số các thư viện backtest Python, vectorbt là một trong những thư viện mình thích nhất khi mục tiêu là research. Không hẳn vì nó giúp viết một chiến lược đơn giản ngắn hơn quá nhiều, mà vì khi câu hỏi chuyển từ “chiến lược này có chạy được không?” sang “nếu thử 500 hay 10.000 biến thể của chiến lược thì kết quả thay đổi thế nào?”, vectorbt bắt đầu thể hiện rõ lợi thế.
vectorbt làm việc trực tiếp với Pandas và NumPy, đồng thời sử dụng Numba và hiện có cả tùy chọn Rust cho các phần cần hiệu năng cao. Thiết kế của nó cho phép đưa nhiều asset, nhiều parameter và nhiều configuration vào cùng cấu trúc dữ liệu để xử lý ở quy mô lớn. Tài liệu chính thức mô tả khả năng kiểm tra hàng nghìn chiến lược trong thời gian rất ngắn, đồng thời hỗ trợ portfolio analytics, trades, drawdown, Plotly và parameter sweep.
Điểm quan trọng là vectorbt không tự tạo ra một chiến lược tốt. Nó giúp quá trình đặt giả thuyết, kiểm tra, loại bỏ và stress-test một chiến lược diễn ra nhanh hơn rất nhiều.

Phần 1. Từ một backtest sang cả một “không gian chiến lược”
Giả sử đang nghiên cứu một chiến lược rất quen thuộc trên FPT: MA nhanh cắt lên MA chậm thì vào vị thế, cắt xuống thì thoát.
Nếu chỉ kiểm tra MA20 và MA50, code gần như không có gì đặc biệt:
import vectorbt as vbt
fast_ma = vbt.MA.run(close, 20)
slow_ma = vbt.MA.run(close, 50)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(
close,
entries,
exits,
init_cash=100_000_000,
fees=0.0015,
freq="1D"
)
print(pf.stats())Portfolio.from_signals() là API chính của vectorbt để chuyển entry/exit signal thành một portfolio backtest; engine còn lưu orders, trades, drawdowns và các performance metrics để tiếp tục phân tích.
Nhưng kết quả đẹp của MA20/50 thực ra chưa cho biết nhiều.
Giả sử Sharpe của MA20/50 là 1,4. Điều cần hỏi tiếp là:
MA19/50 thì sao? MA21/50? MA20/55? Hay chỉ riêng 20/50 đẹp vì vô tình khớp với dữ liệu quá khứ?
Nếu làm theo cách tuần tự, có thể viết hai vòng for rồi chạy từng strategy. Với vài chục combination thì không vấn đề. Nhưng khi mở rộng sang nhiều MA, nhiều cổ phiếu, stop-loss, holding period và volume filter, số lượng experiment tăng rất nhanh.
Ví dụ chỉ cần:
Fast MA: 5–30
Slow MA: 40–100
Stop: 5%, 7%, 10%
30 cổ phiếu
đã tạo ra hàng nghìn phép thử.
vectorbt có run_combs() để tạo trực tiếp các combination của parameter. Indicator Factory của thư viện cũng được thiết kế để nhận input array, parameter array, chạy các combination và nối kết quả thành các output array có cấu trúc.
Một ví dụ gần với tài liệu chính thức:
import numpy as np
import vectorbt as vbt
windows = np.arange(5, 101)
fast_ma, slow_ma = vbt.MA.run_combs(
close,
window=windows,
r=2,
short_names=["fast", "slow"]
)
entries = fast_ma.ma_crossed_above(slow_ma)
exits = fast_ma.ma_crossed_below(slow_ma)
pf = vbt.Portfolio.from_signals(
close,
entries,
exits,
size=np.inf,
fees=0.0015,
freq="1D"
)Lúc này không còn backtest “MA20/50”.
Ta đang có cả một mặt phẳng MA Fast × MA Slow.
Có thể lấy Total Return:
total_return = pf.total_return()hoặc Sharpe:
sharpe = pf.sharpe_ratio()rồi dựng heatmap:
fig = total_return.vbt.heatmap(
x_level="fast_window",
y_level="slow_window",
symmetric=True
)
fig.show()Tài liệu chính thức của vectorbt dùng gần như chính workflow này để kiểm tra 10.000 dual-SMA window combinations và đưa kết quả lên heatmap.
Và đây mới là lý do mình thích vectorbt.
Thay vì nhận được một con số:
MA20/50 → Sharpe 1,42
ta nhìn được một vùng:
MA 15/45 1.31
MA 15/50 1.37
MA 20/45 1.34
MA 20/50 1.42
MA 20/55 1.39
MA 25/50 1.30
MA 25/55 1.35Nếu cả một vùng đều tương đối tốt, chiến lược đáng nghiên cứu tiếp.
Ngược lại:
MA 19/52 0.61
MA 20/52 2.18
MA 21/52 0.73thì Sharpe 2,18 không làm mình phấn khích lắm.
Nó giống một đỉnh nhọn trong parameter space hơn là một edge ổn định.
Phần 2. vectorbt hữu ích nhất khi biến backtest thành một bài toán nghiên cứu dữ liệu
Có thể dùng vectorbt chỉ để backtest MA crossover, nhưng như vậy hơi phí.
Một workflow thực tế hơn với cổ phiếu Việt Nam có thể bắt đầu từ dữ liệu lấy qua Vnstock. vectorbt không bắt buộc phải dùng nguồn dữ liệu tích hợp của nó; phần quan trọng là cuối cùng giá, volume hay feature được đưa về Pandas Series/DataFrame mà engine có thể xử lý. Khả năng làm việc trực tiếp với Pandas/NumPy chính là một phần kiến trúc cốt lõi của thư viện.
Giả sử đã có DataFrame:
df.head()time open high low close volume
2025-01-02 ... ... ... ... ...
2025-01-03 ... ... ... ... ...Chuẩn hóa rất đơn giản:
df["time"] = pd.to_datetime(df["time"])
df = df.set_index("time").sort_index()
close = df["close"].astype(float)
volume = df["volume"].astype(float)Thay vì chỉ nghiên cứu MA, có thể đặt một giả thuyết gần với trading thực tế hơn:
“Breakout có volume xác nhận có tốt hơn breakout không có volume hay không?”
Ví dụ breakout 20 phiên:
high_20 = close.shift(1).rolling(20).max()
breakout = close > high_20Ở đây shift(1) rất quan trọng. Đỉnh 20 phiên dùng để xác định breakout phải là thông tin đã tồn tại trước phiên hiện tại, không nên vô tình dùng chính High/Close tương lai để tạo threshold.
Tiếp theo tạo Volume Z-score:
vol_mean = volume.rolling(20).mean()
vol_std = volume.rolling(20).std()
volume_z = (volume - vol_mean) / vol_stdSau đó tạo hai strategy:
entries_A = breakout
entries_B = breakout & (volume_z > 1.5)Exit có thể rất đơn giản để phục vụ nghiên cứu ban đầu:
ma10 = close.rolling(10).mean()
exits = close < ma10Chạy hai portfolio:
pf_A = vbt.Portfolio.from_signals(
close,
entries_A,
exits,
init_cash=100_000_000,
fees=0.0015,
freq="1D"
)
pf_B = vbt.Portfolio.from_signals(
close,
entries_B,
exits,
init_cash=100_000_000,
fees=0.0015,
freq="1D"
)Sau đó không chỉ nhìn return:
print(pf_A.stats())
print(pf_B.stats())mà xem cả:
Total Return, Sharpe, Max Drawdown, Number of Trades, Win Rate, Average Trade, Profit Factor và trade distribution.
vectorbt có sẵn portfolio/trade/drawdown analytics và hệ thống stats để đi từ portfolio tổng xuống từng giao dịch.
Giả sử kết quả minh họa:
Breakout Breakout + Volume
Sharpe 0.82 1.21
Max DD -24.1% -16.8%
Trades 96 54
Win Rate 43.7% 48.1%Điều quan trọng không phải kết luận ngay “Volume Z-score > 1,5 tốt”.
Bước tiếp theo là thử:
Volume Z:
0.5
1.0
1.5
2.0
2.5và breakout:
20D
30D
40D
60DTa sẽ có một ma trận:
Breakout Window × Volume Threshold
Nếu chỉ 20D + Z=1.5 đẹp còn vùng xung quanh rất xấu, kết quả đáng nghi.
Nếu:
20D / 1.0
20D / 1.5
30D / 1.0
30D / 1.5
40D / 1.5đều cho kết quả tương đối ổn, hypothesis đáng để nghiên cứu tiếp hơn.
Đây là cách mình thích dùng vectorbt: không phải để tìm “tham số tối ưu”, mà để nhìn hình dạng của cả parameter space.
Heatmap vì vậy đôi khi có giá trị hơn equity curve.
Một equity curve đẹp cho biết một strategy đã hoạt động thế nào.
Một heatmap đẹp cho biết edge có tồn tại quanh strategy đó hay không.
Phần 3. Với thị trường Việt Nam, vectorbt rất mạnh cho Research nhưng không nên biến nó thành “máy tối ưu Sharpe”
Có một phản biện khá phổ biến: “vectorbt nhanh thật, nhưng thị trường Việt Nam khác Mỹ, backtest thế này không thực tế”.
Một nửa câu đó đúng.
vectorbt không phải simulator riêng cho HOSE, HNX hay UPCoM. Nó không tự biết toàn bộ đặc thù của thị trường Việt Nam nếu người nghiên cứu không đưa các điều kiện đó vào model.
Backtest vẫn phải tự xử lý những vấn đề như corporate action, liquidity, cách xác định giá thực thi, thời điểm signal available, phí/thuế, giới hạn khả năng khớp lệnh và những quy tắc thị trường liên quan.
Ví dụ một lỗi cực kỳ phổ biến:
entries = close > high_20sau đó giả định:
Signal được biết tại Close hôm nay
và cũng mua được tại chính Close hôm nayĐây có thể là look-ahead ở mức execution.
Một cách thận trọng hơn nếu chiến lược chỉ được xác nhận sau khi phiên kết thúc là:
entries_exec = entries.shift(1).fillna(False)rồi giao dịch từ phiên sau.
Tương tự, một chiến lược kiếm được rất nhiều tiền trên cổ phiếu thanh khoản thấp có thể hoàn toàn không triển khai được với quy mô vốn lớn. vectorbt tính đúng theo giả định mà ta đưa vào; nó không chịu trách nhiệm nếu giả định ban đầu phi thực tế.
Rủi ro lớn hơn nữa đến từ chính tốc độ.
Giả sử thử:
100 fast MA
100 slow MA
10 stop-loss
10 take-profit
20 holding periodsđã có hàng triệu cấu hình tiềm năng.
Trong số đó chắc chắn sẽ có vài backtest cực đẹp chỉ nhờ ngẫu nhiên.
vectorbt hiện được thiết kế rất mạnh cho broadcasting, large-scale parameter sweeps và robustness research; chính vì thế người dùng càng cần phân biệt research với data mining.
Cách dùng hợp lý hơn là:
1. Đặt hypothesis trước.
Ví dụ:
Breakout có Relative Strength tốt và volume bất thường có khả năng tiếp diễn cao hơn breakout thông thường.
2. Chọn một parameter range hợp lý về kinh tế.
Không cần thử RSI 1 đến 200 chỉ vì máy chạy được.
3. Quan sát vùng parameter ổn định.
Không chọn ngay ô Sharpe cao nhất.
4. Tách In-sample / Out-of-sample.
Ví dụ:
2018–2023: Research
2024–2026: Out-of-sampleParameter được chọn ở tập đầu thì không tiếp tục chỉnh dựa vào tập sau.
5. Stress test.
Tăng transaction cost, thay đổi entry một phiên, thay đổi window ±10–20%, loại một số cổ phiếu tốt nhất ra khỏi universe.
Nếu strategy chết ngay sau một thay đổi nhỏ, edge có thể không mạnh như backtest ban đầu thể hiện.
vectorbt còn hỗ trợ interactive visualization bằng Plotly/Jupyter và có thể biểu diễn performance, heatmap, trades và dashboard ngay trong notebook. Tài liệu hiện tại cũng nhấn mạnh khả năng tối ưu qua nhiều parameters, assets và periods trong một lần nghiên cứu.
Đó cũng là lý do trong số các thư viện backtest, vectorbt là lựa chọn mình ưu tiên khi đang ở giai đoạn Quant Research.
Backtrader hay những framework event-driven có lợi thế riêng khi muốn mô phỏng execution theo từng event rất chi tiết. Nhưng khi đang có một hypothesis và muốn kiểm tra nhanh:
20 cổ phiếu
× 30 parameter combinations
× 5 regime
× nhiều năm dữ liệuthì cách tổ chức dữ liệu dạng matrix của vectorbt rất hợp với cách làm research.
Một workflow khá thực tế có thể là:
Vnstock / dữ liệu thị trường
↓
Cleaning + Point-in-time
↓
Feature Engineering
↓
Signal
↓
vectorbt Parameter Sweep
↓
Heatmap / Trade Log / Drawdown
↓
Robustness Test
↓
Out-of-sample
↓
Execution Modelvectorbt nằm ở giữa pipeline, không phải toàn bộ pipeline.
Nó không thay thế việc hiểu thị trường, không sửa được dữ liệu bẩn và cũng không cứu được một hypothesis không có edge.
Nhưng nó giải quyết rất tốt một vấn đề mà Quant Research gặp liên tục:
“Ý tưởng này có thực sự ổn định, hay chỉ đẹp ở đúng một bộ tham số mà mình tình cờ chọn?”
Một backtest trả lời:
MA20/50 kiếm được bao nhiêu?
vectorbt có thể giúp trả lời câu hỏi giá trị hơn:
Nếu thay MA20 thành 15, 18, 22 hay 25; thay MA50 thành 45, 55 hay 60; chạy trên nhiều cổ phiếu và nhiều giai đoạn khác nhau — edge còn tồn tại không?
