Integrating a trading bot with Jupiter SDK: where trades get lost
A trading bot on Solana rarely loses trades in the strategy itself — the chain “signal → order → route → execution” breaks at the junction between calculation and swap confirmation.
The signal is generated, the order is sent, but by the time of signing the route no longer matches the market, and the trade closes at a different price. Here, integrating a trading bot with Jupiter SDK stops being a formality and becomes an engineering task.
Trade routing is calculated based on the current state of pools: the aggregator splits the volume across several hops to assemble the best price.
During volatility, the state changes in fractions of a second — the route calculated before sending becomes stale, and some hops drop out due to liquidity provider limits. The bot then receives not an explicit rejection but execution at a worse price.
Order execution failures look different: a transaction fails the tolerance check, route rebuilding eats up time, price slippage exceeds the boundary set by the strategy.
During sharp moves, this repeats in series, and the cumulative drift from expected profitability stops being random error.
We break down how this chain is closed: where to recalculate the route anew, how to keep slippage within tolerance, what to do with provider rejections, and how to prioritize orders so the signal reaches execution in full.
What integration delivers: execution speed and resilience to failures
A managed connection to a liquidity aggregator is not “plugged in and working” but a set of verifiable indicators. We record the effect of integration with Jupiter before and after implementation across four metrics: they show where the bot gains and where it degrades.
- Share of executed orders. We calculate the ratio of closed trades to sent ones, not to those “that reached the exchange.” A route that accounts for slippage and fees raises this share by tens of percent — some orders that previously ended in rejection are now executed.
- Recovery time after a failure. We look at how long it takes to return to operation after node unavailability or a rejected transaction. If the channel breaks, the bot switches to a backup route without losing the order queue.
- Eliminated failure class. We keep a separate list of causes that stopped occurring: stale quotes, duplicate signatures, connection drops. A failure class that disappears is more important than the overall percentage.
- Volume of manual edits. We count how many times the operator intervened per day. Every intervention is a delay and a risk of error, so the goal is to leave only cases where a human decision is truly needed.
- Control of fee expenses. We consolidate payments for routing and priority into a single trade report to see where there is overpayment and where saving time is justified.
We take measurements on the same set of scenarios before and after — otherwise the comparison is unfair and the numbers cannot be trusted.
Ways to connect the bot to a liquidity aggregator
The connection mode determines not only execution latency but also where the bottleneck will appear under load.
A direct call to the aggregator keeps all routing inside the bot, an intermediary service moves it to a separate loop, and a queue decouples order frequency from the network confirmation rate.
The choice is dictated by the trading profile: arbitrage needs minimal latency, while a portfolio strategy needs resilience and accounting.
| Option | When it fits | Limitations |
|---|---|---|
| Direct call to the aggregator from the bot code | one strategy, moderate order flow, minimal latency needed | the bot holds the keys itself, retries, and request limits; route errors and slippage are handled on its side |
| Own intermediary service | multiple strategies or bots, shared position accounting, action audit needed | an extra segment in the execution chain, a separate monitoring and update loop |
| Third-party relayer | fast start, no resources to maintain your own service | dependence on someone else’s pace and access policy, limited control over the route |
| Batch processing via an order queue | many small orders, peak load, latency not critical | execution latency, idempotency required, and failure handling for each order |
Limitations should be checked before launch, not after a series of rejected transactions. A combined mode is common practice: an order queue at the input, an own intermediary for routing, and a direct call for priority trades.
Work sequence: from strategy review to handover to operations
We work in iterations: first we record what the strategy does and where it breaks, then we assemble the execution loop, and only after that do we allow the bot to touch live funds. Each stage is closed with an artifact by which the result can be verified.
-
Strategy and failure point audit. We review the logic of entries and exits, cancellation conditions, behavior during liquidity shortage and partial execution. We separately record the connection loss scenario: resend, wait, or full stop.
-
Interaction scheme. We describe the order path — from signal to the swap route in Jupiter SDK and network confirmation. We determine where state lives and how the bot restores recent trades after a restart.
-
Connection and key setup. We provide primary and backup network access, separate trading and withdrawal keys, and restrict signing permissions. Secrets are stored separately from the bot code.
-
Historical data validation. We run the strategy over past periods and compare calculated execution with actual prices and fees. We look at the share of rejected routes and behavior during sharp market moves.
-
Pilot launch. We start with minimal limits and manual withdrawal confirmation. Each trade is checked against the log: price, volume, time to confirmation, deviation from the calculation.
-
Handover to operations. We provide documentation, the loop diagram, a monitoring panel with alerts, and an emergency stop procedure, and train the client’s team.
Handover package: configurations, diagrams, procedures
After handover, the client retains not only a working bot but also a documentation package that allows the system to be maintained without us.
We record not the fact that “everything works” but the state: which parameter is responsible for what, where the boundaries of responsibility lie, and what to do when something deviates from the norm.
- Configurations — per-environment files with comments: trading pairs, limits on trade size, price slippage thresholds, network connection points.
- Service interaction diagram — signal collector, route calculation via Jupiter, transaction signer, position accounting, and external sources; with failure zones and places of possible delays.
- Fund access policies — separation of hot and cold storage, limits per trade and daily turnover, key rotation procedure, who can stop trading and how.
- Validation log — historical data and limited-capital runs, a list of tested failure scenarios, a checklist before each update.
- Maintenance procedure — frequency and depth of checks, dependency update procedure, response to changes in network conditions.
- Incident review procedure — what counts as an incident, what data is retained, how quickly we review it, and how we record findings.
- Rollback procedure — how to return to the previous configuration version and pause trading without losing position accounting.
Case study: the bot stopped losing trades during sharp market moves
The client is a trading team operating on the Solana spot market through the liquidity aggregator Jupiter; integration with its SDK placed orders along a route calculated at request time.
During a sharp price move, the route could become stale: the transaction was not confirmed, and some trades were lost.
During hours of elevated volatility, the share of executed orders fell to about two-thirds, and after a series of rejections the bot took minutes to return to operation.
What we rebuilt
The route is now rebuilt before signing: we query several liquidity sources, compare price, depth, and fees, and sign only the current option.
Rejections were broken down into classes — stale route, insufficient liquidity, too-low fee priority — and each was assigned its own behavior instead of one common retry.
Order resubmission became idempotent: an order has one identifier, the previous attempt is brought to a final status, and duplicates are not created.
Two explicit states remain — executed exactly once or not executed, with no hanging trades and no manual edits during position reconciliation.
The result
The share of executed orders during sharp-move windows rose to ~95%, and recovery time after a series of rejected transactions fell from minutes to seconds. Manual intervention in disputed trades is no longer required.
How does a liquidity aggregator differ from direct pools?
A liquidity aggregator is a layer that queries dozens of pools and assembles a trade route from several hops. It splits volume across venues, adds depth where a single pool lacks it, and returns the final price taking into account the fees of each step.
For trades that noticeably move the market, this is often the only way to avoid a worse average rate.
A direct pool is the opposite approach: you work with a specific reserve and see in advance how the price will change after execution. The route will not be rebuilt at the moment the transaction is sent — what is calculated is what will execute.
Hence the price predictability: when a trade is part of an arbitrage bundle or the offset of an open position, the best rate at the moment is secondary compared to a stable result.
Comparison of the approaches comes down to two questions: how large the volume is relative to pool depth, and whether a surprise in execution is acceptable.
An aggregator, including Jupiter, wins on small and medium volumes, where the final rate matters and route splitting adds liquidity. A direct pool is stronger where repeatability, a clear set of wallet states, and control over every step of the trade are needed.
Therefore, choosing a trade route is not a one-time setting but a bot policy: calculate expected drawdown before sending, maintain price limits, and check the freshness of reserves before every entry.
Common technical concerns: limitations and risks
Before launch, clients ask the same questions — and they concern not trade logic but operations: who holds the keys, what happens if an external service fails, what limits volume, and what the team will have after handover. Below are short answers on each point.
Some concerns are resolved at the design stage: the boundaries of the integration module, service key permissions, and retry policy are fixed before the first live trade, not after an incident.
Where are the keys stored, and who has access to them?
The private key never leaves your environment: transaction signing happens locally, and only the signed transaction goes out.
We issue the service key with reduced permissions — trading without withdrawal — and store it in a secure vault or hardware module, not in code or a repository. Access is granted by role, with an access log.
What happens if a network node or the aggregator responds with a delay?
The delay does not turn into duplicate trades: each order is marked with a signature and is retried only with a status check. If the transaction is not confirmed, the volume is not debited and the position is not considered open.
The queue and retry ceiling are set in advance so the bot does not “hammer” the network during degradation.
Are there limits on trade volume and frequency?
Limits are set not only by the network but also by the Jupiter aggregator itself: the larger the order, the wider the execution price deviation. That is why we split large volume, set a ceiling per trade and per time window, and compare the actual price with the calculated one.
Will existing code have to be rewritten?
No. We build the integration as a separate layer on top of your logic: if the bot is already running, we connect a transition module and gradually move trades onto it. Rollback means returning to the old execution path without losing history.
What happens after launch and handover?
We hand over configurations, a flow diagram, and instructions for typical failures, and train the team. After that comes the warranty support period: we monitor execution metrics and update the integration when external rules and response formats change.
Let’s discuss the task: what is needed for an estimate and work plan
A task estimate relies on facts about your current integration, not assumptions. Below is the input data that removes uncertainty about the scope and timeline: if something is missing, that is also part of the picture, and we factor it into the plan.
- strategy description: entry and exit signals, cancellation conditions, what you consider a normal result for the day;
- current Jupiter integration scheme: where the bot runs, how transactions are signed, which swap routes are already in use;
- rejection statistics: share of unexecuted orders, retries, routing errors, cases of slippage above the limit;
- accesses and keys: who owns the wallet, what permissions are granted, what must not be touched in the production environment;
- logs and metrics: what is written now, at what volume, how long it is stored, and who reads it;
- load expectations: how many trades per hour, peak windows, acceptable latency in reacting to the market.
With this set, we come back with an estimate and work plan: stages, dependencies between them, and what we verify at each step.
The approval order — we fix the scope and boundaries, confirm accesses and environment, then start; we discuss new requirements separately and do not silently pull them into the timeline.







