Vnstock Logo

Quản lý danh mục đầu tư

Mở rộng

Mục lục

Agent Guide Notebook minh hoạ

Khuyên dùng: Nên ưu tiên sử dụng Agent Guide để nạp môi trường cho AI Agent trên máy tính cục bộ. Tránh viết code thủ công hoặc dùng AI phiên bản web/Google Colab vì AI không có thông tin mới nhất về thư viện nên dễ viết sai cú pháp.



Tổng quan

Mô-đun vnstock_quant.portfolio cung cấp 38 hàm tính toán cho danh mục và quỹ mở, xây dựng dựa trên giáo trình CFA (Portfolio Management), bao gồm: đo lường tỷ suất sinh lời khi có dòng tiền nạp rút (TWR và MWR/IRR), lợi suất danh mục theo lịch tái cân bằng có tính phí và thuế giao dịch Việt Nam (VN_COSTS), phân rã đóng góp rủi ro thành phần, tối ưu hoá danh mục ràng buộc không bán khống và quy đổi tỷ trọng ra số lượng cổ phiếu chẵn lô 100.

Yêu cầu cấp tài trợ

Toàn bộ các hàm trong mô-đun portfolio yêu cầu tài khoản từ cấp Bronze trở lên và không bị giới hạn số lần thực thi trên máy cá nhân.


Đo lường hiệu suất tài khoản: TWR và MWR

Khi một tài khoản giao dịch phát sinh nhiều lần nạp hoặc rút tiền trong suốt chu kỳ đầu tư, việc tính tỷ suất sinh lời đơn giản sẽ dẫn đến kết quả sai lệch nghiêm trọng. Thư viện cung cấp hai thước đo chuẩn mực:

  1. TWR (Time-Weighted Return - Lợi suất theo thời gian): Loại bỏ hoàn toàn tác động của thời điểm và quy mô dòng tiền nạp/rút. Đây là con số khách quan nhất để đánh giá năng lực lựa chọn tài sản của người quản lý danh mục và để so sánh với chỉ số thị trường (VNINDEX).
  2. MWR (Money-Weighted Return / IRR - Lợi suất theo dòng tiền): Phản ánh tỷ suất sinh lời thực tế trên từng đồng vốn mà chính nhà đầu tư đã bỏ ra (tính theo phương pháp Internal Rate of Return - XIRR chuẩn Actual/365).
Python
import pandas as pd
from vnstock_quant import portfolio as pm

# 1. Tính TWR: Cần giá trị danh mục NGAY TRƯỚC mỗi lần nạp rút và dòng tiền phát sinh
# Ví dụ: Ban đầu có 100 triệu, ngày 03/06 nạp thêm 30 triệu (lúc đó tài khoản trị giá 112 triệu), cuối năm đạt 150 triệu
values = pd.Series([100e6, 112e6, 150e6], index=pd.to_datetime(["2024-01-02", "2024-06-03", "2025-01-02"]))
flows = pd.Series([30e6], index=[pd.Timestamp("2024-06-03")])

twr_result = pm.twr(values, flows)
print(f"Lợi suất TWR: {twr_result['twr']:.4f} (Quy đổi theo năm: {twr_result['annualized']:.4f})")

# 2. Tính MWR (IRR): Dòng tiền đứng ở góc nhìn nhà đầu tư (nạp tiền là âm, rút ra/giá trị cuối kỳ là dương)
cash_flows = pd.Series([-100e6, -30e6, 150e6], index=values.index)
mwr_return = pm.mwr(cash_flows)
print(f"Lợi suất MWR (IRR): {mwr_return:.4f}/năm")

Mô phỏng danh mục và chi phí giao dịch Việt Nam

Khi phân bổ vốn theo một danh mục nhiều tài sản, tỷ trọng của các cổ phiếu sẽ liên tục tự trôi theo biến động giá hàng ngày. Hàm pm.portfolio_returns() tính lợi suất danh mục theo đúng hành vi này:

Python
import numpy as np
import pandas as pd
from vnstock_quant import portfolio as pm

# Giả lập chuỗi lợi suất ngày của 5 cổ phiếu
names = ["FPT", "VCB", "HPG", "MWG", "SSI"]
returns_df = ...  # DataFrame cột là tên cổ phiếu, hàng là chỉ mục ngày
target_weights = pd.Series([0.30, 0.25, 0.20, 0.15, 0.10], index=names)

# Chiến lược 1: Mua và nắm giữ (Buy & Hold - không tái cân bằng, tỷ trọng tự trôi)
held = pm.portfolio_returns(returns_df, target_weights)

# Chiến lược 2: Tái cân bằng hàng quý kèm chi phí thực tế Việt Nam
quarterly = pm.portfolio_returns(
    returns_df,
    target_weights,
    rebalance="Q",
    **pm.VN_COSTS  # Phí giao dịch 0.15% mỗi chiều + Thuế bán 0.1%
)

