3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个使用聚宽(JoinQuant)内置优化器 jqlib.optimizer 实现的**风险平价(Risk Parity)**资产配置策略示例。
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
jqlib.optimizer: 这是聚宽提供的投资组合优化库。相比于自己手写 scipy.optimize,直接调用这个库更加稳定且方便。RiskParity(count=250):
count=250 表示使用过去250个交易日(约一年)的历史数据来计算资产的波动率和相关性矩阵。risk_budget=None 默认意味着每个资产对组合总风险的贡献度是相等的(即 1/N 的风险贡献)。constraints (约束条件):
WeightConstraint(low=0.0, high=1.0): 限制单只标的权重不能为负(不做空),也不能超过100%。WeightEqualConstraint(limit=1.0): 限制所有标的权重之和为1,即全额投资。portfolio_optimizer 返回值的检查。如果因为数据不足(例如ETF刚上市)导致优化失败,函数会返回 None,策略会跳过本次调仓以避免报错。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代表一篮子资产,波动特性更稳定,更适合用于大类资产配置策略。