跳转到主要内容

Documentation Index

Fetch the complete documentation index at: https://docs.solanatracker.io/llms.txt

Use this file to discover all available pages before exploring further.

PnL V2 Datastream 适用于 Premium、Business 和 Enterprise 方案。
使用您的 Data API 密钥连接到 wss://datastream.solanatracker.io/{apiKey}
PnL V2 Datastream 直接将实时交易和余额更新推送到您的 WebSocket 客户端。无需轮询 REST API,订阅某个 room 即可在事件发生时立即接收更新。
通俗解释: PnL 表示盈亏。已实现 PnL 来自已售出的代币。未实现 PnL 是仍持有代币的账面盈亏。成本基础是钱包为当前持仓所支付的金额。

Room 类型

共有三种 room,每种服务于不同的用例:
Room它推送什么何时使用
pnl:{wallet}:{token}单个代币的 tradeUpdatebalanceUpdatepriceUpdate您在追踪某个特定持仓
pnl:{wallet}钱包所有代币的 tradeUpdatebalanceUpdatepriceUpdate您希望钱包持有的每个持仓都有实时更新
pnl:{wallet}:summary总钱包 PnL 摘要您希望获得汇总的钱包级 PnL 总计
PnL room 在每次成交时推送 tradeUpdate,在余额变化时推送 balanceUpdate。它们也会推送 priceUpdate 事件,但频率较低。可将 priceUpdate 视为对沉寂头寸的补发消息,而不是快速价格推送。要获得最快的未实现 PnL 跳动,请将 PnL room 与价格流配合使用。

连接

const ws = new WebSocket("wss://datastream.solanatracker.io/YOUR_API_KEY");

ws.onopen = () => {
  // subscribe to a room
  ws.send(JSON.stringify({
    type: "join",
    room: "pnl:WALLET_ADDRESS:TOKEN_ADDRESS"
  }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  console.log(msg.type, msg.data);
};

Room: pnl:{wallet}:{token}

订阅钱包中某个特定代币的持仓。您将收到两种消息类型: tradeUpdate — 当买入或卖出执行时触发:
{
  "type": "message",
  "room": "pnl:DJqkH...D61:A7Fbn...pump",
  "data": {
    "type": "tradeUpdate",
    "wallet": "DJqkH...D61",
    "token": "A7Fbn...pump",
    "buyCount": 3,
    "sellCount": 1,
    "totalBuyUsd": 0.0257,
    "totalSellUsd": 0.0043,
    "avgCostPerToken": 0.000082,
    "currentBalance": 205.39,
    "currentPrice": 0.0000911,
    "currentValue": 0.01871,
    "holdingCostBasis": 0.01715,
    "realizedPnl": -0.00087,
    "unrealizedPnl": 0.00156,
    "proceeds": 0.0043,
    "totalTransactions": 4
  }
}
balanceUpdate — 当持仓价值因价格变动而变化时触发(没有新交易):
{
  "type": "message",
  "room": "pnl:DJqkH...D61:A7Fbn...pump",
  "data": {
    "type": "balanceUpdate",
    "wallet": "DJqkH...D61",
    "token": "A7Fbn...pump",
    "avgCostPerToken": 0.0000835,
    "currentBalance": 205.39,
    "currentPrice": 0.0000911,
    "currentValue": 0.01871,
    "holdingCostBasis": 0.01715,
    "unrealizedPnl": 0.00156
  }
}

订阅示例

ws.send(JSON.stringify({
  type: "join",
  room: "pnl:DJqkHSmx9XosFpNqdi2DevtNgaf52oow4pFpJECv6D61:A7FbnhhkY2R6hZhkT2oexNNFQyGG7cBZJLWmC32spump"
}));

Room: pnl:{wallet}

一次订阅某钱包的所有代币持仓。推送与上面相同的 tradeUpdatebalanceUpdate 消息,但涵盖钱包交互过的每个代币——无需事先知道代币地址。
ws.send(JSON.stringify({
  type: "join",
  room: "pnl:DJqkHSmx9XosFpNqdi2DevtNgaf52oow4pFpJECv6D61"
}));

ws.onmessage = (event) => {
  const { data } = JSON.parse(event.data);
  if (!data) return;

  if (data.type === "tradeUpdate") {
    console.log(`Trade on ${data.token}: unrealized PnL $${data.unrealizedPnl.toFixed(4)}`);
  }
  if (data.type === "balanceUpdate") {
    console.log(`Price update for ${data.token}: value now $${data.currentValue.toFixed(4)}`);
  }
};
构建实时投资组合面板时使用 pnl:{wallet} — 每个钱包只需一个 room 订阅。

Room: pnl:{wallet}:summary

每当聚合值发生变化时接收钱包的总 PnL 摘要。此 room 返回钱包级总计,而不是单个持仓。
{
  "type": "message",
  "room": "pnl:DJqkH...D61:summary",
  "data": {
    "averages": {
      "buy": 1.1751933473135445,
      "sell": 1.7625162156883138
    },
    "counts": {
      "buys": 1664,
      "sells": 745,
      "tokensHeldEver": 842,
      "tokensTraded": 862,
      "trades": 2409
    },
    "invested": 708.3246017180338,
    "openPositions": {
      "cost": 962.9218710198314,
      "value": 37.196319618271204
    },
    "pnl": {
      "realized": 604.7499789697601,
      "total": -421.18843262884593,
      "unrealized": -1025.938411598606
    },
    "proceeds": 1313.0745806877937,
    "roi": -59.46262936614906,
    "timing": {
      "firstTrade": 1704067810000,
      "lastTrade": 1778966213113
    }
  }
}
ws.send(JSON.stringify({
  type: "join",
  room: "pnl:DJqkHSmx9XosFpNqdi2DevtNgaf52oow4pFpJECv6D61:summary"
}));

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.room !== `pnl:${wallet}:summary`) return;
  const { data } = msg;

  console.log(`Total PnL: $${data.pnl.total.toFixed(2)}`);
  console.log(`Realized: $${data.pnl.realized.toFixed(2)}`);
  console.log(`Unrealized: $${data.pnl.unrealized.toFixed(2)}`);
  console.log(`ROI: ${data.roi.toFixed(2)}%`);
};

