English Русский Deutsch 日本語
preview
价格行为分析工具包开发(第二十五部分):双指数移动平均线(EMA)分形突破策略

价格行为分析工具包开发(第二十五部分):双指数移动平均线(EMA)分形突破策略

MetaTrader 5指标 |
162 3
Christian Benjamin
Christian Benjamin

内容



概述

本文介绍一款工具的开发情况,该工具旨在通过运用比尔·威廉姆斯最初开发的分形指标,为价格行为分析提供更深入、更具洞察力的评估。

比尔M·威廉姆斯(Bill M. Williams)(1932—2019)是一位颇具影响力的美国交易员和作家,在交易心理学、技术分析以及金融市场混沌理论方面颇有建树。在其职业生涯中,威廉姆斯深耕股票、大宗商品和外汇(Forex)市场,开发了一系列创新的技术分析工具,用于识别趋势和潜在的反转点。他的一些最为著名的指标包括加速/减速振荡器鳄鱼指标动量振荡器分形指标鳄鱼振荡器以及市场便利指数。如今,这些指标因其分析市场行为的有效性,在外汇、股票及其他金融市场中得到了广泛应用。

分形指标是威廉姆斯方法论中的一个关键组成部分,在我们的方法中,该指标结合了14周期和200周期的指数移动平均线(EMA)。通过将分形形态与这些动态趋势过滤器相结合,我们的智能交易系统(EA)旨在精准识别潜在市场反转,并确保交易方向与整体趋势一致。这套组合策略能够提早发现反转信号,并确认趋势强度,从而提升交易决策能力,最终带来更可靠、高概率的入场点。分形分析与移动平均线的协同作用,为交易者提供了强大的工具,使其不仅能够更深入地解读价格行为,还有助于优化交易时机并改善交易结果。


策略解析

我们的EA运用了三个关键技术指标:分形指标 、EMA 14EMA 200。分形指标由比尔·威廉姆斯开发,是技术分析中用于识别潜在趋势反转的常用工具。它能检测特定的价格形态(即分形),表现为局部的高点或低点,预示着市场可能的转折点。这些分形可作为支撑或阻力位,为交易者提供直观的提示,以便他们精确地把握入场和离场时机。

指数移动平均线(EMA)与简单移动平均线(SMA)的最大区别在于,EMA更关注最新价格,因此反应更灵敏。这种加权是通过一个平滑因子来放大近期价格的权重,使得EMA能够更迅速地响应市场变化。因此,EMA在捕捉新兴趋势和提供趋势转变的早期信号方面非常有效,尤其是在较短的周期内。

基于我们的设置,EMA 14反映了短期市场动能,能迅速对近期价格变动做出反应,适合及时入场交易。相反,EMA 200是一个长期趋势指标,能平滑掉短期波动,揭示整体市场方向。当价格在EMA 200之上交易时,表明长期为牛市趋势;当价格在EMA 200之下时,则暗示为熊市趋势。此外,EMA 200经常充当动态支撑位或阻力位,帮助交易者过滤掉虚假信号并确认趋势的有效性。

买入信号(看涨突破):

当同时满足多个条件,表明市场将强劲上行时,会触发买入信号。首先,EA会监测最近的分形低点水平(即支撑位)。它会寻找当前市场价格上穿该分形支撑位的情景,这预示着可能出现突破。为确认市场处于上升趋势,EA会检查当前价格是否同时高于EMA 14和EMA 200,且EMA 14位于EMA 200之上,这表明趋势为牛市。当这些条件都满足时,就会生成买入信号。图表上会显示向上箭头和标签等视觉提示,并可触发警报通知交易者。

  • 价格上穿最近的支撑分形水平。
  • 当前价格高于EMA 14和EMA 200。
  • EMA 14高于EMA 200,确认上升趋势。

图例1. 看涨突破

卖出信号(看跌跌破):

