Vnstock Logo

Danh mục

Mở rộng

Mục lục

Tính năng đang thử nghiệm

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.

Tổng quan

vnstock_quant.portfolio giúp bạn đo lợi suất khi danh mục có nạp hoặc rút tiền, mô phỏng lịch tái cân bằng và xem mức đóng góp của từng tài sản. Bạn cũng có thể đặt chi phí trong mô phỏng, tính tỷ trọng danh mục và quy đổi tỷ trọng thành số cổ phiếu theo lô.

Yêu cầu cấp quyền sử dụng

Các hàm trong mô-đun portfolio yêu cầu tài khoản từ cấp Bronze trở lên. Phép tính vẫn tính vào hạn mức sử dụng chung của tài khoản.


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

Khi bạn nạp hoặc rút tiền trong kỳ, chỉ lấy giá trị cuối kỳ chia cho giá trị đầu kỳ sẽ không phản ánh đúng hiệu quả danh mục. Hai cách tính dưới đây xử lý dòng tiền theo hai góc nhìn:

  1. TWR (lợi suất theo thời gian): Tách tác động của các lần nạp, rút tiền để so sánh kết quả danh mục qua các giai đoạn hoặc với một chỉ số tham chiếu.
  2. MWR (lợi suất theo dòng tiền): Tính cả thời điểm và quy mô của dòng tiền bạn đưa vào hoặc rút ra. Hàm dùng cách tính tỷ suất hoàn vốn nội bộ theo ngày (XIRR, quy ước 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 biến động dòng tiền và dòng tiền phát sinh
# Ví dụ: Ban đầu có 100 triệu, ngày 03/06 góp thêm 30 triệu (lúc đó danh mục 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ư (dòng tiền góp vốn là âm, rút vốn / 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 giá từng cổ phiếu thay đổi, tỷ trọng thực tế của chúng cũng thay đổi. pm.portfolio_returns() cho phép bạn tính lợi suất trong trường hợp giữ nguyên danh mục hoặc tái cân bằng theo lịch:

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í giao dịch + 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