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

如何在聚宽 ETF 轮动策略中限制持仓数量和单品种仓位,并在弱势行情切换到防御仓位?

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

问题描述

聚宽多 ETF 轮动策略:如何通过仓位上限降低回撤?
如何在聚宽 ETF 轮动策略中限制持仓数量和单品种仓位,并在弱势行情切换到防御仓位?

解决方案

在量化交易中,ETF 轮动策略因其波动小、跟踪行业板块效果好而广受欢迎。然而,在市场系统性下跌时,若不加限制,轮动策略仍会面临较大的回撤。本文将教你如何在聚宽(JoinQuant)平台上,通过限制最大持仓数量设置单品种仓位上限以及引入大盘动能指标切换防御仓位来有效降低策略回撤。

一、 核心设计思路

  1. 标的池选择:选择代表性强、流动性好的宽基 ETF(如沪深300 ETF、创业板 ETF)和行业 ETF(如半导体 ETF、医药 ETF),并加入防御标的(如国债 ETF)。
  2. 动量排名:计算各 ETF 过去 N 天的收益率,进行降序排列。
  3. 仓位限制
    • 最大持仓数量:限制同时持有的 ETF 数量(例如最多 3 只)。
    • 单品种仓位上限:每只 ETF 的买入价值不超过总资产的固定比例(例如 30%),避免单标的暴雷。
  4. 大盘风控(防御切换):以沪深 300 指数作为大盘风向标。若沪深 300 价格低于其 M 日均线,判定为弱势行情,此时停止买入进攻性 ETF,并将仓位切换至防御标的(国债 ETF)或直接空仓。

二、 聚宽策略源码实现

以下是基于聚宽 API 编写的完整策略代码:

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

def initialize(context):
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    
    # 过滤掉比error级别低的log
    log.set_level('order', 'error')
    
    # 策略参数设置
    g.momentum_window = 20  # 动量计算周期
    g.max_hold_count = 3    # 最大持仓数量
    g.single_cap = 0.3      # 单品种仓位上限 (30%)
    
    # 候选进攻性 ETF 列表
    g.etf_pool = [
        '510300.XSHG',  # 300ETF
        '159915.XSHE',  # 创业板ETF
        '510500.XSHG',  # 500ETF
        '512880.XSHG',  # 证券ETF
        '512660.XSHG',  # 军工ETF
        '515000.XSHG',  # 科技ETF
    ]
    
    # 防御性标的
    g.defense_security = '511010.XSHG'  # 国债ETF
    # 大盘风控参考标的
    g.market_index = '000300.XSHG'
    g.market_ma_window = 60  # 大盘均线周期
    
    # 每月第一个交易日开盘运行调仓
    run_monthly(handle_rotation, monthday=1, time='09:30', reference_security='000300.XSHG')

def handle_rotation(context):
    # 1. 大盘风控判断
    close_data = attribute_history(g.market_index, g.market_ma_window, '1d', ['close'])
    current_market_price = close_data['close'][-1]
    market_ma = close_data['close'].mean()
    
    # 获取当前持仓
    current_positions = list(context.portfolio.positions.keys())
    
    # 如果大盘处于弱势(低于均线),切换到防御仓位
    if current_market_price < market_ma:
        log.info("大盘处于弱势,触发风控,切换至防御仓位。")
        # 卖出所有非防御标的
        for stock in current_positions:
            if stock != g.defense_security:
                order_target(stock, 0)
        # 全仓买入防御标的
        cash = context.portfolio.available_cash
        if cash > 0:
            order_value(g.defense_security, cash)
        return

    # 2. 大盘强势,执行进攻性 ETF 轮动
    # 计算候选池动量(过去N天收益率)
    scores = {}
    for etf in g.etf_pool:
        hist = attribute_history(etf, g.momentum_window + 1, '1d', ['close'])
        if len(hist) > 0:
            return_rate = (hist['close'][-1] - hist['close'][0]) / hist['close'][0]
            scores[etf] = return_rate
            
    # 按收益率降序排列,选出前 max_hold_count 个
    sorted_etfs = sorted(scores.items(), key=lambda x: x[1], reverse=True)
    target_etfs = [x[0] for x in sorted_etfs[:g.max_hold_count] if x[1] > 0] # 过滤掉收益为负的
    
    # 3. 调仓执行
    # 卖出不在目标列表中的持仓(包括防御标的)
    for stock in current_positions:
        if stock not in target_etfs:
            order_target(stock, 0)
            
    # 计算单品种分配资金(总资产 * 单品种上限)
    total_value = context.portfolio.total_value
    target_value_per_etf = total_value * g.single_cap
    
    # 买入目标 ETF
    for etf in target_etfs:
        # 调整到目标价值
        order_target_value(etf, target_value_per_etf)
        
    # 剩余资金(如有)可闲置或自动配置到防御标的
    remaining_cash = context.portfolio.available_cash
    if remaining_cash > total_value * 0.05: # 剩余资金大于5%时买入防御标的填充
        order_value(g.defense_security, remaining_cash)

三、 关键 API 与风控逻辑解析

  1. set_option('use_real_price', True)
    强烈建议开启真实价格模式。在 ETF 发生分红或折算时,系统会自动调整持仓数量,避免前复权回测带来的未来函数问题。
  2. order_target_value(security, value)
    该函数直接将标的仓位调整到指定的绝对价值。通过计算 total_value * g.single_cap,我们严格限制了单品种的资金占用比例(如 30%),即使某只行业 ETF 遭遇单日大跌,整体账户的损失也被控制在可承受范围内。
  3. 大盘风控切换
    策略通过 attribute_history 获取沪深 300 指数的历史收盘价,并计算 60 日均线。当指数跌破均线时,策略果断放弃进攻,通过 order_target(stock, 0) 清空高风险 ETF,并全仓买入 511010.XSHG(国债 ETF)获取低风险收益,从而在熊市中大幅降低回撤。