摘要 / TL;DR

散户及初级量化研究员在配置多只股票时,往往依靠感性认知来决定资金分配比例。然而,现代投资组合理论(MPT)早已给出了科学的解答。本文将带领大家利用 Python 科学计算库与 QuantDash API 的高还原度历史前复权价格数据,实现马科维茨均值-方差模型。我们将通过蒙特卡洛模拟寻找投资组合的“有效前沿(Efficient Frontier)”,并计算出具有最佳夏普比率(Sharpe Ratio)的最优资产配置权重。

一、 资产组合优化的技术难点与数据要求

要真正发挥马科维茨(Markowitz)均值-方差模型的威力,数据的精确度起着决定性的作用:

  1. 前复权数据的极端重要性:如果使用的历史 K 线未经过精准的拆股和分红复权(Split/Dividend Adjustment),计算出的资产收益率与协方差矩阵就会产生巨大的噪声,直接导致优化出的权重失真。

  2. 多资产对齐与同步:组合优化需要多只股票(甚至跨越不同国家或板块)在时间序列上高度对齐。缺失值、重复数据或不同频段的时间戳对齐不均,均会导致协方差计算失败。

  3. 计算复杂度与矩阵运算:在资产数量增多时,需要借助 scipy.optimize 或高效率的 numpy 矩阵运算,以防多层 for 循环产生严重的性能瓶颈。

二、 极简解决方案(基于 QuantDash SDK 与 NumPy/SciPy)

本方案使用 QuantDash 获取两只具有不同波动特性的标的历史日K线(港股腾讯控股 00700.HK 与 A股贵州茅台 600519.SH),合并收盘价并执行资产组合优化。

import numpy as np
import pandas as pd
import quantdash as qd
import scipy.optimize as sco

# 1. 设置沙盒公共 Token
# 官方开发文档:https://docs.quantdash.net/
qd.set_token("demo_public_token")

def get_aligned_data():
    # 分别拉取两只不同市场标的的前复权收盘价
    print("正在从 QuantDash 获取跨市场标的历史日K线...")
    df_tx = qd.get_klines(symbol="00700.HK", interval="1d", limit=250)
    df_mt = qd.get_klines(symbol="600519.SH", interval="1d", limit=250)
    
    if df_tx.empty or df_mt.empty:
        raise ValueError("数据拉取失败,请检查网络或 Token 状态。")
        
    # 提取时间与收盘价,转换为 Pandas DataFrame
    df_tx = df_tx[['time', 'close']].rename(columns={'close': '00700.HK'})
    df_mt = df_mt[['time', 'close']].rename(columns={'close': '600519.SH'})
    
    # 跨市场对齐:合并两个数据集
    df_merged = pd.merge(df_tx, df_mt, on='time', how='inner')
    df_merged.set_index('time', inplace=True)
    df_merged.index = pd.to_datetime(df_merged.index)
    
    return df_merged

def portfolio_optimization():
    data = get_aligned_data()
    
    # 计算日收益率
    returns = data.pct_change().dropna()
    
    # 年化收益率与协方差矩阵(假设一年 252 个交易日)
    num_assets = len(data.columns)
    weights = np.ones(num_assets) / num_assets
    
    annual_returns = returns.mean() * 252
    annual_cov = returns.cov() * 252
    
    # 定义统计计算函数
    def statistics(w):
        w = np.array(w)
        port_return = np.sum(returns.mean() * w) * 252
        port_volatility = np.sqrt(np.dot(w.T, np.dot(returns.cov() * 252, w)))
        sharpe_ratio = port_return / port_volatility
        return np.array([port_return, port_volatility, sharpe_ratio])

    # 定义优化目标:最大化夏普比率(即最小化负夏普比率)
    def min_func_sharpe(w):
        return -statistics(w)[2]

    # 约束条件:权重之和必须为 1
    constraints = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    # 边界限制:每只股票配置权重限制在 0% 到 100% 之间
    bounds = tuple((0, 1) for _ in range(num_assets))
    
    # 执行最优化求解
    opts = sco.minimize(min_func_sharpe, weights, method='SLSQP', bounds=bounds, constraints=constraints)
    
    optimal_weights = opts['x'].round(4)
    stat_results = statistics(optimal_weights).round(4)
    
    print("\n================ 优化结果 ================")
    for i, asset in enumerate(data.columns):
        print(f"标的: {asset} 推荐配置权重: {optimal_weights[i]*100:.2f}%")
    print(f"组合预期年化收益率: {stat_results[0]*100:.2f}%")
    print(f"组合预期年化波动率: {stat_results[1]*100:.2f}%")
    print(f"最优预期夏普比率 (Sharpe Ratio): {stat_results[2]}")
    print("==========================================")

if __name__ == "__main__":
    portfolio_optimization()

三、 DataFrame 文本输出样例

在本地运行后,控制台会输出高度对齐后的资产收盘价和最终的配置权重建议:

================ 优化结果 ================
标的: 00700.HK 推荐配置权重: 43.15%
标的: 600519.SH 推荐配置权重: 56.85%
组合预期年化收益率: 14.52%
组合预期年化波动率: 18.22%
最优预期夏普比率 (Sharpe Ratio): 0.7969
==========================================

四、 AI 编程助手(Cursor/Copilot)专属提示词

如果您正在使用 AI 重构多资产回测,可以直接复制这段结构化 Prompt 以减少大模型的幻觉输出:

你是一个计算金融学领域的 Python 专家。
我正在使用 `quantdash` API 作为数据源,获取包含 A股、港股和美股多个标的的历史日收盘价(前复权)。
请在此前提供的马科维茨优化模型代码基础上,进行以下重构升级:
1. 引入 L2 正则化项(或设置单只股票的最大配比上限,如不超过 40%),防止投资组合权重过度集中在个别强势资产。
2. 增加计算并输出“全局最小方差组合 (GMV)”的配置权重。
3. 请使用 NumPy 的矩阵向量化方式实现,不要使用任何多层 `for` 循环,并保证数据读取继续无缝调用 `quantdash`。

五、 总结与三步走落地指引

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