← Back to guides
April 20, 2026·12 min read

A Latency Budget for a Profitable Solana Copy-Trading Bot (Slot → Bundle in 400ms)

A Latency Budget for a Profitable Solana Copy-Trading Bot (Slot → Bundle in 400ms)
LatencyCopy tradingEngineeringSolanaJitoPerformance

A Solana copy-trade bot is a latency stack. If you don't know where your milliseconds are going, you're not profitable — you're just hoping the slow parts aren't where the other guys are fast. This post breaks the stack apart, stage by stage, with real numbers.

Why 400ms Is The Magic Number

Solana slots are ~400ms today. If you haven't submitted your bundle within a slot of the original KOL trade, you're going to land in block N+1 at the earliest — and on a popular Pump.fun meme launch, block N+1 is where the amateurs play. You're buying into pump momentum that the on-time bots are already exiting.

So the budget is: from the moment the KOL's trade lands in a slot, to the moment your signed bundle is in Jito's mempool, you want p50 < 400ms. p99 is what you care about on laggy days.

The Seven Stages

Every copy-trade bot has roughly the same seven stages, regardless of stack. Here's what they look like:

#StageWhat happens
1Slot landedValidator confirms the transaction containing the KOL's trade
2Geyser hookon_transaction fires inside the validator process
3gRPC pushData serialized and pushed to your subscribed client
4Filter & parseYour matcher checks the watchlist and decodes the instruction
5Strategy evalSize the copy trade, pick slippage, pick priority fee
6SignBuild versioned tx, sign with private key
7Jito bundleShip the signed bundle to the Jito block-engine

Measured p50 / p99 Per Stage

These are real numbers from Subglow's own copy trader, measured over a week of Pump.fun traffic with an AMS-colocated gRPC subscription and a browser-side signer:

Stagep50p99Where it comes from
Slot → Geyser hook3ms8msValidator internal; not tunable from outside
Geyser → gRPC client30ms80msAMS → client network + pre-parse
Filter & parse<1ms5msRust matcher, pre-parsed JSON helps here
Strategy eval5ms25msJupiter quote for non-Pump.fun mints dominates
Sign (browser)80ms180msVersioned tx build + ed25519 sign
Bundle submit50ms140msBrowser → backend → Jito AMS
Total~170ms~440ms

Stage 1 + 2: Slot → Geyser (Not Tunable)

This is validator-internal. Your only lever is picking a gRPC provider whose validator is on healthy hardware and whose Geyser plugin isn't backpressured. If your provider's p50 slot-to-hook is >20ms, they're overloaded or their machine is slow. Test this with a dummy subscription on blockSubscribe and compare slot timestamps.

Stage 3: Geyser → gRPC Client (Colocate Aggressively)

This is the single biggest chunk of wall-clock time you can optimize. Two levers:

  • Physical colocation. AMS → Frankfurt is ~10ms. AMS → NY4 is ~80ms. AMS → Tokyo is ~220ms. If your bot runs in Frankfurt, insist on a Frankfurt gRPC endpoint. Providers that only offer one region are hurting you.
  • Pre-parsed output. Raw Yellowstone protobuf hands you Borsh-encoded instruction data. You have to decode the protobuf envelope, then Borsh-decode the Pump.fun/Raydium/Jupiter instructions before you can act. Pre-parsed providers (Subglow) do this on their side so your matcher goes directly from bytes to struct. On a busy Pump.fun slot this saves 5–15ms.

Stage 4: Filter & Parse (Rust)

If you're writing in TypeScript/Node, this stage can blow up to 20–50ms during a market spike because V8 GC pauses during high allocation pressure. Port the hot path to Rust and it's single-digit ms or better. The pre-parsed path on Subglow cuts this further because Pump.fun/Raydium/Jupiter instructions arrive as structured JSON, not raw Borsh.

Stage 5: Strategy Eval

For Pump.fun mints, strategy is cheap — the bonding curve price is deterministic from the mint's virtual reserves, so you don't need to ask Jupiter. Just do the arithmetic inline.

For Raydium or Jupiter mints, you need a route quote. That's a Jupiter API call with ~10–40ms overhead depending on region. Best practice: pre-fetch routes for watchlist tokens in the background, so when an intent arrives, you have a cached route ready.

Stage 6: Signing

If you have a server-side signer, this is 1ms. If you have a browser signer (non-custodial, like Subglow), it's 80–180ms because you're doing a WebSocket round-trip to the browser and then back. This is the architectural cost of keeping the user's keys out of your database.

On the browser side itself, the sign operation is fast — nacl.sign is under 1ms. What eats the time is versioned transaction build (ALT lookups, compute unit limit simulation, priority fee calc). Do as much of this as possible on the backend and ship the browser a mostly-built transaction template.

Stage 7: Bundle Submission

Submit as a Jito bundle, not a sendTransaction. Bundles land via the Jito block-engine which has its own fast path. A regular sendTransaction takes a trip through the mempool and can be held back by network congestion.

Your Jito endpoint region matters too: AMS is where most block-engine action is. If your bot submits from US-East but the block-engine is AMS, that's 80ms of transatlantic RTT you didn't need to pay.

What A Bad Stack Looks Like

We see this all the time in user churn conversations:

  • Yellowstone provider in US-East, bot in EU: +80ms baseline
  • Raw protobuf, TypeScript matcher with Borsh decoding: +30ms
  • Custodial signer behind 3 proxies: +200ms on a spike
  • sendTransaction to a random RPC: +150ms and frequent drops

Total: ~800ms p50, ~2s p99. Two slots of delay. You'll catch the good trades about half the time.

The Clean Stack

  • Subglow gRPC in your region (AMS or Frankfurt for most of EU, NY4 for US-East)
  • Pre-parsed JSON (skip the Borsh step entirely)
  • Rust or highly optimized TS matcher (avoid V8 GC during bursts)
  • Cached Jupiter routes for watchlist tokens
  • Backend-built, browser-signed versioned tx (non-custodial) or server-signed (custodial)
  • Jito bundle submission from the same region as the block-engine

That's how you get under 400ms p50. Anything else is bonus performance — at 170ms p50 you're ahead of the pack; at 80ms p50 you're using the same stack Subglow runs internally.

Related Reading

Ready to try it?

Get your API key and start receiving filtered data in under 5 minutes. Free tier available.

Get started