feat: 手续费改为逐笔USD累算 + Vite React前端 + system_orders表 + README

This commit is contained in:
jackyu66git
2026-05-04 04:34:27 +08:00
parent 15a4684208
commit 0288dc8284
18 changed files with 2919 additions and 521 deletions
+93 -197
View File
@@ -1,216 +1,112 @@
# Exchange Monitor Go
# ⚡ 跨交易所永续合约套利监控
Cross-exchange perpetual futures arbitrage monitoring and automated trading system. Tracks **Bitget ↔ HyperLiquid** spread in real time, executes simulated trades at configurable thresholds.
Bitget ↔ HyperLiquid 跨交易所永续合约价差套利系统。支持模拟盘/实盘交易、价差监控、自动开仓/加仓/平仓、Web 仪表盘。
## Architecture
## 功能特点
- **实时价差监控** — 200ms 扫描间隔,追踪 DOGE/LINK/ONDO/OP/WIF/ARB 六个币种
- **自动套利交易** — 价差超过阈值自动开仓,收敛自动平仓,支持多级加仓
- **模拟/实盘双模式** — `TestMode` 控制,模拟模式无需真实 API Key
- **Web 仪表盘** — Go 内置 HTTP Server + Vite React 前端,SSE 实时推送
- **SQLite 持久化** — 交易记录、订单明细、手续费明细全量存储
- **手续费精确计算** — 逐笔累加实际 USD 手续费(开仓费+平仓费),非百分比估算
- **Telegram 通知** — 开仓/平仓/异常实时推送
## 架构
```
──────────────┐
│ Bitget │◄──── ticker WS (trading exchange)
└──────────────┘
PriceStore ─────────┼──────────────┤
│ HyperLiquid │◄──── webData2 WS (trading exchange)
└──────────────┘
┌─────────▼─────────┐
│ ScanBGHL (200ms)
│ BG ↔ HL only
└─────────┬─────────┘
┌───────────────▼────────────────┐
│ Trader │
│ TryEntry (async goroutine) │
│ → placeOrder (REST/mock) │
│ Tick / Exit / Scale-in │
│ Config-driven thresholds │
└───────────────┬────────────────┘
┌─────────▼─────────┐
│ SpreadWindowTracker│
│ (opportunity life) │
└─────────┬─────────┘
┌──────────────────┼──────────────────┐
│ │ │
┌─────▼─────┐ ┌────────▼───────┐ ┌─────▼─────┐
│ Notifier │ │ Dashboard │ │ DB │
│ Telegram │ │ :8888 │ │ SQLite │
│ │ │ Stats calc │ │ trades.db │
│ │ │ Blacklist UI │ │ │
└───────────┘ └────────────────┘ └───────────┘
┌─────────────────────────────────────────────────┐
│ scanner.go ← 每 200ms 扫描价差 │
│ ↓ 发现机会 (NetProfit > 阈值) │
│ trader.go ← 开仓/加仓/平仓逻辑 │
│ ↓ 持久化 │
│ db/ ← SQLite (trades / orders / system) │
│ ↓ SSE 推送
│ dashboard.go ← HTTP Server :8888 │
│ ↓
│ frontend/ ← Vite + React 仪表盘
└─────────────────────────────────────────────────┘
```
## Tracked Coins
## 快速开始
| Coin | Bitget | HyperLiquid |
|:----:|:---------:|:-----------:|
| DOGE | DOGEUSDT | DOGE |
| LINK | LINKUSDT | LINK |
| ONDO | ONDOUSDT | ONDO |
| OP | OPUSDT | OP |
| WIF | WIFUSDT | WIF |
| ARB | ARBUSDT | ARB |
### 1. 配置
> **Note:** Binance and dYdX have been removed — only Bitget and HyperLiquid are monitored.
编辑 `config.json`
## Requirements
```json
{
"scan_interval_ms": 200,
"trade_enabled": true,
"test_mode": true,
"trade_threshold": 0.10,
"take_profit_pct": 0.20,
"trade_amount_usd": 5,
"max_positions": 5,
"taker_fee_bitget": 0.060,
"taker_fee_hyperliquid": 0.045,
"telegram_bot_token": "xxx",
"telegram_chat_id": "xxx"
}
```
- Go 1.25+
- WebSocket connectivity to Bitget and HyperLiquid
## Quick Start
### 2. 启动
```bash
cd exchange-monitor-go
go build -o exchange-monitor .
# Edit config.json to set parameters
./exchange-monitor
# 一键启动(自动清理旧进程 + 编译 + 运行)
bash start.sh
# 清空数据库 + 启动
bash start.sh --clean
# 强制重新编译 + 启动
bash start.sh --rebuild
```
Then open [http://localhost:8888](http://localhost:8888) for the Web dashboard.
仪表盘地址:http://localhost:8888
## Configuration
### 3. 前端开发
### config.json (all trading parameters)
All numerical parameters are defined in `config.json`**no need to edit Go source**:
| Parameter | Default | Description |
|:----------|:-------:|:------------|
| `test_mode` | `true` | Simulate orders with mock fills (no real API calls) |
| `trade_enabled` | `true` | Enable automated trading |
| `scan_interval_ms` | `200` | Scanner loop interval (ms) |
| `arb_threshold` | `0.03` | Min net profit % to trigger alert |
| `trade_threshold` | `0.10` | Min net profit % to execute trade |
| `trade_amount_usd` | `5` | USD per leg (per order) |
| `trade_cooldown_ms` | `30000` | Cooldown between same-coin trades (ms) |
| `max_positions` | `5` | Maximum concurrent open positions |
| `initial_capital` | `1000` | Starting capital in USD (for PnL %) |
| `mock_slippage_pct` | `0.005` | Simulated slippage per leg (%) |
| `blacklist_duration_sec` | `3600` | Coin blacklist duration (seconds) |
| `taker_fee_bitget` | `0.060` | Bitget taker fee rate (%) |
| `taker_fee_hyperliquid` | `0.045` | HyperLiquid taker fee rate (%) |
| `take_profit_pct` | `0.20` | Net profit % threshold for take-profit |
| `spread_reverse_exit_pct` | `0` | Spread convergence/reversal exit (0 = exit when ≤ 0) |
| `position_timeout_sec` | `1800` | Max position hold time before auto-close (30 min) |
| `leg_delay_ms` | `300` | Delay between placing long and short legs |
| `reversal_tolerance_pct` | `0.1` | Price movement tolerance for entry sanity check |
| `scale_step_pct` | `0.10` | Spread widening % to trigger each scale-in level |
| `scale_cooldown_sec` | `5` | Minimum seconds between scale-ins |
### .env (secrets only)
Secrets (API keys) go in `.env` — never checked into git:
| Variable | Description |
|:---------|:------------|
| `TELEGRAM_BOT_TOKEN` | Telegram bot token for notifications |
| `TELEGRAM_CHAT_ID` | Target chat ID for notifications |
| `BITGET_API_KEY` | Bitget API key (skipped if test_mode) |
| `BITGET_API_SECRET` | Bitget API secret |
| `BITGET_PASSPHRASE` | Bitget passphrase |
| `HL_PRIVATE_KEY` | HyperLiquid ed25519 private key hex |
| `HL_ADDRESS` | HyperLiquid wallet address |
> **Priority:** `.env` vars > `config.json` > code defaults.
## Fee Model
All trades use **taker** (market orders). Only Bitget and HyperLiquid:
| Exchange | Taker Fee |
|:---------|:---------:|
| Bitget | configurable (`taker_fee_bitget`, default 0.060%) |
| HyperLiquid | configurable (`taker_fee_hyperliquid`, default 0.045%) |
Round trip (2 legs entry + 2 legs exit): configurable, default **0.21%** total fees.
## Trading Logic
1. **Scanner** runs every `scan_interval_ms`, checks all coins for BG ↔ HL spread
2. **Entry** when net profit ≥ `trade_threshold` (after full round-trip fees)
- Uses scan-time prices directly (no re-read to avoid WS jitter)
- **Async goroutine** — `TryEntry` returns immediately, `executeEntry` runs in background
- Reversal tolerance check prevents entry on flipped spreads
- `entering` map prevents duplicate entries on same coin
3. **Scale-in** adds another leg-worth when spread widens another `scale_step_pct` (default 0.10%)
4. **Exit** conditions (whichever hits first):
- **Net profit ≥ `take_profit_pct`** → **利润止盈**(大盈利退出)
- **Spread narrowed to ≤ 0.02% + netPnl > 0** → **价差收敛止盈**(小盈利退出)
- **Spread flipped negative** → **价差反转平仓**(紧急止损)
- **Position held > `position_timeout_sec`** → **超时平仓**
5. **Direction**: BG → HL (buy BG, sell HL) or HL → BG (buy HL, sell BG)
## Blacklist Mechanism
- Positions held open for > 10 minutes without converging are auto-closed and blacklisted
- Blacklisted coins are skipped for `blacklist_duration_sec` (default 1 hour)
- Blacklist state visible on the dashboard
## Spread Window Monitoring
`SpreadWindowTracker` runs every scan tick and measures how long each coin's spread stays above the trade threshold:
- Records window **start time** when net profit first hits threshold
- Tracks real **peak net profit** during the window
- Logs window **duration + peak** when spread converges below threshold
- Covers both directions (BG→HL and HL→BG) independently
## Web Dashboard
Built-in HTTP server at `:8888` with real-time SSE push (1-second refresh):
- **Price table** — live prices from both exchanges
- **BG↔HL spread** — per-coin arbitrage spread
- **Open positions** — live PnL estimate, scaling level, duration, sorting by coin
- **Trade history** — past trades with detail view
- **Blacklist** — currently blacklisted coins and remaining time
- **Connection status** — exchange health (online / stale / offline)
## DB & Persistence
- SQLite at current directory (auto-deleted on each restart in test mode)
- Tracks open positions across restarts (`restoreOpenPositions`)
- Stores all closed trades with full PnL details
## Signals
| Signal | Action |
|:-------|:-------|
| `Ctrl+C` / `SIGINT` | Graceful shutdown (closes all WS connections) |
| `SIGUSR1` | Dump convergence statistics to `trade_stats.txt` |
## Project Structure
```
exchange-monitor-go/
├── main.go # Entry point, WS startup, main loop
├── config.go # config.json + .env hierarchical config
├── config.json # All trading parameters (editable)
├── types.go # PriceStore, TrackedCoin, ArbOpportunity
├── scanner.go # ScanBGHL — arbitrage scanner
├── trader.go # Position management, entry/exit/scale-in
├── dashboard.go # Web server + SSE + stats calc
├── toaster.go # Telegram notifications
├── static.go # Embedded web static files
├── .env # Secrets only (API keys)
├── exchange/
│ ├── connector.go # Generic WS connector with reconnect
│ ├── hyperliquid.go # HyperLiquid webData2 WS
│ ├── hyperliquid_trade.go # HL REST trade API
│ ├── bitget.go # Bitget ticker WS (TextPing for stability)
│ ├── bitget_trade.go # Bitget REST trade API
│ ├── helpers.go # Package helpers
│ └── ping.go # Accessibility check tools
├── db/
│ ├── db.go # SQLite open/migrate
│ └── trade_repo.go # Trade record queries
└── web/static/
├── index.html # Dashboard HTML
├── app.js # SSE client + UI logic
└── style.css # Dashboard CSS
```bash
cd frontend
npm install
npm run dev # 开发模式 (Vite HMR :5173)
npm run build # 构建生产版本
```
## Disclaimer
后端优先从 `frontend/dist/` 读取静态文件(热加载),回退到 Go embed。
This software is for educational/research purposes. Use at your own risk. Cryptocurrency trading involves substantial risk of loss.
## 配置参数
| 参数 | 说明 | 默认 |
|------|------|------|
| `scan_interval_ms` | 扫描间隔 (ms) | 200 |
| `trade_threshold` | 开仓阈值 (%) | 0.10 |
| `take_profit_pct` | 止盈净利 (%) | 0.20 |
| `trade_amount_usd` | 每腿交易额 ($) | 5 |
| `max_positions` | 最大并行持仓 | 5 |
| `taker_fee_bitget` | Bitget 吃单费率 (%) | 0.060 |
| `taker_fee_hyperliquid` | HyperLiquid 吃单费率 (%) | 0.045 |
| `scale_step_pct` | 加仓步长 (%) | 0.10 |
| `position_timeout` | 最长持仓时间 | 10m |
| `leg_delay` | 两腿下单间隔 | 300ms |
## 数据库
SQLite (`data/trades.db`),三张核心表:
| 表 | 说明 |
|----|------|
| `trades` | 交易主表 — 价差、PnL、手续费 ($) |
| `orders` | 订单明细 — 每腿的开仓/加仓/平仓、手续费 ($) |
| `system_orders` | 系统订单 — 双向关联 long↔short 订单 |
## 版本历史
### v1.2 (当前)
-`system_orders` 表,记录系统级开仓/加仓/平仓
- ✨ 手续费改为逐笔累加 USD,不再用百分比估算
- ✨ Vite + React 前端,支持热加载
- ✨ Web 仪表盘持仓 PnL 美元化显示
- 🐛 修复 `persistTrade` 费用在 `SaveTrade` 后才累加导致 fee=0 的 bug
- 🗑 移除老版 Chart.js 图表