first commit

This commit is contained in:
2026-08-07 16:08:34 +08:00
commit e0b5228008
51 changed files with 5175 additions and 0 deletions

270
README.md Normal file
View File

@@ -0,0 +1,270 @@
# 股市回测平台
专业的 **历史回测 + 回放式模拟** 平台A 股为主,**不做实盘**)。
输入技术指标参数MACD/RSI/KDJ…→ 后端回测 → 前端可视化 K 线、指标叠加、买卖点、净值曲线与绩效。
技术选型与架构决策详见 [TECH_STACK.md](./TECH_STACK.md)。
---
## 功能特性
**已实现Phase 0 MVP**
- 自定义指标参数回测(当前内置 MACD 金叉死叉策略)
- K 线主图 + 成交量 + MACD 副图DIF/DEA/柱)
- **买卖点标注**(信号回放到 K 线,↑买 ↓卖)
- **周期切换**:日线 / 周线 / 月线 / 年线(日线为基底,应用层 pandas 聚合)
- **悬停弹框**:鼠标移到任意 K 线,显示当日 开/高/低/收、涨跌幅、振幅、成交量(手)、MACD 值(同花顺式)
- 净值曲线 + 绩效指标(总收益、最大回撤、夏普、年化波动、胜率、交易次数)
- A 股真实成本建模:印花税 0.05%(单边卖出)、过户费 0.001%沪深双边、佣金万1最低5元、T+1、100 股整手
- 回测运行注册表(每次回测落库,可复现/审计的基础)
**规划中(见 TECH_STACK.md 路线图)**
- 接入 Tushare/AKShare 真实 A 股数据(替换 DEMO 合成数据)
- 防过拟合体检walk-forward / 样本外 / FDR 多重比较修正)
- 基准归因对比沪深300/中证500超额、信息比率、beta/alpha
- 复权因子管道、回放式模拟盘
- 切 TimescaleDBhypertable + Continuous Aggregates
---
## 技术栈
| 层 | 选型 |
|---|---|
| 后端 | Python 3.12+ · FastAPI · Pydantic v2 · SQLAlchemy 2.0 (async) · uv包管理 |
| 回测引擎 | 自研单一引擎fast/strict 两档),指标纯 numpy/pandas生产可换 TA-Lib |
| 数据库 | MVP 用 SQLite 零配置;生产切 PostgreSQL 16/17 + TimescaleDB |
| 前端 | Vue 3.5 · Vite · TypeScript · Pinia · PrimeVue 5 · pnpm |
| 图表 | lightweight-charts 5K 线)· ECharts 6净值 |
---
## 目录结构
```
stock/
├── TECH_STACK.md # 技术选型与架构决策(必读)
├── README.md # 本文件
├── backend/ # FastAPI + 回测引擎
│ ├── pyproject.toml # uv 依赖声明
│ ├── .env.example # 配置示例(数据库 / 费率)
│ ├── smoke_test.py # 后端全链路自检脚本
│ └── app/
│ ├── main.py # FastAPI 入口(启动建表)
│ ├── config.py # 配置pydantic-settings
│ ├── db.py # async SQLAlchemy 引擎/会话
│ ├── domain.py # 领域契约Bar/Signal/Fill/Position…
│ ├── models.py # ORMCandle / BacktestRun
│ ├── schemas.py # Pydantic DTO= OpenAPI 契约)
│ ├── commission.py # A 股交易成本(已修正、可配置)
│ ├── indicators.py # 指标MACD/RSI/KDJ/布林/均线(单一事实源)
│ ├── api.py # 路由:/health /candles /backtest
│ ├── data/ # DataProvider 适配器 + 合成数据 + 周期聚合
│ └── backtest/ # engine / broker(PaperBroker) / metrics / strategies
└── frontend/ # Vue SPA
├── src/
│ ├── main.ts # PrimeVue(Aura 深色) + Pinia
│ ├── api/ # 类型化客户端 + DTO 镜像
│ ├── stores/ # Pinia 回测状态
│ ├── components/ # KLineChart / EquityChart / MetricsPanel / BacktestForm
│ └── views/ # BacktestView
└── vite.config.ts # /api 代理到 :8000
```
---
## 环境要求
| 工具 | 版本 | 说明 |
|---|---|---|
| Node.js | ≥ 20实测 24 | 前端 |
| pnpm | ≥ 9实测 11 | 前端包管理 |
| Python | ≥ 3.12(实测 3.14 | 后端 |
| uv | 任意(实测 0.12 | 后端包管理,[安装](https://docs.astral.sh/uv/) |
| 可选PostgreSQL | 16/17 | 生产数据库MVP 用 SQLite 无需安装 |
**安装 uv**(若未装):
```bash
pip install uv
```
---
## 快速开始(开发模式)
需要两个终端,分别跑后端与前端。
### 1) 后端
```bash
cd backend
uv sync # 创建 .venv 并安装依赖(首次)
uv run uvicorn app.main:app --reload --port 8000
```
- 首次启动自动建表SQLite`backend/stock.db`)并播种约 500 个交易日的合成 K 线symbol=`DEMO`)。
- 交互式 API 文档http://localhost:8000/docs
**自检**(无需起服务器,验证全链路):
```bash
uv run --with httpx --directory backend python smoke_test.py
```
### 2) 前端
```bash
cd frontend
pnpm install # 首次
pnpm dev # http://localhost:5173
```
前端 `/api` 请求由 Vite 代理到后端 `:8000`(见 `vite.config.ts`),无需处理跨域。
打开 http://localhost:5173 → 选周期、改参数 → 点「开始回测」。
鼠标悬停 K 线可看当日详情弹框;切日线/周线/月线/年线勾「fast 模式」可对比关闭费用/T+1 的差异。
---
## 配置说明
后端配置通过 `backend/.env`(复制 `.env.example`)或环境变量:
```bash
# 数据库MVP 默认 SQLite零配置
DATABASE_URL=sqlite+aiosqlite:///./stock.db
# 切到你自己的 PostgreSQL / TimescaleDB
# DATABASE_URL=postgresql+asyncpg://user:password@localhost:5432/stock
# 真实数据源Tushare Pro免费版即可。留空则仅 DEMO 合成数据可用)
TUSHARE_TOKEN=你的token
DATA_ADJUST=qfq # 复权qfq 前复权 / hfq 后复权 / 留空不复权
DATA_DEFAULT_START=20200101
# A 股交易成本(基准日 2026-08可覆盖默认值见 app/config.py
# STAMP_DUTY_RATE=0.0005 # 印花税 0.05%,单边卖出
# TRANSFER_FEE_RATE=0.00001 # 过户费 0.001%,沪深双边
# COMMISSION_RATE=0.0001 # 佣金 万1
# COMMISSION_MIN=5.0 # 最低 5 元
```
### 切到 PostgreSQL + TimescaleDB
1. 目标库执行 `CREATE EXTENSION IF NOT EXISTS timescaledb;`
2. `DATABASE_URL=postgresql+asyncpg://...`
3. TODO`candles` 表升级为 hypertable 并配置 Continuous Aggregates 多周期预聚合schema 已兼容,无需改表结构):
```sql
SELECT create_hypertable('candles', 'ts');
```
> 即便能装在现有 Postgres 上,建议为交易系统单独起一个 PG16/17 实例,便于调优/备份/隔离回测扫描负载。
---
## API
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | `/api/health` | 健康检查 |
| GET | `/api/candles/{symbol}?timeframe=1d&limit=1000` | 取 K 线(`timeframe`: `1d`/`1w`/`1M`/`1y` |
| POST | `/api/data/sync` | 拉取并缓存某标的日线Tushare 主 → AKShare 兜底。body: `{symbol, source?, force?}` |
| POST | `/api/backtest` | 跑回测(真实标的首次自动拉取并缓存) |
**回测请求示例**
```json
POST /api/backtest
{
"symbol": "DEMO",
"timeframe": "1d",
"strategy": "macd_cross",
"params": { "fast": 12, "slow": 26, "signal": 9 },
"initial_cash": 100000,
"fast_mode": false
}
```
返回:`candles`K线、`indicators`MACD 三线)、`signals`(买卖点)、`equity`(净值序列)、`metrics`(绩效)、`final_cash`/`final_position`。
> **关于「标的」**`DEMO` 为内置合成数据;其余为真实 A 股代码(如 `000001`、`600519`),首次回测时**自动经 Tushare 拉取并本地缓存**(需配置 `TUSHARE_TOKEN` + 联网),二次回测秒出。默认初始资金 100 万,足够交易高价股(如茅台)。
---
## 生产构建与部署
### 前端构建
```bash
cd frontend
pnpm build # 产物在 frontend/dist
pnpm preview # 本地预览构建产物
```
部署时把 `frontend/dist` 用任意静态服务器nginx / caddy / 对象存储)托管,并把 `/api` 反向代理到后端。
### 后端生产运行
```bash
cd backend
uv run uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
# 或 gunicornLinuxuv run gunicorn app.main:app -w 4 -k uvicorn.workers.UvicornWorker
```
生产务必设置 `DATABASE_URL` 指向 PostgreSQL不要用 SQLite。
### 参考docker-compose具备 Docker 后使用)
```yaml
services:
db:
image: timescale/timescaledb:latest-pg17
environment: { POSTGRES_PASSWORD: stock, POSTGRES_DB: stock }
ports: ["5432:5432"]
volumes: ["pgdata:/var/lib/postgresql/data"]
api:
build: ./backend
environment: { DATABASE_URL: postgresql+asyncpg://postgres:stock@db:5432/stock }
ports: ["8000:8000"]
depends_on: [db]
web:
image: nginx:alpine
volumes: ["./frontend/dist:/usr/share/nginx/html:ro", "./nginx.conf:/etc/nginx/conf.d/default.conf:ro"]
ports: ["80:80"]
depends_on: [api]
volumes: { pgdata: {} }
```
---
## 扩展指南
### 加一个指标
在 `backend/app/indicators.py` 增加纯函数(输入 close/high/low Series输出 DataFrame保持函数签名风格即可被策略复用。例已内置 `macd / rsi / kdj / bollinger / ma`。
### 加一个策略
内置策略:`macd_cross`MACD 金叉死叉)、`ma_cross`(双均线交叉)、`single_ma`(单均线,价格穿越均线)。前端下拉自动出现新策略、参数自适应。
在 `backend/app/backtest/strategies/` 新增继承 `Strategy``base.py`)的类,实现 `compute()`(预算指标)与 `on_bar(i, row, broker)`(决策并向 `broker` 下单),再在 `__init__.py` 的 `STRATEGIES` 注册 `{name: Class}`。策略构造参数由请求 `params(dict)` 以 `**kwargs` 传入。策略只产生买卖意图,撮合/费用交给 `PaperBroker`。
### 数据源(已集成)
真实 A 股行情已集成(`backend/app/data/`
- `tushare_provider.py` —— 主数据源,需 `TUSHARE_TOKEN`,默认前复权(`pro_bar`,积分不足时自动退化为不复权日线)
- `akshare_provider.py` —— 兜底,免费免 token默认**未安装**`uv add akshare` 后自动启用
- `fetcher.py` —— 编排Tushare 失败自动切 AKShare拉取后写入 `candles` 表本地缓存(解耦上游停运/限频)
换复权方式改 `.env` 的 `DATA_ADJUST`;换默认拉取起点改 `DATA_DEFAULT_START`。
---
## 常见问题
- **`pnpm install` 报 `Ignored build scripts: esbuild`**:新版 pnpm 默认不运行依赖的安装脚本。已通过 `frontend/pnpm-workspace.yaml` 的 `allowBuilds: { esbuild: true, vue-demi: true }` 放行;若仍提示,运行 `pnpm approve-builds` 选择允许。
- **前端图表不显示**:确认后端已起(`/api/health` 返回 ok浏览器控制台看是否 404/跨域dev 应走 Vite 代理无跨域)。
- **lightweight-charts 报 `addCandlestickSeries is not a function`**:那是 v4 API。本项目用 v5 的 `chart.addSeries(CandlestickSeries, ...)`,请勿混用旧教程。
- **真实标的首次回测慢/拉取失败**:首次需联网请求 Tushare数秒。Tushare 对 `adj_factor` 限频(每小时 1 次),触发时自动退化为不复权日线,不影响回测。数据已本地缓存,二次回测秒出。若 Tushare 积分不足,`uv add akshare` 后将自动用 AKShare 兜底。
- **高价股(如茅台)回测 0 信号**:一手太贵买不起。默认初始资金已设为 100 万;如仍不够,在表单调高「初始资金」。
- **Python 3.14 兼容**MVP 依赖fastapi/sqlalchemy/pandas/numpy均已支持指标用纯 Python 实现,不依赖 TA-Lib 的 C 库Windows 免编译。
---
## 路线图
详见 [TECH_STACK.md](./TECH_STACK.md)「分阶段落地路线」。下一步优先级:
1. 接入 Tushare 真实数据
2. 更多指标/策略RSI/KDJ/布林策略)
3. 防过拟合体检 + 基准归因
4. 切 TimescaleDB、回测结果缓存与对比