Elder-Ray (多头力度和空头力度)·进阶篇
📊

Elder-Ray (多头力度和空头力度)·进阶篇

(2/3)· 当 EMA 与多空力度同框,信号稀少却致命,这篇文章拆给你看

案例拆解 第 2/3 篇
把多头力度和空头力度直接叠在 EMA 上看盘,很多人以为条件满足就能进,结果等到信号出现时行情已经跑完半截。Elder-Ray 的难点不在公式,而在同一时间帧里读懂三者博弈的节奏。

「按品种路径归集最小交易量档位」

想在 MT5 里批量统计某个分组(比如 Forex 或 Metals)下所有品种的最小成交手数分布,第一步是拿到当前品种的路径前缀。用 SymbolInfoString 取 SYMBOL_PATH,失败就直接 Print 报错并 return,避免后面空跑。 路径是用反斜杠 \\ 分层的,代码里把 sep_ 设为 "\\",用 StringGetCharacter 取它的 ushort 码,再调 StringSplit 把路径拆成 result_ 数组。k_ 是拆出来的段数,若大于 0 就循环拼回除最后一段以外的父路径,这就是后续筛选同组品种的锚点。 真正干活的是第四步:SymbolsTotal(false) 遍历全部品种,对每个取 SYMBOL_PATH,用 StringFind 看是否包含刚才的父路径,不含就 continue 跳过。命中后取 SYMBOL_VOLUME_MIN 得到最小交易量,再塞进 symbols_array[][2]——第 0 列存 min_volume,第 1 列存出现次数。 这套逻辑跑完,你能直接看到某组内 0.01、0.1 等不同最小手数档位各有多少个品种。外汇与贵金属杠杆高、点值波动大,最小交易量只是风控起点,实际下单手数需结合账户净值与止损距离测算,存在穿仓可能。

MQL5 / C++
class=class="str">"cmt">//| 脚本程序开始函数                                                                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnStart()
  {
class=class="str">"cmt">//--- 第一步
   class="type">class="kw">string current_path="";
   if(!SymbolInfoString(Symbol(),SYMBOL_PATH,current_path))
     {
       Print("ERROR: SYMBOL_PATH");
       class="kw">return;
     }
class=class="str">"cmt">//--- 第二步
   class="type">class="kw">string sep_="\\";           class=class="str">"cmt">// 分隔符
   class="type">class="kw">ushort u_sep_;               class=class="str">"cmt">// 分隔符的代码
   class="type">class="kw">string result_[];            class=class="str">"cmt">// 保存字符串的数组 
class=class="str">"cmt">//--- 获取分隔符代码 
   u_sep_=StringGetCharacter(sep_,class="num">0);
class=class="str">"cmt">//--- 将字符串拆分为子字符串 
   class="type">int k_=StringSplit(current_path,u_sep_,result_);
class=class="str">"cmt">//--- 第三步
class=class="str">"cmt">//--- 现在输出所有获得的字符串 
   if(k_>class="num">0)
     {
       current_path="";
       for(class="type">int i=class="num">0;i<k_-class="num">1;i++)
         current_path=current_path+result_[i]+sep_;
     }
class=class="str">"cmt">//--- 第四步
class=class="str">"cmt">/*
   symbols_array[][class="num">2]:
   [*][minimal volume to close a deal]
   [*][counter]
*/
   class="type">class="kw">double symbols_array[][class="num">2];
   class="type">int symbols_total=SymbolsTotal(class="kw">false);
   for(class="type">int i=class="num">0;i<symbols_total;i++)
     {
       class="type">class="kw">string symbol_name=SymbolName(i,class="kw">false);
       class="type">class="kw">string symbol_path="";
       if(!SymbolInfoString(symbol_name,SYMBOL_PATH,symbol_path))
         class="kw">continue;
       if(StringFind(symbol_path,current_path,class="num">0)==-class="num">1)
         class="kw">continue;
       class="type">class="kw">double min_volume=class="num">0.0;
       if(!SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_MIN,min_volume))
         class="kw">continue;
       class="type">int size=ArrayRange(symbols_array,class="num">0);
       class="type">bool found=class="kw">false;
       for(class="type">int j=class="num">0;j<size;j++)
         {
           if(symbols_array[j][class="num">0]==min_volume)
             {
               symbols_array[j][class="num">1]=symbols_array[j][class="num">1]+class="num">1;
               found=true;
               class="kw">continue;
             }
         }
       if(!found)
         {
           ArrayResize(symbols_array,size+class="num">1,class="num">10);

统计最小成交量后挂上 Elder-Ray 指标框架

扫描完各品种的最小成交量后,代码用 ArrayRange 取二维数组行数,若 size 为 0 就直接打印错误路径并 return,避免后续空循环。实测 CAT(D1) 最小成交量 1.00 出现 100.0 次,RTSRIU8(D1) 出现 77.0 次,说明不同标的的流动性阈值差异明显。 随后接入 Elder-Ray 指标工程,用枚举 ENUM_INPUT_SYMBOLS 切换当前品种或文本文件,默认走文件模式。输入参数里 InpNumberMinLots 锁死 1 手底线,配合 MA 周期 13、EMA 平滑、收盘价计算,给后续多品种批量评估留出统一入口。 外汇与贵金属波动剧烈、杠杆风险高,最小手数设定只是风控第一道闸,实盘前务必在 MT5 策略测试器跑一遍对应品种的历史数据。

MQL5 / C++
   symbols_array[size][class="num">0]=min_volume;
   symbols_array[size][class="num">1]=class="num">1.0;
   }
   }
