28 KiB
事件驱动回测引擎详解
quant_event_driven_backtest_demo.py 学习文档
目标读者:零基础量化入门者
配套文件:quant_event_driven_backtest_demo.py
前置知识:建议先阅读前两篇文档
系列位置:第 3 篇 — 事件驱动回测篇
目录
- 为什么需要事件驱动回测?
- 整体架构总览
- 事件类详解 (Event Classes)
- 数据处理器 (DataHandler)
- 策略引擎 (Strategy)
- 组合管理器 (Portfolio)
- 模拟券商 (SimulatedBroker)
- 回测引擎主循环 (BacktestEngine)
- 绩效分析
- 事件驱动 vs 向量化:结果差异解析
- 术语速查表
1. 为什么需要事件驱动回测?
在上一篇文档中,我们用向量化回测验证了策略。向量化回测速度极快,适合快速探索,但它有一个根本局限:
向量化回测是"上帝视角" —— 所有数据同时可见,一次性计算完毕。
真实交易是"时间顺序" —— 只有当前这根 K 线,下一根是未知的。
1.1 向量化回测无法模拟的真实问题
| 现实场景 | 向量化的处理 | 事件驱动的处理 |
|---|---|---|
| 订单类型 (Order Types) | 只能假设"立即以当日收盘价成交" | 可以模拟市价单/限价单/止损单 |
| 成交延迟 (Fill Delay) | 信号日 = 成交日(不现实) | 信号日 T,下一日 T+1 开盘才成交 |
| 资金限制 (Capital Constraints) | 隐性假设可以买任意多 | 按实际现金余额决定买多少股 |
| 整数股 (Integer Shares) | 往往假设可以买分数股 | 强制取整(现实中必须整数) |
| 实时风控 (Real-time Risk Check) | 无法在"每笔交易"时检查 | 每个 OrderEvent 前都可以检查 |
| 多策略组合 (Multi-Strategy) | 难以优雅处理 | 每个策略独立处理 MarketEvent |
| 实盘无缝切换 | 无法复用代码 | 策略代码在回测和实盘中完全相同 |
1.2 核心优势:回测代码 = 实盘代码
事件驱动的最大价值是:你的策略类 (Strategy)、组合管理器 (Portfolio) 在回测中和实盘中运行的是完全相同的代码。
切换到实盘只需要将 DataHandler(历史数据)换成实时行情 API,SimulatedBroker(模拟券商)换成真实券商 API。
回测模式: DataHandler(历史CSV) → 策略 → 组合 → SimulatedBroker(模拟)
实盘模式: MarketDataFeed(API) → 策略 → 组合 → RealBroker(真实券商API)
↑ 这两行代码完全不变 ↑
2. 整体架构总览
2.1 系统的核心:事件队列
整个系统围绕一个事件队列 (Event Queue) 运转。所有组件都通过这个队列通信,彼此之间完全解耦 (Decoupled)——没有任何组件直接调用另一个组件,它们只是往队列里放事件,或者从队列里取事件。
┌─────────────────────────────────┐
│ 事件队列 (Event Queue) │
│ 先进先出 (FIFO deque) │
│ ← 放入事件 取出事件 → │
└─────────────────────────────────┘
↑↑↑↑↑↑↑
┌──────────┬─────────┴──────────┬──────────┐
│ │ │ │
DataHandler Strategy Portfolio SimulatedBroker
数据处理器 策略引擎 组合管理器 模拟券商
│ │ │ │
放入 Market 放入 Signal 放入 Order 放入 Fill
Event Event Event Event
2.2 单个交易日的事件流
每天,系统会经历以下事件序列:
⏰ 时钟滴答:新的一个交易日开始
│
▼
[1] DataHandler.stream_next()
→ 将今天的 OHLCV 数据封装成 MarketEvent 放入队列
│
▼
[2] SimulatedBroker.fill_pending()
→ 以今天的开盘价成交昨天挂的待处理订单
→ 成交后产生 FillEvent 放入队列
│
▼
[3] 处理队列中的所有事件:
│
├─ MarketEvent → Strategy.on_market()
│ 策略看到今天的价格 → 可能产生 SignalEvent
│
├─ SignalEvent → Portfolio.on_signal()
│ 组合管理器决定买多少 → 产生 OrderEvent
│
├─ OrderEvent → SimulatedBroker.on_order()
│ 券商接收订单 → 加入待处理队列(明天开盘成交)
│
└─ FillEvent → Portfolio.on_fill()
更新现金余额和持仓记录
│
▼
[4] Portfolio.record_equity()
→ 记录今天收盘时的账户净值快照
│
▼
⏰ 时钟滴答:下一个交易日开始(回到步骤 1)
为什么第 2 步(成交待处理订单)在第 3 步(处理队列)之前?
这是防止前视偏差的关键设计:
- 今天(T日)策略看到价格后产生信号,挂了一个订单
- 这个订单在今天不成交(因为今天的收盘价就是信号触发的那根 K 线)
- 明天(T+1 日)开盘时,才以开盘价成交这个订单
- 这正确模拟了现实中"当天看到信号,明天才能买到"的情况
3. 事件类详解 (Event Classes)
代码定义了 4 种事件类,像快递单据一样,每类事件携带不同的信息。
3.1 MarketEvent(市场事件)
触发时机:DataHandler 每天推送新的 K 线数据时
携带信息:OHLCV 数据(开高低收量)
@dataclass
class MarketEvent:
symbol : str # 股票代码 例: "000001.SZ"
date : pd.Timestamp # 日期 例: 2024-01-15
open : float # 开盘价 (Opening Price)
high : float # 最高价 (High Price)
low : float # 最低价 (Low Price)
close : float # 收盘价 (Closing Price)
volume : float # 成交量 (Volume)
OHLCV 是什么?
一根 K 线(Candlestick / Bar)记录了某个时间段内的价格运动全貌:
- O (Open 开盘价):这个时间段第一笔成交价
- H (High 最高价):这个时间段最高成交价
- L (Low 最低价):这个时间段最低成交价
- C (Close 收盘价):这个时间段最后一笔成交价
- V (Volume 成交量):这个时间段总成交股数
3.2 SignalEvent(信号事件)
触发时机:策略判断应该开仓/平仓时
携带信息:交易方向、信号强度
@dataclass
class SignalEvent:
symbol : str # 股票代码
date : pd.Timestamp
direction : Direction # LONG(做多)/ SHORT(做空)/ EXIT(平仓)
strength : float # 信号强度 0~1,用于仓位管理
# 1.0 = 满仓,0.5 = 半仓
strategy : str # 产生信号的策略名称(多策略时有用)
关键区别:SignalEvent 是"建议",不是"命令"。
Portfolio 收到 SignalEvent 后,还会根据风控规则决定是否执行、执行多少。
3.3 OrderEvent(订单事件)
触发时机:Portfolio 决定下单后
携带信息:具体下单量、订单类型
@dataclass
class OrderEvent:
symbol : str # 股票代码
date : pd.Timestamp
order_type : OrderType # MARKET(市价单)/ LIMIT(限价单)/ STOP(止损单)
direction : Direction # LONG / SHORT / EXIT
quantity : int # 下单数量(股数,整数!)
limit_price : float # 限价单价格(仅限价单用)
stop_price : float # 止损价格(仅止损单用)
三种订单类型:
| 类型 | 中文 | 说明 | 优点 | 缺点 |
|---|---|---|---|---|
| MARKET | 市价单 | 立即以当前最优价成交 | 100% 成交 | 价格不确定,大单可能有较大滑点 |
| LIMIT | 限价单 | 只在指定价格或更好时成交 | 价格确定 | 可能无法成交(市场未到你的价格) |
| STOP | 止损单 | 价格到达止损价时转为市价单 | 控制最大亏损 | 跳空时实际成交价可能比止损价更差 |
本 Demo 只实现了市价单(最简单),限价单和止损单留作扩展练习。
3.4 FillEvent(成交事件)
触发时机:模拟券商确认订单已成交后
携带信息:实际成交价(含滑点)、佣金
@dataclass
class FillEvent:
symbol : str
date : pd.Timestamp
direction : Direction # LONG / SHORT / EXIT
quantity : int # 实际成交股数
fill_price : float # 实际成交价(已含滑点,比理论价差一点)
commission : float # 本次交易的佣金金额(元)
slippage : float # 滑点额(实际成交价 - 开盘价)
@property
def total_cost(self):
# 买入:现金减少(负号)
# 卖出:现金增加(正号)
sign = -1 if direction in (LONG, SHORT) else +1
return sign * (fill_price * quantity + commission)
FillEvent 是最终事实 (Ground Truth)——一旦产生,就意味着交易确实发生了,Portfolio 必须据此更新账户状态。
4. 数据处理器 (DataHandler)
4.1 核心职责
DataHandler 负责逐根 K 线地向系统"喂"数据,模拟"不知道未来"的真实状态。
class DataHandler:
def stream_next(self) -> bool:
"""
每次调用,只释放"下一根 K 线"的数据。
就像真实交易中,每天只能看到当天的数据,
明天的数据还没发生。
"""
...
self.event_queue.put(bar) # 把新 K 线放入队列
4.2 历史记录与指标计算
DataHandler 还维护了一个"已经看到的 K 线"的历史记录,供策略查询:
def get_close_series(self) -> pd.Series:
"""
返回到目前为止所有已见过的收盘价序列。
策略可以用这个序列计算均线、RSI 等指标。
关键:这个序列只包含"过去"的数据,不包含任何"未来"数据。
这从结构上保证了不会有前视偏差。
"""
为什么不直接给策略整个数据集?
这正是事件驱动的防错设计——即使你不小心让策略代码 data.get_close_series() 在某天调用,它也只能拿到"截至今天"的数据,不可能拿到明天的收盘价,因为 DataHandler 只把已经流出去的数据存入历史记录。
5. 策略引擎 (Strategy)
5.1 抽象基类设计
代码用 Python 的抽象基类 (Abstract Base Class, ABC) 定义了策略的"接口合同":
class Strategy(ABC):
@abstractmethod
def on_market(self, event: MarketEvent) -> None:
"""所有策略都必须实现这个方法"""
...
好处:
- 任何具体策略(MA策略、RSI策略、机器学习策略)只需继承
Strategy并实现on_market - 回测引擎不关心策略具体怎么计算,只知道"来了 MarketEvent 就调用 on_market"
- 这叫开放封闭原则 (Open-Closed Principle):对扩展开放,对修改封闭
5.2 MA 策略的事件驱动实现
向量化版本(上一篇)一次性计算了所有日期的均线。
事件驱动版本在每一根新 K 线到来时,只用截至今天的历史数据重新计算一次:
class MACrossoverStrategy(Strategy):
def on_market(self, event: MarketEvent) -> None:
# 从 DataHandler 获取迄今为止的所有收盘价(不含未来)
closes = self.data.get_close_series()
# 只用"窗口内最近的数据"计算当前均线值
sma_short = closes.iloc[-self.short_window:].mean()
sma_long = closes.iloc[-self.long_window:].mean()
# 检测交叉
sma_short_prev = closes.iloc[-(self.short_window+1):-1].mean()
sma_long_prev = closes.iloc[-(self.long_window+1):-1].mean()
was_above = sma_short_prev > sma_long_prev
is_above = sma_short > sma_long
# 金叉:昨天还在下方,今天穿越到上方
if is_above and not was_above and not self._in_position:
self.send_signal(..., Direction.LONG)
self._in_position = True
# 死叉:昨天还在上方,今天穿越到下方
elif not is_above and was_above and self._in_position:
self.send_signal(..., Direction.EXIT)
self._in_position = False
策略内部状态 _in_position:
这个布尔变量追踪"当前是否已经持有多仓",防止在已经持仓时重复买入,或者在没有持仓时发出平仓信号。这在向量化回测中很难优雅处理,在事件驱动中则非常自然。
5.3 RSI 策略的完整多空逻辑
class RSIMeanReversionStrategy(Strategy):
def on_market(self, event: MarketEvent) -> None:
rsi = self._compute_rsi(closes) # 只用迄今为止的数据
# 空仓时的入场规则(Entry Rules)
if rsi < 30 and self._position == 0:
self.send_signal(..., Direction.LONG, strength=0.8) # 超卖 → 做多
self._position = 1
elif rsi > 70 and self._position == 0:
self.send_signal(..., Direction.SHORT, strength=0.8) # 超买 → 做空
self._position = -1
# 持仓时的出场规则(Exit Rules)
elif self._position == 1 and rsi > 50: # 多仓,RSI 回到中性 → 平多
self.send_signal(..., Direction.EXIT)
self._position = 0
elif self._position == -1 and rsi < 50: # 空仓,RSI 回到中性 → 平空
self.send_signal(..., Direction.EXIT)
self._position = 0
strength=0.8 的含义:
信号强度 0.8 表示"我不是百分之百确定,只动用 80% 的可用资金"。Portfolio 收到这个信号后,会用 capital × position_pct × strength 来计算下单金额。
6. 组合管理器 (Portfolio)
Portfolio 是管理"钱"的大脑,负责:
- 决定买多少(仓位管理)
- 更新现金余额和持仓记录
- 计算盈亏
- 记录净值曲线
6.1 仓位管理:固定比例法 (Fixed Fraction)
def on_signal(self, event: SignalEvent):
current_price = self.data.get_close_series().iloc[-1]
# 计算下单资金 = 当前总资产 × 仓位比例 × 信号强度
capital_to_deploy = self.cash * self.position_pct * event.strength
# 下单股数 = 下单资金 / 当前价格(向下取整,必须整数股)
qty = int(capital_to_deploy / current_price)
仓位管理 (Position Sizing) 是风险管理的核心。代码使用了最简单的固定比例法 (Fixed Fraction):每次动用总资产的固定比例(如 95%)。
更高级的仓位管理方法:
| 方法 | 中文 | 逻辑 |
|---|---|---|
| Fixed Dollar | 固定金额 | 每次固定买 X 元 |
| Fixed Fraction | 固定比例 | 每次买总资产的 X% |
| Kelly Criterion | 凯利公式 | 根据"胜率和盈亏比"计算最优比例,理论上最大化长期增长率 |
| Volatility Scaling | 波动率缩放 | 仓位与波动率反比,高波动时减仓,低波动时加仓 |
6.2 持仓均价与盈亏计算
def on_fill(self, event: FillEvent):
if direction == LONG:
# 更新持仓均价(加权平均成本)
# 例:已有 1000 股均价 10 元,再买 500 股 @ 12 元
# 新均价 = (1000×10 + 500×12) / 1500 = 10.67 元
new_qty = old_qty + qty
self.avg_cost[symbol] = (old_cost * old_qty + price * qty) / new_qty
# 现金减少(买入)
self.cash -= price * qty + commission
elif direction == EXIT:
# 计算已实现盈亏(Realized P&L)
# 盈亏 = (出场价 - 入场均价) × 数量 - 佣金
pnl = (exit_price - entry_price) * qty - commission
# 现金增加(卖出回款)
self.cash += exit_price * qty - commission
两种盈亏概念:
| 概念 | 中文 | 含义 |
|---|---|---|
| Unrealized P&L | 浮动盈亏 / 未实现盈亏 | 持仓中但尚未卖出的账面盈亏 |
| Realized P&L | 已实现盈亏 | 已经卖出、真正落袋的盈亏 |
"浮盈不是赚,浮亏不是亏"——只有平仓后才算真正的盈亏。
7. 模拟券商 (SimulatedBroker)
7.1 两大成本来源
① 佣金 (Commission):
\text{佣金} = \max(\text{交易金额} \times \text{佣金率},\ \text{最低佣金})
代码中:commission_rate = 0.0003(万分之三),min_commission = 5.0(最低 5 元)
最低佣金很重要:小额交易(如 500 元的单子)理论佣金 = 500 × 0.03% = 0.15 元,但实际按 5 元收取,成本率高达 1%!
② 滑点 (Slippage):
def _compute_fill_price(self, order, bar):
if direction == LONG:
# 买入时,实际成交价比开盘价"稍贵"(滑点向上)
return bar.open * (1 + self.slippage_rate)
else:
# 卖出时,实际成交价比开盘价"稍便宜"(滑点向下)
return bar.open * (1 - self.slippage_rate)
滑点向不利方向运动的原因:
- 你想买的时候,有人知道你要买,会把价格稍稍抬高(市场冲击)
- 你想卖的时候,买家会压低报价
7.2 订单的执行时机
今天(T 日)收盘后:策略产生信号 → Portfolio 挂出 OrderEvent
今天(T 日)收盘时:Broker 收到订单,加入 pending_orders 列表
⚠️ 注意:今天不成交!
明天(T+1 日)开盘时:Broker 以今天的开盘价成交昨天挂的所有 pending 订单
→ 产生 FillEvent,更新 Portfolio
代码逻辑:
# 每天开始时,先把昨天的待处理订单用今天的开盘价成交
def fill_pending(self, bar: MarketEvent):
for order in self._pending_orders:
fill_price = bar.open * (1 ± slippage) # 今天的开盘价 ± 滑点
commission = max(fill_price * qty * rate, min_comm)
self.queue.put(FillEvent(...))
self._pending_orders.clear()
8. 回测引擎主循环 (Backtest Engine)
8.1 主循环算法全貌
def run(self):
# 外层循环:每天执行一次
while self.data.has_more_data():
# ① 推进一天:DataHandler 推送今天的 MarketEvent
self.data.stream_next()
current_bar = self.data._history[-1] # 今天的 K 线
# ② 成交待处理订单(以今天开盘价)
# 昨天的信号在今天才能执行,这正确模拟了成交延迟
self.broker.fill_pending(current_bar)
# ③ 处理队列中所有事件(内层循环)
while not self._queue.empty():
event = self._queue.get()
if event.type == MARKET:
self.strategy.on_market(event) # 策略观察市场
elif event.type == SIGNAL:
self.portfolio.on_signal(event) # 组合决定下单量
elif event.type == ORDER:
self.broker.on_order(event) # 券商接收订单
elif event.type == FILL:
self.portfolio.on_fill(event) # 组合更新持仓现金
# ④ 记录今天收盘时的净值快照
self.portfolio.record_equity(current_bar.date, current_bar.close)
8.2 为什么内层循环很重要?
注意步骤 ③ 是一个内层 while 循环,处理所有队列中的事件。
这是因为事件会产生新的事件:
- MarketEvent → 策略处理后产生 SignalEvent(放入队列)
- SignalEvent → Portfolio 处理后产生 OrderEvent(放入队列)
- 下一次循环开始才处理 OrderEvent
但如果有 FillEvent(今天的成交回报),它也需要在今天内处理完,更新好 Portfolio 状态,才能保证 record_equity 记录的是更新后的净值。
8.3 工厂函数 (Factory Function)
代码提供了一个 build_engine 函数,每次调用都创建全新、独立的组件实例:
def build_engine(strategy_class, strategy_kwargs, name, ...):
q = queue.Queue() # ← 全新事件队列,与其他回测完全隔离
data = DataHandler(ohlcv, SYMBOL, q)
strategy = strategy_class(data, q, **strategy_kwargs)
portfolio = Portfolio(data, q, initial_capital=1_000_000.0)
broker = SimulatedBroker(q, ...)
return BacktestEngine(data, strategy, portfolio, broker, name=name)
为什么需要全新实例?
如果两次回测共享同一个 Portfolio,第一次回测的持仓和现金状态会污染第二次回测。每次回测都必须从"干净的初始状态"开始。
9. 绩效分析 (Performance Analytics)
9.1 两个维度的绩效
事件驱动回测比向量化回测多了一个维度:交易级别 (Trade-Level) 分析。
| 分析维度 | 数据来源 | 代表指标 |
|---|---|---|
| 日度水平 (Daily Level) | equity_curve 每日净值 |
夏普、最大回撤、年化收益 |
| 交易水平 (Trade Level) | trade_log 每笔交易 |
交易胜率、单笔平均盈亏、盈亏比 |
9.2 交易日志 (Trade Log)
每笔完成的"买-卖"一个完整轮次叫一笔交易 (Round-Trip Trade)。代码记录了每笔交易:
self.trade_log.append({
"date" : 出场日期,
"symbol" : 股票代码,
"direction" : "LONG→EXIT" 或 "SHORT→EXIT",
"entry_price" : 入场均价(最初买入的均价),
"exit_price" : 出场价格(本次卖出价),
"quantity" : 成交股数,
"pnl" : 盈亏金额 = (出场价 - 入场价) × 股数 - 佣金,
"commission" : 本笔交易总佣金,
})
9.3 逐笔绩效的价值
通过 trade_log 可以深挖很多问题:
- 盈利的交易有多少笔?亏损的有多少笔?(交易胜率)
- 平均每笔赚多少?每笔亏多少?(期望值分析)
- 最大单笔亏损是多少?(单笔风险控制)
- 是否有明显的持仓时长规律?(持有时间分析,虽然本 Demo 未直接展示)
- 特定月份/季节表现如何?(季节性)
10. 事件驱动 vs 向量化:结果差异解析
本 Demo 的最后部分对比了两种方法对完全相同策略、完全相同数据的回测结果差异:
事件驱动 向量化
总收益率 Total Return 165.00% 173.92%
夏普比率 Sharpe Ratio 0.958 0.950
最大回撤 Max Drawdown -18.95% -20.34%
数字有差距,原因如下:
差异 1:成交时机不同
| 向量化 | 事件驱动 | |
|---|---|---|
| 成交价格 | 当天收盘价(信号产生的同一根 K 线) | 下一天开盘价(有 1 天延迟) |
| 现实性 | ❌ 不现实(无法做到) | ✅ 保守但现实 |
在上涨趋势中,明天开盘价通常比今天收盘价稍高(Gap Up),所以事件驱动的买入成本更高,收益率更低。
差异 2:整数股 vs 分数股
| 向量化 | 事件驱动 | |
|---|---|---|
| 交易单位 | 分数股(如 123.456 股) | 整数股(如 123 股,向下取整) |
| 现实性 | ❌ 不现实 | ✅ 现实 |
每次取整都会有少量资金无法部署(零头现金),随着股价升高,这个影响会越来越明显。
差异 3:动态资金 vs 静态假设
| 向量化 | 事件驱动 | |
|---|---|---|
| 仓位计算 | 隐性假设固定一个单位 | 每次用当前实际现金余额计算 |
| 效果 | 每笔交易资金相同 | 盈利后仓位增大,亏损后仓位缩小(自动复利) |
事件驱动更真实地模拟了复利效应 (Compounding Effect)。
差异 4:最低佣金
事件驱动设置了 min_commission = 5 元。当单笔交易金额较小时,按比例算出的佣金不足 5 元,实际收 5 元,成本率会高于向量化的固定比例假设。
11. 术语速查表
| 中文 | English | 简要说明 |
|---|---|---|
| 事件驱动 | Event-Driven | 通过事件队列驱动组件间通信的软件架构 |
| 事件队列 | Event Queue | 存放待处理事件的先进先出数据结构 |
| 解耦 | Decoupling | 各组件通过事件通信,相互不直接依赖 |
| 市场事件 | MarketEvent | 新 K 线数据到达的事件 |
| 信号事件 | SignalEvent | 策略产生买卖建议的事件 |
| 订单事件 | OrderEvent | 组合管理器向券商下单的事件 |
| 成交事件 | FillEvent | 券商确认订单已成交的事件 |
| 数据处理器 | DataHandler | 逐根 K 线流出历史数据的组件 |
| 策略 | Strategy | 观察市场并产生信号的逻辑层 |
| 抽象基类 | ABC (Abstract Base Class) | Python 强制要求子类实现特定方法的基类 |
| 组合管理器 | Portfolio | 管理现金、持仓、盈亏的组件 |
| 仓位管理 | Position Sizing | 决定每次交易动用多少资金的方法 |
| 固定比例法 | Fixed Fraction | 每次用总资产固定比例的仓位管理方法 |
| 凯利公式 | Kelly Criterion | 理论最优仓位比例的数学公式 |
| 持仓均价 | Average Cost (Entry Price) | 所有买入成本的加权平均价 |
| 已实现盈亏 | Realized P&L | 已平仓确认的盈亏金额 |
| 浮动盈亏 | Unrealized P&L | 持仓中尚未平仓的账面盈亏 |
| 模拟券商 | Simulated Broker | 回测中模拟真实券商行为的组件 |
| 市价单 | Market Order | 立即以最优价成交的订单类型 |
| 限价单 | Limit Order | 只在指定价格或更优时成交的订单 |
| 止损单 | Stop Order | 价格触发后转为市价单的保护性订单 |
| 滑点 | Slippage | 理论价格与实际成交价的差值 |
| 佣金 | Commission | 券商收取的交易手续费 |
| 成交延迟 | Fill Delay | 信号产生到实际成交的时间间隔 |
| 回测引擎 | Backtest Engine | 协调所有组件运行的主循环 |
| 主循环 | Main Event Loop | 驱动整个回测顺序推进的核心循环 |
| K 线 | Candlestick / Bar | 一段时间内的开高低收量数据 |
| OHLCV | OHLCV | Open / High / Low / Close / Volume |
| 开盘价 | Opening Price | 当日第一笔成交价 |
| 最高价 | High Price | 当日最高成交价 |
| 最低价 | Low Price | 当日最低成交价 |
| 收盘价 | Closing Price | 当日最后一笔成交价 |
| 成交量 | Volume | 当日总成交股数 |
| 向量化 | Vectorized | 用数组运算一次性处理所有时间步 |
| 前视偏差 | Lookahead Bias | 回测中使用了"当时未知"的未来数据 |
| 整数股 | Integer Shares | 现实中股票必须以整数股交易 |
| 复利效应 | Compounding Effect | 每期收益都在扩大的本金上继续赚取收益 |
| 成交时机 | Fill Timing | 订单被实际成交的时间点 |
| 信号强度 | Signal Strength | 策略对本次信号的置信度(用于仓位缩放) |
| 圆跳交易 | Round-Trip Trade | 从开仓到平仓的一个完整交易周期 |
| 交易胜率 | Trade Win Rate | 盈利交易次数 / 总交易次数 |
| 净值曲线 | Equity Curve | 账户总价值随时间变化的曲线 |
| 工厂函数 | Factory Function | 用于创建初始化好的完整对象的辅助函数 |
上一篇:策略开发与向量化回测
系列完结:恭喜你完成了量化交易基础三部曲!
附录:系列文档导航
| 篇 | 文件 | 文档 | 核心内容 |
|---|---|---|---|
| 第 1 篇 | quant_data_pipeline_demo.py |
doc_01_data_pipeline.md |
复权、收益率、缺失值、异常值、涨跌停 |
| 第 2 篇 | quant_strategy_backtest_demo.py |
doc_02_strategy_backtest.md |
技术指标、策略逻辑、向量化回测、绩效指标 |
| 第 3 篇 | quant_event_driven_backtest_demo.py |
doc_03_event_driven_backtest.md |
事件驱动架构、6大组件、成本模型、与向量化对比 |