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

qmt 平台如何使用 ext_data_rank 实现截面轮动策略?(附 Python 源码)

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

问题描述



Title: qmt 截面轮动策略:使用 ext_data_rank 结合自定义指标排名选股

Question: qmt 的 ext_data_rank 函数怎么用?如何根据扩展指标排名做股票轮动?

解决方案

qmt 平台如何使用 ext_data_rank 实现截面轮动策略?

在量化投资中,截面轮动策略(Cross-sectional Momentum/Rotation)是一种非常经典的策略。它通过在同一时间截面上,对全市场或特定股票池中的股票按照某种指标(如动量、估值、技术指标等)进行排序,选择排名靠前的股票买入,排名靠后的股票卖出或空仓。

在国信证券 QMT (Quantitative Trading System) 平台中,ext_data_rank 函数是实现截面轮动策略的核心利器。本文将详细介绍该函数的用法,并给出一个完整的截面轮动策略实现方案。


1. 核心函数解析:ext_data_rank

ext_data_rank 用于获取引用的扩展数据(如自定义的技术指标、因子值等)在当前时间截面上,所有品种中的排名。通过排名,我们可以非常方便地筛选出强势股或高性价比标的。

函数原型

ext_data_rank(extdataname, stockcode, deviation, ContextInfo)

参数说明

  • extdataname (string): 扩展数据名称(需提前在客户端的“扩展数据”中配置并计算,例如自定义的 myccimymomentum 等)。
  • stockcode (string): 股票代码,格式为 'code.market',例如 '600000.SH'
  • deviation (number): K 线偏移量。0 表示不偏移(当前 K 线),N 表示向右偏移 N 根 K 线,-N 表示向左偏移 N 根 K 线。
  • ContextInfo: 策略运行环境对象。

返回值

  • number: 返回该股票在指定扩展数据中的截面排名(数值越小通常代表排名越靠前,具体取决于扩展数据的定义)。

2. 截面轮动策略设计思路

一个标准的截面轮动策略通常包含以下步骤:

  1. 设定股票池:确定参与轮动的股票范围(如沪深300成份股)。
  2. 获取截面排名:在每个调仓周期,使用 ext_data_rank 获取股票池中所有股票的自定义指标排名。
  3. 筛选标的:选择排名最靠前(如前 5 名)且未停牌的股票作为目标持仓。
  4. 调仓交易:卖出不在目标持仓中的股票,买入新的目标股票,实现等权重或比例配置。

3. 策略 Python 源码实现

以下是基于 QMT 平台编写的截面轮动策略示例。策略每期获取股票池中指标排名靠前的股票并进行轮动调仓:

#encoding:gbk
import pandas as pd

def init(ContextInfo):
    # 1. 设定股票池(以沪深300成份股为例)
    # 实际运行前请确保已下载历史数据
    ContextInfo.stock_pool = ContextInfo.get_stock_list_in_sector('沪深300')
    ContextInfo.set_universe(ContextInfo.stock_pool)
    
    # 2. 设定交易账号
    ContextInfo.account_id = '6000000248' # 请替换为您的真实/模拟资金账号
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 3. 策略参数设置
    ContextInfo.hold_num = 5          # 持股数量
    ContextInfo.ext_data_name = 'mycci' # 预先在QMT配置好的扩展数据名称
    
    print("策略初始化完成,股票池数量:", len(ContextInfo.stock_pool))

def handlebar(ContextInfo):
    # 仅在最后一根 K 线的第一个 tick 触发调仓(避免盘中频繁抖动)
    if not ContextInfo.is_last_bar():
        return
        
    print("----------------- 收到调仓信号 -----------------")
    
    # 1. 获取当前截面所有股票的排名
    rank_dict = {}
    for stock in ContextInfo.stock_pool:
        # 排除停牌股
        if ContextInfo.is_suspended_stock(stock):
            continue
        
        # 获取该股在 'mycci' 扩展数据中的截面排名
        rank = ext_data_rank(ContextInfo.ext_data_name, stock, 0, ContextInfo)
        
        # 过滤掉无有效排名的数据
        if rank is not None and rank > 0:
            rank_dict[stock] = rank
            
    if not rank_dict:
        print("未获取到有效的扩展数据排名,跳过本期调仓。")
        return
        
    # 2. 按照排名从小到大排序(选择排名靠前的股票)
    sorted_stocks = sorted(rank_dict.items(), key=lambda x: x[1])
    target_holdings = [stock for stock, rank in sorted_stocks[:ContextInfo.hold_num]]
    
    print("本期目标持仓股票:", target_holdings)
    
    # 3. 获取当前实际持仓
    position_info = get_trade_detail_data(ContextInfo.account_id, 'STOCK', 'POSITION')
    current_holdings = [pos.m_strInstrumentID for pos in position_info if pos.m_nVolume > 0]
    print("当前实际持仓股票:", current_holdings)
    
    # 4. 执行调仓交易
    # 4.1 卖出不在目标持仓中的股票
    for stock in current_holdings:
        if stock not in target_holdings:
            print(f"卖出股票: {stock}")
            # 使用指定目标比例交易,调仓到 0% 仓位即卖出
            order_target_percent(stock, 0.0, 'LATEST', ContextInfo, ContextInfo.account_id)
            
    # 4.2 买入目标持仓中未持有的股票(等权重分配)
    target_percent = 1.0 / ContextInfo.hold_num
    for stock in target_holdings:
        print(f"调仓/买入股票: {stock},目标比例: {target_percent:.2%}")
        order_target_percent(stock, target_percent, 'LATEST', ContextInfo, ContextInfo.account_id)

4. 避坑指南与注意事项

  1. 扩展数据必须提前创建:在使用 ext_data_rank 之前,必须在 QMT 客户端的 “操作” -> “扩展数据管理” 中新建对应的扩展数据(如 mycci),并勾选需要计算的股票池与周期。否则代码运行会返回 None 或报错。
  2. 数据补充:截面轮动依赖全市场或板块的完整历史行情。运行策略前,务必通过 “数据管理” 补充股票池中所有股票对应周期的历史行情数据。
  3. 停牌股处理:在获取排名和下单前,一定要使用 ContextInfo.is_suspended_stock() 剔除停牌股,避免因无法交易导致资金占用或策略失真。