Initialize crypto_monitor_user (user edition) from monitor codebase.
Retarget git remote, install path, and deploy docs from crypto_monitor to crypto_monitor_user. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env bash
|
||||
# 检查三路子代理端口与 /status(在服务器上运行)
|
||||
set -e
|
||||
|
||||
check_one() {
|
||||
local name="$1" port="$2"
|
||||
echo "=== ${name} :${port} ==="
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -tlnp 2>/dev/null | grep ":${port} " && echo " 端口: 已被占用" || echo " 端口: 空闲"
|
||||
fi
|
||||
if command -v curl >/dev/null 2>&1; then
|
||||
local body
|
||||
body=$(curl -sf --max-time 8 "http://127.0.0.1:${port}/status" 2>/dev/null) || {
|
||||
echo " /status: 无法连接(agent 未启动或崩溃)"
|
||||
return
|
||||
}
|
||||
echo " /status: ${body:0:200}"
|
||||
if echo "${body}" | grep -q '"ok":true'; then
|
||||
echo " 结果: OK"
|
||||
else
|
||||
echo " 结果: ok=false,见上 JSON"
|
||||
fi
|
||||
else
|
||||
echo " (未安装 curl,跳过 HTTP 检测)"
|
||||
fi
|
||||
echo
|
||||
}
|
||||
|
||||
check_one "binance" 15200
|
||||
check_one "okx" 15201
|
||||
check_one "gate" 15202
|
||||
|
||||
echo "PM2 状态:"
|
||||
pm2 status 2>/dev/null | grep -E 'manual-agent|manual-trading' || true
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
# 去掉各目录 .env 的 Windows 换行符(解决 PM2 agent errored: $'\r': command not found)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
||||
|
||||
dirs=(
|
||||
"${REPO}/manual_trading_hub"
|
||||
"${REPO}/crypto_monitor_binance"
|
||||
"${REPO}/crypto_monitor_okx"
|
||||
"${REPO}/crypto_monitor_gate"
|
||||
)
|
||||
|
||||
fixed=0
|
||||
for d in "${dirs[@]}"; do
|
||||
f="${d}/.env"
|
||||
if [[ ! -f "${f}" ]]; then
|
||||
echo "跳过(无文件): ${f}"
|
||||
continue
|
||||
fi
|
||||
if grep -q $'\r' "${f}" 2>/dev/null; then
|
||||
sed -i 's/\r$//' "${f}"
|
||||
echo "已修复 CRLF: ${f}"
|
||||
fixed=$((fixed + 1))
|
||||
else
|
||||
echo "已是 LF: ${f}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "完成,共修复 ${fixed} 个 .env."
|
||||
echo "请重启子代理: cd ${REPO}/manual_trading_hub && pm2 restart manual-agent-gate manual-agent-binance manual-agent-okx"
|
||||
echo "或: bash scripts/pm2_hub.sh restart"
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# 修复中控缺 python-multipart 等问题
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
if [[ ! -d .venv ]]; then
|
||||
python3 -m venv .venv
|
||||
fi
|
||||
# shellcheck source=/dev/null
|
||||
source .venv/bin/activate
|
||||
pip install -U pip
|
||||
pip install -r requirements.txt
|
||||
echo "OK: $(python -c 'import multipart; print("python-multipart", multipart.__version__)' 2>/dev/null || pip show python-multipart | head -1)"
|
||||
echo "Hub ping (需 hub 已启动): curl -s http://127.0.0.1:5100/api/ping"
|
||||
@@ -0,0 +1,16 @@
|
||||
# shellcheck shell=bash
|
||||
# 供 run_agent.sh / run_hub.sh source:加载 .env 并去掉 Windows CRLF($'\r')
|
||||
load_dotenv_file() {
|
||||
local f="$1"
|
||||
if [[ ! -f "${f}" ]]; then
|
||||
return 1
|
||||
fi
|
||||
set -a
|
||||
set +e
|
||||
# shellcheck disable=SC1090
|
||||
. <(sed 's/\r$//' "${f}")
|
||||
local rc=$?
|
||||
set -e
|
||||
set +a
|
||||
return "${rc}"
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# 仅子代理 PM2(中控请用 scripts/pm2_hub.sh 或 ecosystem.config.cjs 一次起全部)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HUB_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
ECO="${HUB_DIR}/ecosystem.agents.config.cjs"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法: bash scripts/pm2_agents.sh <start|stop|restart|status|logs|delete>
|
||||
|
||||
一般请用: bash scripts/pm2_hub.sh start (hub + agent 一起)
|
||||
|
||||
本脚本仅操作 3 路子代理(不含中控)
|
||||
|
||||
仅启动币安: pm2 start ecosystem.agents.config.cjs --only manual-agent-binance
|
||||
EOF
|
||||
}
|
||||
|
||||
cmd="${1:-}"
|
||||
|
||||
if ! command -v pm2 >/dev/null 2>&1; then
|
||||
echo "未找到 pm2,请先: npm install -g pm2" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "${HUB_DIR}"
|
||||
|
||||
case "${cmd}" in
|
||||
start)
|
||||
pm2 start "${ECO}"
|
||||
pm2 save 2>/dev/null || true
|
||||
;;
|
||||
stop)
|
||||
pm2 stop manual-agent-binance manual-agent-okx manual-agent-gate 2>/dev/null || true
|
||||
;;
|
||||
restart)
|
||||
pm2 restart manual-agent-binance manual-agent-okx manual-agent-gate 2>/dev/null \
|
||||
|| pm2 start "${ECO}"
|
||||
;;
|
||||
status)
|
||||
pm2 status
|
||||
;;
|
||||
logs)
|
||||
pm2 logs manual-agent-binance manual-agent-okx manual-agent-gate --lines 100
|
||||
;;
|
||||
delete)
|
||||
pm2 delete manual-agent-binance manual-agent-okx manual-agent-gate 2>/dev/null || true
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env bash
|
||||
# 中控 + 子代理 统一 PM2 快捷脚本
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HUB_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
ECO="${HUB_DIR}/ecosystem.config.cjs"
|
||||
|
||||
# 与 ecosystem.config.cjs 中 name 一致
|
||||
PM2_NAMES=(
|
||||
manual-agent-binance
|
||||
manual-agent-okx
|
||||
manual-agent-gate
|
||||
manual-trading-hub
|
||||
)
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
用法: bash scripts/pm2_hub.sh <start|stop|restart|status|logs|delete>
|
||||
|
||||
start 启动 ecosystem.config.cjs(3 路子代理 + 中控,已存在则 restart 全部)
|
||||
stop 停止全部
|
||||
restart 重启全部
|
||||
status pm2 status
|
||||
logs 全部相关进程日志
|
||||
delete 从 PM2 列表移除全部
|
||||
|
||||
仅中控: pm2 start ecosystem.config.cjs --only manual-trading-hub
|
||||
EOF
|
||||
}
|
||||
|
||||
cmd="${1:-}"
|
||||
|
||||
if ! command -v pm2 >/dev/null 2>&1; then
|
||||
echo "未找到 pm2,请先: npm install -g pm2" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${ECO}" ]]; then
|
||||
echo "未找到 ${ECO}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "${HUB_DIR}"
|
||||
|
||||
_any_running() {
|
||||
local n
|
||||
for n in "${PM2_NAMES[@]}"; do
|
||||
if pm2 describe "${n}" >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
case "${cmd}" in
|
||||
start)
|
||||
if _any_running; then
|
||||
pm2 restart "${ECO}"
|
||||
echo "已重启:hub + 全部 agent"
|
||||
else
|
||||
pm2 start "${ECO}"
|
||||
echo "已启动:hub + 全部 agent(共 ${#PM2_NAMES[@]} 个进程)"
|
||||
fi
|
||||
pm2 save 2>/dev/null || true
|
||||
;;
|
||||
stop)
|
||||
pm2 stop "${PM2_NAMES[@]}" 2>/dev/null || echo "部分或全部进程未在运行"
|
||||
;;
|
||||
restart)
|
||||
if _any_running; then
|
||||
pm2 restart "${ECO}"
|
||||
else
|
||||
pm2 start "${ECO}"
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
pm2 status
|
||||
;;
|
||||
logs)
|
||||
pm2 logs "${PM2_NAMES[@]}" --lines 100
|
||||
;;
|
||||
delete)
|
||||
pm2 delete "${PM2_NAMES[@]}" 2>/dev/null || echo "部分或全部进程不存在"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# 仅重启失败的子代理(保留 hub / 已 online 的 agent)
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
HUB_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
ECO="${HUB_DIR}/ecosystem.config.cjs"
|
||||
|
||||
cd "${HUB_DIR}"
|
||||
chmod +x scripts/run_agent.sh scripts/run_hub.sh 2>/dev/null || true
|
||||
|
||||
AGENTS=(manual-agent-binance manual-agent-okx manual-agent-gate)
|
||||
|
||||
for n in "${AGENTS[@]}"; do
|
||||
pm2 delete "${n}" 2>/dev/null || true
|
||||
done
|
||||
|
||||
pm2 start "${ECO}" --only manual-agent-binance
|
||||
pm2 start "${ECO}" --only manual-agent-okx
|
||||
pm2 start "${ECO}" --only manual-agent-gate
|
||||
|
||||
pm2 save 2>/dev/null || true
|
||||
echo "已重建 binance / okx / gate 子代理,请执行: bash scripts/check_agents.sh"
|
||||
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# PM2 子代理入口:在策略目录(cwd)加载 .env 后启动 agent.py
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
HUB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
REPO_ROOT="$(cd "${HUB_DIR}/.." && pwd)"
|
||||
export PYTHONPATH="${REPO_ROOT}${PYTHONPATH:+:${PYTHONPATH}}"
|
||||
# shellcheck source=lib_load_dotenv.sh
|
||||
source "${HUB_DIR}/scripts/lib_load_dotenv.sh"
|
||||
|
||||
VENV_PY="${HUB_DIR}/.venv/bin/python"
|
||||
AGENT_PY="${HUB_DIR}/agent.py"
|
||||
|
||||
_PM2_EXCHANGE="${EXCHANGE:-}"
|
||||
_PM2_PORT="${PORT:-}"
|
||||
_PM2_HOST="${HOST:-}"
|
||||
|
||||
if [[ ! -x "${VENV_PY}" ]]; then
|
||||
echo "未找到 ${VENV_PY},请先在 manual_trading_hub: python3 -m venv .venv && pip install -r requirements.txt" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f .env ]]; then
|
||||
if grep -q $'\r' .env 2>/dev/null; then
|
||||
echo "警告: $(pwd)/.env 含 Windows 换行(CRLF),请在仓库根执行: bash manual_trading_hub/scripts/fix_env_crlf.sh" >&2
|
||||
fi
|
||||
if ! load_dotenv_file ".env"; then
|
||||
echo "错误: $(pwd)/.env 加载失败" >&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "警告: $(pwd) 下无 .env,agent 可能缺少 API 密钥" >&2
|
||||
fi
|
||||
|
||||
[[ -n "${_PM2_EXCHANGE}" ]] && export EXCHANGE="${_PM2_EXCHANGE}"
|
||||
[[ -n "${_PM2_PORT}" ]] && export PORT="${_PM2_PORT}"
|
||||
[[ -n "${_PM2_HOST}" ]] && export HOST="${_PM2_HOST}"
|
||||
|
||||
if command -v ss >/dev/null 2>&1 && [[ -n "${PORT:-}" ]]; then
|
||||
if ss -tln 2>/dev/null | grep -q ":${PORT} "; then
|
||||
echo "错误: 端口 ${PORT} 已被占用,agent 无法监听(exchange=${EXCHANGE:-?})" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "agent start: exchange=${EXCHANGE:-?} port=${PORT:-?} cwd=$(pwd)" >&2
|
||||
exec "${VENV_PY}" "${AGENT_PY}"
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
# PM2 / 手动启动入口:加载 manual_trading_hub/.env 后运行 hub.py
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
HUB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
REPO_ROOT="$(cd "${HUB_DIR}/.." && pwd)"
|
||||
export PYTHONPATH="${REPO_ROOT}${PYTHONPATH:+:${PYTHONPATH}}"
|
||||
cd "${HUB_DIR}"
|
||||
|
||||
# shellcheck source=lib_load_dotenv.sh
|
||||
source "${HUB_DIR}/scripts/lib_load_dotenv.sh"
|
||||
|
||||
VENV_PY="${HUB_DIR}/.venv/bin/python"
|
||||
if [[ ! -x "${VENV_PY}" ]]; then
|
||||
echo "未找到 ${VENV_PY},请先: python3 -m venv .venv && pip install -r requirements.txt" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "${HUB_DIR}/.env" ]]; then
|
||||
load_dotenv_file "${HUB_DIR}/.env" || {
|
||||
echo "错误: ${HUB_DIR}/.env 加载失败" >&2
|
||||
exit 1
|
||||
}
|
||||
fi
|
||||
|
||||
echo "run_hub: python=${VENV_PY} cwd=${HUB_DIR} PYTHONPATH=${PYTHONPATH}" >&2
|
||||
exec "${VENV_PY}" -u "${HUB_DIR}/hub.py"
|
||||
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env bash
|
||||
# 在服务器上检查中控是否为最新代码(无 api_trade_key,已装 multipart,进程可访问)
|
||||
set -euo pipefail
|
||||
HUB_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
cd "$HUB_DIR"
|
||||
|
||||
HUB_PORT=5100
|
||||
if [[ -f .env ]]; then
|
||||
p=$(grep -E '^HUB_PORT=' .env 2>/dev/null | tail -1 | cut -d= -f2- | tr -d '\r" ')
|
||||
[[ -n "${p}" ]] && HUB_PORT="${p}"
|
||||
fi
|
||||
PING_URL="http://127.0.0.1:${HUB_PORT}/api/ping"
|
||||
|
||||
echo "=== hub.py 检查 ==="
|
||||
if grep -n 'def api_trade_key' hub.py 2>/dev/null; then
|
||||
echo "FAIL: 仍是旧版 hub.py(含 api_trade_key),请 git pull"
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q 'HUB_BUILD' hub.py; then
|
||||
echo "FAIL: hub.py 缺少 HUB_BUILD 标记"
|
||||
exit 1
|
||||
fi
|
||||
echo "OK: 无 api_trade_key,含 HUB_BUILD"
|
||||
|
||||
echo "=== python-multipart ==="
|
||||
# shellcheck source=/dev/null
|
||||
source .venv/bin/activate
|
||||
python -c "import multipart; print('OK:', multipart.__version__)"
|
||||
|
||||
echo "=== 端口 ${HUB_PORT} ==="
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
ss -ltn | grep -E ":${HUB_PORT}\\b" || echo "WARN: 未监听 ${HUB_PORT},请 pm2 restart manual-trading-hub"
|
||||
elif command -v netstat >/dev/null 2>&1; then
|
||||
netstat -ltn | grep -E ":${HUB_PORT}\\b" || echo "WARN: 未监听 ${HUB_PORT}"
|
||||
else
|
||||
echo "(跳过端口检查)"
|
||||
fi
|
||||
|
||||
echo "=== PM2 manual-trading-hub ==="
|
||||
if command -v pm2 >/dev/null 2>&1; then
|
||||
pm2 describe manual-trading-hub 2>/dev/null | grep -E 'status|restarts|uptime|script path' || pm2 list | grep -i hub || true
|
||||
fi
|
||||
|
||||
echo "=== GET ${PING_URL} ==="
|
||||
HTTP_CODE=$(curl -sS -o /tmp/hub_ping_body.txt -w "%{http_code}" "${PING_URL}" || echo "000")
|
||||
echo "HTTP ${HTTP_CODE}"
|
||||
cat /tmp/hub_ping_body.txt
|
||||
echo ""
|
||||
if [[ "${HTTP_CODE}" == "200" ]]; then
|
||||
python -m json.tool /tmp/hub_ping_body.txt
|
||||
if grep -q '20260521-no-trade-ui' /tmp/hub_ping_body.txt; then
|
||||
echo "OK: build 正确"
|
||||
else
|
||||
echo "WARN: build 字段不是 20260521-no-trade-ui,请 pm2 restart manual-trading-hub"
|
||||
fi
|
||||
else
|
||||
echo "FAIL: ping 未返回 200.常见原因:进程未启动或崩溃."
|
||||
echo " 执行: pm2 restart manual-trading-hub && sleep 2 && bash scripts/verify_hub_deploy.sh"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,42 @@
|
||||
# 中控与子代理 · 后台常驻(Ubuntu)
|
||||
|
||||
**唯一推荐方式:PM2.**
|
||||
|
||||
请仅使用 PM2 托管 `hub.py` 与 `agent.py`,勿与 nohup 等方式重复启动同一端口.
|
||||
|
||||
---
|
||||
|
||||
## 启动
|
||||
|
||||
```bash
|
||||
cd /opt/crypto_monitor_user/manual_trading_hub
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
cp -n .env.example .env # 首次
|
||||
|
||||
pm2 start ecosystem.config.cjs
|
||||
pm2 save
|
||||
pm2 list
|
||||
```
|
||||
|
||||
一条 `ecosystem.config.cjs` 会拉起 **4 个子代理 + 1 个 hub**.
|
||||
|
||||
---
|
||||
|
||||
## 常用命令
|
||||
|
||||
```bash
|
||||
pm2 logs manual-trading-hub
|
||||
pm2 restart manual-trading-hub
|
||||
pm2 restart all
|
||||
bash scripts/verify_hub_deploy.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 详细说明
|
||||
|
||||
| 文档 | 内容 |
|
||||
|------|------|
|
||||
| [../部署文档.md](../部署文档.md) | 端口,反代,故障排查 |
|
||||
| [../../docs/ubuntu-server.md](../../docs/ubuntu-server.md) | Python / Node / PM2 版本与三所启动顺序 |
|
||||
Reference in New Issue
Block a user