Short answer: Arbitrage bot development is mostly about what happens when two independent exchanges do not behave as one market. Because no atomic cross-venue execution exists, the build needs pre-funded balances on each venue, leg-risk handling, a fee and transfer model, clock sync, a rate-limit budget per venue and reconciliation after a failed leg, while the price comparison is the small part. An arbitrage bot can lose money, and this guide makes no claim about how often usable gaps exist.
Check the fees on each leg with your own figures: Crypto trading fee calculator and Crypto arbitrage calculator.
Comparing two prices takes a few lines of code. Most of the work in an arbitrage build goes into what happens when two exchanges, each with its own clock, limits and failure modes, disagree with your records in the middle of a trade.
This guide is for leads scoping such a build. The concept, types and cost stack are in crypto arbitrage bot. It gives no opportunity figures, recommends no venue or rule, and excludes flash-loan, MEV and sandwich variants, which raise different risks, including harm to other users. API details come from official Binance, Bybit and Kraken documentation read on 4 November 2026, listed alphabetically and with no recommendation. Disclosure: Fincode sells this kind of work, so this guide is not neutral on that point. Fincode builds from rules the customer wrote and does not supply strategies.
What does arbitrage bot development involve?
Arbitrage bot development involves building an execution system across independent venues, in which the price comparison is one module among several.
| Module | Job | What goes wrong without it |
|---|---|---|
| Venue connectors | Normalize symbols, filters, errors and order states | A rejection is read as a fill, or the reverse |
| Edge calculator | Apply the customer's threshold, net of current fees, at the intended size | Trades that only looked positive before costs |
| Inventory manager | Track balances and limits per venue | A venue runs out of one asset mid-trade |
| Leg coordinator | Send both legs and track each to a final state | Unmatched exposure goes unnoticed |
| Rate-limit budget | Allocate requests per venue | Requests are refused when a cancel is most needed |
| Reconciler | Compare local records with each venue | The bot trades on a position it does not have |
CCXT shortens the first module, but it unifies method names, not the differences covered below, and each venue's book must be synchronized as described in maintaining a local order book.
Pre-funded balances and no atomic execution
Both legs must trade from balances already on each venue, and nothing makes the two orders succeed or fail together.
Transfers wait for network confirmations, so they cannot be part of a live trade. That leaves capital split across venues and exposed to each platform, with rebalancing as a separately controlled step: the bot's keys carry read and trade permission only.
Each venue also decides alone. Bybit's order documentation says an acknowledgement only means the request was accepted and that the result arrives asynchronously. Binance says that after an HTTP 5XX response the execution status is unknown and the order may have succeeded. Immediate-or-cancel and fill-or-kill conditions, documented by all three venues, limit what one order leaves behind, but they do not tie one venue's order to another's. The result is leg risk: one side fills, the other does not, and you hold a position you did not intend.
The specification therefore needs a written leg policy: how long to wait, whether to retry or close, what price is acceptable for closing and when to stop trading. Those are the customer's decisions; the developer makes them executable and testable.
The fee and transfer model
The edge calculation needs current fee and transfer data per venue, read from the API rather than hard-coded.
| Data | Binance | Bybit | Kraken |
|---|---|---|---|
| Your trading fee | GET /api/v3/account/commission | GET /v5/account/fee-rate | POST /0/private/TradeVolume with pair |
| Deposit and withdrawal status | depositEnable, withdrawEnable per network in GET /sapi/v1/capital/config/getall | chainDeposit, chainWithdraw in GET /v5/asset/coin/query-info | status in the public GET /0/public/Assets |
| Withdrawal fee | withdrawFee, same endpoint | withdrawFee, withdrawPercentageFee, same endpoint | WithdrawInfo: deprecated, needs the Withdraw permission |
A fee model must never be the reason a trading key gains withdrawal rights: Kraken's WithdrawInfo requires the Withdraw permission, so read such values outside the trading process. A suspended network is a stop condition, because inventory cannot be moved, and what pauses is set by a rule the customer has written. Model a transfer as both a cost and a delay.
Latency budget and clock sync
Set a maximum age for every quote and keep the host clock synchronized, because each venue judges request times by its own rule.
A latency budget adds up the time from the market data event to the last order reaching a venue. The legs run in parallel, so the slower leg sets the window.
Hypothetical budget. Quote age limit 40 ms, decision 5 ms, order path 30 ms to venue A and 120 ms to venue B. The exposure window is 40 + 5 + 120 = 165 ms, not 195 ms. Measured numbers replace these, per venue and per time of day.
| Venue | Rule | Note |
|---|---|---|
| Binance | Rejects a timestamp more than 1 second ahead of server time or older than recvWindow | Default 5,000 ms, maximum 60,000 ms |
| Bybit | server_time - recv_window <= timestamp < server_time + 1000 | Default 5,000 ms; the guide asks for NTP-synchronized time |
| Kraken | The nonce must always increase per API key | Repeated invalid nonces can bring temporary bans; Add Order also takes a deadline 2 to 60 seconds ahead |
Do not widen these windows to hide drift. Alert on clock offset against each venue's server time, and log your own receive time next to venue timestamps.
A rate-limit budget across venues
Give each venue its own budget and reserve part of it for cancels and reconciliation, because limits count different things on each venue.
| Venue | Counted by | Documented limit | Over the limit |
|---|---|---|---|
| Binance | IP for request weight, account for unfilled orders | 6,000 weight a minute; 100 unfilled orders per 10 seconds; 200,000 a day | HTTP 429, then an automated IP ban (HTTP 418) from 2 minutes to 3 days |
| Bybit | IP, plus a rolling per-second limit per UID by endpoint | 600 requests per 5 seconds per IP; order create per UID 10/s linear, 20/s spot (UTA 2.0 Pro table) | HTTP 403 and a wait of at least 10 minutes at IP level; retCode 10006 at API level |
| Kraken | A counter for each API key | Maximum 15, 20 or 20 with decay of 0.33, 0.5 or 1 per second (Starter, Intermediate, Pro); order calls use a different limiter | EAPI:Rate limit exceeded |
Hypothetical reconciliation on Kraken's Starter tier. Polling has pushed the counter to 14 when a leg fails. Reconciling needs balances (1), open orders (1), an order query (1) and trade history (2), which is 5 points against a maximum of 15. The counter would be 4 points over, and at a decay of 0.33 per second that means about 12 seconds of waiting with a position open.
Stream market data instead of polling, and keep a reserve that normal operation cannot spend. Because Binance counts weight per IP, other processes on the same address draw on the same budget.
Crypto arbitrage bot in Python: a failed-leg sketch
The code path that matters most runs after a leg fails, so write and test it first.
This sketch uses synthetic venues with scripted fill ratios and no network calls. Prices and fees are hypothetical and say nothing about how often such a gap exists. It closes any unmatched quantity on the venue that filled, then compares total inventory with the starting figure and halts on any difference. Venue b holds 100 in quote because buying back costs more than the sale returned; the sketch does not check that a repair can be paid for.
from decimal import Decimal as D
class Venue:
"""Synthetic venue: pre-funded balances, scripted IOC fill ratios."""
def __init__(self, base, quote, fee, fills):
self.base, self.quote, self.fee = D(base), D(quote), D(fee)
self.fills = iter(fills)
def ioc(self, side, qty, price):
got = qty * D(next(self.fills, 0))
cash = got * price
if side == "BUY":
self.base += got
self.quote -= cash * (1 + self.fee)
else:
self.base -= got
self.quote += cash * (1 - self.fee)
return got
def run_pair(buy, sell, qty, ask, bid, slip=D("0.003")):
"""Send both legs, close any unmatched quantity, then reconcile."""
start = buy.base + sell.base
if buy.quote < qty * ask * (1 + buy.fee) or sell.base < qty:
return "SKIP not enough inventory"
bought = buy.ioc("BUY", qty, ask) # concurrent in production
sold = sell.ioc("SELL", qty, bid)
gap = bought - sold # unmatched quantity
if gap > 0:
buy.ioc("SELL", gap, ask * (1 - slip))
elif gap < 0:
sell.ioc("BUY", -gap, bid * (1 + slip))
drift = buy.base + sell.base - start # must be zero after repair
return f"{'HALT' if drift else 'OK'} gap={gap:.1f} drift={drift:.1f}"
cases = {"both legs fill": ([1], [1]),
"sell leg 40%": ([1, 1], [D("0.4")]),
"sell leg rejected": ([1, 1], [0]),
"buy leg rejected": ([0], [1, 1]),
"repair also fails": ([1, 0], [0])}
for name, (fills_a, fills_b) in cases.items():
a = Venue(0, 5000, "0.001", fills_a)
b = Venue(10, 100, "0.001", fills_b)
before = a.quote + b.quote
status = run_pair(a, b, D(10), D("100.00"), D("100.30"))
print(f"{name:18} {status:24} cash {a.quote + b.quote - before:+9.2f}")Output:
both legs fill OK gap=0.0 drift=0.0 cash +1.00
sell leg 40% OK gap=6.0 drift=0.0 cash -2.60
sell leg rejected OK gap=10.0 drift=0.0 cash -5.00
buy leg rejected OK gap=-10.0 drift=0.0 cash -5.02
repair also fails HALT gap=10.0 drift=10.0 cash -1001.00Read the cash column as the shape of the problem, not a forecast: here one rejected leg costs about five times what one filled pair returns, and the last case leaves an open position for a person to resolve. A real connector sends the legs concurrently and sets a client order ID (newClientOrderId on Binance, orderLinkId on Bybit, cl_ord_id on Kraken), so that a timed-out order can be looked up before any resend.
Reconciliation and testing before real money
Reconcile after every failed leg and on a schedule, and use test environments to prove fault handling, not opportunity.
Reconciliation compares open orders, fills and balances with each venue and stops on any unexplained difference. Some venues add a safety net: Kraken's CancelAllOrdersAfter cancels all orders when a timer expires, and its documentation suggests calling it every 15 to 30 seconds with a 60-second timeout. Such a timer cancels orders; it does not close a position.
Test environments cannot tell you whether a gap is real. Binance states that its Spot Testnet's prices and order books are independent from the live exchange, that Demo Mode's are similar to it, and that realistic data is not real data. A check of public endpoints on 4 November 2026 found testnet BTCUSDT quotes close to live ones with different quantities, so a close price on a given day proves nothing about live fills. Test behavior instead: inject rejected legs, partial fills, timeouts and dropped connections, and replay recorded books.
A monitoring-only system built first shows whether your rules would ever trigger after costs, as in the multi-exchange arbitrage monitoring scenario. Limits and the kill switch are covered in trading bot risk management.
Crypto arbitrage software: buy, build or hire?
Buy only if a product's model fits your rules and you accept who holds the keys, and build or hire once you can write down the rules and the leg policy.
- Specify: venues, instruments, leg policy, inventory limits per venue, the rebalancing rule, maximum quote age, budget per venue and halt conditions.
- Accept against: the failure cases above, never a return target.
- Check: each venue's API terms.
An arbitrage trading bot development company can be assessed with the checklist in crypto bot development company, and one seller's deliverables are on the custom trading bot development page. Treat any monthly return quoted for crypto arbitrage trading software as a reason to walk away.
Which venues you may use depends on your jurisdiction and each platform's terms. For readers in Turkey, Article 99/A of Capital Markets Law No. 6362 treats activity by foreign crypto-asset platforms directed at Turkish residents as unauthorized. Never trade against yourself to manufacture volume: wash trading is market abuse. Automated trading can lose money, and correct software does not change that.
Frequently asked questions
How is an arbitrage bot different from a normal trading bot?
It has to coordinate orders on independent venues that cannot be made atomic, so leg handling, per-venue inventory, rate limits and reconciliation dominate the work.
What is leg risk?
The risk that one side of the trade fills and the other does not, leaving an unintended position that has to be closed at whatever price is then available.
Can I build a crypto arbitrage bot in Python?
Yes. Python with a library such as CCXT suits many designs, within the latency your measurements allow. Failure handling is the harder part, whatever the language.
Can an arbitrage bot lose money?
Yes. Fees, slippage, failed legs, outages and counterparty failure can each turn a visible gap into a loss, and backtests do not guarantee future results.
Sources
- Binance Spot API documentation on GitHub: REST API (limits, request security, HTTP 5XX, account commission) (opens in a new tab)
- Binance Wallet API: Capital (coin and network configuration) (opens in a new tab)
- Binance Spot API documentation: Demo Mode (testnet and demo mode compared) (opens in a new tab)
- Bybit V5 API: Place Order (asynchronous response, time in force, orderLinkId) (opens in a new tab)
- Bybit V5 API: Rate Limit (opens in a new tab)
- Bybit V5 API: Integration guidance (timestamps and recv_window) (opens in a new tab)
- Bybit V5 API: Get Coin Info (chain status, withdrawal fees) (opens in a new tab)
- Bybit V5 API: Get Fee Rate (opens in a new tab)
- Kraken Developers: Spot REST Rate Limits (opens in a new tab)
- Kraken Developers: Spot REST Authentication (nonce) (opens in a new tab)
- Kraken Developers: Add Order (time in force, deadline, cl_ord_id) (opens in a new tab)
- Kraken Developers: Cancel All Orders After X (opens in a new tab)
- Kraken Developers: Get Withdrawal Information (permissions, deprecation) (opens in a new tab)
- Kraken Developers: Get Trade Volume (fees per pair) (opens in a new tab)
- Capital Markets Law No. 6362, consolidated text, Article 99/A (Turkish) (opens in a new tab)
This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the editorial policy.