Files
exchange-monitor-go/persistence.md
T

21 KiB
Raw Blame History

Dashboard Design

1. 目录结构

exchange-monitor-go/
├── main.go                    # 入口:启动 engine + web server
├── config.go                  # 配置加载(不变)
├── types.go                   # 公共类型(不变)
│
├── engine/                    # 核心交易引擎(从 main.go 拆分)
│   ├── engine.go              # Engine 结构体:组合所有模块
│   ├── scanner.go             # 价差扫描(从 scanner.go 移入)
│   ├── trader.go              # 交易执行(从 trader.go 移入)
│   ├── notifier.go            # Telegram 通知(从 notifier.go 移入)
│   └── portfolio.go           # 资金管理 + PnL 聚合
│
├── exchange/                  # 交易所连接(不变)
│   └── ...
│
├── db/                        # SQLite 持久化层(新增)
│   ├── db.go                  # DB 初始化、迁移
│   ├── trade_repo.go          # 交易记录 CRUD
│   ├── order_repo.go          # 订单明细 CRUD
│   └── config_repo.go         # 配置快照
│
├── web/                       # Web 仪表盘(新增)
│   ├── server.go              # HTTP 服务器 + 路由
│   ├── handler_dashboard.go   # 页面渲染
│   ├── handler_api.go         # REST API
│   ├── handler_sse.go         # SSE 实时推送
│   ├── static/                # 前端静态资源(go:embed
│   │   ├── index.html
│   │   ├── app.js
│   │   └── style.css
│   └── ws_monitor.go          # WS 状态监控
│
├── risk/                      # 风控层(新增)
│   └── risk.go                # 风控规则引擎
│
├── persistence.md             # 本设计文档
└── ...

2. 数据模型 (SQLite)

┌─────────────────────────────────────────────────────┐
│                    trades                            │
├──────────────┬──────────┬───────────────────────────┤
│ id           │ INTEGER  │ PRIMARY KEY AUTOINCREMENT │
│ coin         │ TEXT     │ NOT NULL                  │
│ direction    │ TEXT     │ BG->HL / HL->BG           │
│ status       │ TEXT     │ open / closed             │
│ entry_spread │ REAL     │ 进场价差 %                │
│ exit_spread  │ REAL     │ 出场价差 %                │
│ long_ex      │ TEXT     │ 多腿交易所                │
│ short_ex     │ TEXT     │ 空腿交易所                │
│ long_entry   │ REAL     │ 多腿进场价                │
│ long_exit    │ REAL     │ 多腿出场价                │
│ short_entry  │ REAL     │ 空腿进场价                │
│ short_exit   │ REAL     │ 空腿出场价                │
│ long_pnl     │ REAL     │ 多腿 PnL %               │
│ short_pnl    │ REAL     │ 空腿 PnL %               │
│ fee_entry    │ REAL     │ 开仓手续费 %              │
│ fee_exit     │ REAL     │ 平仓手续费 %              │
│ net_pnl      │ REAL     │ 净利 %                   │
│ amount_usd   │ REAL     │ 总金额 $                 │
│ scale_count  │ INTEGER  │ 加仓次数                   │
│ exit_reason  │ TEXT     │ 止盈/止损/超时             │
│ convergence  │ TEXT     │ 收敛/发散/持平              │
│ opened_at    │ DATETIME │                           │
│ closed_at    │ DATETIME │                           │
└──────────────┴──────────┴───────────────────────────┘

┌─────────────────────────────────────────────────────┐
│                    orders (每腿一条)                   │
├──────────────┬──────────┬───────────────────────────┤
│ id           │ INTEGER  │                           │
│ trade_id     │ INTEGER  │ FK → trades.id            │
│ leg          │ TEXT     │ long / short              │
│ type         │ TEXT     │ entry / exit / scale      │
│ exchange     │ TEXT     │                           │
│ side         │ TEXT     │ buy / sell                │
│ price        │ REAL     │ 成交价                    │
│ size         │ REAL     │ 数量                      │
│ fee          │ REAL     │ 手续费                    │
│ order_id     │ TEXT     │ 交易所订单 ID             │
│ status       │ TEXT     │ filled / cancelled        │
│ created_at   │ DATETIME │                           │
└──────────────┴──────────┴───────────────────────────┘

