Architecture¶
This page explains how kraken-connector is structured and the reasoning behind key design choices. It's aimed at contributors and anyone who wants to understand the internals.
Module layout¶
kraken_connector/
├── __init__.py # Public API: clients, exceptions, resilience
├── http.py # HTTPClient, HTTPAuthenticatedClient
├── resilience.py # ResilienceConfig, RetryTransport, RateLimiter
├── security.py # HMAC-SHA512 signing, nonce generation
├── exceptions.py # KrakenAPIError, UnexpectedStatus, InvalidResponseModel
├── types.py # Response[T], File, Unset sentinel
├── constants/ # Enums for API parameters (order types, intervals, etc.)
├── api/ # REST endpoint modules by domain
│ ├── market_data/
│ ├── account_data/
│ ├── trading/
│ ├── funding/
│ ├── earn/
│ ├── subaccounts/
│ └── websockets_authentication/
├── schemas/ # attrs-based request/response models (~100+)
└── ws/ # WebSocket v2 client
├── client.py # KrakenWSClient — connection, subscriptions, trading
├── subscribe.py # Typed subscription parameter models
├── trading.py # Trading parameter and result models
├── envelopes.py # WSRequest, WSResponse, WSDataMessage, WSErrorResponse
├── dispatcher.py # Raw JSON → typed message routing
├── channels/ # Typed channel data models (ticker, book, trade, etc.)
├── book.py # OrderBook, OrderBookManager, CRC32 validation
├── sequence.py # SequenceTracker for private channel gap detection
├── subscriptions.py # Subscription state tracking
├── token.py # TokenManager for WS authentication
└── constants.py # ConnectionState, ChannelName, enums
Design decisions¶
attrs over pydantic¶
All data models use attrs (@define / @_attrs_define). attrs was chosen for:
- Lightweight -- no runtime validation overhead; models are thin wrappers
- Compatibility -- the initial schema generation used
openapi-python-client, which targets attrs - Explicit serialization --
to_dict()/from_dict()class methods give full control over field mapping
httpx over requests¶
httpx provides both sync and async clients from a single API. This lets every REST endpoint expose sync() and asyncio() variants without maintaining two HTTP stacks.
The ResilienceConfig integrates at the httpx transport layer (RetryTransport / AsyncRetryTransport), keeping retry logic transparent to endpoint modules.
websockets for WS transport¶
The websockets library was chosen for its clean async API and close adherence to the WebSocket protocol specification. The KrakenWSClient wraps a single websockets.connect() connection.
Code generation for schemas¶
The kraken_connector/schemas/ directory was initially generated from Kraken's OpenAPI specification (openapi.json) using openapi-python-client. This produces one attrs class per schema with consistent to_dict() / from_dict() methods.
The api/ endpoint modules were also generated from the same spec, producing the four-function pattern (sync, sync_detailed, asyncio, asyncio_detailed) for each endpoint.
Unset sentinel¶
Optional fields use a custom Unset sentinel (not None) to distinguish "field not provided" from "field explicitly set to null." This matches the OpenAPI spec's distinction between omitted and null-valued fields.
from kraken_connector.types import UNSET, Unset
if not isinstance(field_value, Unset):
# field was explicitly provided
Data flow¶
REST request¶
Caller
→ endpoint module (get_server_time.sync)
→ HTTPClient.get_or_create_httpx_client()
→ RetryTransport (if configured)
→ httpx.Client.request()
→ Kraken REST API
← httpx.Response
→ _parse_response() → Schema.from_dict()
← Response[T] or parsed model
WebSocket message¶
Kraken WS v2
→ websockets connection
→ _recv_loop()
→ parse_message() (dispatcher)
→ typed message (WSDataMessage, WSResponse, etc.)
→ internal routing:
- status → _handle_status()
- pong → _pong_event.set()
- heartbeat → dropped
- book data → OrderBookManager.process_message()
- sequence check → SequenceTracker.check()
- req_id correlation → resolve pending Future
→ message_queue.put()
← async for msg in client (consumer)
Background tasks¶
KrakenWSClient runs three background asyncio.Tasks:
| Task | Purpose |
|---|---|
_recv_loop |
Receive and dispatch messages from the WebSocket |
_ping_loop |
Send periodic pings and await pong responses |
_heartbeat_monitor |
Detect server silence and trigger reconnection |
All three are cancelled on disconnect and restarted on reconnect.