← Quay lại danh sách bài viết
September 25, 2025
5 phút đọc

Lý thuyết danh mục đầu tư Markowitz cho Crypto: Từ Cơ Bản đến Nâng Cao

Lý thuyết danh mục đầu tư Markowitz cho Crypto: Từ Cơ Bản đến Nâng Cao
#tối ưu hóa danh mục
#Markowitz
#crypto
#Python
#tài chính định lượng
#quản lý rủi ro
#đa dạng hóa
#đường biên hiệu quả
#tỷ lệ Sharpe
#giao dịch thuật toán

Xây dựng danh mục đầu tư crypto tối ưu bằng Python - vì YOLO không phải là chiến lược

Markowitz Portfolio Theory Lý thuyết danh mục đầu tư Markowitz: Tối ưu hóa toán học ứng dụng vào tài sản kỹ thuật số nhằm tối đa hóa lợi nhuận với mức rủi ro cho trước.


Giới thiệu: Tại sao danh mục crypto của bạn cần toán học (chứ không chỉ cảm tính)

Chào các anh em crypto! 👋

Bạn còn nhớ lần bỏ cả đống tiền vào DOGE chỉ vì Elon đăng tweet? Hay lần hoảng loạn bán hết trong đợt crash gần nhất? Vâng, ai trong chúng ta cũng đã từng như vậy. Hôm nay chúng ta sẽ nói về một thứ có thể cứu danh mục của bạn (và cả sự tỉnh táo của bạn): Lý thuyết danh mục đầu tư Markowitz.

Harry Markowitz đã nhận giải Nobel vì công trình này vào năm 1990. Ý tưởng cốt lõi? Bạn có thể tối ưu hóa danh mục bằng toán học để đạt được lợi nhuận tốt nhất có thể với bất kỳ mức rủi ro nào. Giống như có GPS cho khoản đầu tư của bạn thay vì lái xe mà bịt mắt.

Khái niệm cốt lõi: Rủi ro vs Lợi nhuận (Cuộc khiêu vũ vĩnh cửu)

Trước khi đi vào mã lập trình, hãy hiểu những gì chúng ta đang xử lý:

  • Lợi nhuận kỳ vọng: Bạn kỳ vọng kiếm được bao nhiêu tiền
  • Rủi ro (Biến động): Giá trị danh mục của bạn dao động như thế nào
  • Tương quan: Các tài sản khác nhau biến động tương tự nhau như thế nào

Điều kỳ diệu xảy ra khi bạn kết hợp các tài sản không biến động hoàn toàn đồng bộ. Khi Bitcoin sụp đổ, có thể một số token DeFi giữ được giá trị tốt hơn. Đó là đa dạng hóa đang hoạt động cho bạn.

Risk vs Return Rủi ro vs. Lợi nhuận: Cân bằng giữa tài sản lợi suất cao biến động mạnh và nền tảng ổn định để đạt được lợi nhuận trung bình nhân tối ưu.

Thiết lập môi trường Python

Đầu tiên - hãy chuẩn bị các công cụ:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.optimize import minimize
import yfinance as yf
import warnings
warnings.filterwarnings('ignore')

plt.style.use('dark_background')
sns.set_palette("husl")

Cấp độ 1: Bước khởi đầu - Toán học danh mục cơ bản

Hãy bắt đầu với những điều cơ bản. Chúng ta sẽ tính lợi nhuận và rủi ro cho một danh mục 2 tài sản đơn giản.

def get_crypto_data(symbols, period="1y"):
    """
    Fetch crypto data from Yahoo Finance
    symbols: list of crypto symbols (e.g., ['BTC-USD', 'ETH-USD'])
    period: time period for data
    """
    data = yf.download(symbols, period=period)['Adj Close']
    return data

crypto_symbols = ['BTC-USD', 'ETH-USD']
prices = get_crypto_data(crypto_symbols)

returns = prices.pct_change().dropna()
print("Daily Returns Preview:")
print(returns.head())

Bây giờ hãy tính một số chỉ số danh mục cơ bản:

def portfolio_performance(weights, returns):
    """
    Calculate portfolio return and volatility
    weights: array of portfolio weights
    returns: dataframe of asset returns
    """
    portfolio_return = np.sum(returns.mean() * weights) * 252

    portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(returns.cov() * 252, weights)))

    return portfolio_return, portfolio_vol

weights_5050 = np.array([0.5, 0.5])
ret_5050, vol_5050 = portfolio_performance(weights_5050, returns)

