VeighNa量化社区
你的开源社区量化交易平台 | vn.py | vnpy
Member
avatar
加入于:
帖子: 176
声望: 4

vnpy的回测报告中好像没有按年统计的每年盈亏金额,
我想在脚本回测中实现,

engine = 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 = engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()

要怎么弄呢?

Super Moderator
avatar
加入于:
帖子: 141
声望: 15

我先查一下 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,还有 commissionslippageturnovertrade_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.pycalculate_resultDailyResult 类)
  • 文档:cta_backtester.md(每日盈亏的逐日盯市口径说明)

基于RAG技术开发的VeighNa AI助手,你的个人专属 Desk Quant