🚀 AI 一键生成 qmt 策略代码

如何利用换手率指标构建选股策略?(附 QMT Python 源码)

qmt | 发布时间: 2025/12/1
以下内容由 EasyQuant 生成。

问题描述

我想编写一个选股策略,筛选出最近一段时间内日均换手率最高的几只股票,并进行交易。

解决方案

这是一个基于 日均换手率(Turnover Rate) 的选股策略。

策略逻辑

  1. 股票池:以“沪深300”成分股为例(可根据需要修改为全A或其他板块)。
  2. 选股指标:计算过去 N 个交易日(例如 5 天)的平均换手率。
  3. 排序筛选:选取平均换手率最高的 M 只股票(例如 5 只)。
  4. 交易执行
    • 卖出:持仓中不在新选出的股票列表中的股票,全部卖出。
    • 买入/调仓:对新选出的股票进行等权重买入(每只股票占用总资金的 1/M)。
  5. 运行频率:日线级别,每日开盘或收盘进行检测和交易。

策略代码

# -*- coding: gbk -*-
import pandas as pd
import numpy as np

def init(ContextInfo):
    # 1. 设置策略参数
    ContextInfo.holding_num = 5       # 持仓股票数量
    ContextInfo.lookback_days = 5     # 计算过去多少天的平均换手率
    ContextInfo.index_code = '000300.SH' # 股票池:沪深300
    
    # 2. 设置账号(请替换为您自己的资金账号)
    # 格式如:'6000000248',如果是回测模式,这里会自动使用虚拟账号
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID' 
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 3. 设置回测参数(仅回测有效,实盘请在界面设置)
    # 费率设置:万三
    ContextInfo.set_commission(0, [0.0003, 0.0003, 0.0003, 0.0003, 0, 5])
    # 滑点设置:0.02元
    ContextInfo.set_slippage(1, 0.02)

def handlebar(ContextInfo):
    # 仅在日线周期的每根K线结束或新K线开始时运行
    # 这里我们选择在每根K线的第一个tick(即开盘)进行判断和交易
    if not ContextInfo.is_new_bar():
        return

    # 1. 获取当前时间
    barpos = ContextInfo.barpos
    timetag = ContextInfo.get_bar_timetag(barpos)
    current_date = timetag_to_datetime(timetag, '%Y%m%d')
    
    print(f'当前交易日期: {current_date}')

    # 2. 获取股票池(沪深300成分股)
    # 注意:get_sector 获取的是当前成分股,回测中若需历史成分股需用 get_sector(code, timetag)
    stock_list = ContextInfo.get_sector(ContextInfo.index_code, timetag)
    if not stock_list:
        print("未获取到成分股信息")
        return

    # 3. 获取交易日列表,用于确定计算换手率的时间窗口
    # 获取包括当前日期在内的过去 lookback_days + 5 天的交易日,确保数据足够
    # 我们需要用过去的数据来决策,所以截止日期是“昨天”
    trading_dates = ContextInfo.get_trading_dates(stock_list[0], '', current_date, ContextInfo.lookback_days + 1, '1d')
    
    if len(trading_dates) < ContextInfo.lookback_days + 1:
        print("历史数据不足,跳过")
        return
    
    # 取过去N天(不包含今天,因为今天要根据过去的数据下单)
    start_date = trading_dates[0]
    end_date = trading_dates[-2] # 昨天的日期

    # 4. 获取换手率数据
    # get_turnover_rate 返回的是 DataFrame,Index为日期,Columns为股票代码
    df_turnover = ContextInfo.get_turnover_rate(stock_list, start_date, end_date)
    
    if df_turnover.empty:
        print("未获取到换手率数据")
        return

    # 5. 计算平均换手率并排序
    # 计算每只股票的均值
    mean_turnover = df_turnover.mean()
    # 降序排列
    sorted_stocks = mean_turnover.sort_values(ascending=False)
    # 取前 N 只
    target_stocks = sorted_stocks.head(ContextInfo.holding_num).index.tolist()
    
    print(f"今日选股: {target_stocks}")

    # 6. 交易执行
    # 获取当前持仓
    positions = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
    current_holdings = [obj.m_strInstrumentID for obj in positions if obj.m_nVolume > 0]

    # 6.1 卖出不在目标列表中的股票
    for stock in current_holdings:
        if stock not in target_stocks:
            # 目标比例设为0,即清仓
            order_target_percent(stock, 0, 'fix', -1, ContextInfo, ContextInfo.account_id)
            print(f"卖出: {stock}")

    # 6.2 买入/调整目标股票
    # 等权重分配,每只股票目标仓位 = 1 / 持仓数量
    target_weight = 1.0 / ContextInfo.holding_num
    
    for stock in target_stocks:
        # 使用 order_target_percent 自动调整仓位到目标比例
        # 价格类型 'fix' 和价格 -1 在 order_target_percent 中通常代表以市价或最新价成交(视具体回测撮合设置)
        # 实盘中建议根据盘口价格指定,这里演示简化处理
        order_target_percent(stock, target_weight, 'fix', -1, ContextInfo, ContextInfo.account_id)
        print(f"买入/调仓: {stock} 目标权重: {target_weight}")

