Trading & orders
Every write goes through the same normalized arguments and returns an OrderStatus. Venue asymmetries (TP/SL, modify semantics) are handled for you and called out below.
Place an order
const status = await client.placeOrder({
venue: 'hl',
market: 'BTC',
side: 'buy',
size: '0.0003',
price: '50000', // omit for IOC market-style
tif: 'gtc', // gtc | ioc | alo
reduceOnly: false,
clientOrderId: '0x...', // 0x + 32 hex on HL; any string on Decibel
})
clientOrderId is mandatory and is your dedup key — re-using a live one is rejected with INVALID_ARGUMENT, so a retry can't silently create a second order. For market-style execution use tif: 'ioc' with a price that crosses the spread (e.g. ~1% above the best ask for a buy).
On Decibel, TP/SL must be set at placement time — pass tpTriggerPrice /
slTriggerPrice (and optional *LimitPrice to make a leg a limit trigger) on
placeOrder. On Hyperliquid, use attachTpSl after the
fill.
Batch
const results = await client.placeBatch({
venue: 'hl',
orders: [ioc, gtc1, gtc2], // up to 20
})
// results[i] is either an OrderStatus (success) or a TriaError (rejection).
- HL — native multi-order, atomic at the matcher.
- Decibel — parallel txs; failures don't abort siblings.
Cancel
// By clientOrderId or orderId. Idempotent — a second cancel returns not_found.
await client.cancelOrder({ venue: 'hl', clientOrderId: cloid })
await client.cancelAllOrders({ venue: 'decibel', market: 'BTC/USD' }) // market optional
Modify
const updated = await client.modifyOrder({
venue: 'hl',
orderId: '12345',
newPrice: '49800',
newSize: '0.0004', // at least one of newPrice / newSize
})
- HL — native
modify. Mints a new orderId for the same cloid; the returnedorderIdis the new one. - Decibel — emulated as cancel + replace. The original orderId is permanently gone.
Attach TP/SL
const { tp, sl } = await client.attachTpSl({
venue: 'hl',
market: 'BTC',
takeProfit: { triggerPrice: '60000', isMarket: true },
stopLoss: { triggerPrice: '45000' },
})
Trigger orders are reduce-only and opposite-side; they close the position when the venue mark crosses triggerPrice.
- HL — native trigger orders; both appear in
openOrdersimmediately. - Decibel — throws
INVALID_ARGUMENT. Decibel's TP/SL is at the opening order's placement time, not a separate post-fill trigger — set it onplaceOrder.
Close a position
const close = await client.closePosition({
venue: 'hl',
market: 'BTC',
slippageBps: 200, // default 2%
})
Reads positions(), then fires a reduce-only IOC at the slippage band. Throws INVALID_ARGUMENT if there's no open position on market.
Risk — scheduled cancel
// Venue-side dead-man switch. Stop calling before deadlineMs → the venue
// cancels every open order on your account. Omit deadlineMs to clear.
await client.scheduledCancel({ venue: 'hl', deadlineMs: Date.now() + 60_000 })
- HL — native. Requires ≥ 5s in the future and ≥ $1M lifetime traded volume on the account. Below the gate → clean
VENUE_REJECTED. - Decibel — throws
INVALID_ARGUMENT(no venue-native primitive; Direction B's gateway will emulate one).
Venue asymmetries at a glance
| Behavior | HL | Decibel |
|---|---|---|
attachTpSl post-fill | ✅ native trigger orders | ❌ at-placement-time only |
scheduledCancel | ✅ native (≥ $1M volume gate) | ❌ throws |
modifyOrder orderId | mints new orderId | cancel + replace |
| Market identifier | symbol (BTC) | /USD suffix (BTC/USD) |
| Price tick rule | tick-aligned and ≤ 5 sig figs | tick-aligned only |
| Gas | none | operator APT unless gasStation set |