Hoe krijg je toegang tot 'Order Book' data (Level 2) via Python?
Je wilt order book data van niveau 2 (Level 2) uitlezen via Python, zonder dat je database ontploft of je bot vastloopt op trage queries. Je bent een Nederlandse trader die met algoritmische bots werkt, misschien met een backtesting pipeline in Python en een broker die API-toegang biedt.
Je wilt simpelweg weten: hoe pak je L2 data aan, waar berg je het efficiënt op, en hoe integreer je een websocket zoals die van Refinitiv of Bitstamp zonder gekke fouten te maken. In dit stuk lees je een praktische, stap-voor-stap aanpak die je meteen kunt toepassen.
Ingestie en opslag met QuestDB
Begin met een stabiele basis voor je data. QuestDB 9.0.0+ is een goede keuze voor L2 data, omdat het float64 arrays ondersteunt. Je kunt een order book met prijsniveaus en volumes netjes in één regel per timestamp opslaan, zonder dat je database explodeert door duizenden losse regels.
Wat je nodig hebt: Stappenplan:
- QuestDB 9.0.0+ geïnstalleerd (lokaal of in de cloud).
- QuestDB Python client 3.0.0+ (vanwege array-ondersteuning).
- Een Python 3.10+ omgeving met numpy (voor np.float64).
- Een publieke crypto feed (bijvoorbeeld Bitstamp L2) of een betaalde Refinitiv feed.
Veelgemaakte fouten: Tip: gebruik 1-based indexing in QuestDB SQL.
- Installeer QuestDB en start de server. Gebruik de standaard web UI op localhost:9000.
- Maak een table voor L2 data. Voorbeeld SQL:
CREATE TABLE l2_book (symbol SYMBOL, ts TIMESTAMP, bids ARRAY(FLOAT), asks ARRAY(FLOAT)) TIMESTAMP(ts) PARTITION BY DAY; - Installeer de Python client:
pip install questdb-client==3.0.0. - Schrijf een producer die L2 data ophaalt en arrays bouwt met np.float64. Zorg dat bids en asks even lang zijn (bijvoorbeeld 20 levels).
- Gebruik de buffered writer van de client en flush elke 5 seconden om data-verlies te minimaliseren.
- Test inserties en controleer in QuestDB of je per timestamp één regel ziet, niet twintig.
Bijvoorbeeld: SELECT bids[1] AS best_bid, asks[1] AS best_ask FROM l2_book WHERE symbol='BTC/USD' LIMIT 10; Bij L2 data denk je in niveaus: prijs en volume per level. In Python bouw je twee arrays: een voor bids (aflopende prijzen) en een voor asks (oplopende prijzen).
- Relatie tabellen maken met meerdere rows per timestamp: dat leidt tot data-explosie en trage queries.
- Arrays niet typen als float64: QuestDB verwacht duidelijke types voor goede performance.
- Flush-intervals te groot: verlies bij crashes of disconnects.
Sla beide op als numpy arrays met dtype=np.float64. Dat is compact en snel voor analytische workloads.
Data structuur en arrays
Voorbeeld structuur per message: Praktische maatvoering: Veelgemaakte fouten:
- symbol: string, bijvoorbeeld 'BTC/USD'.
- timestamp: nanoseconden (QuestDB verwacht TIMESTAMP).
- bids: array van 20 floats, bijvoorbeeld [60000.0, 59999.5, …].
- asks: array van 20 floats, bijvoorbeeld [60001.0, 60002.0, …].
- 20 levels per kant is gebruikelijk voor L2; je kunt ook 10 of 50 kiezen, afhankelijk van je bot.
- Gebruik aparte arrays voor prijs en volume, of interleave ze in één array. Kies één formaat en houd het vol.
- Cache de arrays in Python met numpy voor snelle toegang tijdens backtests.
- Nested dicts in JSON opslaan zonder tuning: dat is prima voor logging, maar traag voor queries.
- Level 1 logica toepassen op L2: niet alleen de top bid/ask opslaan, maar het hele boek.
Refinitiv Websocket API integratie
Refinitiv L2 data is krachtig, maar je moet de juiste domeinen kiezen.
Level 1 is MarketPrice; Level 2 is MarketByPrice (geaggregeerd order book) of MarketByOrder (individuele orders). Zorg dat je abonnement actief is en je credentials bij de hand hebt.
- Refinitiv EMA/EMA API toegang (betaald abonnement).
- Een App Key en App Secret voor authentication.
- Python 3.10+ en de Refinitiv EMA Python library.
- Een stabiele internetverbinding (websocket blijft open).
Wat je nodig hebt: Stappenplan: Veelgemaakte fouten: Een Refinitiv-request ziet er zo uit: je geeft een instrument (bijvoorbeeld ‘EUR=’), een service (RDF), en het Domain.
- Download de Refinitiv EMA Python distributie en installeer afhankelijkheden.
- Zet je credentials in environment variables of een .env bestand.
- Open een websocket verbinding naar het Refinitiv data platform.
- Stel een request op met het juiste Domain: zet het ‘Domain’ attribute naar ‘MarketByPrice’ voor geaggregeerde L2, of ‘MarketByOrder’ voor individuele orders.
- Parse de response. Refinitiv L2 gebruikt nested structuren (associative arrays/maps), niet flat payloads.
- Converteer de nested data naar arrays van floats en stuur naar QuestDB.
- Flush elke 5 seconden en sluit de websocket netjes bij errors.
De response bevat een nested map met keys zoals ‘bids’ en ‘asks’, waarbinnen per level een map met prijs en volume zit.
- Vergeten het ‘Domain’ attribute te setten: je krijgt dan Level 1 data of errors.
- Flat payloads verwachten: Refinitiv stuurt nested dicts, dus je parser moet dat aankunnen.
- Geen retry logic: websocket disconnects gebeuren; bouw een herstelmechanisme in.
Request en response verwerking
Stappen voor verwerking: Praktische maatvoering: Veelgemaakte fouten:
- Ontvang een message en check het Domain veld. Als het ‘MarketByPrice’ is, ga verder.
- Loop door de nested bids en asks. Voor elk level: lees prijs en volume als floats.
- Bouw numpy arrays van 20 levels (of zoveel als je afspreekt). Gebruik dtype=np.float64.
- Voeg een timestamp toe (nanoseconden) en een symbol.
- Stuur de arrays naar QuestDB via de Python client.
- Log foutieve levels en skip incomplete messages.
- Gebruik een buffer van 100–500 messages voor performance, maar flush minimaal elke 5 seconden.
- Houd arrays consistent: altijd even veel levels, anders krijg je query-fouten.
- Test met een demo-account voordat je met echt geld handelt.
- Refinitiv L2 data rechtstreeks als JSON dump opslaan: dat werkt, maar is traag voor backtests.
- Geen tijdzone handling: QuestDB verwacht UTC timestamps.
Bitstamp publieke L2 feed
Bitstamp biedt publieke, ongeauthenticeerde L2 data via websocket. Wil je meer marktdata verzamelen? Gebruik dan web scraping voor traders om Finviz en Yahoo Finance uit te lezen.
Je krijgt een live order book met prijsniveaus die je direct kunt verwerken. Wat je nodig hebt:
- Een Python omgeving met websocket-client en numpy.
- Geen API key nodig voor publieke feed.
Stappenplan: Veelgemaakte fouten:
- Open een websocket naar wss://ws.bitstamp.net.
- Stuur een subscribe-bericht voor 'order_book' of 'detail_order_book' (L2) voor een pair zoals 'btcusd'.
- Ontvang updates en parse de bids en asks arrays.
- Bouw numpy arrays met np.float64, bijvoorbeeld 20 levels.
- Insert in QuestDB met een timestamp en symbol.
- Flush elke 5 seconden.
- Te veel niveaus opslaan zonder limiet: je database groeit snel.
- Geen array-consistentie: sommige updates hebben meer levels dan andere.
Performance, risicomanagement en verificatie
Performance begint bij de juiste datastructuur. Gebruik arrays in QuestDB of nested dicts in Refinitiv, maar tune ze voor analytische workloads.
Zorg dat je bot geen onnodige queries doet; haal L2 data eens per seconde op voor live trading, vaker voor backtests. Vergeet niet om eerst de 10 stappen voor het opschonen van je trading data te doorlopen voor een betrouwbare basis.
- Test eerst met publieke feeds (Bitstamp) voordat je betaalde data gebruikt.
- Gebruik een demo-account voor Refinitiv; zet order placement uit.
- Monitor database size: QuestDB partitioneert per dag, maar hou schijfruimte in de gaten.
- Log errors en disconnects; bouw retries met backoff.
Risicomanagement: Verificatie-checklist: Met deze aanpak heb je een robuuste L2 data pipeline die werkt voor algoritmische trading bots, backtesting en risicomanagement. Je kunt nu verder bouwen: voeg indicators toe, optimaliseer je broker API calls, en check de integriteit van je database voor snelle queries. Veel succes!
- QuestDB table aangemaakt met arrays van float64? Ja/Nee
- Python client 3.0.0+ geïnstalleerd? Ja/Nee
- Flush elke 5 seconden geconfigureerd? Ja/Nee
- Refinitiv Domain ingesteld op MarketByPrice of MarketByOrder? Ja/Nee
- Bitstamp websocket verbonden en data zichtbaar? Ja/Nee
- Queries met 1-based indexing getest? Ja/Nee
- Backtest pipeline loopt zonder data-gaten? Ja/Nee
