Once prices are streaming, a quote can be executed using the order message
Order Execution
Execute trades instantly on live prices from your price stream subscriptions →.
Overview
After subscribing to a price stream, use the quoteId from price updates to execute trades. Orders are filled at the displayed price with immediate confirmation.
Permission RequiredYour API key must have "Place and Manage Orders" permission enabled. Configure in API Settings →
Rate LimitingOrder requests are rate limited to 1 order every 250 milliseconds. Multiple orders requested inside this window will be rejected after the first order.
Order Request
Execute an order using a quote ID from a price stream update →.
{
"messageType": "order",
"quoteId": "YdB4DodjOC5FkBDrYmKLW/Dwnz+x9bVbXyshrwz8yntjA+kZ...",
"side": "BUY",
"clientRequestId": "TESTCLIENT1",
"idempotencyKey": "ORDER-2026-04-20-001-USDC-AED",
"accountGrpUuid": "7687282f-1073-441b-9ff4-694e6b49effe",
"quantity": "100000"
}Request Fields
| Field | Required | Type | Description |
|---|---|---|---|
messageType | Yes | String | Must be order |
quoteId | Yes | String | Quote ID from price stream update |
side | Yes | String | BUY (execute offer) or SELL (execute bid) |
clientRequestId | Yes | String | Your unique identifier for this order |
accountGrpUuid | Conditional | String | Account group UUID →. Please check with your Account Manager your Account Group setup |
quantity | No | String | Trade quantity. Uses subscription quantity if omitted. Must be ≤ quoted quantity. Quantity is in the currency specified on the subscription. |
idempotencyKey | No | String | Unique key to prevent duplicate order execution. Same key re-used within 1 hour will be rejected. |
Understanding Side
When executing on currency pair USDC.AED:
| Side | You're Executing | You Pay | You Receive |
|---|---|---|---|
BUY | The offer (ask) | AED | USDC |
SELL | The bid | USDC | AED |
Rule:
BUY= You buy the base currency (first in pair)SELL= You sell the base currency (first in pair)
Idempotency
Preventing Duplicate Orders
Use the optional idempotencyKey field to ensure orders are executed exactly once, even if network issues cause request retries.
How it works:
- First order with a given key is processed normally
- Subsequent orders with the same key within 1 hour are rejected
- Rejection occurs regardless of the first order's state (success or failure)
- Keys expire after 1 hour
Request Example with Idempotency Key
{
"messageType": "order",
"quoteId": "YdB4DodjOC5FkBDrYmKLW/Dwnz+x9bVbXyshrwz8yntjA+kZ...",
"side": "BUY",
"clientRequestId": "TESTCLIENT1",
"idempotencyKey": "ORDER-2026-04-20-001-USDC-AED",
"accountGrpUuid": "7687282f-1073-441b-9ff4-694e6b49effe",
"quantity": "100000"
}Idempotency Key Error Response
{
"timestamp": 1718113605032,
"messageType": "error",
"message": "Order with this idempotency key already exists",
"clientRequestId": "TESTCLIENT1",
"code": "RFS600031",
"quoteId": "YdB4DodjOC5FkBDrYmKLW/Dwnz+x9bVbXyshrwz8yntjA+kZ...",
"transactionId": ""
}When you receive this error:
- Your order with this
idempotencyKeywas already processed within the last hour - Check your order history using the Get Trades API to verify the original order
- Use a different
idempotencyKeyif you need to place a new order
Key Expiration
Keys expire after 1 hour from first use:
Idempotency vs Client Request ID
| Field | Purpose | Must be Unique? | Used For |
|---|---|---|---|
clientRequestId | Track order through your system | Yes (per order) | Correlating responses, querying trade history |
idempotencyKey | Prevent duplicate execution | Yes (per order, 1-hour window) | Preventing duplicate orders from retries |
Both fields are important:
- Use
clientRequestIdto identify and track orders in your system - Use
idempotencyKeyto prevent accidental duplicate execution - They can be the same value, but don't have to be
Success Response
Immediate confirmation when your order executes successfully.
{
"timestamp": 409937058888162,
"messageType": "order",
"message": "",
"code": "",
"instrument": "USDC.AED",
"side": "BUY",
"price": "3.673050",
"quantity": "100000",
"clientRequestId": "TESTCLIENT1",
"orderStatus": "SUCCESS",
"quoteId": "YdB4DodjOC5FkBDrYmKLW/Dwnz+x9bVbXyshrwz8yntjA+kZ...",
"transactionId": "774722250a694498b916b7bac07bb48e",
"tenor": "T",
"settleDate": "20240811"
}Response Fields
| Field | Required | Type | Description |
|---|---|---|---|
messageType | Yes | String | Always order for successful orders |
timestamp | Yes | Long | Execution time in milliseconds |
message | Yes | String | Confirmation message (empty on success) |
code | Yes | String | Error code (empty on success) |
instrument | Yes | String | Currency pair executed |
side | Yes | String | BUY or SELL |
price | Yes | String | Fill price |
quantity | Yes | String | Filled quantity in base currency terms |
clientRequestId | Yes | String | Your order identifier |
orderStatus | Yes | String | SUCCESS or FAILED |
quoteId | Yes | String | Quote ID that was executed |
transactionId | Yes | String | System-generated transaction reference |
settleDate | Yes | String | Settlement date in YYYYMMDD format |
tenor | Yes | String | Settlement tenor from subscription |
Store the transactionId - use it to track settlement status via REST API.
Error Response
Returns error details when order execution fails.
{
"timestamp": 1718113605032,
"messageType": "error",
"message": "Please contact your desk support.",
"clientRequestId": "TESTCLIENT1",
"code": "RFS500001",
"quoteId": "YdB4DodjOC5FkBDrYmKLW/Dwnz+x9bVbXyshrwz8yntjA+kZ...",
"transactionId": ""
}Usage Examples
Basic Order Execution
import json
def execute_order(ws, quote_id, side, client_request_id):
"""Execute order on price stream quote"""
order = {
'messageType': 'order',
'quoteId': quote_id,
'side': side,
'clientRequestId': client_request_id
}
ws.send(json.dumps(order))
print(f"Sent {side} order: {client_request_id}")
# Handle price update and execute
def on_price_update(ws, msg):
if msg['messageType'] == 'pricestream':
# Check if price meets criteria
bid = float(msg['bid']['price'])
offer = float(msg['offer']['price'])
if should_buy(offer):
execute_order(
ws,
msg['quoteId'],
'BUY',
f"ORDER-{int(time.time())}"
)
# Handle order response
def on_order_response(ws, msg):
if msg['messageType'] == 'order':
if msg['orderStatus'] == 'SUCCESS':
print(f"✅ Order filled!")
print(f" Price: {msg['price']}")
print(f" Quantity: {msg['quantity']}")
print(f" Transaction ID: {msg['transactionId']}")
else:
print(f"❌ Order failed: {msg['message']}")
elif msg['messageType'] == 'error':
print(f"❌ Error {msg['code']}: {msg['message']}")function executeOrder(ws, quoteId, side, clientRequestId) {
const order = {
messageType: 'order',
quoteId: quoteId,
side: side,
clientRequestId: clientRequestId
};
ws.send(JSON.stringify(order));
console.log(`Sent ${side} order: ${clientRequestId}`);
}
// Handle price update and execute
function onPriceUpdate(ws, msg) {
if (msg.messageType === 'pricestream') {
const bid = parseFloat(msg.bid.price);
const offer = parseFloat(msg.offer.price);
if (shouldBuy(offer)) {
executeOrder(
ws,
msg.quoteId,
'BUY',
`ORDER-${Date.now()}`
);
}
}
}
// Handle order response
function onOrderResponse(ws, msg) {
if (msg.messageType === 'order') {
if (msg.orderStatus === 'SUCCESS') {
console.log('✅ Order filled!');
console.log(` Price: ${msg.price}`);
console.log(` Quantity: ${msg.quantity}`);
console.log(` Transaction ID: ${msg.transactionId}`);
} else {
console.error(`❌ Order failed: ${msg.message}`);
}
}
else if (msg.messageType === 'error') {
console.error(`❌ Error ${msg.code}: ${msg.message}`);
}
}Complete Trading Flow
import json
import time
# Track orders
pending_orders = {}
def handle_message(ws, message):
"""Complete order flow handler"""
msg = json.loads(message)
# 1. Receive price update
if msg['messageType'] == 'pricestream':
quote_id = msg['quoteId']
offer = float(msg['offer']['price'])
bid = float(msg['bid']['price'])
print(f"Price: Bid {bid}, Offer {offer}")
# 2. Decide to trade
if should_execute_buy(offer):
client_id = f"ORDER-{int(time.time())}"
# 3. Send order
order = {
'messageType': 'order',
'quoteId': quote_id,
'side': 'BUY',
'clientRequestId': client_id
}
ws.send(json.dumps(order))
# Track order
pending_orders[client_id] = {
'quote_id': quote_id,
'side': 'BUY',
'sent_at': time.time()
}
# 4. Handle order response
elif msg['messageType'] == 'order':
client_id = msg['clientRequestId']
if msg['orderStatus'] == 'SUCCESS':
print(f"✅ Order {client_id} filled")
print(f" {msg['side']} {msg['quantity']} {msg['instrument']}")
print(f" Price: {msg['price']}")
print(f" Transaction: {msg['transactionId']}")
# Remove from pending
pending_orders.pop(client_id, None)
else:
print(f"❌ Order {client_id} failed: {msg['message']}")
pending_orders.pop(client_id, None)
# 5. Handle errors
elif msg['messageType'] == 'error':
client_id = msg.get('clientRequestId')
print(f"❌ Error {msg['code']}: {msg['message']}")
if client_id:
pending_orders.pop(client_id, None)
def should_execute_buy(price):
"""Your trading logic here"""
return price < 3.67 # Example threshold
# Check for stale orders
def check_pending_orders():
"""Alert on orders that haven't confirmed"""
now = time.time()
for client_id, order in pending_orders.items():
age = now - order['sent_at']
if age > 5: # 5 seconds timeout
print(f"⚠️ Order {client_id} pending for {age:.1f}s")// Track orders
const pendingOrders = {};
function handleMessage(ws, message) {
const msg = JSON.parse(message);
// 1. Receive price update
if (msg.messageType === 'pricestream') {
const quoteId = msg.quoteId;
const offer = parseFloat(msg.offer.price);
const bid = parseFloat(msg.bid.price);
console.log(`Price: Bid ${bid}, Offer ${offer}`);
// 2. Decide to trade
if (shouldExecuteBuy(offer)) {
const clientId = `ORDER-${Date.now()}`;
// 3. Send order
const order = {
messageType: 'order',
quoteId: quoteId,
side: 'BUY',
clientRequestId: clientId
};
ws.send(JSON.stringify(order));
// Track order
pendingOrders[clientId] = {
quoteId: quoteId,
side: 'BUY',
sentAt: Date.now()
};
}
}
// 4. Handle order response
else if (msg.messageType === 'order') {
const clientId = msg.clientRequestId;
if (msg.orderStatus === 'SUCCESS') {
console.log(`✅ Order ${clientId} filled`);
console.log(` ${msg.side} ${msg.quantity} ${msg.instrument}`);
console.log(` Price: ${msg.price}`);
console.log(` Transaction: ${msg.transactionId}`);
delete pendingOrders[clientId];
} else {
console.error(`❌ Order ${clientId} failed: ${msg.message}`);
delete pendingOrders[clientId];
}
}
// 5. Handle errors
else if (msg.messageType === 'error') {
const clientId = msg.clientRequestId;
console.error(`❌ Error ${msg.code}: ${msg.message}`);
if (clientId) {
delete pendingOrders[clientId];
}
}
}
function shouldExecuteBuy(price) {
return price < 3.67; // Example threshold
}Best Practices
Client Request IDs
# ✅ Good - Unique, traceable IDs
import uuid
import time
def generate_order_id():
# Timestamp + UUID
return f"ORD-{int(time.time())}-{str(uuid.uuid4())[:8]}"
client_id = generate_order_id()
# Example: "ORD-1737552000-a1b2c3d4"
# ❌ Bad - Non-unique IDs
client_id = "ORDER1" # Reused across ordersQuote Freshness
# ✅ Good - Use latest quote
def on_price_update(msg):
current_quote = msg['quoteId']
# Use immediately
execute_order(current_quote, 'BUY', ...)
# ❌ Bad - Stale quotes
stored_quote = None
def on_price_update(msg):
global stored_quote
stored_quote = msg['quoteId'] # Storing for later
def execute_later():
# Quote likely expired
execute_order(stored_quote, 'BUY', ...)Error Handling
# ✅ Good - Handle all response types
def handle_order_response(msg):
if msg['messageType'] == 'error':
handle_error(msg)
elif msg['orderStatus'] == 'FAILED':
handle_failed_order(msg)
elif msg['orderStatus'] == 'SUCCESS':
handle_successful_order(msg)
# ❌ Bad - Only checking success
def handle_order_response(msg):
if msg['orderStatus'] == 'SUCCESS':
process_fill(msg)
# Ignoring failures and errorsUse Idempotency Keys
import uuid
import time
from datetime import datetime
# ✅ Good - Generate unique idempotency keys
def generate_idempotency_key(instrument, side):
"""Generate unique idempotency key for each order"""
timestamp = datetime.utcnow().strftime('%Y%m%d-%H%M%S')
unique_id = str(uuid.uuid4())[:8]
return f"ORDER-{timestamp}-{instrument}-{side}-{unique_id}"
# Usage
idempotency_key = generate_idempotency_key('USDC.AED', 'BUY')
# Example: "ORDER-20260420-102535-USDC.AED-BUY-a1b2c3d4"
order = {
'messageType': 'order',
'quoteId': quote_id,
'side': 'BUY',
'clientRequestId': 'ORDER-123',
'idempotencyKey': idempotency_key
}
# ❌ Bad - Reusing same key
idempotency_key = "MY-ORDER-KEY" # Same for all ordersRetry Logic with Idempotency
import time
def execute_order_with_retry(ws, quote_id, side, max_retries=3):
"""Execute order with automatic retry on network errors"""
# Generate once - same key for all retries
client_id = f"ORDER-{int(time.time())}"
idempotency_key = f"IDEMPOTENT-{client_id}"
order = {
'messageType': 'order',
'quoteId': quote_id,
'side': side,
'clientRequestId': client_id,
'idempotencyKey': idempotency_key
}
for attempt in range(max_retries):
try:
ws.send(json.dumps(order))
print(f"Order sent (attempt {attempt + 1})")
# Wait for response
response = wait_for_order_response(client_id, timeout=5)
if response:
if response['orderStatus'] == 'SUCCESS':
print(f"✅ Order filled: {response['transactionId']}")
return response
elif response['code'] == 'RFS600031':
# Duplicate - order already processed
print(f"⚠️ Order already executed with this idempotency key")
# Check order history to get original order
return get_order_from_history(idempotency_key)
else:
print(f"❌ Order failed: {response['message']}")
return response
except ConnectionError as e:
print(f"Connection error on attempt {attempt + 1}: {e}")
if attempt < max_retries - 1:
time.sleep(1) # Wait before retry
else:
raise
return None