Summary: 本文全面讲解MQL4语言中的订单选择与遍历技巧,包括循环遍历订单的方法、按品种/魔术号/类型筛选订单、批量操作实现,通过实际EA代码示例演示完整的订单管理流程。




一、为什么订单选择与遍历在EA开发中如此重要

订单选择与遍历是每个EA开发者必须掌握的基础技能。你的EA需要扫描所有开仓订单,识别哪些属于自己,检查它们的状态,并执行修改止损或平仓等操作。没有正确的遍历技巧,你就无法有效管理多个持仓,也无法实现篮子交易、网格策略或仓位均价等高级功能。

二、订单池完整速查表

| 订单池 | 常量 | 选择方式 | 获取数量函数 |
|--------|------|----------|--------------|
| 开仓订单(市价+挂单) | MODE_TRADES | SELECT_BY_POS | OrdersTotal() |
| 历史已关闭订单 | MODE_HISTORY | SELECT_BY_POS | OrdersHistoryTotal() |
| 按票据号选择(任意池) | 自动检测 | SELECT_BY_TICKET | OrderSelect(ticket,SELECT_BY_TICKET) |

三、基础订单遍历 - 循环遍历所有开仓订单

```mql4
// 基础正向循环(修改/删除时不推荐使用)
void ScanAllOrdersForward() {
int totalOrders = OrdersTotal();
Print("开仓订单总数:", totalOrders);

for(int i = 0; i < totalOrders; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
Print("订单", i, " 票据号:", OrderTicket(), " 类型:", OrderType());
}
}
}

// 推荐:反向循环(修改/删除时安全)
void ScanAllOrdersReverse() {
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
Print("正在处理位置", i, "的订单");
// 此处可以安全地修改或关闭订单
}
}
}

// 提取所有订单的完整信息
void ExtractAllOrderInfo() {
string info = "";
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
info = "位置:" + IntegerToString(i) +
" 票据号:" + IntegerToString(OrderTicket()) +
" 类型:" + IntegerToString(OrderType()) +
" 手数:" + DoubleToString(OrderLots(), 2) +
" 盈利:" + DoubleToString(OrderProfit(), 2);
Print(info);
}
}
}
```

四、按魔术号筛选订单 - 识别EA的订单

```mql4
// 仅按魔术号筛选
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;
}

// 按魔术号和品种筛选
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;
}

// 按魔术号和订单类型筛选
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;
}

// 多条件高级筛选
struct OrderFilter {
int magic;
string symbol;
int type; // -1表示任意类型
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;
}
```

五、收集订单票据号到数组进行批量处理

```mql4
// 收集指定魔术号的所有订单票据号
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;
}

// 仅收集买入订单
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;
}

// 仅收集卖出订单
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;
}

// 使用收集的票据号进行批量处理
void BatchProcessOrders(int magicNumber) {
int tickets[] = GetOrderTicketsByMagic(magicNumber);
int total = ArraySize(tickets);

Print("正在处理", total, "个订单");

for(int i = 0; i < total; i++) {
if(OrderSelect(tickets[i], SELECT_BY_TICKET)) {
// 执行批量操作
Print("正在处理票据号:", tickets[i]);

// 示例:对所有订单应用移动止损
ApplyTrailingStopToOrder(tickets[i]);
}
}
}
```

六、完整订单信息提取

```mql4
// 提取所有订单的完整信息
void PrintAllOrderDetails() {
string separator = "========================================";

Print(separator);
Print("开仓订单报告");
Print(separator);

for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
Print("位置:", i);
Print(" 票据号:", OrderTicket());
Print(" 品种:", OrderSymbol());
Print(" 类型:", OrderTypeToString(OrderType()));
Print(" 手数:", OrderLots());
Print(" 开仓价:", OrderOpenPrice());
Print(" 当前价:", OrderType() == OP_BUY ? Bid : Ask);
Print(" 止损:", OrderStopLoss());
Print(" 止盈:", OrderTakeProfit());
Print(" 盈利:", OrderProfit());
Print(" 隔夜利息:", OrderSwap());
Print(" 手续费:", OrderCommission());
Print(" 魔术号:", OrderMagicNumber());
Print(" 注释:", OrderComment());
Print(" 开仓时间:", TimeToString(OrderOpenTime()));
Print(separator);
}
}
}

// 辅助函数:将订单类型转换为可读字符串
string OrderTypeToString(int type) {
switch(type) {
case OP_BUY: return "买入";
case OP_SELL: return "卖出";
case OP_BUYLIMIT: return "买入限价";
case OP_SELLLIMIT: return "卖出限价";
case OP_BUYSTOP: return "买入止损";
case OP_SELLSTOP: return "卖出止损";
default: return "未知";
}
}

// 计算所有订单的汇总统计
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("========== 订单统计 ==========");
Print("总盈利:", DoubleToString(totalProfit, 2));
Print("总手数:", DoubleToString(totalLots, 2));
Print("买入订单数:", buyCount);
Print("卖出订单数:", sellCount);
Print("最大盈利:", DoubleToString(maxProfit, 2));
Print("最小盈利:", DoubleToString(minProfit, 2));
Print("=======================================");
}
```