相反,当价格下穿重要的分形高点(阻力位)时,会发出卖出信号,表明市场可能出现下行走势。EA会盯紧最近的分形高点,等价格跌穿这个位置。为确认市场处于下行趋势,它会验证当前价格是否低于两条EMA,且EMA 14位于EMA 200下方,这表明趋势为熊市。当满足以下条件时,即价格下穿阻力分形水平且趋势保持熊市,便会发出卖出信号。图表上会绘制类似的视觉标记,如下行箭头和标签,并可能触发警报。

  • 价格下穿最近的阻力分形水平。
  • 当前价格低于EMA 14和EMA 200。
  • EMA 14位于EMA 200下方,确认下行趋势。

图例2. 看跌突破


代码组件分解

该EA将分形分析与移动平均线趋势过滤器相结合,用于识别并直观呈现市场中潜在的突破点。其通过在最近的分形高点和低点处绘制水平线,标示出关键支撑位和阻力位区域。当价格顺沿EMA所确认的趋势方向穿越这些水平位时,该EA会生成视觉信号(箭头和标签)并发出声音警报,从而简化交易者的决策过程。其模块化设计包含专门用于绘图、信号提示和数据管理的功能,使其能够适应各种交易风格和偏好。此外,该系统注重资源管理和清理,确保运行过程中的稳定性和清晰性。总体而言,这款将指标与视觉提示巧妙结合的先进工具,为交易者提供了一个全方位解决方案,有助于在趋势行情中捕捉分形突破机会。

头文件与基础数据

代码的初始部分包含描述脚本作者、版本及许可信息的元数据。以 //+------------------------------------------------------------------+ 包围的注释用于明确脚本的用途并提供归属信息。#property 指令用于指定版权所有者、指向MetaTrader社区作者个人资料的超链接、版本号,并强制实施严格的编译规则。#property strict指令尤为重要,因为它指示编译器遵循更严格的编码标准,从而捕获诸如未声明变量或类型不匹配等潜在错误。这些元数据和指令不会影响代码的实际运行,但可以作为文档,并在编译过程中确保代码质量。

//+------------------------------------------------------------------+
//|                              Fractal Breakout, EMA 14 and EMA 200|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/en/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

输入参数

这里定义了一些可调参数,用户无需修改核心代码就能直接调整参数。这些输入项包括分析所采用的时间周期(InpTimeframe)、要检查的K线数量(InpHistoryBars),以及用作趋势过滤器的两条EMA的周期(InpEMA14Period和InpEMA200Period)

颜色参数(InpBullColor和InpBearColor)允许用户自行定制突破箭头和线条的样式。文本标签(InpBullText和InpBearText)为信号提供描述性注释,增强图表的可读性。箭头偏移量(InpArrowOffset)和字体大小(InpArrowFontSize)进一步控制视觉呈现,使交易者能够方便地定位标签。

警报系统由InpAlertsEnabled控制,该参数用于开启或关闭弹出通知,而InpAlertSoundFile则指定检测到信号时播放的声音文件。这些参数使该EA具有灵活性,能够适应不同的交易风格和视觉偏好,使用户能够根据自己的具体需求进行定制。

input ENUM_TIMEFRAMES InpTimeframe      = PERIOD_CURRENT;  // chart TF
input int             InpHistoryBars    = 200;             // bars back to scan
input int             InpEMA14Period    = 14;              // fast EMA
input int             InpEMA200Period   = 200;             // slow EMA
input color           InpBullColor      = clrLime;         // bullish arrow color
input color           InpBearColor      = clrRed;          // bearish arrow color
input string          InpBullText       = "BULL Break";    // bullish label
input string          InpBearText       = "BEAR Break";    // bearish label
input int             InpArrowOffset    = 20;              // offset in points for label
input int             InpArrowFontSize  = 12;              // size of the arrow glyph
input bool            InpAlertsEnabled  = true;            // show pop-up alerts
input string          InpAlertSoundFile = "alert.wav";     // sound file in /Sounds/

全局变量与指标句柄