class=class="str">"cmt">//--- 第五步
   class="type">int size=ArrayRange(symbols_array,class="num">0);
   if(size==class="num">0)
   {
      PrintFormat("ERROR: On path \"%s\" %d symbols",current_path,size);
      class="kw">return;
   }
class=class="str">"cmt">//--- 第六步
   for(class="type">int i=class="num">0;i<size;i++)
      PrintFormat("Minimal volume %.2f occurs %.1f times",symbols_array[i][class="num">0],symbols_array[i][class="num">1]);
   Print("Everything is fine. There are no errors");
class=class="str">"cmt">//---
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                     Elder-Ray class="num">1.mq5 |
class=class="str">"cmt">//|           版权所有 © class="num">2018, Vladimir Karputov |
class=class="str">"cmt">//|            http://wmua.ru/slesar/ |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "版权所有 © class="num">2018, Vladimir Karputov"
class="macro">#class="kw">property link      "http:class=class="str">"cmt">//wmua.ru/slesar/"
class="macro">#class="kw">property version   "class="num">1.000"
class=class="str">"cmt">//---
class=class="str">"cmt">//---
enum ENUM_INPUT_SYMBOLS
  {
  INPUT_SYMBOLS_CURRENT=class="num">0,   class=class="str">"cmt">// 当前品种
  INPUT_SYMBOLS_FILE=class="num">1,      class=class="str">"cmt">// 文本文件
  };
class=class="str">"cmt">//--- 输入参数
class="kw">input ENUM_INPUT_SYMBOLS   InpInputSymbol       = INPUT_SYMBOLS_FILE;   class=class="str">"cmt">// 操作品种 ...
class="kw">input class="type">uint                 InpNumberMinLots     = class="num">1;                     class=class="str">"cmt">// 最小手数
class=class="str">"cmt">//--- 自定义移动平均线输入
class="kw">input class="type">int                  Inp_MA_ma_period     = class="num">13;                    class=class="str">"cmt">// MA: 均化周期 
class="kw">input class="type">int                  Inp_MA_ma_shift      = class="num">0;                     class=class="str">"cmt">// MA: 水平位移 
class="kw">input ENUM_MA_METHOD       Inp_MA_ma_method     = MODE_EMA;              class=class="str">"cmt">// MA: 平滑类型 
class="kw">input ENUM_APPLIED_PRICE   Inp_MA_applied_price = PRICE_CLOSE;           class=class="str">"cmt">// MA: 价格类型 

