核心概念

QTrade 体量不大,但理清各组件之间的关系会让 API 和源码都好读得多。本页提供整体的心智模型。

组件构成

        ┌─────────────────────────────────────────────────┐
        │                    Backtest                     │
        │  (orchestrates a full run; iterates the bars)   │
        └────────────────────┬────────────────────────────┘
                             │ owns
                             ▼
        ┌─────────────────────────────────────────────────┐
        │                    Strategy                     │
        │  user code: prepare(), on_bar_close()           │
        │  reads market data, places Orders via the Broker│
        └────────────────────┬────────────────────────────┘
                             │ places orders on
                             ▼
        ┌─────────────────────────────────────────────────┐
        │                     Broker                      │
        │  fills Orders into Trades, tracks cash, equity, │
        │  margin, SL/TP, equity history                  │
        └──────────┬──────────────────────────┬───────────┘
                   │ holds                     │ records
                   ▼                           ▼
        ┌──────────────────┐         ┌─────────────────────┐
        │   Position       │         │   Order / Trade     │
        │ active + closed  │         │   filled / closed   │
        │ Trade objects    │         │   queues            │
        └──────────────────┘         └─────────────────────┘

做强化学习时,把 Backtest 换成 TradingEnv —— 底层是相同的 Broker,只是驱动方式从 Python for 循环换成了 Gymnasium 的 step()

每个组件做什么

Broker

模拟核心。你通常不会直接实例化它 —— BacktestTradingEnv 都会自动创建。

Broker 负责:

  • 保存 OHLCV 数据(单资产或多资产的 dict[str, DataFrame]

  • 跟踪 cash,计算 equityunrealized_pnlavailable_margin

  • 维护 pending / executing / filled / closed 几个订单队列

  • 每个 bar 检查止损 / 止盈触发

  • 每个资产持有一个 Position

  • 记录组合级别的 equity_history Series

Order

交易 意图 —— 你想做什么。用带符号的 size 创建(正 = 买,负 = 卖),可选 limit / stop / sl / tp / tag 以及 asset 资产代码。订单进入 broker 的队列后,要么成交,要么被拒(通常是保证金不足)。

Trade

A filled position. Each Order that fills opens (or partially closes) a Trade. While open, the Trade has entry_price, entry_date, size, optional sl / tp (modifiable mid-trade — see Stops), and optional trailing-stop state. When the Trade is closed (by an opposite-side order, an SL/TP trigger, or end-of-backtest), it gets exit_price, exit_date, profit, and an exit_reason.

Position

某个资产上 trades 的容器。active_trades 是开仓中的,closed_trades 是已平仓的。position.size 是该资产所有 active trades 的净有符号 size。

多资产模式下,Broker 持有 positions: dict[str, Position] —— 每个资产一个。broker.position(单数)是单资产模式专用的便捷访问器。

Strategy

你需要自己写的那部分。继承 qtrade.backtest.Strategy 并实现:

  • prepare() —— 在 bar 循环开始前调用一次。在这里加指标。

  • on_bar_close() —— 每根 bar 调用一次。下单和管理仓位的地方。

on_bar_close() 内部你可以使用:

  • self.data(单资产)/ self.data_by_asset[asset](多资产)—— 从开始到当前 bar 的 OHLCV 切片。

  • self.position / self.positions[asset] —— 当前持仓。

  • self.equityself.unrealized_pnl —— 组合级快照。

  • self.buy(...)self.sell(...)self.close(...) —— 下单辅助方法。

Backtest

驱动器。给定 dataStrategy 类、现金、佣金、保证金设置后,它会:

  1. 构造一个 Broker 和一个 Strategy 实例。

  2. 调用 strategy.prepare()

  3. 逐根 bar 循环,依次调用 broker.process_bar(ts)strategy.on_bar_close()

  4. 结束时关闭所有未平仓位。

以及工具方法:optimizewalk_forward_optimizeshow_statsget_trade_historyplot

TradingEnv(强化学习)

一个包装相同 Broker 的 Gymnasium 环境。由三个可插拔的 scheme 组合而成:

Scheme

决定

ActionScheme

动作空间 + 一个动作如何转换为 Orders

ObserverScheme

观测空间 + 智能体每步看到什么

RewardScheme

每步返回的标量奖励

完整内容见自定义交易环境

回测的生命周期

对于一根典型的 bar,按以下顺序执行:

  1. Backtest.run 推进到第 i 根 bar。

  2. Broker.process_bar(ts) 触发:

    • 从 pending 队列中清除已取消 / 已拒绝的订单。

    • 处理 executing 队列订单(排队等下一根 bar 开盘成交)。

    • 对每笔 active trade 检查 SL / TP —— 触发的就平仓。

    • 处理 pending 队列里的 stop / limit 订单。

    • 用新 bar 的 equity 更新 equity_history

  3. Strategy.on_bar_close() 运行 —— 你可以读 self.data(截到 i)并下新单。如果 trade_on_close=True,新单立即成交;否则进入队列等下一根 bar 开盘。

最后一根 bar 之后,Broker.close_all_positions() 用最后一根 bar 的 close 价清掉所有未平仓位。

关键设计选择(以及原因)

  • 事件循环,而非向量化。策略代码读起来就是真实的交易逻辑(if x then buy)。更好写、更好调试。在大规模参数扫描场景比 vectorbt 慢 —— 参见 COMPARISON.md

  • 结构上杜绝未来函数self.data 被切片到 [:current_time],你在 on_bar_close() 里根本读不到未来 bar。

  • 一个现金池 + 各资产独立持仓。多资产回测获得真实的组合行为(保证金跨资产共享),不必管理 N 个独立的 broker。

  • Strategy 和 RL 共用一个 Broker。相同的账户逻辑、相同的 SL/TP 行为、相同的成交语义。TradingEnv 里的 RL 智能体和 Backtest 里的 Strategy 受完全一样的规则约束。