Why Order Management Functions Are the Core of EA Development
Order management functions are the most critical components of any Expert Advisor. They execute the actual trades based on your strategy's signals. Without mastering OrderSend, OrderModify, and OrderSelect, your EA cannot translate market analysis into real trading actions. These functions form the bridge between your trading logic and the broker's execution system.
Complete Order Management Functions Reference Table
| Function | Purpose | Return Value | Key Parameters |
|----------|---------|--------------|----------------|
| OrderSend() | Open market order or place pending order | Ticket number (int) or -1 on error | symbol, cmd, volume, price, slippage, stoploss, takeprofit, comment, magic, expiration |
| OrderModify() | Modify stop loss, take profit, or pending order price | bool (true/false) | ticket, price, stoploss, takeprofit, expiration |
| OrderSelect() | Select an order for further processing | bool (true/false) | index, select, pool |
1. OrderSelect() - The Gateway to Order Information
Before you can read or modify any order, you must select it using OrderSelect(). This function copies order data into the program environment for subsequent functions like OrderStopLoss(), OrderTakeProfit(), OrderOpenPrice(), and OrderModify() .
OrderSelect Syntax and Parameters
```mql4
bool OrderSelect(int index, int select, int pool = MODE_TRADES);
```
Parameter Reference Table:
| Parameter | Values | Description |
|-----------|--------|-------------|
| index | Order index or ticket number | Depends on select parameter |
| select | SELECT_BY_POS (0) or SELECT_BY_TICKET (1) | Selection method |
| pool | MODE_TRADES (0) or MODE_HISTORY (1) | Order pool to search (ignored for SELECT_BY_TICKET) |
OrderSelect Usage Examples
```mql4
// Method 1: Select by position in trading pool
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
// Now you can access order properties
Print("Order ticket: ", OrderTicket());
Print("Order type: ", OrderType());
Print("Open price: ", OrderOpenPrice());
}
}
// Method 2: Select by ticket number (most reliable)
int targetTicket = 12345;
if(OrderSelect(targetTicket, SELECT_BY_TICKET)) {
Print("Found order #", targetTicket);
Print("Profit: ", OrderProfit());
Print("Stop Loss: ", OrderStopLoss());
} else {
Print("Order not found. Error: ", GetLastError());
}
// Method 3: Select from history pool (closed orders)
for(int i = 0; i < OrdersHistoryTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
Print("Closed order ticket: ", OrderTicket());
Print("Close price: ", OrderClosePrice());
Print("Close time: ", OrderCloseTime());
}
}
// Complete order information retrieval
void GetOrderInfo(int ticket) {
if(OrderSelect(ticket, SELECT_BY_TICKET)) {
Print("========== ORDER INFORMATION ==========");
Print("Ticket: ", OrderTicket());
Print("Symbol: ", OrderSymbol());
Print("Type: ", OrderType());
Print("Lots: ", OrderLots());
Print("Open Price: ", OrderOpenPrice());
Print("Open Time: ", OrderOpenTime());
Print("Stop Loss: ", OrderStopLoss());
Print("Take Profit: ", OrderTakeProfit());
Print("Profit: ", OrderProfit());
Print("Swap: ", OrderSwap());
Print("Commission: ", OrderCommission());
Print("Comment: ", OrderComment());
Print("Magic Number: ", OrderMagicNumber());
if(OrderCloseTime() > 0) {
Print("Close Price: ", OrderClosePrice());
Print("Close Time: ", OrderCloseTime());
}
Print("========================================");
}
}
// Best practice: Always call OrderSelect before accessing order data
double GetOrderStopLoss(int ticket) {
if(OrderSelect(ticket, SELECT_BY_TICKET)) {
return OrderStopLoss();
}
return 0; // Return 0 if selection fails
}
```
2. OrderSend() - Opening and Placing Orders
OrderSend is the main function for opening market orders (buy/sell) and placing pending orders. It sends a trade request to the server and returns the ticket number if successful .
OrderSend Syntax and Parameters
```mql4
int OrderSend(
string symbol, // Trading symbol
int cmd, // Operation type
double volume, // Number of lots
double price, // Order price
int slippage, // Maximum price slippage
double stoploss, // Stop loss level
double takeprofit, // Take profit level
string comment = NULL, // Order comment
int magic = 0, // Magic number
datetime expiration = 0, // Pending order expiration
color arrow_color = clrNONE // Arrow color on chart
);
```
Operation Types (cmd parameter)
| Constant | Value | Description |
|----------|-------|-------------|
| OP_BUY | 0 | Buy market order |
| OP_SELL | 1 | Sell market order |
| OP_BUYLIMIT | 2 | Buy limit pending order |
| OP_SELLLIMIT | 3 | Sell limit pending order |
| OP_BUYSTOP | 4 | Buy stop pending order |
| OP_SELLSTOP | 5 | Sell stop pending order |
Opening Market Orders - Complete Example
```mql4
// Function to open a market buy order with complete validation
int OpenBuyOrder(double lotSize, double stopLossPoints, double takeProfitPoints, int magic) {
// Validate trading conditions
if(!IsTradeAllowed()) {
Print("Trading not allowed");
return -1;
}
// Get current market prices
double price = Ask;
double bid = Bid;
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
// Calculate and normalize stop loss and take profit
double stopLoss = 0;
double takeProfit = 0;
if(stopLossPoints > 0) {
stopLoss = NormalizeDouble(price - stopLossPoints * Point(), digits);
// Check minimum distance requirement
if((price - stopLoss) / Point() < stopLevel) {
Print("Stop loss too close. Minimum: ", stopLevel);
stopLoss = NormalizeDouble(price - stopLevel * Point(), digits);
}
}
if(takeProfitPoints > 0) {
takeProfit = NormalizeDouble(price + takeProfitPoints * Point(), digits);
}
// Validate price normalization
if(price != NormalizeDouble(price, digits)) {
Print("Price not normalized");
return -1;
}
// Send the order
int ticket = OrderSend(
Symbol(), // symbol
OP_BUY, // operation
lotSize, // volume
price, // price
30, // slippage
stopLoss, // stop loss
takeProfit, // take profit
"EA Buy Order", // comment
magic, // magic number
0, // expiration (0 for market orders)
clrGreen // arrow color
);
// Handle result
if(ticket < 0) {
int error = GetLastError();
Print("OrderSend failed. Error: ", error);
HandleOrderError(error);
} else {
Print("Buy order opened. Ticket: ", ticket);
Print("Price: ", price, " SL: ", stopLoss, " TP: ", takeProfit);
}
return ticket;
}
// Opening a sell market order
int OpenSellOrder(double lotSize, double stopLossPoints, double takeProfitPoints, int magic) {
double price = Bid;
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
double stopLoss = 0;
double takeProfit = 0;
if(stopLossPoints > 0) {
stopLoss = NormalizeDouble(price + stopLossPoints * Point(), digits);
if((stopLoss - price) / Point() < stopLevel) {
stopLoss = NormalizeDouble(price + stopLevel * Point(), digits);
}
}
if(takeProfitPoints > 0) {
takeProfit = NormalizeDouble(price - takeProfitPoints * Point(), digits);
}
int ticket = OrderSend(
Symbol(),
OP_SELL,
lotSize,
price,
30,
stopLoss,
takeProfit,
"EA Sell Order",
magic,
0,
clrRed
);
if(ticket < 0) {
Print("Sell order failed. Error: ", GetLastError());
}
return ticket;
}
```
Placing Pending Orders
```mql4
// Place a buy limit order
int PlaceBuyLimit(double limitPrice, double lotSize, double stopLossPoints, double takeProfitPoints, int magic, datetime expiration = 0) {
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
// Validate pending order distance
double currentPrice = Ask;
if((currentPrice - limitPrice) / Point() < stopLevel) {
Print("Limit price too close to market. Minimum: ", stopLevel);
return -1;
}
double normalizedPrice = NormalizeDouble(limitPrice, digits);
double stopLoss = 0;
double takeProfit = 0;
if(stopLossPoints > 0) {
stopLoss = NormalizeDouble(limitPrice - stopLossPoints * Point(), digits);
}
if(takeProfitPoints > 0) {
takeProfit = NormalizeDouble(limitPrice + takeProfitPoints * Point(), digits);
}
int ticket = OrderSend(
Symbol(),
OP_BUYLIMIT,
lotSize,
normalizedPrice,
0, // slippage ignored for pending orders
stopLoss,
takeProfit,
"Buy Limit EA",
magic,
expiration,
clrBlue
);
return ticket;
}
// Place a sell stop order
int PlaceSellStop(double stopPrice, double lotSize, double stopLossPoints, double takeProfitPoints, int magic, datetime expiration = 0) {
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
if((stopPrice - Bid) / Point() < stopLevel) {
Print("Stop price too close to market");
return -1;
}
double normalizedPrice = NormalizeDouble(stopPrice, digits);
int ticket = OrderSend(
Symbol(),
OP_SELLSTOP,
lotSize,
normalizedPrice,
0,
0, // stop loss can be set later
0, // take profit can be set later
"Sell Stop EA",
magic,
expiration,
clrOrange
);
return ticket;
}
```
3. OrderModify() - Modifying Stop Loss, Take Profit, and Pending Orders
OrderModify changes the parameters of existing orders. For market orders, you can modify stop loss and take profit levels. For pending orders, you can also modify the open price and expiration time .
OrderModify Syntax
```mql4
bool OrderModify(
int ticket, // Order ticket number
double price, // New open price (pending orders only)
double stoploss, // New stop loss level
double takeprofit, // New take profit level
datetime expiration, // New expiration time (pending orders only)
color arrow_color = clrNONE // Arrow color on chart
);
```
Trailing Stop Implementation with OrderModify
```mql4
// Professional trailing stop function
bool ApplyTrailingStop(int ticket, int trailingPoints) {
if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
Print("OrderSelect failed for ticket ", ticket);
return false;
}
// Only process market orders
int orderType = OrderType();
if(orderType > OP_SELL) {
return false; // Skip pending orders
}
double openPrice = OrderOpenPrice();
double currentStop = OrderStopLoss();
double currentPrice = (orderType == OP_BUY) ? Bid : Ask;
double newStop = 0;
// Calculate new stop loss level
if(orderType == OP_BUY) {
double profitPoints = (currentPrice - openPrice) / Point();
if(profitPoints > trailingPoints) {
newStop = NormalizeDouble(currentPrice - trailingPoints * Point(), Digits);
}
} else { // OP_SELL
double profitPoints = (openPrice - currentPrice) / Point();
if(profitPoints > trailingPoints) {
newStop = NormalizeDouble(currentPrice + trailingPoints * Point(), Digits);
}
}
// Check if modification is needed
if(newStop > 0 && newStop != currentStop) {
// Validate minimum distance
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
if(orderType == OP_BUY && (currentPrice - newStop) / Point() < stopLevel) {
newStop = NormalizeDouble(currentPrice - stopLevel * Point(), Digits);
}
if(orderType == OP_SELL && (newStop - currentPrice) / Point() < stopLevel) {
newStop = NormalizeDouble(currentPrice + stopLevel * Point(), Digits);
}
// Execute modification
bool result = OrderModify(
ticket,
OrderOpenPrice(),
newStop,
OrderTakeProfit(),
0, // No expiration change
clrYellow
);
if(result) {
Print("Trailing stop updated. Ticket: ", ticket, " New SL: ", newStop);
} else {
Print("Trailing stop failed. Error: ", GetLastError());
}
return result;
}
return false;
}
// Modify stop loss and take profit simultaneously
bool ModifyStopLossTakeProfit(int ticket, double newStopLoss, double newTakeProfit) {
if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
return false;
}
double openPrice = OrderOpenPrice();
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
int orderType = OrderType();
// Validate stop loss distance
if(orderType == OP_BUY) {
if(newStopLoss > 0 && (openPrice - newStopLoss) / Point() < stopLevel) {
Print("Stop loss too close. Adjusted to minimum distance.");
newStopLoss = NormalizeDouble(openPrice - stopLevel * Point(), digits);
}
} else {
if(newStopLoss > 0 && (newStopLoss - openPrice) / Point() < stopLevel) {
Print("Stop loss too close. Adjusted to minimum distance.");
newStopLoss = NormalizeDouble(openPrice + stopLevel * Point(), digits);
}
}
bool result = OrderModify(
ticket,
openPrice,
newStopLoss,
newTakeProfit,
0,
clrBlue
);
if(!result) {
Print("Modify failed. Error: ", GetLastError());
}
return result;
}
// Modify pending order price
bool ModifyPendingOrderPrice(int ticket, double newPrice) {
if(!OrderSelect(ticket, SELECT_BY_TICKET)) {
return false;
}
// Verify it's a pending order
int orderType = OrderType();
if(orderType < OP_BUYLIMIT) {
Print("Cannot modify price of market order");
return false;
}
double normalizedPrice = NormalizeDouble(newPrice, Digits);
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
// Validate distance from market
if(orderType == OP_BUYLIMIT || orderType == OP_BUYSTOP) {
if((normalizedPrice - Ask) / Point() < stopLevel) {
Print("New price too close to market");
return false;
}
} else {
if((Bid - normalizedPrice) / Point() < stopLevel) {
Print("New price too close to market");
return false;
}
}
return OrderModify(
ticket,
normalizedPrice,
OrderStopLoss(),
OrderTakeProfit(),
OrderExpiration(),
clrPurple
);
}
```
4. Complete Order Management System with Error Handling
```mql4
//+------------------------------------------------------------------+
//| Complete Order Management System |
//+------------------------------------------------------------------+
class OrderManager {
private:
int magicNumber;
void HandleError(int errorCode) {
switch(errorCode) {
case 1: Print("ERR_NO_RESULT - No result returned"); break;
case 2: Print("ERR_COMMON_ERROR - Common error"); break;
case 129: Print("ERR_INVALID_PRICE - Invalid price"); break;
case 130: Print("ERR_INVALID_STOPS - Invalid stops (too close)"); break;
case 134: Print("ERR_NOT_ENOUGH_MONEY - Not enough money"); break;
case 138: Print("ERR_REQUOTE - Requote"); break;
case 145: Print("ERR_TRADE_MODIFY_DENIED - Modification denied"); break;
case 146: Print("ERR_TRADE_CONTEXT_BUSY - Trading context busy"); break;
case 147: Print("ERR_TRADE_EXPIRATION_DENIED - Expiration denied"); break;
case 148: Print("ERR_TRADE_TOO_MANY_ORDERS - Too many orders"); break;
case 149: Print("ERR_TRADE_HEDGE_PROHIBITED - Hedge prohibited"); break;
case 4108: Print("ERR_TRADE_POSITION_NOT_FOUND - Position not found"); break;
default: Print("Unknown error: ", errorCode);
}
}
public:
OrderManager(int magic) { magicNumber = magic; }
// Open market order with retry mechanism
int OpenMarketOrder(int orderType, double lotSize, double slPoints, double tpPoints) {
int maxRetries = 3;
int retryCount = 0;
while(retryCount < maxRetries) {
RefreshRates(); // Update market prices
double price = (orderType == OP_BUY) ? Ask : Bid;
int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
int stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);
double sl = 0, tp = 0;
if(slPoints > 0) {
if(orderType == OP_BUY) {
sl = NormalizeDouble(price - MathMax(slPoints, stopLevel) * Point(), digits);
} else {
sl = NormalizeDouble(price + MathMax(slPoints, stopLevel) * Point(), digits);
}
}
if(tpPoints > 0) {
if(orderType == OP_BUY) {
tp = NormalizeDouble(price + tpPoints * Point(), digits);
} else {
tp = NormalizeDouble(price - tpPoints * Point(), digits);
}
}
int ticket = OrderSend(
Symbol(), orderType, lotSize, price, 30,
sl, tp, "OrderMgr", magicNumber, 0, clrNONE
);
if(ticket > 0) {
Print("Order opened: ", ticket);
return ticket;
}
int error = GetLastError();
HandleError(error);
// Don't retry for fatal errors
if(error == 134 || error == 148) break;
retryCount++;
Sleep(1000 * retryCount);
}
return -1;
}
// Close market order
bool CloseOrder(int ticket) {
if(!OrderSelect(ticket, SELECT_BY_TICKET)) return false;
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
bool result = OrderClose(ticket, OrderLots(), closePrice, 30, clrRed);
if(!result) {
Print("Close failed. Error: ", GetLastError());
}
return result;
}
// Close all orders by magic number
int CloseAllOrders() {
int closed = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == Symbol()) {
if(CloseOrder(OrderTicket())) {
closed++;
}
}
}
}
Print("Closed ", closed, " orders");
return closed;
}
// Get all open order tickets
int[] GetOpenTickets() {
int tickets[];
ArrayResize(tickets, 0);
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == Symbol()) {
int size = ArraySize(tickets);
ArrayResize(tickets, size + 1);
tickets[size] = OrderTicket();
}
}
}
return tickets;
}
};
```
Common OrderSend Errors and Solutions
| Error Code | Description | Solution |
|------------|-------------|----------|
| 129 | Invalid price | Always normalize prices with NormalizeDouble() and use MarketInfo(MODE_DIGITS) |
| 130 | Invalid stops | Check MODE_STOPLEVEL and ensure stop loss distance > minimum |
| 134 | Not enough money | Reduce lot size or check free margin |
| 138 | Requote | Increase slippage parameter or refresh rates |
| 146 | Trading context busy | Use Sleep() and retry, or use single-threaded approach |
| 147 | Expiration denied | Set expiration = 0 if broker doesn't support it |
| 148 | Too many orders | Close some orders before opening new ones |
Order Management Best Practices Checklist
Reference:
9. Next Step
Part 12 will explain Order Selection and Traversal Techniques – Complete guide to looping through orders, filtering by symbol/magic/type, and batch processing with practical examples.