🚀 AI 一键生成 qmt 策略代码

QMT 平台如何构建一个多资产的时序动量策略?

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

问题描述

请帮我编写一个时序动量策略

解决方案

这是一个基于 QMT (Quantitative Trading Platform) API 编写的 时序动量 (Time Series Momentum) 策略。

策略逻辑说明

时序动量(Time Series Momentum)关注资产自身的历史表现,而不是与其他资产进行横向比较。

  1. 核心逻辑:如果某只股票在过去 N 天的收益率为正(即当前价格 > N天前价格),则做多(买入);如果收益率为负,则平仓或空仓。
  2. 回溯周期:代码中默认设置为 20天(约一个月)。
  3. 资金管理:采用等权重分配。如果有信号的股票数量为 M,则每只股票分配 1/股票池总数 的资金。
  4. 交易标的:示例中使用了一组具有代表性的股票(如贵州茅台、宁德时代等),您可以根据需要修改 ContextInfo.set_universe 中的列表。

QMT 策略代码

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

def init(ContextInfo):
    """
    初始化函数,策略启动时调用一次
    """
    # 1. 设置策略参数
    ContextInfo.lookback_period = 20  # 动量回溯周期 (例如20天)
    ContextInfo.holding_period = 1    # 调仓周期 (此处示例为每天判断)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID' # 【请务必修改】这里填写您的资金账号
    
    # 2. 设置股票池 (示例:选取几只代表性股票)
    # 实际使用时可以使用 ContextInfo.get_sector('000300.SH') 获取沪深300成分股
    stock_list = ['600519.SH', '300750.SZ', '000858.SZ', '601318.SH', '002594.SZ']
    ContextInfo.set_universe(stock_list)
    
    # 3. 设置账号 (实盘/回测必须)
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 4. 设置回测参数 (仅回测有效,实盘需在界面设置)
    # 设定初始资金
    ContextInfo.capital = 1000000
    # 设定手续费 (万三)
    ContextInfo.set_commission(0, [0.001, 0.001, 0.0003, 0.0003, 0, 5])
    # 设定滑点 (0.02元)
    ContextInfo.set_slippage(1, 0.02)

def handlebar(ContextInfo):
    """
    K线运行函数,每根K线执行一次
    """
    # 获取当前K线位置
    index = ContextInfo.barpos
    
    # 获取当前时间
    realtime = ContextInfo.get_bar_timetag(index)
    
    # 过滤掉历史K线中数据不足的情况,防止回溯越界
    if index < ContextInfo.lookback_period:
        return

    # 仅在每根K线结束时或特定时间点运行逻辑 (此处假设运行在日线周期)
    # 如果是实盘,建议结合 ContextInfo.is_last_bar() 和时间判断
    
    # 1. 获取股票池
    stock_list = ContextInfo.get_universe()
    if not stock_list:
        print("股票池为空")
        return

    # 2. 获取历史行情数据
    # 我们需要获取 (lookback_period + 1) 个数据,以便计算 N 天前的价格对比
    # 使用 get_market_data_ex 接口,这是QMT推荐的新接口
    # count = ContextInfo.lookback_period + 1
    # period = '1d' (日线)
    market_data = ContextInfo.get_market_data_ex(
        fields=['close'], 
        stock_code=stock_list, 
        period='1d', 
        count=ContextInfo.lookback_period + 1,
        dividend_type='front' # 前复权,计算收益率必须用复权数据
    )

    # 计算每只股票的目标仓位比例 (等权重)
    # 如果股票池有5只股票,每只目标仓位最大为 20%
    target_weight_per_stock = 1.0 / len(stock_list)

    for stock in stock_list:
        # 获取该股票的收盘价序列
        if stock not in market_data:
            continue
            
        df_price = market_data[stock]
        
        # 检查数据长度是否足够
        if len(df_price) < ContextInfo.lookback_period + 1:
            continue
            
        # 获取当前价格和N天前的价格
        # iloc[-1] 是最新价格,iloc[0] 是N天前的价格 (因为取了 N+1 个数据)
        current_price = df_price['close'].iloc[-1]
        past_price = df_price['close'].iloc[0]
        
        # 3. 计算动量 (Momentum)
        # 动量 = 当前价格 / N天前价格 - 1
        momentum = (current_price / past_price) - 1.0
        
        # 4. 生成交易信号并执行
        # 逻辑:动量 > 0 买入/持有;动量 <= 0 卖出/空仓
        
        if momentum > 0:
            # 动量为正,做多
            # 使用 order_target_percent 自动调整仓位到目标比例
            # 注意:实盘中 order_target_percent 需要配合 passorder 使用,但在回测中可直接调用
            # 这里演示标准的回测/仿真写法
            order_target_percent(stock, target_weight_per_stock, ContextInfo, ContextInfo.account_id)
            # print(f"时间: {realtime}, 标的: {stock}, 动量: {momentum:.4f}, 信号: 买入/持有")
            
        else:
            # 动量为负,清仓
            order_target_percent(stock, 0.0, ContextInfo, ContextInfo.account_id)
            # print(f"时间: {realtime}, 标的: {stock}, 动量: {momentum:.4f}, 信号: 卖出/空仓")

