🚀 AI 一键生成 ptrade 策略代码

如何在 PTrade 策略中高效使用 log 函数进行调试与监控?

ptrade | 发布时间: 2025/12/15
以下内容由 EasyQuant 生成。

问题描述

怎么样才能更好地记录策略的运行日志方便调试?

解决方案

在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. 初始化与盘前检查

initializebefore_trading_start 中记录策略的初始状态、股票池设定和参数配置。

B. 信号触发逻辑

不要只记录“买入”或“卖出”,要记录为什么买卖。记录触发信号时的关键指标值(如均线价格、RSI值、当前股价)。

C. 交易执行

在调用 order 系列函数前后记录日志,确认下单意图。

D. 委托与成交回报 (实盘/仿真尤为重要)

利用 on_order_responseon_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. 调试小技巧

  1. 格式化字符串: PTrade 基于 Python 3,但为了兼容性及日志习惯,推荐使用 % 格式化(如 log.info("价格: %.2f" % price))或 .format()。这比简单的字符串拼接(+)更易读且不易出错。
  2. 避免日志刷屏:
    • handle_datatick_data 中,如果策略频率很高,不要无条件打印日志。
    • 可以使用计数器或状态位,仅在状态改变时打印。例如:if g.last_signal != current_signal: log.info(...)
  3. 查看对象结构: 如果不确定某个对象(如 contextdata)里有什么,可以使用 log.info(dir(object)) 查看属性列表,或者直接打印字典 log.info(some_dict)
  4. 利用 after_trading_end: 在每日收盘后,打印当天的最终持仓和盈亏,作为每日的总结日志,方便对比回测结果。

Q&A

Q: 为什么我在回测中看不到 on_order_response 的日志?
A: 在PTrade的回测模式下,订单状态变化通常是同步或简化的,on_order_responseon_trade_response 主要设计用于交易模式(实盘或仿真)接收柜台的主推消息。在回测中,你应该主要依赖 handle_data 中的返回值和 get_orders() 来检查订单状态。

Q: log.info 打印中文乱码怎么办?
A: PTrade 内部默认支持 UTF-8 编码。如果在某些特定的客户端查看器中出现乱码,请确保你的策略文件是以 UTF-8 格式保存的。通常直接在 PTrade 编辑器中编写不会有问题。

Q: 如何查看历史日志?
A: 在 PTrade 客户端的底部“日志”面板中可以看到实时日志。如果是回测,回测报告页面通常会有“日志”选项卡,可以下载或在线查看完整的运行日志文件。