在函数外部声明的变量用于存储指标句柄和分形数据数组。句柄(hFractals、hEMA14、hEMA200)是对初始化过程中创建的指标实例的引用。这些句柄至关重要,因为它们使EA能够与指标的数据缓冲区进行交互,从而获取实时和历史数据。数组fractalUp[]fractalDown[]是动态分配的缓冲区,将分别存储分形的高点和低点。将这些变量作为全局变量进行管理,可确保在整个脚本中(尤其是在发生实时分析的OnTick处理过程中)能够访问它们。

int  hFractals, hEMA14, hEMA200;
double fractalUp[], fractalDown[];

初始化(OnInit)

在启动时,会调用OnInit函数来配置必要的资源。该函数使用iFractalsiMA等函数为分形和EMA创建指标句柄,并指定交易品种和时间周期。成功创建这些句柄非常关键;如果某个句柄无效(例如,由于参数错误或数据不可用),会导致初始化失败,EA运行异常。

随后,代码将分形数据数组配置为序列模式,这意味着最新数据将位于索引0处,从而简化了向后分析的过程。将这些数组的大小调整为指定的K线数量,可确保数据缓冲区的大小适当,以便进行高效处理。总之,这一步骤为可靠的数据检索奠定了基础,这对于准确地检测突破至关重要。

int OnInit()
  {
   hFractals = iFractals(_Symbol, InpTimeframe);
   hEMA14    = iMA(_Symbol, InpTimeframe, InpEMA14Period, 0, MODE_EMA, PRICE_CLOSE);
   hEMA200   = iMA(_Symbol, InpTimeframe, InpEMA200Period,0, MODE_EMA, PRICE_CLOSE);
   if(hFractals==INVALID_HANDLE || hEMA14==INVALID_HANDLE || hEMA200==INVALID_HANDLE)
      return(INIT_FAILED);

   ArraySetAsSeries(fractalUp,   true);
   ArraySetAsSeries(fractalDown, true);
   ArrayResize(fractalUp,   InpHistoryBars);
   ArrayResize(fractalDown, InpHistoryBars);
   return(INIT_SUCCEEDED);
  }

反初始化(OnDeinit)

当EA从图表中移除或交易终端关闭时,OnDeinit函数将执行以清理资源。该函数使用IndicatorRelease释放指标句柄,从而释放相关内存并防止内存泄漏。此外,它会遍历图表上的所有对象,识别并删除以“FB_”为前缀的对象,这些对象是EA运行过程中创建的可视化信号(箭头、标签、线条)。这种清理操作可确保EA停用后图表上不会残留多余的对象,从而保持图表环境的整洁,并防止在后续分析中出现潜在冲突或混淆。

void OnDeinit(const int reason)
  {
   if(hFractals!=INVALID_HANDLE)
      IndicatorRelease(hFractals);
   if(hEMA14   !=INVALID_HANDLE)
      IndicatorRelease(hEMA14);
   if(hEMA200  !=INVALID_HANDLE)
      IndicatorRelease(hEMA200);

   for(int i=ObjectsTotal(0)-1; i>=0; i--)
     {
      string n = ObjectName(0,i);
      if(StringFind(n,"FB_")>=0)
         ObjectDelete(0,n);
     }
  }

主处理逻辑(OnTick)

核心逻辑位于OnTick函数中,该函数在每次新市场报价到达时执行。它首先使用CopyBuffer获取两条EMA的最新值。这些EMA值作为趋势过滤器:它们的相对位置表明市场处于上升趋势还是下降趋势,从而辅助突破决策。如果EMA数据获取失败,函数将提前退出,以防止生成错误信号。接下来,函数再次通过错误检查获取最近高点和低点的分形数据缓冲区。代码向后遍历这些缓冲区,以查找最近的有效分形点,忽略任何占位符EMPTY_VALUEs。通过识别最近的重要分形点,EA确定可能触发突破的关键支撑位或阻力位。随后,它使用辅助函数在这些水平位绘制水平线,以供视觉参考。终端调试输出提供有关这些水平位、价格和EMA状态的实时信息,帮助交易者验证逻辑。

