问题描述
聚宽的read_file函数不够用,open 函数 总是报“No such file or、、、”。哪里出了问题呢?
解决方案
为什么在回测/模拟环境中使用 open 函数会报错?
在 JoinQuant(聚宽)平台上,研究环境与回测/模拟交易环境在底层是相互隔离的沙盒环境。当您在研究环境(Jupyter Notebook)中看到的文件,其相对路径在回测引擎中并不适用。因此,如果您直接在回测代码中使用 Python 原生的 open('filename.txt'),系统会因为在回测沙盒的当前工作目录下找不到该文件,从而抛出 No such file or directory 的错误。
此外,聚宽官方文档在“模拟盘注意事项”中也明确指出:涉及到 IO(如打开的文件 open("some/path"))的对象是不能被序列化的,直接使用 open 可能会导致模拟盘在每日休眠和重启保存状态时发生崩溃。
为什么觉得 read_file “不够用”?
很多开发者觉得 read_file 不够用,通常是因为第三方库(如 pandas.read_csv 或 json.load)需要传入一个文件对象(File-like object),而聚宽的 read_file(path) 返回的是文件的原始内容(字符串或字节流)。直接将字符串传给需要文件对象的函数自然会报错。
完美的解决方案:read_file + StringIO / BytesIO
要解决这个问题,您不需要(也不能)使用 open 函数。正确的做法是:使用 read_file 获取内容后,利用 Python 内置的 io 模块将其转换为内存中的文件对象。这样既符合聚宽的 API 规范,又能完美兼容所有需要文件对象的第三方库。
场景一:读取 CSV 文件并使用 Pandas 解析
如果您需要用 pandas 读取研究环境中的 CSV 文件,可以结合 BytesIO(Python 3 环境)或 StringIO(Python 2 环境)来实现:
# Python 3 环境下的标准写法(推荐)
import pandas as pd
from six import BytesIO
def initialize(context):
# 1. 使用 read_file 读取文件原始字节流
body = read_file("my_data.csv")
# 2. 使用 BytesIO 将字节流包装成文件对象,传给 pandas
df = pd.read_csv(BytesIO(body))
log.info("成功读取数据,前5行为:\n%s" % df.head())
场景二:读取 JSON 配置文件
如果您在研究环境中保存了股票池或参数配置的 JSON 文件,可以直接使用 json.loads 解析 read_file 返回的字符串:
import json
def initialize(context):
# 1. 读取文件内容(返回字符串)
content = read_file('HS300.stocks.json')
# 2. 直接使用 json.loads 解析字符串
securities = json.loads(content)
log.info("成功加载股票池,数量:%d" % len(securities))
g.my_stocks = securities
场景三:按行读取普通文本文件
如果您习惯了 open().readlines() 的操作,可以对 read_file 的结果直接进行字符串分割:
def initialize(context):
# 读取文本内容
text_content = read_file('config.txt')
# 按行分割,模拟 readlines() 的效果
# 注意:如果是 Python 3,read_file 可能返回 bytes,需要先 decode('utf-8')
if isinstance(text_content, bytes):
text_content = text_content.decode('utf-8')
lines = text_content.splitlines()
for line in lines:
log.info("读取到一行: %s" % line)
总结与注意事项
- 绝对不要在回测/模拟中使用
open():路径隔离会导致找不到文件,且文件句柄无法被聚宽系统序列化保存。 - 统一使用
read_file(path):path是相对于您私有空间(研究模块)根目录的相对路径。 - 善用内存文件对象:遇到需要传入文件句柄的第三方库,请使用
six.BytesIO(read_file('...'))或six.StringIO(read_file('...'))进行桥接。 - 注意 Python 版本差异:聚宽 Python 3 环境下
read_file通常返回bytes,处理纯文本时可能需要.decode('utf-8')。