# 多账户交易中控 — 部署文档（含 PM2）

本文档说明在 **Ubuntu / Linux** 上部署 **manual_trading_hub**（复盘系统中控：监控区、系统设置、登录保护）的推荐步骤。

- 功能与界面：[使用说明.md](./使用说明.md)
- **云服务器（VPS）完整配置**：[云服务器部署说明.md](./云服务器部署说明.md)
- **本地备份并迁到云上**：[本地数据迁移到云端.md](./本地数据迁移到云端.md)
- **局域网 IP:端口 / 反代域名、中控打开实例免登录**：[局域网与反代部署说明.md](./局域网与反代部署说明.md)
- 故障实录：[常见问题.md](./常见问题.md)
- 环境变量模板：[.env.example](./.env.example)

---

## 一、部署目标

| 组件 | 作用 | 默认监听 |
|------|------|----------|
| **hub.py** | 中控 Web + API | `0.0.0.0:5100` |
| **agent.py × N** | 各账户持仓 / 紧急全平 | `127.0.0.1:15200`～`15203` |
| **crypto_monitor_*.app** | 策略、关键位、下单逻辑 | 各目录 `.env` 的 `APP_PORT` |

- 账户列表与 URL 由 **`hub_settings.json`**（网页「系统设置」保存）或内置默认维护；**不再使用** `HUB_AGENTS`。
- 四实例 Flask **无需为中控改业务代码**（已注册 `hub_bridge`）；与中控并行运行。

---

## 二、前置条件

1. **Python 3.10+**、`python3-venv`、`pip`。
2. **Node.js + npm**（用于安装 PM2）：`sudo npm i -g pm2`。
3. 各 `crypto_monitor_*` 目录已 **`cp .env.example .env`** 并填好 API 密钥。
4. 端口无冲突：`5100`、`15200`～`15203`、各实例 `APP_PORT`（5000/5001/5002/5004）。
5. 建议代码路径：`/opt/crypto_monitor/`（下文用此示例，请按实际路径替换）。

---

## 三、安装中控依赖

```bash
cd /opt/crypto_monitor/manual_trading_hub
python3 -m venv .venv
source .venv/bin/activate          # 激活 venv（当前终端后续 pip/python 走虚拟环境）
pip install -r requirements.txt
cp .env.example .env
# 编辑 .env：HUB_PORT、HUB_DISABLED_IDS、公网时 HUB_BRIDGE_TOKEN 等
chmod +x scripts/run_hub.sh scripts/run_agent.sh scripts/pm2_hub.sh scripts/pm2_agents.sh
chmod +x scripts/fix_hub_deps.sh scripts/verify_hub_deploy.sh scripts/fix_env_crlf.sh
deactivate                         # 可选；交给 PM2 时不必保持激活
```

**依赖说明**：`requirements.txt` 含 **`python-multipart`**（FastAPI 表单解析；旧版若保留下单接口时需要）、**`psutil`**（监控区服务器状态条）。当前中控**已移除下单区**，但仍建议安装完整依赖。

说明：

- **安装依赖**：必须在 **`manual_trading_hub/.venv`** 内执行，勿对系统 Python 直接 `pip install`（Debian/Ubuntu 会报 `externally-managed-environment`）。`source .venv/bin/activate` 后用 `pip install` 即可；不写 `activate` 时也可用 **`.venv/bin/pip install -r requirements.txt`**（效果相同）。快捷：`bash scripts/fix_hub_deps.sh`。
- **PM2 启动**：由 `scripts/run_hub.sh` 固定调用 **`.venv/bin/python`**，**不依赖**你是否在 shell 里激活过 venv。
- **监控磁盘**：可选环境变量 **`HUB_HOST_DISK_PATH`**（如 `/opt/crypto_monitor`），未设时 Linux 为 `/`、Windows 为系统盘。

子代理也使用 **本目录 `.venv`** 里的 Python（与各策略 Flask 的 venv 可分开）。

---

## 四、推荐启动顺序

```
1. 各实例 Flask（APP_PORT）      ← 各 crypto_monitor_* 目录 ecosystem.config.cjs
2. 中控 + 子代理（5100 + 15200～15203）  ← 本目录一条 PM2 命令同时启动
```

**`ecosystem.config.cjs` 会一次拉起 4 个 agent + 1 个 hub**，无需再单独 `pm2 start` 子代理。

