REST-only Hummingbot CEX connector for KalqiX, an order-book DEX.
Distributed as a standalone plugin — drops eight Python modules into
your Hummingbot install's connector/exchange/kalqix/ directory.
Auto-discovered by Hummingbot at boot via AllConnectorSettings; no
edits to Hummingbot's own source are required.
Beta. Used in production against testnet-api.kalqix.com and
api.kalqix.com.
This repository is the public drop-in distribution — clone it,
install once, and connect kalqix or connect kalqix_testnet from
your Hummingbot CLI. A parallel repository tracks the work toward
submitting this connector upstream to hummingbot/hummingbot; that
flow requires additional unit-test scaffolding and is not the path
most users will take. If you just want to run a bot today, this repo
is the right place.
| Feature | Status |
|---|---|
| LIMIT / MARKET orders | ✅ |
Post-only / LIMIT_MAKER |
✅ sent as time_in_force: 3; a crossing order is killed by the engine after placement and surfaces as an order failure (see limitations) |
| Place / cancel / status / fills | ✅ |
| Account balances | ✅ |
| Public order book + trade tape | ✅ via REST polling |
client_order_id (idempotency / crash recovery) |
✅ |
| WebSocket channels | ❌ Planned for future |
| Perpetuals | ❌ Planned for future |
Polling cadences (defaults; tweak in kalqix/kalqix_constants.py):
| Endpoint | Default cadence |
|---|---|
| Order book snapshot | 500 ms |
| Public trades | 1 s |
| Open orders | 1 s |
| Balances (framework status loop) | per Hummingbot config |
kalqix-node-api 1.9.0 or newer: every list the connector polls is
cursor-paginated (next_cursor). Against an older API the connector
logs an error and reads only the first page of each list.
A KalqiX account with:
- An API key (
api_key+api_secret) minted viaPUT /v1/api-keysin the KalqiX UI (production or testnet). - An export-pool agent wallet (slot 6..255) and its private key, generated via Settings → Agent Wallets → Generate for export.
Both credentials are shown once and not retrievable later — copy them into your secrets store on generation.
For Path B below (symlink into an existing install) you also need a working Hummingbot 1.x install on your host. Path A (Docker) bundles Hummingbot in the image — nothing else needed on your host beyond Docker Desktop.
BIP-340 Schnorr signing is now done inline in kalqix_auth.py (~50
lines of pure Python, no external curve lib). Earlier releases needed
pip install coincurve — that's no longer the case.
Two paths — pick one:
No host-side Python / Hummingbot install. The container bundles Hummingbot; this repo bind-mounts the connector on top.
git clone https://github.com/kalqix/kalqix-hummingbot-connector.git
cd kalqix-hummingbot-connector
docker compose up --build -d
docker attach kalqix-hummingbot
>>> connect kalqix_testnet # then enter your testnet credsFull step-by-step (incl. troubleshooting + a pure_market_making
smoke test): see DOCKER.md.
git clone https://github.com/kalqix/kalqix-hummingbot-connector.git
cd kalqix-hummingbot-connector
./install.sh # auto-detect via `python -c "import hummingbot"`
# OR
./install.sh /path/to/hummingbot # explicit install dirThe script symlinks kalqix/ into Hummingbot's
connector/exchange/kalqix/. Symlink (not copy) so an update via
git pull here is picked up by Hummingbot without re-running the
script.
To uninstall: rm /path/to/hummingbot/connector/exchange/kalqix.
The plugin registers two named connectors with Hummingbot — pick the one matching the network where you minted your credentials. Both prompt for the same four fields; credentials are stored separately so you can have both configured at once.
$ hummingbot
>>> connect kalqix_testnet # testnet (testnet-api.kalqix.com)
# OR
>>> connect kalqix # mainnet (api.kalqix.com)
Enter your KalqiX API key: <api_key>
Enter your KalqiX API secret: <api_secret>
Enter the agent-wallet slot index: <6..255>
Enter the agent-wallet private key: <64 hex chars, no 0x>
Hummingbot validates by calling GET /v1/positions. A 200 means HMAC
auth works. The agent-wallet credentials are exercised on the first
order placement.
The two names map to two BaseConnectorConfigMap subclasses backed by
the same KalqixExchange Python class with different domain values:
| Hummingbot command | domain passed in |
Base URL |
|---|---|---|
connect kalqix |
"com" |
https://api.kalqix.com/v1 |
connect kalqix_testnet |
"testnet" |
https://testnet-api.kalqix.com/v1 |
kalqix_testnet is wired in via Hummingbot's OTHER_DOMAINS
framework hook (see kalqix/kalqix_utils.py:120-124):
OTHER_DOMAINS = ["kalqix_testnet"]
OTHER_DOMAINS_PARAMETER = {"kalqix_testnet": "testnet"}
OTHER_DOMAINS_KEYS = {"kalqix_testnet": KalqixTestnetConfigMap.model_construct()}URL resolution is centralised in kalqix/kalqix_constants.py:rest_url(domain)
— that's the one place to look if you ever need to verify which
endpoint a request is hitting.
| File | Lines | Purpose |
|---|---|---|
kalqix_constants.py |
165 | REST URLs, rate limits, order-state map, polling cadences |
kalqix_utils.py |
109 | KalqixConfigMap, ticker conversions, exchange-info filter |
kalqix_auth.py |
160 | HMAC-SHA256 transport + BIP-340 Schnorr signing helper |
kalqix_web_utils.py |
69 | URL builder, throttler, /v1/time reader |
kalqix_order_book.py |
114 | Snapshot + trade message adapters |
kalqix_api_order_book_data_source.py |
220 | REST-poll order book + trade tape (overrides Hummingbot's WS hooks) |
kalqix_api_user_stream_data_source.py |
167 | REST-poll open orders + user trades, emits synthetic events |
kalqix_exchange.py |
561 | KalqixExchange(ExchangePyBase) — main connector class |
Every authenticated request carries:
- HMAC-SHA256 over
METHOD|PATH|QUERY|BODY|TIMESTAMPasx-api-key/x-api-signature/x-api-timestampheaders. Handled transparently byKalqixAuth.rest_authenticate. - For state-changing requests (place / cancel), an additional
BIP-340 Schnorr signature over the action's canonical payload,
signed by the export-pool agent wallet's private key, inlined into
the request body or query as
signature+agent_index.
client_order_id is not part of the signed payload — it's a bot
tag, not a security boundary. The server validates its format and
uniqueness independently.
Hummingbot's OrderBookTrackerDataSource and
UserStreamTrackerDataSource are normally WS-driven. The connector
overrides listen_for_subscriptions and
listen_for_order_book_diffs to no-op infinite sleeps, and rewrites
listen_for_order_book_snapshots / listen_for_trades /
listen_for_user_stream as REST poll loops on fixed cadences. The
framework's task scheduling still drives execution; the WS layer is
just bypassed.
These are flagged for early testers — none are blockers for the common case but are worth knowing:
- Decimal scaling race. Order placement looks up market decimals
from
_market_decimals, populated during the first trading-rules load. Placing an order before the framework's first_update_trading_rulestick raisesValueError. Hummingbot's normal startup sequence loads rules first; this would only show as a startup-race bug. - Cancel before place ack. The cancel path needs
tracked_order.exchange_order_id, which arrives via the open-orders poll. A cancel-immediately-after-place can returnFalseand get retried on the next status loop. Not a correctness bug — just a latency spike. - Post-only rejection is asynchronous, and it changes maker
strategies' default. KalqiX accepts a
LIMIT_MAKERorder at the API and the matching engine kills it if any part would trade on arrival (status: EXPIRED,close_reason: POST_ONLY_CROSS, zero fills). Because the connector advertisesLIMIT_MAKER, Hummingbot's stock maker strategies (pure market making, Avellaneda, XEMM's maker leg, liquidity mining) place post-only orders by default; a quote that crosses is rejected instead of filling as a taker. Pure market making can opt out withtake_if_crossed. The rejection never appears in/orders?open=true, so the user-stream poller looks for tracked orders that have dropped out of the open list and fetches their terminal state: a crossing order surfaces as an order failure about one to two poll intervals (1 s each) after placement, and the framework's own status loop (LONG_POLL_INTERVAL, 120 s) remains as the fallback. - Polling cost. With today's ≤10 trading pairs on KalqiX, a
steady-state market-making bot runs around 2–3k req/min — well
under the 18k req/min wallet rate limit. The polling cadences in
kalqix_constants.pyonly need tuning if KalqiX's pair count grows materially or you run multiple bots on one wallet (the limit is per-wallet, not per-bot).
Apache 2.0 — see LICENSE. Matches the upstream
Hummingbot project.
To report a security issue (e.g. anything that touches signature
verification, agent-wallet key handling, or HMAC transport), please
do not open a public issue. See SECURITY.md for
the responsible-disclosure channel.
Issues and PRs welcome. The roadmap (in rough order):
- WebSocket support once
kalqix-node-apiships the channels. - Perpetuals once KalqiX adds a perp venue.
Upstream PR work (Hummingbot-style unit tests with aioresponses /
NetworkMockingAssistant, then the hummingbot/hummingbot:development
submission) is tracked in a parallel repository; it's not on this
repo's roadmap because the two distributions are intentionally
decoupled — changes here ship to users immediately on git pull,
while upstream changes go through a multi-week review cycle.