◍ EA 里怎么挂三条自定义指标线

在 MT5 的 EA 初始化阶段,先把三条外部自定义指标的句柄拿到手,后面才能按索引取缓冲区数据。下面这段输入声明和句柄定义,决定了主图均线、多头力度、空头力度三条线的周期与配色。 多头与空头的均化周期都默认设为 13,线宽统一 2;主图均线颜色用 clrChartreuse,多空分别用 clrBlue 与 clrRed。这些 input 参数在 MT5 属性框里能直接改,不用动代码重编。 句柄变量 handle_iCustom_MA / Bulls / Bears 是全局的,OnInit 里用 iCustom 按当前品种和周期加载名为 "Custom Moving Average Inputs" 的自定义指标,并把外部参数逐个传进去。若句柄返回 INVALID_HANDLE,后续取数会直接报错,所以初始化后最好加一句校验。 外汇与贵金属杠杆高、滑点随机,这类指标句柄加载失败在跨品种切换时概率不低,实盘前先在策略测试器跑一遍品种映射。

MQL5 / C++
class="kw">input class="type">class="kw">color                Inp_MA_Color       = clrChartreuse;      class=class="str">"cmt">// MA: 颜色
class="kw">input class="type">int                  Inp_MA_Width       = class="num">2;                      class=class="str">"cmt">// MA: 宽度
class=class="str">"cmt">//--- 自定义多头力度输入
class="kw">input class="type">int                  Inp_Bulls_ma_period  = class="num">13;                   class=class="str">"cmt">// 多头力度: 均化周期 
class="kw">input class="type">class="kw">color                Inp_Bulls_Color      = clrBlue;              class=class="str">"cmt">// 多头力度: 颜色
class="kw">input class="type">int                  Inp_Bulls_Width      = class="num">2;                    class=class="str">"cmt">// 多头力度: 宽度
class=class="str">"cmt">//--- 自定义空头力度输入
class="kw">input class="type">int                  Inp_Bears_ma_period  = class="num">13;                   class=class="str">"cmt">// 空头力度: 均化周期
class="kw">input class="type">class="kw">color                Inp_Bears_Color      = clrRed;               class=class="str">"cmt">// 空头力度: 颜色
class="kw">input class="type">int                  Inp_Bears_Width      = class="num">2;                    class=class="str">"cmt">// 空头力度: 宽度
class="type">int    handle_iCustom_MA;                                           class=class="str">"cmt">// 用于存储 iCustom 指标句柄的变量
class="type">int    handle_iCustom_Bulls;                                        class=class="str">"cmt">// 用于存储 iCustom 指标句柄的变量
class="type">int    handle_iCustom_Bears;                                        class=class="str">"cmt">// 用于存储 iCustom 指标句柄的变量
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能系统初始化函数                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 智能系统初始化函数                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 创建指标 iCustom 的句柄
   handle_iCustom_MA=iCustom(Symbol(),Period(),"Custom Moving Average Inputs",
                             Inp_MA_ma_period,
                             Inp_MA_ma_shift,

「多指标句柄的创建与失败拦截」

在 MT5 自定义指标初始化阶段,连续调用 iCustom 拉取外部子指标句柄是常见做法。下面这段把移动平均、Bulls Power、Bears Power 三个自定义输入组分别建柄,任何一环拿不到合法句柄就直接 INIT_FAILED 退出,避免后续 OnCalculate 里空指针算崩。 建柄顺序上,先处理 Custom Moving Average Inputs,传入均线周期、算价方式、颜色、线宽与应用价格;随后是 Custom Bulls Power Inputs,只吃均线周期、颜色、线宽三个外部参数;最后 Bears Power 同理。三个 handle 变量独立,互不共用。 每个 if(handle==INVALID_HANDLE) 分支都用 PrintFormat 把品种、周期枚举和 GetLastError 代码打进日志。实盘加载若看到 'Failed to create handle of the iCustom indicator' 配错误码 4001 之类,优先查对应子指标文件是否真在 MQL5/Indicators 目录里——这是 9 成初始化失败的根因。 外汇与贵金属波动大、滑点突兀,这类依赖多自定义指标的 EA 在切换品种或离线重连时句柄失效概率会明显抬升,建议每次 OnTradeTransaction 后顺手校验 handle 有效性。

MQL5 / C++
                     Inp_MA_ma_method,
                     Inp_MA_Color,
                     Inp_MA_Width,
                     Inp_MA_applied_price);
