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

如何在聚宽中根据多个指数与均线判断市场强弱,在强势期运行科技股策略、弱势期运行全球 ETF 动量策略,并实现模式切换清仓?

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

问题描述

聚宽科技股与 ETF 双模式策略:如何根据市场强弱自动切换?
如何在聚宽中根据多个指数与均线判断市场强弱,在强势期运行科技股策略、弱势期运行全球 ETF 动量策略,并实现模式切换清仓?

解决方案

在量化交易中,单一策略很难适应所有的市场环境。通过构建双模式切换策略,在市场强势期(多头市场)聚焦高弹性的科技股,在弱势期(空头/震荡市场)切换至避险或具有低相关性的全球ETF动量组合,是实现资产稳健增值的有效手段。

以下是在聚宽(JoinQuant)平台实现该策略的核心逻辑与完整代码。

一、 策略核心设计逻辑

  1. 市场强弱判别器(Market Regime Filter)

    • 选取代表大盘的沪深300指数(000300.XSHG)和代表科技成长股的创业板指(399006.XSHE)
    • 计算两个指数的20日均线(MA20)。
    • 强势期判定:当两个指数的收盘价均高于其MA20时,判定为强势市场,运行科技股策略
    • 弱势期判定:只要任意一个指数跌破MA20,则判定为弱势市场,运行全球ETF动量策略
  2. 科技股策略(强势期)

    • 股票池:从计算机、互联网行业(聚宽行业代码:I64, I65)中筛选市值前5的龙头科技股。
    • 均等权重配置。
  3. 全球ETF动量策略(弱势期)

    • 标的池:黄金ETF(511880.XSHG)、纳指ETF(513100.XSHG)、标普500ETF(513500.XSHG)、国债ETF(511010.XSHG)。
    • 动量计算:计算过去20个交易日的涨幅,买入涨幅最大的前2只ETF。
  4. 模式切换与清仓逻辑(Switch & Liquidation)

    • 每日开盘前进行市场强弱判断。
    • 若发生模式切换(如强势转弱势,或弱势转强势),必须先将当前持仓全部清仓,释放资金后再买入新模式的目标标的,避免资金占用和交叉持仓。

二、 聚宽 Python 策略源码

请在聚宽回测环境中运行以下代码(建议回测频率:分钟,开启真实价格模式):

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

def initialize(context):
    # 1. 基础设置
    set_benchmark('000300.XSHG')
    set_option('use_real_price', True) # 开启真实价格模式
    
    # 2. 交易税费设置
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 3. 策略参数定义
    g.filter_indexes = ['000300.XSHG', '399006.XSHE'] # 强弱判别指数:沪深300,创业板指
    g.ma_window = 20 # 均线周期
    
    # 科技股池(计算机与互联网行业)
    g.tech_industry = ['I64', 'I65']
    
    # 全球/避险 ETF 池
    g.etf_pool = [
        '511880.XSHG', # 黄金ETF
        '513100.XSHG', # 纳指ETF
        '513500.XSHG', # 标普500ETF
        '511010.XSHG'  # 国债ETF
    ]
    
    # 记录当前运行模式:'INIT' (初始), 'TECH' (科技股), 'ETF' (动量ETF)
    g.current_mode = 'INIT'
    
    # 定时运行:每天9:30运行
    run_daily(market_open, time='9:30')

def market_open(context):
    # 1. 判断市场强弱
    is_strong = check_market_strength(context)
    target_mode = 'TECH' if is_strong else 'ETF'
    
    log.info(f"当前市场判定: {'强势' if is_strong else '弱势'} | 目标模式: {target_mode} | 当前持仓模式: {g.current_mode}")
    
    # 2. 模式切换清仓逻辑
    if g.current_mode != target_mode:
        log.info(f"检测到模式切换!从 {g.current_mode} 切换至 {target_mode}。执行全仓清空...")
        for stock in list(context.portfolio.positions.keys()):
            order_target(stock, 0)
        g.current_mode = target_mode
        # 清仓后直接返回,等待下一分钟或下一交易日再建仓,确保资金完全释放
        return

    # 3. 执行对应模式的建仓/调仓
    cash = context.portfolio.available_cash
    if cash < 10000: # 资金不足则不调仓
        return
        
    if g.current_mode == 'TECH':
        # 强势期:买入龙头科技股
        tech_stocks = get_tech_leaders(context)
        if tech_stocks:
            log.info(f"强势期建仓科技股: {tech_stocks}")
            target_value = context.portfolio.total_value / len(tech_stocks)
            # 卖出不在目标池的股票
            for stock in list(context.portfolio.positions.keys()):
                if stock not in tech_stocks:
                    order_target(stock, 0)
            # 买入目标股票
            for stock in tech_stocks:
                order_target_value(stock, target_value)
                
    elif g.current_mode == 'ETF':
        # 弱势期:全球ETF动量轮动
        target_etfs = get_etf_momentum(context)
        if target_etfs:
            log.info(f"弱势期建仓动量ETF: {target_etfs}")
            target_value = context.portfolio.total_value / len(target_etfs)
            # 卖出不在目标池的ETF
            for stock in list(context.portfolio.positions.keys()):
                if stock not in target_etfs:
                    order_target(stock, 0)
            # 买入目标ETF
            for stock in target_etfs:
                order_target_value(stock, target_value)

def check_market_strength(context):
    """根据多指数均线判断市场强弱"""
    for index in g.filter_indexes:
        # 获取历史收盘价
        close_data = attribute_history(index, g.ma_window + 1, '1d', ['close'])
        ma = close_data['close'][:-1].mean() # 不包含当天的MA
        current_price = close_data['close'][-1]
        
        # 只要有一个指数低于均线,即判定为弱势
        if current_price < ma:
            return False
    return True

def get_tech_leaders(context):
    """获取市值前5的龙头科技股"""
    stocks = []
    for ind in g.tech_industry:
        stocks.extend(get_industry_stocks(ind, date=context.current_dt))
    
    # 查询市值
    q = query(valuation.code, valuation.market_cap).filter(
        valuation.code.in_(stocks)
    ).order_by(valuation.market_cap.desc()).limit(5)
    
    df = get_fundamentals(q, date=context.previous_date)
    return list(df['code'])

def get_etf_momentum(context):
    """计算ETF过去20天涨幅,返回前2名"""
    momentum_dict = {}
    for etf in g.etf_pool:
        close_data = attribute_history(etf, 21, '1d', ['close'])
        if len(close_data) >= 21:
            return_20d = (close_data['close'][-1] - close_data['close'][0]) / close_data['close'][0]
            momentum_dict[etf] = return_20d
            
    # 按涨幅降序排序,取前2名
    sorted_etfs = sorted(momentum_dict.items(), key=lambda x: x[1], reverse=True)
    return [sorted_etfs[0][0], sorted_etfs[1][0]]

三、 策略关键点解析

  1. 避免未来函数
    check_market_strength 中,计算均线时使用了 close_data['close'][:-1].mean(),排除了当天未收盘的价格,确保回测的真实性。
  2. 安全切换机制
    当检测到 g.current_mode != target_mode 时,策略会执行 order_target(stock, 0) 清仓,并执行 return 结束当天的操作。这样可以防止在同一天内由于清仓资金未及时到账而导致建仓失败,在下一个交易日资金充足时再行建仓,更加符合实盘逻辑。