The order management system tracks your positions from creation to closure, providing real-time updates on profit/loss and allowing you to close positions at any time.
Order Lifecycle
Every order goes through these stages:
All open orders are held in-memory in the Engine for instant access and real-time P/L calculations.
Viewing Open Orders
Retrieve all your active positions:
curl -X GET https://api.exness.com/api/v1/trade/open \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"message" : [
{
"orderId" : "550e8400-e29b-41d4-a716-446655440000" ,
"userId" : "user-123" ,
"symbol" : "btc" ,
"type" : "buy" ,
"quantity" : 0.1 ,
"leverage" : 10 ,
"openPrice" : 43000 ,
"currentPrice" : 43500 ,
"openTime" : "2024-03-15T10:30:00.000Z" ,
"takeProfit" : 45000 ,
"stopLoss" : 42000 ,
"status" : "open"
}
]
}
Order Details
Each order includes:
Field Description orderIdUnique identifier for the order symbolTrading pair (btc, eth, sol) typebuy or sell quantityAmount traded leverageLeverage multiplier used openPricePrice when order was created currentPriceLive market price (updated in real-time) openTimeTimestamp when order opened takeProfitAuto-close price for profits stopLossAuto-close price for losses statusCurrent order status
Fetching Open Orders
The backend retrieves orders from the Engine via Redis Streams:
Backend Route
Engine Function
// apps/Backend/src/routes/trade.routes.ts
tradeRouter . get ( "/open" , authMiddleware , async ( req : Request , res : Response ) => {
const userId = req . user ?. id ;
if ( ! userId ) {
return res . status ( 401 ). json ({ error: "Unauthorized" });
}
const RedisStreams = req . app . locals . redisStreams ;
// Request open orders from Engine
const streamResult = await RedisStreams . addToRedisStream (
constant . redisStream ,
{
function: "getOpenOrder" ,
userId
}
);
const requestId = streamResult ?. requestId ;
// Wait for Engine response (5 second timeout)
const result = await RedisStreams . readNextFromRedisStream (
constant . secondaryRedisStream ,
5000 ,
{ requestId }
);
if ( result && result . function === "getOpenOrder" ) {
res . json ({
message: result . message // JSON string of orders
});
} else {
res . json ({
message: JSON . stringify ([])
});
}
});
Open orders are stored in-memory for instant access. The Engine updates current prices every 3 seconds from the Price Poller.
Closing an Order
Close a position to realize profits or losses:
curl -X POST https://api.exness.com/api/v1/trade/close \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"orderId": "550e8400-e29b-41d4-a716-446655440000"
}'
{
"message" : "Order closed successfully" ,
"order" : {
"orderId" : "550e8400-e29b-41d4-a716-446655440000" ,
"openPrice" : 43000 ,
"closePrice" : 43500 ,
"profitLoss" : 50.00 ,
"closeTime" : "2024-03-15T14:30:00.000Z"
}
}
{
"error" : "Order not found"
}
Close Order Processing
When you close an order:
Backend Route
Engine Function
// apps/Backend/src/routes/trade.routes.ts
tradeRouter . post ( "/close" , authMiddleware , async ( req : Request , res : Response ) => {
const { orderId } = req . body ;
const userId = req . user ?. id ;
if ( ! orderId ) {
return res . status ( 400 ). json ({
error: "Missing required parameters: orderId"
});
}
const RedisStreams = req . app . locals . redisStreams ;
// Send close request to Engine
const streamResult = await RedisStreams . addToRedisStream (
constant . redisStream ,
{
function: "createCloseOrder" ,
orderId ,
userId
}
);
const requestId = streamResult ?. requestId ;
// Wait for Engine response
const result = await RedisStreams . readNextFromRedisStream (
constant . secondaryRedisStream ,
5000 ,
{ requestId }
);
if ( result && result . function === "createCloseOrder" ) {
const orderData = JSON . parse ( result . message );
if ( orderData . error ) {
return res . status ( 400 ). json ({ error: orderData . error });
}
res . json ({
message: "Order closed successfully" ,
order: orderData
});
}
});
Profit/Loss Calculation
The platform calculates P/L based on order type:
Buy Orders
// Profit when price increases
profitLoss = ( closePrice - openPrice ) × quantity
// Example: Buy 0.1 BTC at $43,000, close at $43,500
profitLoss = ( 43500 - 43000 ) × 0.1 = $50
Sell Orders
// Profit when price decreases
profitLoss = ( openPrice - closePrice ) × quantity
// Example: Sell 1 ETH at $2,500, close at $2,400
profitLoss = ( 2500 - 2400 ) × 1 = $100
P/L is calculated without accounting for leverage. Your actual return percentage is multiplied by your leverage.
Viewing Closed Orders
Access your trading history:
curl -X GET https://api.exness.com/api/v1/trade/close \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
{
"message" : [
{
"orderId" : "550e8400-e29b-41d4-a716-446655440000" ,
"symbol" : "btc" ,
"type" : "buy" ,
"quantity" : 0.1 ,
"leverage" : 10 ,
"openPrice" : 43000 ,
"closePrice" : 43500 ,
"openTime" : "2024-03-15T10:30:00.000Z" ,
"closeTime" : "2024-03-15T14:30:00.000Z" ,
"profitLoss" : 50.00
}
]
}
Closed orders are persisted to the database by the DBStorage service and can be queried at any time.
Take Profit & Stop Loss
Automate your exit strategy with conditional closes:
Take Profit
Automatically close when price reaches your profit target:
// Buy BTC at $43,000 with take profit at $45,000
{
"symbol" : "BTCUSDT" ,
"type" : "buy" ,
"quantity" : 0.1 ,
"leverage" : 10 ,
"takeProfit" : 45000 // Auto-close when price hits $45,000
}
Stop Loss
Automatically close to limit losses:
// Buy BTC at $43,000 with stop loss at $42,000
{
"symbol" : "BTCUSDT" ,
"type" : "buy" ,
"quantity" : 0.1 ,
"leverage" : 10 ,
"stopLoss" : 42000 // Auto-close when price drops to $42,000
}
Always set both take profit and stop loss to manage risk. A common strategy is to set stop loss at 2% and take profit at 4% for a 2:1 risk-reward ratio.
Order States
State Description Location Open Active position being monitored Engine (in-memory) Closed Position settled, P/L realized Database (DBStorage)
Real-Time Updates
The Engine continuously monitors open orders:
// apps/Engine/src/functions/pricePoller.ts
export function pricePollerFunction ( result : any ) {
const priceUpdates = JSON . parse ( result . message );
// Update global prices array
priceUpdates . forEach (( update : PriceUpdate ) => {
const idx = prices . findIndex (( p ) => p . asset === update . asset );
if ( idx !== - 1 ) {
prices [ idx ] = update ;
} else {
prices . push ( update );
}
});
// Check all open orders for take profit / stop loss triggers
openOrders . forEach (( order ) => {
const priceData = prices . find (( p ) => p . asset === getPriceAssetName ( order . symbol ));
if ( ! priceData ) return ;
const currentPrice = order . type === "buy" ? priceData . bidValue : priceData . askValue ;
// Check take profit
if ( order . takeProfit && currentPrice >= order . takeProfit ) {
createCloseOrderFunction ({ orderId: order . orderId , userId: order . userId });
}
// Check stop loss
if ( order . stopLoss && currentPrice <= order . stopLoss ) {
createCloseOrderFunction ({ orderId: order . orderId , userId: order . userId });
}
});
}
Price updates arrive every 3 seconds. The Engine checks all open orders for take profit and stop loss triggers on each update.
Next Steps
Real-Time Trading Learn how to place trades with live market data
Market Data Access historical data and price charts