前端

前端交易架构设计

详细的交易页面架构设计,涵盖 WebSocket 实时数据流、订单簿渲染、K 线图集成、下单面板以及高性能前端优化方案。

1. 前端整体架构

组件化

每个功能区域独立组件,通过全局 Store 共享数据。组件间松耦合,可独立测试和替换。

双通道

REST API 用于下单/撤单/查询等操作;WebSocket 用于接收实时行情、订单状态、成交回报。

离线缓存

K 线历史数据缓存到 IndexedDB,避免切换周期时重复请求。订单簿增量更新减少带宽。

响应式

桌面端多面板布局;移动端 Tab 切换。使用 CSS Grid 实现自适应,支持面板拖拽调整。

2. WebSocket 连接管理

class WebSocketManager {
  private ws: WebSocket | null = null;
  private subscriptions: Map<string, Set<Function>> = new Map();
  private reconnectAttempts = 0;
  private maxReconnectAttempts = 10;
  private heartbeatInterval: NodeJS.Timer;

  connect(url: string) {
    this.ws = new WebSocket(url);
	    this.ws.onopen = () => {
	      this.reconnectAttempts = 0;
	      this.resubscribeAll();       // Resubscribe after reconnect
	      this.startHeartbeat();
	    };
    this.ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      this.dispatch(msg.channel, msg.data);
    };
    this.ws.onclose = () => this.reconnect();
  }

  subscribe(channel: string, callback: Function) {
    if (!this.subscriptions.has(channel)) {
      this.subscriptions.set(channel, new Set());
      this.send({ action: "subscribe", channel });
    }
    this.subscriptions.get(channel)!.add(callback);
  }

  unsubscribe(channel: string, callback: Function) {
    this.subscriptions.get(channel)?.delete(callback);
    if (this.subscriptions.get(channel)?.size === 0) {
      this.send({ action: "unsubscribe", channel });
      this.subscriptions.delete(channel);
    }
  }

  private reconnect() {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) return;
    const delay = Math.min(1000 * 2 ** this.reconnectAttempts, 30000);
    setTimeout(() => {
      this.reconnectAttempts++;
      this.connect(this.url);
    }, delay);
  }

  private startHeartbeat() {
    this.heartbeatInterval = setInterval(() => {
      this.send({ action: "ping" });
    }, 15000);
  }
}

3. WebSocket 协议设计

订阅频道列表

depth

订单簿深度。首次推送全量快照,之后增量更新。频道: depth@BTCUSDT。包含 bids/asks 二维数组 [price, qty]。

trade

逐笔成交。实时推送每一笔成交记录。频道: trade@BTCUSDT。包含 price, qty, side, time。

kline

K 线更新。按订阅周期推送当前蜡烛的 OHLCV 更新。频道: kline@BTCUSDT:1m。窗口关闭时 isFinal=true。

ticker

24h 行情摘要。包含最新价、涨跌幅、24h 高低价、成交量。频道: ticker@BTCUSDT。约 1 秒推送一次。

order

用户订单更新(需鉴权)。推送订单状态变更、成交回报。频道: order@{userId}。包含完整订单对象。

account

用户账户更新(需鉴权)。余额变更、持仓变更推送。频道: account@{userId}。

消息格式

// 订阅请求
{ "action": "subscribe", "channel": "depth@BTCUSDT" }

// 订单簿全量快照(首次)
{
  "channel": "depth@BTCUSDT",
  "type": "snapshot",
  "data": {
    "lastUpdateId": 100,
    "bids": [["41950.00","1.500"],["41900.00","2.300"]],
    "asks": [["42000.00","0.800"],["42050.00","1.200"]]
  }
}

// 订单簿增量更新
{
  "channel": "depth@BTCUSDT",
  "type": "update",
  "data": {
    "firstUpdateId": 101,
    "lastUpdateId": 103,
    "bids": [["41950.00","1.200"]],     // qty 变更
    "asks": [["42000.00","0.000"]]      // qty=0 表示删除该价位
  }
}