七、历史订单遍历 - 处理已关闭订单

```mql4
// 遍历历史订单(已关闭和已取消)
void ScanHistoryOrders() {
int totalHistory = OrdersHistoryTotal();
Print("历史订单总数:", totalHistory);

for(int i = 0; i < totalHistory; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
Print("历史订单", i);
Print(" 票据号:", OrderTicket());
Print(" 品种:", OrderSymbol());
Print(" 类型:", OrderTypeToString(OrderType()));
Print(" 手数:", OrderLots());
Print(" 开仓价:", OrderOpenPrice());
Print(" 平仓价:", OrderClosePrice());
Print(" 盈利:", OrderProfit());
Print(" 平仓时间:", TimeToString(OrderCloseTime()));
}
}
}

// 计算今日盈利
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;
}

// 获取最近N个已关闭订单
void GetLastClosedOrders(int count) {
int historyCount = OrdersHistoryTotal();
int startIndex = MathMax(0, historyCount - count);

Print("最近", count, "个已关闭订单:");

for(int i = startIndex; i < historyCount; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_HISTORY)) {
Print(" 票据号:", OrderTicket(), " 盈利:", OrderProfit());
}
}
}
```

八、专业订单管理类(含高级遍历功能)

```mql4
//+------------------------------------------------------------------+
//| 专业订单管理器(含高级遍历功能) |
//+------------------------------------------------------------------+
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;
}

// 获取所有订单的票据号数组
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;
}

// 按类型获取订单
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;
}

// 按盈利范围获取订单
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;
}

// 关闭所有订单(批量操作)
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, "个订单");
return closed;
}

// 仅关闭盈利订单
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;
}

// 仅关闭亏损订单
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;
}

// 对所有订单应用移动止损
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);
}
}
}
}

// 对单个订单应用移动止损
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);
}
}

// 获取所有持仓的平均价格
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;
}

// 获取所有持仓的总手数
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;
}

// 检查是否有持仓
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;
}
}
```

九、完整EA示例(使用订单遍历)

```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);

//+------------------------------------------------------------------+
//| EA主执行函数 |
//+------------------------------------------------------------------+
void OnTick() {
static datetime lastBarTime = 0;
if(Time[0] == lastBarTime) return;
lastBarTime = Time[0];

// 对所有现有订单应用移动止损
orderMgr.ApplyTrailingStopToAll(InpTrailingStop);

// 检查是否可以开新仓
if(!orderMgr.HasOpenPosition() && CountTotalOrders() < InpMaxOrders) {
int signal = GenerateSignal();

if(signal == SIGNAL_BUY) {
OpenBuyOrder();
} else if(signal == SIGNAL_SELL) {
OpenSellOrder();
}
}

// 在图表上显示订单信息
DisplayOrderInfo();
}

// 其他辅助函数(GenerateSignal、OpenBuyOrder、OpenSellOrder、CountTotalOrders、DisplayOrderInfo)
```

十、订单遍历最佳实践清单

  • [ ] 修改或关闭订单时始终使用反向循环(i=OrdersTotal()-1; i>=0; i--)

  • [ ] 如果循环期间订单数量不会变化,可先存储OrdersTotal()值

  • [ ] 访问订单属性前始终调用OrderSelect()

  • [ ] 知道票据号时使用SELECT_BY_TICKET可获得更快的访问速度

  • [ ] 使用魔术号筛选,避免影响其他EA或手动交易

  • [ ] 在同一品种的多个图表上运行EA时,始终检查OrderSymbol()

  • [ ] 利用OrderSelect返回值优雅处理选择失败的情况

  • [ ] 处理历史订单时使用OrdersHistoryTotal()和MODE_HISTORY

  • [ ] 实现超时机制,避免订单遍历中的无限循环

  • [ ] 对多个订单的重复操作使用批量处理方式


  • 参考来源:

  • MetaQuotes Ltd.《MQL4官方文档 - 订单选择与池常量》(2024)

  • Elder, Alexander.《新交易以交易为生》(2014)

  • 王强.《MQL4订单管理高级教程》(2023)


  • 9. 下一步

    第13篇将讲解MQL4技术指标调用(iMA、iRSI、iMACD) – 内置指标调用方法、缓冲区数据读取、多周期分析的完整指南,附带实际EA代码示例。