# Trading bot not working? Find the fault in this order

URL: https://fincodelabs.com/en/blog/trading-bot-not-working · Publisher: Fincode (Fincode Yazılım ve Teknoloji A.Ş.), Istanbul, Turkey · Category: Trading bots · Published: 2026-11-06 · Updated: 2026-11-06

Trading bot not working? Find the fault in order: clock, key, symbol rules, rate limits, stale data and restarts, with a tested read-only diagnostic script.

**Short answer:** When a **trading bot is not working**, check from cheapest to hardest: whether the process runs and reaches the exchange, the clock, the key's environment, IP and permissions, the symbol's tick, lot and minimum-value rules, rate limits, data freshness, and whether the bot's records match the exchange. This finds software faults only; it never explains why a rule loses money, and automated trading can lose money. If the message points to a regional restriction or a KYC block, stop and ask the platform, because there is nothing to fix in code.

Codes here come from official Binance Spot, Bybit V5 and Kraken documents, read on 5 November 2026. Naming a platform is not a recommendation; which platforms 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.

Alert-side faults are in [TradingView alerts not working](https://fincodelabs.com/en/blog/tradingview-alerts-not-working), and Binance codes one by one in [Binance API error codes](https://fincodelabs.com/en/blog/binance-api-error-codes).

## Why is my trading bot not working? Start from the symptom

The symptom tells you which check to run first.

**Symptom, first check and next check**

| Symptom | Check first | Then check |
| --- | --- | --- |
| No orders at all | Process running, live mode rather than dry run, a rule firing in the log | Clock offset, then key environment, IP allowlist and permissions |
| Orders rejected | The exact message, not the status line | Symbol rules for tick, lot and minimum value, then balance |
| Works, then stops | Rate-limit replies, bans and key expiry | Age of the last data message; the official API changelog |
| Double orders | Two running copies; a resend after a timeout | Client order ID fixed before the first send; order lookup before any retry |
| Bot disagrees with the exchange after a restart | Open orders and balances on the exchange against local records | No new orders until the difference is explained; see [position reconciliation](https://fincodelabs.com/en/blog/position-reconciliation) |
| Stream looks alive, data is stale | Age of the last data message, not the socket state | Last trade from REST against the last stream event |

On a hosted product, start with its status page and support; the key and symbol checks still apply.

## One fault, three names: Binance, Bybit and Kraken errors

Venues report the same fault differently, so identify the fault first, then read that venue's page.

**One fault, three names; the last column is this guide's reading**

| Fault | Binance Spot | Bybit V5 | Kraken Spot | The software should |
| --- | --- | --- | --- | --- |
| Clock or nonce | `-1021` | `10002` | `EAPI:Invalid nonce` | Fix time sync or nonce order |
| Signature | `-1022` | `10004` | `EAPI:Invalid signature` | Fix signing |
| Key or environment | `-2014`, `-2015` | `10003` | `EAPI:Invalid key` | Match key and host; stop retrying |
| Permission or IP | `-2015` | `10005`, `10010` | `EGeneral:Permission denied`; no IP code listed | Read the key's settings |
| Tick, lot or minimum value | `-1013` with the filter's name | `170134`, `170136`, `170137`, `170140` | `EOrder:Tick size check failed`, `EOrder:Order minimum not met`, `EOrder:Cost minimum not met` | Round or refuse before sending |
| Balance | `-2010`, insufficient balance | `170131` | `EOrder:Insufficient funds` | Read balances |
| Rate limit | HTTP 429, `-1003`, `-1015` | `10006`, `10018`, `170005` | `EOrder:Rate limit exceeded` | Wait as instructed |
| Outcome unknown | HTTP 5XX, `-1006`, `-1007` | `170007` | `EService:Deadline elapsed` | Look up the order before any retry |
| Region or account block | This action is disabled on this account | `10009`, `10024` | `EAuth:Account temporary disabled` | Stop and ask the platform |

- **HTTP 200 can be a failure.** A signed request with no key on 5 November 2026 returned HTTP 200 with `EAPI:Invalid key` on Kraken and HTTP 401 with `-2014` on Binance's Spot Testnet. Bybit's testnet gave HTTP 200 with `retCode` 10001 on `/v5/order/realtime` but HTTP 401 with an empty body on `/v5/account/wallet-balance`, which breaks code that parses every reply as JSON.
- **One 403, two meanings.** Bybit's rate-limit page says a 403 reading access too frequent means wait at least 10 minutes; its integration guide says IP addresses in the US or Mainland China are restricted and also get a 403. The first is a wait; the second is a stop.
- **A dead key can lock live ones.** Kraken says repeating a deleted or expired key triggers `EGeneral:Temporary lockout` for every key from that IP, for about 20 minutes.
- **A library can hide the difference.** In CCXT 4.5.10, tested offline with synthetic error bodies, `-1007` and `170007` raised `RequestTimeout`, but `EService:Deadline elapsed` raised the generic `ExchangeError`, and master (4.5.92) still has no entry for it. Kraken says that order may have been placed, so match the message as well as the class.

## A read-only trading bot check you can run now

This script reads public testnet endpoints only, takes no key and cannot send an order.

```python
# Read-only, no key: python bot_doctor.py binance|bybit SYMBOL [qty price]
import json, sys, time, urllib.error, urllib.request
from decimal import Decimal as D

BN, BY = "https://testnet.binance.vision/api/v3", "https://api-testnet.bybit.com/v5"

def bn_rules(j):
    s = j["symbols"][0]
    f = {x["filterType"]: x for x in s["filters"]}
    return (s["status"], f["PRICE_FILTER"]["tickSize"], f["LOT_SIZE"]["stepSize"],
            f["LOT_SIZE"]["minQty"], f["NOTIONAL"]["minNotional"])

def by_rules(j):
    if not j["result"]["list"]:  # unknown symbol: retCode 0 and an empty list
        sys.exit("REJECTED: unknown symbol")
    s = j["result"]["list"][0]
    lot = s["lotSizeFilter"]
    return (s["status"], s["priceFilter"]["tickSize"], lot["basePrecision"],
            lot["basePrecision"], lot["minOrderAmt"])  # minOrderQty is deprecated for spot

VENUES = {
  "binance": (BN + "/time", lambda j: j["serverTime"],
              BN + "/exchangeInfo?symbol={}", bn_rules,
              BN + "/trades?limit=1&symbol={}", lambda j: j[0]["time"]),
  "bybit": (BY + "/market/time", lambda j: int(j["result"]["timeNano"]) // 10**6,
            BY + "/market/instruments-info?category=spot&symbol={}", by_rules,
            BY + "/market/recent-trade?category=spot&limit=1&symbol={}",
            lambda j: int(j["result"]["list"][0]["time"])),
}

def fetch(url):
    req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
    try:
        with urllib.request.urlopen(req, timeout=10) as r:
            status, head, body = r.status, r.headers, r.read()
    except urllib.error.HTTPError as e:
        status, head, body = e.code, e.headers, e.read()
    except OSError as e:
        sys.exit(f"UNREACHABLE: {e}")
    if status in (403, 451) or "json" not in head.get("Content-Type", ""):
        sys.exit(f"STOP HTTP {status}. If this is a regional or account block, "
                 "ask the platform.")
    data = json.loads(body or "{}")
    if status >= 400 or (isinstance(data, dict) and data.get("retCode")):
        sys.exit(f"REJECTED HTTP {status}: {body[:100]}")
    return data

def order_problems(tick, step, min_qty, min_value, qty, price):
    q, p = D(qty), D(price)
    checks = [(D(tick) and p % D(tick), f"price not a multiple of tick {tick}"),
              (D(step) and q % D(step), f"qty not a multiple of step {step}"),
              (q < D(min_qty), f"qty below minimum {min_qty}"),
              (q * p < D(min_value), f"value below minimum {min_value}")]
    return [why for failed, why in checks if failed]

def main(venue, symbol, qty=None, price=None):
    time_url, time_of, rules_url, rules_of, last_url, last_of = VENUES[venue]
    print(time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()), venue)
    t0 = time.time()
    server_ms = time_of(fetch(time_url))
    offset = server_ms - (t0 + time.time()) / 2 * 1000
    verdict = "ok" if -1000 < offset <= 5000 else "FAULT: fix time sync"
    print(f"clock: server minus local {offset:+.0f} ms, {verdict}")
    status, *limits = rules_of(fetch(rules_url.format(symbol)))
    print(f"{symbol}: {status}; tick, step, min qty, min value:", *limits)
    try:
        age = max(server_ms - last_of(fetch(last_url.format(symbol))), 0) / 1000
        print(f"last public trade about {age:.0f} s old")
    except IndexError:
        print("no recent public trade returned")
    if qty and price:
        try:
            bad = order_problems(*limits, qty, price)
        except ArithmeticError:
            sys.exit("REJECTED: qty and price must be plain numbers")
        print("order check, nothing sent:", "; ".join(bad) or "passes")

if __name__ == "__main__":
    main(*sys.argv[1:])
```

Output of `python bot_doctor.py binance BTCUSDT 0.000015 100.005` on 5 November 2026 (Python 3.11; the order values are deliberately bad):

```
2026-11-05T19:03:21Z binance
clock: server minus local +1124 ms, ok
BTCUSDT: TRADING; tick, step, min qty, min value: 0.01000000 0.00001000 0.00001000 5.00000000
last public trade about 4 s old
order check, nothing sent: price not a multiple of tick 0.01000000; qty not a multiple of step 0.00001000; value below minimum 5.00000000
```

Failure paths were tested too: a local HTML 403 and a 451 end with STOP, an HTTP 200 body error, an empty 401, an unknown symbol and bad numbers with REJECTED, a closed port with UNREACHABLE, and a clock 7 seconds behind or 2 seconds ahead prints FAULT. Key permissions, the IP allowlist and signing need a real key, so they were not run; [API key security for trading bots](https://fincodelabs.com/en/blog/api-key-security-trading-bots) shows a start-up permission check.

## Timestamp errors: fix the host clock first

A timestamp error means the host clock is off or the request waited too long, so fix time sync before touching any window setting.

Binance and Bybit use one rule, read 5 November 2026: a signed request passes only if its timestamp is under 1 second ahead of server time and no older than the window, 5,000 ms by default. A larger window does nothing for a clock running ahead.

- **Windows.** Freqtrade's Docker page says a container's clock on Windows drifts into the past, causing the recvWindow error, and does not recommend that setup for production. CCXT's manual warns that turning on time sync in Windows is usually not enough.
- **Nonces.** Kraken rejects a nonce at or below the last accepted one, so threads sharing a key need one counter behind a mutex.
- **Hosts.** Bybit's `10003` message names four environments a key must match; its guide lists separate regional hosts.

## Works, then stops: limits, stale streams and API changes

A bot that ran and then stopped has usually hit a limit, lost its data unnoticed or met a platform change.

> **Test, 5 November 2026.** Binance documents a server ping every 20 seconds, which many client libraries answer by themselves. A public Spot Testnet trade stream for a quiet symbol stayed open for 66 seconds, received 3 pings and delivered 0 trades, while REST showed the last trade about 2.6 hours old.

A quiet market and a dead feed look identical from the socket, so compare the last data message with the last trade from REST: if REST is newer, the stream is stale. Binance's limits are in the [rate limit guide](https://fincodelabs.com/en/blog/binance-api-rate-limit). For changes, compare the date the bot last worked with the official pages:

**Platform-side changes visible on 5 November 2026**

| Platform | Official page |
| --- | --- |
| Binance Spot | Changelog last updated 30 October 2026: SBE schema 3:3 retires on 9 November 2026 |
| Bybit | Docs banner: trading and matching engine moves to AWS Tokyo on 12 November 2026. WebSocket page: 100 args per connection for Spot and Futures, date TBD (26 November 2026 that morning) |
| Kraken | Public `SystemStatus`, up to 72 hours ahead: 07:00 to 14:00 UTC on 6 November 2026, for Spot REST, WebSocket and FIX |

## How to set up a trading bot so faults show early

Set up five things before the first live order.

1. **Restricted key.** One per bot, read and trade only, never withdrawal, bound to the server's IP where allowed.
2. **Time sync.** Scheduled on the host, then checked with the script from inside any container.
3. **Logs with no secrets.** UTC time, request, response code, client order ID and raw error, with keys and signatures removed.
4. **Alerts that reach a person.** Missing heartbeat, reject bursts or reconnect loops, on a channel someone reads. Limits belong in [trading bot risk management](https://fincodelabs.com/en/blog/trading-bot-risk-management).
5. **A small test order on testnet.** Place it, cancel it and restart with it open; the full plan is in [how to test trading bots](https://fincodelabs.com/en/blog/how-to-test-trading-bots).

How to use a trading bot day to day follows from this: read the alerts, keep the version you tested and treat a platform notice as a change to test. Tests show how the software behaves, not future results.

## What to collect before asking for help

Collect the exact error, the time and the versions with secrets removed.

- **Error:** code, message, HTTP status and raw body, copied rather than retyped.
- **Time and place:** UTC timestamp, the script's clock line, venue, environment and host.
- **Versions:** bot, library, Python and operating system.
- **Request:** symbol, quantity, price and client order ID, with key and signature removed.
- **Last change:** deployment, configuration, key rotation or platform notice.

Never send API keys. If you have the source code and the right to share it, a [trading bot code review](https://fincodelabs.com/en/services/trading-bot-code-review) starts from these items; Fincode sells that review, so this guide is not neutral, and naming Binance, Bybit or Kraken does not mean Fincode integrates them or has built on them. A regional or KYC block is different: stop and ask the platform.

## Frequently asked questions

### Why is my trading bot not placing orders?

Check that the process runs in live mode and logs a rule firing, then the clock, then the key's environment, IP allowlist and permissions. If orders are refused, compare the exact message with the symbol's rules.

### Why does my bot work on testnet but not live?

Test and live environments use separate hosts and keys, and limits can differ. Confirm the key matches the host, then read the rules from the live environment.

### My bot is losing money. Is that a fault?

Not one this guide can find. A bot that follows its rules correctly can still lose money, and this guide suggests no change to any rule or setting.

### Does the diagnostic script need my API key?

No, and that is its limit: it cannot check permissions, the IP allowlist or signing.

## Sources

- [Binance Spot API documentation: Error codes, including filter failures](https://github.com/binance/binance-spot-api-docs/blob/master/errors.md)
- [Binance Spot API documentation: Filters (tick, lot and notional rules)](https://github.com/binance/binance-spot-api-docs/blob/master/filters.md)
- [Binance Spot API documentation: REST API (timing security, recvWindow)](https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md)
- [Binance Spot API documentation: WebSocket streams (server ping every 20 seconds)](https://github.com/binance/binance-spot-api-docs/blob/master/web-socket-streams.md)
- [Binance Spot API: CHANGELOG (last updated 30 October 2026)](https://github.com/binance/binance-spot-api-docs/blob/master/CHANGELOG.md)
- [Bybit V5 API: Error codes](https://bybit-exchange.github.io/docs/v5/error)
- [Bybit V5 API: Integration guidance (timestamp rule, hosts)](https://bybit-exchange.github.io/docs/v5/guide)
- [Bybit V5 API: Rate limit (HTTP 403 cases)](https://bybit-exchange.github.io/docs/v5/rate-limit)
- [Bybit V5 API: WebSocket connect (args limits, date TBD when read on 5 November 2026)](https://bybit-exchange.github.io/docs/v5/ws/connect)
- [Kraken Developers: Error messages (causes, fixes, temporary lockout)](https://docs.kraken.com/exchange/guides/general/errors)
- [Kraken Developers: REST introduction (HTTP status codes and the error field)](https://docs.kraken.com/exchange/guides/rest/introduction)
- [Kraken Developers: Get System Status (upcoming maintenance within 72 hours)](https://docs.kraken.com/api/docs/rest-api/get-system-status)
- [CCXT Manual: Error handling, RequestTimeout and InvalidNonce](https://github.com/ccxt/ccxt/wiki/Manual)
- [Freqtrade documentation: Docker quickstart (Docker on Windows, recvWindow note)](https://www.freqtrade.io/en/stable/docker_quickstart/)
- [Capital Markets Law No. 6362, consolidated text, Article 99/A (Turkish)](https://www.mevzuat.gov.tr/mevzuatmetin/1.5.6362.pdf)

> 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](https://fincodelabs.com/en/editorial-policy).

---

Published by Fincode (Fincode Yazılım ve Teknoloji A.Ş.), a software company in Istanbul, Turkey: https://fincodelabs.com/en/blog/trading-bot-not-working

Cite as: Fincode, "Trading bot not working? Find the fault in this order", updated November 6, 2026, https://fincodelabs.com/en/blog/trading-bot-not-working
