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

QMT 平台如何利用 ext_data_rank 函数获取自定义指标截面排名并构建轮动策略?

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

问题描述

qmt 截面轮动策略:基于 ext_data_rank 指标全市场排名选股
如何利用 qmt 的 ext_data_rank 函数获取自定义扩展指标的截面排名,并构建行业轮动策略?

解决方案

QMT 截面轮动策略:基于 ext_data_rank 指标全市场排名选股

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

QMT 平台提供了强大的扩展数据引用函数 ext_data_rank(),能够直接获取指定自定义指标在全市场所有品种中的实时或历史截面排名。本文将详细介绍其工作原理,并给出一个基于该函数的截面轮动策略实现方案。


1. 核心 API 介绍:ext_data_rank

在 QMT 中,获取扩展数据排名的核心函数为 ext_data_rank

用法

ext_data_rank(extdataname, stockcode, deviation, ContextInfo)

参数说明

  • extdataname (string):自定义扩展数据的名称(例如在客户端中导入或计算好的 'mycci' 或其他因子名称)。
  • stockcode (string):股票代码,格式为 'code.market',例如 '600000.SH'
  • deviation (number):K 线偏移量。0 表示当前 K 线,-1 表示上一个 K 线。
  • ContextInfo:策略运行环境对象。

返回值

  • 返回一个 number,代表该股票的指标值在当前截面所有品种中的绝对排名(或百分比排名,取决于扩展数据的生成设置)。

2. 截面轮动策略构建思路

  1. 初始化环境:在 init 函数中设置股票池(Universe)、交易账号,并定义调仓周期(如每周或每月)。
  2. 获取截面排名:在 handlebar 中,遍历股票池中的所有股票,调用 ext_data_rank 获取它们在当前 Bar 上的自定义指标排名。
  3. 排序与筛选:对所有股票的排名进行升序或降序排列,筛选出排名最靠前(例如前 5 只)的股票作为目标持仓。
  4. 调仓交易
    • 卖出当前持有但不在目标持仓列表中的股票。
    • 买入目标持仓列表中尚未持有的股票,实现等权重或比例配置。

3. 策略 Python 源码示例

以下是一个完整的 QMT 截面轮动策略源码。该策略假设您已经在 QMT 客户端中创建并计算了名为 'mycci' 的扩展数据:

#coding:gbk
import pandas as pd

def init(ContextInfo):
    # 1. 设定基础股票池(例如沪深300成分股)
    # 实际运行前请确保已下载对应历史数据
    ContextInfo.stock_list = ['600000.SH', '600016.SH', '600019.SH', '600030.SH', '600036.SH', '600050.SH', '600104.SH', '600196.SH', '600276.SH', '600309.SH']
    ContextInfo.set_universe(ContextInfo.stock_list)
    
    # 2. 设定交易账号
    ContextInfo.account_id = '6000000248' # 请替换为您的实际资金账号
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 3. 设定策略参数
    ContextInfo.hold_count = 3  # 每次轮动持有的股票数量
    ContextInfo.ext_data_name = 'mycci'  # 预先在QMT中算好的扩展数据名称
    
    # 记录上次调仓时间,用于控制调仓频率(例如每5个Bar轮动一次)
    ContextInfo.last_trade_bar = -1
    ContextInfo.trade_interval = 5 

def handlebar(ContextInfo):
    # 获取当前Bar的索引
    bar_index = ContextInfo.barpos
    
    # 控制调仓频率
    if ContextInfo.last_trade_bar != -1 and (bar_index - ContextInfo.last_trade_bar) < ContextInfo.trade_interval:
        return
        
    # 确保不是历史无数据Bar
    if bar_index < 10:
        return

    # 1. 获取截面排名数据
    rank_dict = {}
    for stock in ContextInfo.stock_list:
        try:
            # 获取该股票在当前截面的排名值
            rank_val = ext_data_rank(ContextInfo.ext_data_name, stock, 0, ContextInfo)
            if rank_val is not None and not pd.isna(rank_val):
                rank_dict[stock] = rank_val
        except Exception as e:
            print(f"获取 {stock} 排名失败: {e}")

    if not rank_dict:
        return

    # 2. 对排名进行排序(假设排名数值越小越好,即升序排列)
    sorted_stocks = sorted(rank_dict.items(), key=lambda x: x[1])
    target_holdings = [stock for stock, rank in sorted_stocks[:ContextInfo.hold_count]]
    
    print(f"当前Bar位置: {bar_index}, 筛选出的目标持仓: {target_holdings}")

    # 3. 获取当前账户的实际持仓
    current_positions = []
    pos_objects = get_trade_detail_data(ContextInfo.account_id, 'STOCK', 'POSITION')
    for pos in pos_objects:
        if pos.m_nVolume > 0:
            current_positions.append(pos.m_strInstrumentID)

    # 4. 执行调仓交易
    # 4.1 卖出不在目标持仓中的股票
    for stock in current_positions:
        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_count
    for stock in target_holdings:
        print(f"买入/调整目标轮动股票: {stock}, 目标比例: {target_percent:.2%}")
        order_target_percent(stock, target_percent, 'LATEST', ContextInfo, ContextInfo.account_id)

    # 更新上次调仓的Bar位置
    ContextInfo.last_trade_bar = bar_index

4. 策略优化与注意事项

  1. 扩展数据预下载与计算:在使用 ext_data_rank 前,必须确保客户端的“数据管理”中已经补充了完整的历史行情数据,并且您自定义的扩展数据(如 'mycci')已经计算完毕。如果本地没有该扩展数据,函数将无法返回有效值。
  2. 停牌股处理:在实际交易中,应配合 ContextInfo.is_suspended_stock() 剔除停牌股,避免因无法交易导致调仓失败。
  3. 滑点与手续费:截面轮动策略调仓频率较高,回测时务必在策略编辑器中设置合理的滑点(Slippage)和手续费,以贴近真实交易成本。