Summary: 提供一份可直接编译的MT4 EA源码,实现双均线交叉策略。代码包含完整的参数配置、风险计算、开仓逻辑和错误处理。附详细参数说明、编译安装步骤以及各品种的优化建议。




# 均线交叉EA源码分享——一份完整的MQL4趋势策略(可编译)

本文提供一份功能完整的MT4智能交易系统源码,实现经典的双移动平均线交叉策略。代码采用MQL4编写,可直接编译,包含可配置的快慢均线参数、风险管理模块和交易过滤条件。适合希望学习EA自动化逻辑或搭建趋势策略的交易者参考。

策略逻辑说明



该EA基于两条简单移动平均线的交叉关系产生交易信号:
  • 买入信号:快均线上穿慢均线

  • 卖出信号:快均线下穿慢均线


  • EA在开仓前会检查以下条件:
  • 图表已加载足够K线数量(可配置最小K线数)

  • 当前点差不高于允许最大值(避免点差过大开仓)

  • 同品种、同MagicNumber下无持仓(避免重复开仓)


  • 同时支持三种仓位计算方式:固定手数、按账户余额百分比风险计算。支持设置止损、止盈(点数),以及限制交易方向(只多、只空、多空均可)。

    完整源码



    将以下代码保存为 `MACrossover_EA.mq4`,放入MT4的 `MQL4/Experts/` 文件夹,然后在MetaEditor中编译即可。

    ```mql4
    //+------------------------------------------------------------------+
    //| MACrossover_EA.mq4 |
    //| 基于双均线交叉逻辑 |
    //+------------------------------------------------------------------+
    #property copyright "ForexEA Strategy"
    #property version "1.00"
    #property strict

    //--- 输入参数
    input double InpLotSize = 0.1; // 固定手数(当RiskPercent=0时生效)
    input double InpRiskPercent = 2.0; // 单笔风险比例(%账户净值)
    input int InpFastMAPeriod = 9; // 快均线周期
    input int InpSlowMAPeriod = 21; // 慢均线周期
    input int InpStopLoss = 300; // 止损点数
    input int InpTakeProfit = 600; // 止盈点数
    input int InpMagicNumber = 202606; // EA唯一标识号
    input int InpMaxSpread = 30; // 允许最大点差(点数)
    input int InpMinBars = 50; // 最小K线数量
    input int InpTradeDirection = 2; // 交易方向: 0=多空均可,1=只多,2=只空

    //--- 全局变量
    double fastMA_prev, fastMA_curr;
    double slowMA_prev, slowMA_curr;
    int slip = 30; // 滑点(点数)

    //+------------------------------------------------------------------+
    //| 初始化函数 |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    if(InpFastMAPeriod >= InpSlowMAPeriod)
    {
    Print("错误: 快均线周期必须小于慢均线周期");
    return(INIT_PARAMETERS_INCORRECT);
    }
    if(InpStopLoss < 0 || InpTakeProfit < 0)
    {
    Print("止损和止盈不能为负数");
    return(INIT_PARAMETERS_INCORRECT);
    }
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| 反初始化函数 |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    Comment("");
    }

    //+------------------------------------------------------------------+
    //| Tick事件函数 |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    //--- 检查K线数量是否足够
    if(Bars(_Symbol, PERIOD_CURRENT) < InpMinBars)
    {
    Comment("K线数量不足: ", Bars(_Symbol, PERIOD_CURRENT));
    return;
    }

    //--- 检查点差是否过大
    if((MarketInfo(_Symbol, MODE_SPREAD) * Point) * 10000 > InpMaxSpread)
    {
    Comment("点差过大: ", MarketInfo(_Symbol, MODE_SPREAD));
    return;
    }

    //--- 计算当前和前一根K线的均线值
    fastMA_curr = iMA(_Symbol, PERIOD_CURRENT, InpFastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
    fastMA_prev = iMA(_Symbol, PERIOD_CURRENT, InpFastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);
    slowMA_curr = iMA(_Symbol, PERIOD_CURRENT, InpSlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
    slowMA_prev = iMA(_Symbol, PERIOD_CURRENT, InpSlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);

    //--- 检查是否已有持仓
    if(CountPositions() > 0)
    return;

    //--- 信号判断:均线交叉
    bool buySignal = (fastMA_prev <= slowMA_prev && fastMA_curr > slowMA_curr);
    bool sellSignal = (fastMA_prev >= slowMA_prev && fastMA_curr < slowMA_curr);

    //--- 根据交易方向过滤信号
    if(InpTradeDirection == 1) sellSignal = false; // 只多模式,禁止做空
    if(InpTradeDirection == 2) buySignal = false; // 只空模式,禁止做多

    //--- 计算实际开仓手数
    double lot = CalculateLotSize();

    //--- 执行开仓
    if(buySignal)
    {
    OpenOrder(OP_BUY, lot);
    }
    else if(sellSignal)
    {
    OpenOrder(OP_SELL, lot);
    }

    //--- 图表显示当前均线值和手数
    Comment("快均线: ", DoubleToStr(fastMA_curr, _Digits),
    "\n慢均线: ", DoubleToStr(slowMA_curr, _Digits),
    "\n开仓手数: ", lot);
    }

    //+------------------------------------------------------------------+
    //| 统计当前持仓数量 |
    //+------------------------------------------------------------------+
    int CountPositions()
    {
    int count = 0;
    for(int i = OrdersTotal() - 1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == _Symbol && OrderMagicNumber() == InpMagicNumber)
    count++;
    }
    }
    return(count);
    }

    //+------------------------------------------------------------------+
    //| 根据风险比例或固定手数计算开仓手数 |
    //+------------------------------------------------------------------+
    double CalculateLotSize()
    {
    if(InpRiskPercent <= 0)
    return(InpLotSize);

    double riskMoney = AccountBalance() * InpRiskPercent / 100.0;
    double stopPoints = InpStopLoss;
    if(stopPoints <= 0)
    return(InpLotSize);

    double tickValue = MarketInfo(_Symbol, MODE_TICKVALUE);
    double lotStep = MarketInfo(_Symbol, MODE_LOTSTEP);
    double minLot = MarketInfo(_Symbol, MODE_MINLOT);
    double maxLot = MarketInfo(_Symbol, MODE_MAXLOT);

    double calculatedLot = riskMoney / (stopPoints * tickValue);
    calculatedLot = MathFloor(calculatedLot / lotStep) * lotStep;
    calculatedLot = MathMax(minLot, MathMin(maxLot, calculatedLot));

    return(calculatedLot);
    }

    //+------------------------------------------------------------------+
    //| 开仓函数 |
    //+------------------------------------------------------------------+
    void OpenOrder(int cmd, double lot)
    {
    double price, slPrice = 0, tpPrice = 0;
    int slippage = slip;

    if(cmd == OP_BUY)
    {
    price = Ask;
    if(InpStopLoss > 0) slPrice = price - InpStopLoss * Point;
    if(InpTakeProfit > 0) tpPrice = price + InpTakeProfit * Point;
    }
    else if(cmd == OP_SELL)
    {
    price = Bid;
    if(InpStopLoss > 0) slPrice = price + InpStopLoss * Point;
    if(InpTakeProfit > 0) tpPrice = price - InpTakeProfit * Point;
    }
    else return;

    int ticket = OrderSend(_Symbol, cmd, lot, price, slippage, slPrice, tpPrice, "MA Crossover EA", InpMagicNumber, 0, clrNONE);

    if(ticket < 0)
    Print("开仓失败, 错误码: ", GetLastError());
    else
    Print("开仓成功, 订单号: ", ticket);
    }
    //+------------------------------------------------------------------+
    ```


    参数详细说明



    | 参数 | 说明 |
    |------|------|
    | InpLotSize | 固定手数,当风险比例 ≤ 0 时使用 |
    | InpRiskPercent | 单笔交易占账户净值的风险百分比,根据止损点数自动计算手数 |
    | InpFastMAPeriod | 快移动平均线周期 |
    | InpSlowMAPeriod | 慢移动平均线周期 |
    | InpStopLoss | 止损距离(点数) |
    | InpTakeProfit | 止盈距离(点数) |
    | InpMagicNumber | EA的唯一标识符,用于区分不同EA的订单 |
    | InpMaxSpread | 允许的最大点差(点数),超过此值不开仓 |
    | InpMinBars | 开仓前要求图表已加载的最小K线数量 |
    | InpTradeDirection | 交易方向限制:0=多空均可,1=只做多,2=只做空 |

    编译与安装步骤



    1. 打开MT4,点击顶部菜单 工具 → MetaQuotes语言编辑器
    2. 在左侧导航栏找到 Experts 文件夹,右键选择 新建 → 智能交易系统
    3. 删除默认生成的全部代码,将本文的完整代码粘贴进去
    4. 按 F7 或点击工具栏的 编译 按钮
    5. 如果下方输出窗口无红色错误提示,说明编译成功
    6. 返回MT4主界面,在导航器 → 智能交易系统中找到 `MACrossover_EA`
    7. 拖拽到任意图表上,勾选“允许实时自动交易”,设置参数后点击确定

    各品种优化参考值



    | 品种 | 快均线 | 慢均线 | 止损点数 | 止盈点数 |
    |------|--------|--------|----------|----------|
    | XAUUSD(黄金) | 14 | 28 | 800 | 1600 |
    | EURUSD | 9 | 21 | 250 | 500 |
    | GBPUSD | 10 | 30 | 350 | 700 |
    | USDJPY | 12 | 24 | 300 | 600 |

    注意: 以上参数仅为参考,使用前请务必在对应品种和周期上进行回测验证。建议回测建模质量至少达到90%。

    常见问题与调试



    EA没有开仓,可能原因:
  • MT4工具栏上的 自动交易 按钮未点亮(绿色)

  • 当前图表K线数量不足 `InpMinBars`

  • 当前点差大于 `InpMaxSpread` 设定值

  • 已有同MagicNumber的持仓未平仓

  • 快均线周期不小于慢均线周期


  • 查看错误日志:
  • 点击MT4底部的 EA 标签页

  • 点击 日志 标签页


  • 更进一步



    均线交叉EA是趋势跟踪策略的经典入门模板。我们提供的专业版EA在此基础上增加了多周期确认、新闻过滤器、AI动态参数优化等功能,并针对黄金(XAUUSD)做了大量底层优化。

    欢迎访问我们的官网,了解更多经过实盘验证的黄金EA产品。

    ---
    参考来源:
    1. MQL4官方文档 – iMA() 函数说明
    2. MetaQuotes – OrderSend() 参数详解
    3. 本文源码经MT4 build 1420+ 编译及基础测试
    ```