Skip to main content
Trades are placed through PerpsClient.placeOrder, which builds the action, signs it with the SDK-managed agent, and submits it — all in one call. The user’s wallet is not prompted; the per-trade signing happens with the agent keypair provisioned during setup.
The examples on this page use Hyperliquid (provider: 'hyperliquid'). The trading API is provider-agnostic — replace the provider value with any supported venue from getProviders(). The placeOrder call shape is identical for every provider; each provider signs with its own configured signer.

placeOrder

Once the user has provisioned an agent (see Signing Model and Hyperliquid / Setup), placing an order is a single call:
const result = await perps.placeOrder({
  address: userAddress,
  provider: 'hyperliquid',
  market: { marketId: 'BTC', categoryId: 'hyperliquid' },
  side: 'BUY',
  type: 'MARKET',
  size: '0.1',
  price: '95500.00',
  leverage: 10,
});

console.log(result.results);
// [{ action: 'placeOrder', success: true, orderId: '12345678' }]
Internally this is the create -> sign -> execute pattern from Action Pattern:
createAction → actions[] → agent signs each typedData → executeAction → results[]
The SDK handles all three steps. If the requested leverage differs from the current setting, the backend prepends an updateLeverage step automatically — both actions are signed and submitted together, so result.results[] may contain more than one entry.

Order Types

Market Order

Executes immediately at the best available price. The price field acts as a slippage limit.
await perps.placeOrder({
  address: userAddress,
  provider: 'hyperliquid',
  market: { marketId: 'BTC', categoryId: 'hyperliquid' },
  side: 'BUY',
  type: 'MARKET',
  size: '0.1',
  price: '95500.00',   // slippage limit
});

Limit Order

Rests on the orderbook until filled or cancelled.
await perps.placeOrder({
  address: userAddress,
  provider: 'hyperliquid',
  market: { marketId: 'ETH', categoryId: 'hyperliquid' },
  side: 'BUY',
  type: 'LIMIT',
  size: '1.0',
  price: '3150.00',
  timeInForce: 'GTC',  // Good til cancelled — Hyperliquid's resting form
});
On Lighter, the equivalent resting limit order uses GTT (Lighter’s native resting form) rather than GTC.

Time in Force options

Not every TIF is available on every venue, and POST_ONLY rests differently depending on the venue:
TIFHyperliquidLighter
GTC✓ Default — rests until filled or cancelled— Use GTT instead (see note below)
IOC✓ Fills immediately, cancels any unfilled portion✓ Same behavior
POST_ONLY✓ Maker-only — rests GTC-style, cancelled if it would immediately match✓ Maker-only — rests GTT-style, cancelled if it would immediately match
GTT— Use GTC instead (see note below)✓ Lighter’s native resting order — stays until filled or cancelled
Some TIFs are venue-specific: GTC is Hyperliquid, GTT is Lighter. IOC and POST_ONLY work on both venues — but POST_ONLY rests GTC-style on Hyperliquid and GTT-style on Lighter.
Lighter has no true good-til-cancelled. A resting order placed on Lighter is reported back (e.g. from getOrders) with timeInForce: 'GTT', even if you submitted it as GTC — don’t rely on reading back the exact TIF you sent on Lighter.
API Reference: POST /createAction · /executeAction