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

回测时要将数据导入vnpy
要进行多品种回测的时候还是挺麻烦的,
比如,我现在有10多个品种的1分钟csv文件,
回测时可以直接读取csv文件,然后用vnpy的回测代码执行,这样可以的么?
如果可以,要如何实现?

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

可以的。VeighNa 回测引擎本质上只是回放 history_data 列表里的 BarData 对象,数据从数据库加载只是默认行为。看完 vnpy_ctastrategy/backtesting.py 源码后,有两种实现思路:

方案一:CSV 转 BarData,直接注入 history_data(不经过数据库)

回测流程是 load_data()run_backtesting(),其中 load_data() 只是从数据库加载数据填充 engine.history_data。因此可以跳过 load_data(),自己从 CSV 构造 BarData 列表

from datetime import datetime
import pandas as pd

from vnpy.trader.constant import Exchange, Interval
from vnpy.trader.object import BarData
from vnpy_ctastrategy.backtesting import BacktestingEngine

def load_bars_from_csv(
    filepath: str,
    symbol: str,
    exchange: Exchange
) -> list[BarData]:
    """从CSV文件加载K线数据,转换为BarData列表"""
    df: pd.DataFrame = pd.read_csv(filepath)
    bars: list[BarData] = []

    for row in df.itertuples():
        bar: BarData = BarData(
            symbol=symbol,
            exchange=exchange,
            datetime=datetime.strptime(row.datetime, "%Y-%m-%d %H:%M:%S"),
            interval=Interval.MINUTE,
            open_price=row.open,
            high_price=row.high,
            low_price=row.low,
            close_price=row.close,
            volume=row.volume,
            gateway_name="CSV"
        )
        bars.append(bar)

    return bars

# 回测引擎设置
engine: BacktestingEngine = BacktestingEngine()
engine.set_parameters(
    vt_symbol="rb2505.SHFE",
    interval=Interval.MINUTE,
    start=datetime(2024, 1, 1),
    end=datetime(2024, 12, 31),
    rate=0.0001,
    slippage=0.2,
    size=10,
    pricetick=0.2,
    capital=1_000_000
)

from vnpy_ctastrategy.strategies.double_ma_strategy import DoubleMaStrategy
engine.add_strategy(DoubleMaStrategy, {})

# 跳过 load_data(),直接注入 CSV 数据
engine.history_data = load_bars_from_csv(
    "rb2505_1min.csv", "rb2505", Exchange.SHFE
)

engine.run_backtesting()
engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()

注意事项:

  1. 数据排序:CSV 数据必须按时间升序排列,回测按列表顺序回放。
  2. 策略初始化问题:如果策略的 on_init() 里调用了 load_bar(10),回测模式下它仍然会去数据库查询历史数据(见 backtesting.pyload_bar 方法)。解决办法:
    • 修改策略 on_init,注释掉 load_bar 调用(前提是你的 CSV 数据足够长,指标会在回放中自然预热);
    • 或者在你的脚本中重写引擎的 load_bar 方法返回空列表。
  3. 多品种循环:对 10 多个品种,只需把上述流程封装成函数,循环每个 CSV 文件执行即可。

方案二:导入数据库(官方推荐方式)

如果之后还要做参数优化(run_bf_optimization 的多进程回测依赖数据库加载),建议用 DataManager 模块一次性批量导入 CSV 到数据库,之后回测直接读取,无需重复导入。DataManager 支持界面批量导入,也可以用脚本调用 database.save_bar_data(bars)

补充:真正的多品种联合回测

如果你的需求是"一个策略同时交易多个品种、共享资金"(而不是逐品种单独回测),CTA 回测引擎不支持,应使用 vnpy_portfoliostrategy(组合策略模块),其回测引擎支持传入多个 vt_symbol 和对应的 history_data 字典。

参考来源

  • vnpy_ctastrategy/backtesting.py(回测引擎源码)
  • 官方文档 cta_backtester.mdcta_strategy.md
Member
avatar
加入于:
帖子: 171
声望: 4

VeighNa_AI wrote:

可以的。VeighNa 回测引擎本质上只是回放 history_data 列表里的 BarData 对象,数据从数据库加载只是默认行为。看完 vnpy_ctastrategy/backtesting.py 源码后,有两种实现思路:

方案一:CSV 转 BarData,直接注入 history_data(不经过数据库)

回测流程是 load_data()run_backtesting(),其中 load_data() 只是从数据库加载数据填充 engine.history_data。因此可以跳过 load_data(),自己从 CSV 构造 BarData 列表

from datetime import datetime
import pandas as pd

from vnpy.trader.constant import Exchange, Interval
from vnpy.trader.object import BarData
from vnpy_ctastrategy.backtesting import BacktestingEngine

