【经典单均线策略】免费源码,零基础学EA编程,读懂经典移动平均线策略源码,评论区持续更新

| 发表于 昨天 12:28 | 显示全部楼层 |复制链接
作为MT4软件自带的EA之一,这个EA真的很经典,原因如下:
一,作为新手教学EA,代码很简单,包括仓位计算,开仓,平仓几个基础部分,修改很容易;
二,包含复利开仓模块,可以直接复制套用到其它EA;
三,包含亏损减仓模块,这其实是最宝贝的部分,只可惜年少不知风控好。

我直接把EA源码进行了备注,对新手很友好,以后我会以这个EA为基础,逐步展开EA的编程与实战的内容分享,欢迎关注。

先上我进行了标注的源码,方便大家进行学习,然后我会把这个EA的使用方法和局限性展开讨论,对源码比较熟悉的可以直接跳过。

--------源码分界线--------
//+------------------------------------------------------------------+
//|                                                                Moving Average.mq4 |
//|                                   Copyright 2000-2026, MetaQuotes Ltd. |
//|                                                               http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright   "J的量化实验室"  //标注EA的作者或者版权,加载EA时可以在窗口看到
#property link        "VX:jiayeeslab"    //标注EA的作者或者版权,加载EA时可以在窗口看到
#property description "Moving Average sample expert advisor"  // 移动平均线示例智能交易系统

#define MAGICMA  20131111  // 魔术码,识别EA订单的主要方法,每个EA对应一个魔术码
//--- 输入参数
input double Lots          =0.1;          // 固定交易手数
input double MaximumRisk   =0.02;         // 最大风险比例(按账户净值的百分比计算手数)
input double DecreaseFactor=3;            // 减仓因子(连续亏损后减少手数)
input int    MovingPeriod  =12;           // 移动平均线周期
input int    MovingShift   =6;            // 移动平均线平移
//+------------------------------------------------------------------+
//| 计算当前持仓订单数量                                             |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)  // 计算指定交易品种当前持仓订单数量
  {
   int buys=0,sells=0;  // 初始化多单和空单计数
//---
   for(int i=0;i<OrdersTotal();i++)  // 遍历所有订单
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;  // 选择订单,如果失败则跳出循环
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA)  // 判断是否为本EA的本品种订单
        {
         if(OrderType()==OP_BUY)  buys++;     // 如果是多单,多单计数加1
         if(OrderType()==OP_SELL) sells++;    // 如果是空单,空单计数加1
        }
     }
//--- 返回订单数量(正数表示多单,负数表示空单,0表示无持仓)
   if(buys>0) return(buys);
   else       return(-sells);
  }
//+------------------------------------------------------------------+
//| 计算最优手数                                                     |
//+------------------------------------------------------------------+
double LotsOptimized()  // 计算优化后的交易手数
  {
   double lot=Lots;  // 初始化为固定手数
   int    orders=HistoryTotal();     // 获取历史订单总数
   int    losses=0;                  // 连续亏损订单计数
//--- 基于风险计算手数
   lot=NormalizeDouble(AccountFreeMargin()*MaximumRisk/1000.0,1);  // 按账户可用保证金的百分比计算手数
//--- 计算连续亏损次数
   if(DecreaseFactor>0)  // 如果减仓因子大于0
     {
      for(int i=orders-1;i>=0;i--)  // 从最近的订单开始向前遍历历史订单
        {
         if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)==false)  // 选择历史订单
           {
            Print("Error in history!");  // 如果选择失败,打印错误
            break;
           }
         if(OrderSymbol()!=Symbol() || OrderType()>OP_SELL)  // 跳过非本品种订单或挂单
            continue;
         //---
         if(OrderProfit()>0) break;     // 如果遇到盈利订单,停止计数(连续亏损被打断)
         if(OrderProfit()<0) losses++;  // 如果是亏损订单,亏损计数加1
        }
      if(losses>1)  // 如果有连续亏损
         lot=NormalizeDouble(lot-lot*losses/DecreaseFactor,1);  // 按亏损次数和减仓因子减少手数
     }
//--- 返回最终手数(确保不低于0.1手)
   if(lot<0.1) lot=0.1;
   return(lot);
  }
//+------------------------------------------------------------------+
//| 检查开仓条件                                                     |
//+------------------------------------------------------------------+
void CheckForOpen()  // 检查是否满足开仓条件
  {
   double ma;  // 移动平均线值
   int    res; // 订单发送结果
//--- 只在新K线的第一个跳动点交易
   if(Volume[0]>1) return;  // 如果当前K线的成交量大于1,说明不是新K线,返回
//--- 获取移动平均线值
   ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);  // 计算当前K线的移动平均线值
//--- 卖出条件:前一根K线开盘在均线上方,收盘在均线下方(形成向下穿越)
   if(Open[1]>ma && Close[1]<ma)
     {
      res=OrderSend(Symbol(),OP_SELL,LotsOptimized(),Bid,3,0,0,"",MAGICMA,0,Red);  // 发送卖出订单
      return;
     }
//--- 买入条件:前一根K线开盘在均线下方,收盘在均线上方(形成向上穿越)
   if(Open[1]<ma && Close[1]>ma)
     {
      res=OrderSend(Symbol(),OP_BUY,LotsOptimized(),Ask,3,0,0,"",MAGICMA,0,Blue);  // 发送买入订单
      return;
     }
//---
  }
//+------------------------------------------------------------------+
//| 检查平仓条件                                                     |
//+------------------------------------------------------------------+
void CheckForClose()  // 检查是否满足平仓条件
  {
   double ma;  // 移动平均线值
//--- 只在新K线的第一个跳动点交易
   if(Volume[0]>1) return;  // 如果当前K线的成交量大于1,说明不是新K线,返回
//--- 获取移动平均线值
   ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);  // 计算当前K线的移动平均线值
