问题描述
http://ptradeapi.com/#after_trading_end
官方文档只提供一个每天盘后执行的函数,没有函数可以在回测结束后,固定执行某些操作。
比如我回测过程保存的历史交易记录,收益率等,要如何保存? 虽然可以在回测的时候,每个交易日保存一次。
但是这样就需要在回测的时候按照天打开文件,盘后写入一次。 使用一个全局对象操作,显得很啰嗦。
那么有没有办法可以做在回测结束后一次性 保存操作呢?
解决方案
在 ptrade 量化平台中,确实没有提供类似 on_backtest_end 这样在整个回测结束后只执行一次的原生回调函数。所有的盘后逻辑都只能在 after_trading_end(每日盘后)中处理。
虽然每天打开文件写入看起来有些“啰嗦”,但在量化回测中,每天覆盖保存(持久化)其实是官方推荐的最佳实践,因为它可以防止回测意外中断导致的数据全部丢失。不过,如果你坚持想要实现“只在回测最后一天一次性保存”或者寻找更简便的方法,可以通过以下三种替代方案来实现:
方案一:手动指定结束日期进行判断(一次性保存)
既然无法直接获取回测引擎的结束状态,你可以在 initialize 中定义一个与你回测设置界面相同的结束日期。然后在 after_trading_end 中判断当前日期是否等于结束日期,如果是,则执行一次性保存。
import pandas as pd
def initialize(context):
set_universe('600570.SS')
# 1. 手动设置与回测界面一致的结束日期
g.end_date = '2023-12-31'
# 2. 初始化用于存储结果的全局列表
g.daily_returns = []
def handle_data(context, data):
pass
def after_trading_end(context, data):
current_date = context.blotter.current_dt.strftime('%Y-%m-%d')
# 收集每天的收益率等数据
g.daily_returns.append({
'date': current_date,
'returns': context.portfolio.returns,
'portfolio_value': context.portfolio.portfolio_value
})
# 判断当前日期是否是回测的最后一天
if current_date == g.end_date:
save_final_results()
def save_final_results():
# 在这里执行一次性保存操作,例如保存为 CSV
df = pd.DataFrame(g.daily_returns)
save_path = get_research_path() + 'my_backtest_results.csv'
df.to_csv(save_path, index=False)
log.info(f"回测结束,结果已一次性保存至:{save_path}")
方案二:每天覆盖写入(推荐,代码其实很精简)
如果你觉得每天追加写入文件很啰嗦,可以把所有数据存在全局变量 g 中,每天盘后直接覆盖写入整个对象。使用 pickle 模块,代码只需要两行,并不啰嗦,而且非常安全。
import pickle
def initialize(context):
set_universe('600570.SS')
g.daily_returns = []
def after_trading_end(context, data):
# 收集数据
g.daily_returns.append(context.portfolio.returns)
# 每天覆盖保存,代码非常简洁
save_path = get_research_path() + 'backtest_results.pkl'
with open(save_path, 'wb') as f:
pickle.dump(g.daily_returns, f, -1)
方案三:使用官方接口直接导出交易记录
如果你想保存的主要是历史交易记录(订单编号、成交价、手续费等),完全不需要自己去写逻辑收集。ptrade 官方提供了一个专门用于回测的接口 get_trades_file(),它可以直接生成对账数据文件。
你只需要在 after_trading_end 中调用它即可(它会自动覆盖更新,最终你得到的就是完整的回测交易记录)。
def initialize(context):
set_universe('600570.SS')
def handle_data(context, data):
order('600570.SS', 100)
def after_trading_end(context, data):
# 获取对账数据,存放到 notebook 下的指定目录
# 最终回测结束后,你去研究目录下找这个 CSV 文件即可
user_data_path = get_trades_file('user_data/trades_record.csv')
总结
- 如果你想保存自定义的复杂指标(如每日收益率、特定因子值),建议使用方案二(每天 pickle 覆盖保存)或方案一(指定结束日期一次性保存)。
- 如果你只是想保存交易流水/交割单,直接使用方案三的
get_trades_file()是最优雅的做法。