Subscribe to Order Channel

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 Required

Your API key must have "Place and Manage Orders" permission enabled. Configure in API Settings →



🚧

Rate Limiting

Order 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

FieldRequiredTypeDescription
messageTypeYesStringMust be order
quoteIdYesStringQuote ID from price stream update
sideYesStringBUY (execute offer) or SELL (execute bid)
clientRequestIdYesStringYour unique identifier for this order
accountGrpUuidConditionalStringAccount group UUID →.
Please check with your Account Manager your Account Group setup
quantityNoStringTrade quantity. Uses subscription quantity if omitted. Must be ≤ quoted quantity. Quantity is in the currency specified on the subscription.
idempotencyKeyNoStringUnique 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:

SideYou're ExecutingYou PayYou Receive
BUYThe offer (ask)AEDUSDC
SELLThe bidUSDCAED

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 idempotencyKey was already processed within the last hour
  • Check your order history using the Get Trades API to verify the original order
  • Use a different idempotencyKey if you need to place a new order

Key Expiration

Keys expire after 1 hour from first use:

Idempotency vs Client Request ID

FieldPurposeMust be Unique?Used For
clientRequestIdTrack order through your systemYes (per order)Correlating responses, querying trade history
idempotencyKeyPrevent duplicate executionYes (per order, 1-hour window)Preventing duplicate orders from retries

Both fields are important:

  • Use clientRequestId to identify and track orders in your system
  • Use idempotencyKey to 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

FieldRequiredTypeDescription
messageTypeYesStringAlways order for successful orders
timestampYesLongExecution time in milliseconds
messageYesStringConfirmation message (empty on success)
codeYesStringError code (empty on success)
instrumentYesStringCurrency pair executed
sideYesStringBUY or SELL
priceYesStringFill price
quantityYesStringFilled quantity in base currency terms
clientRequestIdYesStringYour order identifier
orderStatusYesStringSUCCESS or FAILED
quoteIdYesStringQuote ID that was executed
transactionIdYesStringSystem-generated transaction reference
settleDateYesStringSettlement date in YYYYMMDD format
tenorYesStringSettlement 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 orders

Quote 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 errors

Use 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 orders

Retry 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