# Chiến lược 3: Tái cân bằng theo dải hành lang (Corridor) khi tỷ trọng lệch quá 5%
corridor = pm.portfolio_returns(
    returns_df,
    target_weights,
    rebalance="corridor",
    band=0.05,
    **pm.VN_COSTS
)

Cấu trúc chi phí pm.VN_COSTS

pm.VN_COSTS là bộ tham số chi phí được định nghĩa sẵn theo thực tế thị trường chứng khoán Việt Nam:

  • Phí mua (buy_cost): 0.0015 (0,15% giá trị giao dịch).
  • Phí bán (sell_cost): 0.0025 (gồm 0,15% phí môi giới + 0,1% thuế thu nhập cá nhân từ chuyển nhượng chứng khoán).

Phân rã đóng góp lợi suất theo Cariño

Hàm pm.contribution() áp dụng thuật toán liên kết chuỗi Cariño, phân rã chính xác lợi suất luỹ kế của toàn danh mục thành mức đóng góp của từng mã cổ phiếu:

Python
# Phân rã đóng góp lợi suất luỹ kế
contrib = pm.contribution(held.attrs["weights_bop"], returns_df)
print(contrib.attrs["linked"])  # Tổng các giá trị đóng góp bằng đúng 100% lợi suất danh mục

Làm tròn số lượng cổ phiếu theo lô chẵn 100

Thị trường chứng khoán Việt Nam (HOSE, HNX) quy định giao dịch theo lô chuẩn 100 cổ phiếu. Hàm pm.lot_round() tự động quy đổi tỷ trọng lý thuyết thành số lượng cổ phiếu chẵn lô thực tế mà không vượt quá hạn mức vốn:

Python
# Giá thị trường hiện tại của các mã
current_prices = pd.Series([98500, 61200, 26350, 45600, 33100], index=names)
total_capital = 200_000_000  # Vốn phân bổ 200 triệu đồng

# Tính toán số lượng cổ phiếu thực tế chẵn lô 100
lots = pm.lot_round(target_weights, current_prices, capital=total_capital)
print(lots[["shares", "value", "actual_weight"]])

Đo lường và phân rã rủi ro danh mục

Một cổ phiếu chiếm 20% vốn có thể gánh tới 50% tổng rủi ro của danh mục nếu có độ biến động cao và tương quan thuận chặt chẽ với các cổ phiếu còn lại. vnstock_quant cung cấp công cụ phân rã rủi ro thành phần:

Python
# 1. Ước lượng ma trận hiệp phương sai quy đổi theo năm (hỗ trợ 'sample', 'ledoit_wolf', 'ewma')
cov_matrix = pm.covariance(returns_df, periods=252, method="ledoit_wolf")

# 2. Phân rã tỷ lệ đóng góp rủi ro của từng mã
rc = pm.risk_contribution(target_weights, cov_matrix)
print(rc[["weight", "marginal", "component", "percent"]])
print(f"Độ biến động toàn danh mục: {rc.attrs['volatility']:.4f}")

# 3. Đánh giá mức độ tập trung danh mục
conc = pm.concentration(target_weights)
print(f"Hệ số HHI: {conc['hhi']:.4f}, Số cổ phiếu hiệu dụng: {conc['effective_n']:.2f}")

# 4. Tính toán VaR tham số của danh mục với giá trị 1 tỷ đồng
daily_cov = pm.covariance(returns_df)
port_var = pm.parametric_var(target_weights, daily_cov, level=0.99, value=1e9)
print(f"Mức lỗ tiềm tàng 99% trong 1 ngày: {port_var.attrs['amount']:,.0f} VNĐ")

Tối ưu hoá danh mục đầu tư

Thư viện tích hợp các thuật toán tối ưu hóa danh mục hiện đại với ràng buộc mặc định không bán khống (tỷ trọng từ 0 đến 1, tổng bằng 1):

Python
# 1. Danh mục phương sai tối thiểu (Minimum Variance Portfolio)
w_min_var = pm.min_variance(cov_matrix)

# 2. Danh mục tối đa hoá tỷ số Sharpe (Tangency Portfolio)
w_max_sharpe = pm.max_sharpe(returns_df.mean() * 252, cov_matrix, rf=0.03)

# 3. Dựng đường biên hiệu quả Markowitz (Efficient Frontier)
frontier = pm.frontier(returns_df.mean() * 252, cov_matrix, points=20)

# 4. Danh mục đóng góp rủi ro đồng đều (Risk Parity Portfolio)
w_risk_parity = pm.risk_parity(cov_matrix)

# 5. Tối ưu hoá Black-Litterman kết hợp quan điểm nhà đầu tư
w_bl = pm.black_litterman(market_caps, cov_matrix, views=..., confidences=...)

Đọc tiếp