Monitoring a trading integration
Figures on this page are as of 2026-09. Fees, limits and margin tiers change — check the venue's own docs before acting on a number.
The failures that cost money in a trading integration do not throw exceptions. A stream that silently stopped updating, a permission that expired three months after you set it up, a position that drifted from what your code believes — none of these produce an error.
So monitoring built around “alert on exceptions” misses the entire category that matters.
The four silent failures
1. State divergence. Your code’s view of positions differs from the exchange’s. Caused by a missed stream update, a partial fill you did not process, or a manual trade. Nothing reports it, and every subsequent decision is made on wrong numbers.
2. A stale stream. The socket is open, no data is arriving. A quiet market and a dead connection look identical from the application’s side — see WebSocket reconnection patterns.
3. Silently expired permission. On Binance, a key without an IP whitelist has
spot trading switched off 90 days after activation. Reads keep working. Only
orders fail, with -2015, three months after anyone touched the setup. See
the IP whitelist article.
4. An unprotected position. The entry filled, the stop did not — a partial sequence failure that leaves you exposed with nothing behind it. Everything looks normal until it does not.
What to check, and how often
Continuously
Data freshness. Time since the last message on each subscription. If it exceeds what is plausible for that stream, treat the connection as dead. This single check catches failure 2, which nothing else will.
Position reconciliation. Compare your view against a REST query on an interval and after every reconnect. Any mismatch is an alert, not a log line.
Every position has its stop. For each open position, verify a protective order exists at the expected level. This catches failure 4, and it is the check most worth having.
At startup
Assert key permissions. On Binance,
GET /sapi/v1/account/apiRestrictions returns ipRestrict, enableReading,
enableWithdrawals and the rest. Assert what you expect and refuse to start
if it differs — see
Binance API key permissions.
Log the environment. Which base URL, testnet or live. One line that answers “which environment is this process actually in” without inference.
Log the clock delta against the venue’s server time. Drift causes authentication failures that look like signing bugs — clock sync and timestamp errors.
Daily
Key age. Binance’s 90-day expiry is predictable, so alert at 75 days rather
than discovering it. Bybit UTA keys expire too, surfacing as 10003 on spot or
33004 on derivatives.
Rate limit headroom. Binance reports weight usage in response headers. If you are routinely near the ceiling, you have no capacity left for reconciliation when you need it most.
Realised versus planned R. Not infrastructure, but it degrades silently and it is where execution problems show up first.
What to alert on
The hard part of alerting is not what to include — it is what to leave out, since an alert stream you ignore is worse than none.
Alert:
- Position mismatch between your state and the exchange
- An open position with no protective order
- Stream stale beyond threshold
- Permission assertion failed
- Ambiguous outcome on an order call — rare, consequential, easy to miss
- Rate limit escalation: HTTP
418on Binance is an IP ban, not throttling - Repeated authentication failures
Do not alert:
- Individual transient errors. They are normal
429rate limits handled by backoff- Reconnects that reconciled cleanly
- Individual losing trades. That is not an infrastructure signal
The ambiguous-order alert deserves emphasis. It happens rarely, which means it will be a line in a log that nobody reads, and it is precisely the case where reconciliation before acting is mandatory.
Heartbeat, or the absence of it
A process that dies silently generates no alerts, because alerting is something the process does.
The fix is inverted: have the process emit a heartbeat and alert on its absence. Otherwise a crashed integration looks exactly like a quiet one, and the difference is a position with nobody watching it.
For AI-assisted setups
Two extra things worth capturing:
Tool call volume. A model can fan out into far more requests than a hand-written client, and rate limits arrive faster than expected — see rate limits in an MCP trading setup.
The tool list. MCP servers can change their exposed tools, and
notifications/tools/list_changed exists because this is normal. A server you
reviewed as read-only can gain an order tool in an update. Recording the tool
list at startup turns that into something you can diff.
The principle
Monitor for the absence of expected things, not just the presence of errors.
Expected: data arriving, positions matching, stops existing, the process alive, permissions intact. Each of those failing produces silence, and silence is what you have to instrument for.
FAQ
What should I monitor in a trading bot?
Data freshness on every stream, position reconciliation against the exchange, the existence of a protective order for every open position, and a heartbeat you alert on the absence of. These cover the silent failures; exception logging already covers the loud ones.
How do I detect that my position state is wrong?
Query the exchange on an interval and after every stream reconnect, and compare against your own view. There is no other reliable detector, because a divergence caused by a missed update produces no error and every subsequent update applies cleanly to the wrong base.
Why did my trading integration stop working after three months?
On Binance, most likely the 90-day expiry of spot trading permission on keys
without an IP whitelist. Reads continue working so the failure is
order-specific, typically -2015. Alerting on key age at 75 days turns this
into a scheduled task rather than an outage.
What should I not alert on?
Individual transient errors, handled rate limits, and clean reconnects. They are normal operation, and alerting on them produces a stream you stop reading — which then hides the alerts that matter.