最后,EA检查突破条件:当上一根K线收盘价高于低点分形水平,当前K线收盘价下穿该水平,且价格在下降趋势中位于两条EMA下方时,发生看跌突破。相反,当上一根K线收盘价低于高点分形水平,当前K线收盘价上穿该水平,且价格在上升趋势中位于两条EMA上方时,确认看涨突破。检测到这些条件将触发信号生成函数。

void OnTick()
  {
   // 1) Read current EMAs
   double ema14Arr[1], ema200Arr[1];
   if(CopyBuffer(hEMA14,0,0,1,ema14Arr)<=0 || CopyBuffer(hEMA200,0,0,1,ema200Arr)<=0)
      return;
   double ema14_now  = ema14Arr[0];
   double ema200_now = ema200Arr[0];

   // 2) Read fractal history (skip current bar)
   if(CopyBuffer(hFractals,0,1,InpHistoryBars,fractalUp)<=0 ||
      CopyBuffer(hFractals,1,1,InpHistoryBars,fractalDown)<=0)
      return;

   // 3) Find most recent valid fractals (ignore EMPTY_VALUE)
   int upShift=-1, downShift=-1;
   for(int i=1; i<InpHistoryBars; i++)
     {
      if(fractalUp[i]   != EMPTY_VALUE && upShift<0)
         upShift   = i+1;
      if(fractalDown[i] != EMPTY_VALUE && downShift<0)
         downShift = i+1;
      if(upShift>0 && downShift>0)
         break;
     }

   // 4) Levels
   double lvlUp   = (upShift>0)   ? fractalUp[upShift-1]     : 0.0;
   double lvlDown = (downShift>0) ? fractalDown[downShift-1] : 0.0;

   // 5) Draw level lines
   DrawHLine("FB_LevelUp",   lvlUp,   InpBullColor);
   DrawHLine("FB_LevelDown", lvlDown, InpBearColor);

   // 6) DEBUG print
   double prevC = iClose(_Symbol,InpTimeframe,1);
   double currC = iClose(_Symbol,InpTimeframe,0);
   PrintFormat(
      "DBG lvlUp=%.5f lvlDown=%.5f prevC=%.5f currC=%.5f EMA14=%.5f EMA200=%.5f",
      lvlUp, lvlDown, prevC, currC, ema14_now, ema200_now
   );

   // 7) Breakouts on the last closed candle (shift=1)

   // Bearish breakout detection
   if(lvlDown>0
      && prevC>=lvlDown && currC<lvlDown
      && currC<ema14_now && currC<ema200_now
      && ema200_now>ema14_now)
     {
      Print(">>> Bear breakout triggered");
      Signal(false, 1, lvlDown, ema14_now, ema200_now);
     }

   // Bullish breakout detection
   if(lvlUp>0
      && prevC<=lvlUp && currC>lvlUp
      && currC>ema14_now && currC>ema200_now
      && ema14_now>ema200_now)
     {
      Print(">>> Bull breakout triggered");
      Signal(true, 1, lvlUp, ema14_now, ema200_now);
     }
  }

绘制水平线(DrawHLine)

该实用函数负责在图表上绘制水平线并更新位置。调用时,它会检查是否存在指定名称的水平线;如果不存在,则在给定价格水平处以指定颜色创建一条新的虚线。如果该水平线已存在,则仅更新其位置。该方法可防止出现多条重叠的水平线,并确保视觉效果与最新的分形分析结果保持一致。虚线样式有助于将这些水平线与其他图表对象区分开来,突出其作为支撑位或阻力位的作用。对于交易者而言,这种直观的提示非常有效,能帮助快速识别出突破区域,无论手动或自动化决策都更方便。

void DrawHLine(string name, double price, color clr)
  {
   if(price<=0)
      return;
   if(ObjectFind(0,name)<0)
     {
      ObjectCreate(0,name,OBJ_HLINE,0,0,price);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
      ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
     }
   else
      ObjectSetDouble(0,name,OBJPROP_PRICE,price);
  }

