# `crypto_monitor_binance` 部署指南：SSH SOCKS + Binance + PM2（Ubuntu）

项目功能、环境变量总览见 **[README.md](./README.md)**。Ubuntu 环境（Python / Node / PM2）见 **[docs/ubuntu-server.md](../docs/ubuntu-server.md)**。

本文面向：**在本机或 VPS 上运行本项目**，但 **直连 Binance API 不稳定、超时或被网络策略拦截** 的场景。思路是：

- 本机用 `ssh -D` 做动态转发，把 **SOCKS5 出口**放到能稳定访问 Binance 的机器（常见为一台境外 VPS）
- 项目在 `.env` 中设置 **`BINANCE_SOCKS_PROXY=socks5h://127.0.0.1:1080`**（或你实际端口），`ccxt` 经 SOCKS 访问交易所
- **SSH 隧道**：用 `ssh -D` 在本机常驻（可用 **tmux** 或 **autossh** 保持连接），**不要** 把 `ssh` 交给 PM2
- 使用 **PM2** 仅托管 **Flask 应用**；仓库根目录 **`ecosystem.config.cjs`** 默认进程名为 **`crypto-monitor-binance`**

> 安全提醒：不要把 `.env`、私钥 `.pem`、Binance API Key / Secret 提交到 Git；下文只用占位符。

---

## 0. 你需要准备的东西

- 一台 **Ubuntu**（或同类 Linux）运行项目的机器（下文称「本机」）
- 一台可 SSH 登录、且 **能正常访问 Binance API** 的 VPS（示例：`HostName` 填你的服务器 IP，用户如 `root`）
- SSH：**私钥登录**（推荐，便于隧道脚本无人值守）
- 本机已安装：`python3`、`python3-venv`、`pip`、`curl`、`ssh`、`git`（可选）、`node` + `npm`（安装 PM2）
- Binance 账户：已开通 **USDT-M 永续合约**；API Key 勾选 **合约**、**万向划转**（若使用资金↔合约划转）等所需权限，并配置 **IP 白名单**（若启用）

---

## 1. 获取代码与目录

将包含 `app.py` 的项目放到固定目录，例如：

```bash
mkdir -p /opt/crypto_monitor
cd /opt/crypto_monitor
git clone https://git.bz121.com/dekun/crypto_monitor.git
cd crypto_monitor/crypto_monitor_binance
```

下文用 **`/opt/crypto_monitor/crypto_monitor_binance`** 仅为示例，请换成你的实际绝对路径。

拉取代码后，若目录下尚无 `.env`，先从模板生成（**勿**把填好密钥的 `.env` 提交 Git）：

```bash
cp -n .env.example .env    # -n：已存在 .env 时不覆盖
```

---

## 2. 配置 SSH 私钥与 `~/.ssh/config`

```bash
mkdir -p ~/.ssh
chmod 700 ~/.ssh
# 私钥示例：~/.ssh/vps1.pem
chmod 600 ~/.ssh/vps1.pem
```

编辑 `~/.ssh/config`（示例别名 **`bn-vps`**，与你手工启动 `ssh -D ... bn-vps` 一致即可）：

```sshconfig
Host bn-vps
  HostName 你的_VPS_IP
  User root
  IdentityFile ~/.ssh/vps1.pem
  IdentitiesOnly yes
  ServerAliveInterval 30
  ServerAliveCountMax 3
  ExitOnForwardFailure yes
  BatchMode yes
```

测试：

```bash
ssh bn-vps true
```

> 若尚未完全改为密钥登录，可暂时注释 `BatchMode yes`，调试完成后再打开。

---

## 3. 手工验证：SSH SOCKS + Binance API

### 3.1 本地 SOCKS（示例端口 1080）

```bash
ssh -N -D 127.0.0.1:1080 bn-vps
```

保持运行，另开终端继续。

### 3.2 验证经 SOCKS 可访问 Binance（公开接口）

```bash
curl -4 -sS --max-time 15 --proxy socks5h://127.0.0.1:1080 https://api.binance.com/api/v3/time
```

应返回 JSON（含 `serverTime` 字段）。若此处失败，**不要先启动应用**：先修隧道或 VPS 出站。

---

## 4. Python 虚拟环境

```bash
cd /opt/crypto_monitor/crypto_monitor_binance

python3 -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
pip install flask requests ccxt werkzeug PySocks Pillow
```

走 SOCKS 时 **必须** 安装 **`PySocks`**，否则易出现代理相关报错。

可选：

```bash
export PYTHONDONTWRITEBYTECODE=1
```

---

## 5. 配置环境变量（`.env.example` → `.env`）

| 文件 | 是否进 Git | 说明 |
|------|------------|------|
| **`.env.example`** | ✅ 是 | 变量模板与注释，可随 `git pull` 更新 |
| **`.env`** | ❌ 否 | 本机真实配置；`app.py` **只读此文件** |

### 5.1 首次配置

