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

聚宽平台如何实现每月第一个交易日 ETF 轮动与再平衡?(附 Python 源码)

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

问题描述



Title: 聚宽 月度调仓:如何在每月第一个交易日执行 ETF 轮动?

Question: 如何使用聚宽的交易日 API,在每月第一个交易日的指定时间执行 ETF 轮动和再平衡?

解决方案

在量化交易中,月度调仓资产再平衡是极其常见的策略。在聚宽(JoinQuant)平台上,我们不需要自己去写复杂的交易日判断逻辑,平台提供了强大的定时运行 API run_monthly,可以非常精准、高效地在每月第一个交易日的指定时间执行调仓任务。

本文将为您详细解析如何实现这一功能,并提供一个完整的 ETF 轮动与再平衡策略模板。


一、 核心 API 解析:run_monthly

要在每月第一个交易日执行策略,我们需要在 initialize 初始化函数中注册 run_monthly 函数:

run_monthly(func, monthday, time='9:30', reference_security='000300.XSHG')
  • func: 您自己定义的调仓执行函数(例如 market_open)。该函数必须且只能接受一个 context 参数。
  • monthday: 设为 1 表示每月的第一个交易日。如果是 -1 则表示每月倒数第一个交易日。
  • time: 具体执行时间,例如 '09:30''10:00'
  • reference_security: 时间参照标的,默认是 '000001.XSHG'(上证指数)。对于股票和 ETF 策略,保持默认或使用 '000300.XSHG' 即可。

二、 聚宽月度 ETF 轮动策略源码

以下是一个实用的 ETF 轮动与等权重再平衡策略。策略在每月第一个交易日 09:30 运行,计算候选 ETF 的过去 20 日收益率,选择表现最好的 2 只 ETF 进行等权重配置,并卖出不在持仓列表中的 ETF。

# 导入聚宽函数库
import pandas as pd
from jqdata import *

def initialize(context):
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    
    # 设定交易税费:买入万三,卖出万三加千一印花税
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 定义候选 ETF 资金池
    g.etf_pool = [
        '510300.XSHG',  # 沪深300 ETF
        '510500.XSHG',  # 中证500 ETF
        '159915.XSHE',  # 创业板 ETF
        '518880.XSHG'   # 黄金 ETF
    ]
    
    # 设定每月持有的最大 ETF 数量
    g.max_hold = 2
    
    # 注册定时任务:每月第一个交易日 09:30 执行调仓
    run_monthly(handle_etf_rotation, monthday=1, time='09:30', reference_security='000300.XSHG')

def handle_etf_rotation(context):
    log.info("----------------- 月度调仓开始 -----------------")
    log.info("当前调仓日期: %s" % context.current_dt.date())
    
    # 1. 获取候选 ETF 过去 20 个交易日的收盘价,计算动量(收益率)
    momentum_scores = {}
    for etf in g.etf_pool:
        # 获取历史数据
        hist = attribute_history(etf, 20, '1d', ['close'])
        if not hist.empty:
            # 计算 20 日收益率
            ret = (hist['close'][-1] - hist['close'][0]) / hist['close'][0]
            momentum_scores[etf] = ret
            
    # 2. 按照收益率从大到小排序,选出前 g.max_hold 个 ETF
    sorted_etfs = sorted(momentum_scores.items(), key=lambda x: x[1], reverse=True)
    target_list = [etf[0] for etf in sorted_etfs[:g.max_hold]]
    
    log.info("本月目标持仓 ETF: %s" % target_list)
    
    # 3. 卖出不在目标持仓列表中的 ETF
    current_positions = list(context.portfolio.positions.keys())
    for etf in current_positions:
        if etf not in target_list:
            order_target(etf, 0)
            log.info("卖出不符合轮动条件的 ETF: %s" % etf)
            
    # 4. 计算每个目标 ETF 的等权重价值,并执行再平衡
    if len(target_list) > 0:
        # 获取当前账户总资产
        total_value = context.portfolio.total_value
        # 每个 ETF 分配的资金
        target_value_per_etf = total_value / len(target_list)
        
        for etf in target_list:
            # 调整标的至目标权重价值
            order_target_value(etf, target_value_per_etf)
            log.info("再平衡买入/调整 ETF: %s 至目标价值: %.2f" % (etf, target_value_per_etf))
            
    log.info("----------------- 月度调仓结束 -----------------")

三、 策略设计要点与注意事项

  1. 避免未来函数:在 handle_etf_rotation 中,我们使用 attribute_history 获取历史数据。由于该函数在 09:30 运行,获取的 20 天数据不包含当天未收盘的 Bar,从而有效避免了未来数据。
  2. 真实价格模式:策略中调用了 set_option('use_real_price', True)。在进行 ETF 轮动和分红送配时,强烈建议开启此模式,以确保回测和模拟盘的价格与实际市场一致。
  3. 再平衡逻辑:使用 order_target_value 可以一步到位地将资产调整到目标金额,它会自动计算当前持仓与目标持仓的差额,自动执行买入或卖出,非常适合等权重再平衡策略。