Vissza a bloghoz

ProCat Solutions

Kripto kereskedő és automatizáló botok: amit a Web3 után építettünk

Tőzsdei REST és WebSocket API-k, megbízás-életciklus, rate limitek, kockázatkezelés és monitorozás: mérnöki tapasztalatok kripto botok fejlesztéséből.

ProCat Solutions kriptoweb3websocketnodejsautomatizalaskockazatkezeles
Kripto kereskedő és automatizáló botok: amit a Web3 után építettünk

Ez a cikk nem befektetési tanácsadás. Mi szoftvert építünk: kereskedési és automatizáló botok infrastruktúráját, nem pedig kereskedési stratégiát az ügyfelek pénzére. A stratégia mindig a megbízóé, a mi feladatunk az, hogy a rendszer pontosan, kiszámíthatóan és biztonságosan azt csinálja, amit a stratégia előír. Az alábbiakban azt foglaljuk össze, milyen mérnöki kérdések kerültek elő, amikor a smart contract és token presale projektek után a tőzsdei automatizálás felé fordultunk.

Tőzsdei API-k: REST és WebSocket együtt

Szinte minden centralizált tőzsde két csatornát ad. A REST API-n keresztül adunk le és vonunk vissza megbízást, kérdezzük le az egyenleget és az előzményeket. A WebSocket csatornán érkeznek az árfolyamok, az order book frissítései és a saját megbízásaink állapotváltozásai. A két csatorna nincs szinkronban: előfordul, hogy a WebSocketen már látjuk a teljesülést, a REST még “nyitott” állapotot mutat, vagy fordítva.

A gyakorlati megoldásunk: a WebSocket az elsődleges forrás az állapothoz, de periodikus REST-egyeztetés (reconciliation) fut mögötte, és eltérés esetén a tőzsde REST-válaszát tekintjük igazságnak. A WebSocket kapcsolatot heartbeat figyeli, és újracsatlakozáskor teljes snapshotot kérünk, nem csak a folytatást, mert a kimaradt üzenetek pótlása tőzsdénként másképp működik, és ritkán megbízható.

Megbízás-életciklus, idempotencia és rate limitek

Egy megbízás állapotgépe egyszerűnek látszik (létrehozva, nyitott, részben teljesült, teljesült, visszavont, elutasított), a nehézséget az átmenetek jelentik. Mi történik, ha a REST hívás timeoutol? Nem tudjuk, hogy a megbízás bekerült-e. Ezért minden megbízáshoz saját kliensoldali azonosítót rendelünk, amit a tőzsdék többsége elfogad, így egy ismétlés nem hoz létre duplikátumot, hanem visszaadja a már létező megbízást.

Az összes állapotváltozást append-only eseménynaplóba írjuk PostgreSQL-ben, és az aktuális állapot ebből származtatott. Ha bármi vitás, az eseménysorból rekonstruálható, mi történt és mikor.

A másik korlát, amibe minden bot beleszalad, a rate limit. A tőzsdék súlyozott rate limiteket alkalmaznak: egy order book lekérés többe kerül, mint egy ping. A limit túllépése rövid tiltást hoz, ami épp akkor fáj, amikor gyorsan kellene visszavonni egy megbízást. Ezért:

  • kliensoldali token bucket számolja a fogyasztást, a tőzsde saját fejléceiből visszaolvasva;
  • a kritikus műveletek (visszavonás, kill switch) számára tartalék keretet hagyunk, amit a lekérdezések nem használhatnak el;
  • hibánál exponenciális backoff jitterrel, és a 429-es válasz után a tőzsde által kért várakozást tartjuk be, nem a sajátunkat.

Kockázatkezelés a szoftver szintjén

A kockázatkezelés nálunk nem a stratégia része, hanem attól független, alatta futó réteg, amit a stratégia nem tud megkerülni:

  • pozíciólimitek eszközönként és összesítve, a limit fölött a rendszer nem ad le nyitó megbízást;
  • maximális napi veszteség, amelynek elérésekor a bot csak záró megbízásokat enged;
  • kill switch: egyetlen parancs, ami minden nyitott megbízást visszavon, és a botot passzív módba teszi, amit külön csatornán (nem a bot saját webes felületén) is el lehet érni;
  • paper trading mód, ahol minden ugyanúgy fut, csak a megbízások egy szimulált végrehajtóhoz mennek.

A paper trading mód nem opcionális kényelmi funkció, hanem a tesztelés alapja: minden kódváltozás először ott fut napokig, valós adatfolyamon.

Monitorozás és titkok

Egy bot, ami csendben leáll, rosszabb, mint egy, ami hangosan hibázik. Ezért a bot állapotát heartbeat metrikák jelzik (utolsó árfolyam-frissítés kora, nyitott megbízások száma, kapcsolat állapota), és riasztás megy, ha bármelyik a várt tartományon kívül esik, vagy ha az adatfolyam elakad, még ha nem is jött hibaüzenet. A riasztás több csatornán megy ki, mert a bot maga nem tudhatja, melyik csatorna él éppen.

Az API-kulcsok soha nem kerülnek a repóba és a Docker image-be. Titkos tárolóból, indításkor, környezeti változóként érkeznek, a kulcsok jogosultsága minimális (kereskedés igen, kivétel nem), és IP-korlátozottak. A visszavonhatóság fontosabb, mint a titkosítás: egy kompromittált kulcsot percek alatt cserélni kell tudni.

Determinizmus és a backtesting csapdái

A backtesting csábító, mert szép görbéket mutat, de az eredmény csak annyira jó, mint a feltételezések. A tipikus hibák, amiket a szoftver oldaláról kezelni tudunk:

  • look-ahead: a stratégia olyan adatot használ, ami az adott pillanatban még nem volt elérhető, ezért a backtesting motor csak időbélyeg szerint “már megérkezett” adatot szolgáltat;
  • csúszás és díjak: a szimulált végrehajtó nem a középárfolyamon teljesít, hanem az order book alapján, díjakkal;
  • adathiány: a hiányzó gyertyákat nem interpoláljuk, hanem jelöljük, és a stratégia számára is látható;
  • nem determinisztikus futás: ugyanaz a bemenet mindig ugyanazt az eredményt adja, mert a véletlenszerűséget seedeljük és az időt injektáljuk, nem a rendszerórából olvassuk.

Ez a réteg azt garantálja, hogy a backtest reprodukálható és őszinte. Azt nem garantálja, hogy a stratégia jó. A kettőt fontos szétválasztani, és a fejlesztés során mi az elsőért vállalunk felelősséget.

Széchenyi Terv Plusz kedvezményezetti infoblokk
QR Code