# 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. ## Architecture ``` ┌──────────────┐ │ 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 │ │ │ └───────────┘ └────────────────┘ └───────────┘ ``` ## Tracked Coins | Coin | Bitget | HyperLiquid | |:----:|:---------:|:-----------:| | DOGE | DOGEUSDT | DOGE | | LINK | LINKUSDT | LINK | | ONDO | ONDOUSDT | ONDO | | OP | OPUSDT | OP | | WIF | WIFUSDT | WIF | | ARB | ARBUSDT | ARB | > **Note:** Binance and dYdX have been removed — only Bitget and HyperLiquid are monitored. ## Requirements - Go 1.25+ - WebSocket connectivity to Bitget and HyperLiquid ## Quick Start ```bash cd exchange-monitor-go go build -o exchange-monitor . # Edit config.json to set parameters ./exchange-monitor ``` Then open [http://localhost:8888](http://localhost:8888) for the Web dashboard. ## Configuration ### 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 ``` ## Disclaimer This software is for educational/research purposes. Use at your own risk. Cryptocurrency trading involves substantial risk of loss.