// 逐笔成交
{
  "channel": "trade@BTCUSDT",
  "type": "trade",
  "data": { "tradeId": 1001, "price": "42000.00", "qty": "0.5",
            "buyerIsMaker": false, "time": 1700000000000 }
}

// K 线更新
{
  "channel": "kline@BTCUSDT:1m",
  "type": "kline",
  "data": { "openTime": 1700000000000, "open": "42000", "high": "42100",
            "low": "41950", "close": "42050", "volume": "123.5",
            "isFinal": false }
}

WebSocket 消息流模拟

点击按钮模拟 WebSocket 连接、订阅、推送的完整消息流:

WebSocket 消息流 — 模拟前后端实时通信
未连接
已发送 0 已接收 0
客户端
(Browser)
WebSocket Channel
Client → Server
Server → Client
未建立连接
服务端
(Server)
暂无消息,请先点击“连接”按钮建立 WebSocket 连接

4. 订单簿可视化

// Local order-book maintenance logic
class LocalOrderBook {
  bids: Map<string, string> = new Map(); // price → qty
  asks: Map<string, string> = new Map();
  lastUpdateId = 0;

  applySnapshot(snapshot) {
    this.bids.clear();
    this.asks.clear();
    snapshot.bids.forEach(([p, q]) => this.bids.set(p, q));
    snapshot.asks.forEach(([p, q]) => this.asks.set(p, q));
    this.lastUpdateId = snapshot.lastUpdateId;
  }

  applyUpdate(update) {
    // Drop stale updates
    if (update.lastUpdateId <= this.lastUpdateId) return;
    // Detect sequence gap
    if (update.firstUpdateId > this.lastUpdateId + 1) {
      this.requestSnapshot(); // Request fresh full snapshot
      return;
    }
    update.bids.forEach(([p, q]) => {
      if (q === "0" || q === "0.000") this.bids.delete(p);
      else this.bids.set(p, q);
    });
    update.asks.forEach(([p, q]) => {
      if (q === "0" || q === "0.000") this.asks.delete(p);
      else this.asks.set(p, q);
    });
    this.lastUpdateId = update.lastUpdateId;
  }

  // Group levels by display precision
  getGrouped(precision: number, levels: number) {
    const groupedBids = this.groupByPrecision(this.bids, precision, 'desc');
    const groupedAsks = this.groupByPrecision(this.asks, precision, 'asc');
    return {
      bids: groupedBids.slice(0, levels),
      asks: groupedAsks.slice(0, levels),
      spread: groupedAsks[0]?.[0] - groupedBids[0]?.[0]
    };
  }
}

盘口显示样式

┌─────────────────────────────────────┐
│         卖盘 (Asks) - 红色            │
│  价格         数量      累计           │
│  ████████░░  42200.00   1.200  8.500  │
│  ███████░░░  42150.00   2.800  7.300  │
│  █████░░░░░  42100.00   0.500  4.500  │
│  ████░░░░░░  42050.00   1.200  4.000  │
│  ██░░░░░░░░  42000.00   2.800  2.800  │
├──── Spread: 50.00 (0.12%) ──────────│
│  ██░░░░░░░░  41950.00   1.500  1.500  │
│  ████░░░░░░  41900.00   2.300  3.800  │
│  ██████░░░░  41850.00   1.800  5.600  │
│  ████████░░  41800.00   3.200  8.800  │
│  █████████░  41750.00   2.000 10.800  │
│         买盘 (Bids) - 绿色            │
└─────────────────────────────────────┘

背景条: 累计量占总量的比例,直观展示流动性分布。
点击价格: 自动填入下单面板的限价字段。
点击数量: 自动填入委托数量。

5. 深度图

阶梯面积图

X 轴为价格,Y 轴为累计数量。买盘从右往左递增(绿色),卖盘从左往右递增(红色)。

