| Python Tính Monte Carlo Simulation: Dự Báo Danh Mục Đầu Tư 1 Năm Tới

Monte Carlo Simulation là kỹ thuật mạnh để dự báo phân phối kết quả của danh mục đầu tư — thay vì đưa ra 1 con số, nó cho bạn thấy phạm vi có thể xảy ra và xác suất tương ứng. Đây là công cụ quan trọng trong quản lý rủi ro tài chính.

Ý Tưởng Cơ Bản

Thay vì dự báo “danh mục sẽ đạt X%”, Monte Carlo chạy 10,000 kịch bản ngẫu nhiên dựa trên phân phối lợi nhuận lịch sử và cho biết:

  • Xác suất đạt lợi nhuận dương là bao nhiêu?
  • Worst case (5% tệ nhất) là bao nhiêu?
  • Cần bao nhiêu vốn đệm để không bị margin call?

Bước 1: Lấy Dữ Liệu Danh Mục

from vnstock import stock_historical_data
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# Danh mục: 40% VNM, 30% HPG, 30% FPT
portfolio = {
    'VNM': 0.40,
    'HPG': 0.30,
    'FPT': 0.30
}

prices = {}
for sym in portfolio:
    df = stock_historical_data(sym, "2022-01-01", "2026-06-01", "1D")
    df.index = pd.to_datetime(df['time'])
    prices[sym] = df['close']

price_df = pd.DataFrame(prices).dropna()
returns  = price_df.pct_change().dropna()

print(f"Dữ liệu: {len(returns)} phiên giao dịch")
print("nThống kê lợi nhuận ngày:")
print(returns.describe())

Bước 2: Tính Tham Số Phân Phối

# Lợi nhuận danh mục = weighted average
weights = np.array(list(portfolio.values()))
port_ret = (returns * weights).sum(axis=1)

# Tham số phân phối lợi nhuận ngày
mu    = port_ret.mean()         # Trung bình
sigma = port_ret.std()          # Độ lệch chuẩn

print(f"nLợi nhuận trung bình/ngày: {mu*100:.4f}%")
print(f"Độ lệch chuẩn/ngày:        {sigma*100:.4f}%")
print(f"Lợi nhuận kỳ vọng/năm:    {mu*252*100:.1f}%")
print(f"Volatility/năm:            {sigma*np.sqrt(252)*100:.1f}%")

Bước 3: Chạy Monte Carlo

np.random.seed(42)

N_SIMULATIONS = 10_000
N_DAYS        = 252  # 1 năm giao dịch
INITIAL_VALUE = 1_000_000_000  # 1 tỷ VND

# Ma trận tương quan để tạo returns có tương quan thực tế
cov_matrix = returns.cov()

# Simulate với correlated returns
simulations = np.zeros((N_DAYS, N_SIMULATIONS))
simulations[0] = INITIAL_VALUE

for t in range(1, N_DAYS):
    # Sinh random returns có tương quan
    rand_returns = np.random.multivariate_normal(
        mean=returns.mean(),
        cov=returns.cov(),
        size=N_SIMULATIONS
    )
    # Lợi nhuận danh mục
    port_rand = (rand_returns * weights).sum(axis=1)
    simulations[t] = simulations[t-1] * (1 + port_rand)

final_values = simulations[-1]
final_returns = (final_values / INITIAL_VALUE - 1) * 100

print(f"n=== Kết quả Monte Carlo ({N_SIMULATIONS:,} kịch bản) ===")
print(f"Giá trị trung bình sau 1 năm: {np.mean(final_values)/1e9:.2f} tỷ")
print(f"Lợi nhuận trung bình:         {np.mean(final_returns):.1f}%")

Bước 4: Tính VaR và CVaR

CONFIDENCE = 0.95  # 95% confidence level

# Value at Risk (VaR): mức lỗ tệ nhất ở 5% xác suất
var_95 = np.percentile(final_returns, 5)
print(f"nVaR (95%): {var_95:.1f}%")
print(f"  → Có 5% khả năng lỗ hơn {abs(var_95):.1f}% trong 1 năm")
print(f"  → Tương đương: {abs(var_95)/100 * INITIAL_VALUE/1e9:.2f} tỷ VND")

# Conditional VaR (CVaR / Expected Shortfall): trung bình của 5% tệ nhất
cvar_95 = final_returns[final_returns  0).mean() * 100
prob_loss_20 = (final_returns  20%:   {prob_loss_20:.1f}%")

Bước 5: Vẽ Biểu Đồ

fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Biểu đồ 1: 100 simulation paths
ax1 = axes[0]
for i in range(min(100, N_SIMULATIONS)):
    ax1.plot(simulations[:, i] / 1e9, alpha=0.05, color='blue', linewidth=0.5)
ax1.plot(np.median(simulations, axis=1) / 1e9, color='red', linewidth=2, label='Trung vị')
ax1.set_xlabel('Ngày giao dịch')
ax1.set_ylabel('Giá trị danh mục (tỷ VND)')
ax1.set_title('Monte Carlo: 100 kịch bản')
ax1.legend()

# Biểu đồ 2: Phân phối kết quả cuối năm
ax2 = axes[1]
ax2.hist(final_returns, bins=100, color='steelblue', alpha=0.7, edgecolor='white')
ax2.axvline(var_95,  color='red',    linestyle='--', label=f'VaR 95%: {var_95:.1f}%')
ax2.axvline(cvar_95, color='orange', linestyle='--', label=f'CVaR 95%: {cvar_95:.1f}%')
ax2.axvline(0, color='black', linestyle='-', linewidth=2)
ax2.set_xlabel('Lợi nhuận 1 năm (%)')
ax2.set_ylabel('Số kịch bản')
ax2.set_title('Phân phối lợi nhuận sau 1 năm')
ax2.legend()

plt.tight_layout()
plt.savefig('monte_carlo.png', dpi=150)
plt.show()

Kết Luận

Monte Carlo cho bạn cái nhìn thực tế hơn về rủi ro — thay vì chỉ nhìn lợi nhuận kỳ vọng, bạn biết phân phối đầy đủ của kết quả có thể xảy ra. Đây là công cụ tiêu chuẩn tại các quỹ đầu tư chuyên nghiệp và ngân hàng khi quản lý danh mục lớn.


📌 Muốn ứng dụng Python vào phân tích và giao dịch tài chính thực chiến?
Khóa Python Fintech — Phân Tích Dữ Liệu Lớn & Tự Động Hóa Giao Dịch tại Hướng Nghiệp Dữ Liệu giúp bạn thực hành với dữ liệu VnIndex, Binance API thật — không dạy lý thuyết hàn lâm.
📞 Hotline/Zalo: 0927 909 257

admin

admin

Biên tập viên, Hướng Nghiệp Dữ Liệu
733 Bài viết
15.4k Người theo dõi
120k+ Lượt đọc

Biên tập viên nội dung tại Hướng Nghiệp Dữ Liệu, phụ trách tổng hợp và biên soạn các bài viết về lập trình Python, dữ liệu và công nghệ.