Open-broker
Hyperliquid trading plugin with background position monitoring and custom automations. Execute market orders, limit orders, manage positions, view funding ra...
Hyperliquid trading plugin with background position monitoring and custom automations. Execute market orders, limit orders, manage positions, view funding ra...
Real data. Real impact.
Emerging
Developers
Per week
Open source
Skills give you superpowers. Install in 30 seconds.
Execute trading operations on Hyperliquid DEX with builder fee support.
npm install -g openbroker
# 1. Setup (generates wallet, creates config, approves builder fee) openbroker setup2. Fund your wallet with USDC on Arbitrum, then deposit at https://app.hyperliquid.xyz/
3. Start trading
openbroker account openbroker buy --coin ETH --size 0.1
Always search before trading an unfamiliar asset. Hyperliquid has main perps (ETH, BTC, SOL...), HIP-3 perps (xyz:CL, xyz:GOLD, km:USOIL...), and spot markets. Use search to discover the correct ticker:
openbroker search --query GOLD # Find all GOLD markets across all providers openbroker search --query oil # Find oil-related assets (CL, BRENTOIL, USOIL...) openbroker search --query BTC --type perp # BTC perps only openbroker search --query NATGAS --type hip3 # HIP-3 only
Or with the
ob_search plugin tool: { "query": "gold" } or { "query": "oil", "type": "hip3" }
HIP-3 assets use
format — e.g., dex:COIN
xyz:CL not just CL. If you get an error like "No market data found", search for the asset to find the correct prefixed ticker. Common HIP-3 dexes: xyz, flx, km, hyna, vntl, cash.
Every info JSON output includes an
assetId field — the canonical Hyperliquid asset index. Prefer it over the coin name when persisting references, because the same ticker can exist on multiple providers (e.g. HYPE perp, hyna:HYPE HIP-3, and HYPE/USDC spot all coexist).
| Scope | Formula | Example |
|---|---|---|
| Main perps | universe index | → |
| HIP-3 perps | | → |
| Spot | | → |
openbroker search HYPE --json | jq '.[] | {coin, assetId, type, provider}'
Trading commands still take
--coin <name> (including HIP-3 dex:COIN) — assetId is for queries, comparisons, and agent state, not order placement.
If an
ob_* plugin tool returns unexpected errors, empty results, or crashes, fall back to the equivalent CLI command via Bash. The CLI and plugin tools share the same core code, but the CLI has more mature error handling and output.
Every info command supports
for structured output. The table below covers the commands with dedicated plugin tools; any other info command (e.g. --json
spot, trades, fees, order-status, rate-limit, funding-history, all-markets) can be run as openbroker <command> --json for the same effect.
| Plugin Tool | CLI Equivalent |
|---|---|
| |
| |
| |
| |
| |
| (or ) |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| or |
| |
| (or SIGINT if run in foreground) |
| |
When to use CLI fallback:
null, empty data, or throws an error--verbose debug output)Add
--dry to any trading CLI command to preview without executing. Add --json to info commands for structured output.
openbroker setup # One-command setup (wallet + config + builder approval) openbroker approve-builder --check # Check builder fee status (for troubleshooting)
The
setup command offers three modes:
For options 1 and 2, setup saves config and approves the builder fee automatically. For option 3 (API wallet), see the API Wallet Setup section below.
The simplest setup for agents. A fresh wallet is generated, the builder fee is auto-approved, and the agent is ready to trade immediately after funding.
Flow:
openbroker setup and choose option 1 ("Generate a fresh wallet")API wallets can place trades on behalf of a master account but cannot withdraw funds. Use this if you prefer to keep funds in your existing wallet and only delegate trading access.
Flow:
openbroker setup and choose option 3 ("Generate API wallet")https://openbroker.dev/approve?agent=0xABC...)ApproveAgent (authorizes the API wallet) and ApproveBuilderFee (approves the 1 bps fee)After setup, the config will contain:
HYPERLIQUID_PRIVATE_KEY=0x... # API wallet private key HYPERLIQUID_ACCOUNT_ADDRESS=0x... # Master account address HYPERLIQUID_NETWORK=mainnet
Important for agents: When using an API wallet, pass the approval URL to the agent owner (the human who controls the master wallet). The owner must approve in a browser before the agent can trade. The CLI waits up to 10 minutes for the approval. If it times out, re-run
openbroker setup.
openbroker account # Balance, equity, margin openbroker account --orders # Include open orders openbroker account --address 0xabc... # Look up another account openbroker positions # Open positions with PnL openbroker positions --coin ETH # Specific coin openbroker positions --address 0xabc... # Another account's positions
openbroker funding --top 20 # Top 20 by funding rate openbroker funding --coin ETH # Specific coin openbroker funding --top 20 --json # JSON (includes assetId)
openbroker markets --top 30 # Top 30 main perps openbroker markets --coin BTC # Specific coin openbroker markets --coin BTC --json # JSON (includes assetId)
openbroker all-markets # Show all markets openbroker all-markets --type perp # Main perps only openbroker all-markets --type hip3 # HIP-3 perps only openbroker all-markets --type spot # Spot markets only openbroker all-markets --top 20 # Top 20 by volume openbroker all-markets --json # JSON (includes assetId)
openbroker search --query GOLD # Find all GOLD markets openbroker search --query BTC # Find BTC across all providers openbroker search --query ETH --type perp # ETH perps only openbroker search HYPE --json # JSON with assetId per result
openbroker spot # Show all spot markets openbroker spot --coin PURR # Show PURR market info openbroker spot --balances # Show your spot balances openbroker spot --balances --address 0xabc... # Another account's spot balances openbroker spot --top 20 # Top 20 by volume openbroker spot --json # JSON (includes assetId, base, quote)
openbroker fills # Recent fills openbroker fills --coin ETH # ETH fills only openbroker fills --coin BTC --side buy --top 50 openbroker fills --address 0xabc... # Another account's fills
openbroker orders # Recent orders (all statuses) openbroker orders --open # Currently open orders only openbroker orders --open --coin ETH # Open orders for a specific coin openbroker orders --coin ETH --status filled openbroker orders --top 50 openbroker orders --address 0xabc... --open # Another account's open orders
openbroker order-status --oid 123456789 # Check specific order openbroker order-status --oid 0x1234... # By client order ID openbroker order-status --oid 123456789 --address 0xabc... # On another account openbroker order-status --oid 123456789 --json
openbroker fees # Fee tier, rates, and volume openbroker fees --address 0xabc... # Another account's fees openbroker fees --json
openbroker candles --coin ETH # 24 hourly candles openbroker candles --coin BTC --interval 4h --bars 48 # 48 four-hour bars openbroker candles --coin SOL --interval 1d --bars 30 # 30 daily bars openbroker candles --coin ETH --json # JSON (coin, assetId, interval, candles)
openbroker funding-history --coin ETH # Last 24h openbroker funding-history --coin BTC --hours 168 # Last 7 days openbroker funding-history --coin ETH --json # JSON (coin, assetId, history)
openbroker trades --coin ETH # Last 30 trades openbroker trades --coin BTC --top 50 # Last 50 trades openbroker trades --coin ETH --json # JSON (coin, assetId, trades)
openbroker rate-limit # API usage and capacity openbroker rate-limit --json
openbroker funding-scan # Scan all dexes, >25% threshold openbroker funding-scan --threshold 50 --pairs # Show opposing funding pairs openbroker funding-scan --hip3-only --top 20 # HIP-3 only openbroker funding-scan --watch --interval 120 # Re-scan every 2 minutes openbroker funding-scan --json # JSON (includes assetId per result)
All trading commands support HIP-3 assets using
dex:COIN syntax:
openbroker buy --coin xyz:CL --size 1 # Buy crude oil on xyz dex openbroker sell --coin xyz:BRENTOIL --size 1 # Sell brent oil openbroker limit --coin xyz:GOLD --side buy --size 0.1 --price 2500
openbroker buy --coin ETH --size 0.1 openbroker sell --coin BTC --size 0.01 openbroker buy --coin SOL --size 5 --slippage 100 # Custom slippage (bps)
openbroker market --coin ETH --side buy --size 0.1 openbroker market --coin BTC --side sell --size 0.01 --slippage 100
openbroker limit --coin ETH --side buy --size 1 --price 3000 openbroker limit --coin SOL --side sell --size 10 --price 200 --tif ALO
# Set take profit at $40, stop loss at $30 openbroker tpsl --coin HYPE --tp 40 --sl 30Set TP at +10% from entry, SL at entry (breakeven)
openbroker tpsl --coin HYPE --tp +10% --sl entry
Set only stop loss at -5% from entry
openbroker tpsl --coin ETH --sl -5%
Partial position TP/SL
openbroker tpsl --coin ETH --tp 4000 --sl 3500 --size 0.5
# Take profit: sell when price rises to $40 openbroker trigger --coin HYPE --side sell --size 0.5 --trigger 40 --type tpStop loss: sell when price drops to $30
openbroker trigger --coin HYPE --side sell --size 0.5 --trigger 30 --type sl
openbroker cancel --all # Cancel all orders openbroker cancel --coin ETH # Cancel ETH orders only openbroker cancel --oid 123456 # Cancel specific order
Spot trading uses a separate order path with its own asset indices (see Asset IDs section). Pass the base token symbol as
--coin — quote is always USDC.
# Market orders (shortcuts) openbroker spot-buy --coin PURR --size 1000 openbroker spot-sell --coin HYPE --size 5Full form (specify --side)
openbroker spot-order --coin PURR --side buy --size 1000
Limit orders (add --price)
openbroker spot-order --coin HYPE --side sell --size 50 --price 25.50 openbroker spot-order --coin PURR --side buy --size 500 --price 0.20 --tif Alo
Preview without executing
openbroker spot-buy --coin PURR --size 500 --dry
Spot flags:
--coin, --side, --size, --price (omit → market order), --tif (Gtc/Ioc/Alo, default Gtc), --slippage (bps, market orders only), --dry, --verbose.
# Buy 1 ETH over 30 minutes (exchange handles slicing) openbroker twap --coin ETH --side buy --size 1 --duration 30Sell 0.5 BTC over 2 hours without randomized timing
openbroker twap --coin BTC --side sell --size 0.5 --duration 120 --randomize false
Reduce-only (close position with TWAP). Note: TWAP uses
, not--reduce-only--reduceopenbroker twap --coin ETH --side sell --size 1 --duration 30 --reduce-only
Cancel a running TWAP
openbroker twap-cancel --coin ETH --twap-id 77738308
Check TWAP status
openbroker twap-status --active
# Place 5 buy orders ranging 2% below current price openbroker scale --coin ETH --side buy --size 1 --levels 5 --range 2Scale out with exponential distribution
openbroker scale --coin BTC --side sell --size 0.5 --levels 4 --range 3 --distribution exponential --reduce
# Long ETH with 3% take profit and 1.5% stop loss openbroker bracket --coin ETH --side buy --size 0.5 --tp 3 --sl 1.5Short with limit entry
openbroker bracket --coin BTC --side sell --size 0.1 --entry limit --price 100000 --tp 5 --sl 2
# Chase buy with ALO orders until filled openbroker chase --coin ETH --side buy --size 0.5 --timeout 300Aggressive chase with tight offset
openbroker chase --coin SOL --side buy --size 10 --offset 2 --timeout 60
Limit Orders (
openbroker limit):
Trigger Orders (
openbroker trigger, openbroker tpsl):
| Scenario | Command |
|---|---|
| Buy at specific price below market | |
| Sell at specific price above market | |
| Stop loss (exit if price drops) | |
| Take profit (exit at target) | |
| Add TP/SL to existing position | |
All commands support
--dry for dry run (preview without executing).
| Argument | Description |
|---|---|
| Asset symbol (ETH, BTC, SOL, HYPE, etc.) |
| Order direction: or |
| Order size in base asset |
| Limit price |
| Preview without executing |
| Show command help |
| Argument | Description |
|---|---|
| Trigger price (for trigger orders) |
| Trigger type: or |
| Slippage tolerance in bps (for market orders) |
| Time in force: GTC, IOC, ALO |
| Reduce-only order |
| Format | Example | Description |
|---|---|---|
| Absolute | | Price of $40 |
| Percentage up | | 10% above entry |
| Percentage down | | 5% below entry |
| Entry price | | Breakeven stop |
Config is loaded from (in priority order):
.env in current directory~/.openbroker/.env (global config)Run
openbroker setup to create the global config interactively.
| Variable | Required | Description |
|---|---|---|
| Yes | Wallet private key (0x...) |
| No | (default) or |
| No | Master account address (required for API wallets) |
| No | Dashboard API URL for forwarding audit notes, metrics, and agent actions (e.g. ) |
The builder fee (1 bps / 0.01%) is hardcoded and not configurable.
This skill works standalone via Bash — every command above runs through the
openbroker CLI. For enhanced features, the same openbroker npm package also ships as an OpenClaw plugin that you can enable alongside this skill.
ob_account, ob_buy, ob_limit, etc.) — typed tool calls with proper input schemas instead of Bash strings. The agent gets structured JSON responses.ob_auto_run, ob_auto_stop, ob_auto_list) — start, stop, and manage custom trading automations from within the agent.openclaw ob status and openclaw ob watch for inspecting the watcher.The plugin is bundled in the same
openbroker npm package. To enable it in your OpenClaw config:
plugins: entries: openbroker: enabled: true config: hooksToken: "your-hooks-secret" # Required for watcher alerts watcher: enabled: true pollIntervalMs: 30000 pnlChangeThresholdPct: 5 marginUsageWarningPct: 80
The plugin reads wallet credentials from
~/.openbroker/.env (set up by openbroker setup), so you don't need to duplicate privateKey in the plugin config unless you want to override.
For the position watcher and automations to send alerts to the agent, you must enable webhooks in your OpenClaw gateway config and add a hook mapping. This is a manual configuration step — plugins cannot auto-configure gateway settings.
1. Generate a hook token — any secure random string:
openssl rand -hex 32
2. Enable hooks and add a mapping in your
openclaw.json (or openclaw.yaml) deployment config:
"hooks": { "enabled": true, "path": "/hooks", "token": "<your-generated-token>", "allowedAgentIds": ["hooks", "main", "openbroker"], "mappings": [ { "id": "main", "match": { "path": "openbroker" }, "action": "agent", "wakeMode": "now", "name": "Openbroker", "agentId": "main", "deliver": true, "channel": "last", "model": "anthropic/claude-sonnet-4-6" } ] }
| Field | Description |
|---|---|
| Shared secret — must match in the plugin config |
| Agent IDs allowed to receive webhook requests |
| Matches the webhook path sent by the plugin (always ) |
| triggers an immediate agent turn. queues for the next scheduled heartbeat |
| If , the agent's response is delivered to the user via the configured channel |
| Delivery channel: (most recent), , , , , etc. |
| Model override for webhook-triggered turns. Optional — uses deployment default if omitted |
3. Set the same token in your plugin config:
plugins: entries: openbroker: enabled: true config: hooksToken: "<your-generated-token>" # Same token as hooks.token watcher: enabled: true
4. Restart the gateway and verify:
openclaw ob status
The watcher sends alerts to
POST /hooks/agent with Authorization: Bearer <token>. The gateway matches the request against the mapping and triggers an agent turn. Without hooks enabled, the watcher still tracks state (accessible via ob_watcher_status), but it can't wake the agent.
openbroker buy --coin ETH --size 0.1). No background monitoring.ob_* tools when available (structured data), falls back to Bash for commands not covered by tools (strategies, scale). Background watcher sends alerts automatically.Automations let you write custom event-driven trading logic as TypeScript scripts. Write exactly the logic you need and OpenBroker handles the polling, event detection, and SDK access.
OpenBroker ships bundled automation examples (
dca, grid, funding-arb, mm-spread, mm-maker) that demonstrate common patterns. These examples are meant to be read and learned from — not run directly as production strategies. When the user asks for a strategy (e.g. "run a DCA on HYPE"), you should:
~/.openbroker/automations/ and run it with openbroker auto runDo NOT simply run
openbroker auto run --example dca --set coin=HYPE. The examples use generic defaults and lack the nuanced risk management, position sizing, and TP/SL logic that a real strategy needs. Always write a purpose-built automation.
To view bundled examples and their config schemas:
openbroker auto examples # List examples with config fields
Available examples:
dca, grid, funding-arb, mm-spread, mm-maker, price-alert
An automation is a
.ts file that exports a default function. The function receives an AutomationAPI with the full Hyperliquid client, typed event subscriptions, persistent state, and a logger. The runtime connects a WebSocket for real-time price and order events, with REST polling every 30s as a heartbeat for position/margin data. Use --no-ws to disable WebSocket and fall back to pure REST polling (every 10s).
Create a
.ts file in ~/.openbroker/automations/ (or any path):
// ~/.openbroker/automations/funding-scalp.ts export default function(api) { const COIN = 'ETH';api.on('funding_update', async ({ coin, annualized }) => { if (coin !== COIN) return;
if (annualized > 0.5 && !api.state.get('isShort')) { api.log.info('High positive funding — going short'); await api.client.marketOrder(COIN, false, 0.1); api.state.set('isShort', true); } else if (annualized < -0.1 && api.state.get('isShort')) { api.log.info('Funding normalized — closing short'); await api.client.marketOrder(COIN, true, 0.1); api.state.set('isShort', false); }});
api.onStop(async () => { if (api.state.get('isShort')) { api.log.warn('Closing short on shutdown'); await api.client.marketOrder('ETH', true, 0.1); api.state.set('isShort', false); } }); }
| Property / Method | Description |
|---|---|
| Full HyperliquidClient — , , , , , , , and 35+ more methods |
| Subscribe to a market/account event (see Events below) |
| Run a handler on a recurring interval (aligned to poll loop) |
| Called after all handlers are registered, before first poll |
| Called on shutdown (SIGINT). Use for cleanup — close positions, cancel orders |
| Called when a handler throws. Error is already logged — use for recovery logic |
| Get a persisted value (survives restarts, stored in ) |
| Set a persisted value |
| Delete a persisted value |
| Clear all state |
| Send a message to the OpenClaw agent via webhook. Triggers an agent turn — the agent receives the message and can notify the user, take action, etc. Returns if delivered. Options: |
| Structured logger |
| Add a custom audit note to the local SQLite trail for later reporting |
| Add a numeric metric to the local SQLite trail |
| , , , , , |
| Automation ID (filename or flag) |
| if running with (write methods are intercepted) |
Automations now write a local audit trail automatically to
~/.openbroker/automation-audit.sqlite. The runtime records run config, logs, state changes, write actions, order updates, fills, user events, and per-poll account snapshots so you can generate performance reports later.
Dashboard Forwarding: When
OB_DASHBOARD_URL is set (e.g. http://localhost:3001), audit notes, metrics, and trade actions are automatically forwarded to the OpenBroker Vaults dashboard API in real time. The vault address is read from HYPERSTABLE_VAULT_ADDRESS or VAULT. Forwarding is fire-and-forget — it never blocks the automation or causes errors if the dashboard is unreachable.
| Event | Payload | When |
|---|---|---|
| | Every poll cycle (default: 10s) |
| | Mid price moved > 0.01% between polls |
| | Every poll for all assets |
| | New position detected |
| | Position no longer present |
| | Position size changed |
| | PnL moved > 5% of position value |
| | Margin usage > 80% |
tick — The universal heartbeatFires every single poll cycle (default: 10s) regardless of market conditions. Use this when you need to check something on every poll — absolute price thresholds, custom conditions, periodic account checks. This is the most reliable event because it always fires.
Payload:
{ timestamp: number, pollCount: number }
When to use:
api.every() is better for longer intervals)Example — absolute price alert:
api.on('tick', async () => { const mids = await api.client.getAllMids(); const price = parseFloat(mids['HYPE']); if (price < 38 && !api.state.get('alerted')) { api.state.set('alerted', true); await api.publish(`HYPE dropped below $38 — now at $${price.toFixed(3)}`); } });
Note:
tick does not include price data in its payload — you must fetch it yourself via api.client.getAllMids(). This is because tick fires before any other event processing. If you only care about price movements, use price_change instead.
price_change — Relative price movementsFires when a coin's mid price moves ≥ 0.01% compared to the previous poll. This filters out rounding noise while catching virtually any real price movement. The comparison is between consecutive polls (not from a fixed baseline), so it detects incremental changes.
Payload:
{ coin: string, oldPrice: number, newPrice: number, changePct: number }
When to use:
When NOT to use:
tick instead, because price_change only fires on relative movement between polls. During slow drifts (e.g. price slowly declining $0.001/s), the change between any two 10s polls may be < 0.01%, so the event won't fire even though the price has crossed your threshold.Example — momentum detector:
api.on('price_change', async ({ coin, changePct, newPrice }) => { if (coin !== 'ETH') return; if (changePct > 0.5) { api.log.info(`ETH surging +${changePct.toFixed(2)}% — price $${newPrice}`); // Enter long on strong upward momentum } });
funding_update — Funding rate dataFires every poll for every asset that has funding rate data. This is high-frequency — if there are 150 perp assets, this fires 150 times per poll. Filter by coin in your handler.
Payload:
{ coin: string, fundingRate: number, annualized: number, premium: number }
fundingRate — the raw hourly funding rate (e.g. 0.0001 = 0.01%/hr)annualized — annualized rate (fundingRate × 8760 × 100, as a percentage)premium — the premium componentWhen to use:
Example — funding scalp:
api.on('funding_update', async ({ coin, annualized }) => { if (coin !== 'ETH') return; if (annualized > 50 && !api.state.get('isShort')) { api.log.info(`ETH funding at ${annualized.toFixed(1)}% annualized — shorting`); await api.client.marketOrder('ETH', false, 0.1); api.state.set('isShort', true); } });
position_opened — New position detectedFires when a position appears that wasn't present in the previous poll. Useful for tracking entries made by other systems or confirming your own orders filled.
Payload:
{ coin: string, side: 'long' | 'short', size: number, entryPrice: number }
When to use:
Example — auto TP/SL on new positions:
api.on('position_opened', async ({ coin, side, size, entryPrice }) => { const tpPrice = side === 'long' ? entryPrice * 1.05 : entryPrice * 0.95; const slPrice = side === 'long' ? entryPrice * 0.97 : entryPrice * 1.03; await api.client.takeProfit(coin, side !== 'long', size, tpPrice); await api.client.stopLoss(coin, side !== 'long', size, slPrice); api.log.info(`Set TP at ${tpPrice} / SL at ${slPrice} for ${coin}`); });
position_closed — Position goneFires when a position that existed in the previous poll is no longer present. The position was either closed by you, liquidated, or filled by TP/SL.
Payload:
{ coin: string, previousSize: number, entryPrice: number }
When to use:
Example:
api.on('position_closed', async ({ coin, previousSize, entryPrice }) => { api.log.info(`${coin} position closed (was ${previousSize} @ ${entryPrice})`); api.state.delete(`${coin}_tp`); await api.publish(`Position closed: ${coin} (entry: $${entryPrice})`); });
position_changed — Size or direction changedFires when an existing position's size changes (partial close, add to position, or flip direction). Does NOT fire when a new position opens or an existing one fully closes — use
position_opened and position_closed for those.
Payload:
{ coin: string, oldSize: number, newSize: number, entryPrice: number }
oldSize/newSize are signed: positive = long, negative = shortWhen to use:
Example:
api.on('position_changed', async ({ coin, oldSize, newSize }) => { if (Math.abs(newSize) > Math.abs(oldSize)) { api.log.info(`${coin} position increased: ${oldSize} → ${newSize}`); } else { api.log.info(`${coin} position reduced: ${oldSize} → ${newSize}`); } });
pnl_threshold — Significant PnL movementFires when unrealized PnL changes by ≥ 5% of position value between consecutive polls. This is a large move detector — useful for risk management alerts rather than routine monitoring.
Payload:
{ coin: string, unrealizedPnl: number, changePct: number, positionValue: number }
changePct — the PnL change as a percentage of total position value (not % of PnL itself)When to use:
api.publish()Example:
api.on('pnl_threshold', async ({ coin, unrealizedPnl, changePct }) => { if (unrealizedPnl < 0) { await api.publish( `⚠️ ${coin} PnL dropped sharply: $${unrealizedPnl.toFixed(2)} (${changePct.toFixed(1)}% of position)`, { name: 'pnl-alert' }, ); } });
margin_warning — High margin usageFires when margin usage exceeds 80% of equity. After the first trigger, it only fires again if margin usage increases by another 5 percentage points (prevents spam). Resets when margin drops back below 80%.
Payload:
{ marginUsedPct: number, equity: number, marginUsed: number }
When to use:
Example:
api.on('margin_warning', async ({ marginUsedPct, equity }) => { await api.publish( `🚨 Margin at ${marginUsedPct.toFixed(1)}% — equity: $${equity.toFixed(2)}. Consider reducing exposure.`, { name: 'margin-alert' }, ); });
order_update — Real-time order lifecycle (WebSocket)Fires instantly when any order changes status:
open, filled, canceled, triggered, rejected, marginCanceled, liquidatedCanceled, badAloPxRejected, and 20+ other statuses. Requires WebSocket (enabled by default).
Payload:
{ coin: string, oid: number, side: 'buy' | 'sell', size: number, price: number, origSize: number, status: string, statusTimestamp: number }
Example:
api.on('order_update', async ({ coin, oid, status, side, size, price }) => { if (status === 'filled') { api.log.info(`Order ${oid} filled: ${side} ${size} ${coin} @ $${price}`); } else if (status === 'canceled' || status.includes('Rejected')) { api.log.warn(`Order ${oid} ${status}: ${coin}`); } });
liquidation — Liquidation alert (WebSocket only)Fires when the account is liquidated. This event is only available via WebSocket — there is no REST polling equivalent.
Payload:
{ lid: number, liquidator: string, liquidatedUser: string, liquidatedNtlPos: number, liquidatedAccountValue: number }
Example:
api.on('liquidation', async ({ liquidatedNtlPos, liquidatedAccountValue }) => { await api.publish( `LIQUIDATED: $${liquidatedNtlPos.toFixed(2)} notional, account value: $${liquidatedAccountValue.toFixed(2)}`, { name: 'liquidation-alert' }, ); });
Automations use WebSocket by default for real-time market and account events. The runtime subscribes to:
price_change events in real-time)order_update and order_filled)REST polling continues as a heartbeat (every 60s by default) for position/margin/funding events that aren't covered by WebSocket. If the WebSocket connection fails, the runtime falls back to full REST polling (every 10s) automatically.
To disable WebSocket (pure REST polling):
openbroker auto run my-strategy.ts --no-ws
| Use case | Best event | Why |
|---|---|---|
| Alert when price crosses a fixed level | | Fires every poll — no minimum change threshold |
| React to price momentum/volatility | | Real-time via WebSocket, provides relative change data |
| Funding rate strategy | | Gives annualized rate directly |
| Auto TP/SL on new positions | | Fires exactly when a new position appears |
| Log when positions close | | Fires when position disappears |
| Track position scaling | | Fires on size changes only |
| Risk management — PnL spikes | | Only fires on large moves (≥5% of position value) |
| Risk management — margin | | Fires at 80%+ margin usage |
| React instantly to order fills/rejects | | Real-time via WebSocket — sub-second latency |
| Liquidation alerts | | WebSocket only — no REST equivalent |
| Periodic task (DCA, rebalance) | | Better than tick for longer intervals |
The
api.client object exposes the full HyperliquidClient. All coin params accept HIP-3 prefixed tickers (e.g. xyz:CL). Optional user params default to the configured wallet address.
| Method | Description |
|---|---|
| Market order via IOC limit at mid ± slippage. Returns |
| Limit order. : (default), , . Returns |
| Trigger (conditional) order. : or . Activates when price hits , then fills as limit at . Returns |
| Stop loss shortcut. Sets limit price with slippage buffer (default 100 bps / 1%) to ensure fill. is always true. Returns |
| Take profit shortcut. Limit price = trigger price (favorable direction). is always true. Returns |
| Cancel a single order by numeric OID. Returns |
| Cancel all open orders. If is provided, only cancels orders for that asset. Returns |
| Low-level order placement. : `{ limit: { tif: 'Gtc' |
| Method | Returns |
|---|---|
| — mid prices for all assets (main + HIP-3). Key = coin name, value = price string |
| — market metadata (universe of assets with , ) and asset contexts (funding, open interest, volume, mark/oracle prices) |
| — L2 order book with computed spread |
| — recent trade tape. : (buy) or (sell) |
| — OHLCV candles. : , , , , , . Times are Unix ms |
| — historical hourly funding rates |
| — predicted funding rates across all venues |
| `Array<{ name, fullName, deployer } |
| — metadata + contexts for every perp DEX (main + all HIP-3) |
| — spot market metadata (token info, trading pairs) |
| — spot metadata + price/volume contexts |
| Token details: supply, deployer, prices. Returns if not found |
| Method | Returns |
|---|---|
| — full account state across all dexes: (accountValue, totalMarginUsed, withdrawable), , and (each with , , , , , , , ) |
| — account state for a single dex (omit for main perps) |
| — all open orders across all dexes. Each: |
| — trade fill history. : (buy) or (sell) |
| — all orders (filled, cancelled, etc.) |
| — status of a specific order by numeric OID or string CLOID |
| — funding payments received/paid |
| — fee tier, rates, and volume |
| — API rate limit status |
| — spot token balances |
| — sub-accounts for a master wallet |
| — account abstraction mode: , , , or |
| — if unified or portfolio margin (shared USDC across dexes) |
| Method | Description |
|---|---|
| Set leverage. defaults to (cross margin). HIP-3 assets are forced to isolated and clamped to their max leverage |
| Approve builder fee (must be called from main wallet, not API wallet). Default rate: |
| Check approved builder fee. Returns fee string (e.g. ) or if not approved |
| Property / Method | Description |
|---|---|
| Get numeric asset index for a coin (used internally for order wire) |
| Get size decimal precision for a coin |
| Check if a coin is a HIP-3 asset |
| Get dex name for a coin ( for main perps) |
| Get all known asset names (main + HIP-3) |
| Get only HIP-3 asset names |
| Force refresh of market metadata on next call |
api.utils)| Function | Description |
|---|---|
| Round price to 5 significant figures (max 6 decimals perp, 8 spot) |
| Round size to asset-specific decimal precision |
| Promise-based delay |
| Normalize coin name (uppercase, trim whitespace) |
| Format number as USD string (e.g. ) |
| Convert hourly funding rate to annualized percentage |
// ~/.openbroker/automations/breakout.ts export default function(api) { const COIN = 'ETH'; const BREAKOUT_PCT = 2; // 2% move triggers entry const SIZE = 0.5; let basePrice = null;api.onStart(async () => { const mids = await api.client.getAllMids(); basePrice = parseFloat(mids[COIN]); api.log.info(
); });Watching ${COIN} from $${basePrice} for ${BREAKOUT_PCT}% breakoutapi.on('price_change', async ({ coin, newPrice }) => { if (coin !== COIN || !basePrice) return; const totalChange = ((newPrice - basePrice) / basePrice) * 100;
if (Math.abs(totalChange) >= BREAKOUT_PCT && !api.state.get('inPosition')) { const side = totalChange > 0; // true = long, false = short api.log.info(`Breakout! ${totalChange.toFixed(2)}% — entering ${side ? 'long' : 'short'}`); await api.client.marketOrder(COIN, side, SIZE); api.state.set('inPosition', true); }}); }
// ~/.openbroker/automations/hourly-dca.ts export default function(api) { const COIN = 'ETH'; const USD_PER_BUY = 100;// Buy $100 of ETH every hour api.every(60 * 60 * 1000, async () => { const mids = await api.client.getAllMids(); const price = parseFloat(mids[COIN]); const size = parseFloat(api.utils.roundSize(USD_PER_BUY / price, 4)); await api.client.marketOrder(COIN, true, size); const count = (api.state.get('buyCount') || 0) + 1; api.state.set('buyCount', count); api.log.info(); }); }DCA #${count}: bought ${size} ${COIN} at $${price}
// ~/.openbroker/automations/margin-guard.ts export default function(api) { api.on('margin_warning', async ({ marginUsedPct, equity }) => { api.log.warn(`Margin at ${marginUsedPct.toFixed(1)}% — reducing positions`);// Close the smallest position to free margin const state = await api.client.getUserStateAll(); const positions = state.assetPositions .filter(p => parseFloat(p.position.szi) !== 0) .sort((a, b) => Math.abs(parseFloat(a.position.positionValue)) - Math.abs(parseFloat(b.position.positionValue))); if (positions.length > 0) { const pos = positions[0].position; const size = Math.abs(parseFloat(pos.szi)); const isBuy = parseFloat(pos.szi) < 0; // Close short = buy, close long = sell api.log.info(`Closing smallest position: ${pos.coin} (${pos.szi})`); await api.client.marketOrder(pos.coin, isBuy, size); }}); }
Use
api.publish() to send messages back to the OpenClaw agent. This triggers an agent turn — the agent receives the message and can notify the user via their preferred channel, take trading actions, or log the event.
// Simple notification await api.publish(`ETH broke above $4000 — current price: $${price}`);// With options await api.publish(, { name: 'margin-alert', // appears in logs wakeMode: 'now', // 'now' (default) or 'next-heartbeat' channel: 'slack', // target channel (optional) });Margin at ${pct}% — positions at risk
api.publish() returns true if delivered, false if webhooks are not configured (no hooks token). It requires OPENCLAW_HOOKS_TOKEN to be set (automatically configured when running as an OpenClaw plugin).
Example: Price alert automation with publish
// ~/.openbroker/automations/price-alert.ts export default function(api) { const COIN = 'ETH'; const THRESHOLD = 4000;api.on('price_change', async ({ coin, newPrice, changePct }) => { if (coin !== COIN) return;
const crossed = api.state.get<boolean>('crossed', false); if (!crossed && newPrice >= THRESHOLD) { api.state.set('crossed', true); await api.publish( `${COIN} crossed above $${THRESHOLD}! Price: $${newPrice.toFixed(2)} (+${changePct.toFixed(2)}%)`, ); } else if (crossed && newPrice < THRESHOLD) { api.state.set('crossed', false); }}); }
CLI:
openbroker auto run my-strategy --dry # Test without trading openbroker auto run ./funding-scalp.ts # Run from path openbroker auto run my-strategy --poll 5000 # Poll every 5s openbroker auto run my-strategy --no-ws # Disable WebSocket, pure REST polling openbroker auto run --example dca --set coin=HYPE --set amount=50 --dry # Run bundled example openbroker auto examples # List bundled examples with config openbroker auto list # Show available scripts openbroker auto status # Show running automations openbroker auto stop <id> # Unregister an automation (won't auto-restart) openbroker auto report <id> # Read the local audit report (logs, trades, metrics) for an automation openbroker auto clean # Remove stale entries from the registry
Plugin tools (for OpenClaw agents):
ob_auto_run — { "script": "funding-scalp", "dry": true } — start an automationob_auto_run — { "example": "dca", "config": { "coin": "HYPE", "amount": 50 }, "dry": true } — run a bundled exampleob_auto_stop — { "id": "funding-scalp" } — stop a running automationob_auto_list — {} — list available automations, bundled examples with config schemas, and running automationsOptions:
| Flag | Description | Default |
|---|---|---|
| Intercept write methods — no real trades | false |
| Show debug output | false |
| Custom automation ID | filename |
| Poll interval in milliseconds | 10000 |
| Disable WebSocket; fall back to REST-only polling | WebSocket on |
| Run a bundled example automation | - |
| Set config values (repeatable) | - |
Inspecting automations after they run:
openbroker auto report <id> — reads the local SQLite audit trail at ~/.openbroker/automation-audit.sqlite and prints a summary of logs, write actions, fills, PnL, and custom metrics recorded via api.audit.record() / api.audit.metric(). Use this to review what a strategy actually did.openbroker auto clean — prunes registry entries for automations that are no longer running or whose script file is gone. Safe to run anytime.Guidelines for agents writing automations:
Risk & Safety (mandatory):
margin_warning and pnl_threshold events so the user is never blindsided by liquidation risk. If no margin/liquidation automation is already running, create one before placing trades.api.publish() to notify the user of important events — position opens/closes, TP/SL triggers, large PnL swings, margin warnings, errors, and any situation that requires human attention. Do NOT silently handle critical events.api.onStop() handler to clean up — cancel open orders and close positions (or at minimum alert the user) on shutdown. Never leave orphaned orders or unmanaged positions.--dry unless the user explicitly asks for it. Automations should run live by default.State & Reliability:
api.state to track position state, entry prices, and flags across restarts. Never rely on in-memory variables alone — automations persist across gateway restarts and are automatically restarted.api.state.get/set) to prevent duplicate orders. Events can fire multiple times for the same condition across polls — always check state before placing orders.Communication:
api.publish() to send alerts/events back to the OpenClaw agent — do NOT manually construct webhook requests.General:
~/.openbroker/automations/ by name, or from any absolute path.api.client.marketOrder('xyz:CL', true, 1)).api.every(ms, fn) over tick for periodic tasks with intervals longer than the poll cycle.--dry first to preview ordersHYPERLIQUID_NETWORK=testnet)--reduce for closing positions onlyNo automatic installation available. Please visit the source repository for installation instructions.
View Installation Instructions1,500+ AI skills, agents & workflows. Install in 30 seconds. Part of the Torly.ai family.
© 2026 Torly.ai. All rights reserved.