最快的实时未实现 PnL — 组合 Room

PnL room 在交易上链时为您提供官方的成本基础、FIFO 已实现 PnL 和余额变化。FIFO 表示 “first in, first out”(先进先出):较早的买入先与卖出匹配。PnL room 并非设计为逐笔价格推送。priceUpdate 事件受限速,且仅为长时间无变化的持仓补发。 对于 UI 每个价格跳动都要更新未实现 PnL 数字的实时投资组合界面,请将 PnL room 与价格流组合:
它提供什么
pnl:{wallet} (或 pnl:{wallet}:{token})实时成本基础、FIFO 已实现 PnL、余额变化、交易成交
price:aggregated:{token}最快的跨池价格 — 适合高流动性代币
price-by-token:{token}主池(流动性最高的池)价格 — 适合单池/低流动性代币
将 PnL room 用作 currentBalanceholdingCostBasis 的真实来源,并在每次价格跳动到来时重新计算 unrealizedPnl = (currentBalance × livePrice) − holdingCostBasis

示例:单一持仓的实时未实现 PnL

const wallet = "DJqkHSmx9XosFpNqdi2DevtNgaf52oow4pFpJECv6D61";
const token  = "A7FbnhhkY2R6hZhkT2oexNNFQyGG7cBZJLWmC32spump";

const ws = new WebSocket("wss://datastream.solanatracker.io/YOUR_API_KEY");

const position = {
  balance: 0,
  costBasis: 0,
  realizedPnl: 0,
  livePrice: 0,
};

const recompute = () => {
  const value = position.balance * position.livePrice;
  const unrealized = value - position.costBasis;
  console.log(
    `value=$${value.toFixed(4)}  unrealized=$${unrealized.toFixed(4)}  realized=$${position.realizedPnl.toFixed(4)}`
  );
};

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "join", room: `pnl:${wallet}:${token}` }));
  ws.send(JSON.stringify({ type: "join", room: `price:aggregated:${token}` }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type !== "message") return;
  const d = msg.data;

  if (d?.type === "tradeUpdate" || d?.type === "balanceUpdate") {
    position.balance   = d.currentBalance;
    position.costBasis = d.holdingCostBasis;
    if (d.type === "tradeUpdate") position.realizedPnl = d.realizedPnl;
    position.livePrice = d.currentPrice;
    recompute();
    return;
  }

  if (msg.room?.startsWith("price:aggregated:")) {
    position.livePrice = d.price;
    recompute();
  }
};

示例:整个钱包的实时未实现 PnL

