Skip to content

Trading

Placing and cancelling needs a read_write key. The examples run on the US demo exchange with 1-cent buy orders that will not fill. How orders behave on the exchange is covered in Order types and Risk controls; this page shows the SDK calls.

The examples use a market that is open, accepting orders (trading is true), and whose event has not started:

import { STX } from "@stxapp/stx-typescript";
const client = new STX();
let market;
for await (const m of client.iterMarkets({ trading: true, sortBy: "event_start", sortDirection: "desc" })) {
if (m.event_status === "scheduled") {
market = m;
break;
}
}
if (!market?.market_id) throw new Error("no tradeable market");
const marketId = market.market_id;
const order = await client.placeOrder(marketId, "buy", "limit", {
price: "0.01",
quantity: "1",
clientOrderId: "my-order-1",
});
console.log(order.id, order.status);
await client.cancelOrder(order.id!);

placeOrder(marketId, action, orderType, options) takes action "buy" or "sell" and orderType "limit" or "market". Options:

Option Type
price Dollar string, e.g. "0.56". Required for "limit", omitted for "market"
quantity Contract string, e.g. "2"
clientOrderId Your own id, echoed back and filterable
expiration, expirationTime See Expiration
cancelOnDisconnect See Cancel-on-disconnect
deviceId Optional label
geoLocation See Geo-fencing

If the exchange refuses the order, the call throws with the exchange’s message:

import { STXRejectedException, STXValidationException } from "@stxapp/stx-typescript";
try {
await client.placeOrder(marketId, "buy", "limit", { price: "0.01", quantity: "1.5" });
} catch (err) {
if (err instanceof STXRejectedException || err instanceof STXValidationException) {
console.error(err.statusCode, err.message);
} else {
throw err;
}
}

A market order leaves out price:

await client.placeOrder(marketId, "buy", "market", { quantity: "1" });
const results = await client.placeOrders([
{ marketId, action: "buy", orderType: "limit", price: "0.01", quantity: "1" },
{ marketId, action: "buy", orderType: "limit", price: "0.02", quantity: "1" },
]);
for (const r of results) {
console.log(r.ok ? r.order!.id : r.errors);
}

Each result holds either order or errors, in the order you sent them. One rejection does not stop the rest.

await client.cancelOrder(orderId); // one
await client.cancelOrders([idA, idB]); // several
await client.cancelAllOrders(); // everything on the account

Each returns cancellations with order_id and status. An order can still fill while a cancel is in flight, so reconcile against fills.

for await (const o of client.iterOrders({ status: ["open", "delayed"] })) {
console.log(o.id, o.price, o.filled, "/", o.quantity);
}
const one = await client.order(orderId);

orders() filters on orderIds, clientOrderIds, marketIds and status.

The client never resends a POST after a server error or a dropped connection, because the order may already be on the book. Send a clientOrderId and look it up before trying again:

import { randomUUID } from "node:crypto";
import { STXServerException, STXTransportException } from "@stxapp/stx-typescript";
const clientOrderId = randomUUID();
let order;
try {
order = await client.placeOrder(marketId, "buy", "limit", { price: "0.01", quantity: "1", clientOrderId });
} catch (err) {
if (!(err instanceof STXServerException || err instanceof STXTransportException)) throw err;
order = (await client.orders({ clientOrderIds: [clientOrderId] })).items[0];
}

The two expiration modes are described in Risk controls. In the SDK:

await client.placeOrder(marketId, "buy", "limit", { price: "0.01", quantity: "1", expiration: "good_till_start" });
await client.placeOrder(marketId, "buy", "limit", {
price: "0.01",
quantity: "1",
expiration: "good_till_time",
expirationTime: (Date.now() + 10 * 60_000) * 1000, // microseconds
});

How cancel-on-disconnect works is described in Risk controls. In the SDK, arm it on the orders channel and opt each order in. The client then keeps pinging the channel for you while the socket is up.

const ws = await client.websocket().connect();
const orders = await ws.orders({ cancelOnDisconnect: true, pingTimeout: 5000 });
console.log("granted timeout (ms):", orders.reply.ping_timeout);
await client.placeOrder(marketId, "buy", "limit", { price: "0.01", quantity: "1", cancelOnDisconnect: true });

STX Ontario checks where each order write comes from. A server at a fixed IP address needs that address whitelisted on your account. Otherwise, give the client a geolocation packet, or a function that returns a fresh one, and it is attached to every order write:

const client = new STX({ geoLocation: () => getLatestPacket() });

Check before trading, without placing anything (needs a read_write key; call it once at start-up):

const check = await client.geoCheck();
if (!check.allowed) console.error(check.reason, check.message);

A refused write throws STXGeoLocationException with a reason.

v1.5.9Changelogllms.txtllms-full.txt