Orders
The Order endpoints let you place new trades, query existing orders, modify pending orders, and cancel orders that have not yet been filled. These endpoints are at the core of every trading strategy.
Order types
TestMax supports four order types, matching the TopStep ProjectX API:
| Value | Type | Description | Required price fields |
|---|---|---|---|
1 | Limit | Executes at the specified price or better | limitPrice |
2 | Market | Executes immediately at the current market price | None |
3 | Stop Limit | Becomes a limit order when the stop price is reached | stopPrice and limitPrice |
4 | Stop | Becomes a market order when the stop price is reached | stopPrice |
Order sides
| Value | Side | Description |
|---|---|---|
0 | Buy | Buy to open a long position or close a short position |
1 | Sell | Sell to open a short position or close a long position |
POST Order/place
Place a new order. This is the primary endpoint for entering and exiting trades.
URL: POST /api/topstep-sim/Order/place
Authentication: Bearer token required.
Request body
{ "accountId": 12345, "contractId": "CON.F.US.ENQ.H25", "type": 2, "side": 0, "size": 1, "limitPrice": null, "stopPrice": null}| Field | Type | Required | Description |
|---|---|---|---|
accountId | number | Yes | Account ID from Account/search |
contractId | string | Yes | Contract ID from Contract/search |
type | number | Yes | Order type: 1 = Limit, 2 = Market, 3 = StopLimit, 4 = Stop |
side | number | Yes | 0 = Buy, 1 = Sell |
size | number | Yes | Number of contracts to trade (must be > 0) |
limitPrice | number or null | Conditional | Required for Limit (1) and StopLimit (3) orders |
stopPrice | number or null | Conditional | Required for Stop (4) and StopLimit (3) orders |
Response
{ "success": true, "errorCode": 0, "errorMessage": null, "orderId": "ord_abc123", "orderStatus": 2}| Field | Type | Description |
|---|---|---|
orderId | string | Unique identifier for the created order |
orderStatus | number | Current order status: 1 = Partial, 2 = Filled, 3 = Canceled, 6 = Pending |
Examples
# Buy 1 contract at market priceresult = api("Order/place", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "type": 2, # Market "side": 0, # Buy "size": 1})
if result["success"]: print(f"Order filled: {result['orderId']}")# Buy 1 contract at a limit price of 21500.00result = api("Order/place", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "type": 1, # Limit "side": 0, # Buy "size": 1, "limitPrice": 21500.00})
if result["success"]: order_id = result["orderId"] status = result["orderStatus"] if status == 6: print(f"Limit order pending: {order_id}") elif status == 2: print(f"Limit order immediately filled: {order_id}")# Place a stop-loss: sell 1 contract if price drops to 21480.00result = api("Order/place", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "type": 4, # Stop "side": 1, # Sell "size": 1, "stopPrice": 21480.00})
if result["success"]: stop_order_id = result["orderId"] print(f"Stop order placed: {stop_order_id}")# Stop-limit: when price reaches 21520, place a limit buy at 21525result = api("Order/place", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "type": 3, # StopLimit "side": 0, # Buy "size": 1, "stopPrice": 21520.00, "limitPrice": 21525.00})Error responses
| Scenario | errorMessage |
|---|---|
Missing limitPrice on limit order | "limitPrice is required for Limit orders" |
Missing stopPrice on stop order | "stopPrice is required for Stop orders" |
Invalid accountId | "Account not found" |
Invalid contractId | "Contract not found" |
| Size is 0 or negative | "Size must be greater than 0" |
POST Order/search
Search all orders for an account, including filled, pending, and canceled orders.
URL: POST /api/topstep-sim/Order/search
Authentication: Bearer token required.
Request body
{ "accountId": 12345}| Field | Type | Required | Description |
|---|---|---|---|
accountId | number | Yes | Account ID |
Response
{ "success": true, "errorCode": 0, "errorMessage": null, "orders": [ { "id": "ord_abc123", "accountId": 12345, "contractId": "CON.F.US.ENQ.H25", "type": 2, "side": 0, "size": 1, "limitPrice": null, "stopPrice": null, "status": 2, "fillPrice": 21503.25, "filledSize": 1, "createdAt": "2025-01-15T14:30:05Z" } ]}| Field | Type | Description |
|---|---|---|
orders | array | List of all order objects |
orders[].id | string | Order ID |
orders[].accountId | number | Account ID |
orders[].contractId | string | Contract ID |
orders[].type | number | Order type (1=Limit, 2=Market, 3=StopLimit, 4=Stop) |
orders[].side | number | Order side (0=Buy, 1=Sell) |
orders[].size | number | Requested order size |
orders[].limitPrice | number or null | Limit price, if set |
orders[].stopPrice | number or null | Stop price, if set |
orders[].status | number | Order status (1=Partial, 2=Filled, 3=Canceled, 6=Pending) |
orders[].fillPrice | number or null | Average fill price, if filled |
orders[].filledSize | number | Number of contracts filled so far |
orders[].createdAt | string | ISO 8601 timestamp of when the order was created |
Example
result = api("Order/search", {"accountId": ACCOUNT_ID})
if result["success"]: for order in result.get("orders", []): status_map = {1: "Partial", 2: "Filled", 3: "Canceled", 6: "Pending"} side_str = "BUY" if order["side"] == 0 else "SELL" status_str = status_map.get(order["status"], "Unknown") print(f"{side_str} {order['size']} — Status: {status_str}")POST Order/searchOpen
Get only open/pending orders — orders that have not yet been fully filled or canceled.
URL: POST /api/topstep-sim/Order/searchOpen
Authentication: Bearer token required.
Request body
{ "accountId": 12345}| Field | Type | Required | Description |
|---|---|---|---|
accountId | number | Yes | Account ID |
Response
Same format as Order/search, but only includes orders with status of 6 (Pending) or 1 (Partial).
Example
# Check for unfilled limit ordersresult = api("Order/searchOpen", {"accountId": ACCOUNT_ID})
open_orders = result.get("orders", [])print(f"{len(open_orders)} open orders")
# Cancel all open orders before placing new onesfor order in open_orders: api("Order/cancel", {"orderId": order["id"]})POST Order/cancel
Cancel a pending order. Only works on orders that have not yet been fully filled.
URL: POST /api/topstep-sim/Order/cancel
Authentication: Bearer token required.
Request body
{ "orderId": "ord_abc123"}| Field | Type | Required | Description |
|---|---|---|---|
orderId | string | Yes | The ID of the order to cancel |
Response
{ "success": true, "errorCode": 0, "errorMessage": null}Example
# Cancel a specific stop-loss orderresult = api("Order/cancel", {"orderId": stop_order_id})
if result["success"]: print(f"Order {stop_order_id} canceled")else: print(f"Cancel failed: {result['errorMessage']}")Error responses
| Scenario | errorMessage |
|---|---|
| Order already filled | "Order is already filled" |
| Order already canceled | "Order is already canceled" |
| Order not found | "Order not found" |
POST Order/modify
Modify a pending order’s size, limit price, or stop price. Only works on orders that have not yet been fully filled.
URL: POST /api/topstep-sim/Order/modify
Authentication: Bearer token required.
Request body
{ "orderId": "ord_abc123", "size": 2, "limitPrice": 21510.00, "stopPrice": null}| Field | Type | Required | Description |
|---|---|---|---|
orderId | string | Yes | The ID of the order to modify |
size | number or null | No | New order size. Pass null to keep unchanged. |
limitPrice | number or null | No | New limit price. Pass null to keep unchanged. |
stopPrice | number or null | No | New stop price. Pass null to keep unchanged. |
Response
{ "success": true, "errorCode": 0, "errorMessage": null}Example
# Move a stop-loss order to a new price (trail the stop)result = api("Order/modify", { "orderId": stop_order_id, "stopPrice": 21495.00 # Move stop up to lock in profit})
if result["success"]: print("Stop moved to 21495.00")Common patterns
Bracket order (entry + stop-loss + take-profit)
# 1. Enter with a market buyentry = api("Order/place", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "type": 2, "side": 0, "size": 1})
# 2. Place a stop-lossstop = api("Order/place", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "type": 4, # Stop "side": 1, # Sell to close "size": 1, "stopPrice": entry_price - 20 # 20 points risk})
# 3. Place a take-profittp = api("Order/place", { "accountId": ACCOUNT_ID, "contractId": CONTRACT_ID, "type": 1, # Limit "side": 1, # Sell to close "size": 1, "limitPrice": entry_price + 40 # 40 points target (2:1 R:R)})
stop_id = stop["orderId"]tp_id = tp["orderId"]
# 4. When one fills, cancel the other (in your main loop)positions = get_positions(ACCOUNT_ID)if len(positions) == 0: # Position was closed — cancel remaining order api("Order/cancel", {"orderId": stop_id}) api("Order/cancel", {"orderId": tp_id})Cancel all open orders
result = api("Order/searchOpen", {"accountId": ACCOUNT_ID})for order in result.get("orders", []): api("Order/cancel", {"orderId": order["id"]})print("All open orders canceled")Trailing stop
# Inside your main loop, after checking bar data:if position and bar["h"] > highest_high: highest_high = bar["h"] new_stop = highest_high - trail_distance
api("Order/modify", { "orderId": stop_order_id, "stopPrice": new_stop }) print(f"[INFO] Trailing stop moved to {new_stop}")