信号生成与可视化(Signal)

Signal函数封装了与突破事件信号生成相关的所有操作。该函数首先获取信号发生所在K线的时间戳,以确保可视化对象的精确定位。随后,根据突破是看涨还是看跌,在突破水平位创建一个向上或向下的箭头对象,箭头颜色与趋势保持一致。为确保清晰可见,箭头的外观通过宽度和大小参数进行了自定义。除箭头外,还会在垂直方向上稍作偏移添加一个文本标签,显示诸如“看涨突破”或“看跌突破”等描述性信息,以强化视觉提示。

该函数会在“专家”选项卡中记录一条详细信息,包括确切时间、水平位、收盘价和EMA值,为触发信号提供完整的记录。如果启用了警报功能,则会弹出消息窗口并播放声音文件,以便立即通知交易者。视觉、声音加上日志三管齐下,确保交易者能够及时获知潜在的突破机会,从而迅速采取行动。

void Signal(bool isBull, int shift, double level, double ema14, double ema200)
  {
   datetime t     = iTime(_Symbol,InpTimeframe,shift);
   double   price = level;
   string   side  = isBull ? "Bull" : "Bear";

   // Arrow
   string arrowName = StringFormat("FB_%sArrow_%d", side, (int)t);
   ObjectCreate(0, arrowName, OBJ_ARROW, 0, t, price);
   ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE,    isBull?233:234);
   ObjectSetInteger(0, arrowName, OBJPROP_COLOR,        isBull?InpBullColor:InpBearColor);
   ObjectSetInteger(0, arrowName, OBJPROP_WIDTH,        2);
   ObjectSetInteger(0, arrowName, OBJPROP_FONTSIZE,     InpArrowFontSize);

   // Label
   string lab = arrowName + "_L";
   double offset = (isBull ? InpArrowOffset : -InpArrowOffset) * _Point;
   ObjectCreate(0, lab, OBJ_TEXT, 0, t, price + offset);
   ObjectSetString(0, lab, OBJPROP_TEXT,    isBull?InpBullText:InpBearText);
   ObjectSetInteger(0, lab, OBJPROP_COLOR,  isBull?InpBullColor:InpBearColor);
   ObjectSetInteger(0, lab, OBJPROP_FONTSIZE, 11);

   // Log message
   string msg = StringFormat(
                   "%s breakout at %s | Level=%.5f | Close=%.5f | EMA14=%.5f | EMA200=%.5f",
                   side, TimeToString(t, TIME_DATE|TIME_SECONDS),
                   level, iClose(_Symbol,InpTimeframe,shift),
                   ema14, ema200
                );
   Print(msg);

   // Alert and sound
   if(InpAlertsEnabled)
     {
      Alert(msg);
      if(StringLen(InpAlertSoundFile)>0)
         PlaySound(InpAlertSoundFile);
     }
  }

MQL5代码

//+------------------------------------------------------------------+
//|                              Fractal Breakout, EMA 14 and EMA 200|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/en/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//---inputs
input ENUM_TIMEFRAMES InpTimeframe      = PERIOD_CURRENT;  // chart TF
input int             InpHistoryBars    = 200;             // bars back to scan
input int             InpEMA14Period    = 14;              // fast EMA
input int             InpEMA200Period   = 200;             // slow EMA
input color           InpBullColor      = clrLime;         // bullish arrow color
input color           InpBearColor      = clrRed;          // bearish arrow color
input string          InpBullText       = "BULL Break";    // bullish label
input string          InpBearText       = "BEAR Break";    // bearish label
input int             InpArrowOffset    = 20;              // offset in points for label
input int             InpArrowFontSize  = 12;              // size of the arrow glyph
input bool            InpAlertsEnabled  = true;            // show pop-up alerts
input string          InpAlertSoundFile = "alert.wav";     // sound file in /Sounds/

