馬科維茨投資組合理論之加密貨幣篇:從零到英雄
用Python構建最優加密貨幣投資組合 - 因為YOLO不是策略
馬科維茨投資組合理論:應用於數字資產的數學最佳化,以在給定的風險水平下實現回報最大化。
引言:為什麼你的加密貨幣投資組合需要數學(而不僅僅是感覺)
嘿,加密貨幣投機者們!👋
還記得那次因為埃隆發了條推特,你就把所有資金都投入狗狗幣的時候嗎?或者上次暴跌時你恐慌性拋售了所有持倉?是的,我們都經歷過。今天我們要討論一個可能拯救你投資組合(和理智)的東西:馬科維茨投資組合理論。
哈里·馬科維茨在1990年因為這套理論獲得了諾貝爾獎。基本思想是什麼?你可以通過數學方法最佳化你的投資組合,在任何給定的風險水平下獲得最佳可能回報。這就像為你的投資配備GPS,而不是蒙著眼睛開車。
核心概念:風險與回報(永恆的舞蹈)
在我們深入程式碼之前,讓我們理解我們要處理的內容:
- 預期回報:你期望賺到多少錢
- 風險(波動性):你的投資組合價值波動的幅度
- 相關性:不同資產之間的聯動程度
當你組合那些不完全同步移動的資產時,魔法就發生了。當比特幣暴跌時,也許一些DeFi代幣會表現得更好。這就是分散化為你工作的原理。
風險與回報:平衡波動的超額收益資產和穩定的基礎架構,從而實現最優幾何平均回報。
設定我們的Python環境
首先 - 讓我們準備工具:
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")
第一級:嬰兒步驟 - 簡單的投資組合數學
讓我們從基礎開始。我們將為一個簡單的2資產投資組合計算收益和風險。
def get_crypto_data(symbols, period="1y"):
"""
从Yahoo Finance获取加密货币数据
symbols: 加密货币符号列表 (例如, ['BTC-USD', 'ETH-USD'])
period: 数据时间段
"""
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("日收益率预览:")
print(returns.head())
現在讓我們計算一些基本的投資組合指標:
def portfolio_performance(weights, returns):
"""
计算投资组合收益和波动性
weights: 投资组合权重数组
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投资组合:")
print(f"预期年收益率:{ret_5050:.2%}")
print(f"年波动性:{vol_5050:.2%}")
print(f"夏普比率:{ret_5050/vol_5050:.3f}")
第二級:認真起來 - 有效前沿
現在我們開始做菜!有效前沿向我們展示了所有可能的最優投資組合。每個點代表在給定風險水平下的最佳可能回報。
def generate_random_portfolios(returns, num_portfolios=10000):
"""
生成随机投资组合组合
"""
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) # 标准化使其和为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='夏普比率')
plt.xlabel('波动性(风险)')
plt.ylabel('预期收益')
plt.title('有效前沿 - 随机投资组合')
plt.show()
有效前沿:代表特定風險水平下最大可能預期回報的曲線。
第三級:最佳化大師 - 尋找完美投資組合
隨機抽樣很有趣,但我們想要數學上的最優解。是時候拿出重型武器 - scipy最佳化!
def negative_sharpe_ratio(weights, returns, risk_free_rate=0.02):
"""
计算负夏普比率(我们要最小化它)
"""
portfolio_return, portfolio_vol = portfolio_performance(weights, returns)
sharpe = (portfolio_return - risk_free_rate) / portfolio_vol
return -sharpe
def minimize_volatility(weights, returns):
"""
计算投资组合波动性(我们要最小化它)
"""
_, portfolio_vol = portfolio_performance(weights, returns)
return portfolio_vol
def portfolio_return_objective(weights, returns):
"""
计算投资组合收益(我们要最大化它)
"""
portfolio_return, _ = portfolio_performance(weights, returns)
return -portfolio_return # 负数因为我们在最小化
def optimize_portfolio(returns, objective='sharpe', target_return=None):
"""
基于不同目标优化投资组合
"""
num_assets = len(returns.columns)
constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1}) # 权重和为1
bounds = tuple((0, 1) for _ in range(num_assets)) # 无卖空
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("🎯 最大夏普比率投资组合:")
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"收益:{ret_sharpe:.2%},波动性:{vol_sharpe:.2%}")
print(f"夏普比率:{ret_sharpe/vol_sharpe:.3f}\n")
print("🛡️ 最小波动性投资组合:")
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"收益:{ret_minvol:.2%},波动性:{vol_minvol:.2%}")
第四級:多資產瘋狂 - 真實的加密貨幣投資組合
讓我們將此擴充到具有多個資產的適當加密貨幣投資組合:
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('加密资产相关性矩阵')
plt.show()
def efficient_frontier(returns, num_portfolios=50):
"""
计算有效前沿
"""
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='有效前沿')
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='最大夏普')
plt.scatter(vol_minvol_multi, ret_minvol_multi, marker='*', color='red', s=500, label='最小波动性')
plt.colorbar(label='夏普比率')
plt.xlabel('波动性(风险)')
plt.ylabel('预期收益')
plt.title('多资产加密货币投资组合优化')
plt.legend()
plt.show()
print("🚀 最优多资产配置:")
print("\n最大夏普比率投资组合:")
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: # 只显示重要的配置
print(f"{asset}: {weight:.1%}")
print(f"\n投资组合指标:")
print(f"预期收益:{ret_sharpe_multi:.1%}")
print(f"波动性:{vol_sharpe_multi:.1%}")
print(f"夏普比率:{ret_sharpe_multi/vol_sharpe_multi:.2f}")
多資產資產配置:通過結合不相關的加密數字資產以建立一個健壯穩健的投資組合結構。
第五級:高階技術 - Black-Litterman和風險平價
對於真正的投資組合最佳化忍者,讓我們實現一些高階技術:
def risk_parity_portfolio(returns):
"""
风险平价投资组合 - 每个资产对投资组合风险贡献相等
"""
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("⚖️ 风险平价投资组合:")
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"\n风险平价指标:")
print(f"预期收益:{ret_rp:.1%}")
print(f"波动性:{vol_rp:.1%}")
print(f"夏普比率:{ret_rp/vol_rp:.2f}")
def backtest_portfolio(weights, prices):
"""
投资组合表现的简单回测
"""
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_multi.x,
'最小波动性': min_vol_multi.x,
'风险平价': risk_parity_result.x,
'等权重': 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} (夏普: {backtest_results['sharpe_ratio']:.2f})")
plt.title('投资组合策略回测')
plt.xlabel('日期')
plt.ylabel('累积收益')
plt.legend()
plt.yscale('log')
plt.grid(True, alpha=0.3)
plt.show()

*算法历史回测分析:模拟资产历史记录的表现用以评估和修改理论化的自动投资模型。*
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 = ['年化收益', '年化波动性', '夏普比率', '最大回撤']
print("\n📊 策略表现汇总:")
print(performance_summary)
現實檢查:馬科維茨沒有告訴你的事情
在你全力投入數學最佳化之前,這裡有一些關於加密貨幣的嚴酷真相:
1. 過去表現 ≠ 未來結果 加密貨幣市場年輕且混亂。你計算的那些相關性?當監管發生變化或下一次大型駭客攻擊發生時,它們可能在一夜之間翻轉。
2. 交易成本很重要 重新平衡你的投資組合需要錢。在DeFi中,gas費可能會吃掉你的午餐。將此納入你的策略。
3. 流動性問題 不是所有加密貨幣都同樣具有流動性。那個小盤山寨幣在你的最佳化中可能看起來很棒,但試著在崩盤期間賣掉它。
4. 制度變化 加密貨幣市場有不同的"制度" - 牛市、熊市、橫盤市場。在一個市場中有效的東西可能在另一個市場中無效。
實用實施技巧
def practical_portfolio_rebalancing(target_weights, current_weights, threshold=0.05):
"""
只有当权重偏离超过阈值时才重新平衡
"""
weight_diff = np.abs(target_weights - current_weights)
needs_rebalancing = np.any(weight_diff > threshold)
if needs_rebalancing:
print("🔄 需要重新平衡!")
for i, (target, current) in enumerate(zip(target_weights, current_weights)):
if abs(target - current) > threshold:
print(f"资产{i}: {current:.1%} → {target:.1%}")
else:
print("✅ 投资组合在容忍范围内,无需重新平衡")
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)
結論:你的投資組合最佳化工具包
你現在擁有了一套完整的加密貨幣投資組合最佳化工具包:
- 基本計算用於風險和收益
- 有效前沿視覺化
- 數學最佳化針對不同目標
- 高階策略如風險平價
- 回測框架驗證你的策略
- 實際考慮用於現實世界實施
關鍵要點
- 分散化是免費午餐 - 投資中唯一的免費午餐
- 根據你的風險承受能力最佳化 - 最大夏普對你來說不總是最好的
- 系統性地重新平衡但不要過度交易
- 保持謙遜 - 模型是工具,不是水晶球
- 從簡單開始隨著學習增加複雜性
記住:在加密貨幣中,即使是最好的數學模型也無法預測埃隆何時會發推特談論狗狗幣,或者下一個交易所何時會被駭客攻擊。將投資組合理論作為你的基礎,但總是保留一些火藥乾燥,永遠不要投資超過你能承受損失的金額。
現在去負責任地最佳化吧!🚀
延伸閱讀
程式碼倉庫
本教程的所有程式碼都可以在GitHub上找到:https://github.com/suenot/markowitz
祝最佳化愉快!📈
Authors
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.