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

如何在聚宽(JoinQuant)中构建全球ETF轮动策略?(附Python源码)

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

问题描述

聚宽全球ETF轮动策略如何实现?
如何在聚宽中构建包含海外市场和国内资产的全球ETF轮动策略,并处理不同标的的交易与数据问题?

解决方案

聚宽全球ETF轮动策略构建与实现指南

全球ETF轮动策略是一种经典的动量资产配置策略。通过在不同的资产类别(如国内A股、海外美股、港股、黄金、商品等)之间进行轮动,选择近期表现最强势的ETF进行持有,能够有效分散单一市场风险,获取全球资产增长红利。在聚宽(JoinQuant)平台上,我们可以通过跨境ETF(如纳指ETF、标普500ETF、德国30ETF等)轻松实现这一配置。


一、 策略核心设计

  1. 标的选择(全球资产覆盖)

    • 国内权益:沪深300 ETF (510300.XSHG)、中证500 ETF (510500.XSHG)
    • 海外权益:纳指ETF (513100.XSHG)、标普500 ETF (513500.XSHG)
    • 避险/商品:黄金ETF (518880.XSHG)
  2. 动量指标计算

    • 采用过去 $N$ 个交易日(如 20 日或 60 日)的累积收益率作为动量得分。
    • 每日或每周/月定期调仓,计算候选池中所有 ETF 的动量得分,选择得分最高(且为正)的 $M$ 只 ETF 均仓买入。
  3. 交易与数据处理注意事项

    • 动态复权(真实价格):跨境ETF常有拆分或分红,必须在 initialize 中开启 set_option('use_real_price', True),避免回测使用未来复权因子导致失真。
    • 交易时间一致性:虽然海外ETF跟踪的是境外指数,但其在A股交易时间(9:30-15:00)内正常交易,因此数据获取和下单逻辑与普通A股完全一致。
    • 滑点与税费:场内基金(ETF)不征收印花税,佣金通常比股票更低,且无最低5元限制。需通过 set_order_cost 针对 fund 类型进行单独设置。

二、 聚宽完整策略源码

以下是一个在聚宽平台可直接运行的全球ETF轮动策略示例:

import pandas as pd
import numpy as np
from jqdata import *

def initialize(context):
    # 1. 设定基准
    set_benchmark('000300.XSHG')
    # 2. 开启真实价格模式(动态复权),防止未来函数
    set_option('use_real_price', True)
    
    # 3. 设置ETF交易税费(基金免印花税,无最低5元限制)
    set_order_cost(OrderCost(open_tax=0, close_tax=0, 
                             open_commission=0.0001, close_commission=0.0001, 
                             min_commission=0), type='fund')
    
    # 4. 定义全球轮动资产池
    g.etf_pool = [
        '510300.XSHG',  # 沪深300 ETF (国内大盘)
        '510500.XSHG',  # 中证500 ETF (国内中盘)
        '513100.XSHG',  # 纳指ETF (美国科技)
        '513500.XSHG',  # 标普500 ETF (美国大盘)
        '518880.XSHG'   # 黄金ETF (避险商品)
    ]
    
    # 5. 策略参数设置
    g.momentum_window = 20  # 动量计算窗口(20个交易日)
    g.top_n = 1             # 每次持有最强势的1只ETF
    
    # 6. 定时运行:每周第一个交易日开盘进行调仓
    run_weekly(handle_rotation, weekday=1, time='09:30')

def handle_rotation(context):
    # 获取当前可用资金
    cash = context.portfolio.available_cash
    
    # 1. 计算各ETF的动量得分(过去N日收益率)
    scores = {}
    for etf in g.etf_pool:
        # 获取历史收盘价
        hist = attribute_history(etf, g.momentum_window + 1, '1d', ['close'])
        if len(hist) < g.momentum_window + 1:
            continue
        
        # 计算收益率
        start_price = hist['close'].iloc[0]
        end_price = hist['close'].iloc[-1]
        momentum_score = (end_price - start_price) / start_price
        scores[etf] = momentum_score
        
    # 2. 按照得分从大到小排序
    sorted_etfs = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    
    # 3. 筛选出得分大于0的强势资产
    target_etfs = [etf for etf, score in sorted_etfs if score > 0][:g.top_n]
    
    # 4. 执行调仓
    # 获取当前持仓
    current_positions = list(context.portfolio.positions.keys())
    
    # 卖出不在目标持仓中的ETF
    for etf in current_positions:
        if etf not in target_etfs:
            order_target(etf, 0)
            log.info(f"[调仓] 卖出未上榜资产: {etf}")
            
    # 买入目标ETF
    if target_etfs:
        # 均分资金
        target_value = context.portfolio.total_value / len(target_etfs)
        for etf in target_etfs:
            order_target_value(etf, target_value)
            log.info(f"[调仓] 买入/持有强势资产: {etf},目标价值: {target_value:.2f}")
    else:
        log.info("[调仓] 无动量大于0的资产,空仓避险")

三、 策略优化方向

  1. 引入均线过滤(双重保险):在动量得分大于0的基础上,可以增加限制条件,如目标ETF的价格必须在20日均线(MA20)之上才允许买入,否则空仓避险。
  2. 多周期动量结合:结合 20 日、60 日和 120 日动量进行加权打分,降低单一周期噪声干扰。
  3. 波动率倒数加权(Risk Parity):如果同时持有多个ETF,可以使用聚宽内置的投资组合优化器 portfolio_optimizer,采用风险平价模型分配权重,使组合波动更平稳。