问题描述
/
解决方案
错误解析
在 Python 中,NoneType object is not subscriptable 错误的意思是:你尝试对一个 None 对象进行索引或切片操作(例如使用 [] 获取元素)。因为 None 代表空值,它不是列表、字典或字符串,所以不支持这种操作。
在 ptrade 量化平台编写策略时,这个错误通常是因为调用的 API 接口未能成功获取数据,返回了 None,而你的代码没有进行判空处理,直接对其进行了索引取值。
ptrade 中常见的触发场景
1. 行情接口获取失败
调用 get_history、get_price、get_snapshot 等接口时,如果遇到网络波动、股票停牌、代码错误或请求频率过高触发限流,接口可能会返回 None。
错误示例:
# 如果 get_history 失败返回 None,直接取 ['close'] 就会报错
his = get_history(5, '1d', 'close', '600570.SS')
close_price = his['close'][-1]
2. 逐笔数据/Tick数据无权限或无数据
调用 get_individual_entrust、get_individual_transaction 或在 tick_data 函数中获取逐笔数据时,如果未开通 Level2 行情权限,或者当前时刻没有数据推送,对应的数据字段会返回 None。
错误示例:
def tick_data(context, data):
# 如果没有 L2 权限,data[security]['transcation'] 可能为 None
transcation = data['600570.SS']['transcation']
business_amount = transcation['business_amount'] # 这里会报错
3. 字典键值不存在
使用 dict.get(key) 方法时,如果键不存在默认返回 None,若紧接着对其进行链式索引也会报错。
解决方案与代码优化
为了保证实盘交易的稳定性,必须在策略中加入异常处理和判空保护。
方案一:增加 is not None 判空保护(推荐)
在对获取到的数据进行索引之前,先判断其是否为 None。
正确示例:
def handle_data(context, data):
security = '600570.SS'
his = get_history(5, '1d', 'close', security)
# 增加判空保护
if his is not None and not his.empty:
close_price = his['close'][-1]
log.info("最新收盘价:%s" % close_price)
else:
log.warning("获取 %s 历史行情失败!" % security)
方案二:使用 try...except 异常捕获
对于复杂的逻辑,可以使用 try...except 块来捕获异常,防止策略因为单次数据获取失败而直接中断退出。
正确示例:
def handle_data(context, data):
security = '600570.SS'
try:
snapshot = get_snapshot(security)
# 如果 snapshot 为空字典 {} 或 None,下面的取值可能会抛出异常
last_px = snapshot[security]['last_px']
if last_px > 40:
order(security, 100)
except TypeError as e:
log.error("数据类型错误,可能接口返回了 None: %s" % e)
except KeyError as e:
log.error("字典键值不存在: %s" % e)
except Exception as e:
log.error("发生未知异常: %s" % e)
方案三:针对 Tick 数据的安全取值
在 tick_data 中,务必检查 order 和 transcation 是否存在。
正确示例:
def tick_data(context, data):
security = '600570.SS'
if security in data:
transcation = data[security].get('transcation')
# 检查 transcation 是否为 None
if transcation is not None:
business_amount = list(transcation["business_amount"])
if len(business_amount) > 0:
log.info("最近一笔成交量:%s" % business_amount[0])
else:
log.debug("当前 tick 无逐笔成交数据或未开通 L2 权限")
总结建议
- 防御性编程:量化交易环境复杂,任何网络请求 API 都可能失败,永远不要假设接口一定会返回预期的数据结构。
- 检查权限:如果是逐笔数据报错,请先与券商确认是否已开通 Level2 行情权限。
- 查看日志:善用
log.info()打印接口返回的原始数据,确认数据结构后再进行索引操作。