class=class="str">"cmt">//--- 如果未创建句柄 
   if(handle_iCustom_MA==INVALID_HANDLE)
    {
     class=class="str">"cmt">//--- 告之失败并输出错误代码 
     PrintFormat("Failed to create handle of the iCustom indicator(\"Custom Moving Average Inputs\") for the symbol %s/%s, error code %d",
                 Symbol(),
                 EnumToString(Period()),
                 GetLastError());
     class=class="str">"cmt">//--- 指标提前停止 
     class="kw">return(INIT_FAILED);
    }
class=class="str">"cmt">//--- 创建指标 iCustom 的句柄
   handle_iCustom_Bulls=iCustom(Symbol(),Period(),"Custom Bulls Power Inputs",
                                Inp_Bulls_ma_period,
                                Inp_Bulls_Color,
                                Inp_Bulls_Width);
class=class="str">"cmt">//--- 如果未创建句柄 
   if(handle_iCustom_Bulls==INVALID_HANDLE)
    {
     class=class="str">"cmt">//--- 告之失败并输出错误代码 
     PrintFormat("Failed to create handle of the iCustom indicator(\"Custom Bulls Power Inputs\") for the symbol %s/%s, error code %d",
                 Symbol(),
                 EnumToString(Period()),
                 GetLastError());
     class=class="str">"cmt">//--- 指标提前停止 
     class="kw">return(INIT_FAILED);
    }
class=class="str">"cmt">//--- 创建指标 iCustom 的句柄
   handle_iCustom_Bears=iCustom(Symbol(),Period(),"Custom Bears Power Inputs",
                                Inp_Bears_ma_period,
                                Inp_Bears_Color,
                                Inp_Bears_Width);
class=class="str">"cmt">//--- 如果未创建句柄 
   if(handle_iCustom_Bears==INVALID_HANDLE)
    {
     class=class="str">"cmt">//--- 告之失败并输出错误代码 
     PrintFormat("Failed to create handle of the iCustom indicator(\"Custom Bears Power Inputs\") for the symbol %s/%s, error code %d",
                 Symbol(),
                 EnumToString(Period()),
                 GetLastError());
     class=class="str">"cmt">//--- 指标提前停止 

把多品种指标句柄挂到主图与子窗口

EA 初始化阶段常需要把自定义指标同时铺到图表上:主图放一条 MA,下方子窗口分别放 Bulls、Bears 力量指标。先用 ChartGetInteger(0,CHART_WINDOWS_TOTAL) 拿到已有子窗口数,再按 0、windows_total、windows_total+1 的顺序调 ChartIndicatorAdd,就能避免手动画窗口。 若从公共目录的 txt 文件批量读品种(InpInputSymbol==INPUT_SYMBOLS_FILE),每读一行就用 CreateHandles 建三套句柄;建不出来直接 continue 跳过该品种,而不是整体 INIT_FAILED,这样某个品种无数据也不会拖垮整个 EA。 读到的 name 和对应 MA/Bulls/Bears 句柄会写进 m_symbols_array 与 m_handles_array,ArrayResize 每次 +1 并预留 10 个冗余位,循环里 Print("Iteration ",counter) 能看到实际迭代次数——若文件有 20 行就打印到 Iteration 20,可据此确认文件是否被正确遍历。 外汇与贵金属波动剧烈,这类多品种监控 EA 在极端跳空时可能漏建句柄,实盘前务必在 MT5 用模拟品种文件跑一遍计数。

MQL5 / C++
  class="kw">return(INIT_FAILED);
   }
  ChartIndicatorAdd(class="num">0,class="num">0,handle_iCustom_MA);
  class="type">int windows_total=(class="type">int)ChartGetInteger(class="num">0,CHART_WINDOWS_TOTAL);
  ChartIndicatorAdd(class="num">0,windows_total,handle_iCustom_Bulls);
  ChartIndicatorAdd(class="num">0,windows_total+class="num">1,handle_iCustom_Bears);
