🚀 AI 一键生成 joinquant 策略代码

如何使用 JoinQuant 优化器实现风险平价(Risk Parity)投资组合策略?

joinquant | 发布时间: 2025/12/4
以下内容由 EasyQuant 生成。

问题描述

请提供一个使用风险平价模型进行资产配置的策略示例。

解决方案

这是一个使用聚宽(JoinQuant)内置优化器 jqlib.optimizer 实现的**风险平价(Risk Parity)**资产配置策略示例。

策略逻辑说明

  1. 核心思想:风险平价模型旨在分配资金,使得投资组合中每个资产对总风险的贡献相等。这通常意味着波动率低的资产(如债券)会获得更高的权重,而波动率高的资产(如股票)权重较低。
  2. 标的选择:为了体现风险平价在大类资产配置中的优势,本示例选取了三个代表性的ETF作为标的:
    • 权益类:华泰柏瑞沪深300ETF (510300.XSHG)
    • 债券类:国泰上证5年期国债ETF (511010.XSHG)
    • 商品类:华安黄金ETF (518880.XSHG)
  3. 调仓频率:按月调仓。
  4. 优化器:使用聚宽内置的 portfolio_optimizer 函数配合 RiskParity 目标函数进行计算。

策略代码

# -*- coding: utf-8 -*-
from jqdata import *
from jqlib.optimizer import *
import pandas as pd

def initialize(context):
    """
    初始化函数
    """
    # 设定基准为沪深300
    set_benchmark('000300.XSHG')
    
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # 设定手续费:股票类ETF买卖佣金万分之三,无印花税
    set_order_cost(OrderCost(open_tax=0, close_tax=0, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 定义资产池:股票、债券、黄金
    g.etf_pool = [
        '510300.XSHG', # 沪深300ETF (权益)
        '511010.XSHG', # 5年期国债ETF (债券)
        '518880.XSHG'  # 黄金ETF (商品)
    ]
    
    # 设定调仓周期:每月第一个交易日09:30运行
    run_monthly(rebalance, monthday=1, time='09:30')

def rebalance(context):
    """
    调仓函数
    """
    log.info("开始进行风险平价调仓...")
    
    # 获取当前可交易的标的(过滤停牌或未上市)
    current_date = context.previous_date
    valid_securities = []
    for sec in g.etf_pool:
        # 获取标的信息,检查上市时间
        sec_info = get_security_info(sec)
        if sec_info.start_date < current_date:
            valid_securities.append(sec)
    
    if not valid_securities:
        log.warn("没有可交易的标的")
        return

    # 使用聚宽内置优化器计算风险平价权重
    # target=RiskParity: 风险平价模型
    # count=250: 使用过去250个交易日的数据计算协方差矩阵
    try:
        optimized_weight = portfolio_optimizer(
            date=current_date,
            securities=valid_securities,
            target=RiskParity(count=250, risk_budget=None), # None表示各资产风险贡献相等
            constraints=[
                WeightConstraint(low=0.0, high=1.0), # 权重在0到1之间(不做空)
                WeightEqualConstraint(limit=1.0)     # 总权重等于1(满仓)
            ],
            bounds=[Bound(0.0, 1.0)],
            default_port_weight_range=[0.0, 1.0],
            ftol=1e-09,
            return_none_if_fail=True
        )
    except Exception as e:
        log.error("优化器计算出错: %s" % e)
        return

    # 检查优化结果
    if optimized_weight is None:
        log.warn("组合优化失败,本次不调仓")
        return
    
    log.info("目标权重: \n%s" % optimized_weight)

    # 执行调仓
    # 1. 先卖出不在优化列表中的持仓(如果有)
    for stock in context.portfolio.positions:
        if stock not in optimized_weight.index:
            order_target_value(stock, 0)
    
    # 2. 根据优化权重调整持仓
    total_value = context.portfolio.total_value
    for stock in optimized_weight.index:
        target_value = total_value * optimized_weight[stock]
        order_target_value(stock, target_value)

def before_trading_start(context):
    """
    盘前处理
    """
    pass

def after_trading_end(context):
    """
    盘后处理
    """
    pass

代码关键点解析

  1. jqlib.optimizer: 这是聚宽提供的投资组合优化库。相比于自己手写 scipy.optimize,直接调用这个库更加稳定且方便。
  2. RiskParity(count=250):
    • 这是优化的目标函数。
    • count=250 表示使用过去250个交易日(约一年)的历史数据来计算资产的波动率和相关性矩阵。
    • risk_budget=None 默认意味着每个资产对组合总风险的贡献度是相等的(即 1/N 的风险贡献)。
  3. constraints (约束条件):
    • WeightConstraint(low=0.0, high=1.0): 限制单只标的权重不能为负(不做空),也不能超过100%。
    • WeightEqualConstraint(limit=1.0): 限制所有标的权重之和为1,即全额投资。
  4. 异常处理: 代码中加入了对 portfolio_optimizer 返回值的检查。如果因为数据不足(例如ETF刚上市)导致优化失败,函数会返回 None,策略会跳过本次调仓以避免报错。

常见问题 (Q&A)

Q: 为什么回测初期(如2013年)没有交易?
A: 策略中使用的ETF(如国债ETF 511010 或 黄金ETF 518880)在2013年左右才上市。RiskParity 模型需要过去250天的数据来计算协方差。如果数据长度不足,优化器会返回失败,策略将保持空仓直到数据充足。建议回测时间设置在2014年或2015年之后。

Q: 如何调整风险预算(Risk Budget)?
A: 如果你希望股票承担更多的风险,可以修改 RiskParity 中的 risk_budget 参数。例如,希望股票贡献60%风险,债券和黄金各20%,可以传入 risk_budget=pd.Series([0.6, 0.2, 0.2], index=g.etf_pool)

Q: 可以加入更多资产吗?
A: 可以。只需在 g.etf_pool 列表中添加对应的标的代码即可。风险平价策略通常在资产相关性较低时表现最好,因此建议加入不同类别的资产(如纳指ETF、豆粕ETF等)。

Q: 为什么使用ETF而不是个股?
A: 风险平价模型依赖于对波动率和相关性的估计。个股的特质风险(非系统性风险)较高,且容易发生停牌或暴雷,导致协方差矩阵不稳定。ETF代表一篮子资产,波动特性更稳定,更适合用于大类资产配置策略。