//---indicator handles & buffers
int  hFractals, hEMA14, hEMA200;
double fractalUp[], fractalDown[];

//+------------------------------------------------------------------+
//| Expert initialization                                            |
//+------------------------------------------------------------------+
int OnInit()
  {
   hFractals = iFractals(_Symbol, InpTimeframe);
   hEMA14    = iMA(_Symbol, InpTimeframe, InpEMA14Period, 0, MODE_EMA, PRICE_CLOSE);
   hEMA200   = iMA(_Symbol, InpTimeframe, InpEMA200Period,0, MODE_EMA, PRICE_CLOSE);
   if(hFractals==INVALID_HANDLE || hEMA14==INVALID_HANDLE || hEMA200==INVALID_HANDLE)
      return(INIT_FAILED);

   ArraySetAsSeries(fractalUp,   true);
   ArraySetAsSeries(fractalDown, true);
   ArrayResize(fractalUp,   InpHistoryBars);
   ArrayResize(fractalDown, InpHistoryBars);
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(hFractals!=INVALID_HANDLE)
      IndicatorRelease(hFractals);
   if(hEMA14   !=INVALID_HANDLE)
      IndicatorRelease(hEMA14);
   if(hEMA200  !=INVALID_HANDLE)
      IndicatorRelease(hEMA200);

   for(int i=ObjectsTotal(0)-1; i>=0; i--)
     {
      string n = ObjectName(0,i);
      if(StringFind(n,"FB_")>=0)
         ObjectDelete(0,n);
     }
  }

//+------------------------------------------------------------------+
//| Tick handler                                                     |
//+------------------------------------------------------------------+
void OnTick()
  {
// 1) Read current EMAs
   double ema14Arr[1], ema200Arr[1];
   if(CopyBuffer(hEMA14,0,0,1,ema14Arr)<=0 || CopyBuffer(hEMA200,0,0,1,ema200Arr)<=0)
      return;
   double ema14_now  = ema14Arr[0];
   double ema200_now = ema200Arr[0];

// 2) Read fractal history (skip current bar)
   if(CopyBuffer(hFractals,0,1,InpHistoryBars,fractalUp)<=0 ||
      CopyBuffer(hFractals,1,1,InpHistoryBars,fractalDown)<=0)
      return;

// 3) Find most recent valid fractals (ignore EMPTY_VALUE)
   int upShift=-1, downShift=-1;
   for(int i=1; i<InpHistoryBars; i++)
     {
      if(fractalUp[i]   != EMPTY_VALUE && upShift<0)
         upShift   = i+1;
      if(fractalDown[i] != EMPTY_VALUE && downShift<0)
         downShift = i+1;
      if(upShift>0 && downShift>0)
         break;
     }

// 4) Levels
   double lvlUp   = (upShift>0)   ? fractalUp[upShift-1]     : 0.0;
   double lvlDown = (downShift>0) ? fractalDown[downShift-1] : 0.0;

// 5) Draw level lines
   DrawHLine("FB_LevelUp",   lvlUp,   InpBullColor);
   DrawHLine("FB_LevelDown", lvlDown, InpBearColor);

// 6) DEBUG print
   double prevC = iClose(_Symbol,InpTimeframe,1);
   double currC = iClose(_Symbol,InpTimeframe,0);
   PrintFormat(
      "DBG lvlUp=%.5f lvlDown=%.5f prevC=%.5f currC=%.5f EMA14=%.5f EMA200=%.5f",
      lvlUp, lvlDown, prevC, currC, ema14_now, ema200_now
   );

// 7) Breakouts on the last closed candle (shift=1)

// Bearish breakout
   if(lvlDown>0
      && prevC>=lvlDown && currC<lvlDown
      && currC<ema14_now && currC<ema200_now
      && ema200_now>ema14_now)
     {
      Print(">>> Bear breakout triggered");
      Signal(false, 1, lvlDown, ema14_now, ema200_now);
     }
// Bullish breakout
   if(lvlUp>0
      && prevC<=lvlUp && currC>lvlUp
      && currC>ema14_now && currC>ema200_now
      && ema14_now>ema200_now)
     {
      Print(">>> Bull breakout triggered");
      Signal(true, 1, lvlUp, ema14_now, ema200_now);
     }
  }

