问题描述
解决方案
在 JoinQuant(聚宽)平台中,从 JSON 文件读取策略配置主要涉及两个步骤:使用平台提供的 read_file API 读取文件内容,然后使用 Python 标准库 json 将其解析为字典。
这种方法非常适合将策略逻辑与参数分离,便于在不修改代码的情况下调整参数。
以下是具体的实现方案和代码示例。
核心步骤说明
- 准备 JSON 文件:文件通常存储在聚宽的“研究”环境中(根目录)。
- 读取文件:在策略的
initialize函数中使用read_file('文件名')读取二进制或文本内容。 - 解析 JSON:使用
json.loads()将读取的内容转换为 Python 字典。 - 赋值变量:将解析后的参数赋值给全局变量
g或用于策略逻辑。
策略代码示例
为了演示完整流程,下面的代码包含两个部分:
- 辅助函数:用于在回测开始时自动创建一个测试用的 JSON 配置文件(实际使用时,您通常是在“研究”模块手动创建这个文件)。
- 核心逻辑:在
initialize中读取并应用配置。
# -*- coding: utf-8 -*-
import json
import jqdata
def initialize(context):
"""
策略初始化函数
"""
set_benchmark('000300.XSHG')
set_option('use_real_price', True)
log.set_level('order', 'error')
# ---------------------------------------------------------
# 第一步:创建一个模拟的配置文件(实际场景中,该文件通常已存在于您的研究根目录)
# ---------------------------------------------------------
create_dummy_config_file()
# ---------------------------------------------------------
# 第二步:读取并解析 JSON 配置文件
# ---------------------------------------------------------
file_path = 'my_strategy_config.json'
try:
# read_file 是聚宽专用API,用于读取研究目录下的文件
# 注意:在 Python 3 环境下,read_file 返回的是 bytes 类型
file_content = read_file(file_path)
if file_content is None:
log.error(f"未找到配置文件: {file_path},将使用默认参数。")
load_default_params()
else:
# 将 bytes 解码为字符串 (utf-8) 并解析 JSON
config = json.loads(file_content)
log.info(f"成功加载配置文件: {config}")
# 将配置应用到全局变量 g 中
g.security = config.get('security', '000001.XSHE')
g.short_window = config.get('short_window', 5)
g.long_window = config.get('long_window', 20)
except Exception as e:
log.error(f"读取或解析配置文件失败: {e},将使用默认参数。")
load_default_params()
# 设置要操作的股票
g.security_list = [g.security]
# 按天运行
run_daily(market_open, time='every_bar')
def create_dummy_config_file():
"""
辅助函数:在回测开始前创建一个测试用的 json 文件。
在实际使用中,您应该在【研究】模块中上传或创建此文件,不需要在策略里写这部分。
"""
dummy_config = {
"security": "000001.XSHE",
"short_window": 10,
"long_window": 60,
"comment": "这是一个双均线策略的配置文件"
}
# write_file 也是聚宽专用API
write_file('my_strategy_config.json', json.dumps(dummy_config), append=False)
def load_default_params():
"""
加载默认参数,防止文件读取失败导致策略崩溃
"""
g.security = '000001.XSHE'
g.short_window = 5
g.long_window = 20
log.warn("已加载默认参数。")
def market_open(context):
"""
每日交易逻辑
"""
sec = g.security
# 获取历史收盘价
# 获取长度为长均线窗口的数据
hist_data = attribute_history(sec, g.long_window + 1, '1d', ['close'])
close_prices = hist_data['close']
if len(close_prices) < g.long_window:
return
# 计算均线
# 短均线
ma_short = close_prices[-g.short_window:].mean()
# 长均线
ma_long = close_prices[-g.long_window:].mean()
# 获取当前持仓
current_position = context.portfolio.positions[sec].total_amount
# 交易信号:金叉买入
if ma_short > ma_long and current_position == 0:
order_target_value(sec, context.portfolio.available_cash)
log.info(f"金叉买入 {sec},短均线: {ma_short}, 长均线: {ma_long}")
# 交易信号:死叉卖出
elif ma_short < ma_long and current_position > 0:
order_target(sec, 0)
log.info(f"死叉卖出 {sec},短均线: {ma_short}, 长均线: {ma_long}")
关键点解析
-
read_file(path):- 这是聚宽特有的 API,用于读取“研究”环境根目录下的文件。
- 注意:在 Python 3 回测环境中,
read_file返回的数据类型通常是bytes(字节流)。
-
json.loads():- Python 标准库函数。
- 如果
read_file返回的是 bytes,json.loads在较新的 Python 版本中可以直接处理 bytes,但为了兼容性和稳健性,建议先解码:json.loads(file_content.decode('utf-8'))(如果文件包含中文)。
-
异常处理 (
try...except):- 文件读取涉及 I/O 操作,可能会因为文件不存在、格式错误等原因失败。强烈建议使用
try...except块包裹读取逻辑,并提供默认参数(load_default_params),确保策略在配置文件缺失时也能运行。
- 文件读取涉及 I/O 操作,可能会因为文件不存在、格式错误等原因失败。强烈建议使用
-
文件位置:
- 代码中引用的
'my_strategy_config.json'必须位于您的聚宽研究模块的根目录下。您可以在研究界面点击“新建” -> “文本文件”,输入 JSON 内容并重命名为.json后缀。
- 代码中引用的
Q&A
Q: 为什么使用 read_file 而不是 Python 原生的 open()?
A: 在聚宽的回测和模拟交易容器中,文件系统路径与本地不同。read_file 是平台封装好的接口,专门用于安全地访问用户在“研究”模块中存储的私有文件,路径处理更简便且权限受控。
Q: 如果 JSON 文件中有中文,读取时乱码怎么办?
A: read_file 读取的是二进制数据。在进行 json.loads 之前,请显式进行解码:content_str = file_content.decode('utf-8'),然后再传入 json.loads(content_str)。
Q: 能否在回测过程中修改 JSON 文件来动态调整参数?
A: 在回测模式下,文件是在回测开始时读取的,中途修改文件通常不会影响正在运行的回测(除非你在 handle_data 中反复读取,但这会极大地降低回测速度,不推荐)。在模拟交易中,如果策略重启(每天盘前),会重新读取文件;如果需要在盘中动态调整,可以在 handle_data 中设置定时读取逻辑,但需注意性能消耗。