跨 DEX 永续合约套利机器人,运行在 Arbitrum 链上。监控 AZverse DEX 与多个永续合约交易所之间的价格差异,自动执行套利交易。
- 🔄 实时价格监控 - WebSocket 连接多个交易所,毫秒级价格更新
- 🎯 套利机会检测 - 自动计算价差、手续费、滑点,筛选有利可图的机会
- ⚡ 自动交易执行 - 同时在两个交易所开仓,捕获价差收益
- 📊 仓位管理 - 实时 PnL 监控,自动止盈止损
- 🛡️ 风险控制 - 仓位限制、日亏损限制、最大持仓时间
- 📢 告警通知 - 支持 Telegram、Discord、Webhook 通知
- 💾 数据持久化 - MySQL 存储交易记录和历史价格
- 📈 回测引擎 - 历史数据验证策略有效性
| 交易所 | 类型 | 状态 |
|---|---|---|
| AZverse | DEX (Arbitrum) | ✅ 已实现 |
| Paradex | DEX (Starknet) | ✅ 已实现 |
| Binance Futures | CEX | ✅ 已实现 |
- Rust 1.70+
- MySQL 8.0+
- 稳定的网络连接
# 克隆项目
git clone <repository-url>
cd perp-arbitrage-bot
# 编译
cargo build --release# 复制配置模板
cp .env.example .env
# 编辑配置文件
vim .env运行前需要先初始化数据库:
# 创建数据库
mysql -u root -p -e "CREATE DATABASE arbitrage_bot;"
# 执行建表脚本
mysql -u root -p arbitrage_bot < migrations/001_init.sql程序启动时也会自动检查并创建缺失的表。
# Dry Run 模式 (只观察,不交易)
DRY_RUN=true cargo run --release
# 正式交易模式
DRY_RUN=false cargo run --release# 复制配置文件
cp .env.example .env
# 编辑配置
vim .env
# 启动服务 (包含 MySQL)
docker-compose up -d
# 查看日志
docker-compose logs -f arbitrage-bot
# 停止服务
docker-compose down# 构建镜像
docker build -t perp-arbitrage-bot .
# 运行 (需要外部 MySQL)
docker run -d \
--name arbitrage-bot \
--env-file .env \
-e DB_HOST=host.docker.internal \
perp-arbitrage-bot# 使用生产配置
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d# 设置为 true 启用观察模式,只输出机会不执行交易
DRY_RUN=trueAZVERSE_WS_URL=wss://fstream.azverse.xyz/ws/market?type=SYMBOL
AZVERSE_API_URL=https://app.azverse.xyz/fapi
AZVERSE_TOKEN=your_token # 从浏览器 Cookie 获取
AZVERSE_CLIENT_CODE=your_code # 从浏览器 Cookie 获取
AZVERSE_SESSION_ID=your_session # JSESSIONID获取 AZverse 凭证:
- 登录 https://app.azverse.xyz
- 打开浏览器开发者工具 (F12)
- 在 Application > Cookies 中找到
token,client-code,JSESSIONID
BINANCE_WS_URL=wss://fstream.binance.com/stream?streams=btcusdt@bookTicker/ethusdt@bookTicker
BINANCE_API_URL=https://fapi.binance.com
BINANCE_API_KEY=your_api_key
BINANCE_API_SECRET=your_api_secret
BINANCE_ENABLED=true
BINANCE_MAKER_FEE=0.02
BINANCE_TAKER_FEE=0.04PARADEX_WS_URL=wss://ws.api.prod.paradex.trade/v1?cancel-on-disconnect=false
PARADEX_API_URL=https://api.prod.paradex.trade/v1
PARADEX_ENABLED=true
PARADEX_MAKER_FEE=0.02
PARADEX_TAKER_FEE=0.05Paradex 说明:
- WebSocket 使用 JSON-RPC 格式
- 交易对格式:
BTC-USD-PERP,ETH-USD-PERP - 自动订阅 order_book 频道获取最优买卖价
- 无需 API Key 即可获取行情数据 (交易需要)
# 单个交易对最大仓位 (USD)
MAX_POSITION_SIZE_USD=10000
# 总敞口上限 (USD)
MAX_TOTAL_EXPOSURE_USD=50000
# 日亏损限制 (USD) - 超过后暂停交易
DAILY_LOSS_LIMIT_USD=1000
# 止损百分比
STOP_LOSS_PERCENT=2.0
# 最大持仓时间 (秒) - 超过后强制平仓
MAX_POSITION_DURATION_SECS=3600# 最小价差阈值 (%) - 低于此值不开仓
MIN_SPREAD_PERCENT=0.1
# 平仓价差阈值 (%) - 价差收敛到此值时平仓
EXIT_SPREAD_PERCENT=0.02
# 告警价差阈值 (%)
ALERT_SPREAD_PERCENT=0.05
# 交易对列表
TRADING_PAIRS=BTC_USDT,ETH_USDT# Telegram
TELEGRAM_BOT_TOKEN=your_bot_token
TELEGRAM_CHAT_ID=your_chat_id
# Discord
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
# 告警频率限制 (每分钟)
ALERT_RATE_LIMIT=10DB_HOST=localhost
DB_PORT=3306
DB_USERNAME=root
DB_PASSWORD=your_password
DB_DATABASE=arbitrage_bot只监控价格和检测机会,不执行实际交易。用于验证策略和配置。
DRY_RUN=true cargo run --release输出示例:
INFO 🔍 DRY RUN MODE ENABLED
INFO 📊 Price: AZverse BTC_USDT | bid=50000.00 ask=50010.00 mid=50005.00
INFO 📊 Price: Binance BTC_USDT | bid=50050.00 ask=50060.00 mid=50055.00
INFO 🎯 OPPORTUNITY: BTC_USDT | spread=0.10% | net_profit=$15.00 | long@AZverse short@Binance
INFO └─ [DRY RUN] Would execute: BUY on AZverse, SELL on Binance | size=$10000.00
DRY_RUN=false cargo run --release- 价格监控: 通过 WebSocket 实时获取各交易所价格
- 机会检测: 当 AZverse 与对冲交易所价差超过阈值时触发
- 方向判断:
- AZverse 价格低 → 在 AZverse 开多,对冲交易所开空
- AZverse 价格高 → 在 AZverse 开空,对冲交易所开多
- 利润计算:
净利润 = 毛利润 - 手续费 - Gas费 - 滑点 - 平仓时机:
- 价差收敛到退出阈值
- 达到止盈/止损
- 超过最大持仓时间
- 价格剧烈波动导致亏损
- 交易所 API 故障
- 网络延迟导致执行失败
- 流动性不足导致滑点过大
建议:
- 先使用 Dry Run 模式观察
- 从小仓位开始
- 设置合理的止损
- 监控系统运行状态
回测引擎用于在历史数据上验证策略效果。
# 使用默认参数运行回测(使用模拟数据)
cargo run --bin backtest
# 自定义参数
cargo run --bin backtest -- \
--start-date 2024-01-01 \
--end-date 2024-12-31 \
--capital 50000 \
--min-spread 0.15 \
--exit-spread 0.03 \
--position-size 5
# 运行参数优化扫描
cargo run --bin backtest -- --sweep| 参数 | 说明 | 默认值 |
|---|---|---|
--start-date |
开始日期 (YYYY-MM-DD) | 30天前 |
--end-date |
结束日期 (YYYY-MM-DD) | 今天 |
--capital |
初始资金 (USD) | 10000 |
--min-spread |
最小价差阈值 (%) | 0.1 |
--exit-spread |
平仓价差阈值 (%) | 0.02 |
--position-size |
仓位大小 (% of capital) | 10 |
--sweep |
运行参数优化扫描 | - |
╔══════════════════════════════════════════════════════════════╗
║ Backtest Results ║
╠══════════════════════════════════════════════════════════════╣
║ Performance Summary ║
╠══════════════════════════════════════════════════════════════╣
║ Initial Capital: $ 10000.00 ║
║ Final Capital: $ 12500.00 ║
║ Total Return: 25.00% ║
║ Total PnL: $ 2500.00 ║
╠══════════════════════════════════════════════════════════════╣
║ Trade Statistics ║
╠══════════════════════════════════════════════════════════════╣
║ Total Trades: 150 ║
║ Winning Trades: 120 ║
║ Losing Trades: 30 ║
║ Win Rate: 80.00% ║
╠══════════════════════════════════════════════════════════════╣
║ Risk Metrics ║
╠══════════════════════════════════════════════════════════════╣
║ Max Drawdown: $ 500.00 ║
║ Sharpe Ratio: 2.50 ║
║ Profit Factor: 3.20 ║
╚══════════════════════════════════════════════════════════════╝
- 先运行 bot 收集历史价格数据到数据库
- 修改
src/bin/backtest.rs中的generate_demo_price_data函数,改为从数据库加载:
// 从数据库加载价格数据
let price_data = data_store.get_price_history(
"BTC_USDT",
"AZverse",
config.start_date,
config.end_date
).await?;在 AZverse 和 Paradex 之间进行低成本交易量刷取,使用限价单 (Maker) 降低手续费。
| 费用类型 | AZverse | Paradex | 说明 |
|---|---|---|---|
| Maker 费率 | 0.02% | 0.02% | 限价单成交 |
| Taker 费率 | 0.05% | 0.05% | 市价单成交 |
| 往返成本 (Maker) | - | - | 0.08% |
使用 Maker 单可将成本从 0.20% 降至 0.08%,刷 $10,000 交易量节省 $12。
# 默认配置回测 (7天模拟数据)
cargo run --bin volume_farming
# 自定义参数
cargo run --bin volume_farming -- \
--min-spread 0.08 \
--exit-spread 0.02 \
--position-size 1000 \
--max-hold 600 \
--days 14
# 参数优化扫描 (找最优配置)
cargo run --bin volume_farming -- --sweep
# 使用预设模式
cargo run --bin volume_farming -- --mode minimum_cost # 最低成本模式
cargo run --bin volume_farming -- --mode aggressive # 激进刷量模式
# 使用真实数据回测 (需要先收集数据)
cargo run --bin volume_farming -- --real-data --days 7| 参数 | 说明 | 默认值 |
|---|---|---|
--min-spread |
最小价差开仓阈值 (%) | 0.10 |
--exit-spread |
价差收敛平仓阈值 (%) | 0.02 |
--position-size |
单笔仓位大小 (USD) | 500 |
--max-hold |
最大持仓时间 (秒) | 300 |
--cooldown |
交易冷却时间 (秒) | 30 |
--days |
回测天数 | 7 |
--mode |
预设模式: default/minimum_cost/aggressive | default |
--sweep |
运行参数优化扫描 | - |
--real-data |
使用数据库中的真实数据 | 否 (默认模拟数据) |
回测支持两种数据来源:
| 数据类型 | 说明 | 适用场景 |
|---|---|---|
| 模拟数据 (默认) | 随机生成的价格数据,8% 概率产生价差机会 | 快速验证策略逻辑 |
真实数据 (--real-data) |
从数据库 price_snapshots 表读取 |
准确评估策略效果 |
-
先运行 bot 收集历史价格数据:
# 以 Dry Run 模式运行,只收集数据不交易 DRY_RUN=true cargo run --release -
确保数据库中有数据:
- 数据存储在
price_snapshots表 - 需要 AZverse 和 Paradex 的价格数据
- 建议至少收集 1-2 天的数据
- 数据存储在
-
运行真实数据回测:
cargo run --bin volume_farming -- --real-data --days 7
# .env 刷量配置
MIN_SPREAD_PERCENT=0.06
EXIT_SPREAD_PERCENT=0.02
MAX_POSITION_SIZE_USD=500
MAX_POSITION_DURATION_SECS=600╔══════════════════════════════════════════════════════════════╗
║ Volume Farming Backtest Results ║
╠══════════════════════════════════════════════════════════════╣
║ Volume Statistics ║
╠══════════════════════════════════════════════════════════════╣
║ Total Trades: 3845 ║
║ Total Volume: $ 3845000.00 ║
║ Avg Volume/Trade: $ 1000.00 ║
╠══════════════════════════════════════════════════════════════╣
║ Cost Analysis ║
╠══════════════════════════════════════════════════════════════╣
║ Total Fees Paid: $ 1538.00 ║
║ Spread PnL: $ 1125.27 ║
║ Net PnL: $ -412.73 ║
║ Cost per $1000 Vol: $ 0.5073 ║
╠══════════════════════════════════════════════════════════════╣
║ Trade Performance ║
╠══════════════════════════════════════════════════════════════╣
║ Win Rate: 51.8% ║
║ Avg Spread Captured: -0.1314% ║
║ Avg Hold Time: 143s ║
║ Spread Closes: 3136 ║
║ Timeout Closes: 522 ║
╚══════════════════════════════════════════════════════════════╝
✅ 刷 $100 万交易量成本约 $507 (0.05%)
- 开仓条件: 当 AZverse 与 Paradex 价差超过阈值时
- 对冲方式: 价格低的交易所做多,价格高的做空
- 平仓条件:
- 价差收敛到退出阈值 (盈利平仓)
- 超过最大持仓时间 (超时平仓)
- 费用优化: 使用限价单 (Maker) 而非市价单 (Taker)
src/
├── main.rs # 主入口,交易循环
├── lib.rs # 库导出
├── config.rs # 配置加载和验证
├── types.rs # 核心数据类型
├── price_monitor.rs # WebSocket 价格监控
├── opportunity.rs # 套利机会检测
├── executor.rs # 交易执行
├── position.rs # 仓位管理
├── risk.rs # 风险控制
├── alert.rs # 告警服务
├── storage.rs # 数据库操作
├── backtest.rs # 回测引擎
└── azverse.rs # AZverse 适配器
// 获取价格
let price = monitor.get_price("AZverse", "BTC_USDT").await;
// 检查是否过期
let is_stale = monitor.is_stale("AZverse").await;// 检测机会
let opportunities = detector.detect_opportunities(&prices, position_size);
// 选择最佳机会
let best = detector.select_best_opportunity(&opportunities);// 执行套利
let result = executor.execute_arbitrage(&opportunity, size).await;
// 平仓
let close_result = executor.close_arbitrage_position(&position).await;// 检查是否可以开仓
risk_manager.can_open_position(size)?;
// 记录 PnL
risk_manager.record_pnl(pnl);
// 检查是否暂停交易
if risk_manager.is_trading_paused() { ... }# 运行所有测试
cargo test
# 运行特定模块测试
cargo test opportunity::tests
# 运行属性测试
cargo test prop_# 详细日志
RUST_LOG=debug cargo run
# 只显示警告和错误
RUST_LOG=warn cargo run
# 特定模块日志
RUST_LOG=perp_arbitrage_bot::opportunity=debug cargo runA: 检查网络连接和 WebSocket URL 配置。系统会自动重连,使用指数退避策略。
A: 检查 API 凭证是否正确,账户余额是否充足。查看日志获取详细错误信息。
A: 可能是价差未达到阈值。尝试降低 MIN_SPREAD_PERCENT 或使用 Dry Run 模式观察。
A: 登录 AZverse 网站后,从浏览器 Cookie 中获取 token, client-code, JSESSIONID。
MIT License