添加详细说明文档

This commit is contained in:
2026-07-07 20:25:32 +00:00
parent 2c53efb4f3
commit de5893de3b
+763
View File
@@ -0,0 +1,763 @@
# QSForex 详细说明文档
---
## 一、项目简介
**QSForex** 是一个开源的、基于事件驱动架构的外汇(Forex)量化交易系统,支持**回测**和**实盘交易**。项目由 QuantStart.com 创始人 Michael Halls-Moore 于 2015 年开发,采用 MIT 开源许可证,目前处于 "alpha" 阶段。
### 核心特性
| 特性 | 说明 |
|------|------|
| 事件驱动架构 | 回测与实盘使用同一套事件接口,策略无需修改即可从回测切换到实盘 |
| Tick 级精度 | 支持逐 tick 级别的多日、多货币对回测 |
| 交易成本 | 回测中默认包含点差(Spread)成本 |
| 多货币对 | 支持同时交易多个货币对 |
| 实盘交易 | 通过 OANDA Brokerage API 进行实盘交易 |
| 绩效分析 | 支持权益曲线、收益率、最大回撤等指标计算与可视化 |
| 单元测试 | 核心计算逻辑有完整的单元测试覆盖 |
---
## 二、项目目录结构
```
qsforex/
├── __init__.py # 顶层包入口
├── settings.py # 全局配置(OANDA 凭证、数据路径、杠杆等)
├── logging.conf # 日志配置
├── requirements.txt # Python 依赖包列表
├── event/ # ====== 事件系统(核心通信机制) ======
│ ├── __init__.py
│ └── event.py # 定义 TickEvent / SignalEvent / OrderEvent
├── data/ # ====== 数据层 ======
│ ├── __init__.py
│ ├── price.py # 历史 CSV 数据读取器(回测用)
│ └── streaming.py # OANDA Streaming API 实时数据流(实盘用)
├── strategy/ # ====== 策略层 ======
│ ├── __init__.py
│ └── strategy.py # TestStrategy + MovingAverageCrossStrategy
├── portfolio/ # ====== 组合与仓位管理 ======
│ ├── __init__.py
│ ├── portfolio.py # 资金管理、风险控制、信号执行
│ ├── position.py # 单个仓位的 P&L 计算
│ ├── portfolio_test.py # 组合模块单元测试
│ └── position_test.py # 仓位模块单元测试
├── execution/ # ====== 执行层 ======
│ ├── __init__.py
│ └── execution.py # SimulatedExecution + OANDAExecutionHandler
├── backtest/ # ====== 回测引擎 ======
│ ├── __init__.py
│ ├── backtest.py # Backtest 主循环
│ └── output.py # 可视化输出(权益曲线、回撤图)
├── performance/ # ====== 绩效计算 ======
│ ├── __init__.py
│ └── performance.py # 最大回撤(MDD)计算
├── trading/ # ====== 实盘交易 ======
│ ├── __init__.py
│ └── trading.py # 多线程实时交易循环
├── examples/ # ====== 示例 ======
│ ├── __init__.py
│ └── mac.py # 移动平均线交叉策略回测示例
└── scripts/ # ====== 辅助脚本 ======
├── generate_simulated_pair.py # 生成模拟外汇 tick 数据
└── test_performance.py # 绩效计算调试脚本
```
---
## 三、事件驱动架构详解
### 3.1 概述
整个系统围绕一个**事件队列(Event Queue)** 运转。事件是系统中各组件之间通信的唯一方式。这种设计使得各模块高度解耦,策略代码在回测和实盘之间无需修改即可复用。
### 3.2 事件类型
系统定义了三种事件,按处理顺序依次流转:
```
数据源 → TickEvent → 策略 → SignalEvent → 组合 → OrderEvent → 执行引擎
```
#### TickEvent(行情事件)
由数据层产生,包含市场报价信息。
```python
class TickEvent(Event):
def __init__(self, instrument, time, bid, ask):
self.type = 'TICK'
self.instrument = instrument # 货币对,如 "GBPUSD"
self.time = time # 时间戳
self.bid = bid # 买价
self.ask = ask # 卖价
```
#### SignalEvent(信号事件)
由策略层产生,表示一个交易决策。
```python
class SignalEvent(Event):
def __init__(self, instrument, order_type, side, time):
self.type = 'SIGNAL'
self.instrument = instrument # 货币对
self.order_type = order_type # 订单类型(当前仅支持 "market"
self.side = side # 方向:"buy" 或 "sell"
self.time = time # 产生信号的 tick 时间
```
#### OrderEvent(订单事件)
由组合层产生,表示一个需要发送到经纪商的实际订单。
```python
class OrderEvent(Event):
def __init__(self, instrument, units, order_type, side):
self.type = 'ORDER'
self.instrument = instrument # 货币对
self.units = units # 交易数量
self.order_type = order_type # 订单类型
self.side = side # 方向
```
### 3.3 事件流向图
```
┌──────────────┐
│ 数据源 │
│ (CSV/OANDA) │
└──────┬───────┘
│ TickEvent
┌───────────────────────┐
│ 事件队列 (Queue) │
└───────┬───────────────┘
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 策略 │ │ 组合 │ │ 执行 │
│ (Strategy)│ │(Portfolio)│ │(Execution)│
└─────┬────┘ └─────┬────┘ └──────────┘
│ │
│ SignalEvent │ OrderEvent
└─────────────┘
(放回事件队列)
```
---
## 四、各模块详细说明
### 4.1 数据层(data/
#### 4.1.1 PriceHandler(抽象基类)
[price.py](file:///c:/Users/Administrator/Desktop/qsforex/data/price.py#L15-L76)
所有数据处理器继承自 `PriceHandler`,提供以下核心功能:
- **`_set_up_prices_dict()`**:构建价格字典,同时维护正向和反向汇率对。例如,当交易 `GBPUSD` 时,系统自动计算并维护 `USDGBP` 的 bid/ask,用于跨货币对的盈亏换算。
- **`invert_prices()`**:将 bid/ask 价格取倒数,生成反向汇率对。
#### 4.1.2 HistoricCSVPriceHandler(历史数据处理器)
[price.py](file:///c:/Users/Administrator/Desktop/qsforex/data/price.py#L77-L202)
用于回测场景,从 CSV 文件读取历史 tick 数据。
**数据格式**:兼容 DukasCopy Historical Data Feed 格式,文件名格式为 `{货币对}_{YYYYMMDD}.csv`,例如 `GBPUSD_20140112.csv`
CSV 文件内部格式:
```
Time,Ask,Bid,AskVolume,BidVolume
12.01.2014 22:00:10.123,1.50200,1.50180,1.50,1.50
```
**处理流程**
1. 扫描 `CSV_DATA_DIR` 目录下所有符合格式的 CSV 文件
2. 提取所有日期,去重排序
3. 按天加载数据,使用 Pandas 将多个货币对的数据按时间合并排序
4. `stream_next_tick()` 方法每次被调用时,按时间顺序输出下一个 tick
5. 每输出一个 tick,同时更新正向和反向汇率对的价格
#### 4.1.3 StreamingForexPrices(实时数据流处理器)
[streaming.py](file:///c:/Users/Administrator/Desktop/qsforex/data/streaming.py#L16-L91)
用于实盘交易场景,通过 OANDA 的 Streaming API 获取实时价格。
**处理流程**
1. 建立到 OANDA streaming 端点的 HTTPS 连接
2. 使用 `requests.Session` 的流式传输持续接收数据
3. 逐行解析 JSON 格式的 tick 数据
4. 生成 `TickEvent` 放入事件队列
5. 同步更新正向和反向汇率对价格
---
### 4.2 策略层(strategy/
[strategy.py](file:///c:/Users/Administrator/Desktop/qsforex/strategy/strategy.py)
策略层负责从 `TickEvent` 中分析市场数据,产生交易信号。所有策略必须实现 `calculate_signals(event)` 方法。
#### 4.2.1 TestStrategy(测试策略)
简单的测试策略,每 5 个 tick 交替买入/卖出。设计目的是持续"穿越点差",因此必然亏损,仅用于验证系统运行是否正常。
```
逻辑:
tick 0-4: 无操作
tick 5: BUY → 持仓
tick 10: SELL → 平仓
tick 15: BUY → 持仓
...循环
```
#### 4.2.2 MovingAverageCrossStrategy(双均线交叉策略)
经典的移动平均线交叉策略,仅做多。
**参数**
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `short_window` | 500 | 短期 SMA 窗口(tick 数) |
| `long_window` | 2000 | 长期 SMA 窗口(tick 数) |
**信号逻辑**
- 短期 SMA 上穿长期 SMA 且未持仓 → 产生 `BUY` 信号(开多仓)
- 短期 SMA 下穿长期 SMA 且已持仓 → 产生 `SELL` 信号(平多仓)
**性能优化**:使用**滚动 SMA 算法**,避免每次重新计算完整均线:
```python
def calc_rolling_sma(self, sma_m_1, window, price):
return ((sma_m_1 * (window - 1)) + price) / window
```
该算法将时间复杂度从 O(n) 降为 O(1)。
**多货币对支持**:通过 `pairs_dict` 为每个货币对维护独立的 tick 计数、持仓状态和 SMA 值。
---
### 4.3 组合与仓位管理(portfolio/
#### 4.3.1 Position(仓位类)
[position.py](file:///c:/Users/Administrator/Desktop/qsforex/portfolio/position.py#L5-L108)
管理单个货币对的交易仓位,支持多空双向。
**核心属性**
| 属性 | 说明 |
|------|------|
| `home_currency` | 账户计价货币(如 GBP |
| `position_type` | 仓位方向:`"long"``"short"` |
| `currency_pair` | 交易货币对(如 EURUSD |
| `units` | 持仓数量 |
| `avg_price` | 平均持仓成本 |
| `cur_price` | 当前市场价格 |
| `profit_base` | 以计价货币计算的浮动盈亏 |
| `profit_perc` | 浮动盈亏百分比 |
**关键方法**
| 方法 | 说明 |
|------|------|
| `calculate_pips()` | 计算当前盈亏点数 |
| `calculate_profit_base()` | 计算以账户货币计价的盈亏金额 |
| `update_position_price()` | 更新当前价格并重新计算盈亏 |
| `add_units(units)` | 加仓,重新计算平均成本 |
| `remove_units(units)` | 减仓,返回已实现盈亏 |
| `close_position()` | 平仓,返回已实现盈亏 |
**跨货币对换算**:对于 EUR/USD 仓位,账户以 GBP 计价时的盈亏计算:
```
Profit_GBP = Pips × USD/GBP_Rate × Units
```
#### 4.3.2 Portfolio(组合类)
[portfolio.py](file:///c:/Users/Administrator/Desktop/qsforex/portfolio/portfolio.py#L20-L193)
管理整个交易组合,负责资金管理、风险控制和信号执行。
**核心属性**
| 属性 | 默认值 | 说明 |
|------|--------|------|
| `equity` | 100,000.00 | 账户总权益 |
| `balance` | = equity | 当前余额 |
| `leverage` | 20 | 杠杆倍数 |
| `risk_per_trade` | 0.02 (2%) | 单笔交易风险比例 |
| `trade_units` | equity × 0.02 | 单笔交易头寸规模 |
**关键方法**
| 方法 | 说明 |
|------|------|
| `calc_risk_position_size()` | 计算基于风险的仓位大小 |
| `add_new_position()` | 创建新仓位 |
| `add_position_units()` | 对已有仓位加仓 |
| `remove_position_units()` | 对已有仓位减仓 |
| `close_position()` | 平掉整个仓位 |
| `update_portfolio(tick_event)` | 更新所有持仓的浮动盈亏 |
| `execute_signal(signal_event)` | 执行交易信号,生成 OrderEvent |
| `output_results()` | 输出回测结果到 CSV |
| `create_equity_file()` | 创建回测结果 CSV 文件 |
**信号执行逻辑** [execute_signal](file:///c:/Users/Administrator/Desktop/qsforex/portfolio/portfolio.py#L135-L192)
| 信号方向 | 当前仓位 | 动作 |
|---------|---------|------|
| `buy` | 无仓位 | 创建多头仓位 |
| `buy` | 有多头仓位 | 加仓 |
| `buy` | 有空头仓位 | 若 units 相等则平仓 |
| `sell` | 无仓位 | 创建空头仓位 |
| `sell` | 有多头仓位 | 若 units 相等则平仓 |
| `sell` | 有空头仓位 | 加仓 |
---
### 4.4 执行层(execution/
[execution.py](file:///c:/Users/Administrator/Desktop/qsforex/execution/execution.py)
#### 4.4.1 ExecutionHandler(抽象基类)
定义了 `execute_order()` 接口,所有执行处理器必须实现此方法。
#### 4.4.2 SimulatedExecution(模拟执行)
回测模式下的执行器,`execute_order()` 方法为空操作。实际的成交处理由 `Portfolio` 对象直接完成。
#### 4.4.3 OANDAExecutionHandlerOANDA 执行器)
实盘模式下的执行器,通过 OANDA REST API 发送订单。
**下单流程**
1. 建立到 OANDA API 域名的 HTTPS 连接
2. 构造 HTTP POST 请求,包含认证信息和订单参数
3. 发送到 `/v1/accounts/{account_id}/orders` 端点
4. 记录 API 响应日志
---
### 4.5 回测引擎(backtest/
#### 4.5.1 Backtest 类
[backtest.py](file:///c:/Users/Administrator/Desktop/qsforex/backtest/backtest.py#L12-L82)
回测引擎的核心类,将所有组件组装在一起运行回测。
**初始化参数**
| 参数 | 说明 |
|------|------|
| `pairs` | 交易货币对列表 |
| `data_handler` | 数据处理器类(如 `HistoricCSVPriceHandler` |
| `strategy` | 策略类(如 `MovingAverageCrossStrategy` |
| `strategy_params` | 策略参数字典 |
| `portfolio` | 组合管理类 |
| `execution` | 执行处理器类 |
| `equity` | 初始资金 |
| `heartbeat` | 事件循环心跳间隔(秒) |
| `max_iters` | 最大迭代次数 |
**回测主循环** `_run_backtest()`
```python
while iters < max_iters and ticker.continue_backtest:
try:
event = events.get(False) # 非阻塞获取事件
except queue.Empty:
ticker.stream_next_tick() # 没有事件则推进下一个 tick
else:
if event.type == 'TICK':
strategy.calculate_signals(event) # 策略分析
portfolio.update_portfolio(event) # 更新盈亏
elif event.type == 'SIGNAL':
portfolio.execute_signal(event) # 执行信号
elif event.type == 'ORDER':
execution.execute_order(event) # 执行订单
time.sleep(heartbeat)
iters += 1
```
**关键设计**:当事件队列为空时,主动从数据源获取下一个 tick 并放入队列,而非等待。这种"拉取"模式确保回测的确定性和可复现性。
#### 4.5.2 可视化输出
[output.py](file:///c:/Users/Administrator/Desktop/qsforex/backtest/output.py)
读取 `equity.csv` 文件,使用 Matplotlib + Seaborn 绘制三张图表:
1. **权益曲线**(上图):组合价值随时间变化
2. **期间收益率**(中图):每个 tick 的收益率
3. **回撤曲线**(下图):从历史高点的回撤幅度
---
### 4.6 绩效计算(performance/
[performance.py](file:///c:/Users/Administrator/Desktop/qsforex/performance/performance.py)
`create_drawdowns(pnl)` 函数:
- 计算权益曲线的**峰值到谷值的最大回撤(Maximum Drawdown, MDD**
- 计算**回撤持续时间**
- 返回 `(drawdown_series, max_drawdown, max_duration)`
---
### 4.7 实盘交易(trading/
[trading.py](file:///c:/Users/Administrator/Desktop/qsforex/trading/trading.py)
实盘交易的入口,使用**双线程**架构:
```
线程 1(价格流): StreamingForexPrices.stream_to_queue()
└── 持续从 OANDA 接收实时价格,生成 TickEvent 放入队列
线程 2(交易循环): trade()
└── 从队列获取事件 → 策略 → 组合 → 执行
```
---
## 五、配置文件说明
### 5.1 settings.py
```python
# OANDA 环境配置
ENVIRONMENTS = {
"streaming": {
"real": "stream-fxtrade.oanda.com",
"practice": "stream-fxpractice.oanda.com",
"sandbox": "stream-sandbox.oanda.com"
},
"api": {
"real": "api-fxtrade.oanda.com",
"practice": "api-fxpractice.oanda.com",
"sandbox": "api-sandbox.oanda.com"
}
}
# 数据目录和输出目录(从环境变量读取)
CSV_DATA_DIR = os.environ.get('QSFOREX_CSV_DATA_DIR', None)
OUTPUT_RESULTS_DIR = os.environ.get('QSFOREX_OUTPUT_RESULTS_DIR', None)
# 交易环境选择
DOMAIN = "practice" # "practice" / "real" / "sandbox"
# OANDA 认证
ACCESS_TOKEN = os.environ.get('OANDA_API_ACCESS_TOKEN', None)
ACCOUNT_ID = os.environ.get('OANDA_API_ACCOUNT_ID', None)
# 账户设置
BASE_CURRENCY = "GBP" # 账户基础货币
EQUITY = Decimal("100000.00") # 初始资金
```
### 5.2 logging.conf
[logging.conf](file:///c:/Users/Administrator/Desktop/qsforex/logging.conf)
配置了 root logger 和 `qsforex.trading.trading` logger,均输出到控制台,级别为 DEBUG。
---
## 六、使用流程
### 6.1 回测流程
```
步骤 1: 生成模拟数据
└── python scripts/generate_simulated_pair.py GBPUSD
步骤 2: 运行回测
└── python examples/mac.py
步骤 3: 查看结果
└── python backtest/output.py
```
### 6.2 实盘交易流程
```
步骤 1: 配置 OANDA 凭证(settings.py
步骤 2: 编写策略(继承策略基类)
步骤 3: 启动交易
└── python trading/trading.py
```
---
## 七、技术栈
| 类别 | 依赖包 | 版本 |
|------|--------|------|
| 数值计算 | numpy | 1.9.2 |
| 科学计算 | scipy | 0.15.1 |
| 数据处理 | pandas | 0.16.1 |
| 可视化 | matplotlib | 1.4.3 |
| 统计可视化 | seaborn | 0.5.1 |
| 机器学习 | scikit-learn | 0.16.1 |
| HTTP 请求 | requests | 2.7.0 |
| 测试框架 | nose | 1.3.6 |
| Mock | mock | 1.0.1 |
| 交互环境 | ipython | 3.1.0 |
---
## 八、已知问题与改进建议
### 8.1 兼容性问题
| 问题 | 严重程度 | 说明 |
|------|---------|------|
| Python 2.7 语法 | 高 | 使用 `from __future__``try: import Queue``__metaclass__` 等旧式写法,无法在 Python 3 上直接运行 |
| 依赖版本过旧 | 高 | pandas 0.16、numpy 1.9 发布于 2015 年,存在安全漏洞 |
| OANDA API 版本 | 高 | 使用 OANDA v1 API,该版本已被废弃,需迁移到 v3 REST API |
### 8.2 代码缺陷
| 问题 | 位置 | 说明 |
|------|------|------|
| 缺少 `self.` 前缀 | [portfolio.py L165](file:///c:/Users/Administrator/Desktop/qsforex/portfolio/portfolio.py#L165) | `add_position_units` 应为 `self.add_position_units`,会导致 `NameError` |
| 无 setup.py | 根目录 | 缺少标准的 Python 包安装配置,无法通过 `pip install` 安装 |
| 硬编码 | [generate_simulated_pair.py](file:///c:/Users/Administrator/Desktop/qsforex/scripts/generate_simulated_pair.py) | 模拟数据生成的日期、初始价格等参数硬编码 |
### 8.3 改进建议
1. **Python 3 迁移**:将所有语法升级到 Python 3.6+,移除 `from __future__` 导入
2. **依赖更新**:升级到最新版本的 pandas、numpy 等核心依赖
3. **API 升级**:将 OANDA API 从 v1 迁移到 v3
4. **包管理现代化**:添加 `setup.py``pyproject.toml`
5. **Bug 修复**:修复 portfolio.py 中缺失 `self.` 的问题
6. **配置外部化**:将模拟数据生成参数从硬编码改为配置文件或命令行参数
---
## 九、扩展指南
### 9.1 编写自定义策略
只需继承或模仿现有策略,实现 `calculate_signals(event)` 方法:
```python
class MyCustomStrategy(object):
def __init__(self, pairs, events):
self.pairs = pairs
self.events = events
# 初始化你的策略状态
def calculate_signals(self, event):
if event.type == 'TICK':
# 你的策略逻辑
# 当满足条件时,生成 SignalEvent
signal = SignalEvent(pair, "market", "buy", event.time)
self.events.put(signal)
```
然后在回测或实盘入口中替换策略类即可。
### 9.2 添加新的数据源
继承 `PriceHandler` 基类,实现数据获取和 `stream_next_tick()` 方法。
---
## 十、许可证
MIT License - Copyright (c) 2015 Michael Halls-Moore
---
> **风险提示**:外汇保证金交易具有高风险,可能不适合所有投资者。过去的表现不代表未来的结果。高杠杆可能对您不利也可能对您有利。在决定投资外汇之前,您应仔细考虑您的投资目标、经验水平和风险承受能力。
---
## 十一、可复用模块分析 —— 多项目整合视角
> 本章节专门为**综合多个项目构建自有交易系统**的开发者编写,帮助你快速识别 QSForex 中最值得借鉴和复用的模块,以及它在多项目组合中的定位。
### 11.1 本项目在量化交易系统中的定位
QSForex 的核心价值在于**轻量级、教学友好、架构清晰**。它不是一个功能完备的生产级系统,而是一个**架构原型**——用最少的代码完整展示了事件驱动交易系统的骨架。在多项目整合中,QSForex 适合作为:
| 角色 | 说明 |
|------|------|
| **架构参考蓝图** | 事件驱动模式是量化交易系统的主流范式,本项目的实现简洁易懂 |
| **快速原型验证** | 当你有一个新策略想法时,可以最快速度搭出回测环境 |
| **教学/入门材料** | 代码量小、注释清晰,适合团队新成员快速理解量化系统全貌 |
---
### 11.2 模块可复用性矩阵
以下按照**可复用价值从高到低**排列,并标注每个模块在多项目整合中的建议处理方式。
#### ⭐⭐⭐⭐⭐ 强烈推荐复用/借鉴
| 模块 | 核心价值 | 可复用部分 | 跨市场适用性 |
|------|---------|-----------|-------------|
| **事件驱动架构** | ⭐⭐⭐⭐⭐ | 整个事件队列 + 事件类型设计 | 股票/期货/加密货币均可 |
| **回测引擎** | ⭐⭐⭐⭐⭐ | `Backtest._run_backtest()` 主循环 | 通用,只需替换数据源 |
| **仓位管理** | ⭐⭐⭐⭐⭐ | `Position` 类的盈亏计算逻辑 | 外汇特有,但多空双向模式通用 |
| **滚动 SMA 算法** | ⭐⭐⭐⭐ | `calc_rolling_sma()` 高效增量计算 | 所有时间序列策略通用 |
#### 详细分析
**① 事件驱动架构(最核心的复用价值)**
```
事件类型设计 → 事件队列 → 事件分发循环
```
这是 QSForex 的灵魂。无论你最终使用什么市场、什么语言、什么数据库,这个**三层解耦**的模式是量化交易系统的事实标准:
```
数据层 ──TickEvent──→ 策略层 ──SignalEvent──→ 组合层 ──OrderEvent──→ 执行层
↑ ↑ ↑ ↑
可替换 可替换 可替换 可替换
```
**迁移建议**
- 保留事件类型定义(TickEvent / SignalEvent / OrderEvent),按需扩展(如增加 `FillEvent``RiskEvent`
- 保留事件队列 + `while` 循环分发的模式
- 替换各层具体实现即可适配任何市场
**② 回测引擎主循环**
[backtest.py](file:///c:/Users/Administrator/Desktop/qsforex/backtest/backtest.py#L41-L64) 中的 `_run_backtest()` 方法是经典的回测主循环模式:
```python
while iters < max_iters and ticker.continue_backtest:
try:
event = events.get(False) # 非阻塞取事件
except queue.Empty:
ticker.stream_next_tick() # 无事件时推进数据
else:
if event.type == 'TICK':
strategy.calculate_signals(event)
portfolio.update_portfolio(event)
elif event.type == 'SIGNAL':
portfolio.execute_signal(event)
elif event.type == 'ORDER':
execution.execute_order(event)
```
**迁移建议**
- 这个循环结构可以直接移植到任何市场(股票、期货、加密货币)
- 只需实现对应市场的 `PriceHandler` 子类即可
- 可以扩展为支持 `BarEvent`K线事件)以适配日线级别策略
**③ 仓位盈亏计算**
[position.py](file:///c:/Users/Administrator/Desktop/qsforex/portfolio/position.py) 中的 `Position` 类实现了完整的:
- 多空双向持仓
- 平均成本计算(加仓/减仓时重新计算)
- 已实现/未实现盈亏分离
- 跨货币换算
**迁移建议**
- 外汇特有的汇率换算部分可以剥离,核心的 `avg_price` 加权平均算法是通用的
- 期货/股票场景下,需增加保证金计算、合约乘数等字段
- 加密货币场景几乎可以直接复用
**④ 滚动 SMA 增量算法**
[strategy.py](file:///c:/Users/Administrator/Desktop/qsforex/strategy/strategy.py#L69-L70) 中的高效均线计算:
```python
def calc_rolling_sma(self, sma_m_1, window, price):
return ((sma_m_1 * (window - 1)) + price) / window
```
这个算法将 SMA 计算从 O(n) 优化到 O(1),在高频回测中效果显著。
---
#### ⭐⭐⭐ 可借鉴思路,需重新实现
| 模块 | 核心价值 | 需改造的原因 |
|------|---------|-------------|
| **数据层** | CSV 多文件按天加载、多货币对按时间归并 | 格式绑定 DukasCopy,且 Python 2.7 的 pandas 语法已过时 |
| **信号执行逻辑** | buy/sell 与多空仓位的状态机 | 逻辑有 Bug(缺少 `self.`),且不支持部分平仓 |
| **绩效计算** | 最大回撤计算 | 实现过于简单,现代系统需要 Sharpe/Sortino/Calmar 等 |
| **可视化输出** | 三图布局(权益+收益+回撤) | matplotlib 版本过旧,可用 plotly/bokeh 替换 |
---
#### ⭐⭐ 参考价值有限,建议替换
| 模块 | 原因 |
|------|------|
| **OANDA 执行器** | 使用已废弃的 OANDA v1 API,必须用 v3 重写或换经纪商 |
| **实时数据流** | 同上,API 版本过时 |
| **模拟数据生成** | 仅生成随机游走数据,无市场微观结构,建议用专业回测数据源 |
| **日志配置** | 可用 structlog 或 loguru 替代 |
---
### 11.3 多项目整合的推荐架构
如果你正在综合多个项目构建自己的交易系统,推荐的分层架构如下:
```
┌─────────────────────────────────────────────────────┐
│ 策略层 (Strategy) │
│ 从 QSForex 借鉴:事件接口 + calculate_signals │
│ 从其他项目借鉴:因子计算、ML模型、信号组合 │
├─────────────────────────────────────────────────────┤
│ 组合层 (Portfolio) │
│ 从 QSForex 借鉴:多空仓位管理 + 风险头寸计算 │
│ 从其他项目借鉴:凯利公式、VaR、组合优化 │
├─────────────────────────────────────────────────────┤
│ 执行层 (Execution) │
│ 从 QSForex 借鉴:SimulatedExecution(回测模式) │
│ 从其他项目借鉴:多经纪商适配、订单路由、智能下单 │
├─────────────────────────────────────────────────────┤
│ 数据层 (Data) │
│ 从 QSForex 借鉴:多标的按时间归并的思想 │
│ 从其他项目借鉴:实时数据库、数据清洗、复权处理 │
├─────────────────────────────────────────────────────┤
│ 事件总线 (Event Bus) │
│ 从 QSForex 借鉴:Events Queue + 事件类型体系 ★★★ │
│ 从其他项目借鉴:Redis/Kafka 分布式事件总线 │
└─────────────────────────────────────────────────────┘
```
---
### 11.4 QSForex 在多项目组合中的角色总结
| 维度 | QSForex 的定位 |
|------|---------------|
| **架构教学** | 🥇 最佳入门教材——事件驱动模式的最简实现 |
| **回测引擎** | 🥈 可作为快速原型验证工具 |
| **策略开发** | 🥈 内置策略简单,但策略接口设计值得参考 |
| **生产就绪度** | ❌ 不适合直接用于生产环境 |
| **跨市场支持** | ❌ 仅外汇,需大量改造才能支持股票/期货 |
| **代码质量** | ⚠️ 有已知 Bug,需要修复后再复用 |
**推荐的复用策略**
1. **提取事件驱动骨架**:将 `event/` + `backtest/` 的核心循环提取为通用框架
2. **修复并提取仓位管理**:修复 `portfolio.py` 的 Bug,抽象出通用的 `Position` 基类
3. **保留策略接口规范**:统一 `calculate_signals(event)` 签名作为所有策略的入口规范
4. **替换其他所有模块**:数据源、执行器、绩效分析、可视化全部用现代方案重新实现
> **一句话总结**QSForex 的**事件驱动架构 + 回测主循环 + 仓位管理**是最值得带走的核心资产,其余模块建议用其他项目的更成熟实现替代。