使用Phoenix API构建实时SOL永续合约仪表盘

QuickNode 发布于 2026-05-29 阅读 184

本指南介绍如何使用Phoenix的公共API在Solana上构建实时永续合约仪表盘。

概述

Phoenix 是 Solana 上的一个永续合约(也称为 perps)市场,它通过公共 API 公开价格行为、订单簿深度、交易记录、K 线数据和资金费率历史。本指南将向你展示如何使用该 API 构建一个实时仪表板,用于查看实时 Solana 永续合约数据,并附有一个配套的 React 示例应用程序,你可以克隆、运行和扩展。

TL;DR

  • Phoenix 是一个 Solana 原生永续合约交易平台,通过免费的、只读的公共 API 公开实时市场数据。
  • 本指南解释了永续合约,介绍了 Phoenix,并浏览了其 REST 和 WebSocket API。
  • 你将通过 REST 预载市场配置和历史 K 线,然后通过单个 WebSocket 连接流式传输实时数据。
  • 你将处理 Phoenix 有效载荷的特殊之处,优雅地重新连接,并在不丢失状态的情况下切换图表时间框架。
  • 一个配套的 React 示例应用程序将所有内容整合成一个永续合约仪表板,你可以克隆、运行和扩展。

你将做什么

  • 了解永续合约以及 Phoenix 如何在 Solana 上公开它们。
  • 克隆并运行配套的示例仪表板应用程序。
  • 从单个共享连接订阅所有六个 Phoenix WebSocket 频道(marketorderbooktradescandlesfundingRateexchange)。
  • 在一个边界处处理 Phoenix 的有效载荷特殊之处(数字编码、订单簿形状变化、时间单位混合)。

你需要什么

什么是永续合约?

永续合约是跟踪标的资产价格且没有到期日的衍生品。交易者使用杠杆进行多头或空头头寸,协议通过多头和空头之间的定期资金支付使头寸价格与现货价格保持一致。没有结算日期,无需展期。只要头寸保持偿付能力,就可以无限期持有。

驱动永续合约市场的四个因素:

  • 标记价格 是协议对合约的参考价格。未实现盈亏、清算和资金结算均以此为准。
  • 预言机(或指数)价格 是外部参考价格(通常是现货交易场所的预言机聚合,例如 Pyth)。标记价格和预言机价格应紧密跟踪。持续偏离是压力或流动性不足的信号。
  • 资金费率 是多头和空头之间的定期支付,使标记价格接近预言机价格。本指南和 Phoenix 响应有效载荷中使用的约定:正资金费率表示多头支付空头,负资金费率表示空头支付多头。
  • 未平仓合约 是所有未平仓头寸的总名义价值,即所有未平仓多头和空头的杠杆规模之和,而不是锁定在协议中的抵押品(即 TVL)。它是市场持仓强度的代理指标。

什么是 Phoenix?

Phoenix 是一个 Solana 原生的去中心化交易所。它最初作为现货交易的链上限价订单簿推出,随后推出了永续合约产品,这也是本指南的重点。撮合完全在主网的 Solana 程序中进行,没有链下撮合引擎。

对于任何只需要读取市场数据的应用程序(仪表板、价格源、分析工具),Phoenix 在 perp-api.phoenix.trade 提供了你所需要的一切。有一个用于快照的 REST 主机和一个用于推送更新的 WebSocket 主机。公共读取端点不需要 API 密钥、签名的标头或钱包。链上下单是一个独立的集成,需要连接钱包,不在本指南的讨论范围内。

Phoenix WebSocket API

Phoenix 通过单个 WebSocket 端点 wss://perp-api.phoenix.trade/v1/ws 公开实时市场数据。你打开一个连接,然后订阅你需要的任何频道。本指南中的仪表板订阅了所有六个频道。

每个订阅都遵循相同的模式:发送一个 JSON 消息,其中包含 typesubscribechannel 名称和 symbol

{
  "type": "subscribe",
  "subscription":
    {
      "channel": "<频道>",
      "symbol": "SOL"
    }
}

仪表板使用的六个频道,以及每个频道提供的内容:

  • market:推送核心市场数据:标记价格、预言机价格、中间价、24小时成交量、未平仓合约和当前资金费率。
  • orderbook:每次消息传递一个完整的 L2 订单簿快照,包含所有买盘和卖盘价位及其价格和数量。
  • trades:实时流式传输单个交易记录:价格、数量、方向(买入或卖出)和时间戳。
  • candles:为所选时间框架(1m5m1h 等)流式传输开盘/最高/最低/收盘/成交量(OHLCV)K 线数据。
  • fundingRate:每次资金费率变化时推送当前资金费率。
  • exchange:描述交易所的全局健康状况。连接时发送快照,然后发送增量更新。

Phoenix 还公开了一个完整的 REST API,包含市场数据快照、交易者状态、注册、身份验证和交易构建的端点。

