Runden klingt nach einem gelösten Problem. Sie haben eine Zahl, Sie haben eine Schrittweite, Sie runden auf den nächsten gültigen Wert. Eine Hilfsfunktion, zwanzig Zeilen, für immer erledigt.
Es ist nicht eine Funktion. In einem System, das Orders an mehrere Börsen sendet, ist Präzision eine ganze Domäne, und die Bugs, die sie produziert, teilen eine fiese Eigenschaft: sie werfen keine Ausnahme. Sie produzieren eine Order, die leicht falsch ist, oder eine Order, die aus einem Grund abgelehnt wird, den der Nutzer nicht sehen kann.
So sieht die Arbeit tatsächlich aus, anhand von drei realen Problemen aus unserem Order-Pfad.
Das Wichtigste
- IEEE-754 macht die Schrittausrichtung unzuverlässig: Skaliert man 0.29 mit 100, ergibt sich 28.999999999999996, was auf einen Wert abgerundet wird, der eine volle Schrittweite zu klein ist.
- Drei Börsen erzwingen drei inkompatible Mindestorder-Regeln. Eine prüft den USDT-Wert, eine die Kontraktanzahl, eine beides.
- Unser schlimmster Bug in diesem Bereich entstand daraus, dass wir eine Regel erfunden haben, die eine Börse gar nicht hat, und dann gültige Orders dagegen abgelehnt haben.
- Erfinden Sie nie ein Limit, das die API nicht geliefert hat. Wenn eine Handelsstelle kein Notional-Minimum hat, ist eines aus anderen Feldern zu berechnen keine Vorsicht, sondern Erfindung.
- Fehlende Börsen-Metadaten sollten einen Fehler auslösen, nicht auf einen Standardwert zurückfallen. Ein falscher Standardwert produziert stillschweigend falsche Orders.
1. Das arithmetische Problem: Runden verschiebt Ihre Zahl
Beginnen wir mit dem einfachsten Fall, denn es ist der, den man für sicher hält.
Eine Börse akzeptiert Mengen in Vielfachen einer Schrittweite. Sie haben 0.29 und eine Schrittweite von 0.01. Dieser Wert ist bereits gültig, das Abrunden sollte also nichts verändern. Die Standardimplementierung skaliert hoch, rundet ab und skaliert wieder herunter:
0.29 / 0.01 = 28.999999999999996 ← IEEE-754, not 29
floor(...) = 28
28 × 0.01 = 0.28
Ein Wert, der bereits ausgerichtet war, hat sich eine volle Schrittweite nach unten bewegt, stillschweigend, in Richtung einer kleineren Order. Keine Ausnahme, keine Warnung, und es passiert nur bei bestimmten Kombinationen aus Wert und Schrittweite, was es genau so schwer bemerkbar macht.
Die Lösung ist ein Epsilon, das beim Skalieren angewendet wird, groß genug, um das Darstellungsrauschen zu absorbieren, aber klein genug, dass ein tatsächlich darunterliegender Wert wie 0.289999 weiterhin korrekt abrundet. Über die Entdeckung habe ich in Was ich beim Aufbau dieser Plattform unterschätzt habe geschrieben; hier zählt die Kategorie, zu der es gehört.
Denn sobald Sie akzeptieren, dass Arithmetik auf Mengen unzuverlässig ist, beginnen Sie, alles andere zu betrachten, das eine Menge berührt. Genau dort liegen die echten Überraschungen.
2. Drei Börsen, drei inkompatible Minima
Jede Handelsstelle erzwingt eine Mindestorder. Man würde erwarten, dass sich die Regel in ihrem Wert unterscheidet, nicht in ihrer Form. Sie unterscheidet sich in der Form:
| Börse | Regel |
|---|---|
| Binance | qty × price >= minNotional (eine Prüfung des USDT-Werts) |
| Bybit | qty >= minOrderQty und notional >= minNotional (zwei Prüfungen) |
| OKX-Perpetuals | qty >= minSz (Kontraktanzahl, überhaupt keine Notional-Prüfung) |
Lesen Sie diese Tabelle als drei verschiedene Fragen, die zur selben Order gestellt werden. Binance fragt, was sie wert ist. OKX fragt, wie viele Kontrakte es sind. Bybit fragt beides, und eine Order kann jede der beiden Bedingungen für sich erfüllen und trotzdem abgelehnt werden.
Bei OKX gibt es eine weitere Feinheit: Mengen werden in Kontrakten angegeben, nicht in Coins, sodass eine Kontraktgröße zwischen beiden umrechnen muss, bevor irgendetwas davon Sinn ergibt. Eine Menge, die auf einer Handelsstelle „0.5 BTC" bedeutet, bedeutet auf einer anderen „50 Kontrakte".
Die architektonische Konsequenz, auf die wir uns festgelegt haben: jede Börse validiert ihre eigenen Orders. Die Strategie-Engine enthält keine einzige Bedingung darüber, mit welcher Handelsstelle sie spricht. Sie übergibt die Order an einen Adapter, und der Adapter wendet seine eigenen Regeln an. Jedes Mal, wenn wir versuchten, diese Regeln hinter einer Abstraktion zu vereinheitlichen, wurde die Abstraktion in dem Moment zur Lüge, in dem ein vierter Fall auftauchte.
3. Der teure Fall: eine Regel erfinden, die es nicht gibt
Das ist der Bug, den ich einem anderen Entwickler am meisten ersparen möchte, denn der Fehler sieht aus wie Sorgfalt.
OKX-Perpetuals haben kein Notional-Minimum. Das wussten wir nicht. Im Analogieschluss von den anderen Handelsstellen haben wir eines berechnet: Mindestkontraktanzahl mal Kontraktgröße mal Preis. Es sah vernünftig aus. Es ergab eine plausible USDT-Zahl. Und es war Fiktion.
Wo es kaputtging, ist spezifisch und den Durchgang wert. Eine DCA-Strategie platziert ihre Sicherheitsorders unterhalb des Einstiegspreises. Unser synthetisches Minimum wurde zu einem Preis berechnet und dann mit niedriger bepreisten Orders verglichen, sodass dieselbe Menge, die am oberen Ende der Leiter durchging, weiter unten scheiterte.
Das Ergebnis: Orders von genau 0.1 Kontrakten, was das Börsenminimum ist und akzeptiert worden wäre, wurden von unserem eigenen Validator abgelehnt, bevor sie unser System überhaupt verließen. Die Börse sah sie nie. Aus Sicht des Nutzers tat die Strategie einfach nicht, wofür sie konfiguriert war.
Die Lehre lässt sich weit über den Handel hinaus verallgemeinern:
Erzeugen Sie keine Einschränkung, die die API nicht geliefert hat. Wenn eine Handelsstelle ein Minimum in Kontrakten offenlegt, ist das Minimum in Kontrakten. Ein wertbasiertes Äquivalent daraus abzuleiten ist nicht Sorgfalt, es ist das Erfinden einer Regel und ihr anschließendes Erzwingen gegenüber dem Nutzer.
Das Verräterische war im Nachhinein, dass unser berechneter Wert vom Preis abhing, während die tatsächliche Regel der Börse das nicht tat. Sobald Ihre Version einer Einschränkung einen Eingabewert hat, den die echte nicht hat, haben Sie etwas anderes gebaut, als Sie modellieren wollten.
4. Fehlende Metadaten sollten ein Fehler sein, kein Standardwert
Das letzte Stück ist eher eine Richtlinie als ein Bug, und es ist das, was die ersten drei davon abhält, still wiederzukehren.
Jede Börse liefert uns Metadaten pro Symbol: Schrittweite, Tick-Größe, Mindestmenge, Kontraktgröße. Jedes davon kann fehlen, und die verlockende Reaktion ist ein Fallback. Schrittweite fehlt? Nimm 0.001 an. Mindestmenge fehlt? Nimm null an und lass die Börse entscheiden.
Wir machen das Gegenteil: ein erforderliches Feld, das fehlt, wirft einen Fehler, sofort, bevor irgendeine Order konstruiert wird. Nichts weiter unten in der Kette bekommt die Chance, auf einer Annahme aufzubauen.
Der Grund ist die Asymmetrie der Ergebnisse. Ein geworfener Fehler ist ein lautes Scheitern, das sofort auftaucht, vor jemandem, der es beheben kann. Ein Standardwert ist eine stille falsche Antwort, die eine Order in der falschen Größe ausliefert und sich später offenbart, in Geld. Zwischen einem Fehler, den Sie nicht ignorieren können, und einer Zahl, die Sie nicht verifizieren können, ist der Fehler jedes Mal günstiger.
Das bedeutet auch, dass die Metadaten-Schicht ehrlich darüber ist, was sie weiß. Code, der ein erforderliches Feld liest, bekommt einen echten Wert oder eine Ausnahme. Er bekommt nie einen Platzhalter, der die Form echter Daten trägt.
Was Sie mitnehmen sollten
- Behandeln Sie Schritt- und Tick-Ausrichtung als Arithmetik, die Ihren Wert verschieben kann, nicht als Formatierung.
- Vereinheitlichen Sie keine Regeln, die wirklich verschieden sind. Validierung pro Handelsstelle bleibt korrekt, wenn Handelsstellen hinzukommen; eine gemeinsame Abstraktion über inkompatible Regeln bleibt es nicht.
- Leiten Sie nie ein Limit ab, das die API nicht angegeben hat. Wenn Ihre Version einer Einschränkung einen Eingabewert nimmt, den die echte nicht hat, ist es eine andere Einschränkung.
- Scheitern Sie laut bei fehlenden Metadaten. Standardwerte verwandeln einen sichtbaren Fehler in eine unsichtbare, falsch bepreiste Order.
Präzisionsarbeit ist unglamourös und taucht nie auf einer Feature-Liste auf. Aber jeder dieser Bugs produzierte eine Order, die falsch war, oder eine Order, die nie existierte, und in einem System, das im Auftrag von jemandem handelt, gehören diese in dieselbe Fehlerkategorie wie der schlichte Verlust seines Geldes.
