问题描述
解决方案
在 QMT 中,可以通过 Python 标准的文件读取操作(open 函数)或者 QMT 提供的 ContextInfo.load_stk_list 接口来读取本地 TXT 文件,并将读取到的股票代码列表赋值给 ContextInfo.set_universe()。
以下是具体的实现方案和代码。
实现思路
- 准备 TXT 文件:假设您的文件名为
stocks.txt,内容格式为一行一个股票代码(例如600000.SH),或者用逗号分隔。 - 编写读取函数:在策略中编写一个函数,用于打开指定路径的文件,解析出股票代码列表。
- 在
init中调用:在策略初始化时调用该函数,并将结果传入ContextInfo.set_universe()。
策略代码
# -*- coding: gbk -*-
import os
def init(ContextInfo):
# ================= 参数设置 =================
# 请将此处修改为您 txt 文件的实际绝对路径
# 注意:Windows路径建议使用双反斜杠 \\ 或正斜杠 /
file_path = 'D:\\quant_data\\stocks.txt'
# ================= 读取股票池 =================
stock_list = get_stocks_from_txt(file_path)
if stock_list:
# 设定股票池
ContextInfo.set_universe(stock_list)
print(f"成功从文件读取 {len(stock_list)} 只股票")
print(f"股票池示例: {stock_list[:5]}") # 打印前5个确认
else:
print("警告:未能从文件中读取到股票,请检查路径或文件内容")
# 如果读取失败,可以设置一个默认列表,防止报错
ContextInfo.set_universe(['600000.SH'])
# 设置账号(示例)
# ContextInfo.set_account('您的资金账号')
def handlebar(ContextInfo):
# 获取当前设置的股票池
universe = ContextInfo.get_universe()
# 简单的示例逻辑:遍历股票池打印最新价
# 注意:实盘中请勿在每一根K线都打印大量日志
if ContextInfo.is_last_bar():
for stock in universe:
# 获取最新收盘价
data = ContextInfo.get_market_data_ex(
['close'],
[stock],
period='1d',
count=1,
subscribe=True
)
if stock in data and not data[stock].empty:
price = data[stock].iloc[-1]['close']
print(f"{stock} 最新价: {price}")
def get_stocks_from_txt(file_path):
"""
读取txt文件并返回股票代码列表
兼容格式:
1. 一行一个代码 (600000.SH \n 000001.SZ)
2. 逗号分隔 (600000.SH,000001.SZ)
"""
stock_list = []
if not os.path.exists(file_path):
print(f"错误:文件不存在 -> {file_path}")
return []
try:
# 尝试使用 gbk 编码读取(Windows常见),如果报错则尝试 utf-8
try:
with open(file_path, 'r', encoding='gbk') as f:
content = f.read()
except UnicodeDecodeError:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# 处理换行符和逗号,统一替换为逗号后分割
content = content.replace('\n', ',').replace(' ', '')
raw_list = content.split(',')
# 过滤空字符串并清理格式
for code in raw_list:
clean_code = code.strip()
if clean_code:
stock_list.append(clean_code)
return stock_list
except Exception as e:
print(f"读取文件发生异常: {e}")
return []
注意事项
-
文件路径:
- 代码中的
file_path必须是绝对路径(例如D:\\data\\stocks.txt)。 - 请确保路径中使用双反斜杠
\\或者单正斜杠/,不要直接使用单反斜杠\,否则会被 Python 识别为转义字符。
- 代码中的
-
文件编码:
- 代码中已做了兼容处理(优先尝试 GBK,失败则尝试 UTF-8)。如果您的 txt 文件出现乱码,请确保文件保存为 ANSI (GBK) 或 UTF-8 格式。
-
股票代码格式:
- TXT 文件中的股票代码必须包含后缀,例如
600000.SH或000001.SZ。 - 如果您的文件里只有数字(如
600000),需要在代码的get_stocks_from_txt函数中手动添加后缀补全逻辑。
- TXT 文件中的股票代码必须包含后缀,例如
-
运行机制:
init函数通常只在策略启动时运行一次。如果您每天更新 txt 文件,需要重启策略或者点击“运行”按钮,策略才会重新读取新的股票池。- 如果是盘中实时运行且不想重启,可以在
handlebar中添加定时逻辑(例如每天 9:25 分)重新调用读取函数并执行set_universe。
Q&A
Q: 如果我的 txt 文件里只有数字代码(如 600000),没有 .SH/.SZ 后缀怎么办?
A: 您需要修改 get_stocks_from_txt 函数中的处理逻辑,根据代码首位数字判断后缀。例如:
# 在 append 之前加入判断
if clean_code.startswith('6'):
clean_code += '.SH'
elif clean_code.startswith('0') or clean_code.startswith('3'):
clean_code += '.SZ'
stock_list.append(clean_code)
Q: 策略运行中修改了 txt 文件,策略会自动更新吗?
A: 不会。上述代码是在 init 中读取的,只在策略启动瞬间执行。如果需要在盘中动态更新,需要编写定时任务重新读取,或者手动停止并重新运行策略。
Q: QMT 自带的 ContextInfo.load_stk_list 和这个有什么区别?
A: ContextInfo.load_stk_list 是 QMT 的内置函数,功能类似,但使用 Python 原生的 open 函数灵活性更高,可以更容易地处理换行、空格、编码以及自定义的文件格式。建议使用上述提供的 Python 原生写法。