Trading
The trading namespace covers account state, tradable assets, orders, positions, and account/order events. Start in paper trading, make live mode a deliberate configuration choice, and give every order a stable client ID.
Paper and live safety
Trading defaults to the paper host. Keep the setting explicit in applications that can place orders:
import { Alpaca } from "@alpacahq/alpaca-trade-api";
const paper = new Alpaca({ keyId, secret, paper: true });
const live = new Alpaca({ keyId, secret, paper: false });
Use separate credentials and deployment configuration for paper and live
accounts. The paper flag changes trading REST and trading-stream endpoints;
it does not change market-data entitlements or feeds.
Accounts and assets
Generated REST APIs are available directly beneath alpaca.trading:
const account = await paper.trading.account.getAccount();
console.log(account.status, account.buyingPower);
const assets = await paper.trading.assets.getV2Assets({
status: "active",
assetClass: "us_equity",
});
const aapl = assets.find((asset) => asset.symbol === "AAPL");
console.log(aapl?.tradable, aapl?.fractionable);
Money and quantity fields intentionally remain numeric strings. See Values & types before doing arithmetic with them.
Typed order builders
alpaca.trading.orders is the generated OrdersApi plus one typed builder per
common order shape. The builders accept number | string amounts, normalize
them to wire strings, default timeInForce to "day", and enforce required
fields at compile time.
await paper.trading.orders.market({
symbol: "AAPL",
side: "buy",
qty: 1,
clientOrderId: `market-${crypto.randomUUID()}`,
});
await paper.trading.orders.limit({
symbol: "AAPL",
side: "buy",
qty: 1,
limitPrice: 150,
clientOrderId: `limit-${crypto.randomUUID()}`,
});
await paper.trading.orders.stop({
symbol: "AAPL",
side: "sell",
qty: 1,
stopPrice: 140,
clientOrderId: `stop-${crypto.randomUUID()}`,
});
await paper.trading.orders.stopLimit({
symbol: "AAPL",
side: "sell",
qty: 1,
stopPrice: 140,
limitPrice: 139.5,
clientOrderId: `stop-limit-${crypto.randomUUID()}`,
});
await paper.trading.orders.trailingStop({
symbol: "AAPL",
side: "sell",
qty: 1,
trailPercent: 5,
clientOrderId: `trailing-stop-${crypto.randomUUID()}`,
});
await paper.trading.orders.bracket({
symbol: "AAPL",
side: "buy",
qty: 10,
limitPrice: 150,
takeProfit: { limitPrice: 155 },
stopLoss: { stopPrice: 145, limitPrice: 144.5 },
clientOrderId: `bracket-${crypto.randomUUID()}`,
});
The same facade also provides oco and oto. Market orders require exactly one
of qty or notional; trailing stops require exactly one of trailPrice or
trailPercent.
Build first, submit explicitly
The exported orders namespace also exposes every builder as a pure function.
This separates request construction from placement:
import { orders } from "@alpacahq/alpaca-trade-api";
const postOrderRequest = orders.buildLimitOrder({
symbol: "AAPL",
side: "buy",
qty: 1,
limitPrice: 150,
clientOrderId: `limit-${crypto.randomUUID()}`,
});
// The network request happens only here.
const order = await paper.trading.orders.postOrder({ postOrderRequest });
orders.buildLimitOrder (and the market, stop, stop-limit, trailing-stop,
bracket, OCO, and OTO equivalents) validates and normalizes the request without
performing a network call. Pure builders are useful in tests, for inspection,
and for composition before an explicit postOrder.
Generic and raw escape hatches
For shapes without a dedicated builder, such as multi-leg option orders, use the
near-raw submit helper:
await paper.trading.orders.submit({
type: "market",
orderClass: "mleg",
qty: 1,
timeInForce: "day",
clientOrderId: `mleg-${crypto.randomUUID()}`,
legs: [
{
symbol: "AAPL270115C00150000",
ratioQty: "1",
side: "buy",
positionIntent: "buy_to_open",
},
{
symbol: "AAPL270115C00160000",
ratioQty: "1",
side: "sell",
positionIntent: "sell_to_open",
},
],
});
The generated method is always available too:
const clientOrderId = `raw-market-${crypto.randomUUID()}`;
await paper.trading.orders.postOrder({
postOrderRequest: {
symbol: "AAPL",
side: "buy",
type: "market",
timeInForce: "day",
qty: "1",
clientOrderId,
},
});
Stable client order IDs and reconciliation
A stable, unique clientOrderId makes a placement auditable and gives you a
recovery key. It does not provide response replay: Alpaca rejects another order
that reuses the same ID.
Order-placement POSTs are never automatically retried. If a FetchError
leaves placement ambiguous, query by the same client ID before deciding what to
do next:
const recovered = await paper.trading.orders.getOrderByClientOrderId({
clientOrderId,
});
A lookup miss is not proof that placement failed, and the SDK does not promise eventual lookup visibility. Apply your application's reconciliation policy before any further submission.
Submit and wait for a terminal state
submitAndWait combines the trade-updates stream and one REST placement. It
waits for the server's listening acknowledgement, places once per invocation,
never places again after reconnect, and uses one deadline for connection,
authentication, subscription, placement, and terminal-event waiting.
const terminalOrder = await paper.trading.submitAndWait(
{
type: "market",
symbol: "AAPL",
side: "buy",
qty: 1,
clientOrderId: `workflow-${crypto.randomUUID()}`,
},
{ timeoutMs: 30_000 },
);
if (terminalOrder.status === "filled") {
console.log(terminalOrder.id, terminalOrder.filledAvgPrice);
} else {
console.warn(terminalOrder.id, terminalOrder.status);
}
The promise resolves for any event this workflow treats as terminal, not
necessarily a fill. filled, canceled, rejected, and expired settle the
order lifecycle. done_for_day only pauses execution until the next trading day
and an eligible order can resume then. Treat it as a terminal wait outcome, not
proof that the order lifecycle is over, and check status === "filled" before
using fill fields.
After an ambiguous placement FetchError, this workflow makes one
getOrderByClientOrderId request and continues waiting when appropriate. It
does not promise exactly-once execution or eventual lookup visibility.
Post-placement failures reject with SubmitAndWaitError. Inspect
clientOrderId, optional orderId, phase, placementAmbiguous, and cause.
If placement remains ambiguous, reconcile the client ID before resubmitting.
Close all positions
Use the workflow helper to flatten the account, optionally cancelling open orders first:
await paper.trading.closeAllPositions({ cancelOrders: true });
This is a consequential operation even in paper trading. In live mode, gate it behind explicit application authorization and observability.
Reference and related guides
- Browse the curated facade overview in the Trading API Reference. For the complete installed surface, use the published TypeScript declarations and your editor.
- Configure environments and OAuth in Authentication.
- Understand retry and error behavior in Resilience & configuration.
- Consume order/trade updates in Streaming & Events.