风险提示:虚拟货币不具有法定货币等同的法律地位,参与虚拟货币投资交易存在法律风险,继续浏览代表你同意以上所有声明,否则请立即关闭本站!

Bybit Live MVP 部署说明(PM2 维护进程)

文档版本:与当前仓库一致(多策略机共库 machine_idMVP_ENGINES_JSON、中控默认端口 9000、Docker 中控 5000)。

本文说明在 Linux 服务器(推荐 Ubuntu 22.04 LTS)上使用 PostgreSQL + PM2 部署 策略机(后端 FastAPI)中控(Flask) 的流程,以及常见问题处理。

重要:策略机进程内通过 守护线程 启动实盘决策循环(main.py_engine_loop)。必须使用单 worker 的 Uvicorn--workers 1),否则多进程会重复跑循环,导致重复请求交易所等异常行为。

零、术语速查(与《架构说明》一致)

术语指什么本仓库路径PM2 示例应用名(下文)
策略机跑实盘引擎的进程:FastAPI + 单 worker Uvicorn + 内置 _engine_loopbackend/bybit-mvp-backend
中控只负责 Web 面板与 HTTP 调策略机,不下单control/bybit-mvp-control

最小可运行组合:一台机器上 PostgreSQL + 策略机 + 中控(三个角色;PG 可与策略机同机)。
分机部署:策略机单独一台(装 PG 或连远程 PG),中控另一台,用 MVP_ENGINES_JSON(或单项 MVP_ENGINE_BASE_URL)指向各策略机地址,并配置好 Token;不要在多进程或多机上对 同一 Bybit 账户 各起一套引擎。
单机多策略机:多个 Uvicorn 进程(不同端口)共用 同一 DB_URL,每台配置不同 MACHINE_ID不同 API Key(建议不同子账户);中控用 MVP_ENGINES_JSON 列出各 base_urlmachine_id。详见 §2.6§4.1~4.2 与仓库 control/.env.example

逻辑关系、拓扑图与检查清单见 架构说明.md 第一章。


一、架构与端口(PM2 场景)

组件角色说明端口约定
PostgreSQL数据层多策略机可 共用一个库;表中带 machine_id 区分实例5432
Backend(Uvicorn)策略机每进程一个:FastAPI + 内置引擎循环;--workers 1单实例常见 8000;单机多进程示例 9001 / 9002 / 9003(与 control/.env.example 一致即可)
Control(Flask)中控统一操作面板,按 MVP_ENGINES_JSON 轮询各策略机config_control 默认 9000infra/docker-compose.yml 内为 5000

中控连接策略机的方式(二选一,不要同时混用导致重复列表——以 MVP_ENGINES_JSON 为准):

  1. MVP_ENGINES_JSON(推荐,多台):JSON 数组,每项至少 base_urlmachine_idname,可选 tokenip
  2. 单项:不设 MVP_ENGINES_JSON、且 MVP_ENGINE_ENABLED=true 时,使用 MVP_ENGINE_BASE_URL + MVP_ENGINE_MACHINE_ID 等原有变量(等价于只有一台)。

二、环境准备(先策略机,后中控)

以下顺序对应 先让策略机可连库、可连交易所,再配置中控指向该策略机。