价格范围

默认显示 bestPrice +/- 2% 范围;支持缩放和拖拽查看更大范围的流动性分布。

实时更新

订阅 depth 频道,每次增量更新后重新计算累计量并重绘。使用 Canvas 绘制确保帧率。

交互

鼠标悬停显示对应价格的累计量和预计成交均价;点击直接填入下单面板价格。

深度图示例

下方 Canvas 渲染的深度图展示买卖盘的累计流动性分布:

深度图可视化 — 买卖盘流动性分布
最优买价 (Best Bid)41,950.00
最优卖价 (Best Ask)42,000.00
价差 (Spread)50.00 (0.119%)
买盘总深度27.6 BTC
卖盘总深度23.5 BTC

6. K 线图集成

TradingView

使用 TradingView Charting Library 或 Lightweight Charts。支持技术指标(MA/EMA/MACD/RSI/BOLL)、画线工具。

数据适配

后端返回 [openTime, open, high, low, close, volume] 数组;前端转换为图表库需要的格式。

多周期

支持 1m/5m/15m/30m/1h/4h/1d/1w 等周期。切换时先查本地缓存,缺失部分通过 REST API 补充。

标记层

在 K 线上叠加标记层:用户的历史成交点、挂单价格线、强平价格线、MA 均线等。

K 线蜡烛图示例

下方 Canvas 渲染的 K 线图支持切换周期和悬停查看 OHLCV 详情:

K 线蜡烛图 — 鼠标悬停查看详情
阳线 (涨)阴线 (跌)— MA5— MA20最新价
交易对BTC / USDT
时间周期15m
最新收盘价42997.28
区间最高 / 最低43411.84 / 42851.46
总成交量138.049 BTC

7. 下单面板

┌───────────────────────────────┐
│  [限价]  [市价]  [止损]        │
├───────────────────────────────┤
│  ┌─ 买入 BTC ─┐ ┌─ 卖出 BTC ─┐│
│  │   (选中)    │ │            ││
│  └────────────┘ └────────────┘│
├───────────────────────────────┤
│  价格   [  42000.00  ] USDT   │
│  数量   [     1.00   ] BTC    │
│                               │
│  [25%] [50%] [75%] [100%]     │
│                               │
│  杠杆   [ 1x ▾ ]              │
│  ☐ Post-Only  ☐ IOC          │
├───────────────────────────────┤
│  可用    12,500.00 USDT       │
│  总额    42,000.00 USDT       │
│  手续费  ~25.20 USDT (0.06%)  │
├───────────────────────────────┤
│  ┌─────────────────────────┐  │
│  │     买入 BTC (绿色)       │  │
│  └─────────────────────────┘  │
└───────────────────────────────┘

百分比按钮

25%/50%/75%/100% 按可用余额计算最大可买/可卖数量。考虑手续费预留,100% 时扣除预估费用。

价格联动

点击订单簿的价格行自动填入价格字段;市价单隐藏价格输入。

实时预估

输入价格和数量后实时计算:总额、预估手续费、预估滑点(市价单参考当前盘口)。

确认弹窗

大额订单或市价单可选择显示确认弹窗,展示预估成交明细,防止误操作。

8. 订单管理

当前委托

展示 NEW / PARTIALLY_FILLED 状态的活跃订单。显示价格、数量、已成交量、进度条。支持单个撤单和一键全撤。

历史委托

展示所有已结束订单(FILLED/CANCELED/EXPIRED)。分页加载,支持按交易对、时间范围、方向筛选。

成交记录

展示个人的成交明细。每笔 Trade 显示成交价、成交量、手续费、角色(Maker/Taker)。

实时更新

通过 order@{userId} 频道实时接收订单状态变更。新订单插入列表顶部,状态变更原地刷新。