配套的示例应用程序是一个只读的 React 仪表板,它打开一个到 Phoenix 的单个 WebSocket 连接,订阅所有六个频道,并将数据渲染到五个面板中:

  • 市场概览:顶部栏显示标记价格、预言机价格、24小时变化、24小时成交量、未平仓合约和当前资金费率。由 marketfundingRate 频道驱动。包含来自 exchange 频道的连接状态徽章。
  • 价格图表:K 线图,带有时间框架切换器(1m5m15m1h4h1d)。加载时和重新连接时从 REST 预载,然后从 candles 频道实时更新。
  • 订单簿:前 15 个买盘和卖盘价位,包含累计数量和基点点差。每次 orderbook 消息时替换。
  • 交易流:最新交易的实时流,按方向着色。每次 trades 消息时更新。
  • 市场信息:静态参考面板,显示费用、杠杆层级、保证金要求、最小变动价位和合约单位。

Phoenix 永续合约仪表板屏幕截图,显示 SOL 的所有面板

src/ws/PhoenixWebSocket.tsx 中的 PhoenixProvider / usePhoenix() 拥有单个共享的 WebSocket、订阅记账、消息分发、重新连接逻辑、时间框架切换和暴露的状态对象。

克隆并在本地运行,然后跟随本指南的其余部分:

git clone https://github.com/quiknode-labs/qn-guide-examples.git
cd solana/phoenix-dashboard
npm install
npm run dev

获取市场配置和预载 K 线

一个由 WebSocket 支持的仪表板仍然需要 REST。WebSocket 只推送后续更新,不发送初始快照。在实时更新到达之前,需要填充两部分状态:静态市场配置(WebSocket 从不推送)和历史 K 线序列(以便图表有超过一个柱)。

GET /exchange/market/{symbol} 返回一个包含静态市场参数的 JSON 对象。一个精简的示例响应:

{
  "symbol": "SOL",
  "assetId": 1,
  "marketStatus": "active",
  "marketPubkey": "...",
  "tickSize": 0.01,
  "baseLotsDecimals": 3,
  "takerFee": 0.0005,
  "makerFee": 0.0001,
  "fundingIntervalSeconds": 3600,
  "fundingPeriodSeconds": 86400,
  "maxFundingRatePerIntervalPercentage": 0.05,
  "openInterestCapBaseLots": "100000000",
  "maxLiquidationSizeBaseLots": "5000000",
  "isolatedOnly": false,
  "leverageTiers": [\
    { "maxLeverage": 20, "maxSizeBaseLots": 1000000, "limitOrderRiskFactor": 0.05 },\
    { "maxLeverage": 10, "maxSizeBaseLots": 5000000, "limitOrderRiskFactor": 0.1 }\
  ],
  "riskFactors": {
    "maintenance": 0.03,
    "backstop": 0.01,
    "highRisk": 0.05,
    "upnl": 0.5,
    "upnlForWithdrawals": 0.25,
    "cancelOrder": 0.001
  }
}

仪表板将此用于市场信息参考面板(最大杠杆、层级表、保证金要求、费用、最小变动价位/合约单位)。这些字段都不会在任何 WebSocket 频道上推送,因此 REST 是唯一的来源。

GET /candles?symbol=SOL&timeframe=1m&limit=500 返回一个 K 线对象数组,时间戳为毫秒:

[\
  {\
    "time": 1747556400000,\
    "open": 170.21,\
    "high": 170.55,\
    "low": 170.14,\
    "close": 170.42,\
    "volume": 1284.5,\
    "volumeQuote": 218842.71,\
    "tradeCount": 42,\
    "markOpen": 170.20,\
    "markHigh": 170.54,\
    "markLow": 170.13,\
    "markClose": 170.41\
  }\
]

订阅 Phoenix WebSocket

const ws = new WebSocket('wss://perp-api.phoenix.trade/v1/ws');

const subscriptions = [\
  { type: 'subscribe', subscription: { channel: 'market', symbol: 'SOL' } },\
  { type: 'subscribe', subscription: { channel: 'orderbook', symbol: 'SOL' } },\
  { type: 'subscribe', subscription: { channel: 'trades', symbol: 'SOL' } },\
  { type: 'subscribe', subscription: { channel: 'candles', symbol: 'SOL', timeframe: '1m' } },\
  { type: 'subscribe', subscription: { channel: 'fundingRate', symbol: 'SOL' } },\
  { type: 'subscribe', subscription: { channel: 'exchange', encoding: 'json' } },\
];

ws.onopen = () => {
  for (const msg of subscriptions) ws.send(JSON.stringify(msg));
};

需要注意两点:candles 带有 timeframeexchange 带有 encoding: 'json'(没有 symbol)。

每个有效载荷都有一个 channeltype 字段。根据实际存在的字段进行分发:

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  const key = msg.channel ?? msg.type;
  switch (key) {
    case 'market':         return onMarketStats(msg);
    case 'orderbook':      return onOrderbook(msg);
    case 'trades':         return onTrades(msg);
    case 'candles':        return onCandle(msg);
    case 'fundingRate':    return onFundingRate(msg);
    case 'exchange':       return onExchange(msg);
    case 'subscriptionConfirmed': return;
    case 'subscriptionError':
    case 'error':
      console.error('Phoenix error', msg);
      return;
  }
};

连接打开并发送订阅后,服务器开始推送消息。下面是六个频道中每个频道的有效载荷分解:

Market

MarketStatsUpdate 推送顶部数据:

{
  "channel": "market",
  "markPx": 170.42,
  "oraclePx": 170.41,
  "midPx": 170.42,
  "prevDayPx": 168.15,
  "dayNtlVlm": 218842710.42,
  "openInterest": 4521234.5,
  "funding": 0.00012
}

每次收到消息时,完全替换内存中的 MarketStats。根据 markPxprevDayPx 计算 24 小时百分比变化。仪表板将 funding 用于头部徽章,同时也将其存储为会话本地的历史序列,以便与图表一并展示。

Orderbook

L2BookUpdate 是一个完整的 L2 快照,而不是增量。每次收到消息时替换内存中的订单簿。Phoenix 以两种形状发送此有效载荷(请参阅 处理 Phoenix 的有效载荷特殊之处)。如果 MarketStatsUpdate 尚未到达,则从订单簿顶部推导出 midPx(最佳买价 + 最佳卖价) / 2

{
  "channel": "orderbook",
  "symbol": "SOL",
  "orderbook": {
    "bids": [[170.41, 42.5], [170.40, 118.0], [170.38, 75.2]],
    "asks": [[170.42, 30.1], [170.43, 95.0], [170.45, 210.3]],
    "mid": 170.415
  },
  "bypassExecutionBand": false
}

Trades

TradesMessage 在线路上是仅追加的。仪表板将其前置并限制在 100 行。关键字段:

{
  "channel": "trades",
  "trades": [\
    {\
      "tradeSequenceNumber": 482113,\
      "slot": 281234567,\
      "slotIndex": 3,\
      "timestamp": "1747556421",\
      "time": 1747556421000,\
      "side": "b",\
      "price": 170.42,\
      "size": 12.5,\
      "notional": 2130.25,\
      "numFills": 1\
    }\
  ]
}

Candles

CandleData 流式传输正在进行的 K 线(更新时)和已关闭的 K 线(区间结束时)。此频道的时间单位是秒(REST 是毫秒)。仪表板按 time 进行更新插入:如果具有相同 time 的 K 线已存在,则替换;否则追加。

{
  "channel": "candles",
  "symbol": "SOL",
  "timeframe": "1m",
  "candle": {
    "time": 1747556460,
    "open": 170.42,
    "high": 170.50,
    "low": 170.38,
    "close": 170.45,
    "volume": 92.1,
    "volumeQuote": 15710.2
  }
}

FundingRate

FundingRateUpdate 替换当前资金费率。仪表板还将一个 { timestamp: Date.now(), rate } 条目推送到会话本地数组中,以便资金历史可以与价格一起绘制图表。不需要持久化。

{
  "channel": "fundingRate",
  "funding": 0.00012,
  "fundingTime": 1747556400
}

Exchange

exchange 频道描述交易所的全局健康状况。它在订阅时发送一次 snapshot,然后发送 delta 消息。每个 delta 都有一个 op;此仪表板仅对 exchangeStatusChanged 进行响应。其他市场级别的增量操作被有意忽略。

{
  "channel": "exchange",
  "type": "snapshot",
  "active": true,
  "gated": false
}

总结

现在,你拥有了一个完全由 Phoenix 公共 API 驱动的基于推送的 SOL 永续合约终端。你知道了要从哪些 REST 端点预载数据,要订阅哪六个 WebSocket 频道,以及如何在一个边界处统一处理 Phoenix 的有效载荷特殊之处(数字编码、订单簿形状、时间单位),以便其余代码保持干净。重新连接、重新订阅和时间框架切换逻辑都已配置好,使仪表板在连接断开时不会过时。从这里开始,你可以将其指向任何 Phoenix 市场,通过 Quicknode RPC 端点添加链上上下文,或在你喜欢的任何框架中重建 UI。

常见问题

Phoenix WebSocket 是否有限速或需要身份验证?

本文档介绍的公共只读数据源不需要 API 密钥、签名的标头或钱包。请查看 Phoenix 文档了解当前的速率限制,因为公共基础设施的限制可能会发生变化。

标记价格和预言机价格有什么区别?

标记价格是协议对永续合约的参考价格,用于结算未实现盈亏、清算和资金。预言机(或指数)价格是外部参考(通常是现货交易场所的聚合)。两者应紧密跟踪;持续偏离是压力信号,仪表板会将其显示为图表叠加层。

我可以将其用于其他 Phoenix 市场,如 BTC 或 ETH 吗?

可以。Phoenix 的 REST 和 WebSocket 接口在各个市场上都是相同的。在整个 provider 中参数化符号,将内存状态提升为按符号映射,相同的六个频道即可提供仪表板渲染所需的所有内容。

我可以从这个仪表板进行交易吗?

不可以。交易不在本指南的讨论范围内。在 Phoenix 上下单是一种链上 Solana 程序交互,需要连接钱包和签署交易,而本指南使用的只读公共 API 接口均不支持这些。

资源

我们喜欢反馈!

如果你有任何反馈或新主题请求,请告诉我们。我们期待你的来信。

  • 原文链接: quicknode.com/guides/sol...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论