Monitoring and stopping a bot
The live bot console, the customer emergency stop and why it disables keys before cancelling, the optional local agent and the eight commands it relays, and what an operator should watch on a venue full of market makers.
A bot running on someone else's machine is still fully observable from here, because every order it places is a row in your own tables. The console is a read-back of state the platform was storing anyway — it does not host the strategy, run the bot, or hold its logs. That is what makes it affordable to give to every customer.
The bot console
/hb/console. A full-viewport terminal; the site header and footer are
suppressed so the workspace fills the screen.
It shows, live:
- Resting quotes on both sides, with each one's distance from the touch and how long it has been resting.
- Fills as they land, deduplicated by order id and cumulative filled amount, so a replay cannot double-count one.
- Inventory skew and open perpetual positions.
- A per-market rail, when the account is quoting more than one pair.
- A health verdict for the bot as a whole, with the remedy attached.
Order book reads are coalesced and shared across everyone watching the same
market for a fraction of a tick, so two hundred people watching one pair cost
roughly what one costs. The page paints from a WebSocket; a one-shot
GET /api/hb/console covers the first render and any browser or proxy that
blocks upgrades.
When an account has no activity yet, the console does not replace itself with a signup wall — it shows the terminal it is about to become, with a setup panel in the health column stating which of the four steps is outstanding.
The emergency stop
The red Emergency stop control disables the account's Hummingbot keys and then cancels every resting order.
Keys are disabled first. That makes the bot's next signed request fail with 423 Locked, and only then are the resting orders pulled. Cancelling first accomplishes nothing visible — a market maker re-quotes within a second and the customer watches their own cancel undo itself.
Both halves are on by default and either can be run alone. The reply states plainly what happened, including the part people do not expect: the cancellation is account-wide and includes orders placed by hand, not only the bot's.
Re-enabling is a single action on the API Keys page and keeps the same secret, so nothing in the bot needs reconfiguring. A key disabled this way is stamped as the customer's own disable, which is what stops it laundering an operator block: if an administrator switched the key off, the customer cannot switch it back on and the console says so rather than offering a button that will fail.
The control is disabled when there is nothing to stop — no key trading and no order resting.
The local agent
Everything above is one-way: you see what the bot does because it does it here. The optional agent adds the other direction — starting, stopping and reconfiguring a bot from the exchange's own page.
your machine the exchange
hummingbot --headless
│ MQTT to 127.0.0.1:1883
▼
bicrypto_agent.py ── signed WebSocket, outbound ──► /api/hb/agent
(embedded broker) │
▼
/hb/consoleNothing connects to the customer. There is no inbound port to open, no SSH key, and the platform holds no credential that could reach their machine. The agent dials out and authenticates with a key the customer minted and can revoke, using the same signed handshake the market-data stream uses.
It needs its own scope
The agent's key must carry hb:control:bot, which is deliberately absent from
every trading preset. A key minted to let a bot trade cannot also reconfigure
it. The Bot control preset pairs it with hb:read:account and nothing else,
so an exposed agent key cannot place an order either.
Existing keys never gain the scope by upgrade — permissions are stored as a literal array at creation — so a customer opts in by minting or editing a key.
Eight commands, and nothing else
| Command | Effect |
|---|---|
status |
Current strategy status, as the bot's own status prints it |
start |
Start the strategy, optionally naming a script and config |
stop |
Stop it, optionally skipping order cancellation |
history |
Recent trades |
config |
Change strategy parameters on the running bot |
import |
Load a different strategy file |
balance limit |
Set a per-exchange asset limit |
balance paper |
Set a paper-trading balance |
The set is enumerated on the server. Nothing that names a file path, a shell
command or a Python expression is accepted — names are validated as bare
filenames against an allow-list of characters, with no separators, no .. and no
drive letters. That ceiling is what keeps a compromise of the platform from
becoming code execution on a customer's machine.
Validation happens here rather than at the bot because the bot builds each
request inside a try whose failure branch logs and returns without
publishing a reply. A misspelled field therefore produces silence, and the
operator watches a spinner for the full command timeout and learns nothing.
Setting it up
-
Mint a key with Bot control at
/hb/keys. Withouthb:control:botthe agent is refused and says so. -
Point Hummingbot at the agent in
conf/conf_client.yml:mqtt_bridge: mqtt_host: 127.0.0.1 mqtt_port: 1883 mqtt_commands: true mqtt_logger: true -
Run the agent with the same interpreter as the bot. It ships inside the connector kit and needs nothing installed — it carries its own MQTT broker.
conda activate hummingbot BICRYPTO_API_SECRET='...' python agent/bicrypto_agent.py \ --url https://exchange.example --key <API_KEY>The secret is passed through the environment, never as a
--secretargument: argv is world-readable inpson most hosts. -
Start the bot as usual. The agent discovers it automatically, including its instance id and MQTT namespace, which it reads from the bot's own subscriptions.
Once connected, the console gains a Control tab and a Bot log tab. With no agent the control tab explains how to get one and the log tab is simply empty.
On Windows, Hummingbot's MQTT bridge cannot attach to any broker — ours,
mosquitto's or EMQX's. It drives paho through aiomqtt, which registers its
socket with loop.add_reader(); Windows' default ProactorEventLoop does not
implement that and Hummingbot never sets a selector policy. The TCP connection
succeeds and the client then times out during CONNECT, forever.
Run the bot in a Linux guest, under WSL2, in Docker, or on Linux or macOS directly. Everything else works normally on Windows — orders, balances, market data, REST and WebSocket — and the console still shows every order, because those are rows on this side.
If the bot runs in a VM, run the agent inside the guest next to it, so
mqtt_host: 127.0.0.1 stays correct and the guest needs no inbound ports.
--url is then the exchange's address as seen from inside the guest, which is
rarely localhost. Rather than guessing, ask the exchange — /api/hb/ping needs
no authentication:
curl -s http://192.168.157.1:4000/api/hb/pingAn answer means that is the address for --url.
What an operator should watch
From /admin/hb and the Command Center:
- Authentication failures. A sustained rise is usually a drifted clock or a key rotated on one side only, not an attack. Rejections are throttled to one audit row per key per action per minute, so a hammering bot does not flood the table — a small number there can still mean a lot of refusals.
- Keys issued versus keys used. Long-idle keys are worth expiring.
- Order-to-fill ratio per account. A bot cancelling and replacing far more than it fills is spending rate-limit budget for nothing and adding load to your database.
- Concentration. If one bot is most of the volume on a pair, its outage is your liquidity outage.
Cancelling a running bot's orders does not stop it — it re-quotes on its next refresh. To actually stop one account, disable its key first, then cancel. To stop everything, use the global kill switch on the settings screen.