Building a Trading Bot
This guide shows how to build a simple trading bot using the hlz CLI and shell scripting. For complex bots, use the Zig SDK directly.
Prerequisites
# Install hlz
curl -fsSL https://raw.githubusercontent.com/dzmbs/hlz/main/install.sh | sh
# Set up your key
hlz keys new bot
export HL_KEY_NAME=bot
export HL_PASSWORD=your_password
export HL_OUTPUT=jsonSimple Grid Bot
Places buy and sell orders at fixed intervals around the current price:
#!/bin/bash
set -euo pipefail
COIN="BTC"
SIZE="0.01"
LEVELS=5
SPREAD="50" # dollars between levels
# Get current mid price
MID=$(hlz price $COIN -q)
echo "Mid price: $MID"
# Place grid orders
for i in $(seq 1 $LEVELS); do
OFFSET=$(echo "$i * $SPREAD" | bc)
BUY_PX=$(echo "$MID - $OFFSET" | bc)
SELL_PX=$(echo "$MID + $OFFSET" | bc)
hlz buy $COIN $SIZE @$BUY_PX --json
hlz sell $COIN $SIZE @$SELL_PX --json
done
echo "Grid placed: $LEVELS levels, $SPREAD spread"Monitor and Rebalance
#!/bin/bash
# Check positions every 30 seconds, rebalance if needed
while true; do
POS=$(hlz positions --json | jq -r '.[] | select(.coin == "BTC") | .szi')
if [ -z "$POS" ]; then
POS="0"
fi
# If position exceeds threshold, reduce
if (( $(echo "$POS > 0.5" | bc -l) )); then
echo "Position too large ($POS), reducing..."
hlz sell BTC 0.1 --reduce-only --json
elif (( $(echo "$POS < -0.5" | bc -l) )); then
echo "Short too large ($POS), reducing..."
hlz buy BTC 0.1 --reduce-only --json
fi
sleep 30
doneUsing the Zig SDK
For lower latency and more control, use the SDK directly:
const hlz = @import("hlz");
const Client = hlz.hypercore.client.Client;
const Signer = hlz.crypto.signer.Signer;
const Decimal = hlz.math.decimal.Decimal;
const types = hlz.hypercore.types;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const signer = try Signer.fromHex("your_key");
var client = Client.mainnet(allocator);
defer client.deinit();
// Fetch current mid price
var mids = try client.getAllMids(null);
defer mids.deinit();
// Build and sign order (34.5µs, zero allocs)
const order = types.OrderRequest{
.asset = 0,
.is_buy = true,
.limit_px = try Decimal.fromString("50000"),
.sz = try Decimal.fromString("0.01"),
.reduce_only = false,
.order_type = .{ .limit = .{ .tif = .Gtc } },
.cloid = types.ZERO_CLOID,
};
const batch = types.BatchOrder{
.orders = &[_]types.OrderRequest{order},
.grouping = .na,
};
const nonce = @as(u64, @intCast(std.time.milliTimestamp()));
var result = try client.place(signer, batch, nonce, null, null);
defer result.deinit();
}Best Practices
- Always use
--dry-runfirst — Test your bot logic without real orders - Set position limits — Use
--reduce-onlyto prevent runaway positions - Handle all exit codes —
3= auth error,4= network error - Use keystores — Don't put private keys in scripts
- Rate limit yourself — Hyperliquid allows 1200 req/min per IP