```bash
cd /opt/crypto_monitor/crypto_monitor_binance

cp -n .env.example .env    # 已存在 .env 时不覆盖
nano .env                  # 填入 API、登录密码、端口、代理等
```

### 5.2 备份与 `git pull`

- **`.env` 已被仓库根目录 `.gitignore` 忽略**：`git pull` **不会**覆盖或删除你本地的 `.env`。
- 若远端更新了 **`.env.example`**（新增变量名），pull 后请对照模板，**手动把新行补进你的 `.env`**（不会自动合并进 `.env`）。
- **建议在每次 `git pull` 或大批量改配置前备份**：

```bash
cp .env .env.backup.$(date +%Y%m%d)
# 恢复示例：cp .env.backup.20260516 .env
```

- **换机 / 迁移**：用 `scp` 复制整份 `.env` 到新机器对应目录；或在新机重新 `cp .env.example .env` 后填写。

### 5.3 AI 复盘与模型（可选）

三所共用仓库根目录 **`ai_client.py`**（PM2 的 **`PYTHONPATH=..`** 须包含仓库根）。在 `.env` 中配置 **`AI_PROVIDER`**：

| 模式 | 主要变量 |
|------|----------|
| **`openai`**（默认） | `OPENAI_API_BASE=https://op.bz121.com/v1`、`OPENAI_API_KEY`、`OPENAI_MODEL=gemma4:e4b` |
| **`ollama`** | `OLLAMA_API`、`AI_MODEL`（本机 Ollama） |

