first commit
This commit is contained in:
270
README.md
Normal file
270
README.md
Normal 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)
|
||||
- 复权因子管道、回放式模拟盘
|
||||
- 切 TimescaleDB(hypertable + 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 5(K 线)· 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 # ORM(Candle / 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
|
||||
# 或 gunicorn(Linux):uv 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、回测结果缓存与对比
|
||||
Reference in New Issue
Block a user