问题描述
解决方案
在PTrade中,记录清晰、结构化的日志是调试策略逻辑、追踪交易行为以及排查错误最有效的方法。PTrade提供了内置的 log 对象,用法与Python标准的 logging 模块非常相似。
以下是如何在PTrade中更好地记录策略运行日志的详细指南和最佳实践。
1. 使用不同的日志级别
合理使用日志级别可以帮助你快速筛选重要信息。PTrade支持以下级别:
log.info(msg): 用于记录正常的程序运行流程,如每日开盘、信号触发、下单操作等。log.warning(msg): 用于记录非致命的异常情况,例如某只股票数据缺失、资金不足导致下单调整等。log.error(msg): 用于记录程序错误或导致策略无法继续执行的严重问题,通常配合try...except使用。log.debug(msg): 用于记录详细的调试信息(注意:在某些回测设置中可能默认不显示,且过多Debug日志会影响回测速度)。
2. 关键节点的日志记录策略
为了方便调试,建议在以下几个关键环节加入日志:
A. 初始化与盘前检查
在 initialize 和 before_trading_start 中记录策略的初始状态、股票池设定和参数配置。
B. 信号触发逻辑
不要只记录“买入”或“卖出”,要记录为什么买卖。记录触发信号时的关键指标值(如均线价格、RSI值、当前股价)。
C. 交易执行
在调用 order 系列函数前后记录日志,确认下单意图。
D. 委托与成交回报 (实盘/仿真尤为重要)
利用 on_order_response 和 on_trade_response 回调函数,记录柜台返回的真实状态(如废单原因、实际成交价)。
E. 异常捕获
使用 try...except 块包裹复杂的逻辑,并在 except 中使用 log.error 打印具体的错误堆栈或信息。
3. 完整代码示例
以下是一个包含完善日志记录机制的策略示例。该代码兼容 Python 3.5,展示了如何在不同阶段进行有效记录。
def initialize(context):
"""
初始化函数
"""
# 1. 记录策略启动
log.info("=== 策略初始化开始 ===")
g.security = '600570.SS'
set_universe(g.security)
# 记录参数设置
g.ma_short = 5
g.ma_long = 10
log.info("设置股票池: %s, 短期均线: %d, 长期均线: %d" % (g.security, g.ma_short, g.ma_long))
def before_trading_start(context, data):
"""
盘前处理
"""
log.info("--- 新交易日开始: %s ---" % context.blotter.current_dt)
# 记录当前账户资产概况,方便核对资金
portfolio = context.portfolio
log.info("盘前资产检查 - 可用资金: %.2f, 总资产: %.2f, 持仓市值: %.2f" % (
portfolio.cash,
portfolio.portfolio_value,
portfolio.positions_value
))
def handle_data(context, data):
"""
盘中逻辑
"""
# 使用 try-except 防止单次循环错误导致策略崩溃
try:
security = g.security
# 1. 数据获取与检查
# 获取历史收盘价
history = get_history(g.ma_long + 2, '1d', 'close', security, fq='pre')
if history is None or len(history) < g.ma_long:
log.warning("股票 %s 行情数据不足,跳过本次计算" % security)
return
close_prices = history['close'].values
# 2. 计算指标
ma_short_val = close_prices[-g.ma_short:].mean()
ma_long_val = close_prices[-g.ma_long:].mean()
current_price = data[security]['close']
# 3. 记录关键指标 (建议保留小数位,保持整洁)
# 注意:如果频率很高(如tick级),不要每笔都打印,可以加条件限制
# log.info("价格: %.2f, MA5: %.2f, MA10: %.2f" % (current_price, ma_short_val, ma_long_val))
# 4. 交易逻辑与下单日志
position = get_position(security)
# 金叉买入
if ma_short_val > ma_long_val and position.amount == 0:
log.info(">>> 触发买入信号: MA5(%.2f) > MA10(%.2f), 现价: %.2f" % (
ma_short_val, ma_long_val, current_price))
# 下单
order_id = order(security, 1000)
if order_id:
log.info("下单成功,订单ID: %s,标的: %s,数量: 1000" % (order_id, security))
else:
log.error("下单失败,返回None")
# 死叉卖出
elif ma_short_val < ma_long_val and position.amount > 0:
log.info("<<< 触发卖出信号: MA5(%.2f) < MA10(%.2f), 现价: %.2f" % (
ma_short_val, ma_long_val, current_price))
order_id = order_target(security, 0)
log.info("执行清仓,订单ID: %s" % order_id)
except Exception as e:
# 捕获未知错误,防止策略停止,并打印错误详情
log.error("handle_data 运行出错: %s" % e)
def on_order_response(context, order_list):
"""
委托回报主推 (仅交易/仿真有效,回测中部分场景有效)
用于追踪订单状态变化(如废单、已报、部成)
"""
for order_info in order_list:
# 打印详细的委托变更信息
log.info("[委托回调] 编号: %(entrust_no)s, 状态: %(status)s, 说明: %(error_info)s" % order_info)
# 如果是废单,记录严重警告
if order_info['status'] == '9':
log.error("出现废单! 股票: %s, 原因: %s" % (order_info['stock_code'], order_info['error_info']))
def on_trade_response(context, trade_list):
"""
成交回报主推
用于确认实际成交的价格和数量
"""
for trade in trade_list:
log.info("[成交回调] 股票: %s, 方向: %s, 价格: %.2f, 数量: %s" % (
trade['stock_code'],
"买入" if trade['entrust_bs'] == '1' else "卖出",
trade['business_price'],
trade['business_amount']
))
4. 调试小技巧
- 格式化字符串: PTrade 基于 Python 3,但为了兼容性及日志习惯,推荐使用
%格式化(如log.info("价格: %.2f" % price))或.format()。这比简单的字符串拼接(+)更易读且不易出错。 - 避免日志刷屏:
- 在
handle_data或tick_data中,如果策略频率很高,不要无条件打印日志。 - 可以使用计数器或状态位,仅在状态改变时打印。例如:
if g.last_signal != current_signal: log.info(...)。
- 在
- 查看对象结构: 如果不确定某个对象(如
context或data)里有什么,可以使用log.info(dir(object))查看属性列表,或者直接打印字典log.info(some_dict)。 - 利用
after_trading_end: 在每日收盘后,打印当天的最终持仓和盈亏,作为每日的总结日志,方便对比回测结果。
Q&A
Q: 为什么我在回测中看不到 on_order_response 的日志?
A: 在PTrade的回测模式下,订单状态变化通常是同步或简化的,on_order_response 和 on_trade_response 主要设计用于交易模式(实盘或仿真)接收柜台的主推消息。在回测中,你应该主要依赖 handle_data 中的返回值和 get_orders() 来检查订单状态。
Q: log.info 打印中文乱码怎么办?
A: PTrade 内部默认支持 UTF-8 编码。如果在某些特定的客户端查看器中出现乱码,请确保你的策略文件是以 UTF-8 格式保存的。通常直接在 PTrade 编辑器中编写不会有问题。
Q: 如何查看历史日志?
A: 在 PTrade 客户端的底部“日志”面板中可以看到实时日志。如果是回测,回测报告页面通常会有“日志”选项卡,可以下载或在线查看完整的运行日志文件。