仅反代中控到公网时：Flask / agent 仍只监听 **127.0.0.1**；系统设置里 URL 填 `http://127.0.0.1:端口`。

---

## 五、PM2 托管（hub + agent 一起启动，推荐）

### 5.1 一条命令启动全部

| 文件 | 包含进程 |
|------|----------|
| `ecosystem.config.cjs` | `manual-agent-binance` / `okx` / `gate` / `gate-bot` + **`manual-trading-hub`** |

`run_hub.sh` 加载 **`manual_trading_hub/.env`** 后执行 `hub.py`；各 agent 经 **`run_agent.sh`** 在对应策略目录加载 **`.env`**（含 API 密钥），再执行 `agent.py`。

```bash
cd /opt/crypto_monitor/manual_trading_hub
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env

pm2 start ecosystem.config.cjs    # 5 个进程一起起
pm2 save

# 或
bash scripts/pm2_hub.sh start
```

### 5.2 PM2 进程一览

| 进程名 | 工作目录 | 端口/说明 |
|--------|----------|-----------|
| manual-agent-binance | crypto_monitor_binance | agent `15200` |
| manual-agent-okx | crypto_monitor_okx | agent `15201` |
| manual-agent-gate | crypto_monitor_gate | agent `15202` |
| manual-agent-gate-bot | crypto_monitor_gate_bot | agent `15203` |
| manual-trading-hub | manual_trading_hub | hub `5100` |

OKX 子代理会启动，但中控默认 `HUB_DISABLED_IDS=1` 不参与监控；不用 OKX 可 `pm2 stop manual-agent-okx`。

### 5.3 常用运维命令

```bash
pm2 status
pm2 logs manual-trading-hub --lines 200
pm2 restart ecosystem.config.cjs     # 重启 hub + 全部 agent

bash scripts/pm2_hub.sh restart      # 同上
bash scripts/pm2_hub.sh stop
bash scripts/pm2_hub.sh logs
```

仅重启中控、不动 agent：

```bash
pm2 restart manual-trading-hub
```

仅重启子代理：

```bash
pm2 restart manual-agent-binance manual-agent-gate manual-agent-gate-bot
# 或
bash scripts/pm2_agents.sh restart
```

### 5.4 开机自启

```bash
pm2 save
pm2 startup
# 按终端提示执行一行 sudo 命令后，再 pm2 save
```

### 5.5 与各实例 Flask 一起查看

```bash
pm2 status
# 示例同时存在：
# manual-trading-hub、manual-agent-*
# crypto_binance / crypto_gate …（各策略目录自有 ecosystem.config.cjs）
```

### 5.6 Gate 子代理「一会能连、一会子代理不可用」（Windows `.env` 换行）

**现象**：仅 Gate 训练 / Gate 趋势卡片红字「子代理不可用」；`pm2 logs manual-agent-gate` 反复出现：

```text
./.env: line 22: $'\r': command not found
agent start: exchange=gate port=15202 ...
```

**原因**：在 Windows 编辑的 `crypto_monitor_gate/.env`、`crypto_monitor_gate_bot/.env` 为 **CRLF**，Linux 上 `source` 失败；PM2 反复重启，中控轮询时偶发连不上（**不是外网问题**）。

**处理**（在服务器仓库根执行）：

```bash
cd /opt/crypto_monitor
sed -i 's/\r$//' crypto_monitor_gate/.env crypto_monitor_gate_bot/.env
bash manual_trading_hub/scripts/fix_env_crlf.sh
cd manual_trading_hub
pm2 delete manual-agent-gate manual-agent-gate-bot 2>/dev/null || true
pm2 start ecosystem.config.cjs --only manual-agent-gate
pm2 start ecosystem.config.cjs --only manual-agent-gate-bot
pm2 save
curl -s http://127.0.0.1:15202/status | head -c 200   # 应 ok:true
```

**预防**：`.env` 保存为 **LF**（勿在 Windows 记事本直接保存 CRLF）。子代理须经 **`scripts/run_agent.sh`** 启动（内置去 CRLF 的 `load_dotenv_file`），勿裸跑 `python agent.py`。

详见 [常见问题.md](./常见问题.md) **§3.1**、**§3.3**。

---

## 六、手动启动（不用 PM2 时）

需**分别**起 agent 与 hub（与 PM2 合并启动不同）：