class=class="str">"cmt">//---
  class="kw">return(INIT_SUCCEEDED);
   }
            if(!m_symbol.Name(name)) class=class="str">"cmt">// 设置品名
             {
              m_file_txt.Close();
              class="kw">return(INIT_FAILED);
             }
class=class="str">"cmt">//---
  if(m_file_txt.Open(InpFileName,FILE_READ|FILE_COMMON)==INVALID_HANDLE)
   {
    PrintFormat("ERROR: \"%s\" file in the Data Folder Common folder is not open: %d",InpFileName,GetLastError());
    class="kw">return(INIT_FAILED);
   }
class=class="str">"cmt">//--- 品种信息对象或文件 txt 对象
  if(InpInputSymbol==INPUT_SYMBOLS_FILE)
   {
    class=class="str">"cmt">//--- 从文件读数据 
    class="type">int counter=class="num">0;
    class="kw">while(!m_file_txt.IsEnding())
     {
      counter++;
      Print("Iteration ",counter);
      class="type">class="kw">string name=m_file_txt.ReadString();
      if(!m_symbol.Name(name)) class=class="str">"cmt">// 设置品名
       {
        m_file_txt.Close();
        class="kw">return(INIT_FAILED);
       }
      class="type">int MA,Bulls,Bears;
      if(!CreateHandles(name,Period(),MA,Bulls,Bears))
        class="kw">continue; class=class="str">"cmt">//class="kw">return(INIT_FAILED);
      class="type">int size=ArraySize(m_symbols_array);
      ArrayResize(m_symbols_array,size+class="num">1,class="num">10);
      ArrayResize(m_handles_array,size+class="num">1,class="num">10);
      m_symbols_array[size]=name;
      m_handles_array[size][class="num">0]=MA;
      m_handles_array[size][class="num">1]=Bulls;
      m_handles_array[size][class="num">2]=Bears;
     }
    m_file_txt.Close();
   }
  else
   {
    if(!m_symbol.Name(Symbol())) class=class="str">"cmt">// 设置品名
      class="kw">return(INIT_FAILED);
   }
class=class="str">"cmt">//---
  ChartIndicatorAdd(class="num">0,class="num">0,handle_iCustom_MA);

◍ 定时器里的新柱判定与趋势投票

把扫描逻辑塞进 OnTimer,核心是先拦掉旧柱重复计算:用 iTime 取当前品种 0 号柱时间,和上次记录的 m_prev_bars[i] 比对,相等就 continue,只在时间变了(新柱诞生)才往下跑。这套做法能让多品种监控省掉大量无效刷新。 趋势判定走的是 EMA 序列的“票数统计”:从指标句柄取长度 InpBarsEMA+2 的数组并设为时间序列,随后对 j=1 到 InpBarsEMA 的区间逐根比较 ema_array[j] 与 ema_array[j+1],向上计数 upwards、向下计数 downward。若某一方向占比超过 InpMinTrendPercentage%(如设 60 即 0.6*InpBarsEMA 票),就 Print 出 UP / DOWN,否则 NONE。 极值搜索另写 NearestExtremum,传 MODE_LOW 时 CopyLow 拉最近 100 根最低价,线性扫出最小值赋给 price 引用返回。外汇与贵金属波动剧烈,这类“投票式”趋势信号只代表概率倾向,实盘前请在 MT5 策略测试器用真实点差验证。

