完整产品需求与技术实现文档
版本:v17
日期:2026-02-11
作者:BitHappy, DXH430725
变更摘要:架构从 Cloudflare Workers + D1 迁移至本地 Node.js 后端(Express + SQLite + node-cron);新增 OKX DEX SDK 作为数据源之一;凭据改为 .env 管理;前端集成到 Hugo 站点。
一、项目概述 #
1.1 目标 #
网页端工具,实时监控单一目标代币在多个市场(CEX + DEX + 多链)上的对 USDT 汇率,当偏差达到阈值时,通过 Telegram 推送 + 网页告警通知用户。
1.2 核心价值 #
- 快速发现跨市场套利机会(稳定币溢价/折价、非稳定币跨所价差)。
- 本地单进程运行,多数据源各自独立 Cron 调度,互不干扰。
- SQLite 本地存储,查询灵活、历史可追溯。
- 前端集成到现有 Hugo 站点 (bithappy.xyz)。
1.3 关键约束 #
- Cron 频率:1~5 分钟(按数据源不同)。
- 前端轮询:每 30 秒。
- 存储:本地 SQLite(via sql.js,纯 JS 无需编译)。
- 凭据:
.env文件管理(OKX API Keys、Telegram Token、API Key)。
二、整体架构 #
Hugo 前端 (bithappy.xyz / Cloudflare Pages)
↕ fetch REST API
本地 Node.js 后端 (Express, port 8787)
├─ REST API 路由(配置 CRUD、日志查询、Bot 管理)
├─ node-cron 调度器(多数据源独立 Cron)
└─ SQLite 数据库 (sql.js → data/monitor.db)
调度器内部:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ okx-dex │ │ binance │ │ dexscreener │ │ coingecko │
│ (*/1 min) │ │ (*/1 min) │ │ (*/3 min) │ │ (*/5 min) │
│ OKX DEX SDK │ │ REST API │ │ REST API │ │ REST API │
│ getQuote │ │ ticker/price│ │ dex/tokens │ │ simple/price│
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │ │
└─────────────────┴─────────────────┴─────────────────┘
│
读配置 → 获取报价 → 计算偏差
→ 退避判断 → 写日志 → 发 Telegram
2.1 数据源分工 #
| 数据源名称 | 获取方式 | Cron 频率 | 数据来源 |
|---|---|---|---|
okx-dex |
OKX DEX SDK getQuote |
*/1 * * * * | 链上 DEX 聚合报价(支持 EVM/Solana/Sui 等) |
binance |
Binance REST API | */1 * * * * | api.binance.com/api/v3/ticker/price |
dexscreener |
DexScreener REST API | */3 * * * * | api.dexscreener.com/latest/dex/tokens/{ca} |
coingecko |
CoinGecko REST API | */5 * * * * | api.coingecko.com/api/v3/simple/price |
用户在配置表中勾选启用哪些数据源,调度器执行时只处理包含对应数据源的配置行。
2.2 OKX DEX SDK 报价原理 #
fetchOkxDexPrice(config):
1. 取 config.contract_address(或 native token 地址)
2. 取目标链上的 USDT 合约地址
3. 调用 OKXDexClient.dex.getQuote({
chainId, fromTokenAddress, toTokenAddress,
amount: 10^decimals (即 1 个完整单位), slippage: 0.01
})
4. 返回值 toTokenAmount / 10^6 = 该 token 的 USDT 价格
OKX DEX SDK 需要 4 个凭据:OKX_API_KEY, OKX_SECRET_KEY, OKX_API_PASSPHRASE, OKX_PROJECT_ID,配置在 .env 中。
三、数据模型(SQLite) #
3.1 表 token_config(币种配置表)
#
| 列名 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
id |
INTEGER PRIMARY KEY | 自增 | - | 主键 |
name |
TEXT | 是 | - | 币种名称(如 “USDF”, “ETH”) |
chain_id |
TEXT | 是 | - | 链 ID(如 “1” 为以太坊主网, “56” 为 BSC) |
contract_address |
TEXT | 否 | NULL | 合约地址(CEX 币种可为空,原生代币用 0xEeee…) |
base_price |
REAL | 是 | 1.0 | 基准价格(稳定币=1,非稳定币为参考值) |
price_variable |
INTEGER | 是 | 0 | 布尔:0=固定基准, 1=需实时获取基准价格 |
price_source_url |
TEXT | 否 | NULL | 可变基准价格的查询 URL(price_variable=1 时必填) |
alert_threshold |
REAL | 是 | 0.5 | 偏差警报阈值(%) |
enabled_sources |
TEXT | 是 | ‘[]’ | JSON 数组,启用的数据源名称列表,如 ["okx-dex","binance"] |
telegram_chat_ids |
TEXT | 否 | NULL | JSON 数组,推送目标 chat_id 列表 |
enabled |
INTEGER | 是 | 1 | 启用状态:1=启用, 0=禁用 |
note |
TEXT | 否 | NULL | 备注 |
created_at |
TEXT | 是 | datetime(’now') | 创建时间 |
updated_at |
TEXT | 是 | datetime(’now') | 更新时间 |
3.2 表 alert_log(警报记录表)
#
| 列名 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
INTEGER PRIMARY KEY | 自增 | 主键 |
config_id |
INTEGER | 是 | 关联 token_config.id |
source_name |
TEXT | 是 | 触发警报的数据源(如 “okx-dex”, “binance”) |
token_name |
TEXT | 是 | 冗余存储币种名称 |
fetched_price |
REAL | 是 | 数据源获取到的报价 |
base_price |
REAL | 是 | 当时的基准价格 |
deviation_pct |
REAL | 是 | 偏差百分比 |
alert_sent |
INTEGER | 是 | 是否实际发送了 Telegram(1=是, 0=被退避抑制) |
backoff_level |
INTEGER | 是 | 当前退避级别 |
created_at |
TEXT | 是 | 记录时间 |
3.3 表 tg_bots(Telegram Bot 表)
#
| 列名 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
INTEGER PRIMARY KEY | 自增 | 主键 |
name |
TEXT | 是 | Bot 名称 |
token |
TEXT | 是 | Bot Token(敏感,仅后端存储) |
chat_id |
TEXT | 是 | 推送目标 Chat ID |
created_at |
TEXT | 是 | 创建时间 |
3.4 DDL #
CREATE TABLE IF NOT EXISTS token_config (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
chain_id TEXT NOT NULL,
contract_address TEXT,
base_price REAL NOT NULL DEFAULT 1.0,
price_variable INTEGER NOT NULL DEFAULT 0,
price_source_url TEXT,
alert_threshold REAL NOT NULL DEFAULT 0.5,
enabled_sources TEXT NOT NULL DEFAULT '[]',
telegram_chat_ids TEXT,
enabled INTEGER NOT NULL DEFAULT 1,
note TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS alert_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
config_id INTEGER NOT NULL,
source_name TEXT NOT NULL,
token_name TEXT NOT NULL,
fetched_price REAL NOT NULL,
base_price REAL NOT NULL,
deviation_pct REAL NOT NULL,
alert_sent INTEGER NOT NULL DEFAULT 0,
backoff_level INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
FOREIGN KEY (config_id) REFERENCES token_config(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS tg_bots (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
token TEXT NOT NULL,
chat_id TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_alert_log_config_source ON alert_log(config_id, source_name, created_at);
CREATE INDEX IF NOT EXISTS idx_alert_log_created ON alert_log(created_at);
CREATE INDEX IF NOT EXISTS idx_token_config_enabled ON token_config(enabled);
四、核心运作流程 #
4.1 调度器执行流程(每个数据源独立 Cron) #
node-cron 触发某数据源(如 okx-dex)
│
├─ 1. 查询 SQLite: SELECT * FROM token_config
│ WHERE enabled = 1
│ AND enabled_sources LIKE '%"okx-dex"%'
│
├─ 2. 遍历匹配的配置行:
│ │
│ ├─ 2a. 确定基准价格:
│ │ ├─ price_variable = 0 → 使用 base_price 字段
│ │ └─ price_variable = 1 → fetch(price_source_url) 获取实时基准价
│ │
│ ├─ 2b. 调用对应数据源的 fetcher 获取当前报价:
│ │ ├─ okx-dex → OKXDexClient.dex.getQuote({chainId, from, to, amount})
│ │ ├─ binance → GET api.binance.com/api/v3/ticker/price?symbol={NAME}USDT
│ │ ├─ dexscreener→ GET api.dexscreener.com/latest/dex/tokens/{contract_address}
│ │ └─ coingecko → GET api.coingecko.com/api/v3/simple/price?ids={name}&vs_currencies=usd
│ │
│ ├─ 2c. 计算偏差:
│ │ deviation = |fetched_price - base_price| / base_price * 100%
│ │
│ ├─ 2d. 判断是否触发警报:
│ │ deviation >= alert_threshold ?
│ │ │
│ │ ├─ 否 → 跳过
│ │ │
│ │ └─ 是 → 进入退避判断(4.2)
│ │
│ └─ 2e. 写入 alert_log 记录(无论是否发送)
│
└─ 结束
4.2 退避式警报机制 #
退避级别与间隔:
| 级别 | 含义 | 距上次发送需等待 | 触发后升级到 |
|---|---|---|---|
| 0 | 首次触发 | 立即发送 | 级别 1 |
| 1 | 第二次 | >= 1 分钟 | 级别 2 |
| 2 | 第三次 | >= 5 分钟 | 级别 3 |
| 3 | 第四次 | >= 15 分钟 | 级别 4 |
| 4 | 持续触发 | >= 60 分钟 | 保持级别 4 |
判断逻辑(伪代码):
function shouldSendAlert(config_id, source_name):
last_sent = SELECT * FROM alert_log
WHERE config_id = ? AND source_name = ?
AND alert_sent = 1
ORDER BY created_at DESC LIMIT 1
if last_sent is NULL:
return { send: true, level: 0 }
backoff_intervals = [0, 1, 5, 15, 60] // 分钟
current_level = min(last_sent.backoff_level + 1, 4)
required_gap = backoff_intervals[current_level]
elapsed_minutes = (now - last_sent.created_at) in minutes
if elapsed_minutes >= required_gap:
return { send: true, level: current_level }
else:
return { send: false, level: current_level }
退避重置条件:
- 退避仅基于时间间隔,不主动重置——如果偏差恢复正常后再次超阈值,因为距离上次发送已超过间隔,会自然从当前级别恢复发送。
- 如果偏差恢复正常超过 60 分钟后再次触发,等效于「首次触发」。
4.3 Telegram 推送消息模板 #
🚨 汇率偏差警报!
币种:{name}
链:{chain_id}
数据源:{source_name}
报价:{fetched_price} USDT
基准:{base_price} USDT
偏差:{deviation_pct}%
警报级别:第 {backoff_level + 1} 次(退避 {interval}min)
时间:{YYYY-MM-DD HH:MM:SS UTC}
五、前端功能需求 #
5.1 配置管理页(主表单) #
表单字段对应 token_config 表:
| 表单控件 | 对应字段 | 控件类型 | 说明 |
|---|---|---|---|
| 币种名称 | name | 文本输入 | 如 USDF, ETH |
| 链 ID | chain_id | 下拉选择 | 预设:1(ETH), 56(BSC), 137(Polygon), 43114(Avalanche), 42161(Arbitrum), 10(Optimism), 8453(Base) |
| 合约地址 | contract_address | 文本输入 | 可选,DEX/DexScreener 查询需要 |
| 基准价格 | base_price | 数字输入 | 稳定币默认 1 |
| 币价可变 | price_variable | 开关 | 开启后显示"可变币价源URL"字段 |
| 可变币价源 URL | price_source_url | 文本输入 | 条件显示 |
| 警报阈值 | alert_threshold | 数字输入 + % | 默认 0.5 |
| 启用数据源 | enabled_sources | 多选复选框 | okx-dex, binance, dexscreener, coingecko |
| Telegram 推送 | telegram_chat_ids | 多选 | 从已配置的 Bot/Chat 列表选择 |
| 启用状态 | enabled | 开关 | 默认启用 |
| 备注 | note | 多行文本 | 可选 |
5.2 UI 布局(Hugo 集成) #
前端作为 Hugo 站点的一个工具页面,位于 bithappy.xyz/toolbox/arbitrage-monitor/。
+---------------------------------------------------------------+
| Hugo Header / 导航栏 |
+---------------------------------------------------------------+
| 页面标题:加密货币套利监控工具 |
+---------------------------------------------------------------+
| 状态栏:监控 X 个币种 | 最近24h警报 N 条 | 已发送 N 条 |
+---------------------------------------------------------------+
| Tabs: [ 币种配置 ] [ 警报日志 ] [ 设置 ] |
+---------------------------------------------------------------+
| 主内容区 |
+---------------------------------------------------------------+
| Hugo Footer |
+---------------------------------------------------------------+
5.2.1 币种配置 Tab #
[搜索框] [+ 新增币种] [刷新]
配置表格:
+------+--------+--------+------+--------+-------------------+------+------+------+
| 名称 | 链 | 合约 | 基准 | 可变? | 数据源 | 阈值 | 状态 | 操作 |
+------+--------+--------+------+--------+-------------------+------+------+------+
| USDF | 1 | 0xfa...| 1.0 | 否 | okx-dex,dexscreen | 0.5% | 启用 | 编辑/删除 |
| ETH | 1 | 0xEe...| var | 是 | okx-dex,binance | 1.0% | 启用 | 编辑/删除 |
+------+--------+--------+------+--------+-------------------+------+------+------+
5.2.2 警报日志 Tab #
[筛选:币种 + 数据源] [导出CSV]
+------------------+------+----------+--------+------+-------+------+----------+
| 时间 | 币种 | 数据源 | 报价 | 基准 | 偏差 | 已发送| 退避级别 |
+------------------+------+----------+--------+------+-------+------+----------+
| 2026-02-11 08:00 | USDF | okx-dex | 0.993 | 1.0 | 0.7% | 是 | 0 |
+------------------+------+----------+--------+------+-------+------+----------+
[上一页] 第 1 页 [下一页]
5.2.3 设置 Tab #
┌─ Telegram Bot 管理 ─────────────┐ ┌─ API 设置 ──────────────────┐
│ +-----------+---------+--------+ │ │ API 端点:[http://localhost:8787] │
│ | Bot 名称 | Chat ID | 操作 | │ │ API Key: [**************] │
│ +-----------+---------+--------+ │ │ [保存] │
│ | 主Bot | xxxxx | 测试/删除| │ └────────────────────────────────┘
│ +-----------+---------+--------+ │
│ [+ 添加 Bot] │ ┌─ 配置导入/导出 ──────────────┐
└─────────────────────────────────┘ │ [导出全部配置] [导入配置] │
└────────────────────────────────┘
六、API 接口设计(Express 后端) #
| 方法 | 路径 | 功能 | 认证 |
|---|---|---|---|
| GET | /api/configs | 获取所有币种配置 | API Key |
| POST | /api/configs | 新增币种配置 | API Key |
| PUT | /api/configs/:id | 更新币种配置 | API Key |
| DELETE | /api/configs/:id | 删除币种配置 | API Key |
| GET | /api/logs | 获取警报日志(支持分页、筛选) | API Key |
| GET | /api/logs/stats | 获取统计摘要 | API Key |
| GET | /api/sources | 获取已注册数据源列表 | API Key |
| GET | /api/bots | 获取 Telegram Bot 列表 | API Key |
| POST | /api/bots | 添加 Telegram Bot | API Key |
| DELETE | /api/bots/:id | 删除 Bot | API Key |
| POST | /api/bots/:id/test | 测试 Bot 发送 | API Key |
| GET | /api/export | 导出全部配置 | API Key |
| POST | /api/import | 导入配置 | API Key |
认证方式:请求头 X-API-Key: <key>,Key 配置在 .env 文件中。
七、项目目录结构与开发流程 #
7.1 项目目录结构 #
D:\HAPPY\arbitrage-monitor\
├── package.json # 根配置(start/dev 脚本)
│
├── backend/ # ★ 本地后端(v2 架构)
│ ├── package.json # 依赖:express, sql.js, @okx-dex/okx-dex-sdk, node-cron, dotenv, cors
│ ├── server.js # Express 主入口(REST API + 启动调度器 + seed)
│ ├── db.js # sql.js SQLite 封装(全部 CRUD)
│ ├── config.js # 常量(退避间隔、数据源列表、链名映射、代币地址)
│ ├── alert.js # 退避算法 + Telegram 发送 + processAlert
│ ├── price.js # 价格获取器集合:
│ │ # fetchOkxDexPrice → OKX DEX SDK getQuote
│ │ # fetchBinancePrice → Binance REST API
│ │ # fetchDexScreenerPrice → DexScreener API
│ │ # fetchCoingeckoPrice → CoinGecko API
│ │ # resolveBasePrice → 动态基准价获取
│ ├── scheduler.js # node-cron 调度器(4 个数据源各自独立 Cron)
│ ├── seed.js # 初始数据种子:USDF + ETH
│ ├── .env # 凭据(OKX API keys, TG token, API key)
│ ├── .env.example # 凭据模板
│ ├── .gitignore # 忽略 node_modules, data/, .env
│ └── data/
│ └── monitor.db # SQLite 数据库文件(自动创建)
│
├── workers/ # ⚠️ 已弃用 - 旧 Cloudflare Workers 架构
│ ├── DEPRECATED.md
│ ├── shared/ # 旧共享模块
│ ├── api-gateway/ # 旧 API Worker
│ ├── binance-worker/ # 旧 Binance Worker
│ ├── okx-worker/ # 旧 OKX Worker
│ ├── coingecko-worker/ # 旧 CoinGecko Worker
│ └── dexscreener-worker/ # 旧 DexScreener Worker
│
├── frontend/ # 旧独立前端(已集成到 Hugo)
├── migrations/ # 旧 D1 DDL(仅参考)
└── tests/ # 旧测试
Hugo 站点相关文件:
D:\HAPPY\bithappy.xyz-main\
├── layouts/_default/
│ └── tool-arbitrage-monitor.html # 套利监控前端页面模板
├── static/js/
│ └── arbitrage-monitor.js # 前端交互 JS
└── content/toolbox/arbitrage-monitor/ # Hugo 内容页
7.2 开发与运行 #
# 安装依赖
cd arbitrage-monitor/backend
npm install
# 配置凭据
cp .env.example .env
# 编辑 .env 填入:
# OKX_API_KEY, OKX_SECRET_KEY, OKX_API_PASSPHRASE, OKX_PROJECT_ID
# TG_BOT_TOKEN(可选)
# API_KEY(前端认证用)
# 启动后端
npm start # 或 npm run dev(自动重载)
# 启动后自动:
# 1. 初始化 SQLite 数据库(首次创建表)
# 2. 种子 USDF + ETH 配置(仅数据库为空时)
# 3. 初始化 OKX DEX SDK 客户端
# 4. 注册 4 个数据源的 Cron 任务
# 5. 监听 http://localhost:8787
7.3 初始种子配置 #
| 币种 | 链 | 合约地址 | 基准价 | 可变 | 基准源 | 阈值 | 数据源 |
|---|---|---|---|---|---|---|---|
| USDF | Ethereum (1) | 0xfa2b947eec368f42195f24f36d2af29f7c24cec2 |
1.0 | 否 | - | 0.5% | okx-dex, dexscreener |
| ETH | Ethereum (1) | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
动态 | 是 | Binance ETHUSDT | 1.0% | okx-dex, binance |
八、异常处理与容错 #
| 场景 | 处理方式 |
|---|---|
| 价格 API 超时/失败 | 捕获异常,记录 console.error,不写 alert_log,不影响其他配置行 |
| OKX DEX SDK 无凭据 | 打印警告,okx-dex 数据源的 Cron 任务自动跳过 |
| OKX DEX 无流动性 | getQuote 返回空数据,抛出异常,跳过该配置行 |
| price_source_url 获取基准价失败 | 跳过该配置行本轮检查 |
| Telegram 发送失败 | alert_log.alert_sent = 0,但 backoff_level 不升级,下轮重试 |
| SQLite 写入失败 | 捕获异常,记录 console.error,下轮重试 |
| 配置被删除 | 调度器每轮都从 SQLite 读最新配置,自然跳过已删除项 |
九、安全考虑 #
- API 认证:所有
/api/*请求需携带X-API-KeyHeader,Key 配置在.env中。 - OKX 凭据:仅存储在
.env,不进入 Git(.gitignore排除)。 - Telegram Token:仅存储在 SQLite 的
tg_bots表和.env中,前端不接触。 - CORS:Express 使用
cors()中间件,可配置允许的 Origin。 - 输入校验:合约地址格式校验、URL 格式校验、数值范围校验。
十、已知代币地址参考 #
| 代币 | 链 | 地址 | 精度 |
|---|---|---|---|
| ETH (native) | Ethereum | 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE |
18 |
| USDF (Falcon Finance) | Ethereum | 0xfa2b947eec368f42195f24f36d2af29f7c24cec2 |
18 |
| USDT | Ethereum | 0xdAC17F958D2ee523a2206206994597C13D831ec7 |
6 |
| USDC | Ethereum | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 |
6 |
文档版本 v17 - 2026-02-11