Files
stock/README.md

301 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 股市回测平台
专业的 **历史回测 + 回放式模拟** 平台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
# 数据库(开发、测试、生产统一使用 PostgreSQL
DATABASE_URL=postgresql+asyncpg://user:password@host:5432/stock
# 鉴权:本地 HTTP 为 false线上 HTTPS 必须为 true
AUTH_COOKIE_SECURE=false
AUTH_SESSION_HOURS=12
CORS_ORIGINS=http://localhost:5173
# 生产建议关闭接口文档
EXPOSE_API_DOCS=true
# 真实数据源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 元
```
### 初始化登录系统
数据库结构由 Alembic 管理。首次部署或更新代码后执行:
```bash
cd backend
uv run alembic upgrade head
```
系统不提供注册接口。使用服务器交互式命令创建唯一用户;再次执行会重置密码并吊销该用户的所有旧会话:
```bash
uv run python -m app.cli.create_user --username admin
```
密码使用 Argon2id 保存。浏览器只接收 `HttpOnly` 会话 Cookie数据库只保存随机会话 Token 的 SHA-256 摘要。默认会话有效期 12 小时,连续 5 次登录失败后锁定 15 分钟。
生产环境必须启用 HTTPS并设置
```bash
AUTH_COOKIE_SECURE=true
CORS_ORIGINS=https://你的域名
EXPOSE_API_DOCS=false
```
### 切到 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 alembic upgrade head
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、回测结果缓存与对比