//---
   for(int i=0;i<OrdersTotal();i++)  // 遍历所有订单
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;  // 选择订单
      if(OrderMagicNumber()!=MAGICMA || OrderSymbol()!=Symbol()) continue;  // 跳过非本EA的本品种订单
      //--- 检查订单类型
      if(OrderType()==OP_BUY)  // 如果是多单
        {
         if(Open[1]>ma && Close[1]<ma)  // 如果价格向下穿越均线
           {
            if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,White))  // 平掉多单
               Print("OrderClose error ",GetLastError());  // 如果平仓失败,打印错误
           }
         break;  // 找到本EA的订单后就跳出循环(此EA假设只持有一个方向的单子)
        }
      if(OrderType()==OP_SELL)  // 如果是空单
        {
         if(Open[1]<ma && Close[1]>ma)  // 如果价格向上穿越均线
           {
            if(!OrderClose(OrderTicket(),OrderLots(),Ask,3,White))  // 平掉空单
               Print("OrderClose error ",GetLastError());  // 如果平仓失败,打印错误
           }
         break;  // 找到本EA的订单后就跳出循环
        }
     }
//---
  }
//+------------------------------------------------------------------+
//| 每次价格跳动时执行的主函数                                       |
//+------------------------------------------------------------------+
void OnTick()  // MT4的每个跳动点都会调用此函数
  {
//--- 检查历史数据和交易是否允许
   if(Bars<100 || IsTradeAllowed()==false)  // 如果K线数量不足或交易被禁止
      return;  // 直接返回
//--- 计算当前品种的持仓订单数量
   if(CalculateCurrentOrders(Symbol())==0) CheckForOpen();  // 如果没有持仓,检查开仓条件
   else                                    CheckForClose(); // 如果已有持仓,检查平仓条件
//---
  }
//+------------------------------------------------------------------+


------源码分界线-------

这个源码我只进行了备注,没有对代码进行过修改。
接下来我把EA源码进行拆解,大家有问题的可以评论或者私信。


------EA参数说明------
先从EA的外设参数开始说。
Lots,就是初始仓位,这个EA年代久远,那个时候很多平台最小手数都只能下0.1手,所以源码设计的最小仓位是0.1手,这里输入0.01手也不起作用,EA最小仓位只会是0.1手。如何修改,我们稍后再说。

MaximumRisk,单笔交易最大风险,按照源码的意思,是通过账户可用保证金(AccountFreeMargin)来进行计算,这里以后可以修改为按照账户余额或者账户净值进行计算。

DecreaseFactor,减仓因子,按照源码的公式,可以实现连续亏损次数越多,开仓手数越少的效果。配合上面的复利模块加上这个减仓模块,就能实现我们常说的“赢冲输缩”的逻辑!!!当然,这里面还有可以优化的空间。

MovingPeriod,均线周期的参数,10就代表10日均线,20就代表20日均线。源码里面缺少对均线模式的选择,默认SMA(简单移动均线),后期我们可以加上均线模式的选择,来实现不同的效果。

MovingShift,均线的平移,6就代表均线向右边平移6个K线的位置,可以过滤掉一些假信号。我个人对均线平移默认0,如果以后有必要再研究看看。

------EA逻辑说明------
这个EA逻辑很简单,就是一个价格突破均线的交易策略。
开仓逻辑:
当前一根K线的开盘价OPEN位于均线下方,收盘价CLOSE位于均线上方,就开多单;空单相反。
平仓逻辑:
当前一根K线的开盘价OPEN位于均线上方,收盘价CLOSE位于均线下方,就平掉多单;空单相反。

------EA的优势与缺陷------
1.这个EA的最大好处就是,一旦趋势确立,在不人为干预的情况下,能拿到很大一波趋势,理论上图表周期越大(比如日线),均线周期越大(比如100日均线),能拿多少利润,取决于趋势、图表周期,均线周期这三个变量。比如下图,使用H4图表,顺利抓到了比较大的波段,而且一个接近20年前的EA还能盈利!
2.带有复利和亏损减仓模块,可以实现“赢冲输缩”!
3.根据图表,均线等变量可以实现基础的差异化交易。
ScreenShot_2026-03-15_121252_017.png
ScreenShot_2026-03-15_121125_967.png

------EA的弊端------
1.最小只能下0.1手,且因为魔术号不能修改,每次只能做一个品种和一个图表;
2.每次只能持有一个方向的仓位,会错过一些交易信号;
3.没有初始止损,止盈以及移动止损等,风险管理和盈利能力有限;
4.如果价格在均线上下震荡,会频繁亏损;
5.还有没有其它?欢迎留言讨论

今天的分享先到这里,下次我们将讨论如何解决上面的问题,让这个EA可以实盘交易。
欢迎大家留言或私信讨论,我会根据大家的需求来调整分享内容。
filetype

Moving Average 备注版.mq4

11.41 KB, 下载次数: 0, 下载积分: 活跃度 -5  [下载]

源码备注版

评分
  • 1
  • 2
  • 3
  • 4
  • 5
平均分:NAN    参与人数:0    我的评分:未评 下载时遇到问题?
如果有帮助,就支持一下我呗
举报

评论 使用道具

发新帖
EA交易
您需要登录后才可以评论 登录 | 立即注册

Jiayee85

Jiayee85 DDD

从事交易和EA开发十几年,有兴趣的朋友加V交流。
手上各种平台代理已经很多,代理商可以略过,谢谢。

广告位