Files
stock/README.md
2026-08-07 16:08:34 +08:00

12 KiB
Raw Blame History

股市回测平台

专业的 历史回测 + 回放式模拟 平台A 股为主,不做实盘)。 输入技术指标参数MACD/RSI/KDJ…→ 后端回测 → 前端可视化 K 线、指标叠加、买卖点、净值曲线与绩效。

技术选型与架构决策详见 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 后端包管理,安装
可选PostgreSQL 16/17 生产数据库MVP 用 SQLite 无需安装

安装 uv(若未装):

pip install uv

快速开始(开发模式)

需要两个终端,分别跑后端与前端。

1) 后端

cd backend
uv sync                                    # 创建 .venv 并安装依赖(首次)
uv run uvicorn app.main:app --reload --port 8000
  • 首次启动自动建表SQLitebackend/stock.db)并播种约 500 个交易日的合成 K 线symbol=DEMO)。
  • 交互式 API 文档:http://localhost:8000/docs

自检(无需起服务器,验证全链路):

uv run --with httpx --directory backend python smoke_test.py

2) 前端

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)或环境变量:

# 数据库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. TODOcandles 表升级为 hypertable 并配置 Continuous Aggregates 多周期预聚合schema 已兼容,无需改表结构):
    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 跑回测(真实标的首次自动拉取并缓存)

回测请求示例

POST /api/backtest
{
  "symbol": "DEMO",
  "timeframe": "1d",
  "strategy": "macd_cross",
  "params": { "fast": 12, "slow": 26, "signal": 9 },
  "initial_cash": 100000,
  "fast_mode": false
}

返回:candlesK线indicatorsMACD 三线)、signals(买卖点)、equity(净值序列)、metrics(绩效)、final_cash/final_position

关于「标的」DEMO 为内置合成数据;其余为真实 A 股代码(如 000001600519),首次回测时自动经 Tushare 拉取并本地缓存(需配置 TUSHARE_TOKEN + 联网),二次回测秒出。默认初始资金 100 万,足够交易高价股(如茅台)。


生产构建与部署

前端构建

cd frontend
pnpm build          # 产物在 frontend/dist
pnpm preview        # 本地预览构建产物

部署时把 frontend/dist 用任意静态服务器nginx / caddy / 对象存储)托管,并把 /api 反向代理到后端。

后端生产运行

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 后使用)

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_crossMACD 金叉死叉)、ma_cross(双均线交叉)、single_ma(单均线,价格穿越均线)。前端下拉自动出现新策略、参数自适应。

backend/app/backtest/strategies/ 新增继承 Strategybase.py)的类,实现 compute()(预算指标)与 on_bar(i, row, broker)(决策并向 broker 下单),再在 __init__.pySTRATEGIES 注册 {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 表本地缓存(解耦上游停运/限频)

换复权方式改 .envDATA_ADJUST;换默认拉取起点改 DATA_DEFAULT_START


常见问题

  • pnpm installIgnored build scripts: esbuild:新版 pnpm 默认不运行依赖的安装脚本。已通过 frontend/pnpm-workspace.yamlallowBuilds: { 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「分阶段落地路线」。下一步优先级:

  1. 接入 Tushare 真实数据
  2. 更多指标/策略RSI/KDJ/布林策略)
  3. 防过拟合体检 + 基准归因
  4. 切 TimescaleDB、回测结果缓存与对比