一、为什么循环结构在EA开发中如此重要
循环结构让你的EA能够多次执行同一段代码,而无需编写重复的代码。它们对于批量处理多个订单、遍历数组元素、实现网格策略和执行重复性计算至关重要。没有循环,EA只能处理固定数量的项目。
二、循环结构完整速查表
| 循环类型 | 语法格式 | 执行特点 | 最佳适用场景 |
|----------|----------|----------|--------------|
| for循环 | for(初始化; 条件; 增量) { 代码 } | 每次迭代前检查条件 | 已知迭代次数 |
| while循环 | while(条件) { 代码 } | 每次迭代前检查条件 | 未知迭代次数 |
| do-while循环 | do { 代码 } while(条件); | 先执行一次再检查条件 | 必须至少执行一次 |
三、for循环 - 固定次数迭代
当你知道需要迭代的确切次数时,for循环是理想选择。它将初始化、条件检查和增量合并在一行中。
```mql4
// 基础for循环语法
for(初始化; 条件; 增量) {
// 重复执行的代码
}
// 简单for循环示例
for(int i = 0; i < 10; i++) {
Print("当前迭代次数:", i);
}
// 倒序循环
for(int i = 10; i > 0; i--) {
Print("倒计时:", i);
}
// 步长为2的循环
for(int i = 0; i < 20; i += 2) {
Print("偶数:", i);
}
// 实用EA示例 - 处理所有开仓订单
void ProcessAllOrders() {
int totalOrders = OrdersTotal();
Print("正在处理", totalOrders, "个开仓订单");
for(int i = 0; i < totalOrders; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol()) {
Print("订单", i, " 票据号:", OrderTicket(), " 类型:", OrderType());
// 计算订单盈利点数
double profitPoints = 0;
if(OrderType() == OP_BUY) {
profitPoints = (Bid - OrderOpenPrice()) / Point();
} else if(OrderType() == OP_SELL) {
profitPoints = (OrderOpenPrice() - Ask) / Point();
}
Print("订单盈利点数:", profitPoints);
}
}
}
}
// 计算所有持仓的平均价格
double CalculateAveragePrice() {
double totalPrice = 0;
int positionCount = 0;
int totalOrders = OrdersTotal();
for(int i = 0; i < totalOrders; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber) {
totalPrice += OrderOpenPrice();
positionCount++;
}
}
}
if(positionCount > 0) {
return totalPrice / positionCount;
}
return 0;
}
// 使用for循环处理数组
double CalculateMovingAverage(double &prices[], int period) {
if(ArraySize(prices) < period) return 0;
double sum = 0;
for(int i = 0; i < period; i++) {
sum += prices[i];
}
return sum / period;
}
// 使用嵌套for循环进行多周期分析
void AnalyzeMultipleTimeframes() {
int timeframes[] = {PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_H1, PERIOD_H4};
string tfNames[] = {"M1", "M5", "M15", "H1", "H4"};
for(int i = 0; i < ArraySize(timeframes); i++) {
double maValue = iMA(Symbol(), timeframes[i], 20, 0, MODE_SMA, PRICE_CLOSE, 1);
Print(tfNames[i], "周期的MA20值:", maValue);
// 内层循环获取多个K线值
for(int j = 1; j <= 3; j++) {
double close = iClose(Symbol(), timeframes[i], j);
Print(" ", tfNames[i], "周期的收盘价[", j, "]:", close);
}
}
}
```
四、while循环 - 基于条件的迭代
只要指定条件保持为真,while循环就会继续执行。它在每次迭代前检查条件,因此如果条件一开始就为假,循环永远不会执行。
```mql4
// 基础while循环语法
while(条件) {
// 条件为真时重复执行的代码
}
// 简单while循环示例
int counter = 0;
while(counter < 10) {
Print("计数器值:", counter);
counter++;
}
// 带复杂条件的while循环
double balance = AccountBalance();
while(balance > 10000 && !IsTradeAllowed() == false) {
balance = AccountBalance();
Print("当前余额:", balance);
Sleep(1000); // 等待1秒
}
// 实用EA示例 - 带索引管理的订单处理
void ProcessOrdersWithWhile() {
int i = 0;
while(i < OrdersTotal()) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber) {
// 检查是否需要修改订单
if(OrderStopLoss() == 0 && OrderProfit() > 10) {
double newStopLoss = OrderType() == OP_BUY ?
Bid - 30 * Point() :
Ask + 30 * Point();
OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, clrNONE);
Print("已为订单", OrderTicket(), "添加止损");
}
}
i++; // 仅当订单存在时才递增
} else {
i++; // 选择失败时也递增
}
}
}
// 使用while循环实现网格策略
void ExecuteGridStrategy(double startPrice, double gridSpacing, int maxLevels) {
int currentLevel = 0;
double currentPrice = startPrice;
while(currentLevel < maxLevels) {
if(MathAbs(Bid - currentPrice) >= gridSpacing) {
// 价格穿越网格线 - 下单
if(Bid > currentPrice) {
OpenSellOrder(0.1, currentPrice + gridSpacing);
Print("在网格层级", currentLevel, "处下卖出单");
} else {
OpenBuyOrder(0.1, currentPrice - gridSpacing);
Print("在网格层级", currentLevel, "处下买入单");
}
currentPrice = Bid;
currentLevel++;
}
Sleep(100); // 短暂暂停,防止CPU过载
}
}
// 订单重试机制
bool PlaceOrderWithRetry(int maxRetries) {
int retryCount = 0;
bool orderPlaced = false;
while(retryCount < maxRetries && !orderPlaced) {
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 30, 0, 0, "重试订单", magicNumber, 0, clrNONE);
if(ticket > 0) {
orderPlaced = true;
Print("在第", retryCount, "次重试时成功下单");
} else {
retryCount++;
Print("第", retryCount, "次重试失败。错误码:", GetLastError());
Sleep(1000 * retryCount); // 指数退避
}
}
return orderPlaced;
}
```
五、do-while循环 - 先执行后检查
do-while循环在检查条件之前先执行一次代码块。这保证了至少执行一次,非常适合需要在评估循环条件之前执行操作的场景。
```mql4
// 基础do-while循环语法
do {
// 至少执行一次的代码
} while(条件);
// 简单do-while示例
int counter = 0;
do {
Print("这段代码至少执行一次,计数器:", counter);
counter++;
} while(counter < 5);
// 用户输入验证(模拟)
int GetValidLotSize() {
int lotSize = 0;
do {
lotSize = 1; // 实际场景中这里会获取用户输入
if(lotSize < 0.01 || lotSize > 10) {
Print("无效手数,必须在0.01到10之间");
}
} while(lotSize < 0.01 || lotSize > 10);
return lotSize;
}
// 实用EA示例 - 确保至少尝试一次订单修改
void ModifyAllOrdersWithDoWhile() {
int modifiedCount = 0;
int totalOrders = OrdersTotal();
int attemptCount = 0;
if(totalOrders == 0) {
Print("没有需要修改的订单");
return;
}
do {
attemptCount++;
modifiedCount = 0;
for(int i = 0; i < totalOrders; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber) {
// 尝试修改订单
if(OrderStopLoss() == 0 && OrderProfit() > 5) {
double newStop = OrderType() == OP_BUY ? Bid - 20 * Point() : Ask + 20 * Point();
if(OrderModify(OrderTicket(), OrderOpenPrice(), newStop, OrderTakeProfit(), 0, clrNONE)) {
modifiedCount++;
}
}
}
}
}
if(modifiedCount > 0) {
Print("第", attemptCount, "次尝试中修改了", modifiedCount, "个订单");
} else if(attemptCount < 3) {
Print("第", attemptCount, "次尝试中没有订单被修改,重试中...");
Sleep(500);
}
} while(modifiedCount == 0 && attemptCount < 3);
}
// 确保至少执行一次的全平仓操作
void EnsurePositionClosing() {
int remainingAttempts = 5;
bool allClosed = false;
do {
int openOrders = 0;
// 统计并尝试关闭所有持仓
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber) {
openOrders++;
bool closed = OrderType() == OP_BUY ?
OrderClose(OrderTicket(), OrderLots(), Bid, 30, clrNONE) :
OrderClose(OrderTicket(), OrderLots(), Ask, 30, clrNONE);
if(closed) {
Print("已关闭订单", OrderTicket());
} else {
Print("关闭订单", OrderTicket(), "失败,错误码:", GetLastError());
}
}
}
}
allClosed = (openOrders == 0);
remainingAttempts--;
if(!allClosed && remainingAttempts > 0) {
Print("仍有", openOrders, "个持仓未关闭,重试中...");
Sleep(1000);
}
} while(!allClosed && remainingAttempts > 0);
if(allClosed) {
Print("所有持仓已成功关闭");
} else {
Print("达到最大尝试次数,仍有持仓未能关闭");
}
}
```
六、循环控制语句 - break和continue
```mql4
// break - 立即退出循环
void FindFirstProfitableOrder() {
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderProfit() > 0) {
Print("找到盈利订单:", OrderTicket(), " 盈利:", OrderProfit());
break; // 立即退出循环
}
}
}
}
// continue - 跳过本次迭代,继续下一次
void ProcessOnlyBuyOrders() {
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderType() != OP_BUY) {
continue; // 跳过非买入订单
}
// 仅处理买入订单
Print("正在处理买入订单:", OrderTicket());
}
}
}
// 带标签的嵌套循环跳出
void FindSpecificOrder(int targetTicket) {
bool found = false;
for(int i = 0; i < OrdersTotal() && !found; i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderTicket() == targetTicket) {
Print("找到目标订单:", OrderTicket());
found = true;
break;
}
}
}
}
```
七、完整EA模板(使用所有循环类型)
```mql4
//+------------------------------------------------------------------+
//| LoopsEA.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024"
#property version "1.00"
#property strict
input double InpLotSize = 0.1;
input int InpMagic = 12345;
input int InpMaxOrders = 5;
input int InpTrailingStop = 30;
input bool InpUseGrid = false;
input double InpGridSpacing = 50; // 点数
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit() {
Print("循环结构EA已初始化");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA主执行函数 |
//+------------------------------------------------------------------+
void OnTick() {
static datetime lastBarTime = 0;
if(Time[0] == lastBarTime) return;
lastBarTime = Time[0];
ManageAllPositions();
if(CountOrders(InpMagic) < InpMaxOrders) {
CheckAndEnterTrades();
}
if(InpUseGrid) {
ManageGridOrders();
}
}
//+------------------------------------------------------------------+
//| 使用for循环统计订单数量 |
//+------------------------------------------------------------------+
int CountOrders(int magic) {
int count = 0;
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magic) {
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| 使用for循环管理所有持仓(移动止损) |
//+------------------------------------------------------------------+
void ManageAllPositions() {
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic) {
ApplyTrailingStop(OrderTicket());
}
}
}
}
//+------------------------------------------------------------------+
//| 对单个订单应用移动止损 |
//+------------------------------------------------------------------+
void ApplyTrailingStop(int ticket) {
if(OrderSelect(ticket, SELECT_BY_TICKET)) {
double currentStop = OrderStopLoss();
double newStop = 0;
double currentPrice = (OrderType() == OP_BUY) ? Bid : Ask;
double openPrice = OrderOpenPrice();
double profitPoints = 0;
if(OrderType() == OP_BUY) {
profitPoints = (currentPrice - openPrice) / Point();
if(profitPoints > InpTrailingStop) {
newStop = currentPrice - InpTrailingStop * Point();
}
} else {
profitPoints = (openPrice - currentPrice) / Point();
if(profitPoints > InpTrailingStop) {
newStop = currentPrice + InpTrailingStop * Point();
}
}
if(newStop > 0 && newStop != currentStop) {
OrderModify(ticket, openPrice, newStop, OrderTakeProfit(), 0, clrNONE);
Print("订单", ticket, "的移动止损已更新");
}
}
}
//+------------------------------------------------------------------+
//| 使用for循环检查多个周期并决定入场 |
//+------------------------------------------------------------------+
void CheckAndEnterTrades() {
int timeframes[] = {PERIOD_M15, PERIOD_H1, PERIOD_H4};
for(int tf = 0; tf < ArraySize(timeframes); tf++) {
double rsi = iRSI(Symbol(), timeframes[tf], 14, PRICE_CLOSE, 1);
if(rsi < 30) {
Print("在", timeframes[tf], "周期上检测到买入信号");
OpenTrade(OP_BUY);
break; // 每根K线只入场一次
} else if(rsi > 70) {
Print("在", timeframes[tf], "周期上检测到卖出信号");
OpenTrade(OP_SELL);
break;
}
}
}
//+------------------------------------------------------------------+
//| 开立新订单 |
//+------------------------------------------------------------------+
void OpenTrade(int orderType) {
double price = (orderType == OP_BUY) ? Ask : Bid;
int ticket = OrderSend(Symbol(), orderType, InpLotSize, price, 30, 0, 0, "循环EA", InpMagic, 0, clrNONE);
if(ticket > 0) {
Print("订单已开立:", ticket);
} else {
Print("订单开立失败,错误码:", GetLastError());
}
}
//+------------------------------------------------------------------+
//| 使用while循环管理网格订单 |
//+------------------------------------------------------------------+
void ManageGridOrders() {
static double lastGridPrice = 0;
if(lastGridPrice == 0) {
lastGridPrice = Bid;
return;
}
double priceMovement = MathAbs(Bid - lastGridPrice) / Point();
while(priceMovement >= InpGridSpacing) {
if(Bid > lastGridPrice) {
// 价格上涨 - 在上方挂卖出单
double sellPrice = lastGridPrice + InpGridSpacing * Point();
OrderSend(Symbol(), OP_SELL, InpLotSize * 0.5, sellPrice, 30, 0, 0, "网格卖出", InpMagic, 0, clrNONE);
Print("在", sellPrice, "处挂网格卖出单");
} else {
// 价格下跌 - 在下方挂买入单
double buyPrice = lastGridPrice - InpGridSpacing * Point();
OrderSend(Symbol(), OP_BUY, InpLotSize * 0.5, buyPrice, 30, 0, 0, "网格买入", InpMagic, 0, clrNONE);
Print("在", buyPrice, "处挂网格买入单");
}
lastGridPrice = Bid;
priceMovement -= InpGridSpacing;
}
}
//+------------------------------------------------------------------+
//| 使用do-while循环全平仓(保证至少执行一次) |
//+------------------------------------------------------------------+
void CloseAllOrders() {
int attempts = 0;
int maxAttempts = 5;
int remainingOrders = 0;
do {
remainingOrders = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--) {
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
if(OrderSymbol() == Symbol() && OrderMagicNumber() == InpMagic) {
bool closed = OrderClose(OrderTicket(), OrderLots(), OrderType() == OP_BUY ? Bid : Ask, 30, clrNONE);
if(!closed) {
remainingOrders++;
Print("关闭订单", OrderTicket(), "失败");
}
}
}
}
attempts++;
if(remainingOrders > 0 && attempts < maxAttempts) {
Print("剩余", remainingOrders, "个订单未关闭,重试第", attempts, "次");
Sleep(1000);
}
} while(remainingOrders > 0 && attempts < maxAttempts);
if(remainingOrders == 0) {
Print("所有订单已成功关闭");
} else {
Print("经过", maxAttempts, "次尝试后,仍有", remainingOrders, "个订单未能关闭");
}
}
```
八、循环性能优化技巧
| 问题现象 | 原因分析 | 优化方案 |
|----------|----------|----------|
| 订单处理速度慢 | 订单数量大 | 反向遍历:for(int i=OrdersTotal()-1; i>=0; i--) |
| CPU占用过高 | 无限循环 | 确保有退出条件,while循环中加入Sleep() |
| 遗漏订单 | 订单数量动态变化 | 循环前先存储总数:int total=OrdersTotal() |
| 数据过期 | 缓存未刷新 | 在循环内修改订单后重新选择 |
| 内存占用大 | 数组过大 | 限制数组大小,使用动态调整 |
九、循环结构最佳实践清单
参考来源:
9. 下一步
第10篇将讲解MQL4函数定义与调用 – 创建自定义函数、参数传递、返回值处理以及模块化EA设计,附带实际代码示例。