问题描述
解决方案
在QMT中,从外部文件(TXT或CSV)读取股票列表和数量并进行批量交易,通常有两种主要方式:
- 使用 Python 标准库或 Pandas 库(推荐):这种方式最灵活,支持标准的表格格式(如每行一个股票),易于编辑和维护。
- 使用 QMT 内置接口
load_stk_vol_list:这种方式需要文件符合特定的紧凑格式(代码和数量成对出现),适合特定场景。
下面我将为您提供一个基于 Pandas 读取 CSV 文件的完整策略代码,这是最通用且稳健的方法。
策略说明
- 文件格式:假设您的 CSV 文件名为
trade_list.csv,内容格式如下(包含表头):stock_code,volume 600000.SH,100 000001.SZ,200 600519.SH,100 - 运行逻辑:
- 策略初始化时设置账号。
- 在
handlebar中,当运行到最新一根K线(实时行情)且尚未交易过时,读取文件。 - 遍历文件内容,调用
order_shares下单。 - 设置标志位防止重复下单。
策略代码
# -*- coding: gbk -*-
import pandas as pd
import os
def init(ContextInfo):
# 1. 设置资金账号 (请替换为您真实的资金账号)
ContextInfo.accID = '6000000000'
ContextInfo.set_account(ContextInfo.accID)
# 2. 设置文件路径 (请修改为您的实际文件路径)
# 注意:Windows路径请使用双斜杠 \\ 或反斜杠 /
ContextInfo.file_path = 'D:/qmt_data/trade_list.csv'
# 3. 设置全局变量,控制是否已执行,防止重复下单
ContextInfo.has_traded = False
print("策略初始化完成,等待读取文件交易...")
def handlebar(ContextInfo):
# 只在实时行情的最后一根K线执行,且只执行一次
if not ContextInfo.is_last_bar() or ContextInfo.has_traded:
return
# 检查文件是否存在
if not os.path.exists(ContextInfo.file_path):
print(f"错误:文件未找到 - {ContextInfo.file_path}")
return
try:
# 读取CSV文件
# encoding='gbk' 或 'utf-8' 取决于您CSV文件的保存编码
df = pd.read_csv(ContextInfo.file_path, encoding='gbk')
print(f"成功读取文件,共 {len(df)} 条指令")
# 遍历每一行进行交易
for index, row in df.iterrows():
stock_code = row['stock_code'] # 对应CSV中的列名
volume = int(row['volume']) # 对应CSV中的列名
# 过滤掉无效数据
if not stock_code or volume <= 0:
continue
# 执行下单:指定股数交易
# order_shares(代码, 数量, 报价类型, 价格, ContextInfo, 账号)
# 'LATEST' 表示使用最新价下单,也可以改为 'FIX' 并传入指定价格
order_shares(stock_code, volume, 'LATEST', -1, ContextInfo, ContextInfo.accID)
print(f"已发送委托:{stock_code} 数量:{volume}")
# 标记为已交易,避免下一笔Tick重复执行
ContextInfo.has_traded = True
print("批量交易执行完毕")
except Exception as e:
print(f"读取文件或交易过程中发生错误: {e}")
关键点说明
-
文件路径:
- 代码中的
D:/qmt_data/trade_list.csv需要替换为您电脑上的实际路径。 - 如果是 TXT 文件,只要内容是用逗号分隔的(类似 CSV),
pd.read_csv同样可以读取。
- 代码中的
-
编码问题:
- 代码中使用了
# -*- coding: gbk -*-,这是 QMT Python 编辑器的标准编码。 pd.read_csv(..., encoding='gbk'):如果您的 CSV 文件是用 Excel 保存的,通常是 GBK 编码。如果是用现代编辑器保存的 UTF-8 格式,请将此处改为utf-8。
- 代码中使用了
-
下单函数:
- 本例使用的是
order_shares(按股数下单)。 - 如果您希望按金额下单,可以使用
order_value(stock_code, money, ...)。 - 如果您希望按目标仓位下单(例如调仓到指定股数),可以使用
order_target_shares(需自行封装逻辑或使用order_target_value)。
- 本例使用的是
-
防重复机制:
ContextInfo.has_traded变量非常重要。因为handlebar会随行情 Tick 不断触发,如果不加锁,会导致瞬间重复下无数单。
附:使用 QMT 内置 load_stk_vol_list 的方式(备选)
如果您偏好使用 QMT 原生 API 读取文件,可以使用 load_stk_vol_list。但请注意,该接口对文件内容格式要求较特殊。
文件内容格式(TXT/CSV):
必须是成对出现的 代码,数量,例如:
600000.SH,100,600004.SH,200
代码片段:
def handlebar(ContextInfo):
# ... 前置判断逻辑同上 ...
# 读取文件内容
# 参数1:文件夹路径,参数2:文件名
content = ContextInfo.load_stk_vol_list('D:/qmt_data/', 'trade_list.txt')
if content:
# content 返回的是一个长字符串,需要自行分割处理
items = content.split(',')
# 步长为2进行遍历
for i in range(0, len(items), 2):
if i + 1 < len(items):
code = items[i].strip()
vol = int(items[i+1].strip())
order_shares(code, vol, 'LATEST', -1, ContextInfo, ContextInfo.accID)
总结:建议使用第一种 Pandas 方式,因为它对文件格式的容错率更高,且代码逻辑更符合通用的量化开发习惯。
Q&A
Q: 如何实现定时读取文件进行交易(例如每天特定时间)?
A: 可以在 handlebar 中加入时间判断。例如:
import time
# 获取当前时间戳对应的HHMMSS
current_time = int(time.strftime("%H%M%S", time.localtime(time.time())))
# 比如在 10:00:00 到 10:00:05 之间触发
if 100000 <= current_time <= 100005 and not ContextInfo.has_traded:
# 执行读取和交易逻辑
pass
Q: 如果我想指定价格下单而不是用最新价怎么办?
A: 修改 order_shares 的参数。例如,如果 CSV 中有 price 列:
price = row['price']
# 'FIX' 表示限价单
order_shares(stock_code, volume, 'FIX', price, ContextInfo, ContextInfo.accID)
Q: 为什么读取 CSV 时报错 UnicodeDecodeError?
A: 这通常是编码不匹配造成的。请检查 pd.read_csv 中的 encoding 参数。如果文件是 UTF-8 编码,请改为 encoding='utf-8';如果是 Windows 默认格式(Excel 保存),通常是 encoding='gbk' 或 encoding='gb18030'。