2.1 系统依赖

  • Python 3.11+(与项目一致即可,推荐 3.12)
  • Node.js 18+(仅用于安装 PM2:npm i -g pm2
  • PostgreSQL 14+(推荐 16)
  • Git

2.2 安装 PostgreSQL 服务(apt)与开机自启

策略机所在机器(或与策略机同机、本机监听 127.0.0.1:5432 的数据库机)上安装服务端(不是只装客户端):

apt update
apt install -y postgresql postgresql-contrib
systemctl enable postgresql
systemctl start postgresql
systemctl status postgresql
  • systemctl enable postgresql:把 PostgreSQL 注册为 systemd 开机自启。Ubuntu 用 apt install postgresql 后多数已默认 enable,仍建议显式执行一次,避免个别镜像未打开自启。
  • systemctl is-enabled postgresql 输出 enabled 即表示已随开机拉起。
  • 若使用本仓库 Docker 里的 postgres 容器:由 Docker 守护进程管理;宿主机 开机自启数据库 需保证 docker 服务已 systemctl enable docker,且 Compose 中服务使用 restart: unless-stopped(本仓库 infra/docker-compose.yml 已配置)。

确认监听:

ss -lntp | grep 5432

2.3 创建数据库与用户

以下 SQL 必须在 psql 里执行,不能贴在 root@...# 的 shell 里直接回车(否则会报 CREATE: command not found)。

进入 psql(Ubuntu 常见)

cd /tmp
sudo -u postgres psql

若提示 could not change directory to "/root": Permission denied,可忽略;用 cd /tmp 再进 psql 可避免该提示。

postgres=# 提示符下(注意:若是 postgres'# 表示上一句字符串未闭合,先按 Ctrl+C 清掉或 Ctrl+D 退出后重进),执行:

CREATE USER bybit_mvp WITH PASSWORD '你的强密码';
CREATE DATABASE bybit_live_mvp OWNER bybit_mvp;
\q
  • 密码必须用 成对的英文单引号 包住:'密码内容';漏写结尾 ' 会导致提示符变成 postgres'# 且后续命令无效。
  • 密码里含 @:在 SQL 中写 'woaini88@' 即可;写入 backend/.envDB_URL 时,@ 需改为 URL 编码 %40,例如 postgresql+psycopg2://bybit_mvp:woaini88%40@127.0.0.1:5432/bybit_live_mvp

2.4 拉取代码与虚拟环境

cd /opt   # 或你的部署目录
git clone <你的仓库地址> bybit-live-mvp
cd bybit-live-mvp

# 策略机 + 中控共用虚拟环境(推荐)
python3 -m venv .venv
source .venv/bin/activate
pip install -r backend/requirements.txt

# 若中控单独 venv:可在 control/ 下再 venv;此处假设与策略机共用根目录 .venv
pip install -r control/requirements.txt

2.5 策略机(backend)环境变量

策略机 必须backend/ 目录下读取 .env(与 app.core.settings 一致)。

cp backend/.env.example backend/.env

编辑 backend/.env,至少配置:

  • DB_URL:例如 postgresql+psycopg2://bybit_mvp:密码@127.0.0.1:5432/bybit_live_mvp
  • MACHINE_ID:多策略机共库时 每台进程不同(如 mvp-1),与中控 MVP_ENGINES_JSON 里的 machine_id 一致;单实例可保留默认 mvp-live
  • BYBIT_API_KEY / BYBIT_API_SECRET
  • 策略与风控相关:SYMBOLRISK_PCT_PER_TRADEMAX_DAILY_LOSS_USDT 等(见示例文件说明)

生产建议:设置 CONTROL_API_TOKEN(非空则保护 /api/engine/*/api/engine/state)。中控需配置 相同 的 Bearer Token(见下文 MVP_ENGINE_TOKEN)。
可选通知WECHAT_WEBHOOK_URL / WECHAT_NOTIFY_ENABLE(企业微信群机器人,见 backend/.env.example)。

策略机建表(仅在该机器执行即可)

cd backend
source ../.venv/bin/activate
python scripts/init_db.py

已有旧库升级到「多机共库」:在 backend 目录执行一次 python scripts/migrate_add_machine_id.py(PostgreSQL),再为各进程配置不同 MACHINE_ID 与 Bybit Key。

2.6 中控(control)环境变量

中控读取 control/config_control.py进程环境变量。在 PM2 或 shell 中建议显式设置:

变量含义
MVP_ENGINES_JSON多台策略机:JSON 数组,每项含 base_urlmachine_idname、可选 token/ip;与各自进程的 MACHINE_ID 对应。示例见仓库 control/.env.example
MVP_ENGINE_BASE_URL未配置 MVP_ENGINES_JSONMVP_ENGINE_ENABLED=true 时的 单台 策略机地址
MVP_ENGINE_TOKEN与策略机 CONTROL_API_TOKEN 一致(多机时也可写在 JSON 各条的 token
CONTROL_HOST默认 0.0.0.0
CONTROL_PORT默认 9000(Docker Compose 内会设为 5000
CONTROL_ADMIN_USERNAME / CONTROL_ADMIN_PASSWORD_HASH / CONTROL_SECRET_KEY生产务必修改(勿使用仓库默认值)

三、安装 PM2

npm install -g pm2
pm2 startup systemd -u $USER --hp $HOME   # 按提示执行 sudo 命令,实现开机自启

四、PM2 进程配置示例

在仓库根目录创建 ecosystem.config.cjs(可加入 .gitignore)。策略机与中控 cwd 不同,可共用根目录 .venv
Pydantic Settings 会 用进程环境变量覆盖 backend/.env 中同名项,故多进程时可在 PM2 各 app 的 env 里写 MACHINE_IDBYBIT_API_KEY 等(密钥勿提交仓库)。

4.1 单策略机 + 中控(最简)

const path = require("path");
const root = __dirname;
const venvBin = path.join(root, ".venv", "bin");
const uvicorn = path.join(venvBin, "uvicorn");
const python = path.join(venvBin, "python3");

module.exports = {
  apps: [
    {
      name: "bybit-mvp-backend",
      cwd: path.join(root, "backend"),
      script: uvicorn,
      args: "app.main:app --host 0.0.0.0 --port 8000 --workers 1",
      env: { PYTHONUNBUFFERED: "1" },
      max_restarts: 10,
      min_uptime: "10s",
      error_file: path.join(root, "logs", "pm2-backend-error.log"),
      out_file: path.join(root, "logs", "pm2-backend-out.log"),
    },
    {
      name: "bybit-mvp-control",
      cwd: path.join(root, "control"),
      script: path.join(root, "control", "run_control.py"),
      interpreter: python,
      env: {
        PYTHONUNBUFFERED: "1",
        CONTROL_PORT: "9000",
        MVP_ENGINE_BASE_URL: "http://127.0.0.1:8000",
        // MVP_ENGINE_TOKEN: "与 backend/.env 中 CONTROL_API_TOKEN 一致",
      },
      max_restarts: 10,
      min_uptime: "10s",
      error_file: path.join(root, "logs", "pm2-control-error.log"),
      out_file: path.join(root, "logs", "pm2-control-out.log"),
    },
  ],
};

4.2 三策略机(9001–9003)+ 中控(9000)+ 共库

  • :三个进程 同一 DB_URL;表内用 machine_id 区分。
  • 策略机:每进程 MACHINE_ID 不同Bybit Key 不同(勿多进程共用一个交易账户)。
  • 中控CONTROL_PORT=9000MVP_ENGINES_JSON 与上述端口、machine_id 一致。完整 JSON 示例见 control/.env.example。在 ecosystem.config.cjs 里可用 JSON.stringify([...]) 避免手写转义。
const path = require("path");
const root = __dirname;
const venvBin = path.join(root, ".venv", "bin");
const uvicorn = path.join(venvBin, "uvicorn");
const python = path.join(venvBin, "python3");

const backendEnv = (machineId) => ({
  PYTHONUNBUFFERED: "1",
  MACHINE_ID: machineId,
  // BYBIT_API_KEY / BYBIT_API_SECRET 建议在此注入,勿三进程共用同一 Key
});

module.exports = {
  apps: [
    {
      name: "bybit-mvp-backend-1",
      cwd: path.join(root, "backend"),
      script: uvicorn,
      args: "app.main:app --host 0.0.0.0 --port 9001 --workers 1",
      env: backendEnv("mvp-1"),
      max_restarts: 10,
      min_uptime: "10s",
      error_file: path.join(root, "logs", "pm2-b1-error.log"),
      out_file: path.join(root, "logs", "pm2-b1-out.log"),
    },
    {
      name: "bybit-mvp-backend-2",
      cwd: path.join(root, "backend"),
      script: uvicorn,
      args: "app.main:app --host 0.0.0.0 --port 9002 --workers 1",
      env: backendEnv("mvp-2"),
      max_restarts: 10,
      min_uptime: "10s",
      error_file: path.join(root, "logs", "pm2-b2-error.log"),
      out_file: path.join(root, "logs", "pm2-b2-out.log"),
    },
    {
      name: "bybit-mvp-backend-3",
      cwd: path.join(root, "backend"),
      script: uvicorn,
      args: "app.main:app --host 0.0.0.0 --port 9003 --workers 1",
      env: backendEnv("mvp-3"),
      max_restarts: 10,
      min_uptime: "10s",
      error_file: path.join(root, "logs", "pm2-b3-error.log"),
      out_file: path.join(root, "logs", "pm2-b3-out.log"),
    },
    {
      name: "bybit-mvp-control",
      cwd: path.join(root, "control"),
      script: path.join(root, "control", "run_control.py"),
      interpreter: python,
      env: {
        PYTHONUNBUFFERED: "1",
        CONTROL_PORT: "9000",
        MVP_ENGINES_JSON: JSON.stringify([
          { base_url: "http://127.0.0.1:9001", machine_id: "mvp-1", name: "策略机-1", token: "", ip: "127.0.0.1" },
          { base_url: "http://127.0.0.1:9002", machine_id: "mvp-2", name: "策略机-2", token: "", ip: "127.0.0.1" },
          { base_url: "http://127.0.0.1:9003", machine_id: "mvp-3", name: "策略机-3", token: "", ip: "127.0.0.1" },
        ]),
      },
      max_restarts: 10,
      min_uptime: "10s",
      error_file: path.join(root, "logs", "pm2-control-error.log"),
      out_file: path.join(root, "logs", "pm2-control-out.log"),
    },
  ],
};

uvicorn 未安装到 venv 的 bin 下,可改为:script: pythonargs: "-m uvicorn app.main:app --host 0.0.0.0 --port <端口> --workers 1"(以 pm2 logs 实际拉起命令为准)。

创建日志目录并启动:

mkdir -p logs
pm2 start ecosystem.config.cjs
pm2 save
pm2 status

常用命令(多进程时替换应用名):

pm2 logs bybit-mvp-backend-1
pm2 logs bybit-mvp-control
pm2 restart bybit-mvp-backend-1
pm2 restart bybit-mvp-control
pm2 stop all

五、部署后自检

  1. 策略机健康:对每个监听端口执行
    curl -s http://127.0.0.1:<端口>/api/health
    返回 JSON 中 ok: true,且含 machine_id 字段(应与该进程 MACHINE_ID 一致)。
  2. 引擎状态(若设置了 CONTROL_API_TOKEN):
    curl -s -H "Authorization: Bearer <token>" http://127.0.0.1:<端口>/api/engine/state
    响应中同样含 machine_id
  3. 中控:浏览器访问 http://服务器IP:<CONTROL_PORT>(PM2 示例为 9000;Docker 为 5000)。登录后应出现 多台 卡片(MVP_ENGINES_JSON)或 一台(单项 MVP_ENGINE_BASE_URL)。若某台离线,检查对应端口策略机是否启动、JSON 中 base_url / machine_id 是否与 backend/.env 一致。
  4. 企业微信等(若已配置):策略机启动推送中会带 机器标识,便于区分多实例。

六、防火墙与安全组

  • 按实际端口放行:单实例常见 8000(策略机)+ 9000(中控,PM2 默认);多实例示例 9001–9003 + 9000
  • 生产可 只对外开放中控端口,策略机端口 仅本机或仅中控所在安全组 访问。
  • 使用 HTTPS 反向代理(Nginx / Caddy)时,将 TLS 终止在代理层,后端仍监听本机端口。

七、Windows 说明(简要)

PM2 对 原生 Windows 支持有限,常见做法:

  1. 使用 WSL2(Ubuntu)按上文 Linux 流程部署;或
  2. 使用 NSSM / Windows 服务uvicornpython run_control.py 注册为服务。

若必须在 Windows 本机调试,可直接前台运行:

cd backend
..\.venv\Scripts\python -m uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 1

另开终端(中控端口默认 9000;若需 5000 请设置 CONTROL_PORT):

cd control
$env:CONTROL_PORT="9000"
$env:MVP_ENGINE_BASE_URL="http://127.0.0.1:8000"
..\.venv\Scripts\python run_control.py

多策略机时可在当前会话设置 MVP_ENGINES_JSON(单行 JSON 字符串),与 Linux 行为一致。


八、常见问题与处理

8.1 PM2 状态 errored 或不断重启

  • 查看日志pm2 logs <应用名> --lines 200
  • 虚拟环境路径错误:确认 ecosystem.config.cjsinterpreter 指向的 python3 存在且已 pip install -r backend/requirements.txt
  • 端口占用ss -lntp | grep 8000(或 9000 / 9001–9003 等你配置的端口),结束占用进程或改 ecosystem / 环境变量。
  • 工作目录错误cwd 必须为 backendcontrol,脚本才能找到 app 包与 config_control

8.2 策略机日志出现数据库连接失败

  • 检查 DB_URL 主机、端口、库名、用户密码。
  • 确认 PostgreSQL 已启动:systemctl status postgresql
  • 云服务器安全组是否放行 本机回环 一般无需;若 DB 在另一台机器,需放行对应 IP 的 5432。

8.3 relation "xxx" does not exist

未执行或未成功执行建表脚本:

cd backend && source ../.venv/bin/activate && python scripts/init_db.py

8.4 Bybit 下单失败 / 401 / 签名错误

  • 核对 BYBIT_API_KEY / BYBIT_API_SECRET、是否主网与 BYBIT_BASE_URL 一致。
  • API Key 是否开启合约权限、IP 白名单是否包含服务器出口 IP。

8.5 中控页面「离线」或 engine/state 401

  • 策略机未启动,或 MVP_ENGINES_JSON 中某条 base_url 不可达(单机多进程时核对 9001–9003 等端口)。
  • 若仅配单台:检查 MVP_ENGINE_BASE_URL(末尾多 / 一般可容忍)。
  • 若策略机开启了 CONTROL_API_TOKEN:JSON 里每条可写 token,或与单项模式一样设置中控环境变量 MVP_ENGINE_TOKEN(与 CONTROL_API_TOKEN 一致);否则受保护接口返回 401。

8.6 疑似重复下单或行为异常

  • 几乎一定是 Uvicorn 开了多个 worker。请改为 --workers 1,并 pm2 restart bybit-mvp-backend

8.7 信号/门控异常或 Ollama 相关错误

  • 检查 backend/.envOLLAMA_BASE_URLOLLAMA_MODELUSE_LLM;临时关闭 LLM 可设 USE_LLM=false(逻辑会走硬规则分支,以代码为准)。
  • 确认服务器能访问 Ollama 地址(防火墙、内网互通)。

8.8 升级代码后如何平滑重启

git pull
source .venv/bin/activate
pip install -r backend/requirements.txt
pip install -r control/requirements.txt
cd backend && python scripts/init_db.py   # 新库或缺表时执行
# 若从旧版升级到「多机共库」且 PostgreSQL 已存在数据,再执行一次:
# python scripts/migrate_add_machine_id.py
cd ..
pm2 restart all

8.9 重启服务器后策略机连不上数据库

  • 执行 systemctl status postgresql,若为 inactive,则启动并打开自启:systemctl start postgresqlsystemctl enable postgresql
  • 若 PostgreSQL 在 Docker 中:执行 systemctl status dockerdocker ps,确认 postgres 容器在跑;必要时 docker compose -f infra/docker-compose.yml up -d postgres(路径以实际为准)。

8.10 多机共库但数据串了或仓位不对

  • 核对每台策略机进程 MACHINE_ID 是否与中控 MVP_ENGINES_JSON 里对应项的 machine_id 完全一致(区分大小写)。
  • 核对是否误用 同一套 Bybit API Key 跑多个引擎(应避免;每进程应独立 Key / 子账户)。
  • 若库是旧版升级:是否已执行 python scripts/migrate_add_machine_id.py

8.11 中控读不到多台 / 只显示一台

  • 若设置了 MVP_ENGINES_JSON,中控 只使用 JSON 列表;此时单项 MVP_ENGINE_BASE_URL 不会作为第二套列表合并进去。
  • JSON 必须是 合法 UTF-8 字符串;在 shell 里 export 时注意引号转义,或写入由 PM2 / systemd 加载的环境文件。
  • 配置修改后需 重启中控进程(如 pm2 restart bybit-mvp-control)。

九、与 Docker 部署的关系

本仓库 infra/docker-compose.yml 提供容器化一键环境(PostgreSQL + 单策略机 backend + 中控 control)。Compose 中为中控设置 CONTROL_PORT=5000,与源码默认 9000 区分,避免与宿主机 PM2 中控端口习惯冲突。

若生产已采用 PM2(尤其 单机多策略机),则 不必 再对同一台机重复起 Compose 里的 策略机,避免 双实例抢同一账户。容器与 PM2 二者选其一 即可。


十、文档与架构

  • 逻辑架构、数据表与 machine_id 含义见 架构说明.md
  • 中控多机环境变量示例见 control/.env.example;策略机示例见 backend/.env.example

发表评论