🚀 AI 一键生成 ptrade 策略代码
立即体验

Ptrade 平台如何实现风险平价(Risk Parity)资产配置策略?(附 Python 源码)

ptrade | 发布时间: 2026/7/31
以下内容由 EasyQuant 生成。

问题描述

请根据以下 SEO 主题生成完整页面内容:

Title: PTrade 资产配置:基于风险平价(Risk Parity)模型的动态调仓策略

Question: 如何在 ptrade 中使用 Python 计算多资产组合的风险贡献并实现风险平价调仓?

解决方案

PTrade 资产配置:基于风险平价(Risk Parity)模型的动态调仓策略

风险平价(Risk Parity)策略的核心思想是让组合中各个资产(或资产类别)对整体组合的风险贡献保持相等,而非简单地平均分配资金(如 1/N 策略)。这种方法能有效避免传统组合过度集中于高波动资产(如股票)风险的问题。

本文将介绍如何在 PTrade 平台上,利用 Python 及第三方科学计算库(如 scipy.optimize)求解资产权重,并结合 PTrade 的定时任务接口实现动态调仓。


一、核心数学原理

假设资产组合包含 $N$ 个资产,权重向量为 $w = [w_1, w_2, ..., w_N]^T$,资产收益率的协方差矩阵为 $\Sigma$。

  1. 组合总体波动率
    $$\sigma_p = \sqrt{w^T \Sigma w}$$

  2. 资产 $i$ 的边际风险贡献(MRC)
    $$MRC_i = \frac{\partial \sigma_p}{\partial w_i} = \frac{(\Sigma w)_i}{\sigma_p}$$

  3. 资产 $i$ 的总风险贡献(TRC)
    $$TRC_i = w_i \times MRC_i = \frac{w_i (\Sigma w)_i}{\sigma_p}$$

风险平价的目标是寻找一组权重 $w$,使得所有资产的 $TRC_i$ 相等,即对于任意 $i, j$ 满足 $TRC_i = TRC_j$。


二、PTrade 策略实现逻辑

  1. 初始化(initialize:定义多资产股票/ETF池(例如覆盖权益、债券、黄金等不同风险特征的标的),设置定时调仓任务(如每月第一个交易日执行)。
  2. 盘前准备(before_trading_start:过滤停牌及状态异常标的。
  3. 历史数据获取与协方差计算:调用 get_history 获取历史日线收盘价,计算各资产的日收益率及协方差矩阵 $\Sigma$。
  4. 权重优化求解:利用 scipy.optimize.minimize 求解目标函数,得到使各资产风险贡献相等的最终权重 $w$。
  5. 调仓执行(order_target_percent / order_target_value:结合 PTrade 的账户持仓与资产总值,将持仓调整至目标权重。

三、PTrade 完整策略源码

import numpy as np
import pandas as pd
from scipy.optimize import minimize

def initialize(context):
    # 设置多资产ETF标的池(涵盖股票、债券、黄金等)
    # 510300.SS: 沪深300ETF, 511010.SS: 国债ETF, 518880.SS: 黄金ETF, 513100.SS: 纳指ETF
    g.security_list = ['510300.SS', '511010.SS', '518880.SS', '513100.SS']
    set_universe(g.security_list)
    
    # 历史数据窗口大小(交易日)
    g.lookback_days = 120
    
    # 设置每月初第一个交易日执行调仓
    run_daily(context, rebalance_portfolio, time='09:35')

# 风险平价目标函数
def risk_parity_objective(weights, cov_matrix):
    # 计算组合波动率
    portfolio_vol = np.sqrt(np.dot(weights.T, np.dot(cov_matrix, weights)))
    # 计算各资产边际风险贡献
    mrc = np.dot(cov_matrix, weights) / portfolio_vol
    # 计算各资产总风险贡献
    trc = weights * mrc
    # 目标:最小化各资产TRC两两之间的方差/差距
    target_trc = portfolio_vol / len(weights)
    diff = trc - target_trc
    return np.sum(np.square(diff))

# 求解风险平价权重
def get_risk_parity_weights(cov_matrix):
    n = len(cov_matrix)
    init_weights = np.array([1.0 / n] * n)
    
    # 约束条件:权重之和为1
    constraints = ({'type': 'eq', 'fun': lambda w: np.sum(w) - 1.0})
    # 边界条件:做多限制,各资产权重在 0 到 1 之间
    bounds = tuple((0.0, 1.0) for _ in range(n))
    
    res = minimize(risk_parity_objective, init_weights, args=(cov_matrix,),
                   method='SLSQP', bounds=bounds, constraints=constraints)
    return res.x if res.success else init_weights

def rebalance_portfolio(context):
    # 仅在每月第一个交易日调仓
    current_dt = context.blotter.current_dt
    if g.last_month == current_dt.month:
        return
    
    log.info("开始执行月度风险平价调仓分析...")
    
    # 获取历史日收盘价
    price_df = get_history(g.lookback_days, '1d', 'close', security_list=g.security_list, fq='pre')
    if price_df is None or price_df.empty:
        log.error("历史行情数据获取失败")
        return
        
    # 处理数据格式,计算收益率与协方差矩阵
    returns = price_df.pct_change().dropna()
    cov_matrix = returns.cov().values * 252  # 年化协方差矩阵
    
    # 计算风险平价目标权重
    target_weights = get_risk_parity_weights(cov_matrix)
    
    # 获取组合总资产
    total_value = context.portfolio.portfolio_value
    
    # 执行调仓下单
    for idx, security in enumerate(g.security_list):
        weight = target_weights[idx]
        target_value = total_value * weight
        log.info("标的: %s, 目标权重: %.2f%%, 目标金额: %.2f" % (security, weight * 100, target_value))
        order_target_value(security, target_value)
        
    g.last_month = current_dt.month

def before_trading_start(context, data):
    if not hasattr(g, 'last_month'):
        g.last_month = -1

def handle_data(context, data):
    pass

四、策略注意事项与优化方向

  1. 第三方库支持:PTrade 内置支持 numpypandasscipy,可以直接调用相关数学求解器。
  2. 价格精度与委托限制:不同品种(股票、ETF、可转债)有不同的小数位数与最小交易单位需求,在实盘下单时,可通过自定义单量控制避免废单。
  3. 协方差矩阵收缩(Covariance Shrinkage):在资产数量较多时,样本协方差矩阵可能存在噪声,可引入 Ledoit-Wolf 收缩估计来提高稳健性。