密钥在 [op.bz121.com](https://op.bz121.com/) 的 **`gateway.json`** 页面获取。改 `.env` 后需 **`pm2 restart`** 对应进程。详见根目录 **[AI复盘与模型配置说明.md](../AI复盘与模型配置说明.md)**。

### 5.4 自动备份（数据库 + 复盘图片）

默认每天 **北京时间 0:00** 备份到 **`/root/backups`**，保留 **30 天** 后自动删除更早的目录。

备份内容（路径来自 `.env` 的 `DB_PATH`、`UPLOAD_DIR`）：

- `crypto.db`（优先 `sqlite3 .backup` 热备）
- `static/images` 打包为 `static_images.tar.gz`

目录结构示例：

```text
/root/backups/crypto_monitor_binance/2026-05-17/
  crypto.db
  static_images.tar.gz
  manifest.txt
```

**一次性安装定时任务**（在对应项目目录执行，Binance / Gate 各执行一次）：

```bash
cd /opt/crypto_monitor/crypto_monitor_binance
chmod +x scripts/backup_data.sh scripts/install_backup_cron.sh
bash scripts/install_backup_cron.sh
```

Gate 实例：

```bash
cd /opt/crypto_monitor/crypto_monitor_gate
bash scripts/install_backup_cron.sh
```

实例（趋势回调等）：

```bash
cd /opt/crypto_monitor/crypto_monitor_gate
bash scripts/install_backup_cron.sh
```

**立即试跑**（不写 cron）：

```bash
bash scripts/backup_data.sh
```

日志默认：`/var/log/crypto-monitor-backup-<项目目录名>.log`。可选在 `.env` 中覆盖：`BACKUP_ROOT`、`BACKUP_RETENTION_DAYS`、`BACKUP_INSTANCE`。

**恢复示例**（先停 PM2，再覆盖文件）：

```bash
pm2 stop crypto-monitor-binance
cp /root/backups/crypto_monitor_binance/2026-05-16/crypto.db ./crypto.db
tar -xzf /root/backups/crypto_monitor_binance/2026-05-16/static_images.tar.gz -C .
pm2 start ecosystem.config.cjs
```

建议安装：`apt install -y sqlite3`（热备更稳）。

### 5.5 必填项检查（Binance + 代理）

与交易所相关的变量使用 **`BINANCE_`** 前缀（与代码一致）。至少确认：

```env
APP_HOST=127.0.0.1
APP_PORT=5000

# 实盘（按需）
LIVE_TRADING_ENABLED=false
BINANCE_API_KEY=你的_Key
BINANCE_API_SECRET=你的_Secret

# 保证金：cross=全仓 isolated=逐仓（与币安账户/习惯一致）
BINANCE_MARGIN_MODE=cross

# 持仓模式：hedge=双向（需在币安开启双向持仓）；oneway=单向
BINANCE_POSITION_MODE=hedge

# 条件单触发参考：CONTRACT_PRICE=最新成交价 MARK_PRICE=标记价
BINANCE_TRIGGER_WORKING_TYPE=CONTRACT_PRICE

# 经本机 SSH 动态转发访问 Binance（端口与隧道一致）
BINANCE_SOCKS_PROXY=socks5h://127.0.0.1:1080

# 若不用 SOCKS，可改用 HTTP 代理（一般二选一）
# BINANCE_HTTP_PROXY=http://127.0.0.1:7890
# BINANCE_HTTPS_PROXY=http://127.0.0.1:7890
```

说明：**推荐 `socks5h://`**，由 SOCKS 端解析域名，与 `curl --proxy socks5h://...` 行为一致。

**止盈止损说明（应用逻辑）**：实盘开仓后，程序会在 Binance USDT-M 永续上挂 **`STOP_MARKET`（止损）** 与 **`TAKE_PROFIT_MARKET`（止盈）**；`BINANCE_POSITION_MODE=hedge` 时会自动带 **`positionSide`**，须与币安合约「双向持仓」开关一致。不显式传 **`reduceOnly`**（否则易触发 API **`-1106`**：`Parameter 'reduceOnly' sent when not required`）。

---

## 6. 自检脚本（可选）

在已配置 `.env` 且网络可达的前提下：

```bash
cd /opt/crypto_monitor/crypto_monitor_binance
source .venv/bin/activate
python scripts/verify_binance_funding.py
```

用于粗测资金钱包与合约钱包 USDT 读取（需有效 API 与权限）。

---

## 7. 手工启动 Flask（验证）

1. SOCKS 已监听 `127.0.0.1:1080`（若使用代理）
2. 已 `source .venv/bin/activate`
3. `.env` 已按需配置 `BINANCE_SOCKS_PROXY` 等

```bash
cd /opt/crypto_monitor/crypto_monitor_binance
source .venv/bin/activate
python app.py
```

浏览器访问：`http://127.0.0.1:5000`（或你在 `.env` 中的端口）。

---

## 8. 安装 PM2

```bash
sudo npm i -g pm2
pm2 -v
```

---

## 9. PM2：使用仓库内 `ecosystem.config.cjs`（推荐）

在项目根目录：

```bash
cd /opt/crypto_monitor/crypto_monitor_binance
pm2 start ecosystem.config.cjs
pm2 status
pm2 logs --lines 200
```

默认只启动 **`crypto-monitor-binance`**（`.venv/bin/python app.py`）。

### 本机已可直连 Binance、不需要隧道时

`.env` 里应 **去掉或留空** `BINANCE_SOCKS_PROXY`（除非仍要走别的代理），再 `pm2 start ecosystem.config.cjs`。

### 开机自启

```bash
pm2 save
pm2 startup
# 按屏幕提示执行一条 sudo 命令
```

---

## 10. 等价手工命令（不使用 ecosystem 文件时）

### 10.1 SSH SOCKS（自行后台常驻，不推荐用 PM2）

示例（前台调试；生产请用 **PM2**，见本文 §6 与 [docs/ubuntu-server.md](../docs/ubuntu-server.md)）：

```bash
ssh -N -D 127.0.0.1:1080 bn-vps \
  -o ServerAliveInterval=30 -o ServerAliveCountMax=3 \
  -o ExitOnForwardFailure=yes
```

### 10.2 Flask

```bash
cd /opt/crypto_monitor/crypto_monitor_binance
pm2 start /opt/crypto_monitor/crypto_monitor_binance/.venv/bin/python --name crypto-monitor-binance -- \
  /opt/crypto_monitor/crypto_monitor_binance/app.py
```

---

## 11. 交易所「连接不上」排查清单

1. **`.env` 是否为 Binance 变量**：`BINANCE_SOCKS_PROXY` / `BINANCE_HTTP_PROXY` / `BINANCE_API_KEY` / `BINANCE_API_SECRET` 等前缀需与代码一致。
2. **隧道是否在本机端口监听**（若配置了 `BINANCE_SOCKS_PROXY`）：
   ```bash
   ss -lntp | grep 1080 || true
   ```
3. **curl 复测 Binance**（与第 3.2 节相同）；curl 不通则应用也不会通。
4. **PySocks**：`pip show PySocks`，缺失则 `pip install PySocks`。
5. **SSH 隧道连不上**：检查私钥权限、`~/.ssh/config`、VPS 出站与端口是否与 `.env` 一致。
6. **API 权限与 IP 白名单**：Secret 错误、权限不足、未放行当前出口 IP 时，私有接口会失败。
7. **启动顺序**：若走代理，先保证 SOCKS 已监听，再 `pm2 start` 应用（或重启应用）。

---

## 12. 推荐启动顺序（习惯）

1. 若走代理：先启动并确认 SSH SOCKS 已监听，再 `curl --proxy socks5h://127.0.0.1:1080 https://api.binance.com/api/v3/time` 成功
2. `pm2 start ecosystem.config.cjs`
3. 再确认页面与余额等接口正常

---

## 13. 免责声明

交易所有合规与地区政策要求。请确保使用方式符合当地法律法规与交易所条款。本文仅描述网络与工程部署路径。

---

## 附录：数据库标签修复脚本 `scripts/fix_breakeven_labels.py`

在 Ubuntu 上：

1）预览（不写库）：

```bash
python scripts/fix_breakeven_labels.py --db ./crypto.db --dry-run
```

2）确认后执行：

```bash
python scripts/fix_breakeven_labels.py --db ./crypto.db --apply
```

默认修复条件：`monitor_type='下单监控'` 且 `result='止损'` 且 `pnl_amount > 0` → 改为 `result='保本止盈'`。
