POST Place Order
- NT8
- Tradovate
Place an order NT8
Submits a new market, limit, stop, or other supported order to the selected NT8 account.
Supply the full native NT8 instrument name, including the intended contract month and year for futures. Your integration is responsible for contract selection and rollover. Unlike webhook ingestion, this endpoint does not translate continuous symbols such as MNQ1!. See NT8 instrument names.
Endpoint
POST /v1/api/accounts/{account}/orders/place
Headers
| Name | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer <token> |
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
account | string | Required | Name of account in NT8 |
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
instrument | string | Required | Name of underlying instrument (e.g., "ES 12-25") |
action | string | Required | BUY, SELL |
quantity | int | Required | Contract quantity of new order |
orderType | string | Required | MARKET, LIMIT, STOPMARKET, STOPLIMIT |
timeInForce | string | Required | DAY, GTC |
limitPrice | float | Optional | Limit price when submitting limit order type |
stopPrice | float | Optional | Stop price when submitting stop order type |
ocoId | string | Optional | Create or append to OCO order by ID |
strategy | string | Optional | ATM strategy name if opening with ATM template |
orderId | string | Optional | Custom order ID to assign at placement. If provided, this ID can be used to look up or cancel the order later. |
requireMarketPosition | string | Optional | Comma-separated list of position states that must be true for the order to be accepted: "flat", "long", "short". Example: "flat" blocks the order if a position is already open. "flat,long" allows entry from flat or adding to a long. If the condition is not met, a 400 error is returned and no order is placed. |
maxPositions | int | Optional | Maximum number of distinct non-flat positions allowed on the account. A new instrument position is rejected when the account already has this many open positions; adding to an existing instrument position does not increase the count. |
Code examples
- Python
- JavaScript
- cURL
import requests
token = 'my-secret-token'
url = "https://app.crosstrade.io/v1/api/accounts/Sim101/orders/place"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
data = {
"instrument": "MES 12-25",
"action": "BUY",
"orderType": "MARKET",
"quantity": 1,
"timeInForce": "DAY",
"orderId": "my-strategy-entry-001",
# "limitPrice": 5500
# "stopPrice": 0,
# "ocoId": "abc123",
# "strategy": "MyAtmStrategy"
}
try:
response = requests.post(url, headers=headers, json=data)
print(f"Response Code: {response.status_code}, Response Text: {response.text}")
except Exception as e:
print(f"An error occurred: {e}")
const token = 'my-secret-token';
const url = "https://app.crosstrade.io/v1/api/accounts/Sim101/orders/place";
const data = {
instrument: "MES 12-25",
action: "BUY",
orderType: "MARKET",
quantity: 1,
timeInForce: "DAY",
orderId: "my-strategy-entry-001",
// limitPrice: 5500,
// stopPrice: 0,
// ocoId: "abc123",
// strategy: "MyAtmStrategy"
};
fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
TOKEN="my-secret-token"
curl -X POST "https://app.crosstrade.io/v1/api/accounts/Sim101/orders/place" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"instrument": "MES 12-25",
"action": "BUY",
"orderType": "MARKET",
"quantity": 1,
"timeInForce": "DAY",
"orderId": "my-strategy-entry-001"
}'
Response
- 200
- 400
{
"orderId": "cb1fc8d4e1a84d29ae38fea964aaac8c",
"success": true
}
In this example, cb1fc8d4e1a84d29ae38fea964aaac8c is the NT8-assigned order ID returned by the add-on. The custom ID my-strategy-entry-001 remains stored in NT8 UserData under AutomatedTradingOrderId and can also be used for subsequent order lookups.
{
"error": "Invalid request"
}
Platform nuances
orderId behavior
The optional orderId body field is a caller-supplied reconciliation ID, not the same field as NinjaTrader's live Order.OrderId. If you provide orderId, CrossTrade stores it inside the NT8 order's UserData XML as AutomatedTradingOrderId so the order can still be found later by your original ID.
NT8, the connected broker, or a prop firm connection has ultimate control over the actual order ID. That ID can change over the lifetime of an order, so CrossTrade stores your custom ID separately from the current NT8 order ID instead of trying to overwrite NT8's own identifier.
The orderId returned by a successful place response is the current NT8-assigned order ID at placement time. It may be different from the custom orderId you sent. For later lookup, cancel, change, replace, or lifecycle calls, you can use either the returned NT8 ID or the custom orderId you supplied.
If you are retrying after a timeout, dropped connection, or ambiguous placement result, generate a unique custom orderId for the intended order, send it with the first request, then query by that same ID before sending another placement. Do not assume a separate duplicate-suppression window beyond this ID-based reconciliation path.
WebSocket API
This request can also be made over the WebSocket API. The account path parameter and request body fields are all passed inside args.
{
"action": "rpc",
"id": "my-request",
"api": "PlaceOrder",
"args": {
"account": "Sim101",
"instrument": "ES 09-26",
"action": "Buy",
"orderType": "Market",
"quantity": 1,
"timeInForce": "Gtc",
"orderId": "my-strategy-entry-001"
}
}
Place an order Tradovate
Submits a new order to a linked Tradovate account. No NinjaTrader add-on is involved: the call executes server-side against the Tradovate Web API and runs through the same validation and controls as a Tradovate webhook signal, so field validation, Account Manager locks, and Trade Copier fan-out all apply.
Endpoint
POST /v1/api/tv/accounts/{account}/orders/place
Headers
| Name | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer <token> |
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
account | string | Required | Tradovate account name, for example DEMO12345678. |
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
instrument | string | Required | Continuous (ES1!), NT8 (ES 09-26), or Tradovate (ESU6) instrument form. |
action | string | Required | buy or sell. |
qty | int | Required | Contract quantity. |
orderType | string | Required | market, limit, stop, stoplimit, mit, trailingstop, or trailingstoplimit. |
limitPrice | number | Optional | Limit price for an order type that uses one. |
stopPrice | number | Optional | Stop price for an order type that uses one. |
tif | string | Optional | Time in force. |
orderId | string | Optional | Caller-supplied order reference. |
text | string | Optional | Free-form order text. Truncated to 64 characters (Tradovate's limit). |
takeProfit | number | Optional | Absolute target price for a server-side OCO bracket. |
stopLoss | number | Optional | Absolute stop price for a server-side OCO bracket. |
atmTargets | string | Optional | Inline ATM profit-target tiers. |
atmStops | string | Optional | Inline ATM stop tiers. |
atmQtys | string | Optional | Contract quantities for the inline ATM tiers. |
atmTrail | string | Optional | Per-tier trailing-stop toggles. |
atmTrailTrigger | string | Optional | Profit distance in ticks or points that activates trailing tiers. Set with atmTrailOffset. |
atmTrailOffset | string | Optional | Distance in ticks or points the activated stop follows behind price. Set with atmTrailTrigger. |
atmBreakeven | int | Optional | Target tier whose fill moves remaining stops to breakeven. |
atmBreakevenOffset | string | Optional | Breakeven offset for the inline ATM bracket. |
flattenFirst | bool | Optional | Flatten the position and cancel working orders on this instrument before entering. |
requireMarketPosition | string | Optional | Only place the order when the current position matches flat, long, short, or an accepted comma-separated combination. |
maxPositions | int | Optional | Block the entry when this many positions are already open on the account. |
cancelAfter | int | Optional | Cancel an unfilled limit entry after this many minutes. Accepted range: 1 to 180. |
expireTime | string | Optional | ISO-8601 expiry. Required when tif=gtd. |
maxShow | int | Optional | Native iceberg display quantity. |
trailOffset | number | Optional | Native trailing-stop distance. Required for trailing order types. |
pegDifference | number | Optional | Alias of trailOffset. |
syncStrategy | bool | Optional | Enable Tradovate Strategy Sync for this entry command. |
marketPosition | string | Optional | Remote strategy target side: flat, long, or short. |
prevMarketPosition | string | Optional | Remote strategy side before this signal. |
outOfSync | string | Optional | Mismatch policy: wait, flatten, ignore, or resync. |
targetQuantity | int | Optional | Exact signed target quantity for resync. |
strategyExitBlock | bool | Optional | Reject non-opening strategy transitions. |
atmTargets, atmStops, atmQtys, and triggerless atmTrail become a Tradovate-native multibracket. atmTrailTrigger/atmTrailOffset and atmBreakeven are CrossTrade-exclusive management. Triggered trails use CrossTrade's own live pricing, never Tradovate entitlement data; target-fill breakeven is driven by broker fill state. The fixed native stop remains active until trail conversion. Managed fields require a Day entry and have a 24-hour watcher lifetime.
Code examples
- Python
- JavaScript
- cURL
import requests
token = 'my-secret-token'
url = "https://app.crosstrade.io/v1/api/tv/accounts/DEMO12345678/orders/place"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
data = {
"instrument": "ES1!",
"action": "buy",
"qty": 1,
"orderType": "market",
# "limitPrice": 5000.25,
# "stopPrice": 4995.00,
# "takeProfit": 5020.00,
# "stopLoss": 4990.00
}
try:
response = requests.post(url, headers=headers, json=data)
print(f"Response Code: {response.status_code}, Response Text: {response.text}")
except Exception as e:
print(f"An error occurred: {e}")
const token = 'my-secret-token';
const url = "https://app.crosstrade.io/v1/api/tv/accounts/DEMO12345678/orders/place";
const data = {
instrument: "ES1!",
action: "buy",
qty: 1,
orderType: "market",
// limitPrice: 5000.25,
// stopPrice: 4995.00,
// takeProfit: 5020.00,
// stopLoss: 4990.00
};
fetch(url, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
TOKEN="my-secret-token"
curl -X POST "https://app.crosstrade.io/v1/api/tv/accounts/DEMO12345678/orders/place" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"instrument": "ES1!", "action": "buy", "qty": 1, "orderType": "market"}'
Response
Mutations return the dispatcher envelope. A successful ordinary placement includes the Tradovate order id in response.orderId. Inline ATM placement instead returns response.orderStrategyId, not a resolved entry/target/stop order-id map. There is no public REST or MCP strategy-child lookup.
{
"success": true,
"destination": "tradovate",
"api": "place_order",
"account": "DEMO12345678",
"instrument": "ES1!",
"response": {
"orderId": 123456789
},
"durationMs": 214
}
A placement with takeProfit and/or stopLoss is one native Tradovate OSO, and response also carries the child ids: oso1Id and oso2Id in the order the legs were sent (take-profit, then stop-loss), plus osoChildIds as an array.
{
"success": true,
"destination": "tradovate",
"api": "place_with_brackets",
"account": "DEMO12345678",
"instrument": "ES1!",
"response": {
"orderId": 123456789,
"oso1Id": 123456790,
"oso2Id": 123456791,
"osoChildIds": [123456790, 123456791]
},
"durationMs": 231
}
Platform nuances
- Body fields use the same camelCase keys as the NT8 REST surface. Enum values stay lowercase (
buy,market), unlike NT8'sBUY/MARKET. takeProfitandstopLossattach a server-side OCO bracket hosted by Tradovate. The exits are sent GTC (the entry keeps thetimeInForceyou send), their prices are fixed at submission and never recalculated from the fill, and Tradovate activates them on the entry's first fill, sized to that fill. See Order Types and Exits on Tradovate for the activation sequence and what CrossTrade does about later fills. Use the inline ATM fields for a multi-level bracket.- An id in the response means Tradovate accepted the request, not that the entry is resting or filled. For an ordinary order, read
GET .../orders/{id}/statusor the lifecycle to check the outcome; an ATMorderStrategyIdis not an order id for those reads. Late broker rejections are alerted through your configured channels; REST placements have no Alert History row. - For ordinary placement,
orderId(your own tracking id) is forwarded asclOrdIdunless you supplyclOrdIdseparately. For inline ATM placement, CrossTrade storesorderIdas a strategy reference after acceptance; neither it norclOrdIdis submitted as a broker client-order id. See Reconciling an ambiguous placement. - Market entries have no client-defined maximum execution price or slippage allowance. To bound price, use a limit order at your worst acceptable price. Where supported by the broker and order configuration,
tif=iocallows a partial fill and cancels the remainder;tif=fokrequires the full quantity or no fill. Neither guarantees execution. - Account Manager kill and closing-only locks apply. A signal-mode Trade Copier configured on this account fans the order out to its followers.
- On failure, the response is
{"success": false, "error": "<message>"}.
See the Tradovate API overview for the full field grammar, mutation safety guarantees, and error table.