MQL5 / C++
class="type">void OnTimer()
  {
class=class="str">"cmt">//---
   class="type">int size=ArraySize(m_symbols_array);
   for(class="type">int i=class="num">0;i<size;i++)
     {
      class=class="str">"cmt">//--- 我们仅在新柱线诞生时工作
      class="type">class="kw">datetime PrevBars=m_prev_bars[i];
      class="type">class="kw">datetime time_0=iTime(m_symbols_array[i],Period(),class="num">0);
      if(time_0==PrevBars)
         class="kw">continue;
      m_prev_bars[i]=time_0;
      class="type">class="kw">double ema_array[];
      ArraySetAsSeries(ema_array,true);
class=class="str">"cmt">/*
   m_handles_array[*][MA][Bulls][Bears]
*/
      if(!iMAGetArray(m_handles_array[i][class="num">0],class="num">0,InpBarsEMA+class="num">2,ema_array))
         class="kw">continue;
      class="type">int upwards=class="num">0;
      class="type">int downward=class="num">0;
      for(class="type">int j=class="num">1;j<InpBarsEMA+class="num">1;j++)
        {
         if(ema_array[j]>ema_array[j+class="num">1])
            upwards++;
         else if(ema_array[j]<ema_array[j+class="num">1])
            downward++;
        }
      if((class="type">class="kw">double)upwards>InpBarsEMA*InpMinTrendPercentage/class="num">100.0)
         Print("On ",m_symbols_array[i]," trend UP!");
      else if((class="type">class="kw">double)downward>InpBarsEMA*InpMinTrendPercentage/class="num">100.0)
         Print("On ",m_symbols_array[i]," trend DOWN!");
      else
         Print("On ",m_symbols_array[i]," trend NONE!");
     }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| 搜索最近的极值                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool NearestExtremum(ENUM_SERIESMODE type,class="type">class="kw">double &price)
  {
   if(type==MODE_LOW)
     {
      class=class="str">"cmt">//--- 搜索最近的低点极值
      class="type">class="kw">double low_array[];
      ArraySetAsSeries(low_array,true);
      class="type">int copy=CopyLow(m_symbol.Name(),Period(),class="num">0,class="num">100,low_array);
      if(copy==-class="num">1)
         class="kw">return(class="kw">false);
      class="type">class="kw">double low=DBL_MAX;
      for(class="type">int k=class="num">0;k<copy;k++)
        {
         if(low_array[k]<low)
            low=low_array[k];
让小布替你扫这些稀少信号
小布盯盘的 AIGC 已内置 Elder-Ray 类多空力度诊断,打开对应品种日线页即可看到 EMA 与力度的背离提示,你只需要在信号区做决策。

常见问题

因为要求趋势向上、空头力度低于零且走高,这种组合在日线级别本就是少数共振,属于高过滤系统,信号少但噪音低。
空头力度最小是单根柱线上空头能将价格压到的最低点,反映竭尽;空头力度是持续将价格拉低于 EMA 的能力,前者看拐点后者看动能。
目前品种页会标注最近两日最高与最低区,结合多空力度状态你可快速定位挂单位,但具体止损距离仍需按仓位自行设定,外汇贵金属波动高风险大。
原文 EA 仅适配净持账户,意味着同品种单向持仓,对冲平台需改平仓逻辑,否则可能触发平台拒绝。
它用更高周期定趋势、中周期看力度、小周期找入场,缓解单图信号稀少的问题,但参数错位会放大滞后。