均线交叉是最经典且广泛使用的交易策略之一。此MQL4 EA源码实现了一个完整功能的外汇机器人:当快均线上穿慢均线时开多单,下穿时开空单。内置资金管理、时间过滤和移动止损,该EA可直接用于实盘交易或历史回测。
策略逻辑
完整MQL4源码
```cpp
//+------------------------------------------------------------------+
//| MovingAverage_Crossover_EA.mq4 |
//| Generated by AutoCompile AI |
//| |
//+------------------------------------------------------------------+
#property copyright "AutoCompile AI"
#property link ""
#property version "1.00"
#property strict
//--- 输入参数:移动平均线
input int FastMAPeriod = 10; // 快均线周期
input int SlowMAPeriod = 30; // 慢均线周期
input int MAShift = 0; // 均线偏移
input ENUM_MA_METHOD MAMethod = MODE_SMA; // 均线类型: SMA, EMA等
input ENUM_APPLIED_PRICE MAPrice = PRICE_CLOSE; // 应用价格
//--- 输入参数:资金管理
input double FixedLotSize = 0.1; // 固定手数(UseMoneyManagement=false时生效)
input bool UseMoneyManagement = true; // 使用自动手数(基于风险百分比)
input double RiskPercent = 2.0; // 每笔交易风险占账户百分比
input int StopLossPips = 50; // 止损点数
input int TakeProfitPips = 150; // 止盈点数
//--- 输入参数:交易过滤
input bool UseTimeFilter = false; // 仅在特定时段交易
input int StartHour = 8; // 交易开始小时(服务器时间)
input int EndHour = 17; // 交易结束小时
input int MaxSpread = 30; // 允许最大点差(点数)
input int Slippage = 20; // 下单滑点
//--- 输入参数:移动止损
input bool UseTrailingStop = true; // 启用移动止损
input int TrailStartPips = 20; // 盈利达到此点数后启动移动止损
input int TrailStepPips = 10; // 移动止损距离(点数)
//--- 全局变量
double fastMA[], slowMA[];
int magicNumber = 202510;
string comment = "MA_Crossover";
//+------------------------------------------------------------------+
//| EA初始化函数 |
//+------------------------------------------------------------------+
int OnInit()
{
if(FastMAPeriod >= SlowMAPeriod)
{
Print("错误: 快均线周期必须小于慢均线周期");
return(INIT_FAILED);
}
Print("均线交叉EA初始化成功");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| EA反初始化函数 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("EA已从图表移除。原因代码: ", reason);
}
//+------------------------------------------------------------------+
//| EA tick函数 |
//+------------------------------------------------------------------+
void OnTick()
{
//--- 检查市场是否可交易及点差条件
if(!IsTradeAllowed()) return;
if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread * Point) return;
//--- 时间过滤
if(UseTimeFilter)
{
datetime now = TimeCurrent();
int currentHour = TimeHour(now);
if(currentHour < StartHour || currentHour >= EndHour) return;
}
//--- 获取均线值
int barsToCopy = SlowMAPeriod + 5;
ArraySetAsSeries(fastMA, true);
ArraySetAsSeries(slowMA, true);
if(CopyBuffer(iMA(Symbol(), 0, FastMAPeriod, MAShift, MAMethod, MAPrice), 0, 0, barsToCopy, fastMA) < barsToCopy) return;
if(CopyBuffer(iMA(Symbol(), 0, SlowMAPeriod, MAShift, MAMethod, MAPrice), 0, 0, barsToCopy, slowMA) < barsToCopy) return;
//--- 检查现有持仓数量
int buyCount = 0, sellCount = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber)
{
if(OrderType() == OP_BUY) buyCount++;
if(OrderType() == OP_SELL) sellCount++;
}
}
}
//--- 交叉检测
bool fastAboveSlow = (fastMA[1] > slowMA[1]);
bool fastBelowSlow = (fastMA[1] < slowMA[1]);
bool prevFastAboveSlow = (fastMA[2] > slowMA[2]);
bool prevFastBelowSlow = (fastMA[2] < slowMA[2]);
bool buySignal = (prevFastBelowSlow && fastAboveSlow);
bool sellSignal = (prevFastAboveSlow && fastBelowSlow);
//--- 计算手数
double lot = FixedLotSize;
if(UseMoneyManagement)
{
double riskMoney = AccountBalance() * RiskPercent / 100.0;
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double stopLossPoints = StopLossPips * 10;
lot = riskMoney / (stopLossPoints * tickValue);
lot = NormalizeDouble(lot, 2);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
if(lot < minLot) lot = minLot;
if(lot > maxLot) lot = maxLot;
}
//--- 开仓逻辑
if(buySignal && buyCount == 0)
{
double entryPrice = Ask;
double sl = entryPrice - StopLossPips * 10 * Point;
double tp = entryPrice + TakeProfitPips * 10 * Point;
int ticket = OrderSend(Symbol(), OP_BUY, lot, entryPrice, Slippage, sl, tp, comment, magicNumber, 0, clrGreen);
if(ticket > 0)
Print("买单开仓成功。订单号: ", ticket);
else
Print("买单开仓失败。错误码: ", GetLastError());
}
else if(sellSignal && sellCount == 0)
{
double entryPrice = Bid;
double sl = entryPrice + StopLossPips * 10 * Point;
double tp = entryPrice - TakeProfitPips * 10 * Point;
int ticket = OrderSend(Symbol(), OP_SELL, lot, entryPrice, Slippage, sl, tp, comment, magicNumber, 0, clrRed);
if(ticket > 0)
Print("卖单开仓成功。订单号: ", ticket);
else
Print("卖单开仓失败。错误码: ", GetLastError());
}
//--- 移动止损管理
if(UseTrailingStop)
{
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == magicNumber)
{
if(OrderType() == OP_BUY)
{
double profitPips = (Bid - OrderOpenPrice()) / Point;
if(profitPips > TrailStartPips * 10)
{
double newSL = Bid - TrailStepPips * 10 * Point;
if(newSL > OrderStopLoss())
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrBlue);
}
}
else if(OrderType() == OP_SELL)
{
double profitPips = (OrderOpenPrice() - Ask) / Point;
if(profitPips > TrailStartPips * 10)
{
double newSL = Ask + TrailStepPips * 10 * Point;
if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0, clrBlue);
}
}
}
}
}
}
}
//+------------------------------------------------------------------+
```
如何编译与测试
1. 保存为 `MovingAverage_Crossover_EA.mq4` 放入 `Experts` 文件夹。
2. 在MetaEditor中按F7编译,应无报错。
3. 在策略测试器(Ctrl+R)中对EURUSD、H1周期进行回测。
4. 针对不同品种优化均线周期和风险设置。
参数说明
| 参数 | 说明 |
|------|------|
| FastMAPeriod / SlowMAPeriod | 交叉速度。典型值:10和30、5和20、20和50 |
| UseMoneyManagement / RiskPercent | 基于账户百分比风险自动计算手数 |
| StopLossPips / TakeProfitPips | 固定止损/止盈点数 |
| UseTrailingStop / TrailStartPips | 盈利达到设定点数后启动移动止损 |
| MaxSpread | 高波动时避免交易 |
| UseTimeFilter | 限制交易时段(服务器时间) |
回测建议
此免费EA下载是EA编程入门的优秀起点。修改MQL4代码可添加RSI过滤器、新闻过滤器或多周期确认。如需包含AI优化的完整自动化交易套件,请关注我们的高级版外汇AI EA——订阅获取每周高级策略更新。
参考来源:AutoCompile AI - 基于经典交叉策略的原创MQL4 EA实现,2025年。
```