feat(engine): 添加事件驱动回测引擎
- backtest/engine.py: 事件驱动回测引擎核心,支持 K 线推进/订单撮合/权益曲线 - backtest/models.py: 回测数据模型(订单/成交/持仓/账户快照) - backtest/README.md: 回测模块使用说明 - backtest/STRATEGY.md: 策略开发指南与最佳实践 - backtest/TIMEFRAME_COMPARISON*.md: 多周期回测对比分析报告
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
# engine/backtest — 回测引擎
|
||||
|
||||
事件驱动的历史回测框架,基于 `DataService` 从 TimescaleDB 读取历史 K 线,
|
||||
按时间顺序逐根推送给策略,模拟订单成交、跟踪资金曲线、计算绩效指标。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
from engine.backtest import BacktestEngine, BacktestConfig
|
||||
from engine.common.config import config
|
||||
|
||||
async def main():
|
||||
bt_config = BacktestConfig(
|
||||
symbol="BTCUSDT",
|
||||
interval="1h",
|
||||
start_time=datetime(2025, 1, 1),
|
||||
end_time=datetime(2025, 6, 1),
|
||||
initial_capital=10_000.0,
|
||||
)
|
||||
|
||||
engine = BacktestEngine(bt_config, db_config=config.db)
|
||||
result = await engine.run(MyStrategy, my_strategy_config)
|
||||
print(result.summary())
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
## 核心概念
|
||||
|
||||
### 回测流程
|
||||
|
||||
```
|
||||
加载历史 K 线 → 预热阶段 → 主循环 → 计算指标 → 输出结果
|
||||
↓
|
||||
逐根 K 线推送:
|
||||
1. 执行上根 Bar 产生的买单(在开盘价执行)
|
||||
2. 推送 K 线给策略 → 产生信号
|
||||
3. 卖出信号立即执行,买入信号延迟到下一根 Bar
|
||||
4. 记录资金曲线
|
||||
```
|
||||
|
||||
### 避免未来函数
|
||||
|
||||
- **买入信号**:在当前 K 线收盘生成 → **下一根 K 线开盘价**执行
|
||||
- **卖出信号**:在当前 K 线收盘生成 → **当前 K 线收盘价**执行
|
||||
|
||||
这样可以避免使用已知收盘价来获利的偏差。
|
||||
|
||||
### 交易成本
|
||||
|
||||
引擎模拟以下交易成本:
|
||||
|
||||
| 成本项 | 默认值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| 手续费 | 0.1% | 按成交额收取 |
|
||||
| 滑点 | 0.05% | 买卖双向滑点 |
|
||||
|
||||
### 绩效指标
|
||||
|
||||
回测完成后自动计算以下指标:
|
||||
|
||||
| 指标 | 说明 |
|
||||
|------|------|
|
||||
| 总收益率 | (最终权益 - 初始资金) / 初始资金 × 100% |
|
||||
| 年化收益率 | 以复利方式年化 |
|
||||
| 夏普比率 | (日均收益 / 日收益标准差) × √365 |
|
||||
| 最大回撤 | 权益从峰值下跌的最大百分比 |
|
||||
| 回撤持续天数 | 从峰值到恢复(或结束)的最长天数 |
|
||||
| 胜率 | 盈利交易 / 总交易 |
|
||||
| 盈亏比 | 总盈利 / 总亏损绝对值 |
|
||||
| 卡尔玛比率 | 年化收益 / 最大回撤绝对值 |
|
||||
|
||||
## API 参考
|
||||
|
||||
### BacktestConfig
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class BacktestConfig:
|
||||
symbol: str # 交易对
|
||||
exchange: str = "binance" # 交易所
|
||||
interval: str = "1h" # K 线周期
|
||||
start_time: datetime | None = None # 起始时间
|
||||
end_time: datetime | None = None # 结束时间
|
||||
commission_pct: float = 0.001 # 手续费率
|
||||
slippage_pct: float = 0.0005 # 滑点率
|
||||
min_order_qty: float = 0.001 # 最小下单量
|
||||
initial_capital: float = 10_000.0 # 初始资金
|
||||
warmup_bars: int = 100 # 预热条数
|
||||
```
|
||||
|
||||
### BacktestEngine
|
||||
|
||||
```python
|
||||
class BacktestEngine:
|
||||
def __init__(self, config: BacktestConfig, db_config=None)
|
||||
|
||||
async def run(
|
||||
self,
|
||||
strategy_cls: Type[BaseStrategy],
|
||||
strategy_config: StrategyConfig,
|
||||
) -> BacktestResult
|
||||
```
|
||||
|
||||
### BacktestResult
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class BacktestResult:
|
||||
config: BacktestConfig # 回测配置
|
||||
strategy_config: dict # 策略配置
|
||||
metrics: BacktestMetrics # 绩效指标
|
||||
trades: list[BacktestTrade] # 交易记录
|
||||
equity_curve: list[dict] # 资金曲线
|
||||
|
||||
def summary(self) -> str # 人类可读摘要
|
||||
```
|
||||
|
||||
### 编写策略
|
||||
|
||||
策略必须继承 `BaseStrategy`,实现 `on_kline()` 方法:
|
||||
|
||||
```python
|
||||
from engine.common.base import BaseStrategy, Signal, StrategyConfig
|
||||
from engine.common.models import Kline
|
||||
|
||||
class MyConfig(StrategyConfig):
|
||||
param1: int = 10
|
||||
|
||||
class MyStrategy(BaseStrategy):
|
||||
strategy_type = "my_strategy"
|
||||
|
||||
def __init__(self, config: MyConfig):
|
||||
super().__init__(config)
|
||||
self._closes = []
|
||||
|
||||
async def on_kline(self, kline: Kline) -> Signal | None:
|
||||
self._closes.append(kline.close)
|
||||
# 策略逻辑 ...
|
||||
if 买入条件:
|
||||
return Signal(
|
||||
symbol=self.config.symbol,
|
||||
side="BUY",
|
||||
signal_type="MARKET",
|
||||
reason="...",
|
||||
timestamp=kline.open_time,
|
||||
)
|
||||
return None
|
||||
```
|
||||
|
||||
### 技术指标库
|
||||
|
||||
`engine/indicators/` 提供常用的技术指标计算函数,纯 Python 实现,无外部依赖:
|
||||
|
||||
```python
|
||||
from engine.indicators import sma, ema, macd, rsi, bollinger, atr, obv, vwap
|
||||
|
||||
closes = [100.0, 101.0, 102.0, ...]
|
||||
ma = sma(closes, period=20) # 简单移动平均
|
||||
ema_vals = ema(closes, period=12) # 指数移动平均
|
||||
rsi_vals = rsi(closes, period=14) # RSI [0, 100]
|
||||
upper, mid, lower = bollinger(closes, period=20, std=2) # 布林带
|
||||
macd_line, signal, hist = macd(closes, fast=12, slow=26, signal=9) # MACD
|
||||
atr_vals = atr(highs, lows, closes, period=14) # ATR
|
||||
```
|
||||
|
||||
| 模块 | 指标 | 函数 |
|
||||
|------|------|------|
|
||||
| `trend` | 趋势 | `sma`, `ema`, `macd`, `macd_signal`, `macd_histogram` |
|
||||
| `momentum` | 动量 | `rsi`, `stoch`, `stoch_k`, `stoch_d` |
|
||||
| `volatility` | 波动率 | `bollinger`, `bollinger_upper`, `bollinger_mid`, `bollinger_lower`, `atr` |
|
||||
| `volume` | 成交量 | `obv`, `vwap` |
|
||||
|
||||
所有函数返回与输入等长的 `list[float]`,不足周期的位置填充为 `0.0`。
|
||||
|
||||
## 运行示例
|
||||
|
||||
```bash
|
||||
cd engine
|
||||
source .venv/bin/activate
|
||||
python example/backtest_demo.py
|
||||
```
|
||||
Reference in New Issue
Block a user