//+------------------------------------------------------------------+
//| Draw or update a dotted HLine                                    |
//+------------------------------------------------------------------+
void DrawHLine(string name, double price, color clr)
  {
   if(price<=0)
      return;
   if(ObjectFind(0,name)<0)
     {
      ObjectCreate(0,name,OBJ_HLINE,0,0,price);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
      ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
      ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
     }
   else
      ObjectSetDouble(0,name,OBJPROP_PRICE,price);
  }

//+------------------------------------------------------------------+
//| Plot arrow, label, log & alert                                   |
//+------------------------------------------------------------------+
void Signal(bool isBull, int shift, double level, double ema14, double ema200)
  {
   datetime t     = iTime(_Symbol,InpTimeframe,shift);
   double   price = level;
   string   side  = isBull ? "Bull" : "Bear";

// Arrow
   string arrowName = StringFormat("FB_%sArrow_%d", side, (int)t);
   ObjectCreate(0, arrowName, OBJ_ARROW, 0, t, price);
   ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE,    isBull?233:234);
   ObjectSetInteger(0, arrowName, OBJPROP_COLOR,        isBull?InpBullColor:InpBearColor);
   ObjectSetInteger(0, arrowName, OBJPROP_WIDTH,        2);
   ObjectSetInteger(0, arrowName, OBJPROP_FONTSIZE,     InpArrowFontSize);

// Label
   string lab = arrowName + "_L";
   double offset = (isBull ? InpArrowOffset : -InpArrowOffset) * _Point;
   ObjectCreate(0, lab, OBJ_TEXT, 0, t, price + offset);
   ObjectSetString(0, lab, OBJPROP_TEXT,    isBull?InpBullText:InpBearText);
   ObjectSetInteger(0, lab, OBJPROP_COLOR,  isBull?InpBullColor:InpBearColor);
   ObjectSetInteger(0, lab, OBJPROP_FONTSIZE, 11);

// Expert log
   string msg = StringFormat(
                   "%s breakout at %s | Level=%.5f | Close=%.5f | EMA14=%.5f | EMA200=%.5f",
                   side, TimeToString(t, TIME_DATE|TIME_SECONDS),
                   level, iClose(_Symbol,InpTimeframe,shift),
                   ema14, ema200
                );
   Print(msg);

// Pop-up alert + optional sound
   if(InpAlertsEnabled)
     {
      Alert(msg);
      if(StringLen(InpAlertSoundFile)>0)
         PlaySound(InpAlertSoundFile);
     }
  }
//+------------------------------------------------------------------+


成果

要测试EA,首先需使用MetaEditor编译代码。编译完成后,您既可以在MetaTrader 5中将EA应用于图表进行实时测试,也可以直接通过策略测试器进行历史回测。我已经在实盘市场环境和历史回测中对这款EA进行了测试。这一过程非常关键,只有这样才能把EA参数调顺,实现预期的交易行为,并优化其性能。

下图展示了该EA如何利用分形和EMA指标识别重大市场反转。具体而言,EA在关键支撑位检测到看跌分形突破,并在图表上进行可视化标记。随后的价格走势证实了反转,市场持续下行,与EMA趋势一致,其中14周期EMA下穿200周期EMA,表明市场转为看跌趋势。“看跌突破”标记等视觉提示有助于交易者做出严谨的入场和出场决策。这一结果展示了EA识别趋势反转早期迹象的能力,使交易者能够把握高概率的交易机会,同时避免虚假信号。

图例3. 阶梯指数上的看跌突破

