我先查一下 vnpy_ctastrategy 回测引擎源码,确认 calculate_result() 返回的结构和逐日盈亏字段,再给你按年统计的实现方案。
文档确认了“每日盈亏”按逐日盯市口径计算,最终用的是“净盈亏(net_pnl)”。再读一下回测引擎源码,确认 calculate_result() 返回的 DataFrame 列名,以便做按年统计。
回测引擎自带的统计指标确实没有“按年盈亏”,但 calculate_result() 返回的 df 里包含了按逐日盯市口径计算的每日净盈亏 net_pnl(已扣除手续费和滑点,与报告图表口径一致),只要在脚本里对它按年分组汇总即可。
实现方法
在 engine.calculate_result() 之后,对 df 做按年分组统计:
import pandas as pd
from pandas import DataFrame, Series
from vnpy_ctastrategy.backtesting import BacktestingEngine
# from your_strategy_file import BollChannelStrategy
engine: BacktestingEngine = BacktestingEngine()
engine.set_parameters(
vt_symbol="000001.SZSE",
interval="d",
start=datetime(2018, 3, 23),
end=datetime(2018, 4, 23),
rate=0,
slippage=0,
size=10,
pricetick=0.2,
capital=1_000_000,
)
engine.add_strategy(BollChannelStrategy, {})
engine.load_data()
engine.run_backtesting()
df: DataFrame = engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()
# ---------- 按年统计盈亏 ----------
# daily_df 的索引是 datetime.date 对象,先转成 DatetimeIndex 便于分组
df.index = pd.to_datetime(df.index)
yearly_pnl: Series = df["net_pnl"].groupby(df.index.year).sum()
print("按年净盈亏(元):")
for year, pnl in yearly_pnl.items():
print(f"{year} 年:{pnl:,.2f}")
可选:输出更完整的年度汇总表
df 中除了 net_pnl,还有 commission、slippage、turnover、trade_count 等列,可以一次性聚合:
yearly_stats: DataFrame = df.groupby(df.index.year).agg(
净盈亏=("net_pnl", "sum"),
手续费=("commission", "sum"),
滑点成本=("slippage", "sum"),
成交笔数=("trade_count", "sum"),
交易日数=("net_pnl", "count"),
)
print(yearly_stats)
可选:画按年盈亏柱状图
import plotly.graph_objects as go
fig: go.Figure = go.Figure()
fig.add_trace(go.Bar(x=yearly_pnl.index, y=yearly_pnl.values, name="Yearly Pnl"))
fig.update_layout(height=400, width=800, title="按年盈亏")
fig.show()
几点说明
df["net_pnl"] 是逐日盯市结算后的每日净盈亏(总盈亏扣除手续费和滑点),也就是 show_chart() 里"Daily Pnl"图所使用的数据(来源:vnpy_ctastrategy/backtesting.py 中的 DailyResult.calculate_pnl)。
calculate_result() 返回的 DataFrame 列包括:close_price, pre_close, trade_count, start_pos, end_pos, turnover, commission, slippage, trading_pnl, holding_pnl, total_pnl, net_pnl,索引为日期。balance 等列是 calculate_statistics() 之后才追加的,对按年盈亏统计不是必需的(文档来源:cta_backtester.md 每日盈亏说明;源码:vnpy_ctastrategy/backtesting.py)。
- 你示例里的回测区间(2018-03-23 至 2018-04-23)只在一个自然年内,按年分组只会得到一条记录;要验证跨年度效果,请把
start / end 扩展到多年。
- 若需要计算“年收益率”,直接用
pnl / engine.capital 即可。
参考来源:
- 源码:
vnpy_ctastrategy/backtesting.py(calculate_result、DailyResult 类)
- 文档:
cta_backtester.md(每日盈亏的逐日盯市口径说明)
基于RAG技术开发的VeighNa AI助手,你的个人专属 Desk Quant