代码关键点解析

  1. 数据获取 (get_turnover_rate):

    • QMT 提供了直接获取换手率的接口 ContextInfo.get_turnover_rate,这比手动用成交量除以流通股本更方便且准确。
    • 注意:使用此接口前,请务必在 QMT 客户端的【数据管理】中下载补充好对应的日线行情数据财务数据
  2. 时间窗口处理:

    • 策略逻辑通常是“利用过去 N 天的数据,在今天开盘进行交易”。
    • 代码中使用 ContextInfo.get_trading_dates 获取交易日历,取 trading_dates[-2] 作为数据截止日期(即昨天),避免用到今天的未来数据(Look-ahead bias)。
  3. 交易函数 (order_target_percent):

    • 这是一个非常方便的高级交易函数。它会自动计算当前持仓与目标仓位的差额,并执行买入或卖出操作。
    • 例如:如果目标权重是 20%,当前持仓已经是 20%,则不操作;如果是 0%,则买入;如果是 30%,则卖出多余部分。
  4. 股票池 (get_sector):

    • 使用了 000300.SH (沪深300) 作为基础池。传入 timetag 参数是为了在回测时获取当时的历史成分股,防止幸存者偏差。

如何运行

  1. 打开 QMT 客户端,进入【模型研究】或【策略编辑器】。
  2. 新建一个 Python 策略,将上述代码复制进去。
  3. 修改 init 函数中的 ContextInfo.account_id 为您的真实资金账号(如果是模拟/回测,QMT会自动分配,但建议显式设置)。
  4. 补充数据:确保下载了沪深300成分股的日线数据。
  5. 点击【回测】查看历史表现,或在【模型交易】中加载进行实盘/模拟盘运行。

常见问题与建议

  • 数据缺失:如果打印出“未获取到换手率数据”,请检查是否在数据管理中下载了对应品种的日线数据。
  • 停牌处理get_turnover_rate 返回的数据中,停牌期间通常为 NaN 或 0。mean() 计算时会自动忽略 NaN,但如果某只股票长期停牌,换手率为 0,自然会被排序过滤掉,符合策略逻辑。
  • 实盘注意:实盘运行时,建议结合 ContextInfo.get_market_data_ex 获取实时行情来指定更精确的委托价格(如对手价、买一卖一价),而不是简单的使用默认价格。

Q&A

Q: 如何将股票池改为全市场股票?
A: 可以将 ContextInfo.index_code 修改为 '沪深A股',并使用 ContextInfo.get_stock_list_in_sector('沪深A股') 来获取全市场代码。但请注意,全市场计算量较大,回测速度会变慢。

Q: 为什么使用 order_target_percent 而不是 order_lots?
A: order_target_percent 更适合这种定期调仓的策略。它能自动根据当前总资产计算需要买入多少股,并且自动处理卖出逻辑(比如从持仓 1000 股调整到 0 股),大大简化了代码逻辑。

Q: 换手率数据需要额外下载吗?
A: 是的。QMT 的基础行情数据通常包含开高低收和成交量,但换手率可能涉及财务数据(流通股本)。建议在【数据管理】中同时补充【日线数据】和【财务数据】。