┌──────────────────────────────────────────────────────────┐
│ [当前委托(3)]  [历史委托]  [成交记录]  [持仓]              │
├──────────────────────────────────────────────────────────┤
│ 交易对    方向   价格       数量     已成交   状态     操作  │
│ BTCUSDT  买入   42000.00   2.000   0.800   进行中   [撤单]│
│ ETHUSDT  卖出   2800.00    5.000   5.000   已成交   -     │
│ BTCUSDT  买入   41500.00   1.000   0.000   挂单中   [撤单]│
├──────────────────────────────────────────────────────────┤
│                               [撤销 BTCUSDT] [全部撤销]   │
└──────────────────────────────────────────────────────────┘

9. 状态管理

// Zustand store example
const useMarketStore = create<MarketState>((set, get) => ({
  symbol: 'BTCUSDT',
  ticker: null,
  orderBook: { bids: [], asks: [], spread: 0 },
  precision: 2,

  setSymbol: (symbol) => {
    set({ symbol });
    wsManager.unsubscribe(`depth@${get().symbol}`);
    wsManager.subscribe(`depth@${symbol}`, (data) => {
      set({ orderBook: data });
    });
  },

  updateOrderBook: (update) => {
    const book = get().localBook;
    book.applyUpdate(update);
    set({ orderBook: book.getGrouped(get().precision, 20) });
  },

  setPrecision: (precision) => {
    set({ precision });
    const book = get().localBook;
    set({ orderBook: book.getGrouped(precision, 20) });
  },
}));

10. 前端性能优化

节流渲染

高频数据(盘口、行情)使用 requestAnimationFrame 节流。每帧最多渲染一次,避免过度重绘。WebSocket 消息先缓冲到队列,每帧批量处理。

虚拟列表

成交列表、历史订单使用虚拟滚动(react-virtualized/react-window),只渲染可视区域的 DOM 节点,降低内存和渲染开销。

Web Worker

K 线数据聚合、技术指标计算放入 Web Worker,避免阻塞主线程。Worker 计算完毕后 postMessage 回主线程渲染。

增量更新

订单簿只传输变化的价位(增量更新),不是每次全量。本地维护完整簿,增量 patch 后计算显示数据。

Canvas 渲染

深度图和 K 线图使用 Canvas/WebGL 渲染,避免大量 DOM 操作。仅在数据变更时重绘,使用双缓冲防闪烁。

代码分割

交易页面按需加载。K 线图库(TradingView ~500KB)懒加载;未使用的指标库 dynamic import。

11. 页面布局

┌─────────────────────────────────────────────────────────────────┐
│ [Header] BTCUSDT ▾ | 42,050.00 | +2.5% | H:43,000 L:41,000    │
├──────────────────────────────┬──────────────┬────────────────────┤
│                              │              │                    │
│                              │   订单簿      │    下单面板         │
│     K 线图区域                │   (盘口)      │                    │
│     TradingView              │              │  [限价][市价][止损]  │
│     支持全屏                  │  42200 1.20  │  价格 [42000]      │
│                              │  42150 2.80  │  数量 [1.00]       │
│                              │  42100 0.50  │  [25%][50%][100%]  │
│                              │── 50.00 ──── │                    │
│                              │  42050 1.50  │  [  买入 BTC  ]    │
│                              │  42000 2.30  │                    │
│                              │  41950 1.80  │   可用: 12,500     │
│                              │              │   手续费: ~25      │
├──────────────────────────────┴──────────────┴────────────────────┤
│                   最新成交列表  |  深度图                          │
├─────────────────────────────────────────────────────────────────┤
│ [当前委托] [历史委托] [成交记录] [持仓]                            │
│ BTCUSDT  BUY  42000  2.00  0.80  进行中  [撤单]                 │
└─────────────────────────────────────────────────────────────────┘

桌面端: CSS Grid 多列布局,面板可拖拽调整大小
移动端: Tab 切换(K线 / 盘口 / 下单 / 订单),底部固定下单按钮