问题描述
解决方案
在 QMT 中读取本地 CSV 文件并进行批量下单,通常推荐使用 Python 内置的 pandas 库来处理 CSV 数据,结合 QMT 的交易函数(如 order_shares)来实现。
以下是具体的实现步骤和代码示例。
1. 准备工作
首先,你需要准备一个 CSV 文件(例如命名为 orders.csv),文件内容应包含股票代码和数量。
CSV 文件格式示例(orders.csv):
code,volume
600000.SH,100
000001.SZ,200
600519.SH,100
注意:股票代码需要包含后缀(.SH 或 .SZ),数量为股数。
2. 策略代码实现
该策略会在 init 中初始化账户,并在 handlebar 中读取指定路径的 CSV 文件,遍历每一行数据进行下单。为了防止重复下单,代码中增加了一个标志位 ContextInfo.has_ordered。
# -*- coding: gbk -*-
import pandas as pd
import os
def init(ContextInfo):
# 1. 设置资金账号 (请替换为您真实的资金账号)
ContextInfo.account_id = '6000000000'
ContextInfo.set_account(ContextInfo.account_id)
# 2. 设置 CSV 文件路径 (建议使用绝对路径,防止路径错误)
# 注意:Windows路径请使用双斜杠 \\ 或反斜杠 /
ContextInfo.csv_path = 'D:/qmt_data/orders.csv'
# 3. 设置一个标志位,防止在每一根K线上重复下单
ContextInfo.has_ordered = False
print("策略初始化完成,准备读取文件下单...")
def handlebar(ContextInfo):
# 只在最后一根K线(实时行情)执行,且仅执行一次
if not ContextInfo.is_last_bar():
return
if ContextInfo.has_ordered:
return
# 检查文件是否存在
if not os.path.exists(ContextInfo.csv_path):
print(f"错误:找不到文件 {ContextInfo.csv_path}")
ContextInfo.has_ordered = True # 标记为已执行,避免报错刷屏
return
try:
# 1. 读取 CSV 文件
# encoding='gbk' 或 'utf-8' 取决于你的 CSV 保存格式
df = pd.read_csv(ContextInfo.csv_path, encoding='gbk')
print(f"成功读取文件,共 {len(df)} 条指令")
# 2. 遍历数据并下单
for index, row in df.iterrows():
stock_code = row['code'] # 获取代码列
volume = int(row['volume']) # 获取数量列
# 过滤掉无效数据
if volume <= 0:
continue
# 执行下单:按最新价买入指定股数
# order_shares(代码, 数量, 报价类型, 价格, ContextInfo, 账号)
# 'LATEST' 表示最新价,价格填 0 即可
order_shares(stock_code, volume, 'LATEST', 0, ContextInfo, ContextInfo.account_id)
print(f"已发送下单指令:代码 {stock_code}, 数量 {volume}")
# 3. 修改标志位,确保只运行一次
ContextInfo.has_ordered = True
print("所有文件内指令已处理完毕。")
except Exception as e:
print(f"读取文件或下单过程中发生错误: {e}")
ContextInfo.has_ordered = True
3. 代码关键点解析
# -*- coding: gbk -*-:- QMT 的 Python 编辑器默认编码通常为 GBK,加上这行可以防止中文注释乱码。
pd.read_csv:- 使用 pandas 读取数据非常方便。如果你的 CSV 文件包含中文字符(如列名),请注意
encoding参数。Excel 保存的 CSV 通常是gbk编码。
- 使用 pandas 读取数据非常方便。如果你的 CSV 文件包含中文字符(如列名),请注意
ContextInfo.has_ordered:- 非常重要。
handlebar函数会随着行情推送不断被调用。如果不加这个锁,策略会不断地重复读取文件并重复下单,导致资金耗尽或违规交易。
- 非常重要。
order_shares:- 这是最常用的下单函数之一,用于指定股数交易。
- 参数说明:
order_shares(stockcode, shares, style, price, ContextInfo, accId)。 - 示例中使用
'LATEST'以最新价下单。如果需要限价,可以将'LATEST'改为'FIX',并将后面的0改为具体的price。
- 路径问题:
- 建议使用绝对路径(如
D:/data/orders.csv),因为 QMT 运行时的相对路径可能不是你想象的当前文件夹。
- 建议使用绝对路径(如
4. 替代方案(QMT 原生 API)
QMT API 文档中也提供了一个专门用于读取“一篮子证券及数量”的接口,如果你不想使用 pandas,也可以使用 ContextInfo.load_stk_vol_list。
- 用法:
ContextInfo.load_stk_vol_list(filePath, fileName) - 文件格式要求:
代码,数量,代码,数量(成对出现)。 - 缺点:该接口对文件格式要求较死板(通常用于系统生成的篮子文件),且不如 pandas 处理数据灵活(例如 pandas 可以方便地过滤掉数量为 0 的行)。因此,推荐使用上面的 pandas 方法。
Q&A
Q: 如果我的 CSV 文件里只有数字代码(如 600000)没有后缀(.SH)怎么办?
A: 你可以在代码中进行字符串拼接处理。
# 在遍历循环中修改
stock_code = str(row['code']).zfill(6) # 补全6位
if stock_code.startswith('6'):
stock_code += '.SH'
else:
stock_code += '.SZ'
Q: 如何实现卖出?
A: 在 order_shares 函数中,数量传入负数即为卖出。你可以在 CSV 中将卖出的数量写为负数(如 -100),或者在代码逻辑中乘以 -1。
Q: 这个策略可以直接回测吗?
A: 可以。在回测模式下,order_shares 会根据历史行情撮合交易。但要注意回测时 ContextInfo.account_id 是虚拟账号,且需要确保 CSV 中的股票在回测时间段内是上市交易的。