问题描述
解决方案
在聚宽(JoinQuant)平台上进行策略开发时,NameError 和 SecurityNotExist(找不到标的)是最常见的两类错误。以下是针对这两个问题的详细调试方法和解决方案。
1. 解决 NameError
错误原因:
通常是因为使用了未定义的变量。在聚宽的策略框架中,最常见的情况是变量作用域问题。
- 局部变量:在一个函数内定义的变量无法在另一个函数中直接使用。
- 拼写错误:变量名拼写不一致。
解决方案:使用全局对象 g
聚宽提供了一个全局对象 g,用于在不同的函数(如 initialize 和 handle_data)之间传递变量。
错误示例:
def initialize(context):
# 错误:security 是局部变量,只能在 initialize 中访问
security = '000001.XSHE'
def handle_data(context, data):
# 报错:NameError: global name 'security' is not defined
order(security, 100)
正确示例:
def initialize(context):
# 正确:使用 g.security,将其存储在全局对象中
g.security = '000001.XSHE'
def handle_data(context, data):
# 正确:通过 g.security 调用
order(g.security, 100)
2. 解决 “找不到标的” (SecurityNotExist)
错误原因:
- 代码格式错误:聚宽要求股票代码必须包含后缀。上海证券交易所后缀为
.XSHG,深圳证券交易所后缀为.XSHE。 - 标的已退市或未上市:在回测的时间段内,该股票不存在。
- 拼写错误:代码数字写错。
解决方案:
- 检查后缀:确保代码格式如
'000001.XSHE'(平安银行) 或'600519.XSHG'(贵州茅台)。 - 使用
normalize_code:如果不确定后缀,可以使用此函数自动补全。
错误示例:
# 错误:缺少后缀
order('000001', 100)
# 报错:Exception: 找不到标的000001
正确示例:
# 方法1:手动补全后缀
order('000001.XSHE', 100)
# 方法2:使用 normalize_code 自动转换
code = normalize_code('000001')
order(code, 100)
3. 通用调试技巧
当遇到报错时,请按照以下步骤进行排查:
A. 利用日志输出 (log.info 或 print)
在关键代码行之前打印变量的值,确认程序是否按预期执行。
def handle_data(context, data):
# 打印当前时间和变量值,方便在日志中追踪
log.info("当前时间: %s" % context.current_dt)
log.info("尝试交易: %s" % g.security)
# 检查数据是否存在
if g.security not in data:
log.warn("数据缺失: %s" % g.security)
return
order(g.security, 100)
B. 定位报错行号
在回测界面的“日志”面板中,报错信息通常会指出具体的行号(Line X)。
- 找到报错的行。
- 检查该行使用的所有变量是否已定义(是否需要加
g.)。 - 检查该行调用的 API 参数是否正确。
C. 检查标的是否存在
如果不确定某个标的在当前日期是否可交易,可以使用 get_security_info 或 get_all_securities 查询。
# 检查标的信息
info = get_security_info('000001.XSHE')
if info:
log.info("上市日期: %s" % info.start_date)
else:
log.error("标的不存在或代码错误")
Q&A
Q: 为什么我在 initialize 里定义了变量,在 handle_data 里还是报错说没定义?
A: 因为 Python 函数中的变量默认是局部的。在聚宽中,必须将变量赋值给 g 对象(例如 g.my_var = 1),才能在其他函数中通过 g.my_var 访问,或者该变量确实是拼写错误。
Q: 我输入的股票代码是 600000,为什么提示找不到?
A: 聚宽的股票代码需要包含交易所后缀。上海股票需加 .XSHG(如 600000.XSHG),深圳股票需加 .XSHE。您可以使用 normalize_code('600000') 来自动获取正确的代码格式。
Q: 如何查看我的策略运行到了哪一步?
A: 使用 log.info('这里是步骤1') 或 print('这里是步骤1')。这些信息会显示在回测页面的“日志”或“输出”栏中,帮助您判断代码执行流程。