问题描述
解决方案
这是一个非常实用的需求。在 QMT 策略中,我们可以利用 Python 标准库中的文件操作功能(如 open 函数)或第三方库(如 pandas)来实现本地文件的读写。
以下是一个完整的策略示例代码。该策略在初始化时读取本地 TXT 文件中的股票代码设置股票池,并在盘中产生交易信号时将记录追加写入到本地 CSV 文件中。
策略代码实现
# -*- coding: gbk -*-
import pandas as pd
import os
import datetime
def init(ContextInfo):
"""
初始化函数:读取本地股票列表,初始化CSV文件
"""
# ================= 配置路径 =================
# 请确保文件夹存在,或者手动创建 D:\qmt_data\ 目录
# 注意:路径字符串前加 r 表示原始字符串,防止转义字符错误
ContextInfo.stock_list_path = r'D:\qmt_data\stock_list.txt'
ContextInfo.record_save_path = r'D:\qmt_data\trade_records.csv'
# ================= 1. 读取本地 TXT 股票列表 =================
stock_list = []
if os.path.exists(ContextInfo.stock_list_path):
try:
with open(ContextInfo.stock_list_path, 'r', encoding='utf-8') as f:
# 逐行读取,去除空白符,并过滤空行
lines = f.readlines()
for line in lines:
code = line.strip()
if code:
stock_list.append(code)
print(f"成功读取股票列表,共 {len(stock_list)} 只: {stock_list}")
# 设定股票池
ContextInfo.set_universe(stock_list)
ContextInfo.my_stocks = stock_list # 存入ContextInfo以便handlebar使用
except Exception as e:
print(f"读取股票列表文件出错: {e}")
else:
print(f"错误:找不到文件 {ContextInfo.stock_list_path},请先创建该文件。")
ContextInfo.my_stocks = []
# ================= 2. 初始化 CSV 记录文件 =================
# 如果文件不存在,则创建并写入表头
if not os.path.exists(ContextInfo.record_save_path):
try:
with open(ContextInfo.record_save_path, 'w', encoding='gbk') as f:
# 写入表头:时间,代码,操作,价格,数量
f.write("Time,StockCode,Action,Price,Volume\n")
print(f"已创建新的交易记录文件: {ContextInfo.record_save_path}")
except Exception as e:
print(f"创建CSV文件失败: {e}")
# 设置账号(请替换为您自己的账号)
ContextInfo.account_id = '6000000000'
ContextInfo.set_account(ContextInfo.account_id)
def handlebar(ContextInfo):
"""
K线周期运行函数:执行策略并记录交易
"""
# 获取当前K线对应的时间
index = ContextInfo.barpos
timetag = ContextInfo.get_bar_timetag(index)
# 将时间戳转换为易读的字符串格式
current_time = timetag_to_datetime(timetag, '%Y-%m-%d %H:%M:%S')
# 遍历我们在 init 中读取到的股票列表
for stock in ContextInfo.my_stocks:
# 获取最新收盘价
# 注意:get_market_data_ex 返回的是字典 {code: dataframe}
market_data = ContextInfo.get_market_data_ex(
['close', 'open'],
[stock],
period=ContextInfo.period,
count=1,
subscribe=True
)
if stock in market_data and not market_data[stock].empty:
close_price = market_data[stock].iloc[-1]['close']
open_price = market_data[stock].iloc[-1]['open']
# === 示例策略逻辑:如果收盘价大于开盘价,模拟买入 ===
# (这里仅为演示写入CSV功能,非真实盈利策略)
if close_price > open_price:
action = "BUY"
volume = 100
# 执行下单 (此处注释掉,仅演示记录功能)
# order_shares(stock, volume, 'fix', close_price, ContextInfo, ContextInfo.account_id)
# === 3. 将交易记录写入 CSV 文件 ===
write_trade_record(ContextInfo.record_save_path, current_time, stock, action, close_price, volume)
# 为了防止一根K线重复写入,实际策略中通常需要控制信号触发频率
# 这里简单打印日志
print(f"[{current_time}] 记录写入: {stock} {action} {close_price}")
def write_trade_record(filepath, time_str, code, action, price, volume):
"""
辅助函数:追加写入一行记录到CSV
"""
try:
# 使用 'a' 模式 (append) 追加内容
with open(filepath, 'a', encoding='gbk') as f:
line = f"{time_str},{code},{action},{price},{volume}\n"
f.write(line)
except Exception as e:
print(f"写入CSV失败: {e}")
代码详解
-
文件路径设置:
- 使用了
r'...'原始字符串格式来定义路径(例如r'D:\qmt_data\...'),这在 Windows 系统中非常重要,可以避免反斜杠\被识别为转义字符。
- 使用了
-
读取 TXT 文件 (
init函数中):- 使用
open(..., 'r')模式打开文件。 - 使用
readlines()读取所有行,并配合strip()去除每行末尾的换行符和空格。 - 数据准备:您需要在
D:\qmt_data\下创建一个stock_list.txt,内容格式如下(一行一个代码):600000.SH 000001.SZ 600519.SH
- 使用
-
写入 CSV 文件 (
write_trade_record函数):- 初始化:在
init中检查文件是否存在。如果不存在,使用'w'(write) 模式创建文件并写入表头(Header)。 - 追加写入:在交易发生时,使用
'a'(append) 模式打开文件。这确保了新的交易记录会添加到文件末尾,而不会覆盖旧数据。 - 编码:示例中使用了
encoding='gbk',这是为了确保生成的 CSV 文件在 Excel 中打开时中文不乱码。如果您主要用于程序处理,也可以改为utf-8。
- 初始化:在
-
ContextInfo 的作用:
- 我们将读取到的
stock_list和文件路径保存在ContextInfo对象中(如ContextInfo.my_stocks)。这样,在handlebar函数中就可以直接访问这些变量,而不需要重新读取文件或重新定义路径。
- 我们将读取到的
注意事项
- 文件权限:请确保 QMT 软件有权限读写您设置的文件夹(如 D 盘根目录有时需要管理员权限,建议放在子文件夹中)。
- 性能:频繁打开和关闭文件(IO操作)会消耗一定时间。在回测模式下影响不大,但在高频实盘策略中,建议使用内存缓存数据,定期批量写入,或者使用 Python 的
logging模块。 - 数据格式:
get_market_data_ex返回的是 DataFrame,取值时要注意判空。
Q&A
Q: 如果我的 txt 文件里股票代码是用逗号分隔的怎么办?
A: 可以修改读取逻辑,读取整个文件内容后使用 split(',') 分割。例如:
content = f.read()
stock_list = [x.strip() for x in content.split(',') if x.strip()]
Q: 为什么生成的 CSV 文件用 Excel 打开是乱码?
A: 这通常是编码问题。Excel 默认对 CSV 使用 ANSI (GBK) 编码。请确保在 open 函数中指定 encoding='gbk'。如果指定了 utf-8,在 Excel 中导入数据时需要选择 UTF-8 格式。
Q: 这个策略可以直接回测吗?
A: 可以。在回测模式下,handlebar 会随着历史数据逐根 K 线运行,您的 CSV 文件中将会生成大量的历史模拟交易记录。建议在回测前手动删除旧的 CSV 文件,以免数据混淆。