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).

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 returned orderId is 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 openOrders immediately.
  • Decibel — throws INVALID_ARGUMENT. Decibel's TP/SL is at the opening order's placement time, not a separate post-fill trigger — set it on placeOrder.

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

BehaviorHLDecibel
attachTpSl post-fill✅ native trigger orders❌ at-placement-time only
scheduledCancel✅ native (≥ $1M volume gate)❌ throws
modifyOrder orderIdmints new orderIdcancel + replace
Market identifiersymbol (BTC)/USD suffix (BTC/USD)
Price tick ruletick-aligned and ≤ 5 sig figstick-aligned only
Gasnoneoperator APT unless gasStation set

Was this page helpful?