🚀 AI 一键生成 joinquant 策略代码

如何利用 JoinQuant 实现基于风险平价的股债轮动资产配置策略?

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

问题描述

请帮我编写一个简单的股债轮动策略,根据股票和债券的强弱进行切换。

解决方案

这是一个基于 动量效应(Momentum) 的简单股债轮动策略。

策略逻辑

  1. 标的选择
    • 股票代表:沪深300 ETF(510300.XSHG),代表大盘股表现。
    • 债券代表:国债 ETF(511010.XSHG),代表债券市场表现。
  2. 信号计算
    • 每天开盘时,计算两个标的过去 20个交易日 的涨跌幅。
  3. 交易逻辑
    • 如果 股票涨幅 > 债券涨幅:全仓持有股票 ETF,卖出债券 ETF。
    • 如果 债券涨幅 > 股票涨幅:全仓持有债券 ETF,卖出股票 ETF。
  4. 资金管理:每日轮动,持有强势资产。

策略代码

# -*- coding: utf-8 -*-
from jqdata import *

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='fund')
    
    # 定义全局变量
    # 股票标的:沪深300ETF
    g.stock_etf = '510300.XSHG'
    # 债券标的:5年期国债ETF
    g.bond_etf = '511010.XSHG'
    
    # 动量回看窗口天数(例如过去20天)
    g.lag = 20
    
    # 每天9:30执行交易逻辑
    run_daily(trade, '09:30')

def trade(context):
    """
    交易逻辑函数
    """
    # 获取历史数据
    # 注意:attribute_history 获取的是不包含当前时刻的过去数据(避免未来函数)
    # 获取过去 g.lag 天的收盘价
    stock_data = attribute_history(g.stock_etf, g.lag, '1d', ['close'])
    bond_data = attribute_history(g.bond_etf, g.lag, '1d', ['close'])
    
    # 如果数据长度不足(例如刚上市),则不操作
    if len(stock_data) < g.lag or len(bond_data) < g.lag:
        return

    # 计算收益率:(最新收盘价 - N天前收盘价) / N天前收盘价
    # .iloc[-1] 取最新一天数据,.iloc[0] 取最早一天数据
    stock_ret = (stock_data['close'].iloc[-1] - stock_data['close'].iloc[0]) / stock_data['close'].iloc[0]
    bond_ret = (bond_data['close'].iloc[-1] - bond_data['close'].iloc[0]) / bond_data['close'].iloc[0]
    
    # 记录日志,方便回测查看
    log.info("股票20日涨幅: %.4f, 债券20日涨幅: %.4f" % (stock_ret, bond_ret))
    
    # 确定目标持仓
    target_security = ''
    
    if stock_ret > bond_ret:
        target_security = g.stock_etf
        log.info("股票强于债券,计划持有股票")
    else:
        target_security = g.bond_etf
        log.info("债券强于股票,计划持有债券")
        
    # 执行交易
    # 1. 获取当前持仓中非目标标的,如果持有则卖出
    for security in context.portfolio.positions:
        if security != target_security:
            order_target_value(security, 0)
            
    # 2. 全仓买入目标标的
    # context.portfolio.total_value 包含现金和持仓总价值
    if target_security:
        order_target_value(target_security, context.portfolio.total_value)

代码解析

  1. initialize:

    • 设置了 set_order_cost,因为 ETF 交易免印花税,这里专门针对 type='fund' 设置了低佣金,使回测更接近真实环境。
    • 定义了两个 ETF 代码作为轮动对象。
  2. trade:

    • 使用 attribute_history 获取过去 20 天的收盘价。
    • 计算公式为 (当前价 - 20天前价) / 20天前价
    • 比较逻辑:直接比较两个收益率的大小。
    • 交易执行
      • 先遍历持仓,卖出非目标资产(例如当前持有债券但信号显示股票强,则卖出债券)。
      • 使用 order_target_value 将目标资产调整至账户总价值(即全仓买入)。

建议回测设置

  • 起始资金:100,000 元
  • 频率:天 (Daily)
  • 时间范围:建议包含牛熊周期,例如 2018-01-01 至今,以观察策略在不同市场环境下的表现。

Q&A

Q: 为什么要使用 ETF 而不是指数代码?
A: 指数(如 000300.XSHG)本身不能直接交易。在实盘或模拟交易中,我们需要买卖对应的基金产品(如 510300.XSHG)。虽然可以使用指数计算信号,但为了代码简洁和直接可交易,本策略直接使用 ETF 的历史价格来计算信号和交易。

Q: 如何降低交易频率?
A: 当前策略每天都会判断。如果两个资产收益率非常接近,可能会导致频繁买卖(震荡磨损)。可以通过以下方式优化:

  1. 增加阈值:只有当 股票收益 - 债券收益 > 1% 时才切换。
  2. 延长调仓周期:将 run_daily 改为 run_weekly,每周只判断一次。

Q: 什么是“二八轮动”?
A: 这是一个经典的变种。通常是指在 沪深300(大盘股)和 中证500(小盘股)以及 国债 之间轮动。如果大盘或小盘哪个强就买哪个,如果都走弱(比如收益率为负),则空仓或买入国债避险。