Files
sanguo_vnpy_v2/docs/deployment/vps-unified-deploy.md
T

174 lines
12 KiB
Markdown
Raw 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.
# 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`。