Metadata-Version: 2.4
Name: qaetf
Version: 1.2.1
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Rust
Classifier: Topic :: Office/Business :: Financial :: Investment
Requires-Dist: pymongo>=4.0 ; extra == 'data'
Requires-Dist: pytdx>=1.72 ; extra == 'data'
Requires-Dist: pymongo>=4.0 ; extra == 'mongo'
Requires-Dist: pytdx>=1.72 ; extra == 'pytdx'
Provides-Extra: data
Provides-Extra: mongo
Provides-Extra: pytdx
License-File: LICENSE
Summary: ETF backtest engine for the QUANTAXIS ecosystem
Keywords: quant,backtest,etf,quantaxis,qifi
Author: QUANTAXIS
License: Apache-2.0
Requires-Python: >=3.9, <3.13
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Changelog, https://github.com/Hkxtor/qaetf-rs/blob/master/docs/v1.2-implementation-plan.md
Project-URL: Homepage, https://github.com/Hkxtor/qaetf-rs
Project-URL: Issues, https://github.com/Hkxtor/qaetf-rs/issues
Project-URL: Repository, https://github.com/Hkxtor/qaetf-rs

# qaetf-rs

[![Rust](https://img.shields.io/badge/Rust-1.75+-orange.svg)](https://www.rust-lang.org)
[![Python](https://img.shields.io/badge/Python-3.9--3.12-blue.svg)](https://www.python.org)
[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](LICENSE)

`qaetf-rs` 是 [QUANTAXIS](https://github.com/quantaxis/quantaxis) 生态中的下一代高性能 ETF 回测引擎。它采用 Rust 编写核心计算逻辑，通过 PyO3 提供极致性能的 Python 接口。

## 🏗️ 架构设计

项目遵循 **Ports-and-Adapters (Hexagonal Architecture)** 架构原则，确保业务逻辑的纯粹性与确定性：

```mermaid
graph TD
    User["Python 策略 / 研究员"] --> PyAdapter["qaetf-py (Adapter: PyO3)"]
    PyAdapter --> Core["qaetf-core (Domain: 撮合 & 账户)"]
    Data["MongoDB (Persistence)"] <-.-> DataAdapter["qaetf-data (Adapter)"]
    DataAdapter <-.-> Core
```

- **qaetf-core**: 纯领域逻辑。包含确定性撮合引擎、多资产账户管理、QIFI 协议支持。
- **qaetf-data**: 数据持久化适配器。支持 MongoDB 8.2.6+，用于存储历史数据与回测结果。
- **qaetf-py**: Python 语言接口。基于 PyO3 构建，实现零拷贝数据传递。

## ✨ 核心特性

- **高性能**: 相比纯 Python 回测引擎，在处理百万级 Bar 数据时速度提升 10x - 100x。
- **确定性**: 核心撮合逻辑不依赖系统时间或随机数，保证回测结果 100% 可重复。
- **QIFI 兼容**: 原生支持 QUANTAXIS 交互式协议，易于在回测与实盘环境间切换。
- **ETF 交易语义**: 显式支持 ETF T+1、最小下单单位、价格 tick、手续费与滑点配置。
- **内存安全**: 利用 Rust 所有权模型，消除复杂的并发数据竞争与内存泄漏风险。

## 🛠️ 安装与开发

### 环境要求
- Rust 1.75+
- Python 3.9 - 3.12
- `uv` (推荐) 或 `pip`

### 编译安装 (Python 环境)
使用 `uv` 进行快速开发编译：
```bash
# 克隆仓库
git clone https://github.com/your-repo/qaetf-rs.git
cd qaetf-rs

# 编译并安装 Rust 扩展到本地开发环境
uv run maturin develop
```

### Rust 工作空间编译
```bash
cargo build --release
```

## 🚀 快速上手 (Python)

```python
from qaetf import BacktestConfig, BacktestEngine, Bar, OrderIntent


def my_strategy(context):
    bar = context["bar"]
    if bar["datetime"] == "20240101093000":
        return [OrderIntent(bar["code"], "buy", 100, None)]
    return []


config = BacktestConfig("test_user", 100000.0)
engine = BacktestEngine(config, my_strategy)

bars = [
    Bar(
        code="510300",
        datetime="20240101093000",
        open=10.0,
        high=10.5,
        low=9.9,
        close=10.2,
        volume=100000,
        adj_factor=None,
    )
]
result = engine.run(bars)

print(f"最终净值: {result['summary']['final_equity']}")
```

## v1.1 ETF 回测语义

默认构造保持兼容旧基线：`BacktestConfig("account-1", 100000.0)` 使用 legacy 交易规则、0 手续费、0 滑点。显式配置才启用新的 ETF 语义：

```python
config = BacktestConfig(
    "account-1",
    100000.0,
    commission_rate=0.0003,
    min_commission=5.0,
    slippage_bps=1.0,
    trading_rule_preset="etf_t1",
)
```

- `trading_rule_preset="legacy"`：默认行为，最小下单单位为 1，不强制 price tick，允许同日买入后卖出，不允许裸空。
- `trading_rule_preset="etf_t1"`：ETF T+1 行为，最小下单单位为 100，price tick 为 0.001，今日买入进入今仓，同日不可卖；交易日切换后今仓结转为昨仓，昨仓才可卖。
- `commission_rate` 与 `min_commission`：按 `max(price * volume * commission_rate, min_commission)` 计算手续费。买入扣 `notional + fee`，卖出入账 `notional - fee`，卖出已实现盈亏扣卖出 fee。为保持最小行为变更，买入 fee 不摊入 `average_cost`，但 cash、final equity 和 total return 会体现买入 fee。
- `slippage_bps`：按基点调整成交价，买入价上浮，卖出价下浮。默认 0 不改变旧成交价。
- Python result 中 `trade["fee"]` 仅在手续费非零时出现，默认 0 手续费不改变旧 trade schema。

QIFI 字段兼容范围见 [`docs/qifi_compatibility_matrix.md`](docs/qifi_compatibility_matrix.md)。性能与测试收口说明见 [`docs/performance_report.md`](docs/performance_report.md) 和 [`docs/test_coverage.md`](docs/test_coverage.md)。

v1.1 当前支持：

- A 股 ETF bar-only 回测。
- 日线与分钟线 Bar。
- 单策略、单账户、多标的持仓与订单维护。
- ETF T+1、手续费、滑点、最小下单单位和 price tick。
- Mongo 历史行情读写与回测结果 repository 存储。
- PyO3 Python 策略接口。

v1.1 当前不支持：

- Tick / L2 行情撮合。
- 实盘券商 API。
- 多策略共享账户。
- ETF 申赎。
- 复杂撮合、复杂订单类型和数据库迁移。

## Python ETF 数据层

`qaetf.data` 提供最小可用的 ETF 行情采集、清洗、验证与 Mongo 导入链路。Python 数据层只负责数据侧适配，不修改 Rust core，也不参与回测撮合逻辑。

模块结构：
- `ETFDataProvider`：数据源抽象。
- `InMemoryETFDataProvider`：测试和示例用内存数据源。
- `PytdxETFDataProvider`：pytdx 数据源入口，未安装 pytdx 时会在使用时给出明确错误。
- `ETFDataFetcher`：调用 provider 并返回统一 Bar schema。
- `ETFBarValidator`：校验和规范化 Bar。
- `ETFMongoStorage`：写入与读取 Mongo 行情集合。

默认安装不强制安装真实数据链路依赖。需要真实 Mongo 或 pytdx 时安装 optional extra：

```bash
uv sync --extra mongo
uv sync --extra pytdx
uv sync --extra data
```

统一 Bar schema 使用可排序字符串时间：日线为 `YYYY-MM-DD`，分钟线为 `YYYY-MM-DD HH:MM:SS`。字段包括：`code`, `market`, `datetime`, `frequency`, `open`, `high`, `low`, `close`, `volume`, `amount`, `source`。

validator 会检查必填字段、OHLC 非负与区间关系、`volume`/`amount` 非负、频率白名单，并按 ETF 代码前缀推断 `SSE`/`SZSE`。Mongo 行情集合为 `etf_daily` 与 `etf_minute`，均建立 `{ code: 1, datetime: 1, frequency: 1 }` 复合唯一索引。

InMemory provider 示例：

```python
from qaetf.data import ETFDataFetcher, InMemoryETFDataProvider

provider = InMemoryETFDataProvider(
    daily_bars=[
        {
            "code": "510300",
            "datetime": "2024-01-02",
            "open": 1.0,
            "high": 1.2,
            "low": 0.9,
            "close": 1.1,
            "volume": 1000,
        }
    ]
)
fetcher = ETFDataFetcher(provider=provider)
bars = fetcher.fetch_daily("510300", start="2024-01-01", end="2024-01-31")
```

Pytdx provider 示例：

```python
from qaetf.data import ETFDataFetcher, PytdxETFDataProvider

provider = PytdxETFDataProvider()
fetcher = ETFDataFetcher(provider=provider)
bars = fetcher.fetch_daily("510300", start="2024-01-01", end="2024-01-31")
```

`PytdxETFDataProvider` 默认连接公开通达信行情服务器，适用于研究和测试场景；生产级数据链路应使用受控行情网关，并在落库前通过 `ETFBarValidator` 校验。

数据采集 CLI dry-run：

```bash
python -m qaetf.data.cli fetch \
  --code 510300 \
  --frequency 1d \
  --start 2024-01-01 \
  --end 2024-01-31 \
  --dry-run
```

写入 Mongo：

```bash
python -m qaetf.data.cli fetch \
  --code 510300 \
  --frequency 1d \
  --start 2024-01-01 \
  --end 2024-01-31 \
  --mongo-uri mongodb://localhost:27017 \
  --database qaetf
```

JSON 输出：

```bash
python -m qaetf.data.cli fetch \
  --code 510300 \
  --frequency 1d \
  --dry-run \
  --json
```

CLI 会输出采集 summary、preview 和 quality report。安装后也可使用等价 console script：

```bash
uv run qaetf-fetch-etf fetch --code 510300 --frequency 1d --dry-run
```

Mongo URI 不应提交到代码或配置文件中；生产环境不要在命令行传递带账号密码的 Mongo URI，避免进入 shell history、进程列表或 CI 日志。生产数据采集调度不属于当前 v1.1 范围。

Mongo storage 示例：

```python
from qaetf.data import ETFMongoStorage

storage = ETFMongoStorage(uri="mongodb://localhost:27017", database="qaetf_test_local")
storage.ensure_indexes()
storage.insert_bars(bars, frequency="1d")
loaded = storage.load_bars("510300", frequency="1d")
```

可选 integration test 默认跳过。需要手动启用：

```bash
QAETF_TEST_MONGO_URI=mongodb://localhost:27017 pytest tests/integration/test_mongo_storage_integration.py
QAETF_TEST_PYTDX=1 pytest tests/integration/test_pytdx_provider_integration.py
```

Mongo integration 默认只允许单机或多机本地 Mongo URI，`mongodb+srv://` 和远程 host 必须额外设置 `QAETF_TEST_MONGO_ALLOW_REMOTE=1`；测试库名必须匹配 `qaetf_test` 或 `qaetf_test_*`，不要指向生产库。测试清理只删除本轮生成的唯一 `source` 数据。

## 🧪 测试

```bash
# 运行 Rust 单元测试
cargo test

# 运行 Python 集成测试
pytest tests/
```

### Golden 回测基线

`tests/fixtures/golden/510300_1d_dual_ma_bars.json` 提供固定的 510300 日线小样本，`tests/test_golden_dual_ma.py` 使用 3/5 日双均线策略、`next_bar_open` 撮合和 100000 初始资金验证回测确定性。期望结果保存在 `tests/fixtures/golden/dual_ma_510300_expected.json`。

更新 golden expected：

```bash
uv run maturin develop
uv run python scripts/update_golden_dual_ma.py --update
uv run pytest tests/test_golden_dual_ma.py
```

### Differential 回测测试

`tests/test_differential_reference.py` 使用极简 Python reference engine 对照正式 Rust/PyO3 `BacktestEngine`，用于验证核心回测语义没有偏离。当前覆盖范围包括单标的 510300 日线 dual-ma legacy 场景、分钟线 ETF T+1 + 手续费 + 滑点场景，以及多标的 ETF T+1 独立持仓场景。

多标的 deterministic 单元覆盖位于 `tests/test_backtest_engine.py`。Mongo repository/E2E 覆盖位于 `crates/qaetf-data/tests/mongo_backtest_repository.rs`，真实 Mongo 路径需要显式环境变量启用。

运行 differential test：

```bash
uv run pytest tests/test_differential_reference.py
```

### Benchmark

Golden 和 differential tests 负责 correctness；性能观察由 [`benchmarks/`](benchmarks/README.md) 中的 FFI、Mongo read 与 E2E benchmark 脚本完成。Benchmark 默认不作为普通 CI gate。覆盖率运行说明见 [`docs/test_coverage.md`](docs/test_coverage.md)，性能报告模板见 [`docs/performance_report.md`](docs/performance_report.md)。

## 📏 编码规范

- 遵循 **SOLID**, **KISS**, **DRY** 原则。
- 核心逻辑层禁止直接使用 IO（必须通过 Adapter 注入）。
- 提交代码前请确保 `cargo clippy` 与 `cargo fmt` 已通过。