```bash
# 子代理：由 ecosystem.config.cjs 经 run_agent.sh 启动（勿用手动多终端）
# 中控：
cd /opt/crypto_monitor/manual_trading_hub
bash scripts/run_hub.sh
```

---

## 七、浏览器验收

1. **http://127.0.0.1:5100/login** — 若 `.env` 已设 `HUB_PASSWORD`，用 `HUB_USERNAME` / `HUB_PASSWORD` 登录。
2. **http://127.0.0.1:5100/monitor** — 已启用账户显示持仓；Flask 已起时有关键位/趋势信息。
3. **http://127.0.0.1:5100/market** — 行情区可选交易所与周期拉 K 线；升级后强刷浏览器，详见 [行情区说明.md](./行情区说明.md)。
4. **http://127.0.0.1:5100/ai** — AI 教练（四户今日总结 + 聊天）；`manual_trading_hub/.env` 配与四实例相同的 `AI_*` 变量，见 [AI教练说明.md](./AI教练说明.md)。
5. **http://127.0.0.1:5100/settings** — 保存后生成 `hub_settings.json`（增加第五户、Gate 子账户等见 [使用说明.md §4.5](./使用说明.md#45-增加账户例如再挂一个-gate)）。
5. 监控卡片 **「实例」** — 在各 `crypto_monitor_*` 网页做下单、关键位、趋势；中控**不提供**下单表单。

**命令行验收**（推荐）：

```bash
cd /opt/crypto_monitor/manual_trading_hub
bash scripts/verify_hub_deploy.sh
```

应看到：`OK: 无 api_trade_key`，`HTTP 200`，JSON 含 `"build":"20260521-no-trade-ui"`。

```bash
curl -s http://127.0.0.1:5100/api/ping
curl -s http://127.0.0.1:15200/status | head -c 200
```

---

## 八、仅反代中控到公网（实例不反代）

1. Nginx/Caddy 反代到 **`127.0.0.1:5100`**，配置 **HTTPS**。
2. 反代需传递（登录 Cookie 正确识别 HTTPS）：
   ```nginx
   proxy_set_header Host $host;
   proxy_set_header X-Forwarded-Proto $scheme;
   proxy_set_header X-Real-IP $remote_addr;
   ```
3. `manual_trading_hub/.env` **务必设置**：
   ```env
   HUB_USERNAME=你的用户名
   HUB_PASSWORD=你的强密码
   HUB_SESSION_SECRET=随机长串
   HUB_COOKIE_SECURE=true
   ```
4. `hub_settings.json` 中 Flask/Agent 保持 **`http://127.0.0.1:...`**（中控本机调 API）。
5. 四实例 **`APP_AUTH_DISABLED=false`** + 与中控相同 **`HUB_BRIDGE_TOKEN`**。
6. 子代理 **`HOST=127.0.0.1`**；防火墙勿对公网开放 `15200`～`15203`、各 `APP_PORT`。
7. **复盘/实例外链**：`HUB_PUBLIC_ORIGIN=https://你的域名` 或内网 IP；否则其它设备点「复盘」会跳到 `127.0.0.1`。

**说明**：HTTPS 域名与 HTTP `内网IP:5100` Cookie **不共用**；内网访问 IP 需在 IP 地址再登录一次（见 [常见问题.md](./常见问题.md) §2.1）。

---

## 九、环境变量（中控 `.env`）

| 变量 | 默认 | 说明 |
|------|------|------|
| `HUB_HOST` | `0.0.0.0` | 监听地址 |
| `HUB_PORT` | `5100` | 端口 |
| `HUB_DISABLED_IDS` | `1` | 强制关闭的账户 id（OKX） |
| `HUB_TRUST_LAN` | `true` | 私网可访问；仅本机可 `false` |
| `HUB_PUBLIC_ORIGIN` | 空 | 浏览器用复盘链接；如 `http://192.168.1.100`（**内网其它电脑访问中控时建议设置**） |
| `HUB_BRIDGE_TOKEN` | 空 | 与四实例一致；公网建议配置 |
| `HUB_USERNAME` | `admin` | Web 登录用户名 |
| `HUB_PASSWORD` | 空 | 非空即启用登录 |
| `HUB_SESSION_SECRET` | — | 会话签名 |
| `HUB_COOKIE_SECURE` | `false` | HTTPS 反代建议 `true` |
| `HUB_SESSION_DAYS` | `7` | 登录保持天数 |

