用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)
结论:你的投资组合优化工具包
你现在拥有了一套完整的加密货币投资组合优化工具包:
- 基本计算用于风险和收益
- 有效前沿可视化
- 数学优化针对不同目标
- 高级策略如风险平价
- 回测框架验证你的策略
- 实际考虑用于现实世界实施
关键要点
- 分散化是免费午餐 - 投资中唯一的免费午餐
- 根据你的风险承受能力优化 - 最大夏普对你来说不总是最好的
- 系统性地重新平衡但不要过度交易
- 保持谦逊 - 模型是工具,不是水晶球
- 从简单开始随着学习增加复杂性
记住:在加密货币中,即使是最好的数学模型也无法预测埃隆何时会发推特谈论狗狗币,或者下一个交易所何时会被黑客攻击。将投资组合理论作为你的基础,但总是保留一些火药干燥,永远不要投资超过你能承受损失的金额。
现在去负责任地优化吧!🚀
免责声明:这仅供教育目的。不是财务建议。DYOR(自己做研究)。加密货币市场高度波动和风险。过去的表现不保证未来的结果。
延伸阅读
代码仓库
本教程的所有代码都可以在GitHub上找到:https://github.com/suenot/markowitz
祝优化愉快!📈
MarketMaker.cc Team
量化研究与策略