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} | 单个代币的 tradeUpdate、balanceUpdate、priceUpdate | 您在追踪某个特定持仓 |
pnl:{wallet} | 钱包所有代币的 tradeUpdate、balanceUpdate、priceUpdate | 您希望钱包持有的每个持仓都有实时更新 |
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}
一次订阅某钱包的所有代币持仓。推送与上面相同的 tradeUpdate 和 balanceUpdate 消息,但涵盖钱包交互过的每个代币——无需事先知道代币地址。
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 用作 currentBalance 和 holdingCostBasis 的真实来源,并在每次价格跳动到来时重新计算 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:aggregated 和 price-by-token room — 将它们与 PnL 配对以获得实时未实现 PnL。
钱包分析
使用 REST API 分析钱包 PnL、持仓、交易时机和敞口。
钱包追踪
将实时钱包活动与 PnL 流结合,构建实时投资组合面板。