2026-09-05 00:12:19 +08:00
2026-09-05 00:12:19 +08:00
2026-09-05 00:12:19 +08:00
2026-08-14 23:18:54 +08:00
2026-09-05 00:12:19 +08:00
2026-09-04 19:05:23 +08:00
2026-09-04 19:05:23 +08:00
2026-08-07 16:08:34 +08:00

股市回测平台

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

技术选型与架构决策详见 TECH_STACK.md


快速启动

两个终端分别启动后端与前端:

# 终端 1 —— 后端 APIhttp://localhost:8000
cd backend
env -u SSLKEYLOGFILE uv run uvicorn app.main:app --reload --port 8000

# 终端 2 —— 前端开发服务器http://localhost:5173
cd frontend
pnpm dev

env -u SSLKEYLOGFILE 是本机Windows必需的用户环境变量 SSLKEYLOGFILE 值开头混有不可见控制符asyncpg 建连即崩。详见下文「快速开始(开发模式)」与「常见问题」。

首次运行前先安装依赖:后端 uv syncbackend 下)、前端 pnpm installfrontend 下)。 浏览器打开 http://localhost:5173 即可使用。详细说明见下文「快速开始(开发模式)」。


功能特性

已实现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 路线图)

  • 防过拟合体检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
数据库 PostgreSQL 16/17后端强制要求+ Redis 缓存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 适配器Tushare/AKShare+ 周期聚合
│       └── 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 数据库(必装,后端已不再支持 SQLite

安装 uv(若未装):

pip install uv

快速开始(开发模式)

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

1) 后端

实际启动命令venv 解释器直启,日志重定向到仓库根 backend_run.log,改代码自动热重载):

cd backend
uv sync                                    # 创建 .venv 并安装依赖(仅首次)
env -u SSLKEYLOGFILE .venv/Scripts/python.exe -m uvicorn app.main:app --reload --port 8000 > ../backend_run.log 2>&1

看日志:tail -f backend_run.log;确认起没起:curl http://localhost:8000/api/health

等价的 uv run 写法(不带日志重定向,直接打到当前终端):

cd backend
env -u SSLKEYLOGFILE uv run uvicorn app.main:app --reload --port 8000
  • env -u SSLKEYLOGFILE(本机 Windows 必需):用户环境变量 SSLKEYLOGFILE 的值开头混有 U+202A 不可见控制符asyncpg 建连时执行 ssl.keylog_filename 直接抛 OSError: [Errno 22]uvicorn 启动即崩。Git Bash 下用 env -u 剔除即可;根治可 setx SSLKEYLOGFILE "C:\Users\cirry\Desktop\fhzg.log"(重开终端后不再需要前缀)。验证:python -c "import os; print(repr(os.environ.get('SSLKEYLOGFILE')))"
  • Windows --reload 僵死watcher 偶尔改文件不重载且日志无 Reloading 行,此时只能杀进程树重启——netstat -ano | grep :8000 找 PIDtaskkill //PID <pid> //T //F
  • 数据库结构由 Alembic 管理:首次部署/更新代码后先执行 uv run alembic upgrade head(见「初始化登录系统」)。
  • 交互式 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)或环境变量:

# 数据库(开发、测试、生产统一使用 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免费版即可
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 管理。首次部署或更新代码后执行:

cd backend
uv run alembic upgrade head

系统不提供注册接口。使用服务器交互式命令创建唯一用户;再次执行会重置密码并吊销该用户的所有旧会话:

uv run python -m app.cli.create_user --username admin

密码使用 Argon2id 保存。浏览器只接收 HttpOnly 会话 Cookie数据库只保存随机会话 Token 的 SHA-256 摘要。默认会话有效期 12 小时,连续 5 次登录失败后锁定 15 分钟。

生产环境必须启用 HTTPS并设置

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. 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": "000001",
  "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

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


生产构建与部署

前端构建

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

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

后端生产运行

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 使用 PostgreSQLpostgresql+asyncpg://),已不再支持 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 免编译。
  • 后端启动即崩,栈在 asyncpg 深处(OSError: [Errno 22] Invalid argument:是本机环境变量 SSLKEYLOGFILE 值里混入的 U+202A 不可见控制符所致不是数据库问题。Git Bash 下启动命令前加 env -u SSLKEYLOGFILE;或 setx SSLKEYLOGFILE "C:\Users\cirry\Desktop\fhzg.log" 重写该变量后重开终端。
  • uvicorn --reload 改代码不生效Windows 下 watcher 会僵死(日志无 Reloading 行touch 无效)。netstat -ano | grep :8000 找到 PID 后 taskkill //PID <pid> //T //F 杀进程树重启。

路线图

详见 TECH_STACK.md「分阶段落地路线」。下一步优先级:

  1. 接入 Tushare 真实数据
  2. 更多指标/策略RSI/KDJ/布林策略)
  3. 防过拟合体检 + 基准归因
  4. 切 TimescaleDB、回测结果缓存与对比
Description
No description provided
Readme 2 MiB
Languages
Python 54.5%
Vue 36.1%
TypeScript 8.3%
CSS 0.7%
Shell 0.2%
Other 0.1%