┌─────────────────────────────────────────────────────┐
│                  price_snapshots                      │
├──────────────┬──────────┬───────────────────────────┤
│ id           │ INTEGER  │                           │
│ coin         │ TEXT     │                           │
│ exchange     │ TEXT     │                           │
│ price        │ REAL     │                           │
│ bid          │ REAL     │                           │
│ ask          │ REAL     │                           │
│ spread_basis │ REAL     │ bid-ask spread %           │
│ recorded_at  │ DATETIME │                           │
└──────────────┴──────────┴───────────────────────────┘

┌─────────────────────────────────────────────────────┐
│                 config_snapshots                      │
├──────────────┬──────────┬───────────────────────────┤
│ id           │ INTEGER  │                           │
│ key          │ TEXT     │ 参数名                    │
│ value        │ TEXT     │ 参数值                    │
│ changed_at   │ DATETIME │                           │
│ changed_by   │ TEXT     │ web / cli                 │
└──────────────┴──────────┴───────────────────────────┘

3. REST API

Base URL: http://localhost:8080/api/v1

┌────────┬────────────────────────┬────────────────────────────┐
│ Method │ Path                    │ 说明                       │
├────────┼────────────────────────┼────────────────────────────┤
│ GET    │ /api/v1/stats/summary   │ 总览指标                   │
│ GET    │ /api/v1/stats/coins     │ 各币种明细                 │
│ GET    │ /api/v1/stats/pnl       │ PnL 曲线(按天/时)        │
│ GET    │ /api/v1/stats/daily     │ 每日统计                   │
├────────┼────────────────────────┼────────────────────────────┤
│ GET    │ /api/v1/trades         │ 交易列表(分页)            │
│ GET    │ /api/v1/trades/:id     │ 单笔交易详情 + 订单明细    │
│ GET    │ /api/v1/trades/active  │ 当前持仓                   │
├────────┼────────────────────────┼────────────────────────────┤
│ GET    │ /api/v1/exchanges      │ 交易所连接状态             │
│ GET    │ /api/v1/prices         │ 所有币种实时价差           │
├────────┼────────────────────────┼────────────────────────────┤
│ GET    │ /api/v1/config         │ 当前配置                   │
│ PUT    │ /api/v1/config         │ 更新配置                   │
├────────┼────────────────────────┼────────────────────────────┤
│ GET    │ /api/v1/status         │ 系统运行状态(uptime等)    │
│ POST   │ /api/v1/action/restart │ 重启扫描器                 │
└────────┴────────────────────────┴────────────────────────────┘

GET /api/v1/stats/summary 响应:
{
  "total_trades": 387,
  "total_pnl_pct": 4.27,
  "total_pnl_usd": 0.85,
  "win_rate": 56.5,
  "avg_pnl_pct": 0.011,
  "max_drawdown": -2.1,
  "active_positions": 3,
  "running_time": "13h 22m",
  "exchanges_connected": 4,
  "mode": "simulation"
}

GET /api/v1/stats/coins 响应:
[
  {
    "coin": "ONDO",
    "trades": 115,
    "pnl_pct": 3.11,
    "win_rate": 56.5,
    "avg_pnl": 0.027,
    "best_trade": 0.18,
    "worst_trade": -0.05,
    "long_pct": 94,
    "short_pct": 6,
    "active": true
  },
  ...
]

GET /api/v1/trades?page=1&limit=20&coin=ONDO 响应:
{
  "trades": [
    {
      "id": 1,
      "coin": "ONDO",
      "direction": "BG->HL",
      "entry_spread": 0.17,
      "exit_spread": 0.01,
      "net_pnl": 0.10,
      "duration": "52s",
      "opened_at": "2026-05-03T15:42:00+08:00",
      "scale_count": 0
    }
  ],
  "total": 115,
  "page": 1
}

4. SSE (Server-Sent Events) 实时推送

Endpoint: GET /api/v1/stream

