一、备份前准备
先在网页上手动关闭“真仓开关”,确认页面显示:真仓开关:关闭
然后确认当前数据库正常:
cd /opt/polymarket-mcp-server
sqlite3 data/btc5m_polymarket.sqlite "
PRAGMA quick_check;
SELECT COUNT(*), MAX(id) FROM btc5m_live_trade_intents;
SELECT COUNT(*), MAX(id) FROM btc5m_paper_trades;
SELECT COUNT(*), MAX(id) FROM btc5m_orderbook_snapshots;
"
第一行必须返回:ok
二、安装安全备份脚本
执行:
nano /root/backup_polymarket_mcp_safe.sh
粘贴下面完整内容:
#!/usr/bin/env bash
set -Eeuo pipefail
umask 077
PROJECT_DIR="/opt/polymarket-mcp-server"
DB_PATH="${PROJECT_DIR}/data/btc5m_polymarket.sqlite"
BACKUP_DIR="/root/polymarket-backups"
STAMP="$(date -u +%Y%m%d_%H%M%S)"
HOST_NAME="$(hostname -s)"
STAGE_DIR="$(mktemp -d "/root/polymarket-backup-stage-${STAMP}-XXXXXX")"
ARCHIVE="${BACKUP_DIR}/polymarket-mcp-offline-${HOST_NAME}_${STAMP}.tar.gz"
CHECKSUM="${ARCHIVE}.sha256"
DB_SNAPSHOT="${STAGE_DIR}/project/data/btc5m_polymarket.sqlite"
mkdir -p "$BACKUP_DIR"
cleanup() {
rm -rf "$STAGE_DIR"
}
trap cleanup EXIT
if [ ! -d "$PROJECT_DIR" ]; then
echo "STOP: 项目目录不存在:$PROJECT_DIR"
exit 1
fi
if [ ! -f "$DB_PATH" ]; then
echo "STOP: 数据库不存在:$DB_PATH"
exit 1
fi
echo "=== 1. 备份前检查生产数据库 ==="
db_check="$(sqlite3 "$DB_PATH" "PRAGMA quick_check;" 2>&1 || true)"
if [ "$db_check" != "ok" ]; then
echo "STOP: 生产数据库检查失败:"
echo "$db_check"
exit 1
fi
file "$DB_PATH"
echo "生产数据库检查通过"
echo "=== 2. 记录服务原始状态 ==="
WEB_WAS_ACTIVE=0
SETTLE_TIMER_WAS_ACTIVE=0
PREVIEW_TIMER_WAS_ACTIVE=0
systemctl is-active --quiet polymarket-mcp-web.service \
&& WEB_WAS_ACTIVE=1 || true
systemctl is-active --quiet btc5m-live-settlement-sync.timer \
&& SETTLE_TIMER_WAS_ACTIVE=1 || true
systemctl is-active --quiet btc5m-live-backtest-preview.timer \
&& PREVIEW_TIMER_WAS_ACTIVE=1 || true
printf 'web=%s settlement_timer=%s preview_timer=%s\n' \
"$WEB_WAS_ACTIVE" \
"$SETTLE_TIMER_WAS_ACTIVE" \
"$PREVIEW_TIMER_WAS_ACTIVE"
echo "=== 3. 停止数据库写入来源 ==="
systemctl stop btc5m-live-settlement-sync.timer 2>/dev/null || true
systemctl stop btc5m-live-settlement-sync.service 2>/dev/null || true
systemctl stop btc5m-live-backtest-preview.timer 2>/dev/null || true
systemctl stop btc5m-live-backtest-preview.service 2>/dev/null || true
systemctl stop btc5m-db-maintenance.timer 2>/dev/null || true
systemctl stop btc5m-db-maintenance.service 2>/dev/null || true
systemctl stop polymarket-mcp-web.service
sleep 3
if systemctl is-active --quiet polymarket-mcp-web.service; then
echo "STOP: Web服务没有停止"
exit 1
fi
if fuser "$DB_PATH" >/dev/null 2>&1; then
echo "STOP: 仍有进程占用数据库:"
fuser -v "$DB_PATH" || true
exit 1
fi
echo "所有已知数据库写入服务均已停止"
echo "=== 4. 停写后再次检查数据库 ==="
db_check="$(sqlite3 "$DB_PATH" "PRAGMA quick_check;" 2>&1 || true)"
if [ "$db_check" != "ok" ]; then
echo "STOP: 停止服务后数据库检查失败:"
echo "$db_check"
exit 1
fi
echo "数据库完整性正常"
echo "=== 5. 复制项目文件 ==="
mkdir -p "${STAGE_DIR}/project"
tar \
--exclude='./venv' \
--exclude='./.git' \
--exclude='./__pycache__' \
--exclude='*/__pycache__' \
--exclude='*.pyc' \
--exclude='./data/btc5m_polymarket.sqlite' \
--exclude='./data/btc5m_polymarket.sqlite-wal' \
--exclude='./data/btc5m_polymarket.sqlite-shm' \
--exclude='./data/btc5m_polymarket.sqlite-journal' \
-C "$PROJECT_DIR" \
-cpf - . |
tar -C "${STAGE_DIR}/project" -xpf -
mkdir -p "$(dirname "$DB_SNAPSHOT")"
echo "=== 6. 使用SQLite生成一致性快照 ==="
sqlite3 \
-cmd ".timeout 30000" \
"$DB_PATH" \
".backup '$DB_SNAPSHOT'"
snapshot_check="$(sqlite3 "$DB_SNAPSHOT" "PRAGMA quick_check;" 2>&1 || true)"
if [ "$snapshot_check" != "ok" ]; then
echo "STOP: 快照数据库检查失败:"
echo "$snapshot_check"
exit 1
fi
header="$(od -An -tc -N16 "$DB_SNAPSHOT")"
if ! printf '%s\n' "$header" | grep -q "S.*Q.*L.*i.*t.*e"; then
echo "STOP: 快照数据库文件头异常:"
echo "$header"
exit 1
fi
echo "快照数据库检查通过"
echo "=== 7. 保存数据库统计清单 ==="
mkdir -p "${STAGE_DIR}/manifest"
sqlite3 -header -column "$DB_SNAPSHOT" "
SELECT
COUNT(*) AS live_total,
MAX(id) AS latest_live_id
FROM btc5m_live_trade_intents;
SELECT
COUNT(*) AS paper_total,
MAX(id) AS latest_paper_id
FROM btc5m_paper_trades;
SELECT
COUNT(*) AS snapshot_total,
MAX(id) AS latest_snapshot_id
FROM btc5m_orderbook_snapshots;
" | tee "${STAGE_DIR}/manifest/database-counts.txt"
file "$DB_SNAPSHOT" \
> "${STAGE_DIR}/manifest/database-file-type.txt"
sha256sum "$DB_SNAPSHOT" \
> "${STAGE_DIR}/manifest/database.sha256"
echo "=== 8. 保存systemd配置 ==="
mkdir -p "${STAGE_DIR}/systemd/etc/systemd/system"
find /etc/systemd/system \
-maxdepth 2 \
-type f \
\( \
-name 'btc5m-*' \
-o -name 'polymarket-mcp-web.service' \
-o -path '*/polymarket-mcp-web.service.d/*' \
\) \
-print0 |
while IFS= read -r -d '' unit_file; do
relative_path="${unit_file#/etc/systemd/system/}"
destination="${STAGE_DIR}/systemd/etc/systemd/system/${relative_path}"
mkdir -p "$(dirname "$destination")"
cp -a "$unit_file" "$destination"
done
systemctl list-timers --all --no-pager \
> "${STAGE_DIR}/manifest/systemd-timers.txt"
systemctl list-unit-files --no-pager |
grep -E 'btc5m|polymarket-mcp' \
> "${STAGE_DIR}/manifest/systemd-unit-files.txt" || true
echo "=== 9. 保存环境信息 ==="
{
date -u
hostnamectl 2>/dev/null || true
python3 --version
sqlite3 --version
du -sh "$PROJECT_DIR"
} > "${STAGE_DIR}/manifest/environment.txt"
if [ -x "${PROJECT_DIR}/venv/bin/pip" ]; then
"${PROJECT_DIR}/venv/bin/pip" freeze \
> "${STAGE_DIR}/manifest/pip-freeze.txt" || true
fi
echo "=== 10. 生成压缩包 ==="
tar -C "$STAGE_DIR" -czf "$ARCHIVE" .
sha256sum "$ARCHIVE" |
sed "s| ${ARCHIVE}| $(basename "$ARCHIVE")|" \
> "$CHECKSUM"
echo "=== 11. 验证压缩包 ==="
gzip -t "$ARCHIVE"
(
cd "$BACKUP_DIR"
sha256sum -c "$(basename "$CHECKSUM")"
)
tar -tzf "$ARCHIVE" |
grep -q 'project/data/btc5m_polymarket.sqlite'
echo "压缩包及数据库文件均已验证"
echo "=== 12. 恢复原来运行的服务 ==="
if [ "$WEB_WAS_ACTIVE" -eq 1 ]; then
systemctl start polymarket-mcp-web.service
fi
if [ "$SETTLE_TIMER_WAS_ACTIVE" -eq 1 ]; then
systemctl start btc5m-live-settlement-sync.timer
fi
if [ "$PREVIEW_TIMER_WAS_ACTIVE" -eq 1 ]; then
systemctl start btc5m-live-backtest-preview.timer
fi
# 数据库维护定时器不会自动恢复。
# 等维护脚本确认安全后,再单独启用。
sleep 5
echo "=== 13. 最终状态 ==="
systemctl is-active polymarket-mcp-web.service || true
systemctl is-active btc5m-live-settlement-sync.timer || true
systemctl is-active btc5m-live-backtest-preview.timer || true
echo
echo "OK: 完整安全备份成功"
echo "备份文件:$ARCHIVE"
echo "校验文件:$CHECKSUM"
echo "注意:真仓开关保持你备份前的配置;请人工确认后再开启。"
保存并退出:Ctrl+O
回车
Ctrl+X
设置权限:
chmod 700 /root/backup_polymarket_mcp_safe.sh
三、执行备份
确定真仓已手动关闭后运行:
/root/backup_polymarket_mcp_safe.sh
成功时最后会显示:
OK: 完整安全备份成功
备份文件:/root/polymarket-backups/polymarket-mcp-offline-…
校验文件:/root/polymarket-backups/polymarket-mcp-offline-….sha256
如果中途显示 STOP,不要手动开启真仓,先根据错误检查数据库和服务。
四、备份完成后验证
找到最新备份
cd /root/polymarket-backups
latest_archive="$(ls -1t polymarket-mcp-offline-*.tar.gz | head -1)"
printf '最新备份:%s\n' "$latest_archive"
sha256sum -c "${latest_archive}.sha256"
gzip -t "$latest_archive"
tar -tzf "$latest_archive" |
grep 'project/data/btc5m_polymarket.sqlite'
应分别看到:
OK以及数据库文件路径。
再做一次独立解压验证:
verify_dir="$(mktemp -d /root/polymarket-verify-XXXXXX)"
tar -xzf "$latest_archive" \
-C "$verify_dir" \
./project/data/btc5m_polymarket.sqlite
verify_db="$verify_dir/project/data/btc5m_polymarket.sqlite"
file "$verify_db"
sqlite3 "$verify_db" "
PRAGMA quick_check;
SELECT COUNT(*), MAX(id) FROM btc5m_live_trade_intents;
SELECT COUNT(*), MAX(id) FROM btc5m_paper_trades;
SELECT COUNT(*), MAX(id) FROM btc5m_orderbook_snapshots;
"
确认第一行是:ok
验证目录暂时保留即可;确认无误后可删除:
rm -rf -- "$verify_dir"
这里只删除刚刚由 mktemp 创建的明确目录。
五、确认系统恢复运行
systemctl is-active polymarket-mcp-web.service
systemctl is-active btc5m-live-settlement-sync.timer
systemctl is-active btc5m-live-backtest-preview.timer
journalctl \
-u polymarket-mcp-web.service \
-n 80 \
--no-pager
再检查数据库是否继续增长:
cd /opt/polymarket-mcp-server
sqlite3 data/btc5m_polymarket.sqlite "
PRAGMA quick_check;
SELECT COUNT(*), MAX(id)
FROM btc5m_orderbook_snapshots;
"
间隔约十秒后再运行一次,快照数量或最大 ID 应继续增加。
最后打开网页确认:
- 数据记录器运行中;
- 模拟交易运行中;
- 自动结算运行中;
- 真仓统计能读取;
- 当前真仓规则正确;
- 需要真仓时,再人工开启真仓开关。
六、一定要把备份复制出VPS
只存放在原VPS不算完整备份。VPS损坏或到期后,本机备份也会一起消失。
复制到电脑:
scp root@你的VPS地址:/root/polymarket-backups/polymarket-mcp-offline-*.tar.gz .
scp root@你的VPS地址:/root/polymarket-backups/polymarket-mcp-offline-*.sha256 .
或者复制到新VPS:
scp \
/root/polymarket-backups/polymarket-mcp-offline-*.tar.gz \
root@新VPS地址:/root/
scp \
/root/polymarket-backups/polymarket-mcp-offline-*.sha256 \
root@新VPS地址:/root/
备份包可能包含 .env、API凭据和私钥,因此:
- 不要上传到公开网盘或公开 GitHub;
- 本地备份文件应加密保存;
- 备份目录保持 root 专用权限;
- 不要把备份文件发送给陌生人。
这套流程不会直接复制运行中的 WAL 数据库,也不会自动开启真仓;数据库维护定时器仍保持停用。
!!!备份脚本后,为保护数据库没有自动重启 Web 服务
1. 检查数据库
cd /opt/polymarket-mcp-server
file data/btc5m_polymarket.sqlite
sqlite3 data/btc5m_polymarket.sqlite "
PRAGMA quick_check;
SELECT COUNT(*), MAX(id) FROM btc5m_live_trade_intents;
SELECT COUNT(*), MAX(id) FROM btc5m_paper_trades;
SELECT COUNT(*), MAX(id) FROM btc5m_orderbook_snapshots;
"
如果第一行返回:ok
继续下一步。
2. 启动网页服务
systemctl daemon-reload
systemctl start polymarket-mcp-web.service
systemctl is-active polymarket-mcp-web.service
正常应显示:active
然后查看启动日志:
journalctl \
-u polymarket-mcp-web.service \
-n 120 \
--no-pager -l
3. 测试本机网页接口
curl -m 20 -i \
http://127.0.0.1:8080/
再测试健康接口:
curl -m 20 -sS \
"http://127.0.0.1:8080/api/btc5m/system/health?_=$(date +%s)" |
python3 -m json.tool
如果首页使用的不是8080端口,先执行:
systemctl cat polymarket-mcp-web.service --no-pager
查看 ExecStart 中的实际端口。
4. 恢复两个安全定时器
网页正常后执行:
systemctl start btc5m-live-settlement-sync.timer
systemctl start btc5m-live-backtest-preview.timer
检查:
systemctl is-active \
btc5m-live-settlement-sync.timer \
btc5m-live-backtest-preview.timer
两个都应该显示:active
active
数据库维护定时器先不要启动:
systemctl stop btc5m-db-maintenance.timer
重新开启数据库维护定时器,但不要手动启动维护 .service,只启用 .timer。
systemctl daemon-reload
systemctl enable --now btc5m-db-maintenance.timer
检查是否成功:
systemctl is-enabled btc5m-db-maintenance.timer
systemctl is-active btc5m-db-maintenance.timer
应该显示:enabled
active
查看下次维护时间:
systemctl list-timers \
btc5m-db-maintenance.timer \
--all \
--no-pager
再检查详细状态:
systemctl show btc5m-db-maintenance.timer \
-p ActiveState \
-p LastTriggerUSec \
-p NextElapseUSecRealtime \
-p Unit \
--no-pager
最后确认没有立即执行失败:
journalctl \
-u btc5m-db-maintenance.service \
-n 80 \
--no-pager -l