API-Edge-Cases bei Brokern: was Sie aus der Doku nie erfahren.
Jede Broker-API hat eine offizielle Doku — und eine zweite, ungeschriebene, die man erst durch Schaden lernt. Rate-Limits, die niedriger sind als dokumentiert. Reject-Codes, die nirgends erklärt werden. Partial-Fills, die als Done markiert sind. Hier die wichtigsten Edge-Cases aus zehn Jahren Live-Trading-Bots.
Wenn ich neue Trader berate, ist die häufigste Frage: „Welcher Broker hat die beste API?" Die ehrliche Antwort: keiner. Sie haben alle ihre Eigenheiten, und jede einzelne wird Ihren Bot irgendwann beißen. Was hilft, ist defensive Programmierung als Default — und ein Katalog typischer Stolpersteine.
Rate-Limits: meistens niedriger als dokumentiert.
Broker veröffentlichen Rate-Limits typischerweise als „X Requests pro Minute" oder als „Weight-Score". Die Realität:
- Binance Spot: dokumentiert 1.200 Weight pro Minute. In der Praxis sehe ich 429-Responses schon bei 900–1000, wenn mehrere Endpoints parallel laufen. Bursts triggern aggressiveres Throttling.
- IBKR TWS API: 50 Messages pro Sekunde dokumentiert. Funktioniert — aber nur, wenn die Sequenz gleichmäßig ist. Burst von 50 in 100 ms führt zu Disconnect.
- Alpaca: 200 Requests pro Minute. Stimmt fürs Trading-Endpoint. Market-Data-Endpoint hat separates Limit, das in der Hauptdoku nur am Rand erwähnt wird.
Schutz: Token-Bucket-Rate-Limiter clientseitig, eingestellt auf 70–80 % des dokumentierten Limits. Plus: 429-Response als Backoff-Trigger behandeln, nicht als reines Logging.
Order-Reject-Reasons: kryptisch und uneinheitlich.
Wenn eine Order abgelehnt wird, sollte die API klar sagen warum. Die Realität:
- IBKR: Reject-Codes wie
201(„Order rejected — reason: The order is held while securities are located") oder2104(eigentlich eine Info, kein Fehler — aber sieht aus wie einer). Über 200 verschiedene Codes, viele undokumentiert. - Binance:
-2010 NEW_ORDER_REJECTEDohne weitere Details. Der genaue Grund (Insufficient Balance? Filter? PERCENT_PRICE? LOT_SIZE?) steht in der Fehlernachricht — als Freitext, nicht maschinenlesbar. - Alpaca: HTTP-403 mit JSON-Body, in dem
codeundmessagestehen. Tendenziell die saubersten Errors, aber auch hier kommen gelegentlich neue Codes ohne Doku-Update.
Mein Vorgehen: zentrale Reject-Handler-Funktion mit einem manuell gepflegten Mapping von Code/Message zu Handlungsklasse — „Retry erlaubt", „Position-Check nötig", „Strategie pausieren", „Sofort-Alert". Jeder neue Reject-Code löst einen Telegram-Alert aus, dann erweitere ich das Mapping.
Partial-Fills, die als Done markiert sind.
Klassischer Bug, vor allem bei IBKR und MT5: eine Limit-Order wird zu 80 %
gefüllt, der Rest verfällt, die Order wechselt in Status Filled oder
Cancelled. Wer naiv aus dem Order-Status auf die Position schließt, hat
ein Problem — geplant: 100 Stück long, real: 80.
Schutz: nach jedem Order-Done-Event nicht den Order-Status vertrauen, sondern die tatsächliche Position abfragen. Bei Diskrepanz: Reconciliation aktivieren. Das gilt auch für FX und Crypto, wo Partial-Fills bei Marktorder seltener sind, aber bei Limit-Orders mit IOC oder GTD-Flag durchaus vorkommen.
Disconnects ohne Notification.
Eine Verbindung kann abreißen, ohne dass die API es Ihnen mitteilt. Sie senden weiter Orders, die im Nichts verschwinden, oder Sie warten auf Quotes, die nie kommen. Bei IBKR TWS passiert das z. B. nach dem täglichen Auto-Restart (typisch 23:45–00:15 Uhr ET) — die Socket-Connection bleibt offen, aber alle Subscriptions sind weg.
Schutz: Heartbeat-Mechanismus auf Anwendungsebene. Pro Verbindung ein „last event received"-Timestamp. Wenn länger als X Sekunden nichts kommt (je nach Marktphase 5–60 s), aktiv pingen — wenn das fehlschlägt, hartes Reconnect mit Resubscribe und Position-Reconciliation.
Verzögerte Account-Updates nach Trades.
Nach einem Trade ist die neue Position oft nicht sofort in /account
oder /positions sichtbar. Typische Verzögerungen:
- IBKR: 1–3 Sekunden für Aktien, gelegentlich 10–15 s für Optionen
- Binance: Spot meist sub-second, Futures-Account-Balance kann 2–5 s nachhängen
- Alpaca: 1–2 Sekunden typisch, in Volatilität bis 10 s
Konsequenz: wenn Ihr Bot direkt nach einem Trade die Account-Position abfragt, um die nächste Order zu sizen, rechnet er mit veralteten Daten. Schutz: lokales Position-Tracking führen, das Sie aus Order-Fill-Events selbst aktualisieren. Die Broker-Position-API nur als Reconciliation alle X Minuten heranziehen.
Maintenance-Windows ohne Vorwarnung.
Selbst große Broker führen Wartungen ohne ausreichende Ankündigung durch. Binance
ist hier berüchtigt: Spot-Markt geht für 30 Minuten in „No Trading"-Mode, API
antwortet mit -1003 oder einfach mit leeren Responses. Bei IBKR ist
der tägliche Server-Restart-Slot dokumentiert (Mitternacht ET), aber ungeplante
Wartungen kommen ohne Email.
Schutz: jeder Bot muss eine Strategie für „Markt vorübergehend nicht erreichbar" haben. Mein Default: nach 3 aufeinanderfolgenden Fail-Responses Strategie pausieren (keine neuen Orders), bestehende Stop-Loss-Orders aber halten, alle 60 s Probe-Request senden. Erst nach 3 erfolgreichen Probes wieder aktiv.
Token-Expiry mitten in der Session.
OAuth-basierte APIs (Alpaca OAuth, einige Banken-APIs) und Session-basierte
Logins (Binance User-Stream listenKey, IBKR-Login) haben begrenzte
Lebensdauer:
- Binance listenKey: 60 Minuten, verlängerbar per
PUT. Wer nicht alle 30 Min verlängert, verliert lautlos den User-Stream. - IBKR-Session: über die Web-API (Client Portal) max. 24 Stunden, oft mit re-auth nach 6 Stunden bei sensiblen Actions.
- Alpaca OAuth-Token: typisch 1 Stunde, Refresh-Token-Flow erforderlich.
Schutz: zentraler Token-Manager mit pro-aktivem Refresh, lange bevor das Token abläuft. Faustregel: refresh bei 50 % der Token-Lifetime. Nie warten, bis ein Call wegen abgelaufenem Token fehlschlägt.
Time-Zone-Bugs.
Zeitzonen sind eine eigene Klasse von Schmerz:
- Broker-Timezone vs. Markt-Timezone: MT5-Broker liefern Bar-Timestamps in Server-Zeit (typisch GMT+2 oder GMT+3, je nach Broker), nicht in Markt-Zeit.
- Daylight-Saving-Wechsel: zwischen den DST-Wechseln in US und EU (zwei Wochen Versatz im März und Oktober/November) liegen NYSE und Xetra anders zueinander als sonst. Strategien mit fester Stunden-Logik („nur zwischen 15:30 und 22:00 traden") brechen leise.
- Bar-Close-Timestamps: manche Broker setzen den Timestamp auf Bar-Open, andere auf Bar-Close. Wer das vertauscht, lookahead-biased die Strategie.
Schutz: alles in UTC intern speichern. Konvertierung nur an UI-Grenzen. Plus: explizite Tests rund um DST-Wechsel.
Numerische Precision.
FX-Quotes haben 5 oder 8 Nachkommastellen. Crypto-Quotes manchmal 12. JSON-Parser
in Python und JavaScript machen daraus standardmäßig float —
verlustbehaftet. Wer Stop-Levels auf 0.000000001 setzt und das durch JSON jagt,
findet Stops bei 0.00000000099999... wieder, mit netten Reject-Errors.
Schutz: Decimal in Python verwenden, niemals float für
Preise und Quantities. JSON-Parser mit parse_float=Decimal
konfigurieren. Auf Datenbankebene NUMERIC(20,10) statt
DOUBLE.
Meine Praxis: defensive Programmierung als Default.
Alles, was ich oben aufgezählt habe, fließt in einen Wrapper, den ich pro Broker einmal baue und dann wiederverwende. Die Komponenten:
- Rate-Limiter mit Token-Bucket auf 75 % des dokumentierten Limits
- Retry-Layer mit exponential backoff für Network-Fehler und 5xx-Responses
- Reject-Handler mit gepflegtem Code-Mapping und Telegram-Alert für unbekannte Codes
- Position-Reconciliator, alle 60 s lokale Position gegen Broker-Position abgleichen
- Token-Manager mit proaktivem Refresh bei 50 % Lifetime
- Heartbeat-Watchdog pro Verbindung, mit hartem Reconnect bei Stille
- Strikte UTC-Politik und
Decimalfür alle Preis-/Quantity-Felder - Strukturiertes Logging aller API-Calls in eine Timeseries-DB
Klingt nach viel Overhead — ist es auch. Aber es ist der Unterschied zwischen einem Bot, der drei Wochen live läuft und dann „komische Sachen macht", und einem, der zwei Jahre 24/7 läuft, ohne dass ich nachts geweckt werde.
Sie schreiben einen eigenen Bot gegen IBKR, Binance, Alpaca oder einen anderen Broker und wollen typische API-Fallen vermeiden? Erstgespräch buchen — ich schaue Ihr API-Layer durch und zeige die kritischen Punkte.