本地联调、实例 `APP_AUTH_DISABLED=true` 时可不配 `HUB_BRIDGE_TOKEN`；本机不设 `HUB_PASSWORD` 则无需登录页。

---

## 十、升级与回滚

```bash
cd /opt/crypto_monitor
git pull

cd manual_trading_hub
bash scripts/fix_hub_deps.sh
bash scripts/verify_hub_deploy.sh

pm2 restart ecosystem.config.cjs
# 若只改了中控：pm2 restart manual-trading-hub
```

- **`hub_settings.json`**、**`hub_ai_summaries.json`**、**`hub_ai_chat.json`**、**`.env`** 不在 Git 中，`git pull` 不会覆盖。
- 升级前可备份：`cp hub_settings.json hub_settings.json.bak`、`cp hub_ai_*.json hub_ai_backup/`、`cp .env .env.bak`。

**升级后自检**：`curl -s http://127.0.0.1:5100/api/ping` 须含 `"trade_ui":false`。若仍见 `api_trade_key` 报错，说明代码未更新或未重启，见 [常见问题.md](./常见问题.md) §1。

---

## 十一、故障排查（速查）

**完整实录**见 **[常见问题.md](./常见问题.md)**。

| 现象 | 处理 |
|------|------|
| PM2 启动后立刻退出 | `pm2 logs manual-trading-hub`；检查 `.venv`、`.env`、`run_hub.sh` |
| `api_trade_key` / `python-multipart` | `git pull` → `bash scripts/fix_hub_deps.sh` → `verify_hub_deploy.sh` → 重启 hub |
| `verify` ping 解析失败 | hub 未起：`pm2 restart manual-trading-hub` |
| 余额显示 — | agent 未加载 `.env`；`fix_env_crlf.sh`；`run_agent.sh` |
| agent `$'\r': command not found` | `bash scripts/fix_env_crlf.sh` |
| 监控无持仓 | `curl http://127.0.0.1:15200/status` |
| 无关键位 / 401 | 启动 Flask；核对 `HUB_BRIDGE_TOKEN` / `hub_bridge` |
| 域名能登、IP 不能登 | 见常见问题 §2.1（Cookie / HTTP vs HTTPS） |
| 公网访问中控 403 | 反代到 `127.0.0.1:5100`，勿公网直连 5100 |
| 改 `.env` 不生效 | `pm2 restart manual-trading-hub` |

---

## 十二、进程托管说明

中控与子代理 **仅使用 PM2**（`ecosystem.config.cjs`）。勿再用 screen / systemd / nohup 启动 `hub.py` 或 `agent.py`，以免端口冲突。  
环境要求见 **[docs/ubuntu-server.md](../docs/ubuntu-server.md)**。

---

## 十三、安全清单

- [ ] 公网仅暴露反代端口，不暴露 Flask/agent 端口  
- [ ] 已设 `HUB_USERNAME` + `HUB_PASSWORD`（中控 Web 登录）  
- [ ] HTTPS 反代已设 `HUB_COOKIE_SECURE=true` 且传递 `X-Forwarded-Proto`  
- [ ] 公网已配置 `HUB_BRIDGE_TOKEN` + 实例关闭 `APP_AUTH_DISABLED`  
- [ ] API Key 最小权限；交易所 IP 白名单  
- [ ] 已告知操作人员「全局全平」不可撤销；中控**不在网页下单**  

---

## 十四、文档索引

| 文档 | 内容 |
|------|------|
| [使用说明.md](./使用说明.md) | 功能、页面、API、环境变量 |
| [常见问题.md](./常见问题.md) | 故障实录 |
| [README.md](./README.md) | 速览 |
| [.env.example](./.env.example) | 环境变量模板 |
| [scripts/后台运行-Ubuntu.md](./scripts/后台运行-Ubuntu.md) | PM2 常驻（唯一推荐） |
| [docs/ubuntu-server.md](../docs/ubuntu-server.md) | Ubuntu / Python / Node / PM2 |
| `scripts/fix_hub_deps.sh` | 安装依赖 |
| `scripts/verify_hub_deploy.sh` | 部署验收 |
| `scripts/fix_env_crlf.sh` | 修复 .env 换行 |