代码关键点解析

  1. # -*- coding: gbk -*-:

    • QMT 的 Python 编辑器默认使用 GBK 编码,这行代码必须放在第一行,否则中文注释会导致报错。
  2. ContextInfo.get_market_data_ex:

    • 这是获取行情的关键接口。
    • count=ContextInfo.lookback_period + 1: 如果我们要计算20天前的动量,我们需要取21根K线的数据(今天 + 过去20天)。
    • dividend_type='front': 非常重要。计算收益率和动量时,必须使用前复权数据,否则分红派息会导致价格跳空,产生错误的动量信号(例如除息日价格下跌会被误判为动量转负)。
  3. order_target_percent:

    • 这是一个非常方便的交易函数。它会将仓位调整至指定的百分比。
    • 如果当前没持仓且目标是 20%,它会买入。
    • 如果当前持仓 20% 且目标是 0%,它会卖出平仓。
    • 如果当前持仓 10% 且目标是 20%,它会加仓。
  4. ContextInfo.account_id:

    • 请务必将 'YOUR_ACCOUNT_ID' 替换为您实际的 QMT 资金账号,否则无法下单。

如何在 QMT 中运行

  1. 打开 QMT 客户端,进入【模型研究】或【策略研究】界面。
  2. 新建一个 Python 策略。
  3. 将上述代码完整复制粘贴进去。
  4. 修改 init 函数中的 ContextInfo.account_id 为你的真实账号。
  5. 在右侧设置回测参数(如周期选择“日线”,时间段选择最近两年),点击【回测】即可看到结果。
  6. 如果是实盘,请确保在【模型交易】中运行,并注意风险。

Q&A: 常见问题解答

Q: 为什么使用 get_market_data_ex 而不是 get_market_data?
A: get_market_data_ex 是 QMT 较新的接口,返回的数据结构更规范(通常是 Pandas DataFrame 字典),处理多只股票的数据时效率更高,且参数定义更清晰。

Q: 如何将策略改为“横截面动量”策略(选最强的几只)?
A: 目前的代码是时序动量(只要涨就买)。如果要改为横截面动量,需要在 handlebar 中先计算所有股票的 momentum 值,将其存入一个列表,然后使用 sort 进行排序,只对排名前 K 位的股票执行买入操作,其余股票执行卖出操作。

Q: 策略中的 order_target_percent 在实盘中有效吗?
A: 有效,但前提是您必须在 init 中正确设置了 set_account。此外,实盘交易中通常建议结合 passorder 函数以获得更精细的控制(如指定报价模式、盘口价格等),但在简单的日线策略中,order_target_percent 是最简洁的实现方式。

Q: 如何修改回溯周期?
A: 修改 init 函数中的 ContextInfo.lookback_period = 20,将其改为您想要的数值(如 60 代表 60日动量)。