跟单听起来像个数据问题。盯着一个钱包,看它买什么,跟着买同一个。
实际上它是个披着数据外衣的延迟问题。等你检测到这笔交易、判断要跟、再把自己的交易送上链,价格已经动了 —— 而它动了多少,就是你的全部利润空间。
没有"跟单 API"这种东西
没有任何接口提供"这个钱包交易了就通知我"。你得自己拼三块:
检测。 盯住一组钱包的 swap 活动。 判断。 决定这一笔值不值得跟。 执行。 构造并送上链你自己的交易。
大多数教程把篇幅花在第一块。★而钱是在第三块流走的。★
如果检测那块已经跑通、只缺执行的一半,免费领个 BoltTx key 改一行就行。这篇剩下的是你得自己写的那些。
检测:三种做法
轮询签名
定时对每个钱包调 getSignaturesForAddress 拉最近签名,和上次比对差异。
async function pollWallet(connection: Connection, wallet: PublicKey, seen: Set<string>) {
const sigs = await connection.getSignaturesForAddress(wallet, { limit: 10 });
// ★getSignaturesForAddress 返回的是最新在前 —— 重放要按时间正序★
const fresh = sigs.filter((s) => !seen.has(s.signature) && !s.err).reverse();
fresh.forEach((s) => seen.add(s.signature));
return fresh;
}
简单。但也是最慢的,而且扩展性很差 —— 50 个钱包每秒轮一次,就是每秒 50 个请求,这还没开始解析。
日志订阅
通过 WebSocket 订阅 —— 盯程序活动用 logsSubscribe,盯具体账户用 accountSubscribe —— 让事件推给你,而不是你去问。
const sub = connection.onLogs(
wallet,
(logs) => {
if (logs.err) return; // 失败的交易,没什么可跟的
handleActivity(logs.signature);
},
"processed", // 用 "confirmed" 会白白多花一个 slot 以上,追不回来
);
★用 processed,别用 confirmed。★ 等确认,等的就是你正在抢的那个东西。代价是你要承担"这笔交易可能被丢弃"的风险 —— 这个风险靠动手前先验证来处理,而不是靠等。
Geyser 类流式推送
专门的账户和交易更新流。最快,运维最重,通常要付费。
判断:到底该跟什么
检测给你一个签名。接下来你要知道发生了什么、以及要不要跟。
const tx = await connection.getParsedTransaction(signature, {
maxSupportedTransactionVersion: 0,
});
// 读余额差比解析指令可靠,
// 因为它在所有 DEX 和路由器上表现一致。
const pre = tx.meta.preTokenBalances ?? [];
const post = tx.meta.postTokenBalances ?? [];
多花一点力气读余额差、而不是去解码指令,是值得的。★每个 DEX 和聚合器都有自己的指令布局,而且会变。余额差不会。★
几个比想象中更重要的过滤条件:
相对于该钱包的仓位大小。 一个交易者拿 0.5% 的仓位买入,和拿 20% 买入,表达的完全不是一回事。两种一视同仁地跟,等于你没读懂这个信号。
这是建仓还是加仓? 买入一个他已经持有的代币是在摊平成本;买入一个全新的是开新仓。含义不同,紧迫性也不同。
代币年龄和流动性。 一笔进入浅流动性池子的交易,你跟进去时价格会往不利方向走 —— 因为你自己这笔就占了池子深度的可观比例。
他之前是不是一直这么干? 一个每小时都在同一个代币上买进卖出的钱包,跑的是你没法靠"跟单笔交易"复制的策略。
执行:利润在这里决出胜负
下面这个算术决定了跟单机器人能不能成立。
你跟的那个交易者在某个价格买入。你在 N 个 slot 之后买入,而价格已经动了 —— 因为他那笔推动了价格,也因为所有跟他的人都在买。
★你的入场价按结构就一定比他差。★ 唯一的问题是差多少,而这是"你落后多少个 slot"的函数。
按 Solana 的出块节奏换算:
| 落后 slot | 落后时间 |
|---|---|
| 2 | 不到一秒 |
| 5 | 约两秒 |
| 10 | 约四秒 |
对一个刚吃了一笔大买单的代币来说,四秒很长。
要缩短它,靠的还是老三样:priority fee 按实时行情算、重试到 blockhash 过期、以及一条拥堵时不会被降级的提交路径。
import { ComputeBudgetProgram } from "@solana/web3.js";
// 费用按账户竞争。要传你实际会写入的账户。
const recent = await connection.getRecentPrioritizationFees({
lockedWritableAccounts: writableAccounts,
});
const fees = recent.map((r) => r.prioritizationFee).sort((a, b) => a - b);
const median = fees[Math.floor(fees.length / 2)] ?? 0;
instructions.unshift(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: Math.max(median * 2, 5_000),
}),
ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 }),
);
再加上持续重试到 blockhash 过期,以及一条在区块空间被抢时不会被降级的提交路径。
滑点陷阱
跟单有一个特有的失败模式,值得单独点名。
你检测到一笔买入,决定跟,滑点容忍度设了个看起来合理的 1%。但你跟的那笔已经把价格推高了 3%,其他跟单的人又推了 2%。你的交易因滑点失败。
于是你把容忍度放宽到 10%。现在成交了 —— 成交在一个比原钱包差 8% 的价格上,而他可能在 5% 利润时就出场了。
★两种结果都是亏。真正该修的不是容忍度,是 slot 数。★
如果你稳定落后五个以上 slot,任何滑点设置都救不了这个策略。容忍度是症状旋钮,延迟才是真正的变量。
上链应该是什么水平
我们投递节点上的真实交易:中位确认 336 毫秒 —— 不到一个 slot。
两个 slot 就是比你跟的那个钱包晚不到一秒 —— 而这还没算上你自己的检测和判断耗时。这就是你的预算。
怎么衡量有没有效果
盯差距,别盯成交率。一个每笔都以糟糕价格成交的机器人,在成交率看板上很健康,但在亏钱。
log({
sourceWallet,
sourceSlot, // 他那笔落块的 slot
ourSlot, // 我们这笔落块的 slot
slotGap: ourSlot - sourceSlot,
sourcePrice,
ourPrice,
entryGapBps: Math.round(((ourPrice - sourcePrice) / sourcePrice) * 10_000),
});
★slotGap 和 entryGapBps 两个放一起,能说明全部问题。★ 如果 slot 差很小、入场差还是很大,说明你跟的这批交易对市场冲击太大、本来就不适合跟。如果 slot 差很大,那是个你能修的执行问题。
BoltTx 在这里的位置
我们做执行那一半。不做检测,不做钱包分析,不做索引。
提交走我们自建的四区域投递节点,带 SWQoS 路由,不暴露公共 mempool —— 交易在落块之前,传输途中观察不到。你在本地签名,我们不托管资金、不代签、不修改交易内容。
tip 带在交易里,从你自己的钱包链上支付。交易 revert 的话 tip 跟着一起 revert —— 这是 Solana 原子交易的机制决定的。只有到达链上的交易才计费。
免费领 API key,没有月费:
// 选离你机器人最近的区域
const connection = new Connection("https://la.bolttx.io/?api-key=YOUR_KEY");
接上之后记录一周的 slotGap,看它有没有变。
常见问题
Solana 有跟单 API 吗? 没有单一接口能做这件事。跟单是由三块拼起来的:盯钱包活动、解析他们交易了什么、以及提交你自己的交易。前两块是对任意 RPC 的读操作,第三块决定你的利润。
怎么实时追踪一个 Solana 钱包的交易?
用 WebSocket 在 processed 承诺级别订阅该钱包的日志。轮询 getSignaturesForAddress 也行,但更慢,而且钱包一多就扩展不动。
跟单要多快才成立? 快到价格还没跑过你的利润空间。落后两个 slot 是不到一秒;落后十个 slot 约四秒 —— 对一个刚吃了大买单的代币来说,通常已经太晚。
为什么我的跟单老是滑点失败? 因为你跟的那笔推动了价格,其他跟单的人也在推。放宽容忍度只会让你在更差的价格成交,而不是解决成因。如果你落后五个以上 slot,slot 数才是问题。
怎么知道一个钱包买了什么代币?
从解析后的交易里读 preTokenBalances 和 postTokenBalances 算差值。这个做法在各个 DEX 和路由器上表现一致,而解码指令则因程序而异、还会变。
该跟一个钱包的每一笔交易吗? 不该。按仓位占比、是建仓还是加仓、以及代币流动性来过滤。把 0.5% 仓位和 20% 仓位同等对待地跟,等于忽略了你本来想读的那个信号。
检测该用哪个承诺级别?
processed。等 confirmed 会花掉一个以上你追不回来的 slot。代价是你看到的东西里有一小部分可能被丢弃,而这靠"动手前先验证"来处理,不是靠等。
不自己写机器人能做 Solana 跟单吗? 有托管服务。代价是你继承了他们的延迟和过滤逻辑,而且看不到某笔交易为什么被跟或没被跟。
能同时盯多少个钱包? 用 WebSocket 订阅可以很多 —— 你是在接收推送,不是在发请求。用轮询很快就会撞限流,因为每个钱包每一轮都是一个独立请求。
为什么我成交的价格比被跟的钱包差很多? 你的入场价按结构就更差,因为他那笔在你到达之前就推动了价格。差多少是"落后几个 slot"的函数。把两个 slot 号和价差都记下来,才知道该修哪一段。
跟单适合新上线的代币吗? 那里最难。流动性浅意味着原始交易和你的交易都会显著推动价格,而两者之间的差距,恰好在代币最新的时候最大。
跟单和抢跑有什么区别? 跟单是在一笔交易已经上链之后跟进。抢跑是在交易被收录之前、基于对它的知情去行动。前者用的是公开信息,后者依赖在传输途中看到东西。
怎么避免跟到一个钱包的亏损交易? 事先无法知道。你能做的是按交易特征过滤 —— 仓位大小、是不是新仓、流动性 —— 并且按来源钱包分别统计自己的表现,而不是看总和。
跟单需要 WebSocket 吗? 做检测的话,比轮询快很多,而且钱包一多扩展性好得多。提交自己的交易不需要 —— 那是 HTTP 调用,而且往往走另一个端点。
为什么我的跟单交易都上链了,还是在亏? 去看入场差,别看成交率。一个每笔都比来源钱包差 8% 成交的机器人,在成交率看板上很健康,照样亏钱。
怎么挑要跟的钱包? 上线跟之前,先拿他们的历史做回测。一个在几百笔交易上有稳定实现收益的钱包,和一个靠几笔大赢冲上榜的钱包,是完全不同的东西 —— 而排行榜上后者要常见得多。