hqbacktest 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hqbacktest-0.1.4/LICENSE +21 -0
- hqbacktest-0.1.4/PKG-INFO +202 -0
- hqbacktest-0.1.4/README.md +165 -0
- hqbacktest-0.1.4/pyproject.toml +120 -0
- hqbacktest-0.1.4/setup.cfg +4 -0
- hqbacktest-0.1.4/src/hqbacktest/__init__.py +104 -0
- hqbacktest-0.1.4/src/hqbacktest/__main__.py +13 -0
- hqbacktest-0.1.4/src/hqbacktest/cli/__init__.py +7 -0
- hqbacktest-0.1.4/src/hqbacktest/cli/__main__.py +109 -0
- hqbacktest-0.1.4/src/hqbacktest/cli/config.py +380 -0
- hqbacktest-0.1.4/src/hqbacktest/cli/runner.py +347 -0
- hqbacktest-0.1.4/src/hqbacktest/data/__init__.py +56 -0
- hqbacktest-0.1.4/src/hqbacktest/data/_converters.py +88 -0
- hqbacktest-0.1.4/src/hqbacktest/data/cache.py +57 -0
- hqbacktest-0.1.4/src/hqbacktest/data/data_view.py +308 -0
- hqbacktest-0.1.4/src/hqbacktest/data/errors.py +84 -0
- hqbacktest-0.1.4/src/hqbacktest/data/hqdata_portal.py +548 -0
- hqbacktest-0.1.4/src/hqbacktest/data/memory_portal.py +246 -0
- hqbacktest-0.1.4/src/hqbacktest/data/portal.py +70 -0
- hqbacktest-0.1.4/src/hqbacktest/data/validators.py +109 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/__init__.py +85 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/adjustment.py +60 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/bar.py +73 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/enums.py +115 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/errors.py +16 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/fill.py +130 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/money.py +79 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/order.py +210 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/portfolio.py +155 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/position.py +124 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/serialization.py +50 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/snapshot.py +112 -0
- hqbacktest-0.1.4/src/hqbacktest/domain/state_machine.py +64 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/__init__.py +128 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/broker.py +248 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/config.py +105 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/context.py +626 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/corporate_actions.py +294 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/cost_model.py +70 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/engine.py +789 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/errors.py +63 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/events.py +65 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/intents.py +118 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/iterator.py +67 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/metrics.py +286 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/result.py +312 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/rule_set.py +261 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/scheduler.py +158 -0
- hqbacktest-0.1.4/src/hqbacktest/engine/strategy.py +116 -0
- hqbacktest-0.1.4/src/hqbacktest.egg-info/PKG-INFO +202 -0
- hqbacktest-0.1.4/src/hqbacktest.egg-info/SOURCES.txt +54 -0
- hqbacktest-0.1.4/src/hqbacktest.egg-info/dependency_links.txt +1 -0
- hqbacktest-0.1.4/src/hqbacktest.egg-info/entry_points.txt +2 -0
- hqbacktest-0.1.4/src/hqbacktest.egg-info/requires.txt +13 -0
- hqbacktest-0.1.4/src/hqbacktest.egg-info/top_level.txt +1 -0
- hqbacktest-0.1.4/tests/test_package.py +137 -0
hqbacktest-0.1.4/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HonestQuant
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hqbacktest
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: A股量化策略回测与交易模拟引擎
|
|
5
|
+
Author-email: HowieMen <howiemen@honestquant.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://honestquant.com/hqbacktest
|
|
8
|
+
Project-URL: Source, https://github.com/HonestQuantTech/hqbacktest
|
|
9
|
+
Keywords: hqbacktest,quant,quantitative,backtest,investment,algotrading,A股,trading-simulation
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
14
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Requires-Dist: pandas>=2.0.0
|
|
27
|
+
Requires-Dist: tomli>=2.0
|
|
28
|
+
Requires-Dist: hqdata>=0.1.22
|
|
29
|
+
Provides-Extra: tushare
|
|
30
|
+
Provides-Extra: ricequant
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
34
|
+
Requires-Dist: black>=25.1.0; extra == "dev"
|
|
35
|
+
Requires-Dist: build>=1.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# hqbacktest - A股量化策略回测与交易模拟引擎
|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
<img src="https://img.shields.io/pypi/v/hqbacktest.svg"/>
|
|
42
|
+
<img src="https://img.shields.io/pypi/pyversions/hqbacktest.svg"/>
|
|
43
|
+
<img src="https://img.shields.io/badge/hqdata-%3E%3D0.1.22-blue"/>
|
|
44
|
+
<img src="https://img.shields.io/badge/python-%3E%3D3.10-blue"/>
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
`hqbacktest` 是 HonestQuant 量化系统的**策略回测与交易模拟层**,面向 A 股日线策略。它给量化研究者一个**确定性的、可复现的、与实盘严格隔离**的回测沙盒:策略只通过受控的 `Context` / `DataView` 读写数据、提交订单和查询组合,不接触数据源实现或内部账本;引擎负责时钟、撮合、规则与成本、指标和可审计的结果导出。
|
|
48
|
+
|
|
49
|
+
## 定位
|
|
50
|
+
|
|
51
|
+
- **对下:** 通过 `hqdata.api` 的 `csv` source 读取 `hqdata` CLI 已落盘的 CSV 快照;不调用任何数据源 SDK、也不在回测运行时访问网络。
|
|
52
|
+
- **对中:** 提供严格的交易日事件时钟、数据可见性控制、订单生命周期、虚拟经纪商、持仓账本和交易规则。
|
|
53
|
+
- **对上:** 让策略只通过 `Context` / `DataView` 读取数据、提交订单和查询组合,不接触数据源实现或修改内部账本。
|
|
54
|
+
- **对外:** 输出可复现的净值、订单、成交、持仓、费用和绩效指标,用于研究和模拟,不连接真实券商。
|
|
55
|
+
|
|
56
|
+
## 支持的数据源
|
|
57
|
+
|
|
58
|
+
| 数据 | 来源 | 适用场景 |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| 真实日线 | `hqdata` CLI 落盘的 CSV 快照(`tushare` / `ricequant`)通过 [`HqDataCsvPortal`](src/hqbacktest/data/hqdata_portal.py) 读取 | 任何需要真实行情的回测 |
|
|
61
|
+
| 内存 fixture | `InMemoryDataPortal` | 单元测试、示例、`tests/examples/` 端到端 fixture |
|
|
62
|
+
|
|
63
|
+
`HqDataCsvPortal` 在构造时把 snapshot 路径传给 `hqdata.init_source("csv", root=...)`,所有 CSV 解析由 `hqdata.sources.csv_source.CsvSource` 负责(列名校验、文件存在性、整日缺失抛 `SnapshotFileMissingError`)。
|
|
64
|
+
|
|
65
|
+
`hqdata` 当前 `akshare` 适配器不稳定,按其官方说明**不**作为本项目首选数据源。需要日线请使用 `tushare` 或 `ricequant`;具体数据下载与落盘见 [`hqdata` README](https://github.com/HonestQuantTech/hqdata)。
|
|
66
|
+
|
|
67
|
+
## 首个可用版本的范围
|
|
68
|
+
|
|
69
|
+
### 已支持
|
|
70
|
+
|
|
71
|
+
- **市场与频率:** 沪深普通股票的日线回测;统一代码 `600000.SH` / `000001.SZ`。
|
|
72
|
+
- **账户:** 单个人民币现金账户、股票现货多头;不使用杠杆或保证金。
|
|
73
|
+
- **数据:** 每次回测固定使用一个 `hqdata` CSV 快照;`data_root` 默认 `~/.hqdata`,`source` 指向其下的数据源目录。
|
|
74
|
+
- **时间:** `YYYYMMDD` 8 位日期;`before_trading_start(D)` 看到 D-1 及以前,可在 D 开盘撮合;`on_bar(D)` 收盘后看到 D 日线,最早 D+1 开盘成交。
|
|
75
|
+
- **成交量单位:** `Bar.volume` = 手(1 手 = 100 股;与 `hqdata` `tushare` 适配器口径一致)。
|
|
76
|
+
- **成交:** 市价单按符合规则的开盘价全额成交;订单、拒绝、费用与成交全程留痕。
|
|
77
|
+
- **复权:** 成交、现金账本和 v0.1 净值使用未复权价格;`adjustment_policy="none"`。
|
|
78
|
+
- **结果:** 每次运行产出净值曲线、订单、成交、每日持仓、成本、配置和运行元数据。
|
|
79
|
+
|
|
80
|
+
### 明确不支持
|
|
81
|
+
|
|
82
|
+
- 实盘交易、券商连接、实时行情和自动下单。
|
|
83
|
+
- 分钟线、Tick、盘中撮合、成交量参与率、限价单和止损单。
|
|
84
|
+
- 融资融券、卖空、期货、期权、多账户、多币种和组合级保证金。
|
|
85
|
+
- 没有可靠证券状态数据支撑的 ST / 涨跌停 / 新股首日 / 北交所细则。
|
|
86
|
+
- 仅由复权因子推断精确的现金分红、送配、配股及税费。
|
|
87
|
+
- 在 `hqdata` 尚未提供指数日线前,把基准收益率作为运行的必需输入。
|
|
88
|
+
|
|
89
|
+
## 安装
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
git clone git@github.com:HonestQuantTech/hqbacktest.git
|
|
93
|
+
cd hqbacktest
|
|
94
|
+
|
|
95
|
+
# 创建虚拟环境
|
|
96
|
+
python -m venv .venv
|
|
97
|
+
source .venv/bin/activate
|
|
98
|
+
|
|
99
|
+
# 装数据层(hqbacktest 只依赖 hqdata 的 csv source;具体数据源 tushare/ricequant 由 hqdata CLI 异步下载落盘)
|
|
100
|
+
pip install -e "../hqdata"
|
|
101
|
+
|
|
102
|
+
# 可编辑安装本项目 + 开发依赖
|
|
103
|
+
pip install -e ".[dev]"
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`pyproject.toml` 声明的 Python 下限为 `>=3.10`(与 `hqdata` 一致)。
|
|
107
|
+
|
|
108
|
+
## 配置数据源
|
|
109
|
+
|
|
110
|
+
`hqbacktest` 不接触任何数据源 token,也不在回测运行时联网。回测侧只声明 `source`(数据源名或绝对路径)与 `data_root`(父目录),`hqbacktest` 内部把它们解析成 hqdata 要求的 `(root, source_name)` 并交给 [`hqdata.init_source("csv", root=..., source_name=...)`](https://github.com/HonestQuantTech/hqdata)。
|
|
111
|
+
|
|
112
|
+
| 写法 | 含义 |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| `data_root="~/.hqdata"`, `source="tushare"` | 解析为 `(~/.hqdata, tushare)`,传给 hqdata 的 `root=~/.hqdata/tushare`、`source_name="tushare"` |
|
|
115
|
+
| `data_root="/mnt/market-data"`, `source="ricequant"` | 解析为 `(/mnt/market-data, ricequant)` |
|
|
116
|
+
| `source="~/.hqdata/tushare"`(绝对路径) | 直接拆分 `(parent_dir, basename)`,忽略 `data_root` |
|
|
117
|
+
|
|
118
|
+
`source` 接受**名称**(搭配 `data_root`)或**绝对路径**(拆分)。底层 CSV 布局由 `hqdata` CLI 在回测前写入;`hqbacktest` 既不下载数据,也不保存凭证。
|
|
119
|
+
|
|
120
|
+
## 使用
|
|
121
|
+
|
|
122
|
+
最小 Python 示例(公共 API,详见 `examples/`):
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
from decimal import Decimal
|
|
126
|
+
from hqbacktest import BacktestConfig, BacktestEngine, BaseStrategy
|
|
127
|
+
from hqbacktest.data import DataView
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class MovingAverageStrategy(BaseStrategy):
|
|
131
|
+
def initialize(self, context):
|
|
132
|
+
context.set_universe(["600000.SH"])
|
|
133
|
+
|
|
134
|
+
def on_bar(self, context, data):
|
|
135
|
+
closes = data.history("600000.SH", field="close", bar_count=5)
|
|
136
|
+
if len(closes) < 5:
|
|
137
|
+
return
|
|
138
|
+
avg = sum(closes) / len(closes)
|
|
139
|
+
if closes[-1] > avg:
|
|
140
|
+
context.order_target_percent("600000.SH", Decimal("0.95"))
|
|
141
|
+
else:
|
|
142
|
+
context.order_target("600000.SH", 0)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
result = BacktestEngine(
|
|
146
|
+
BacktestConfig(start_date="20240102", end_date="20240110", initial_cash="100000", source="tushare"),
|
|
147
|
+
strategy=MovingAverageStrategy(),
|
|
148
|
+
).run()
|
|
149
|
+
result.save("results/moving-average")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
命令行(推荐用于 CI 与可复现实验):
|
|
153
|
+
|
|
154
|
+
`hqbacktest` 不下载数据,命令行示例需要你本地已有一份 `hqdata` CSV 快照。先用 `hqdata` CLI 抓一段真实日线(换成你自己的 tushare/ricequant token 和日期区间):
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
hqdata --source tushare calendar --start 20260123 --end 20260212
|
|
158
|
+
hqdata --source tushare stock-list --start 20260123 --end 20260212
|
|
159
|
+
hqdata --source tushare stock-daily --start 20260123 --end 20260212
|
|
160
|
+
hqdata --source tushare stock-factor --start 20260123 --end 20260212
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
再跑仓库自带的 [`configs/moving_average.toml`](configs/moving_average.toml)(日期区间需要和你抓取的快照区间对上):
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
hqbacktest run --config configs/moving_average.toml --output results/moving-average
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## 示例
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
python examples/buy_and_hold.py
|
|
173
|
+
python examples/moving_average.py
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
两份示例都用 7 天确定性 `InMemoryDataPortal` 数据走通端到端流程,不访问网络、不需要任何凭证。`tests/examples/` 下有 10 项端到端回归测试,覆盖买-持、均线、T+1、费用、净值与指标。
|
|
177
|
+
|
|
178
|
+
## 测试
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
pytest tests/ -v
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
单元测试必须使用内存数据或 mock,不依赖网络和本地行情文件;确需在真实数据上验证 `tushare` / `ricequant` 适配的集成测试必须在 `~/.hqdata/{name}` 不存在或不可读时自动跳过。
|
|
185
|
+
|
|
186
|
+
## 文档导览
|
|
187
|
+
|
|
188
|
+
| 文档 | 内容 |
|
|
189
|
+
| --- | --- |
|
|
190
|
+
| [`docs/design/mvp-contract.md`](docs/design/mvp-contract.md) | v0.1 产品契约:术语、模块边界、日事件顺序、不可变规则、非目标 |
|
|
191
|
+
| [`docs/cli.md`](docs/cli.md) | `hqbacktest run` 详细配置 schema、输出目录、错误码、复现性 |
|
|
192
|
+
| [`docs/output.md`](docs/output.md) | `BacktestResult.save(dir)` 输出文件结构与 `PerformanceMetrics` 字段含义 |
|
|
193
|
+
| [`docs/strategy-api.md`](docs/strategy-api.md) | 策略回调与下单时点、`Context` / `DataView` 可见性矩阵 |
|
|
194
|
+
| [`docs/matching.md`](docs/matching.md) | 撮合顺序、整手 / 零股、费用量化、`realized_pnl` 口径 |
|
|
195
|
+
| [`docs/metrics.md`](docs/metrics.md) | 首日 P&L、波动率样本、幂运算桥接、metrics 输出约定 |
|
|
196
|
+
| [`docs/isolation.md`](docs/isolation.md) | `Order` 不可变、`DataView` 私有 portal、universe 生效、历史股票池 |
|
|
197
|
+
| [`docs/factor-diagnostics.md`](docs/factor-diagnostics.md) | 因子诊断接入、分红偏差显性化、CLI 警告 |
|
|
198
|
+
| [`docs/performance.md`](docs/performance.md) | 双层缓存、真实数据基准、性能冒烟测试 |
|
|
199
|
+
|
|
200
|
+
## 免责声明
|
|
201
|
+
|
|
202
|
+
`hqbacktest` 面向研究、教育和历史模拟。回测结果依赖数据质量、交易规则、成本模型、公司行为处理和策略假设,不能代表真实可实现收益,也不构成任何投资、交易或风险管理建议。项目在明确实现实盘能力前不会连接券商或执行真实委托。
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# hqbacktest - A股量化策略回测与交易模拟引擎
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://img.shields.io/pypi/v/hqbacktest.svg"/>
|
|
5
|
+
<img src="https://img.shields.io/pypi/pyversions/hqbacktest.svg"/>
|
|
6
|
+
<img src="https://img.shields.io/badge/hqdata-%3E%3D0.1.22-blue"/>
|
|
7
|
+
<img src="https://img.shields.io/badge/python-%3E%3D3.10-blue"/>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
`hqbacktest` 是 HonestQuant 量化系统的**策略回测与交易模拟层**,面向 A 股日线策略。它给量化研究者一个**确定性的、可复现的、与实盘严格隔离**的回测沙盒:策略只通过受控的 `Context` / `DataView` 读写数据、提交订单和查询组合,不接触数据源实现或内部账本;引擎负责时钟、撮合、规则与成本、指标和可审计的结果导出。
|
|
11
|
+
|
|
12
|
+
## 定位
|
|
13
|
+
|
|
14
|
+
- **对下:** 通过 `hqdata.api` 的 `csv` source 读取 `hqdata` CLI 已落盘的 CSV 快照;不调用任何数据源 SDK、也不在回测运行时访问网络。
|
|
15
|
+
- **对中:** 提供严格的交易日事件时钟、数据可见性控制、订单生命周期、虚拟经纪商、持仓账本和交易规则。
|
|
16
|
+
- **对上:** 让策略只通过 `Context` / `DataView` 读取数据、提交订单和查询组合,不接触数据源实现或修改内部账本。
|
|
17
|
+
- **对外:** 输出可复现的净值、订单、成交、持仓、费用和绩效指标,用于研究和模拟,不连接真实券商。
|
|
18
|
+
|
|
19
|
+
## 支持的数据源
|
|
20
|
+
|
|
21
|
+
| 数据 | 来源 | 适用场景 |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| 真实日线 | `hqdata` CLI 落盘的 CSV 快照(`tushare` / `ricequant`)通过 [`HqDataCsvPortal`](src/hqbacktest/data/hqdata_portal.py) 读取 | 任何需要真实行情的回测 |
|
|
24
|
+
| 内存 fixture | `InMemoryDataPortal` | 单元测试、示例、`tests/examples/` 端到端 fixture |
|
|
25
|
+
|
|
26
|
+
`HqDataCsvPortal` 在构造时把 snapshot 路径传给 `hqdata.init_source("csv", root=...)`,所有 CSV 解析由 `hqdata.sources.csv_source.CsvSource` 负责(列名校验、文件存在性、整日缺失抛 `SnapshotFileMissingError`)。
|
|
27
|
+
|
|
28
|
+
`hqdata` 当前 `akshare` 适配器不稳定,按其官方说明**不**作为本项目首选数据源。需要日线请使用 `tushare` 或 `ricequant`;具体数据下载与落盘见 [`hqdata` README](https://github.com/HonestQuantTech/hqdata)。
|
|
29
|
+
|
|
30
|
+
## 首个可用版本的范围
|
|
31
|
+
|
|
32
|
+
### 已支持
|
|
33
|
+
|
|
34
|
+
- **市场与频率:** 沪深普通股票的日线回测;统一代码 `600000.SH` / `000001.SZ`。
|
|
35
|
+
- **账户:** 单个人民币现金账户、股票现货多头;不使用杠杆或保证金。
|
|
36
|
+
- **数据:** 每次回测固定使用一个 `hqdata` CSV 快照;`data_root` 默认 `~/.hqdata`,`source` 指向其下的数据源目录。
|
|
37
|
+
- **时间:** `YYYYMMDD` 8 位日期;`before_trading_start(D)` 看到 D-1 及以前,可在 D 开盘撮合;`on_bar(D)` 收盘后看到 D 日线,最早 D+1 开盘成交。
|
|
38
|
+
- **成交量单位:** `Bar.volume` = 手(1 手 = 100 股;与 `hqdata` `tushare` 适配器口径一致)。
|
|
39
|
+
- **成交:** 市价单按符合规则的开盘价全额成交;订单、拒绝、费用与成交全程留痕。
|
|
40
|
+
- **复权:** 成交、现金账本和 v0.1 净值使用未复权价格;`adjustment_policy="none"`。
|
|
41
|
+
- **结果:** 每次运行产出净值曲线、订单、成交、每日持仓、成本、配置和运行元数据。
|
|
42
|
+
|
|
43
|
+
### 明确不支持
|
|
44
|
+
|
|
45
|
+
- 实盘交易、券商连接、实时行情和自动下单。
|
|
46
|
+
- 分钟线、Tick、盘中撮合、成交量参与率、限价单和止损单。
|
|
47
|
+
- 融资融券、卖空、期货、期权、多账户、多币种和组合级保证金。
|
|
48
|
+
- 没有可靠证券状态数据支撑的 ST / 涨跌停 / 新股首日 / 北交所细则。
|
|
49
|
+
- 仅由复权因子推断精确的现金分红、送配、配股及税费。
|
|
50
|
+
- 在 `hqdata` 尚未提供指数日线前,把基准收益率作为运行的必需输入。
|
|
51
|
+
|
|
52
|
+
## 安装
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
git clone git@github.com:HonestQuantTech/hqbacktest.git
|
|
56
|
+
cd hqbacktest
|
|
57
|
+
|
|
58
|
+
# 创建虚拟环境
|
|
59
|
+
python -m venv .venv
|
|
60
|
+
source .venv/bin/activate
|
|
61
|
+
|
|
62
|
+
# 装数据层(hqbacktest 只依赖 hqdata 的 csv source;具体数据源 tushare/ricequant 由 hqdata CLI 异步下载落盘)
|
|
63
|
+
pip install -e "../hqdata"
|
|
64
|
+
|
|
65
|
+
# 可编辑安装本项目 + 开发依赖
|
|
66
|
+
pip install -e ".[dev]"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`pyproject.toml` 声明的 Python 下限为 `>=3.10`(与 `hqdata` 一致)。
|
|
70
|
+
|
|
71
|
+
## 配置数据源
|
|
72
|
+
|
|
73
|
+
`hqbacktest` 不接触任何数据源 token,也不在回测运行时联网。回测侧只声明 `source`(数据源名或绝对路径)与 `data_root`(父目录),`hqbacktest` 内部把它们解析成 hqdata 要求的 `(root, source_name)` 并交给 [`hqdata.init_source("csv", root=..., source_name=...)`](https://github.com/HonestQuantTech/hqdata)。
|
|
74
|
+
|
|
75
|
+
| 写法 | 含义 |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `data_root="~/.hqdata"`, `source="tushare"` | 解析为 `(~/.hqdata, tushare)`,传给 hqdata 的 `root=~/.hqdata/tushare`、`source_name="tushare"` |
|
|
78
|
+
| `data_root="/mnt/market-data"`, `source="ricequant"` | 解析为 `(/mnt/market-data, ricequant)` |
|
|
79
|
+
| `source="~/.hqdata/tushare"`(绝对路径) | 直接拆分 `(parent_dir, basename)`,忽略 `data_root` |
|
|
80
|
+
|
|
81
|
+
`source` 接受**名称**(搭配 `data_root`)或**绝对路径**(拆分)。底层 CSV 布局由 `hqdata` CLI 在回测前写入;`hqbacktest` 既不下载数据,也不保存凭证。
|
|
82
|
+
|
|
83
|
+
## 使用
|
|
84
|
+
|
|
85
|
+
最小 Python 示例(公共 API,详见 `examples/`):
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from decimal import Decimal
|
|
89
|
+
from hqbacktest import BacktestConfig, BacktestEngine, BaseStrategy
|
|
90
|
+
from hqbacktest.data import DataView
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class MovingAverageStrategy(BaseStrategy):
|
|
94
|
+
def initialize(self, context):
|
|
95
|
+
context.set_universe(["600000.SH"])
|
|
96
|
+
|
|
97
|
+
def on_bar(self, context, data):
|
|
98
|
+
closes = data.history("600000.SH", field="close", bar_count=5)
|
|
99
|
+
if len(closes) < 5:
|
|
100
|
+
return
|
|
101
|
+
avg = sum(closes) / len(closes)
|
|
102
|
+
if closes[-1] > avg:
|
|
103
|
+
context.order_target_percent("600000.SH", Decimal("0.95"))
|
|
104
|
+
else:
|
|
105
|
+
context.order_target("600000.SH", 0)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
result = BacktestEngine(
|
|
109
|
+
BacktestConfig(start_date="20240102", end_date="20240110", initial_cash="100000", source="tushare"),
|
|
110
|
+
strategy=MovingAverageStrategy(),
|
|
111
|
+
).run()
|
|
112
|
+
result.save("results/moving-average")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
命令行(推荐用于 CI 与可复现实验):
|
|
116
|
+
|
|
117
|
+
`hqbacktest` 不下载数据,命令行示例需要你本地已有一份 `hqdata` CSV 快照。先用 `hqdata` CLI 抓一段真实日线(换成你自己的 tushare/ricequant token 和日期区间):
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
hqdata --source tushare calendar --start 20260123 --end 20260212
|
|
121
|
+
hqdata --source tushare stock-list --start 20260123 --end 20260212
|
|
122
|
+
hqdata --source tushare stock-daily --start 20260123 --end 20260212
|
|
123
|
+
hqdata --source tushare stock-factor --start 20260123 --end 20260212
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
再跑仓库自带的 [`configs/moving_average.toml`](configs/moving_average.toml)(日期区间需要和你抓取的快照区间对上):
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
hqbacktest run --config configs/moving_average.toml --output results/moving-average
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## 示例
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
python examples/buy_and_hold.py
|
|
136
|
+
python examples/moving_average.py
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
两份示例都用 7 天确定性 `InMemoryDataPortal` 数据走通端到端流程,不访问网络、不需要任何凭证。`tests/examples/` 下有 10 项端到端回归测试,覆盖买-持、均线、T+1、费用、净值与指标。
|
|
140
|
+
|
|
141
|
+
## 测试
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
pytest tests/ -v
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
单元测试必须使用内存数据或 mock,不依赖网络和本地行情文件;确需在真实数据上验证 `tushare` / `ricequant` 适配的集成测试必须在 `~/.hqdata/{name}` 不存在或不可读时自动跳过。
|
|
148
|
+
|
|
149
|
+
## 文档导览
|
|
150
|
+
|
|
151
|
+
| 文档 | 内容 |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| [`docs/design/mvp-contract.md`](docs/design/mvp-contract.md) | v0.1 产品契约:术语、模块边界、日事件顺序、不可变规则、非目标 |
|
|
154
|
+
| [`docs/cli.md`](docs/cli.md) | `hqbacktest run` 详细配置 schema、输出目录、错误码、复现性 |
|
|
155
|
+
| [`docs/output.md`](docs/output.md) | `BacktestResult.save(dir)` 输出文件结构与 `PerformanceMetrics` 字段含义 |
|
|
156
|
+
| [`docs/strategy-api.md`](docs/strategy-api.md) | 策略回调与下单时点、`Context` / `DataView` 可见性矩阵 |
|
|
157
|
+
| [`docs/matching.md`](docs/matching.md) | 撮合顺序、整手 / 零股、费用量化、`realized_pnl` 口径 |
|
|
158
|
+
| [`docs/metrics.md`](docs/metrics.md) | 首日 P&L、波动率样本、幂运算桥接、metrics 输出约定 |
|
|
159
|
+
| [`docs/isolation.md`](docs/isolation.md) | `Order` 不可变、`DataView` 私有 portal、universe 生效、历史股票池 |
|
|
160
|
+
| [`docs/factor-diagnostics.md`](docs/factor-diagnostics.md) | 因子诊断接入、分红偏差显性化、CLI 警告 |
|
|
161
|
+
| [`docs/performance.md`](docs/performance.md) | 双层缓存、真实数据基准、性能冒烟测试 |
|
|
162
|
+
|
|
163
|
+
## 免责声明
|
|
164
|
+
|
|
165
|
+
`hqbacktest` 面向研究、教育和历史模拟。回测结果依赖数据质量、交易规则、成本模型、公司行为处理和策略假设,不能代表真实可实现收益,也不构成任何投资、交易或风险管理建议。项目在明确实现实盘能力前不会连接券商或执行真实委托。
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hqbacktest"
|
|
7
|
+
version = "0.1.4"
|
|
8
|
+
description = "A股量化策略回测与交易模拟引擎"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{ name="HowieMen", email="howiemen@honestquant.com" },
|
|
12
|
+
]
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
license = { text = "MIT" }
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
19
|
+
"Topic :: Office/Business :: Financial",
|
|
20
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
21
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
|
+
"License :: OSI Approved :: MIT License",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.10",
|
|
26
|
+
"Programming Language :: Python :: 3.11",
|
|
27
|
+
"Programming Language :: Python :: 3.12",
|
|
28
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
29
|
+
]
|
|
30
|
+
keywords = [
|
|
31
|
+
"hqbacktest",
|
|
32
|
+
"quant",
|
|
33
|
+
"quantitative",
|
|
34
|
+
"backtest",
|
|
35
|
+
"investment",
|
|
36
|
+
"algotrading",
|
|
37
|
+
"A股",
|
|
38
|
+
"trading-simulation",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
dependencies = [
|
|
42
|
+
"pandas>=2.0.0",
|
|
43
|
+
"tomli>=2.0",
|
|
44
|
+
# hqbacktest reads CSV snapshots exclusively via the hqdata API.
|
|
45
|
+
# `pip install -e "../hqdata"` before installing hqbacktest, or rely
|
|
46
|
+
# on an editable install of hqdata that satisfies the dependency.
|
|
47
|
+
"hqdata>=0.1.22",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.scripts]
|
|
51
|
+
hqbacktest = "hqbacktest.cli.__main__:main"
|
|
52
|
+
|
|
53
|
+
[project.urls]
|
|
54
|
+
Homepage = "https://honestquant.com/hqbacktest"
|
|
55
|
+
Source = "https://github.com/HonestQuantTech/hqbacktest"
|
|
56
|
+
|
|
57
|
+
[project.optional-dependencies]
|
|
58
|
+
# hqbacktest must NOT import Tushare / RiceQuant / AkShare SDKs directly.
|
|
59
|
+
# These extras are reserved placeholders so users have a consistent way to
|
|
60
|
+
# declare source-related optional deps; hqdata remains the only integration layer.
|
|
61
|
+
tushare = []
|
|
62
|
+
ricequant = []
|
|
63
|
+
|
|
64
|
+
dev = [
|
|
65
|
+
"pytest>=7.0",
|
|
66
|
+
"pytest-cov>=4.0",
|
|
67
|
+
"black>=25.1.0",
|
|
68
|
+
"build>=1.0",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[tool.setuptools.packages.find]
|
|
72
|
+
where = ["src"]
|
|
73
|
+
include = ["hqbacktest*"]
|
|
74
|
+
|
|
75
|
+
[tool.pytest.ini_options]
|
|
76
|
+
testpaths = ["tests"]
|
|
77
|
+
pythonpath = ["."]
|
|
78
|
+
# Integration tests live under tests/integration/ and are skipped by
|
|
79
|
+
# default (no snapshot on the test machine). Run with
|
|
80
|
+
# `pytest tests/integration/ -v` on machines that have `~/.hqdata/tushare`.
|
|
81
|
+
addopts = "--ignore=tests/integration"
|
|
82
|
+
markers = [
|
|
83
|
+
"integration: marks tests as integration tests against real hqdata sources (skipped by default)",
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
[tool.black]
|
|
87
|
+
line-length = 88
|
|
88
|
+
# Black target-version mirrors the package's runtime floor
|
|
89
|
+
# (`requires-python = ">=3.10"`); newer versions are unaffected because
|
|
90
|
+
# black auto-applies its PEP-level rules across supported Pythons.
|
|
91
|
+
target-version = ["py310"]
|
|
92
|
+
# pyproject.toml and README.md are not Python sources; black tries to
|
|
93
|
+
# parse them and fails on the TOML author table / HTML badges. Skip them.
|
|
94
|
+
extend-exclude = '''
|
|
95
|
+
/(
|
|
96
|
+
\.toml
|
|
97
|
+
| README\.md
|
|
98
|
+
)/
|
|
99
|
+
'''
|
|
100
|
+
|
|
101
|
+
[tool.coverage.run]
|
|
102
|
+
branch = true
|
|
103
|
+
source = ["src/hqbacktest"]
|
|
104
|
+
# Only the `python -m hqbacktest` shim is omitted; it is a 4-line entry
|
|
105
|
+
# point whose only body runs under `if __name__ == "__main__"` and is
|
|
106
|
+
# exercised via subprocess (not measured by in-process coverage).
|
|
107
|
+
# `cli/__main__.py` is real, tested business code and stays measured.
|
|
108
|
+
omit = [
|
|
109
|
+
"src/hqbacktest/__main__.py",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
[tool.coverage.report]
|
|
113
|
+
show_missing = true
|
|
114
|
+
skip_covered = false
|
|
115
|
+
fail_under = 80
|
|
116
|
+
exclude_lines = [
|
|
117
|
+
"pragma: no cover",
|
|
118
|
+
"if __name__ == .__main__.:",
|
|
119
|
+
"raise NotImplementedError",
|
|
120
|
+
]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""hqbacktest - A-share quantitative strategy backtest and trading simulation engine."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import version
|
|
4
|
+
|
|
5
|
+
from hqbacktest.data import (
|
|
6
|
+
CacheKey,
|
|
7
|
+
DataCache,
|
|
8
|
+
DataVersion,
|
|
9
|
+
DataView,
|
|
10
|
+
HqDataCsvPortal,
|
|
11
|
+
InMemoryDataPortal,
|
|
12
|
+
MarketDataPortal,
|
|
13
|
+
resolve_source_location,
|
|
14
|
+
)
|
|
15
|
+
from hqbacktest.domain import (
|
|
16
|
+
AccountSnapshot,
|
|
17
|
+
AdjustmentPolicy,
|
|
18
|
+
Bar,
|
|
19
|
+
CorporateActionAdjustment,
|
|
20
|
+
EventType,
|
|
21
|
+
Fill,
|
|
22
|
+
Order,
|
|
23
|
+
OrderStatus,
|
|
24
|
+
OrderType,
|
|
25
|
+
Portfolio,
|
|
26
|
+
Position,
|
|
27
|
+
PositionSnapshot,
|
|
28
|
+
PriceMode,
|
|
29
|
+
RejectReason,
|
|
30
|
+
Side,
|
|
31
|
+
)
|
|
32
|
+
from hqbacktest.engine import (
|
|
33
|
+
BacktestConfig,
|
|
34
|
+
BacktestEngine,
|
|
35
|
+
BacktestResult,
|
|
36
|
+
BaseStrategy,
|
|
37
|
+
Context,
|
|
38
|
+
CorporateAction,
|
|
39
|
+
CorporateActionProvider,
|
|
40
|
+
CostModel,
|
|
41
|
+
DefaultCostModel,
|
|
42
|
+
EngineEvent,
|
|
43
|
+
EquityPoint,
|
|
44
|
+
EventLog,
|
|
45
|
+
FactorDiagnostic,
|
|
46
|
+
FactorDiagnosticCollector,
|
|
47
|
+
MetricsConfig,
|
|
48
|
+
NullStrategy,
|
|
49
|
+
PerformanceMetrics,
|
|
50
|
+
SimulatedBroker,
|
|
51
|
+
Strategy,
|
|
52
|
+
TradingDayIterator,
|
|
53
|
+
TradingRuleSet,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
__version__ = version("hqbacktest")
|
|
57
|
+
|
|
58
|
+
__all__ = [
|
|
59
|
+
"__version__",
|
|
60
|
+
"AccountSnapshot",
|
|
61
|
+
"AdjustmentPolicy",
|
|
62
|
+
"BacktestConfig",
|
|
63
|
+
"BacktestEngine",
|
|
64
|
+
"BacktestResult",
|
|
65
|
+
"Bar",
|
|
66
|
+
"BaseStrategy",
|
|
67
|
+
"CacheKey",
|
|
68
|
+
"Context",
|
|
69
|
+
"CorporateAction",
|
|
70
|
+
"CorporateActionAdjustment",
|
|
71
|
+
"CorporateActionProvider",
|
|
72
|
+
"CostModel",
|
|
73
|
+
"DataCache",
|
|
74
|
+
"DataVersion",
|
|
75
|
+
"DataView",
|
|
76
|
+
"DefaultCostModel",
|
|
77
|
+
"EngineEvent",
|
|
78
|
+
"EquityPoint",
|
|
79
|
+
"EventLog",
|
|
80
|
+
"EventType",
|
|
81
|
+
"FactorDiagnostic",
|
|
82
|
+
"FactorDiagnosticCollector",
|
|
83
|
+
"Fill",
|
|
84
|
+
"HqDataCsvPortal",
|
|
85
|
+
"InMemoryDataPortal",
|
|
86
|
+
"MarketDataPortal",
|
|
87
|
+
"MetricsConfig",
|
|
88
|
+
"NullStrategy",
|
|
89
|
+
"Order",
|
|
90
|
+
"OrderStatus",
|
|
91
|
+
"OrderType",
|
|
92
|
+
"PerformanceMetrics",
|
|
93
|
+
"Portfolio",
|
|
94
|
+
"Position",
|
|
95
|
+
"PositionSnapshot",
|
|
96
|
+
"PriceMode",
|
|
97
|
+
"RejectReason",
|
|
98
|
+
"Side",
|
|
99
|
+
"SimulatedBroker",
|
|
100
|
+
"Strategy",
|
|
101
|
+
"TradingDayIterator",
|
|
102
|
+
"TradingRuleSet",
|
|
103
|
+
"resolve_source_location",
|
|
104
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Entry point so `python -m hqbacktest run --config ...` works.
|
|
2
|
+
|
|
3
|
+
Delegates to `hqbacktest.cli.__main__:main` and re-raises SystemExit so
|
|
4
|
+
argparse error codes propagate.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from hqbacktest.cli.__main__ import main
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
if __name__ == "__main__":
|
|
13
|
+
sys.exit(main())
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Command-line interface for hqbacktest.
|
|
2
|
+
|
|
3
|
+
The CLI is intentionally tiny: it loads a TOML config file, validates it,
|
|
4
|
+
constructs a `BacktestConfig` and a strategy class, runs the backtest, and
|
|
5
|
+
writes the result to an output directory. Heavy lifting lives in
|
|
6
|
+
`engine` / `data` / `domain`; this package only orchestrates.
|
|
7
|
+
"""
|