────────────── 连接建立 ──────────────→

←── event: snapshot  ── 全量数据推送 ──
    { prices: {...}, positions: [...], summary: {...} }

←── event: price_update  ── 价差变化 ── (每 500ms)
    { coin: "ONDO", spread: 0.15, bg: 0.28, hl: 0.2815 }

←── event: trade_opened  ── 新开仓 ──
    { id: 42, coin: "ONDO", direction: "BG->HL", spread: 0.17, ... }

←── event: trade_closed  ── 平仓 ──
    { id: 42, net_pnl: 0.10, exit_spread: 0.01, ... }

←── event: exchange_status  ── WS 状态变化 ──
    { exchange: "Bitget", connected: true, latency_ms: 120 }

←── event: alert  ── 系统告警 ──
    { level: "warn", message: "WS reconnected", ... }

5. 前端页面布局

┌──────────────────────────────────────────────────────┐
│ [logo] 套利机器人仪表盘          [模拟/实盘] [设置]   │
├──────────────────────────────────────────────────────┤
│ ┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ ┌────────┐ │
│ │总交易  │ │总净利   │ │胜率    │ │当前持仓│ │运行时间 │ │
│ │ 387   │ │+4.27%  │ │56.5%  │ │  3    │ │ 13h22m │ │
│ └───────┘ └───────┘ └───────┘ └───────┘ └────────┘ │
├──────────────────────────────────────────────────────┤
│  Tab: [📈 概览] [📋 交易记录] [⚙️ 配置] [🔌 连接]  │
├──────────────────────────────────────────────────────┤
│                                                        │
│  Tab: 概览                                              │
│  ┌──────────────────────────────────────────────────┐  │
│  │  价差实时折线图(可切换币种)                      │  │
│  │  ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲                            │  │
│  │  ──── 0.1% 阈值线 ────                            │  │
│  │  ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱                            │  │
│  │  [ONDO] [WIF] [OP] [DOGE] [ARB] [LINK]           │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌────┬──────┬──────┬──────┬──────┬──────┬───────┐    │
│  │币种 │方向  │持仓$ │价差% │已持  │浮动% │ P&L$  │    │
│  ├────┼──────┼──────┼──────┼──────┼──────┼───────┤    │
│  │ONDO│BG→HL │$5    │0.15  │12s   │+0.10 │+0.005 │    │
│  │WIF │BG→HL │$10   │0.08  │2m30s │+0.22 │+0.022 │    │
│  │OP  │HL→BG │$5    │0.22  │1m    │+0.05 │+0.003 │    │
│  └────┴──────┴──────┴──────┴──────┴──────┴───────┘    │
│                                                        │
│  最近成交                                              │
│  15:42:23  ONDO  BG→HL  入场0.17% 出场0.01% +0.10%    │
│  15:41:55  WIF   BG→HL  入场0.15% 出场0.02% +0.08%    │
│  15:41:30  OP    HL→BG  入场0.22% 出场0.01% +0.15%    │
│                                                        │
│  Tab: 交易记录                                          │
│  ┌────┬──────┬──────┬──────┬──────┬──────┬──────┬───┐  │
│  │时间│币种  │方向  │入场   │出场  │净利%  │持仓  │详情│  │
│  ├────┼──────┼──────┼──────┼──────┼──────┼──────┼───┤  │
│  │... │      │      │      │      │      │      │  >│  │
│  └────┴──────┴──────┴──────┴──────┴──────┴──────┴───┘  │
│  [上一页]                              [1/23] [下一页]   │
│                                                        │
│  Tab: 配置                                              │
│  阈值: [0.1%   ]   每腿金额: [$5    ]   模式:  ○模拟   │
│  冷却时间: [30000]ms  最大持仓: [3    ]   ●实盘         │
│  [保存配置]                                             │
│                                                        │
│  Tab: 连接                                              │
│  ┌──────────┬──────────┬───────┬──────────┐            │
│  │交易所    │状态      │延迟   │最后更新   │            │
│  ├──────────┼──────────┼───────┼──────────┤            │
│  │Bitget    │● 已连接  │120ms  │15:42:23  │            │
│  │HL        │● 已连接  │85ms   │15:42:23  │            │
│  │Binance   │● 已连接  │90ms   │15:42:22  │            │
│  │dYdX      │⚠ 重连中  │--     │15:41:48  │            │
│  └──────────┴──────────┴───────┴──────────┘            │
└──────────────────────────────────────────────────────┘

