Files

12 KiB
Raw Permalink Blame History

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
首尔 VPSHTTPS 入口) 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.218NameSilo/dnsowl 托管)。
  • 访问https://vnpy.mysanguo.top(Mac 浏览器;ClashX 关闭或在 /etc/hosts43.133.235.218 vnpy.mysanguo.top 绕 fake-ip)。
  • 北京防火墙:已放行 22 / 80 / 443 / 3389 / 8000。

首尔 Caddyfilevnpy block,关键:header_up Host

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 兜底)

  • 权威 DBC:\sanguo_vnpy_v2\data\quant_trading.dbvnpy 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}.logfile_dir = dirname(db_path))。
  • 读取口径sanguo_data/datareader.py):
    • read_db_daily(个股)→ vnpy load_bar_dataguess_exchange 判交易所)。
    • read_index_daily(指数/基准)→ 从 DB 读(前缀解析交易所:sh→SSE/sz→SZSE,避免 guess_exchange 把 000300 误判 SZSE)。返回 pd.DataFrame。parquet 仅备份不再读。
  • 灌库scripts/data_platform/import_vnpy_daily_fast.pyparquet → DB,向量化,INSERT OR REPLACE)。env VNPY_DB_PATH/DAILY_DIRWindows 用正斜杠 C:/...)。日线增量走 daily_update_xtdata.pyxtdata 本地缓存,零漂移)。
  • 用户铁律:下载质量不可控,绝不直接写主库——staging 隔离→验证→合并。

5. 后端服务 sanguo_api

  • 进程schtasks sanguo-apiSYSTEM,开机自启)→ C:\sanguo_vnpy_v2\run_api.ps1
  • 端口8000/api/v1/* + /docs + SPA /)。
  • 日志C:\Users\Administrator\sanguo_api.log找 worker 异常/traceback 看 here)。
  • 回测执行Orchestratorsanguo_orchestrator/runner.py+ ProcessPoolExecutorWindows spawn)。提交 → pool worker 跑 run_cta_backtest → 结果存 DB + JSON。状态走 WebSocket + 轮询 /task/{id}

envrun_api.ps1,保 git 真相,不改 yaml

$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/v1WebSocket 自适配协议/host → Caddy 反代即打通,无需 build 时配 host。
  • 构建Mac 上 cd frontend && npm run buildfrontend/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_appstatic_dir 挂载在 /history fallback,深链刷新可用)。
  • 结果页Result.vuePromise.allSettled 隔离 7 个接口(benchmark/risk 缺数据不拖垮整页);时间范围筛选前端本地过滤。

7. 实盘链路(miniQMT,进程内直连)

  • miniQMT 模拟账户 66639661C:\国金QMT交易端模拟\XtMiniQmt.exe 常驻)。
  • 进程内 qmt_gateway_clientcommit 992f53d):sanguo_trader 同进程直连 miniQMTconnect=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.pyAShareBacktestingEngine 子类化修复 vnpy 失真:1股定寸 size=N/拒做空/费用)。记忆 backtest-engine-ashare-adapter
  • 指标sanguo_backtest/metrics.py::compute_metricsempyrical,聚宽同源)。关键修复:导入 empyrical 前补回 NumPy 2.0 移除的别名,否则 sortino_rationp.NINF
    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 端点/resultstatistics+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 生产)

# 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. 运维速查

# 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.md07-15)、nas-deploy-plan.mddocker-deployment.md
  • 数据:data-download.mdenv-version-matrix.md
  • 记忆:windows-vps-accessempyrical-numpy2-npinf-crashbacktest-engine-ashare-adapterdb-primary-parquet-fallbackbigqmt-rpc-bridge-fallbackd-phase-mock-trading-linkdep-baseline-locked