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:

from stx import STX
with STX() as client:
market = next(
m
for m in client.iter_markets(
status="open", trading=True, sort_by="event_start", sort_direction="desc"
)
if m.event_status == "scheduled"
)
print(market.market_id, market.symbol, market.max_price)
from stx import STX
with STX() as client:
market = next(
m
for m in client.iter_markets(
status="open", trading=True, sort_by="event_start", sort_direction="desc"
)
if m.event_status == "scheduled"
)
order = client.place_order(
market.market_id, "buy", "limit", price="0.01", quantity="1", client_order_id="quickstart-1"
)
print(order.id, order.status, order.price, order.quantity, order.client_order_id)
client.cancel_order(order.id)
Argument Meaning
market_id The market
action "buy" or "sell"
order_type "limit" or "market"
price Dollars as a string, e.g. "0.56". Required for a limit order, omitted for a market order
quantity Contracts as a string, e.g. "2"
client_order_id Your own id, echoed back and filterable
expiration, expiration_time See Expiration
cancel_on_disconnect See Cancel-on-disconnect
device_id Optional device label

price and quantity must be strings (or decimal.Decimal). A float raises TypeError before anything is sent, because a float cannot carry an exact decimal. The exchange validates everything else: a price off the tick, a price at or above max_price, or a fractional quantity comes back as STXRejectedException (422) or STXValidationException (400) carrying the exchange’s message:

from stx import STX, STXAPIException
with STX() as client:
market = next(
m
for m in client.iter_markets(
status="open", trading=True, sort_by="event_start", sort_direction="desc"
)
if m.event_status == "scheduled"
)
try:
client.place_order(market.market_id, "buy", "limit", price="0.01", quantity="1.5")
except STXAPIException as exc:
print(exc.status_code, exc)

The returned Order shows the state at acceptance (accepted, open, filled…). Follow it on the orders channel or with client.order(order.id).

place_orders() sends one POST /api/v1/orders/batched. Each order is a dict with the place_order fields; the result has one entry per order, in order, holding either order or errors:

from stx import STX
with STX() as client:
market = next(
m
for m in client.iter_markets(
status="open", trading=True, sort_by="event_start", sort_direction="desc"
)
if m.event_status == "scheduled"
)
results = client.place_orders(
[
{"market_id": market.market_id, "action": "buy", "order_type": "limit",
"price": "0.01", "quantity": "1"},
{"market_id": market.market_id, "action": "buy", "order_type": "limit",
"price": "0.02", "quantity": "1"},
]
)
for r in results:
print("placed" if r.ok else "rejected", r.order.id if r.ok else r.errors)
cancelled = client.cancel_orders([r.order.id for r in results if r.ok])
print("cancelled", len(cancelled))

One rejected order does not stop the others.

from stx import STX
with STX() as client:
market = next(
m
for m in client.iter_markets(
status="open", trading=True, sort_by="event_start", sort_direction="desc"
)
if m.event_status == "scheduled"
)
a = client.place_order(market.market_id, "buy", "limit", price="0.01", quantity="1")
b = client.place_order(market.market_id, "buy", "limit", price="0.01", quantity="1")
c = client.place_order(market.market_id, "buy", "limit", price="0.01", quantity="1")
print(client.cancel_order(a.id)) # one
print(client.cancel_orders([b.id, c.id])) # several by id
print(client.cancel_all_orders()) # everything left on the account

Each returns Cancellation objects with order_id and status. A cancel is a request: an order can fill between sending the cancel and the exchange processing it, so reconcile against fills rather than assuming you are flat.

from stx import STX
with STX() as client:
for order in client.orders(status=["open", "delayed"], limit=20):
print(order.id, order.market_id, order.action, order.price, order.filled, "/", order.quantity)
recent = client.orders(limit=1)
if recent.items:
print(client.order(recent[0].id).status)

orders() filters on order_ids, client_order_ids, market_ids and status (one or a list of created, requested, accepted, delayed, open, filled, rejected, cancelled, partially_cancelled).

A POST is never retried after a server error or a dropped connection: the order may already be on the book. If that happens, look it up by client_order_id before placing again:

import uuid
from stx import STX, STXServerException, STXTransportException
with STX() as client:
market = next(
m
for m in client.iter_markets(
status="open", trading=True, sort_by="event_start", sort_direction="desc"
)
if m.event_status == "scheduled"
)
coid = str(uuid.uuid4())
try:
order = client.place_order(
market.market_id, "buy", "limit", price="0.01", quantity="1", client_order_id=coid
)
except (STXServerException, STXTransportException):
found = client.orders(client_order_ids=[coid])
order = found[0] if found.items else None
print(order.id if order else "not placed")
if order:
client.cancel_order(order.id)

expiration="good_till_start" pulls the order when the event starts. expiration="good_till_time" pulls it at expiration_time, in Unix microseconds:

import time
from stx import STX
with STX() as client:
market = next(
m
for m in client.iter_markets(
status="open", trading=True, sort_by="event_start", sort_direction="desc"
)
if m.event_status == "scheduled"
)
in_one_hour = int((time.time() + 3600) * 1_000_000)
order = client.place_order(
market.market_id, "buy", "limit", price="0.01", quantity="1",
expiration="good_till_time", expiration_time=in_one_hour,
)
print(order.id, order.status)
client.cancel_order(order.id)

Cancel-on-disconnect takes two halves: join the orders channel with it armed, and place orders with cancel_on_disconnect=True. While the socket is up, the SDK pings the channel at 60% of the timeout the server granted, so your orders stay on the book. If your process dies, the exchange cancels them. The timeout limits and the grace period are on Risk controls.

import asyncio
from stx import AsyncSTX
async def main():
async with AsyncSTX() as client:
market = None
async for m in client.iter_markets(
status="open", trading=True, sort_by="event_start", sort_direction="desc"
):
if m.event_status == "scheduled":
market = m
break
async with client.websocket() as ws:
orders = await ws.orders(cancel_on_disconnect=True, ping_timeout=5000)
print("armed, server timeout (ms):", orders.reply["ping_timeout"])
order = await client.place_order(
market.market_id, "buy", "limit", price="0.01", quantity="1",
cancel_on_disconnect=True,
)
await asyncio.sleep(8) # longer than the timeout: the pings keep it alive
print((await client.order(order.id)).status)
await client.cancel_order(order.id)
asyncio.run(main())
v1.5.9Changelogllms.txtllms-full.txt