Why Order Selection and Traversal Matter in EA Development
Order selection and traversal are fundamental skills for any EA developer. Your EA needs to scan through open orders, identify which ones belong to it, check their status, and perform operations like modifying stops or closing positions. Without proper traversal techniques, you cannot manage multiple positions effectively or implement advanced features like basket trading, grid strategies, or position averaging.
Complete Order Pool Reference Table
| Pool | Constant | Selection Method | Access Function |
|------|----------|------------------|-----------------|
| Open Orders (Market + Pending) | MODE_TRADES | SELECT_BY_POS | OrdersTotal() |
| Closed Orders History | MODE_HISTORY | SELECT_BY_POS | OrdersHistoryTotal() |
| Order by Ticket (Any Pool) | Auto-detected | SELECT_BY_TICKET | OrderSelect(ticket,SELECT_BY_TICKET) |
1. Basic Order Traversal - Looping Through All Open Orders
```mql4
// Basic forward loop (not recommended when modifying/deleting)
void ScanAllOrdersForward() {
int totalOrders = OrdersTotal();
Print("Total open orders: ", totalOrders);
for(int i = 0; i < totalOrders; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
Print("Order ", i, " Ticket: ", OrderTicket(), " Type: ", OrderType());
}
}
}
// Recommended: Reverse loop (safe for modification/deletion)
void ScanAllOrdersReverse() {
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
Print("Processing order at position ", i);
// Safe to modify or close here
}
}
}
// Complete order information extraction
void ExtractAllOrderInfo() {
string info = "";
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
info = "Pos:" + IntegerToString(i) +
" Ticket:" + IntegerToString(OrderTicket()) +
" Type:" + IntegerToString(OrderType()) +
" Lots:" + DoubleToString(OrderLots(), 2) +
" Profit:" + DoubleToString(OrderProfit(), 2);
Print(info);
}
}
}
```
2. Filtering Orders by Magic Number - Identifying Your EA's Orders
```mql4
// Filter by magic number only
int CountOrdersByMagic(int magicNumber) {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber) {
count++;
}
}
}
return count;
}
// Filter by magic number AND symbol
int CountOrdersByMagicAndSymbol(int magicNumber, string symbol) {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == symbol) {
count++;
}
}
}
return count;
}
// Filter by magic number and order type
int CountOrdersByMagicAndType(int magicNumber, int orderType) {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderType() == orderType) {
count++;
}
}
}
return count;
}
// Advanced filtering with multiple criteria
struct OrderFilter {
int magic;
string symbol;
int type; // -1 for any type
double minProfit;
double maxProfit;
};
int CountOrdersWithFilter(OrderFilter &filter) {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
bool match = true;
if(filter.magic != -1 && OrderMagicNumber() != filter.magic) match = false;
if(filter.symbol != "" && OrderSymbol() != filter.symbol) match = false;
if(filter.type != -1 && OrderType() != filter.type) match = false;
if(filter.minProfit > 0 && OrderProfit() < filter.minProfit) match = false;
if(filter.maxProfit > 0 && OrderProfit() > filter.maxProfit) match = false;
if(match) count++;
}
}
return count;
}
```
3. Collecting Order Tickets into Arrays for Batch Processing
```mql4
// Collect all tickets for a specific magic number
int[] GetOrderTicketsByMagic(int magicNumber) {
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;
}
// Collect buy orders only
int[] GetBuyOrderTickets(int magicNumber) {
int tickets[];
ArrayResize(tickets, 0);
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderType() == OP_BUY) {
int size = ArraySize(tickets);
ArrayResize(tickets, size + 1);
tickets[size] = OrderTicket();
}
}
}
return tickets;
}
// Collect sell orders only
int[] GetSellOrderTickets(int magicNumber) {
int tickets[];
ArrayResize(tickets, 0);
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderType() == OP_SELL) {
int size = ArraySize(tickets);
ArrayResize(tickets, size + 1);
tickets[size] = OrderTicket();
}
}
}
return tickets;
}
// Batch processing using collected tickets
void BatchProcessOrders(int magicNumber) {
int tickets[] = GetOrderTicketsByMagic(magicNumber);
int total = ArraySize(tickets);
Print("Processing ", total, " orders");
for(int i = 0; i < total; i++) {
if(OrderSelect(tickets[i], SELECT_BY_TICKET)) {
// Perform batch operation
Print("Processing ticket: ", tickets[i]);
// Example: Apply trailing stop to all
ApplyTrailingStopToOrder(tickets[i]);
}
}
}
```
4. Comprehensive Order Information Extraction
```mql4
// Extract comprehensive information for all orders
void PrintAllOrderDetails() {
string separator = "========================================";
Print(separator);
Print("OPEN ORDERS REPORT");
Print(separator);
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
Print("Position: ", i);
Print(" Ticket: ", OrderTicket());
Print(" Symbol: ", OrderSymbol());
Print(" Type: ", OrderTypeToString(OrderType()));
Print(" Lots: ", OrderLots());
Print(" Open Price: ", OrderOpenPrice());
Print(" Current Price: ", OrderType() == OP_BUY ? Bid : Ask);
Print(" Stop Loss: ", OrderStopLoss());
Print(" Take Profit: ", OrderTakeProfit());
Print(" Profit: ", OrderProfit());
Print(" Swap: ", OrderSwap());
Print(" Commission: ", OrderCommission());
Print(" Magic: ", OrderMagicNumber());
Print(" Comment: ", OrderComment());
Print(" Open Time: ", TimeToString(OrderOpenTime()));
Print(separator);
}
}
}
// Helper: Convert order type to readable string
string OrderTypeToString(int type) {
switch(type) {
case OP_BUY: return "BUY";
case OP_SELL: return "SELL";
case OP_BUYLIMIT: return "BUY LIMIT";
case OP_SELLLIMIT: return "SELL LIMIT";
case OP_BUYSTOP: return "BUY STOP";
case OP_SELLSTOP: return "SELL STOP";
default: return "UNKNOWN";
}
}
// Calculate aggregate statistics for all orders
void CalculateOrderStatistics(int magicNumber) {
double totalProfit = 0;
double totalLots = 0;
int buyCount = 0;
int sellCount = 0;
double maxProfit = -999999;
double minProfit = 999999;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == Symbol()) {
double profit = OrderProfit() + OrderSwap() + OrderCommission();
totalProfit += profit;
totalLots += OrderLots();
if(OrderType() == OP_BUY) buyCount++;
else if(OrderType() == OP_SELL) sellCount++;
if(profit > maxProfit) maxProfit = profit;
if(profit < minProfit) minProfit = profit;
}
}
}
Print("========== ORDER STATISTICS ==========");
Print("Total Profit: ", DoubleToString(totalProfit, 2));
Print("Total Lots: ", DoubleToString(totalLots, 2));
Print("Buy Orders: ", buyCount);
Print("Sell Orders: ", sellCount);
Print("Max Profit: ", DoubleToString(maxProfit, 2));
Print("Min Profit: ", DoubleToString(minProfit, 2));
Print("=======================================");
}
```
5. History Order Traversal - Processing Closed Orders
```mql4
// Traverse history orders (closed and cancelled)
void ScanHistoryOrders() {
int totalHistory = OrdersHistoryTotal();
Print("Total history orders: ", totalHistory);
for(int i = 0; i < totalHistory; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
Print("History order ", i);
Print(" Ticket: ", OrderTicket());
Print(" Symbol: ", OrderSymbol());
Print(" Type: ", OrderTypeToString(OrderType()));
Print(" Lots: ", OrderLots());
Print(" Open Price: ", OrderOpenPrice());
Print(" Close Price: ", OrderClosePrice());
Print(" Profit: ", OrderProfit());
Print(" Close Time: ", TimeToString(OrderCloseTime()));
}
}
}
// Calculate today's profit from history
double CalculateTodayProfit() {
double todayProfit = 0;
datetime midnight = GetMidnight();
for(int i = 0; i < OrdersHistoryTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
if(OrderCloseTime() >= midnight) {
todayProfit += OrderProfit() + OrderSwap() + OrderCommission();
}
}
}
return todayProfit;
}
// Get last N closed orders
void GetLastClosedOrders(int count) {
int historyCount = OrdersHistoryTotal();
int startIndex = MathMax(0, historyCount - count);
Print("Last ", count, " closed orders:");
for(int i = startIndex; i < historyCount; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
Print(" Ticket: ", OrderTicket(), " Profit: ", OrderProfit());
}
}
}
```
6. Professional Order Management Class with Advanced Traversal
```mql4
//+------------------------------------------------------------------+
//| Professional Order Manager with Advanced Traversal |
//+------------------------------------------------------------------+
class OrderManagerPro {
private:
int magicNumber;
string symbol;
bool SelectOrderSafe(int ticket) {
return OrderSelect(ticket, SELECT_BY_TICKET);
}
public:
OrderManagerPro(int magic, string sym = "") {
magicNumber = magic;
symbol = (sym == "") ? Symbol() : sym;
}
// Get all orders as an array of tickets
int[] GetAllOrderTickets() {
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;
}
// Get orders by type
int[] GetOrdersByType(int orderType) {
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 && OrderType() == orderType) {
int size = ArraySize(tickets);
ArrayResize(tickets, size + 1);
tickets[size] = OrderTicket();
}
}
}
return tickets;
}
// Get orders by profit range
int[] GetOrdersByProfit(double minProfit, double maxProfit) {
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) {
double profit = OrderProfit() + OrderSwap() + OrderCommission();
if(profit >= minProfit && profit <= maxProfit) {
int size = ArraySize(tickets);
ArrayResize(tickets, size + 1);
tickets[size] = OrderTicket();
}
}
}
}
return tickets;
}
// Close all orders (batch operation)
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) {
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
if(OrderClose(OrderTicket(), OrderLots(), closePrice, 30, clrNONE)) {
closed++;
}
}
}
}
Print("Closed ", closed, " orders");
return closed;
}
// Close only profitable orders
int CloseProfitableOrders() {
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(OrderProfit() > 0) {
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
if(OrderClose(OrderTicket(), OrderLots(), closePrice, 30, clrNONE)) {
closed++;
}
}
}
}
}
return closed;
}
// Close only losing orders
int CloseLosingOrders() {
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(OrderProfit() < 0) {
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
if(OrderClose(OrderTicket(), OrderLots(), closePrice, 30, clrNONE)) {
closed++;
}
}
}
}
}
return closed;
}
// Apply trailing stop to all orders
void ApplyTrailingStopToAll(int trailingPoints) {
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == symbol) {
ApplyTrailingStopToOrder(OrderTicket(), trailingPoints);
}
}
}
}
// Apply trailing stop to a single order
void ApplyTrailingStopToOrder(int ticket, int trailingPoints) {
if(!SelectOrderSafe(ticket)) return;
double openPrice = OrderOpenPrice();
double currentStop = OrderStopLoss();
double currentPrice = (OrderType() == OP_BUY) ? Bid : Ask;
double newStop = 0;
if(OrderType() == OP_BUY) {
double profitPoints = (currentPrice - openPrice) / Point();
if(profitPoints > trailingPoints) {
newStop = NormalizeDouble(currentPrice - trailingPoints * Point(), Digits);
}
} else if(OrderType() == OP_SELL) {
double profitPoints = (openPrice - currentPrice) / Point();
if(profitPoints > trailingPoints) {
newStop = NormalizeDouble(currentPrice + trailingPoints * Point(), Digits);
}
}
if(newStop > 0 && newStop != currentStop) {
OrderModify(ticket, openPrice, newStop, OrderTakeProfit(), 0, clrNONE);
}
}
// Get average price of all positions
double GetAveragePrice() {
double totalPrice = 0;
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == symbol) {
totalPrice += OrderOpenPrice();
count++;
}
}
}
return (count > 0) ? totalPrice / count : 0;
}
// Get total volume of all positions
double GetTotalVolume() {
double totalLots = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == symbol) {
totalLots += OrderLots();
}
}
}
return totalLots;
}
// Check if position exists
bool HasOpenPosition() {
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == magicNumber && OrderSymbol() == symbol) {
return true;
}
}
}
return false;
}
}
```
7. Complete EA Example Using Order Traversal
```mql4
//+------------------------------------------------------------------+
//| TraversalEA.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024"
#property version "1.00"
#property strict
input double InpLotSize = 0.1;
input int InpMagic = 12345;
input int InpTrailingStop = 30;
input int InpMaxOrders = 5;
OrderManagerPro orderMgr(InpMagic);
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
static datetime lastBarTime = 0;
if(Time[0] == lastBarTime) return;
lastBarTime = Time[0];
// Apply trailing stop to all existing orders
orderMgr.ApplyTrailingStopToAll(InpTrailingStop);
// Check if we can open new position
if(!orderMgr.HasOpenPosition() && CountTotalOrders() < InpMaxOrders) {
int signal = GenerateSignal();
if(signal == SIGNAL_BUY) {
OpenBuyOrder();
} else if(signal == SIGNAL_SELL) {
OpenSellOrder();
}
}
// Display order information on chart
DisplayOrderInfo();
}
//+------------------------------------------------------------------+
//| Generate trading signal |
//+------------------------------------------------------------------+
int GenerateSignal() {
double maFast = iMA(NULL, 0, 10, 0, MODE_SMA, PRICE_CLOSE, 1);
double maSlow = iMA(NULL, 0, 30, 0, MODE_SMA, PRICE_CLOSE, 1);
if(maFast > maSlow) return SIGNAL_BUY;
if(maFast < maSlow) return SIGNAL_SELL;
return SIGNAL_NONE;
}
//+------------------------------------------------------------------+
//| Open buy order |
//+------------------------------------------------------------------+
void OpenBuyOrder() {
int ticket = OrderSend(Symbol(), OP_BUY, InpLotSize, Ask, 30, 0, 0, "Traversal EA", InpMagic, 0, clrGreen);
if(ticket > 0) {
Print("Buy order opened: ", ticket);
}
}
//+------------------------------------------------------------------+
//| Open sell order |
//+------------------------------------------------------------------+
void OpenSellOrder() {
int ticket = OrderSend(Symbol(), OP_SELL, InpLotSize, Bid, 30, 0, 0, "Traversal EA", InpMagic, 0, clrRed);
if(ticket > 0) {
Print("Sell order opened: ", ticket);
}
}
//+------------------------------------------------------------------+
//| Count total orders across all symbols |
//+------------------------------------------------------------------+
int CountTotalOrders() {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderMagicNumber() == InpMagic) {
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Display order information on chart |
//+------------------------------------------------------------------+
void DisplayOrderInfo() {
string info = "=== Order Information ===\n";
info += "Total Orders: " + IntegerToString(CountTotalOrders()) + "\n";
info += "Has Position: " + (orderMgr.HasOpenPosition() ? "YES" : "NO") + "\n";
info += "Avg Price: " + DoubleToString(orderMgr.GetAveragePrice(), 5) + "\n";
info += "Total Volume: " + DoubleToString(orderMgr.GetTotalVolume(), 2);
Comment(info);
}
```
Order Traversal Best Practices Checklist
Reference:
9. Next Step
Part 13 will explain Technical Indicators in MQL4 (iMA, iRSI, iMACD) – Complete guide to calling built-in indicators, reading buffer values, and multi-timeframe analysis with practical EA examples.