🚀 AI 一键生成 qmt 策略代码

如何在 QMT 中读取本地文件(CSV/TXT)实现批量下单?

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

问题描述

我有一个包含股票代码和买卖数量的 CSV 文件,怎么写一个策略来读取它并自动执行所有交易?

解决方案

这是一个非常实用的量化交易需求,通常被称为“篮子交易”或“清单交易”。

为了实现这个策略,我们需要完成以下步骤:

  1. 准备 CSV 文件:定义好文件的格式(例如包含“证券代码”和“交易数量”两列)。
  2. 编写 Python 策略:使用 pandas 读取文件,遍历每一行,并调用 QMT 的 order_shares 函数进行下单。
  3. 控制执行频率:设置逻辑确保策略启动后只执行一次,避免重复下单。

1. CSV 文件格式准备

假设您的 CSV 文件名为 trade_list.csv,内容格式如下(请确保包含表头):

stock_code volume
600000.SH 100
000001.SZ -100
600519.SH 200
  • stock_code: 完整的证券代码(包含后缀 .SH 或 .SZ)。
  • volume: 交易数量(正数为买入,负数为卖出)。

2. QMT 策略代码实现

以下是完整的策略代码。请将代码复制到 QMT 的策略编辑器中。

# -*- coding: gbk -*-
import pandas as pd
import os

def init(ContextInfo):
    """
    初始化函数,策略启动时执行一次
    """
    # 1. 设置资金账号 (请修改为您自己的资金账号)
    ContextInfo.accid = '6000000000' 
    ContextInfo.set_account(ContextInfo.accid)
    
    # 2. 设置 CSV 文件路径 (请修改为您的实际文件路径)
    # 注意:Windows路径建议使用双斜杠 \\ 或反斜杠 /,或者在字符串前加 r
    ContextInfo.csv_path = r'D:\QMT_Data\trade_list.csv'
    
    # 3. 设置一个标志位,防止重复交易
    # True 表示已经执行过,False 表示尚未执行
    ContextInfo.has_traded = False
    
    print("策略初始化完成,等待行情驱动...")

def handlebar(ContextInfo):
    """
    行情驱动函数,每根K线或Tick触发一次
    """
    # 1. 检查是否为实时行情的最后一根K线
    # 我们只在最新的时间点执行交易,不希望在回测历史K线时触发
    if not ContextInfo.is_last_bar():
        return

    # 2. 检查是否已经执行过交易
    if ContextInfo.has_traded:
        return

    # 3. 开始读取文件并交易
    trade_from_csv(ContextInfo)

    # 4. 标记为已执行,防止下一根Tick重复下单
    ContextInfo.has_traded = True

def trade_from_csv(ContextInfo):
    """
    读取CSV并执行交易的逻辑函数
    """
    file_path = ContextInfo.csv_path
    
    # 检查文件是否存在
    if not os.path.exists(file_path):
        print(f"错误:找不到文件 {file_path}")
        return

    try:
        # 读取 CSV 文件 (假设文件编码为 gbk 或 utf-8,根据实际情况调整)
        # 如果您的CSV是用Excel保存的,通常是 gbk 编码
        df = pd.read_csv(file_path, encoding='gbk')
        
        print(f"成功读取文件,共 {len(df)} 条交易指令")

        # 遍历每一行数据
        for index, row in df.iterrows():
            stock_code = row['stock_code'] # 获取代码
            volume = int(row['volume'])    # 获取数量
            
            # 过滤无效数据
            if volume == 0:
                continue
                
            # 执行下单
            # order_shares: 指定股数交易
            # 参数说明: 代码, 数量, 报价类型('LATEST'为最新价), 价格(最新价模式下填0), ContextInfo, 账号
            order_shares(stock_code, volume, 'LATEST', 0, ContextInfo, ContextInfo.accid)
            
            action = "买入" if volume > 0 else "卖出"
            print(f"指令发送: {action} {stock_code} 数量: {abs(volume)}")
            
    except Exception as e:
        print(f"读取文件或交易过程中发生错误: {e}")

3. 代码关键点解析

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

    • 这是 Python 文件的编码声明,QMT 环境通常要求使用 GBK 编码,这行代码能防止中文注释乱码。
  2. ContextInfo.has_traded 标志位:

    • handlebar 函数会随着行情(Tick)不断被调用。为了防止策略一启动就对着同一个清单疯狂重复下单,我们设置了一个 has_traded 变量。一旦交易完成,将其设为 True,后续的行情推送将直接被忽略。
  3. ContextInfo.is_last_bar():

    • 这个判断非常重要。当您点击“运行”时,QMT 可能会先快速回放一部分历史 K 线。加上这个判断,确保只有在“此时此刻”的最新行情下才读取文件下单,避免在历史时间点错误下单。
  4. pd.read_csv:

    • 利用 Pandas 库读取 CSV。注意 encoding 参数,如果您的 CSV 是 Excel 另存为的,通常是 'gbk';如果是其他编辑器生成的,可能是 'utf-8'。如果读取乱码,请尝试更改此参数。
  5. order_shares:

    • 这是最直接的下单函数。
    • 'LATEST' 代表以最新价下单。如果您希望以对手价下单,可以改为 'COMPETE',或者指定价格 'FIX'
    • volume 为正代表买入,为负代表卖出。

4. 使用前注意事项

  • 修改账号:请务必将代码中的 ContextInfo.accid = '6000000000' 修改为您真实的资金账号。
  • 修改路径:请将 ContextInfo.csv_path 修改为您 CSV 文件的实际绝对路径。
  • 实盘模式:请确保策略运行在“实盘”模式下(在策略编辑器顶部选择“实盘”而非“回测”),并且交易账号已登录。
  • 文件权限:确保 CSV 文件没有被 Excel 打开占用,否则 Python 可能无法读取。

Q&A

Q: 如果我想用限价单(指定价格)而不是最新价交易怎么办?
A: 您需要在 CSV 中增加一列 price,然后修改 order_shares 的调用方式。
代码修改如下:

# 假设 CSV 有一列叫 'price'
price = row['price']
# 使用 'FIX' 类型,并传入价格
order_shares(stock_code, volume, 'FIX', price, ContextInfo, ContextInfo.accid)

Q: 怎么让策略每天定时(比如早上9:30)自动读取文件交易?
A: 可以在 handlebar 中增加时间判断。

import datetime
# 获取当前K线时间
timetag = ContextInfo.get_bar_timetag(ContextInfo.barpos)
current_time = timetag_to_datetime(timetag, '%H:%M:%S')

# 比如在 09:30:00 到 09:31:00 之间且未交易过时执行
if "09:30:00" <= current_time <= "09:31:00" and not ContextInfo.has_traded:
    trade_from_csv(ContextInfo)
    ContextInfo.has_traded = True

Q: 为什么读取 CSV 时报错 UnicodeDecodeError
A: 这通常是编码问题。请尝试将 pd.read_csv(..., encoding='gbk') 改为 encoding='utf-8',或者反之。这取决于您的 CSV 文件是如何保存的。