feat(deploy): VPS原生大脑部署—env覆盖配置+部署文档(Option B落地)

- sanguo_data/config.py: load_config 加 env 覆盖(SANGUO_DATA_ROOT 重映射
  daily/raw/qfq/15min/vnpy_db;SANGUO_LIVE_ENABLED/SANGUO_BRIDGE_URL 覆盖 live 段),
  三机共用一份 git config 零漂移
- sanguo_api/main.py: build_app 加 SANGUO_DB_PATH/SANGUO_FILE_PATH env 覆盖
- docs/deployment/vps-native-brain.md: VPS原生部署全记录(安装/配置/服务/验证/
  数据staging/Phase2暂缓理由)

实证:VPS vnpy4.4.0+vnpy_qmt0.3.3 连真实miniQMT读真实账户/持仓;
live_step全链路+影子transport(本机bridge)通过。详见文档。
This commit is contained in:
2026-07-15 12:55:58 +08:00
parent c66f7cb14e
commit 3ef8c617f0
3 changed files with 115 additions and 4 deletions
+88
View File
@@ -0,0 +1,88 @@
# VPS 原生大脑部署(Option Bvnpy native,无 docker
2026-07-15 落地。生产大脑从 NAS 迁到 VPS Windows 宿主**原生运行**(非容器),
vnpy 4.4.0 源码引用 + vnpy_qmt 进程内直连 miniQMT(同机同会话)。三机角色切分:
**VPS=实盘生产 / Mac=开发 / NAS=研究·回测·测试备份**NAS `live.enabled=false`)。
## 头号风险已实证解除
vnpy_qmt 0.3.3 只测过 vnpy 3.5;本项目 vnpy 4.4.0。逐符号核对全部 vnpy import
BaseGateway / OrderData / Status 枚举成员 / ZoneInfo 等)对 4.4.0 源码**零漂移**
VPS 真机 `from vnpy_qmt import QmtGateway` import+实例化通过,**连真实 miniQMT 读到真实
账户(66639661 余额 9999998.51+ 持仓(600000.SSE 200股 / 000001.SZSE 100股,与 bridge
完全一致)+ 7551 合约**。无需改 gateway 代码。
## VPS 安装清单(C:\sanguo_vnpy_v2\
| 组件 | 来源 | 位置 |
|------|------|------|
| vnpy 4.4.0 源码 | 项目内 `vnpy_v4.4.0/`(纯 Python,零 C 扩展,scp | `C:\sanguo_vnpy_v2\vnpy_v4.4.0\` |
| sanguo_* 大脑包 | 项目 tarscp | `C:\sanguo_vnpy_v2\sanguo_*\` |
| vnpy-qmt 0.3.3 | pip(阿里镜像) | site-packages |
| TA-Lib 0.6.8 | pipself-contained wheel | site-packages |
| vnpy_ctastrategy 1.4.1 / vnpy_sqlite 1.1.3 | pip | site-packages |
| pyarrow 24.0.0 / pandas / numpy / fastapi / uvicorn / sqlalchemy / apscheduler / auth(web) | pip | site-packages |
| 数据(策略标的 raw/qfq 日线 parquet | NAS 同步→VPS | `C:\sanguo_vnpy_v2\data\{raw,qfq}\{year}\` |
> **pip 镜像必配**VPS 默认直连 pypi.org(从国内极慢/超时)。已配
> `pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/`(写入
> `C:\Users\Administrator\AppData\Roaming\pip\pip.ini`)。
## 配置本地化(env 覆盖,保 git 真相)
不改部署态 yaml,全部走环境变量(代码见 `sanguo_data/config.py::load_config` +
`sanguo_api/main.py::build_app`):
| env | 作用 | VPS 值 |
|-----|------|--------|
| `SANGUO_DATA_ROOT` | 重映射 daily/raw/qfq/15min*/vnpy_db 到该根下 | `C:\sanguo_vnpy_v2\data` |
| `SANGUO_DB_PATH` | API 的 db_pathpaper_* 表+回测结果) | `C:\sanguo_vnpy_v2\data\backtest_results.db` |
| `SANGUO_LIVE_ENABLED` | live 总开关 | `true` |
| `SANGUO_BRIDGE_URL` | 影子下单 bridge 地址(**本机**,不再走 Mac tunnel | `http://127.0.0.1:8765` |
| `BRIDGE_TOKEN` | bridge 鉴权(`live_orchestrator` 已有 env fallback | `<BRIDGE_TOKEN>` |
不设这些 envNAS/Mac)则用 yaml 原值 → 三机共用同一份 git config,零漂移。
## 服务(schtasks,脱离 ssh 持久,开机自启)
| 任务 | 命令 | 端口 |
|------|------|------|
| `sanguo-api` | `powershell -File C:\sanguo_vnpy_v2\run_api.ps1`(设 env + `run_web.py` | 8000`/docs``/api/v1/*` |
| `sanguo-bridge` | `C:\run_bridge.ps1`xtquant→miniQMT 执行通道) | 8765 |
| `sanguo-caddy` | `C:\run_caddy.ps1`(反代 bridge.mysanguo.top | 80/443 |
管理(同 bridge):
```bash
ssh -i ~/.ssh/id_ed25519 Administrator@49.232.102.198 \
"schtasks /end /tn sanguo-api; schtasks /run /tn sanguo-api"
```
## 验证记录(2026-07-15,全部通过)
1. `import sanguo_api.main` + `create_app()` — 大脑原生可导入。
2. schtasks sanguo-api 常驻,`:8000/docs` → 200。
3. DB 自动建库(init_db 幂等),6 张 paper_* 表齐。
4. **live_step2026-07-13)全链路**warmup 重放 2024-01-01→07-12 → 读 raw/qfq bar
pyarrow)→ PaperEngine.step → 存 daily_balancecash=1M/equity=1M)。当日 DoubleMa
无交叉信号→无交易(正常)。
5. **影子 transport**brain 的 `BridgeClient(localhost:8765).get_account/get_positions`
读到真实 miniQMT 账户/持仓 → brain→本机 bridge→miniQMT transport 通(POST /order 同
transport,策略出信号即镜像)。
## 数据 stagingNAS→VPS
NAS 是数据权威(baostock 5.5 年干净数据)。策略标的的 raw+qfq 日线 parquet 从 NAS
rsync 到 Mac 再 scp 到 VPS`{raw,qfq}/{year}/sh{sym}_daily.parquet`,每股全历史 ~200KB)。
当前已 staging6000002021-2026)。新增标的照此同步。
> 光猫拦 NAS→VPS,故走 NAS→Mac→VPS 两跳。Mac tunnel(旧 NAS→VPS bridge 通道)**已废弃**
> (brain 在 VPS 本机,不再需要)。
## Phase 2vnpy_qmt 进程内换 bridge)—— 暂缓
用户初衷"vnpy_qmt 直接对接 miniQMT、bridge 废弃"针对的是 **bridge 跨机依赖**Mac tunnel)。
现在 brain+bridge **同机 VPS**(localhost),跨机问题已消除,bridge 退化为本机执行适配器
(仍 vnpy 原生、wrap xtquant)。进程内换 vnpy_qmt 的收益仅是少一个本地进程 + 一个 localhost
HTTP 跳(延迟可忽略),代价是要写 async→sync 适配器 + 把 xtquant 生命周期塞进 brain 进程
xtquant 崩溃会拖垮 brain,反而损失进程隔离)。**故暂缓**,bridge 保留为本机执行通道。
待 bridge 本机化成为实测瓶颈再重启 Phase 2。
+2 -2
View File
@@ -42,8 +42,8 @@ def build_app(config_path: str = "config/backtest.yaml", static_dir: str | None
auth = cfg.get("auth", {})
pool = cfg.get("pool", {})
db_path = bt.get("db_path", "/tmp/backtest_results.db")
file_dir = bt.get("file_dir", "/tmp/backtest_files")
db_path = os.environ.get("SANGUO_DB_PATH") or bt.get("db_path", "/tmp/backtest_results.db")
file_dir = os.environ.get("SANGUO_FILE_DIR") or bt.get("file_dir", "/tmp/backtest_files")
auth_config = None
if auth:
auth_config = {
+25 -2
View File
@@ -24,13 +24,36 @@ def load_config(path: str) -> DataConfig:
if not raw:
raise ValueError(f"配置文件为空: {path}")
data_paths = dict(raw.get("data_paths", {}))
# 部署环境路径覆盖(VPS native 等):SANGUO_DATA_ROOT 设定时,*_dir 与 vnpy_db
# 重映射到该根下,保持 datareader 期望的叶子结构(daily/raw/qfq/15min*/quant_trading.db)。
# 不设则用 yaml 原值(NAS/Mac)。避免改部署态配置文件,保 git 真相。
root = os.environ.get("SANGUO_DATA_ROOT")
if root:
_leaf = {"daily_dir": "daily", "raw_dir": "raw", "qfq_dir": "qfq",
"minute_15_dir": "15min", "minute_15_qfq_dir": "15min_qfq",
"minute_15_raw_dir": "15min_raw"}
for _k, _suf in _leaf.items():
if _k in data_paths:
data_paths[_k] = os.path.join(root, _suf)
if "vnpy_db" in data_paths:
data_paths["vnpy_db"] = os.path.join(root, "quant_trading.db")
live = raw.get("live")
if isinstance(live, dict):
live = dict(live)
if os.environ.get("SANGUO_LIVE_ENABLED"):
live["enabled"] = os.environ["SANGUO_LIVE_ENABLED"].strip().lower() in ("1", "true", "yes", "on")
if os.environ.get("SANGUO_BRIDGE_URL"):
live["bridge_url"] = os.environ["SANGUO_BRIDGE_URL"].strip()
return DataConfig(
data_paths=raw.get("data_paths", {}),
data_paths=data_paths,
data_sources=raw.get("data_sources", {}),
validation=raw.get("validation", {}),
performance=raw.get("performance", {}),
risk_free_rate=float(raw.get("risk_free_rate", 0.02)),
live=raw.get("live"),
live=live,
)