以下GIF图展示了该EA实时检测并确认趋势反转的过程。其重点呈现了指标系统如何识别分形支撑位或阻力位,并发出潜在突破信号,促使交易者考虑建仓。随着市场按预测方向运行,通过EMA的排列来验证EA信号;14周期EMA下穿200周期EMA,确认了看跌趋势。箭头和警报等视觉提示,展示了EA如何助力交易者及时做出决策。这一实例凸显了早期检测与趋势确认相结合的重要性,增强了交易信心,并降低了误入场的风险。

图例4. V75(1s)回测


结论

该EA借助比尔·威廉姆斯的分形指标以及短期和长期EMA的力量,精准定位高概率入场点。通过结合这些工具,它能够捕捉早期反转信号,并根据当前市场趋势进行验证,从而提高交易执行的精准度和一致性。严格的回测和实盘测试表明,该EA在不同市场条件下均表现出良好的灵活性,并且经过微调后,可适配其参数以实现最优性能。图表上呈现直观的箭头和标签,再加上全自动下单,该EA能让您的交易流程既严谨又快速。本质上讲,它提供了一个系统化、基于规则的价格行为分析框架,特别适合那些既重视技术,又希望省力而采用自动化的交易者。

特色工具概述请参阅下表。





   
图表展示器
分析评论
分析大师
分析预测 
波动率导航仪
均值回归信号收割器
信号脉冲 
指标看板 
外部数据流
VWAP
Heikin Ashi   FibVWAP  
RSI背离
抛物线止损与反转指标 (PSAR) 
四分位绘制脚本
侵入探测器
TrendLoom工具  四分位看板 
ZigZag分析仪  相关性探索  市场结构反转检测工具
关联仪表盘  货币强度计 
PAQ分析工具 
针形柱、吞噬形态与相对强弱指数(RSI)背离
双EMA分形突破器        

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/18297

附加的文件 |
最近评论 | 前往讨论 (3)
Delly
Delly | 4 6月 2025 在 09:17
这篇文章很有意思,感谢您提供 EA源代码。我会试用并提供反馈。
Christian Benjamin
Christian Benjamin | 9 6月 2025 在 19:54
Delly Kabongo #:
这篇文章很有意思,感谢您提供 EA 源代码。我会试用并提供反馈。
感谢您的联系。非常感谢并随时欢迎您的反馈。
ABEL OLUFEMI FAMODU
ABEL OLUFEMI FAMODU | 11 6月 2025 在 20:29
非常有趣的文章。它对简化交易策略 大有裨益。
价格行为分析工具包开发(第二十六部分):针形线、吞没形态与RSI背离(多模式)工具 价格行为分析工具包开发(第二十六部分):针形线、吞没形态与RSI背离(多模式)工具
与我们开发实用型价格行为工具的初衷相一致,本文将探讨如何开发一款 EA。该 EA 能够识别 Pin Bar 和吞没形态,并利用 RSI 背离作为确认信号,仅在条件满足时生成交易提示。
风险管理(第四部分):完善关键类方法 风险管理(第四部分):完善关键类方法
这是我们关于 MQL5 风险管理系列文章的第四部分,我们将继续探索保护和优化交易策略的高级方法。在前几篇文章中奠定了重要的基础之后,我们现在将专注于完成第三部分中推迟的所有剩余方法,包括检查是否达到特定利润或亏损水平的函数。此外,我们将引入新的关键事件,以实现更准确、更灵活的风险管理。
交易中的神经网络:二维连接空间模型(终篇) 交易中的神经网络:二维连接空间模型(终篇)
我们继续探索创新的奇美拉(Chimera)框架 — 这款二维状态空间模型,利用神经网络技术多维度分析时间序列。该方法提供了高预测精度、及低计算成本。
精通日志记录(第七部分):如何在图表上显示日志 精通日志记录(第七部分):如何在图表上显示日志
了解如何在MetaTrader图表上以条理清晰的方式直接显示日志,包括边框、标题和自动 滚动功能。本文将演示如何用MQL5打造可视化日志系统,助您实时监控交易机器人的运行状态。