print(f"50/50 Portfolio:")
print(f"Expected Annual Return: {ret_5050:.2%}")
print(f"Annual Volatility: {vol_5050:.2%}")
print(f"Sharpe Ratio: {ret_5050/vol_5050:.3f}")

Cấp độ 2: Nghiêm túc hơn - Đường biên hiệu quả

Bây giờ mới thú vị! Đường biên hiệu quả cho chúng ta thấy tất cả các danh mục tối ưu có thể có. Mỗi điểm đại diện cho lợi nhuận tốt nhất có thể với một mức rủi ro nhất định.

def generate_random_portfolios(returns, num_portfolios=10000):
    """
    Generate random portfolio combinations
    """
    num_assets = len(returns.columns)
    results = np.zeros((4, num_portfolios))

    for i in range(num_portfolios):
        weights = np.random.random(num_assets)
        weights /= np.sum(weights)  # Normalize to sum to 1

        portfolio_return, portfolio_vol = portfolio_performance(weights, returns)
        sharpe_ratio = portfolio_return / portfolio_vol

        results[0,i] = portfolio_return
        results[1,i] = portfolio_vol
        results[2,i] = sharpe_ratio
        results[3,i:] = weights

    return results

results = generate_random_portfolios(returns)

portfolio_results = pd.DataFrame({
    'Returns': results[0],
    'Volatility': results[1],
    'Sharpe_Ratio': results[2]
})

plt.figure(figsize=(12, 8))
scatter = plt.scatter(portfolio_results['Volatility'],
                     portfolio_results['Returns'],
                     c=portfolio_results['Sharpe_Ratio'],
                     cmap='viridis', alpha=0.6)
plt.colorbar(scatter, label='Sharpe Ratio')
plt.xlabel('Volatility (Risk)')
plt.ylabel('Expected Return')
plt.title('Efficient Frontier - Random Portfolios')
plt.show()

Efficient Frontier Đường biên hiệu quả: Đường cong biểu diễn lợi nhuận kỳ vọng tối đa có thể đạt được với một mức rủi ro nhất định.

Cấp độ 3: Bậc thầy tối ưu hóa - Tìm danh mục hoàn hảo

Lấy mẫu ngẫu nhiên thì vui, nhưng chúng ta muốn có giải pháp tối ưu về mặt toán học. Đã đến lúc dùng đến vũ khí hạng nặng - tối ưu hóa scipy!

def negative_sharpe_ratio(weights, returns, risk_free_rate=0.02):
    """
    Calculate negative Sharpe ratio (we minimize this)
    """
    portfolio_return, portfolio_vol = portfolio_performance(weights, returns)
    sharpe = (portfolio_return - risk_free_rate) / portfolio_vol
    return -sharpe

def minimize_volatility(weights, returns):
    """
    Calculate portfolio volatility (we minimize this)
    """
    _, portfolio_vol = portfolio_performance(weights, returns)
    return portfolio_vol

def portfolio_return_objective(weights, returns):
    """
    Calculate portfolio return (we maximize this)
    """
    portfolio_return, _ = portfolio_performance(weights, returns)
    return -portfolio_return  # Negative because we minimize

def optimize_portfolio(returns, objective='sharpe', target_return=None):
    """
    Optimize portfolio based on different objectives
    """
    num_assets = len(returns.columns)

    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})  # Weights sum to 1
    bounds = tuple((0, 1) for _ in range(num_assets))  # No short selling

    initial_guess = num_assets * [1. / num_assets]

    if objective == 'sharpe':
        result = minimize(negative_sharpe_ratio, initial_guess,
                         args=(returns,), method='SLSQP',
                         bounds=bounds, constraints=constraints)

    elif objective == 'min_vol':
        result = minimize(minimize_volatility, initial_guess,
                         args=(returns,), method='SLSQP',
                         bounds=bounds, constraints=constraints)

    elif objective == 'target_return':
        constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1},
                      {'type': 'eq', 'fun': lambda x: portfolio_performance(x, returns)[0] - target_return})

        result = minimize(minimize_volatility, initial_guess,
                         args=(returns,), method='SLSQP',
                         bounds=bounds, constraints=constraints)

    return result

max_sharpe = optimize_portfolio(returns, 'sharpe')
min_vol = optimize_portfolio(returns, 'min_vol')