def load_bars_from_csv(
    filepath: str,
    symbol: str,
    exchange: Exchange
) -> list[BarData]:
    """从CSV文件加载K线数据,转换为BarData列表"""
    df: pd.DataFrame = pd.read_csv(filepath)
    bars: list[BarData] = []

    for row in df.itertuples():
        bar: BarData = BarData(
            symbol=symbol,
            exchange=exchange,
            datetime=datetime.strptime(row.datetime, "%Y-%m-%d %H:%M:%S"),
            interval=Interval.MINUTE,
            open_price=row.open,
            high_price=row.high,
            low_price=row.low,
            close_price=row.close,
            volume=row.volume,
            gateway_name="CSV"
        )
        bars.append(bar)

    return bars

# 回测引擎设置
engine: BacktestingEngine = BacktestingEngine()
engine.set_parameters(
    vt_symbol="rb2505.SHFE",
    interval=Interval.MINUTE,
    start=datetime(2024, 1, 1),
    end=datetime(2024, 12, 31),
    rate=0.0001,
    slippage=0.2,
    size=10,
    pricetick=0.2,
    capital=1_000_000
)

from vnpy_ctastrategy.strategies.double_ma_strategy import DoubleMaStrategy
engine.add_strategy(DoubleMaStrategy, {})

# 跳过 load_data(),直接注入 CSV 数据
engine.history_data = load_bars_from_csv(
    "rb2505_1min.csv", "rb2505", Exchange.SHFE
)

engine.run_backtesting()
engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()

注意事项:

  1. 数据排序:CSV 数据必须按时间升序排列,回测按列表顺序回放。
  2. 策略初始化问题:如果策略的 on_init() 里调用了 load_bar(10),回测模式下它仍然会去数据库查询历史数据(见 backtesting.pyload_bar 方法)。解决办法:
    • 修改策略 on_init,注释掉 load_bar 调用(前提是你的 CSV 数据足够长,指标会在回放中自然预热);
    • 或者在你的脚本中重写引擎的 load_bar 方法返回空列表。
  3. 多品种循环:对 10 多个品种,只需把上述流程封装成函数,循环每个 CSV 文件执行即可。

方案二:导入数据库(官方推荐方式)

如果之后还要做参数优化(run_bf_optimization 的多进程回测依赖数据库加载),建议用 DataManager 模块一次性批量导入 CSV 到数据库,之后回测直接读取,无需重复导入。DataManager 支持界面批量导入,也可以用脚本调用 database.save_bar_data(bars)

补充:真正的多品种联合回测

如果你的需求是"一个策略同时交易多个品种、共享资金"(而不是逐品种单独回测),CTA 回测引擎不支持,应使用 vnpy_portfoliostrategy(组合策略模块),其回测引擎支持传入多个 vt_symbol 和对应的 history_data 字典。

参考来源

  • vnpy_ctastrategy/backtesting.py(回测引擎源码)
  • 官方文档 cta_backtester.mdcta_strategy.md

你提供的方案一很好,我测试成功了。
请问在这种代码形式回测下,测试生成的成交记录保存在哪里?
怎么查看?

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

在这种纯脚本回测模式下,成交记录只保存在内存中,不会写入任何文件。它们存放在回测引擎的 trades 字典里,回测结束后需要通过接口取出查看或导出。

查看方式

BacktestingEngine 提供了 get_all_trades() 方法,返回成交记录列表(TradeData 对象列表):

engine.run_backtesting()

# 获取全部成交记录
trades = engine.get_all_trades()

for trade in trades:
    print(
        trade.datetime, trade.direction, trade.offset,
        trade.price, trade.volume, trade.vt_orderid
    )

导出到 CSV(推荐)

方便后续用 Excel 或 pandas 分析:

import pandas as pd

def export_trades(engine: BacktestingEngine, filepath: str) -> None:
    """将回测成交记录导出为CSV"""
    records: list[dict] = []

    for trade in engine.get_all_trades():
        records.append({
            "成交时间": trade.datetime,
            "合约代码": trade.vt_symbol,
            "方向": trade.direction.value,
            "开平": trade.offset.value,
            "价格": trade.price,
            "数量": trade.volume,
            "委托号": trade.vt_orderid,
            "成交号": trade.vt_tradeid,
        })

    df: pd.DataFrame = pd.DataFrame(records)
    df.to_csv(filepath, index=False, encoding="utf-8-sig")

engine.run_backtesting()
export_trades(engine, "trades_rb2505.csv")

相关补充

类似的接口还有:

方法 内容
get_all_trades() 全部成交记录
get_all_orders() 全部委托记录(含未成交/撤单)
get_all_daily_results() 逐日盈亏统计
calculate_statistics(df, output=True) 汇总指标(胜率、盈亏比、回撤等),终端打印

另外,如果你希望像 CtaBacktester 界面那样直观地看到每笔开平仓在 K 线图上的标注,直接调用 engine.show_chart() 即可,图上会用箭头标出各笔成交。

参考来源:vnpy_ctastrategy/backtesting.py 源码中的 get_all_tradesupdate_tradeshow_chart 方法。

© 2015-2022 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号

沪公网安备 31011502017034号

【用户协议】
【隐私政策】
【免责条款】