Files
exchange-monitor-go/persistence.md
T

396 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 曲线图
```