🚀 AI 一键生成 ptrade 策略代码

PTrade 策略进阶:如何使用 after_trading_end 函数进行盘后复盘与分析?

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

问题描述

请展示一下如何在交易日结束后,进行数据统计和复盘分析

解决方案

在PTrade量化交易平台中,进行盘后数据统计和复盘分析主要依赖于 after_trading_end 函数。

该函数是一个可选的生命周期函数,会在每个交易日的 15:30(盘后)自动触发。这是汇总当日盈亏、检查持仓变化、统计成交记录以及打印复盘日志的最佳时机。

以下是一个完整的策略示例,展示了如何在盘后统计当日的资产状况持仓详情以及当日成交记录

策略代码示例

def initialize(context):
    """
    初始化函数
    """
    # 设置股票池,这里以恒生电子为例
    g.security = '600570.SS'
    set_universe(g.security)
    
    # 用于记录昨日净值,以便计算当日盈亏
    g.last_total_value = context.portfolio.starting_cash

def before_trading_start(context, data):
    """
    盘前处理:记录开盘前的总资产,用于盘后计算当日盈亏
    """
    g.last_total_value = context.portfolio.portfolio_value

def handle_data(context, data):
    """
    盘中逻辑:这里简单模拟一个买入操作用于产生数据
    """
    # 如果没有持仓,买入100股
    if get_position(g.security).amount == 0:
        order(g.security, 100)
        log.info("盘中触发买入: %s" % g.security)

def after_trading_end(context, data):
    """
    盘后处理:数据统计与复盘分析
    运行时间:交易日 15:30
    """
    log.info("=" * 30)
    log.info("【开始盘后复盘统计】日期: %s" % context.blotter.current_dt.strftime("%Y-%m-%d"))
    
    # 1. 账户资金统计
    portfolio = context.portfolio
    total_value = portfolio.portfolio_value # 总资产
    cash = portfolio.cash                   # 可用资金
    daily_pnl = total_value - g.last_total_value # 当日盈亏
    daily_return = (daily_pnl / g.last_total_value) * 100 if g.last_total_value > 0 else 0 # 当日收益率
    
    log.info("[资金概况]")
    log.info("总资产: %.2f" % total_value)
    log.info("可用现金: %.2f" % cash)
    log.info("当日盈亏: %.2f" % daily_pnl)
    log.info("当日收益率: %.2f%%" % daily_return)
    log.info("累计收益率: %.2f%%" % (portfolio.returns * 100))

    # 2. 持仓详情统计
    positions = portfolio.positions
    log.info("-" * 20)
    log.info("[持仓详情]")
    
    if len(positions) > 0:
        for stock_code, pos in positions.items():
            # 过滤掉已清仓(数量为0)的标的
            if pos.amount > 0:
                # 获取当前收盘价(注意:after_trading_end时data为空,需用get_price或pos.last_sale_price)
                current_price = pos.last_sale_price
                market_value = pos.amount * current_price
                float_pnl = pos.amount * (current_price - pos.cost_basis) # 简单浮动盈亏计算
                
                log.info("标的: %s | 持仓: %d | 成本: %.2f | 现价: %.2f | 市值: %.2f | 浮盈: %.2f" % (
                    stock_code, 
                    pos.amount, 
                    pos.cost_basis, 
                    current_price, 
                    market_value,
                    float_pnl
                ))
    else:
        log.info("当前无持仓")

    # 3. 当日成交记录复盘
    log.info("-" * 20)
    log.info("[今日成交记录]")
    
    # 获取当日所有成交
    trades = get_trades()
    
    if trades:
        # get_trades 返回的是字典,key是订单ID,value是成交列表
        for order_id, trade_list in trades.items():
            for trade in trade_list:
                # trade结构: [成交编号, 委托编号, 标的代码, 买卖类型, 成交数量, 成交价格, 成交金额, 成交时间]
                # 注意:不同版本PTrade返回结构可能微调,建议参考文档或打印查看
                stock = trade[2]
                action = trade[3]
                amount = trade[4]
                price = trade[5]
                time_str = trade[7]
                
                log.info("时间: %s | 标的: %s | 操作: %s | 数量: %s | 均价: %.2f" % (
                    time_str, stock, action, amount, price
                ))
    else:
        log.info("今日无成交")
        
    log.info("【盘后复盘统计结束】")
    log.info("=" * 30)

代码解析

  1. after_trading_end(context, data):

    • 这是核心函数,专门用于盘后处理。
    • 注意:在此函数中,参数 data 通常为空或不包含实时行情数据,因此获取价格应依赖 context.portfolio.positions 中的 last_sale_price 或者调用 get_price 获取收盘价。
  2. 资金统计 (context.portfolio):

    • 通过 context.portfolio 对象获取当前的总资产 (portfolio_value) 和现金 (cash)。
    • 为了计算当日盈亏,我们在 before_trading_start 中记录了开盘前的净值 g.last_total_value,然后在盘后进行减法计算。
  3. 持仓遍历 (context.portfolio.positions):

    • context.portfolio.positions 是一个字典,键是股票代码,值是 Position 对象。
    • 通过遍历这个字典,可以获取每只股票的持仓量 (amount)、持仓成本 (cost_basis) 和最新价格 (last_sale_price)。
  4. 成交记录 (get_trades()):

    • get_trades() 函数返回当日的成交回报。
    • 这对于复盘非常重要,可以用来核对策略在当天的交易逻辑是否符合预期。

常见问题 (Q&A)

Q: after_trading_end 函数在回测和实盘中的运行时间一致吗?
A: 在回测模式下,该函数通常在模拟时间的 15:30 运行。在实盘交易模式下,运行时间由券商配置决定,通常也是在收盘后的 15:30 左右执行。

Q: 我可以在 after_trading_end 中下单交易吗?
A: 不可以。该函数运行在盘后(15:30),此时交易所已闭市,普通的 order 系列函数无法撮合成交。如果需要进行盘后固定价格交易(科创板/创业板等),需使用专门的 after_trading_order 接口,且通常需要在交易时段内或特定的盘后时段调用。

Q: 如何将统计的数据导出到本地文件?
A: PTrade 不支持 import os 或随意读写本地文件系统。在回测中,可以使用 log.info 输出并在日志中查看。如果是在研究环境中,可以使用 Python 的标准文件操作写入到 get_research_path() 获取的目录下,例如:

path = get_research_path() + "daily_report.txt"
with open(path, 'a') as f:
    f.write("Date: %s, Value: %s\n" % (date, value))

Q: 为什么盘后获取的 data 参数里没有数据?
A: data 参数主要用于 handle_data 中传输实时的 tick 或 bar 数据。在 after_trading_end 阶段,行情推送已经结束,因此该参数通常不包含行情数据。如需获取收盘价,请使用 get_history 或查看持仓对象中的价格属性。