订阅 pnl:{wallet} 以获取每个持仓的交易和余额更新,然后为钱包持有的每个代币动态订阅价格 room。
价格流可能更新频繁。生产环境中请批量处理 UI 更新或限流日志,避免每次价格跳动都让控制台或浏览器被淹没。
const wallet = "DJqkHSmx9XosFpNqdi2DevtNgaf52oow4pFpJECv6D61";
const ws = new WebSocket("wss://datastream.solanatracker.io/YOUR_API_KEY");
const positions = new Map(); // token -> { balance, costBasis, realizedPnl, livePrice }
const subscribed = new Set();

const portfolioUnrealized = () => {
  let total = 0;
  for (const p of positions.values()) {
    total += p.balance * p.livePrice - p.costBasis;
  }
  return total;
};

const subscribePrice = (token) => {
  if (subscribed.has(token)) return;
  subscribed.add(token);
  // Use price:aggregated for high-liquidity tokens, price-by-token for memecoins / single-pool
  ws.send(JSON.stringify({ type: "join", room: `price:aggregated:${token}` }));
};

ws.onopen = () => {
  ws.send(JSON.stringify({ type: "join", room: `pnl:${wallet}` }));
};

ws.onmessage = (event) => {
  const msg = JSON.parse(event.data);
  if (msg.type !== "message") return;
  const d = msg.data;

  if (d?.type === "tradeUpdate" || d?.type === "balanceUpdate") {
    const p = positions.get(d.token) ?? { balance: 0, costBasis: 0, realizedPnl: 0, livePrice: 0 };
    p.balance   = d.currentBalance;
    p.costBasis = d.holdingCostBasis;
    p.livePrice = d.currentPrice;
    if (d.type === "tradeUpdate") p.realizedPnl = d.realizedPnl;
    positions.set(d.token, p);
    subscribePrice(d.token);
  }

  if (msg.room?.startsWith("price:aggregated:")) {
    const token = msg.room.split(":")[2];
    const p = positions.get(token);
    if (!p) return;
    p.livePrice = d.price;
    positions.set(token, p);
  }

  console.log(`Portfolio unrealized PnL: $${portfolioUnrealized().toFixed(4)}`);
};
选哪个价格 room?
  • price:aggregated:{token} — 推荐默认。跨池中位数价格;当代币在多个池中交易时更平滑、更准确。
  • price-by-token:{token} — 主池价格。对于 memecoin 或实际仅在单一池中交易的代币,延迟略低。

priceUpdate payload(PnL room)

PnL room 会在持仓报价一段时间未变时推送 priceUpdate 作为兜底。请将其视为周期性刷新,而不是实时推送:
{
  "type": "message",
  "room": "pnl:DJqkH...D61:A7Fbn...pump",
  "data": {
    "type": "priceUpdate",
    "wallet": "DJqkH...D61",
    "token": "A7Fbn...pump",
    "currentBalance": 205.39,
    "currentPrice": 0.0000911,
    "currentValue": 0.01871,
    "holdingCostBasis": 0.01715,
    "unrealizedPnl": 0.00156
  }
}

取消订阅

通过发送 leave 消息离开任何 room:
ws.send(JSON.stringify({
  type: "leave",
  room: "pnl:DJqkHSmx9XosFpNqdi2DevtNgaf52oow4pFpJECv6D61:summary"
}));

选择合适的 Room

使用 pnl:{wallet}:{token}。噪音最少 — 仅在该代币上触发。
使用 pnl:{wallet}。一个订阅涵盖所有持仓。每次更新都能获得代币级细节。
使用 pnl:{wallet}:summary。它返回聚合的钱包级 PnL 总计,而不包含每个代币的持仓行。
pnl:{wallet}(或 pnl:{wallet}:{token})与 price:aggregated:{token}price-by-token:{token} 组合。PnL room 提供成本基础和余额;价格 room 驱动实时跳动。请参阅组合 Room
可以。发送多条 join 消息。每个 room 独立运行。

相关指南

PnL V2 概览

核心 PnL V2 概念、端点以及钱包索引的工作方式。

实时价格与图表

price:aggregatedprice-by-token room — 将它们与 PnL 配对以获得实时未实现 PnL。

钱包分析

使用 REST API 分析钱包 PnL、持仓、交易时机和敞口。

钱包追踪

将实时钱包活动与 PnL 流结合,构建实时投资组合面板。