# WorldQuant Alpha 研究工作空间 个人单账户系统。首期实现平台资料、Alpha 同步与查询、PnL 缓存、本地备注/标签/收藏/研究状态。平台接口只读,认证除外;不会回测、触发检查、回写属性或提交 Alpha。 需求与后续路线图见 [项目方案](docs/project-plan.md)。前端 React 19 + TypeScript + Semi Design,后端 Python 3.12 + FastAPI + HTTPX + SQLAlchemy,PostgreSQL 保存数据,Caddy 提供 Web 入口。前后端独立依赖、独立构建,所有部署文件位于根目录。 ## 本机启动 需要 Docker Engine/Desktop 和 Docker Compose v2;初始化脚本需要 Python 3。项目根目录执行: ```bash python3 scripts/init_env.py docker compose up -d --build --wait docker compose ps ``` 打开 **http://localhost:8080**。用户名默认 `admin`,初始密码读取本机 `.env` 中的 `ADMIN_PASSWORD`。脚本以仅当前用户可读写的权限创建 `.env`,不会覆盖已有文件或打印密钥。已有 `.env` 时直接启动即可。 `web` 仅绑定 `127.0.0.1`;`backend` 和 `db` 没有宿主机端口。使用 `localhost` 访问以匹配请求来源校验。修改端口时更改 `.env` 中的 `LOCAL_PORT` 并重建容器。 首次使用: 1. 登录系统,在“个人信息”保存 WorldQuant 邮箱和密码,点击“连接 WorldQuant”。 2. 如平台要求人工验证,在显示的入口完成操作,再点击“继续验证”;后台保留同一挑战会话。 3. 在“Alpha 管理”手动同步平台数据,或导入指定 Alpha ID。任务面板显示进度、错误、取消和重试。 4. 点击 Alpha 打开详情。研究记录保存在本地;PnL 点击获取后缓存。下次同步会更新平台数据并保留本地研究记录。 平台未返回的资料与指标保留为空。数值筛选采用平台原始单位,例如 Turnover `0.15` 表示 15%。日期筛选边界为 UTC;时间显示采用个人页的时区偏好。 ## 公网 HTTPS 部署 `compose.public.yaml` 是独立配置,不与本机配置叠加。先在服务器完成上面的密钥初始化,将 `.env` 中 `DOMAIN` 改为自己的域名(无协议、路径、端口)。DNS 指向服务器,允许入站 TCP 80/443,UDP 443 可选。 ```bash docker compose -f compose.public.yaml up -d --build --wait docker compose -f compose.public.yaml logs --tail=100 web ``` 访问 `https://你的域名`。Caddy 自动签发/续期证书并重定向 HTTP;后端固定启用 `Secure`、`HttpOnly`、`SameSite=Strict` 的服务端会话 Cookie,并校验写请求来源。无开放注册。数据库仅在 Compose 网络中可见。证书持久化到 `caddy_data` 卷。 不要使用示例域名申请证书。公网证书签发取决于实际 DNS、服务器网络与 ACME 服务;本地测试不能代替真实域名的 HTTPS 验收。生产环境请保持单个 backend 容器、单个 Uvicorn worker;首期任务执行不支持多副本。 ## 配置与账户安全 | 配置 | 用途 | | --- | --- | | `ADMIN_USERNAME` / `ADMIN_PASSWORD` | 仅首次空库初始化管理员,重启不会重置现有密码 | | `POSTGRES_PASSWORD` | 数据库密码;初始化脚本使用随机十六进制,避免连接 URL 转义问题 | | `ENCRYPTION_KEY` | 独立 Fernet 密钥,加密数据库中的 WorldQuant 密码 | | `LOCAL_PORT` | 本机入口端口,默认 8080 | | `DOMAIN` | 公网域名 | WorldQuant 密码仅在后端解密。平台 Cookie 仅保存在后端内存,进程重启后重新认证。前端不保存密码或 Cookie 副本;日志与响应不输出平台认证正文。`.env` 不进入 Docker 构建上下文,应与数据库备份分别安全保管。丢失 `ENCRYPTION_KEY` 后须重新输入平台密码;切勿在正常升级时重新生成它。 修改系统密码(同时撤销所有系统会话): ```bash docker compose exec backend python -m app.cli reset-password ``` 公网时为命令补上 `-f compose.public.yaml`。单账户一旦同步确认身份,不允许更换邮箱混入其他账户数据。断开平台连接会暂停未完成任务;重新连接并确认身份后恢复。 ## 升级与数据库迁移 依赖锁文件分别是 `backend/uv.lock` 和 `frontend/pnpm-lock.yaml`。每次后端启动都会执行 `alembic upgrade head`,失败时不会启动 API。升级前先备份数据库与 `.env`,然后: ```bash docker compose up -d --build --wait docker compose exec backend alembic current docker compose logs --tail=100 backend ``` 普通 `docker compose down` 不删除数据卷,重建后数据保留。**`down -v` 会删除数据库与证书卷,正常停机/升级不要使用。** 新建迁移的开发命令为 `uv run alembic revision --autogenerate -m "说明"`;须人工审阅生成文件,再运行 `uv run alembic upgrade head` 和测试。已发布的迁移文件保持不变。 ## 备份与恢复 以下为本机配置命令;公网统一补上 `-f compose.public.yaml`,自定义项目名时保持相同 `-p`。数据库备份包括平台快照、研究记录、账户密文和任务。备份文件仍属于私有数据。 ```bash mkdir -p backups umask 077 docker compose exec -T db pg_dump -U wq -d wq -Fc --no-owner > backups/wq.dump ``` 先恢复到独立数据库校验,不覆盖正在使用的数据: ```bash docker compose exec -T db createdb -U wq wq_restore_check docker compose exec -T db pg_restore -U wq -d wq_restore_check --no-owner --exit-on-error < backups/wq.dump docker compose exec -T db psql -U wq -d wq_restore_check -c 'SELECT count(*) FROM alphas; SELECT count(*) FROM research;' ``` 确认备份正确后,若需要恢复正式库,下面操作会覆盖现有库内容,应先另做当前备份并停止写入: ```bash docker compose stop web backend docker compose exec -T db pg_restore -U wq -d wq --clean --if-exists --no-owner --exit-on-error < backups/wq.dump docker compose up -d --wait ``` 跨机器恢复时同时使用原来的 `ENCRYPTION_KEY`。恢复后运行中的任务自动回到队列,按已提交检查点继续;平台会话可能要求重新连接或人工验证。 ## 本地开发与验证 后端使用 `uv` 和 Python 3.12,前端使用 Node 22.12+ 与 pnpm 9.15.0。 ```bash cd backend uv sync --python 3.12 uv run ruff check app tests uv run pytest -q ``` 自动化测试使用隔离的 SQLite 与 HTTPX 模拟上游,不读取真实凭据或请求 WorldQuant。涵盖 API 鉴权、加密、查询/导出一致性、同步重试与恢复、人工验证、类型差异和 PnL schema。部署验收另用真实 PostgreSQL 和 Docker。 ```bash cd frontend corepack enable pnpm install --frozen-lockfile pnpm build pnpm exec playwright install chromium pnpm test ``` 浏览器测试自动启动临时数据库、模拟平台 API 和 Vite,使用 620 条明确标记 `TEST` 的合成 Alpha。不会向正式数据库写入样例。测试验证系统登录、账户连接、多页同步、SUPER 详情、备注与收藏在刷新后保留、PnL、超过 500 条 CSV 及退出。截图写入忽略目录 `output/playwright/`。 需要重跑 Docker 持久化和备份验收时,先停止占用 8080 的本机实例(不删除卷),创建独立测试环境。以下脚本只接受 `wq-alpha-acceptance*` 项目名: ```bash mkdir -p .local python3 scripts/init_env.py --output .local/docker-test.env docker compose --env-file .local/docker-test.env -p wq-alpha-acceptance up -d --build --wait python3 backend/tests/docker_acceptance.py --env-file .local/docker-test.env # 仅删除上面新建的测试容器和测试卷 docker compose --env-file .local/docker-test.env -p wq-alpha-acceptance down -v ``` 独立启动模拟环境以开发 UI(两个终端): ```bash # 终端 1,backend/ 下 uv run uvicorn tests.browser_server:create_test_app --factory --host 127.0.0.1 --port 18000 # 终端 2,frontend/ 下 WQ_DEV_API=http://127.0.0.1:18000 pnpm dev --port 5179 ``` 访问 `http://127.0.0.1:5179`,系统测试密码 `browser-test-password`,平台邮箱 `test@example.com`、密码任意。每次停止服务即丢弃临时测试数据。 开发真实后端时显式配置 `DATABASE_URL` 指向自己的开发 PostgreSQL,设置 `ADMIN_PASSWORD`、`ENCRYPTION_KEY`、`PUBLIC_ORIGIN=http://localhost:5173`,执行迁移后用 `uv run uvicorn app.main:create_app --factory --host 127.0.0.1 --port 8000` 启动。`pnpm dev` 默认代理到此地址。生产 Compose 不开放开发数据库端口。 ## 接口与数据约定 FastAPI 的 `/openapi.json` 与 `/docs` 可在后端开发端口访问;生产 Web 入口只代理 `/api/*`,不对外公开文档页面。 - `/api/v1/auth`:登录、退出、会话;除登录与健康检查外,业务接口都需要 Cookie。 - `/api/v1/account`:偏好、加密凭据、连接/验证/断开/资料刷新。 - `/api/v1/alphas`:服务端筛选与排序、详情、本地研究记录、批量编辑、流式 CSV。 - `/api/v1/alphas/{id}/pnl`:只读缓存;刷新通过 `pnl_refresh` 任务。 - `/api/v1/sync-jobs`:创建任务立即返回 202 和 ID,查询、取消与重试。 写请求需 `X-WQ-Request: 1`;浏览器跨站写入被拒绝。Alpha 平台快照、`research` 本地研究、`pnl_cache` 分开存储。研究状态固定为 `inbox/candidate/optimizing/archived`;平台类型、语言、状态按原值显示。 同步按“未提交/已提交 × 可见/隐藏”分页,每页数据与检查点同事务提交,Alpha ID 幂等更新。失败任务保留进度,重试只处理剩余页或失败 ID。上游 `Retry-After` 等待可被取消。分页过程中平台记录移动可能造成重复或遗漏,通过 ID 去重和再次全量同步校正;单次没有查到不自动删除本地记录。 ## 日志排查与验证边界 ```bash docker compose ps docker compose logs --tail=100 backend docker compose logs --tail=100 web docker compose logs --tail=100 db curl -f http://localhost:8080/api/v1/health ``` 平台任务失败时先查看任务面板的错误及失败 ID。401 登录失效需重新登录系统;平台人工验证需回个人页;429 会按平台等待时间自动重试。密钥损坏/丢失时重新配置平台凭据。不要为排错把密码、认证响应或 Cookie 加入日志。 实现使用旧项目已知请求形态并对模拟上游做自动化验证。WorldQuant 当前真实账号权限、人工验证页面行为、实际数据 schema、真实账户全量同步及公网证书签发,均需要在自己的账户/域名完成只读联调;未取得该证据前不宣称已验证。验收实测结果见 [验收记录](docs/verification.md)。