print("🎯 Maximum Sharpe Ratio Portfolio:")
for i, symbol in enumerate(crypto_symbols):
    print(f"{symbol}: {max_sharpe.x[i]:.3f}")

ret_sharpe, vol_sharpe = portfolio_performance(max_sharpe.x, returns)
print(f"Return: {ret_sharpe:.2%}, Volatility: {vol_sharpe:.2%}")
print(f"Sharpe Ratio: {ret_sharpe/vol_sharpe:.3f}\n")

print("🛡️ Minimum Volatility Portfolio:")
for i, symbol in enumerate(crypto_symbols):
    print(f"{symbol}: {min_vol.x[i]:.3f}")

ret_minvol, vol_minvol = portfolio_performance(min_vol.x, returns)
print(f"Return: {ret_minvol:.2%}, Volatility: {vol_minvol:.2%}")

Cấp độ 4: Đa tài sản - Danh mục crypto thực tế

Hãy mở rộng lên danh mục crypto thực sự với nhiều tài sản:

crypto_portfolio = ['BTC-USD', 'ETH-USD', 'BNB-USD', 'ADA-USD', 'SOL-USD', 'DOT-USD']
prices_multi = get_crypto_data(crypto_portfolio, period="2y")
returns_multi = prices_multi.pct_change().dropna()

correlation_matrix = returns_multi.corr()

plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix, annot=True, cmap='RdYlBu_r', center=0)
plt.title('Crypto Asset Correlation Matrix')
plt.show()

def efficient_frontier(returns, num_portfolios=50):
    """
    Calculate the efficient frontier
    """
    ret_range = np.linspace(returns.mean().min()*252, returns.mean().max()*252, num_portfolios)

    efficient_portfolios = []

    for target_ret in ret_range:
        try:
            result = optimize_portfolio(returns, 'target_return', target_ret)
            if result.success:
                ret, vol = portfolio_performance(result.x, returns)
                efficient_portfolios.append([ret, vol, result.x])
        except:
            continue

    return np.array(efficient_portfolios)

efficient_port = efficient_frontier(returns_multi)

plt.figure(figsize=(14, 10))

random_results = generate_random_portfolios(returns_multi, 5000)
plt.scatter(random_results[1], random_results[0],
           c=random_results[2], cmap='viridis', alpha=0.3, s=10)

if len(efficient_port) > 0:
    plt.plot(efficient_port[:,1], efficient_port[:,0], 'r-', linewidth=3, label='Efficient Frontier')

max_sharpe_multi = optimize_portfolio(returns_multi, 'sharpe')
min_vol_multi = optimize_portfolio(returns_multi, 'min_vol')

ret_sharpe_multi, vol_sharpe_multi = portfolio_performance(max_sharpe_multi.x, returns_multi)
ret_minvol_multi, vol_minvol_multi = portfolio_performance(min_vol_multi.x, returns_multi)

plt.scatter(vol_sharpe_multi, ret_sharpe_multi, marker='*', color='gold', s=500, label='Max Sharpe')
plt.scatter(vol_minvol_multi, ret_minvol_multi, marker='*', color='red', s=500, label='Min Volatility')

plt.colorbar(label='Sharpe Ratio')
plt.xlabel('Volatility (Risk)')
plt.ylabel('Expected Return')
plt.title('Multi-Asset Crypto Portfolio Optimization')
plt.legend()
plt.show()

print("🚀 Optimal Multi-Asset Allocations:")
print("\nMaximum Sharpe Ratio Portfolio:")
sharpe_weights = pd.Series(max_sharpe_multi.x, index=crypto_portfolio).sort_values(ascending=False)
for asset, weight in sharpe_weights.items():
    if weight > 0.01:  # Only show significant allocations
        print(f"{asset}: {weight:.1%}")

print(f"\nPortfolio Metrics:")
print(f"Expected Return: {ret_sharpe_multi:.1%}")
print(f"Volatility: {vol_sharpe_multi:.1%}")
print(f"Sharpe Ratio: {ret_sharpe_multi/vol_sharpe_multi:.2f}")

Multi-Asset Portfolio Optimization Đa dạng hóa đa tài sản: Xây dựng danh mục vững chắc bằng cách kết hợp các tài sản crypto không tương quan vào một cấu trúc hình học ổn định.

Cấp độ 5: Kỹ thuật nâng cao - Black-Litterman và Cân bằng rủi ro

Dành cho những ninja tối ưu hóa danh mục thực sự, hãy triển khai một số kỹ thuật nâng cao:

def risk_parity_portfolio(returns):
    """
    Risk Parity Portfolio - each asset contributes equally to portfolio risk
    """
    def risk_contribution(weights, cov_matrix):
        portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
        marginal_contrib = np.dot(cov_matrix, weights) / portfolio_vol
        contrib = weights * marginal_contrib
        return contrib

    def risk_parity_objective(weights, cov_matrix):
        contrib = risk_contribution(weights, cov_matrix)
        target_contrib = np.ones(len(weights)) / len(weights)
        return np.sum((contrib - target_contrib)**2)

    num_assets = len(returns.columns)
    cov_matrix = returns.cov() * 252

    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    bounds = tuple((0.001, 1) for _ in range(num_assets))
    initial_guess = num_assets * [1. / num_assets]

    result = minimize(risk_parity_objective, initial_guess,
                     args=(cov_matrix,), method='SLSQP',
                     bounds=bounds, constraints=constraints)

    return result

risk_parity_result = risk_parity_portfolio(returns_multi)

print("⚖️ Risk Parity Portfolio:")
rp_weights = pd.Series(risk_parity_result.x, index=crypto_portfolio).sort_values(ascending=False)
for asset, weight in rp_weights.items():
    print(f"{asset}: {weight:.1%}")

ret_rp, vol_rp = portfolio_performance(risk_parity_result.x, returns_multi)
print(f"\nRisk Parity Metrics:")
print(f"Expected Return: {ret_rp:.1%}")
print(f"Volatility: {vol_rp:.1%}")
print(f"Sharpe Ratio: {ret_rp/vol_rp:.2f}")

def backtest_portfolio(weights, prices):
    """
    Simple backtest of portfolio performance
    """
    returns = prices.pct_change().dropna()
    portfolio_returns = (returns * weights).sum(axis=1)

    cumulative_returns = (1 + portfolio_returns).cumprod()

    total_return = cumulative_returns.iloc[-1] - 1
    annualized_return = (1 + total_return) ** (252 / len(portfolio_returns)) - 1
    annualized_vol = portfolio_returns.std() * np.sqrt(252)
    sharpe_ratio = annualized_return / annualized_vol

    max_dd = (cumulative_returns / cumulative_returns.expanding().max() - 1).min()

    return {
        'total_return': total_return,
        'annualized_return': annualized_return,
        'annualized_volatility': annualized_vol,
        'sharpe_ratio': sharpe_ratio,
        'max_drawdown': max_dd,
        'cumulative_returns': cumulative_returns
    }

strategies = {
    'Max Sharpe': max_sharpe_multi.x,
    'Min Volatility': min_vol_multi.x,
    'Risk Parity': risk_parity_result.x,
    'Equal Weight': np.ones(len(crypto_portfolio)) / len(crypto_portfolio)
}

plt.figure(figsize=(14, 8))

for name, weights in strategies.items():
    backtest_results = backtest_portfolio(weights, prices_multi)
    plt.plot(backtest_results['cumulative_returns'], label=f"{name} (Sharpe: {backtest_results['sharpe_ratio']:.2f})")

plt.title('Portfolio Strategy Backtests')
plt.xlabel('Date')
plt.ylabel('Cumulative Returns')
plt.legend()
plt.yscale('log')
plt.grid(True, alpha=0.3)
plt.show()

![Portfolio Strategy Backtests](/images/blog/markowitz-backtest.webp)
*Kiểm tra ngược thuật toán: Mô phỏng hiệu suất lịch sử để xác nhận các mô hình tối ưu hóa lý thuyết.*


performance_summary = pd.DataFrame()
for name, weights in strategies.items():
    results = backtest_portfolio(weights, prices_multi)
    performance_summary[name] = [
        f"{results['annualized_return']:.1%}",
        f"{results['annualized_volatility']:.1%}",
        f"{results['sharpe_ratio']:.2f}",
        f"{results['max_drawdown']:.1%}"
    ]

performance_summary.index = ['Annual Return', 'Annual Volatility', 'Sharpe Ratio', 'Max Drawdown']
print("\n📊 Strategy Performance Summary:")
print(performance_summary)

Kiểm tra thực tế: Những điều Markowitz không nói với bạn

Trước khi bạn đặt cược tất cả vào tối ưu hóa toán học, đây là một số sự thật khắc nghiệt về crypto:

1. Hiệu suất quá khứ ≠ Kết quả tương lai Thị trường crypto còn non trẻ và hỗn loạn. Những tương quan bạn đã tính toán? Chúng có thể đảo ngược qua đêm khi quy định thay đổi hoặc khi vụ hack lớn tiếp theo xảy ra.

2. Chi phí giao dịch quan trọng Tái cân bằng danh mục tốn tiền. Trong DeFi, phí gas có thể ăn hết lợi nhuận của bạn. Hãy tính yếu tố này vào chiến lược của bạn.

3. Vấn đề thanh khoản Không phải tất cả các crypto đều có thanh khoản như nhau. Altcoin vốn hóa nhỏ đó có thể trông rất tốt trong tối ưu hóa của bạn, nhưng hãy thử bán nó trong đợt crash.

4. Thay đổi chế độ thị trường Thị trường crypto có các "chế độ" khác nhau - thị trường tăng, thị trường giảm, thị trường đi ngang. Điều có hiệu quả trong chế độ này có thể không hiệu quả trong chế độ khác.

Mẹo triển khai thực tế

def practical_portfolio_rebalancing(target_weights, current_weights, threshold=0.05):
    """
    Only rebalance when weights drift beyond threshold
    """
    weight_diff = np.abs(target_weights - current_weights)
    needs_rebalancing = np.any(weight_diff > threshold)

    if needs_rebalancing:
        print("🔄 Rebalancing needed!")
        for i, (target, current) in enumerate(zip(target_weights, current_weights)):
            if abs(target - current) > threshold:
                print(f"Asset {i}: {current:.1%}{target:.1%}")
    else:
        print("✅ Portfolio within tolerance, no rebalancing needed")

    return needs_rebalancing

current_allocation = np.array([0.35, 0.25, 0.15, 0.10, 0.10, 0.05])
target_allocation = max_sharpe_multi.x

practical_portfolio_rebalancing(target_allocation, current_allocation)

Kết luận: Bộ công cụ tối ưu hóa danh mục của bạn

Bây giờ bạn đã có một bộ công cụ hoàn chỉnh để tối ưu hóa danh mục crypto:

  1. Tính toán cơ bản về rủi ro và lợi nhuận
  2. Trực quan hóa đường biên hiệu quả
  3. Tối ưu hóa toán học cho các mục tiêu khác nhau
  4. Chiến lược nâng cao như cân bằng rủi ro
  5. Khung kiểm tra ngược để xác nhận chiến lược của bạn
  6. Các cân nhắc thực tế cho triển khai thực tế

Những bài học quan trọng

  • Đa dạng hóa là bữa ăn miễn phí - bữa ăn miễn phí duy nhất trong đầu tư
  • Tối ưu hóa dựa trên khả năng chịu rủi ro của bạn - tỷ lệ Sharpe tối đa không phải lúc nào cũng tốt nhất cho bạn
  • Tái cân bằng có hệ thống nhưng không giao dịch quá nhiều
  • Khiêm tốn - các mô hình là công cụ, không phải quả cầu pha lê
  • Bắt đầu đơn giản và tăng độ phức tạp khi bạn học hỏi thêm

Hãy nhớ: Trong crypto, ngay cả những mô hình toán học tốt nhất cũng không thể dự đoán khi nào Elon sẽ tweet về Dogecoin hay khi nào sàn giao dịch tiếp theo bị hack. Hãy dùng lý thuyết danh mục làm nền tảng, nhưng luôn giữ một phần dự phòng và không bao giờ đầu tư nhiều hơn những gì bạn có thể chấp nhận mất.

Bây giờ hãy tiến lên và tối ưu hóa một cách có trách nhiệm! 🚀

Đọc thêm

Kho lưu trữ mã nguồn

Tất cả mã từ hướng dẫn này có sẵn trên GitHub: https://github.com/suenot/markowitz

Chúc tối ưu hóa vui vẻ! 📈

Tuyên bố miễn trừ trách nhiệm: Thông tin được cung cấp trong bài viết này chỉ nhằm mục đích giáo dục và thông tin, không cấu thành lời khuyên về tài chính, đầu tư hoặc giao dịch. Giao dịch tiền mã hóa tiềm ẩn rủi ro thua lỗ đáng kể.

Tác Giả

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Đi Trước Thị Trường

Đăng ký nhận bản tin của chúng tôi để có những thông tin chuyên sâu độc quyền về AI trading, phân tích thị trường và các cập nhật nền tảng.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất kỳ lúc nào.