docs(deploy): VPS统一部署权威文档(取代vps-native-brain,覆盖07-17全统一架构)

This commit is contained in:
2026-07-17 08:10:04 +08:00
parent a75e094e7d
commit 37c850d0c5
+173
View File
@@ -0,0 +1,173 @@
# VPS 统一部署(全量权威文档)
> **状态:2026-07-17,端到端验证通过。** 本文档覆盖当前生产真实状态,**取代** `vps-native-brain.md`07-15,架构已变)。
> 所有服务统一到 VPS;NAS 降为备份;Mac mini 仅开发。
## 1. 三机角色(定稿)
| 机器 | IP / 角色 | 跑什么 |
|------|-----------|--------|
| **北京 VPS**(生产) | `49.232.102.198` Windows Server 20224C16G/180G,腾讯云**境内** | sanguo_api + 前端 dist + miniQMT + 进程内 qmt_gateway_client + 权威 DB + 数据采集 schtasks |
| **首尔 VPS**HTTPS 入口) | `43.133.235.218` Ubuntu,腾讯云**境外** | Caddy 签 Let's Encrypt 证书(境外免备案)+ 反代到北京:8000 |
| **NAS** | `192.168.2.154` Synology | 纯备份(xtdata zip 历史库);sanguo_api 容器**已停用** |
| **Mac mini** | 开发机 | 改代码、构建前端、rsync/scp 部署;采集/同步 cron **已停用** |
> **为什么 HTTPS 入口在首尔**:北京(境内)用未备案域名 Host 走 80/443 会被腾讯**webblock**(见 §6)。首尔境外无需 ICP 备案,Caddy 自动签 LE 证书。北京只暴露 8000(API),由首尔反代。
## 2. 网络入口与域名
- **域名**`vnpy.mysanguo.top` → A 记录指向**首尔** `43.133.235.218`NameSilo/dnsowl 托管)。
- **访问**`https://vnpy.mysanguo.top`(Mac 浏览器;ClashX 关闭或在 `/etc/hosts``43.133.235.218 vnpy.mysanguo.top` 绕 fake-ip)。
- **北京防火墙**:已放行 22 / 80 / 443 / 3389 / 8000。
### 首尔 Caddyfilevnpy block,关键:`header_up Host`
```caddyfile
vnpy.mysanguo.top {
reverse_proxy 49.232.102.198:8000 {
header_up Host 49.232.102.198:8000
}
}
```
> **`header_up Host` 是 webblock 修复的核心**:不加它,Caddy 会透传 `Host: vnpy.mysanguo.top` 给北京:8000,腾讯基于该未备案域名 Host 对 **GET 请求** webblockHEAD/POST 放行,故早先 login 能通、GET / 被拦)。改成北京 IP 后 Host 不带域名,北京不拦。备份在首尔 `/etc/caddy/Caddyfile.bak0716`。
> 北京本机的 `sanguo-caddy` schtasks **已 Disabled**——入口是首尔,北京不直接serve web。
## 3. 北京 VPS 安装清单(`C:\sanguo_vnpy_v2\`
| 组件 | 来源 | 位置 |
|------|------|------|
| vnpy 4.4.0 源码 | 项目内 `vnpy_v4.4.0/`(纯 Python 零 C 扩展) | `C:\sanguo_vnpy_v2\vnpy_v4.4.0\``sys.path`/`PYTHONPATH` 引用,**不 pip install**|
| sanguo_* 大脑包 | 项目 scp | `C:\sanguo_vnpy_v2\sanguo_*\` |
| Python 3.10 | 华为镜像 | `C:\Python310\python.exe` |
| vnpy-qmt 0.3.3vendor 自维护)/ vnpy_ctastrategy 1.4.1 / vnpy_sqlite 1.1.3 / TA-Lib 0.6.8 / empyrical 0.5.5 / pandas / numpy / fastapi / uvicorn / apscheduler | pip(阿里镜像)| site-packages |
| 前端 dist | Mac 构建 scp | `C:\sanguo_vnpy_v2\frontend\dist\` |
pip 镜像:`pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/`(写 `C:\Users\Administrator\AppData\Roaming\pip\pip.ini`)。
## 4. 数据(DB 为主,parquet 兜底)
- **权威 DB**`C:\sanguo_vnpy_v2\data\quant_trading.db`vnpy sqlite,表 `dbbardata`/`dbbaroverview`)。
- 个股日线:**13,484,621 行 / 5201 只 / 2010-01-04 ~ 2026-07-16**。
- 指数:`000300`(沪深300)/`399001`(深证成指)/`399006`(创业板指)/`000905`(中证500) 全在 DB。
- **回测结果库**`C:\sanguo_vnpy_v2\data\backtest_results.db`(表 `backtest_stats`)。
- **结果文件**`C:\sanguo_vnpy_v2\data\{task_id}_{equity,trades,metrics}.json` + `{task_id}.log``file_dir = dirname(db_path)`)。
- **读取口径**`sanguo_data/datareader.py`):
- `read_db_daily`(个股)→ vnpy `load_bar_data``guess_exchange` 判交易所)。
- `read_index_daily`(指数/基准)→ **从 DB 读**(前缀解析交易所:`sh→SSE`/`sz→SZSE`,避免 `guess_exchange` 把 000300 误判 SZSE)。返回 `pd.DataFrame`。parquet 仅备份不再读。
- **灌库**`scripts/data_platform/import_vnpy_daily_fast.py`parquet → DB,向量化,`INSERT OR REPLACE`)。env `VNPY_DB_PATH`/`DAILY_DIR`Windows 用正斜杠 `C:/...`)。日线增量走 `daily_update_xtdata.py`xtdata 本地缓存,零漂移)。
- **用户铁律**:下载质量不可控,**绝不直接写主库**——staging 隔离→验证→合并。
## 5. 后端服务 sanguo_api
- **进程**`schtasks sanguo-api`SYSTEM,开机自启)→ `C:\sanguo_vnpy_v2\run_api.ps1`
- **端口**8000`/api/v1/*` + `/docs` + SPA `/`)。
- **日志**`C:\Users\Administrator\sanguo_api.log`**找 worker 异常/traceback 看 here**)。
- **回测执行**`Orchestrator``sanguo_orchestrator/runner.py`+ `ProcessPoolExecutor`Windows spawn)。提交 → pool worker 跑 `run_cta_backtest` → 结果存 DB + JSON。状态走 WebSocket + 轮询 `/task/{id}`
### env`run_api.ps1`,保 git 真相,不改 yaml
```powershell
$env:PYTHONUTF8 = "1"
$env:PYTHONPATH = "C:\sanguo_vnpy_v2;C:\sanguo_vnpy_v2\vnpy_v4.4.0"
$env:SANGUO_DATA_ROOT = "C:\sanguo_vnpy_v2\data" # 重映射 daily/raw/qfq/15min/vnpy_db
$env:SANGUO_DB_PATH = "C:\sanguo_vnpy_v2\data\backtest_results.db"
$env:SANGUO_FILE_DIR = "C:\sanguo_vnpy_v2\data\backtest_files"
$env:SANGUO_LIVE_ENABLED = "true"
$env:SANGUO_BRIDGE_URL = "http://127.0.0.1:8765" # 兼容留,进程内不用
$env:BRIDGE_TOKEN = "<见 windows-vps-access 记忆>"
$env:SANGUO_USE_QMT_GATEWAY = "1" # 进程内 qmt_gateway_client 直连 miniQMT
$env:SANGUO_QMT_ACCOUNT = "66639661"
# SPA_STATIC_DIR 未设 → 默认 repo/frontend/dist = C:\sanguo_vnpy_v2\frontend\dist
```
不设这些 envNAS/Mac)则用 `config/backtest.yaml` 原值 → 三机共用同一份 git config。
## 6. 前端(Vue SPA
- **同源相对路径**`frontend/src/api/client.ts``/api/v1`WebSocket 自适配协议/host → Caddy 反代即打通,无需 build 时配 host。
- **构建**Mac 上 `cd frontend && npm run build``frontend/dist/`
- **部署**`scp -i ~/.ssh/id_ed25519 -r frontend/dist/* Administrator@49.232.102.198:C:/sanguo_vnpy_v2/frontend/dist/`
- **挂载**`sanguo_api/main.py::build_app``static_dir` 挂载在 `/`history fallback,深链刷新可用)。
- **结果页**`Result.vue``Promise.allSettled` 隔离 7 个接口(benchmark/risk 缺数据不拖垮整页);时间范围筛选前端本地过滤。
## 7. 实盘链路(miniQMT,进程内直连)
- miniQMT 模拟账户 `66639661``C:\国金QMT交易端模拟\`XtMiniQmt.exe 常驻)。
- **进程内 `qmt_gateway_client`**commit 992f53d):sanguo_trader 同进程直连 miniQMT`connect=0` 成功),替 HTTP bridge。
- **bridge 已废弃**`sanguo-bridge` schtasks = Plan B,仅 miniQMT 权限被收回/换券商/跨机时翻出,见记忆 `bigqmt-rpc-bridge-fallback`)。
- 实盘走影子模式(sanguo 影子下单 + xtquant 独立脚本),详见记忆 `d-phase-mock-trading-link`
## 8. 回测(CTA + 优化 + 历史)
- **A股适配**`sanguo_backtest/ashare_engine.py``AShareBacktestingEngine` 子类化修复 vnpy 失真:1股定寸 `size=N`/拒做空/费用)。记忆 `backtest-engine-ashare-adapter`
- **指标**`sanguo_backtest/metrics.py::compute_metrics`empyrical,聚宽同源)。**关键修复**:导入 empyrical 前补回 NumPy 2.0 移除的别名,否则 `sortino_ratio``np.NINF`
```python
for _a,_v in (("NINF",-np.inf),("Inf",np.inf),("PINF",np.inf),("NaN",np.nan),("NAN",np.nan),("infty",np.inf)):
if not hasattr(np,_a): setattr(np,_a,_v)
```
> 不修则 compute_metrics 静默崩 → `_metrics.json` 不生成 → 结果页回退 vnpy 原始字段(单位混乱)→ 显示 3305% 收益 / -5000万% 回撤 / 无图表。记忆 `empyrical-numpy2-npinf-crash`。
- **结果页 7 端点**`/result`statistics+relative_metrics/`/benchmark-curve`/`/risk-series`/`/equity-curve`/`/daily-pnl`/`/trades`/`/log`。benchmark/risk 缺 metrics 文件时返空 200(不再 404 拖垮整页)。
- **基准**`hs300`(sh000300) / `zz500`(sz000905),从 DB 读。
- **老任务回填**`backfill_metrics.py`(本地 `/tmp`,按需移 `scripts/`)从 `_equity.json`+基准重算 compute_metricsUPDATE statistics + 补 `_metrics.json`。
## 9. 部署流程(Mac 开发 → VPS 生产)
```bash
# 1) 改代码(Mac ~/.openclaw/sanguo_projects/sanguo_vnpy_v2
# 2) 前端构建
cd frontend && npm run build
# 3) 同步后端 + 前端到 VPS
scp -i ~/.ssh/id_ed25519 -r frontend/dist/* Administrator@49.232.102.198:C:/sanguo_vnpy_v2/frontend/dist/
scp -i ~/.ssh/id_ed25519 sanguo_backtest/metrics.py Administrator@49.232.102.198:C:/sanguo_vnpy_v2/sanguo_backtest/metrics.py
# (其余改动的 .py 同理 scp 到对应路径)
# 4) 重启 API(让新代码 + 新 worker 生效)
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 "schtasks /end /tn sanguo-api & schtasks /run /tn sanguo-api"
```
## 10. 运维速查
```bash
# SSH / scp / RDP
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198
scp -i ~/.ssh/id_ed25519 本地文件 Administrator@49.232.102.198:C:/目标
# RDP 49.232.102.198:3389 (Administrator + 腾讯云控制台密码)
# 跑命令的坑(重要)
# - Python 一律 C:\Python310\python.exe -X utf8emoji/中文不加 -X utf8 会 UnicodeEncodeError
# - 复杂命令用 stdin 脚本:ssh ... "C:\Python310\python.exe -X utf8 -" < /tmp/script.py
# - Windows 路径在 ssh stdin 里用正斜杠 C:/...\raw 等 \r 转义会炸)
# - .ps1 含中文路径用搜索代替字面量
# 服务管理
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 "schtasks /end /tn sanguo-api & schtasks /run /tn sanguo-api"
# API 日志(worker traceback / 启动报错)
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 "powershell -Command \"Get-Content C:\Users\Administrator\sanguo_api.log -Tail 50\""
```
## 11. 已知坑
| 坑 | 现象 | 解法 |
|----|------|------|
| **境内未备案 webblock** | GET vnpy.mysanguo.top 返腾讯备案页 | 入口走首尔境外 + Caddy `header_up Host <北京IP>` |
| **empyrical×numpy2.0** | 结果页垃圾值(3305%)+无图 | metrics.py 补回 np 别名(§8|
| **SSH 密集连接触发限连/fail2ban** | `Connection closed by 49.232.102.198 port 22` | 等 ~30-60s 冷却;长脚本用 detached(`Start-Process`) + 轮询文件;或 Monitor 稀疏 20s 轮询 |
| **回测静默吞异常** | 指标崩被 `except` 吞,显示垃圾不报错 | cta_engine metrics 块已加 `traceback.format_exc()` 到 warning(见 sanguo_api.log|
| **ProcessPool worker 缓存旧代码** | 改 .py 后 worker 仍旧行为 | `schtasks /end & /run sanguo-api` 重启(spawn 新 worker|
## 12. 验证记录(2026-07-17,浏览器端到端)
- `https://vnpy.mysanguo.top` 登录(admin)→ 前端加载 ✓
- CTA 回测 DoubleMa 600000:统计正确(33.06%/-29.58%/Alpha9.30%/Beta0.524/Sortino1.02) + 5 图表渲染 + 成交明细 ✓
- zz500 基准切换:benchmark_return 80.3%(≠ hs300 36.7%)✓
- 参数优化、BollChannel 策略:跑通 ✓
- K线 / 每日收益(485点) / 日志:有数据 ✓
- 老任务回填:8 个有交易的修复(含历史垃圾值任务)✓
- 实盘链路:进程内 miniQMT connect=0 ✓(见 §7
## 13. 相关文档与记忆
- 旧版(已取代):`vps-native-brain.md`07-15)、`nas-deploy-plan.md`、`docker-deployment.md`。
- 数据:`data-download.md`、`env-version-matrix.md`。
- 记忆:`windows-vps-access`、`empyrical-numpy2-npinf-crash`、`backtest-engine-ashare-adapter`、`db-primary-parquet-fallback`、`bigqmt-rpc-bridge-fallback`、`d-phase-mock-trading-link`、`dep-baseline-locked`。