返回文章列表
September 25, 2025
5 分钟阅读

马科维茨投资组合理论之加密货币篇:从零到英雄

投资组合优化
马科维茨
加密货币
Python
量化金融
风险管理
分散化
有效前沿
夏普比率
算法交易

用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)

结论:你的投资组合优化工具包

你现在拥有了一套完整的加密货币投资组合优化工具包:

  1. 基本计算用于风险和收益
  2. 有效前沿可视化
  3. 数学优化针对不同目标
  4. 高级策略如风险平价
  5. 回测框架验证你的策略
  6. 实际考虑用于现实世界实施

关键要点

  • 分散化是免费午餐 - 投资中唯一的免费午餐
  • 根据你的风险承受能力优化 - 最大夏普对你来说不总是最好的
  • 系统性地重新平衡但不要过度交易
  • 保持谦逊 - 模型是工具,不是水晶球
  • 从简单开始随着学习增加复杂性

记住:在加密货币中,即使是最好的数学模型也无法预测埃隆何时会发推特谈论狗狗币,或者下一个交易所何时会被黑客攻击。将投资组合理论作为你的基础,但总是保留一些火药干燥,永远不要投资超过你能承受损失的金额。

现在去负责任地优化吧!🚀


免责声明:这仅供教育目的。不是财务建议。DYOR(自己做研究)。加密货币市场高度波动和风险。过去的表现不保证未来的结果。

延伸阅读

代码仓库

本教程的所有代码都可以在GitHub上找到:https://github.com/suenot/markowitz

祝优化愉快!📈

MarketMaker.cc Team

量化研究与策略

在 Telegram 中讨论