6. 前端技术选型

框架:   无框架,纯 HTML + CSS + vanilla JS
        原因:零构建步骤,单文件嵌入

图表:   Chart.js (CDN https://cdn.jsdelivr.net/npm/chart.js)
        原因:轻量、灵活、CDN 无需 npm

实时通: EventSource (浏览器原生 SSE)
        原因:比 WebSocket 简单,自动重连

UI:     纯 CSS Grid + Flexbox
        深色主题(适合交易屏长时间看)

体积:   < 300KB 总大小(含 Chart.js CDN

7. Web Server 设计 (Go)

// web/server.go

package web

type Server struct {
    engine *engine.Engine
    db     *db.DB
    mux    *http.ServeMux
    sse    *SSEHub  // SSE 连接管理器
}

// SSEHub 管理所有 SSE 客户端连接
type SSEHub struct {
    clients    map[chan SSEEvent]struct{}
    register   chan chan SSEEvent
    unregister chan chan SSEEvent
    broadcast  chan SSEEvent
}

// 从 Engine 接收事件并广播
func (h *SSEHub) Broadcast(eventType string, data interface{})

8. 数据流

三层数据分层

┌──────────────────────────────────────────────┐
│  HOT (内存 only, 500ms)                       │
│  PriceStore: 6币×4所 实时价                   │
│  Trader.positions: 当前持仓                    │
│  Scanner: 扫描结果                             │
│  不落盘,重启丢失,但重连 WS 秒恢复             │
├──────────────────────────────────────────────┤
│  WARM (SQLite, 事件驱动)                      │
│  平仓 → INSERT trades                        │
│  每腿成交 → INSERT orders                     │
│  配置修改 → INSERT/UPSERT config_snapshots    │
│  WS 重连 → INSERT exchange_events(可选)       │
│  写入频率: < 1次/秒                           │
├──────────────────────────────────────────────┤
│  COLD (时序方案待定, 未来)                     │
│  价格存档:每分钟采样 × 6币 × 4所             │
│  日产量: ~34,560行 → 可存 SQLite 也可用       │
│  TimescaleDB / InfluxDB (如果要做回测平台)    │
└──────────────────────────────────────────────┘

实时数据 → 网页

WS 数据流 (500ms):
┌─────────┐   price   ┌──────────┐   SSE push   ┌─────────┐
│ Exchange│──────────►│  Engine  │─────────────►│ Browser │
│   WS    │          │ (HOT层)   │              │(实时更新)│
└─────────┘          └────┬─────┘              └─────────┘
                          │ 仅事件写入
                    ┌─────▼──────┐
                    │  SQLite     │
                    │ (WARM层)    │
                    └────────────┘

API 请求 (读 WARM 层):
┌─────────┐   GET /api/...   ┌──────────┐   SQL    ┌────────┐
│ Browser │────────────────►│  Server  │──────────►│ SQLite │
│ (页面)  │◄────────────────│(REST API)│◄──────────┘        │
└─────────┘     JSON        └──────────┘

9. 实现顺序

Phase 1 — 基础设施
  1. db/ 包:SQLite 初始化 + schema 迁移
  2. 程序启动时保存交易记录到 SQLite
  3. 重启时从 SQLite 恢复历史数据

Phase 2 — Web Server
  1. web/server.go:路由 + SSE Hub
  2. REST APIsummary, trades, prices, config
  3. 前端 index.html:概览页(指标卡片 + 当前持仓 + 最近成交)

Phase 3 — 实时
  1. SSE stream:价格、持仓、交易实时推送
  2. Chart.js 实时价差折线图

Phase 4 — 完善
  1. 交易记录页(分页、筛选、详情弹窗)
  2. 配置页(在线修改参数)
  3. 连接状态页
  4. PnL 曲线图