English Русский Deutsch 日本語
preview
MQL5 交易策略自动化(第二十七部分):创建带可视化反馈的价格行为螃蟹谐波形态

MQL5 交易策略自动化(第二十七部分):创建带可视化反馈的价格行为螃蟹谐波形态

MetaTrader 5交易系统 |
68 7
Allan Munene Mutiiria
Allan Munene Mutiiria

引言

在我们的上一篇文章(第 26 部分)中,我们基于MetaQuotes Language 5(MQL5)开发了一套针形 K 线(Pin Bar)加仓系统,该系统利用针形 K 线形态发起交易,并通过加仓策略管理多个持仓,同时配备动态仪表盘用于实时监控。在第 27 部分,我们搭建一套螃蟹形态系统,通过枢轴点和斐波那契比例识别看涨、看跌螃蟹谐波形态,以精准的入场、止损、止盈点位实现交易自动化,并借助三角形、趋势线等可视化图表对象清晰呈现形态结构。本文将包括几个方面:

  1. 理解螃蟹谐波形态框架
  2. 在MQL5中的实现
  3. 回测
  4. 结论

最后,你将得到一套成熟的 MQL5 谐波形态交易策略,可直接进行自定义修改 —— 让我们开始深入学习!


理解螃蟹谐波形态框架

螃蟹形态是一种谐波交易形态,由五个关键枢轴点 X、A、B、C、D 定义,分为看涨形态与看跌形态两种类型。看涨螃蟹形态的结构遵循低点‑高点‑低点‑高点‑低点的序列:X 点为波段低点,A 点为波段高点,B 点为波段低点(回撤 XA 段的 0.618),C 点为波段高点(BC 相对 AB 的幅度处于 0.382–0.886 区间),D 点为波段低点(XA 段的 1.618 倍延伸,位置低于 X 点)。与之相反,看跌螃蟹形态遵循高点‑低点‑高点‑低点‑高点序列:X 点为波段高点,A 点为波段低点,B 点为波段高点,C 点为波段低点,D 点为波段高点(XA 段的 1.618 倍延伸,位置高于 X 点)。下面是各类形态的可视化示意图。

看涨螃蟹谐波形态:

看涨螃蟹谐波形态

看跌螃蟹谐波形态:

看跌螃蟹谐波形态

为了识别这些形态,我们采用以下结构化方法:

  • 定义 XA 波段:从 X 点到 A 点的初始驱动行情构成形态的基础,决定整体方向(看涨为向下,看跌为向上),同时作为斐波那契计算的基准。
  • 确立 AB 波段:B 点需要回撤 XA 波段约 0.618,代表行情进入回调,但不会对初始趋势形成过强反转。
  • 分析 BC 波段:该波段需要达到 AB 波段 0.382~0.886 的延伸幅度,形成一轮明显反向波动,为最终的延伸段做好铺垫。
  • 确定 CD 波段:最后这一波段需要达到 XA 波段 1.618 倍的延伸,D 点即为潜在反转区域,形态在此完成并生成交易信号。

通过应用这套几何与斐波那契判定标准 ,我们的交易系统将在价格数据中系统性识别有效的螃蟹形态。识别完成后,系统会在图表上渲染该形态:使用三角形、趋势线、X/A/B/C/D 点位标签,并使用虚线标记入场位与止盈位。该机制可以在 D 点自动执行交易,使用计算得出的止损与多级止盈,利用该形态的高概率反转特性实现高效入场。接下来我们进入代码实现环节!


在MQL5中的实现

要在 MQL5 中创建程序,请打开MetaEditor,转到“导航器”窗口,找到“指标”文件夹,点击“新建”选项卡,然后按照提示创建文件。文件创建完成后,在代码编辑环境中,我们需要声明一些将在整个程序中使用的全局变量

//+------------------------------------------------------------------+
//|                                             Crab Pattern EA.mq5. |
//|                        Copyright 2025, Forex Algo-Trader, Allan. |
//|                                 "https://t.me/Forex_Algo_Trader" |
//+------------------------------------------------------------------+
#property copyright   "Forex Algo-Trader, Allan"
#property link        "https://t.me/Forex_Algo_Trader"
#property version     "1.00"
#property description "This EA trades based on Crab Strategy"
#property strict

#include <Trade\Trade.mqh>                         //--- Include Trade library for order management
CTrade obj_Trade;                                  //--- Instantiate trade object for executing orders

//--- Input parameters for user configuration
input int    PivotLeft = 5;                        // Number of bars to the left for pivot identification
input int    PivotRight = 5;                       // Number of bars to the right for pivot identification
input double Tolerance = 0.10;                     // Allowed deviation for Fibonacci levels (10% of XA move)
input double LotSize = 0.01;                       // Lot size for opening new trade positions
input bool   AllowTrading = true;                  // Enable or disable automated trading functionality

//---------------------------------------------------------------------------
//--- Crab pattern definition:
//--- Bullish Crab:
//--- Pivots (X-A-B-C-D): X swing low, A swing high, B swing low, C swing high, D swing low.
//--- Normally XA > 0; Ideal B = A - 0.5*(A-X); Legs within specified ranges.
//--- Bearish Crab:
//--- Pivots (X-A-B-C-D): X swing high, A swing low, B swing high, C swing low, D swing high.
//--- Normally XA > 0; Ideal B = A + 0.5*(X-A); Legs within specified ranges.
//---------------------------------------------------------------------------

struct Pivot {                                     //--- Define structure for pivot points
   datetime time;                                  //--- Store time of pivot bar
   double   price;                                 //--- Store price (high for swing high, low for swing low)
   bool     isHigh;                                //--- Indicate true for swing high, false for swing low
};

Pivot pivots[];                                    //--- Declare array to store pivot points
int      g_patternFormationBar = -1;               //--- Store bar index of pattern formation (-1 if none)
datetime g_lockedPatternX = 0;                     //--- Store X pivot time for locked pattern

我们开始实现螃蟹形态,首先引入 "<Trade\Trade.mqh>" 库,并实例化 "obj_Trade" 为CTrade对象,用于处理订单管理,例如发送买入、卖出请求。接下来我们定义可供用户自定义的输入参数:"PivotLeft" 与 "PivotRight" 均设为 5 根 K 线,用于指定识别波段枢轴点的回溯周期;"Tolerance" 设为 0.10,代表斐波那契比例允许的偏差;"LotSize" 设为 0.01 作为交易手数;"AllowTrading" 设置为 true 以开启自动交易。

接下来我们定义 "Pivot"结构体,包含 "time"(datetime 时间类型)、"price"(double浮点类型)以及 "isHigh"(布尔类型),用来存储枢轴点;声明 "pivots" 为 Pivot 类型数组;同时初始化全局变量:"g_patternFormationBar" 赋值‑1,用于记录形态形成所在 K 线;"g_lockedPatternX" 赋值 0,用于锁定 X 枢轴点的时间以供校验,至此完成形态识别的基础环境搭建。可视化方面,我们将编写函数用来绘制线条、文本标签以及三角形图形。

//+------------------------------------------------------------------+
//| Draw filled triangle on chart                                    |
//+------------------------------------------------------------------+
void DrawTriangle(string name, datetime t1, double p1, datetime t2, double p2, datetime t3, double p3, color cl, int width, bool fill, bool back) {
   if (ObjectCreate(0, name, OBJ_TRIANGLE, 0, t1, p1, t2, p2, t3, p3)) { //--- Create triangle with three points
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set triangle color
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); //--- Set solid line style
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width); //--- Set line width
      ObjectSetInteger(0, name, OBJPROP_FILL, fill); //--- Enable or disable fill
      ObjectSetInteger(0, name, OBJPROP_BACK, back); //--- Set background or foreground
   }
}

//+------------------------------------------------------------------+
//| Draw trend line on chart                                         |
//+------------------------------------------------------------------+
void DrawTrendLine(string name, datetime t1, double p1, datetime t2, double p2, color cl, int width, int style) {
   if (ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) { //--- Create trend line between two points
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set line color
      ObjectSetInteger(0, name, OBJPROP_STYLE, style); //--- Set line style (solid, dotted, etc.)
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width); //--- Set line width
   }
}

//+------------------------------------------------------------------+
//| Draw dotted horizontal line on chart                             |
//+------------------------------------------------------------------+
void DrawDottedLine(string name, datetime t1, double p, datetime t2, color lineColor) {
   if (ObjectCreate(0, name, OBJ_TREND, 0, t1, p, t2, p)) { //--- Create horizontal dotted line
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); //--- Set line color
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT); //--- Set dotted style
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); //--- Set line width to 1
   }
}

//+------------------------------------------------------------------+
//| Draw anchored text label for pivots                              |
//+------------------------------------------------------------------+
void DrawTextEx(string name, string text, datetime t, double p, color cl, int fontsize, bool isHigh) {
   if (ObjectCreate(0, name, OBJ_TEXT, 0, t, p)) { //--- Create text label at specified coordinates
      ObjectSetString(0, name, OBJPROP_TEXT, text); //--- Set label text content
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set text color
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontsize); //--- Set font size
      ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold"); //--- Set font to Arial Bold
      if (isHigh) {                                //--- Check if pivot is swing high
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_BOTTOM); //--- Anchor label above pivot
      } else {                                     //--- Handle swing low
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_TOP); //--- Anchor label below pivot
      }
      ObjectSetInteger(0, name, OBJPROP_ALIGN, ALIGN_CENTER); //--- Center-align text
   }
}

此处我们为程序实现可视化函数,用于绘制代表螃蟹谐波形态及其交易价位的图表对象。首先创建 "DrawTriangle" 函数,该函数调用ObjectCreate绘制填充三角形OBJ_TRIANGLE,三角形三个顶点由时间 ("t1"、"t2"、"t3") 与价格 ("p1"、"p2"、"p3") 定义;通过ObjectSetInteger函数设置OBJPROP_COLOR为指定颜色、OBJPROP_STYLE 为 STYLE_SOLID 实线样式、OBJPROP_WIDTH 为传入线宽、OBJPROP_FILL 控制是否填充、OBJPROP_BACK 控制对象置于前景还是背景。

接下来实现 "DrawTrendLine" 函数,使用 ObjectCreate 在两点之间创建趋势线对象 OBJ_TREND,通过 ObjectSetInteger 配置 OBJPROP_COLOR、OBJPROP_STYLE(实线、虚线等)以及OBJPROP_WIDTH,实现可自定义的线条样式。然后编写 "DrawDottedLine" 函数,在指定价格位置,从时间 t1 到 t2 绘制水平虚线OBJ_TREND,实现逻辑与上面保持一致。最后实现 "DrawTextEx" 函数,调用对象创建函数在坐标 ("t", "p") 处生成文本标签OBJ_TEXT,格式与前述函数保持统一,确保螃蟹形态与交易价位可以在图表上清晰展示。现在我们进入OnTick事件处理函数,开始寻找波段枢轴点,后续将利用这些点位做形态识别。以下是实现该功能的代码。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   static datetime lastBarTime = 0;               //--- Store time of last processed bar
   datetime currentBarTime = iTime(_Symbol, _Period, 1); //--- Get time of current confirmed bar
   if (currentBarTime == lastBarTime) return;     //--- Exit if no new bar
   lastBarTime = currentBarTime;                  //--- Update last processed bar time
   ArrayResize(pivots, 0);                        //--- Clear pivot array for fresh analysis
   int barsCount = Bars(_Symbol, _Period);        //--- Retrieve total number of bars
   int start = PivotLeft;                         //--- Set starting index for pivot detection
   int end = barsCount - PivotRight;              //--- Set ending index for pivot detection
   for (int i = end - 1; i >= start; i--) {       //--- Iterate through bars to identify pivots
      bool isPivotHigh = true;                    //--- Assume bar is a swing high
      bool isPivotLow = true;                     //--- Assume bar is a swing low
      double currentHigh = iHigh(_Symbol, _Period, i); //--- Get current bar high price
      double currentLow = iLow(_Symbol, _Period, i); //--- Get current bar low price
      for (int j = i - PivotLeft; j <= i + PivotRight; j++) { //--- Check surrounding bars
         if (j < 0 || j >= barsCount) continue;   //--- Skip out-of-bounds indices
         if (j == i) continue;                    //--- Skip current bar
         if (iHigh(_Symbol, _Period, j) > currentHigh) isPivotHigh = false; //--- Invalidate swing high
         if (iLow(_Symbol, _Period, j) < currentLow) isPivotLow = false; //--- Invalidate swing low
      }
      if (isPivotHigh || isPivotLow) {            //--- Check if bar is a pivot
         Pivot p;                                 //--- Create new pivot structure
         p.time = iTime(_Symbol, _Period, i);     //--- Set pivot bar time
         p.price = isPivotHigh ? currentHigh : currentLow; //--- Set pivot price
         p.isHigh = isPivotHigh;                  //--- Set pivot type
         int size = ArraySize(pivots);            //--- Get current pivot array size
         ArrayResize(pivots, size + 1);           //--- Resize pivot array
         pivots[size] = p;                        //--- Add pivot to array
      }
   }
}

接下来我们实现OnTick事件处理函数的初始逻辑,用来检测波段枢轴点,以此作为识别螃蟹谐波形态的基础。首先检测是否产生新 K 线:将静态变量 lastBarTime(初始值为 0)与偏移为 1 的iTime获取的 currentBarTime 做对比,避免使用尚未闭合的当前 K 线;时间无变化则直接退出,检测到新 K 线就更新 lastBarTime。然后调用ArrayResize清空 pivots 数组,保证每一轮都是全新分析。接着通过Bars获取总 K 线数量,设定枢轴点检测区间:start 等于 PivotLeft,end 等于总 K 线数减去 PivotRight,再从 end‑1 向 start 遍历每一根 K 线。

对于每一根 K 线,先假定它是波段高点(isPivotHigh 置 true)同时也是波段低点(isPivotLow 置 true);通过iHighiLow读取该 K 线的最高、最低价;再遍历 PivotLeft 与 PivotRight 范围内的周边 K 线,如果相邻 K 线出现更高的高点或者更低的低点,则取消该 K 线的枢轴点判定。最后,如果该 K 线依旧是有效枢轴点(高点或低点),就创建Pivot 结构体 ,用 iTime 赋值 time,依据 isPivotHigh 把 price 赋值为高点或低点,并设置 isHigh 标记;调用 ArrayResize 扩容数组,将该点位存入 pivots 数组。打印该数组之后,可以得到如下输出结果。

PIVOTS DATA

有了这些数据,我们可以提取枢轴点;如果我们有足够的枢轴点,就可以分析并检测形态。以下是实现这一功能的代码。

int pivotCount = ArraySize(pivots);            //--- Get total number of pivots
if (pivotCount < 5) {                          //--- Check if insufficient pivots
   g_patternFormationBar = -1;                 //--- Reset pattern formation bar
   g_lockedPatternX = 0;                       //--- Reset locked X pivot
   return;                                     //--- Exit function
}
Pivot X = pivots[pivotCount - 5];              //--- Extract X pivot (earliest)
Pivot A = pivots[pivotCount - 4];              //--- Extract A pivot
Pivot B = pivots[pivotCount - 3];              //--- Extract B pivot
Pivot C = pivots[pivotCount - 2];              //--- Extract C pivot
Pivot D = pivots[pivotCount - 1];              //--- Extract D pivot (latest)
bool patternFound = false;                     //--- Initialize pattern detection flag
if (X.isHigh && !A.isHigh && B.isHigh && !C.isHigh && D.isHigh) { //--- Check bearish Crab pattern
   double diff = X.price - A.price;            //--- Calculate XA leg difference
   if (diff > 0) {                             //--- Ensure positive XA move
      double idealB = A.price + 0.618 * diff;  //--- Compute ideal B (0.618 retracement)
      if (MathAbs(B.price - idealB) <= Tolerance * diff) { //--- Verify B within tolerance
         double AB = B.price - A.price;        //--- Calculate AB leg length
         double BC = B.price - C.price;        //--- Calculate BC leg length
         if (BC >= 0.382 * AB && BC <= 0.886 * AB) { //--- Check BC within Fibonacci range
            double extension = D.price - A.price; //--- Calculate AD extension
            if (MathAbs(extension - 1.618 * diff) <= Tolerance * diff && D.price > X.price) { //--- Verify 1.618 extension and D > X
               patternFound = true;            //--- Confirm bearish pattern
            }
         }
      }
   }
}
if (!X.isHigh && A.isHigh && !B.isHigh && C.isHigh && !D.isHigh) { //--- Check bullish Crab pattern
   double diff = A.price - X.price;            //--- Calculate XA leg difference
   if (diff > 0) {                             //--- Ensure positive XA move
      double idealB = A.price - 0.618 * diff;  //--- Compute ideal B (0.618 retracement)
      if (MathAbs(B.price - idealB) <= Tolerance * diff) { //--- Verify B within tolerance
         double AB = A.price - B.price;        //--- Calculate AB leg length
         double BC = C.price - B.price;        //--- Calculate BC leg length
         if (BC >= 0.382 * AB && BC <= 0.886 * AB) { //--- Check BC within Fibonacci range
            double extension = A.price - D.price; //--- Calculate AD extension
            if (MathAbs(extension - 1.618 * diff) <= Tolerance * diff && D.price < X.price) { //--- Verify 1.618 extension and D < X
               patternFound = true;            //--- Confirm bullish pattern
            }
         }
      }
   }
}

为识别各类形态,我们采用基于斐波那契的判定标准。首先通过 "ArraySize(pivots)"获取枢轴点总数量并存入"pivotCount";螃蟹形态需要 X、A、B、C、D 五个点位,如果检测到的枢轴点少于 5 个,则重置"g_patternFormationBar"为‑1、"g_lockedPatternX" 为 0 并直接退出。接下来从 "pivots" 数组中提取最新的五个枢轴点,分别赋值给 "X"(最早点位)、"A"、"B"、"C"、"D"(最新点位),用来代表形态结构。

接下来校验看跌螃蟹形态:校验点位序列(X 为高点、A 为低点、B 为高点、C 为低点、D 为高点);计算 XA 波段价差(X.price‑A.price)并确保结果为正数;计算理论 B 点价格为 "A.price + 0.618 * diff",并通过MathAbs函数确认实际 B 点偏差不超过 "Tolerance * diff";随后校验 BC 波段(为 AB 波段的 0.382‑0.886 倍)以及 AD 延伸段(XA 的 1.618 倍,且 D 点位于 X 点上方);全部条件满足则将 "patternFound" 置为 true。最后校验看涨螃蟹形态(X 低点、A 高点、B 低点、C 高点、D 低点);XA 价差计算为 "A.price‑X.price" 并保证为正数;校验 B 点处于 0.618 回撤位,BC 波段处于 AB 的 0.382‑0.886 区间,AD 为 XA 的 1.618 倍延伸且 D 点低于 X 点;校验通过则将 "patternFound" 置为 true。如果识别到形态,就可以在图表上对其进行可视化绘制。

string patternType = "";                                                //--- Initialize pattern type
if (patternFound) {                                                     //--- Check if pattern detected
   if (D.price > X.price) patternType = "Bearish";                      //--- Set bearish pattern (sell signal)
   else if (D.price < X.price) patternType = "Bullish";                 //--- Set bullish pattern (buy signal)
}
if (patternFound) {                                                     //--- Process valid Crab pattern
   Print(patternType, " Crab pattern detected at ", TimeToString(D.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS)); //--- Log pattern detection
   string signalPrefix = "CR_" + IntegerToString(X.time);               //--- Generate unique prefix for objects
   color triangleColor = (patternType == "Bullish") ? clrBlue : clrRed; //--- Set triangle color based on pattern
   DrawTriangle(signalPrefix + "_Triangle1", X.time, X.price, A.time, A.price, B.time, B.price, triangleColor, 2, true, true); //--- Draw XAB triangle
   DrawTriangle(signalPrefix + "_Triangle2", B.time, B.price, C.time, C.price, D.time, D.price, triangleColor, 2, true, true); //--- Draw BCD triangle
}

为了在图表上对识别出的形态做分类与可视化,我们初始化空字符串 "patternType",用来存储形态是看涨还是看跌。当 "patternFound" 等于 true 时,对比 D.price 与 X.price 判断形态类型:D 点高于 X 点,"patternType" 赋值为 "Bearish"(代表卖出信号);D 点低于 X 点则赋值为 "Bullish"(代表买入信号)。接下来,确认存在有效螃蟹形态后,调用Print输出日志,打印形态类型以及 D 枢轴点时间,使用 TimeToString 格式化输出日期、分钟、秒。

最后生成唯一标识 "signalPrefix",由字符串 "CR"拼接转为字符串格式的 X.time 得到;看涨形态三角形颜色设为蓝色,看跌形态设为红色;两次调用 DrawTriangle 函数渲染形态:第一次绘制 XAB 三角形(连接 X、A、B),第二次绘制 BCD 三角形(连接 B、C、D);对象名称使用 signalPrefix 分别拼接后缀"_Triangle1"与"_Triangle2",传入对应枢轴点时间、价格、三角形颜色、线宽 2,开启填充与背景绘制;确保识别到的螃蟹形态清晰可见,辅助交易决策。这是我们当前完成的阶段性效果。

带三角形渲染的形态

从图像中可以看出,我们能够正确地映射和可视化检测到的形态。接下来我们需要继续补充趋势线,使整个形态轮廓更加完整清晰,同时增加文本标签,方便识别各个价位。

DrawTrendLine(signalPrefix + "_TL_XA", X.time, X.price, A.time, A.price, clrBlack, 2, STYLE_SOLID); //--- Draw XA trend line
DrawTrendLine(signalPrefix + "_TL_AB", A.time, A.price, B.time, B.price, clrBlack, 2, STYLE_SOLID); //--- Draw AB trend line
DrawTrendLine(signalPrefix + "_TL_BC", B.time, B.price, C.time, C.price, clrBlack, 2, STYLE_SOLID); //--- Draw BC trend line
DrawTrendLine(signalPrefix + "_TL_CD", C.time, C.price, D.time, D.price, clrBlack, 2, STYLE_SOLID); //--- Draw CD trend line
DrawTrendLine(signalPrefix + "_TL_XB", X.time, X.price, B.time, B.price, clrBlack, 2, STYLE_SOLID); //--- Draw XB trend line
DrawTrendLine(signalPrefix + "_TL_BD", B.time, B.price, D.time, D.price, clrBlack, 2, STYLE_SOLID); //--- Draw BD trend line
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Retrieve symbol point size
double offset = 15 * point;                    //--- Calculate label offset (15 points)
double textY_X = X.isHigh ? X.price + offset : X.price - offset; //--- Set X label Y coordinate
double textY_A = A.isHigh ? A.price + offset : A.price - offset; //--- Set A label Y coordinate
double textY_B = B.isHigh ? B.price + offset : B.price - offset; //--- Set B label Y coordinate
double textY_C = C.isHigh ? C.price + offset : C.price - offset; //--- Set C label Y coordinate
double textY_D = D.isHigh ? D.price + offset : D.price - offset; //--- Set D label Y coordinate
DrawTextEx(signalPrefix + "_Text_X", "X", X.time, textY_X, clrBlack, 11, X.isHigh); //--- Draw X pivot label
DrawTextEx(signalPrefix + "_Text_A", "A", A.time, textY_A, clrBlack, 11, A.isHigh); //--- Draw A pivot label
DrawTextEx(signalPrefix + "_Text_B", "B", B.time, textY_B, clrBlack, 11, B.isHigh); //--- Draw B pivot label
DrawTextEx(signalPrefix + "_Text_C", "C", C.time, textY_C, clrBlack, 11, C.isHigh); //--- Draw C pivot label
DrawTextEx(signalPrefix + "_Text_D", "D", D.time, textY_D, clrBlack, 11, D.isHigh); //--- Draw D pivot label
datetime centralTime = (X.time + B.time) / 2;                                       //--- Calculate central label time
double centralPrice = D.price;                                                      //--- Set central label price
if (ObjectCreate(0, signalPrefix + "_Text_Center", OBJ_TEXT, 0, centralTime, centralPrice)) { //--- Create central pattern label
   ObjectSetString(0, signalPrefix + "_Text_Center", OBJPROP_TEXT, patternType == "Bullish" ? "Bullish Crab" : "Bearish Crab"); //--- Set pattern name
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_COLOR, clrBlack);     //--- Set text color
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_FONTSIZE, 11);        //--- Set font size
   ObjectSetString(0, signalPrefix + "_Text_Center", OBJPROP_FONT, "Arial Bold");   //--- Set font type
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_ALIGN, ALIGN_CENTER); //--- Center-align text
}

为了描绘形态结构,我们继续添加线段和标签。首先调用 "DrawTrendLine" 函数,传入唯一的 "signalPrefix" 标识,绘制六条趋势线连接关键枢轴点:XA、AB、BC、CD、XB 以及 BD;每条线条的端点由对应枢轴点的时间与价格定义(例如 "X.time"、"X.price"),线条颜色为 "clrBlack" 黑色,线宽 2,STYLE_SOLID代表实线样式,勾勒出 XABCD 整体结构与辅助波段。接下来计算标签偏移量:通过 "SymbolInfoDouble(_Symbol, SYMBOL_POINT)"获取品种的 point 最小报价单位并乘以 15;依据每个枢轴点是波段高点("isHigh"为 true)还是波段低点,对偏移量做加减运算,得到各个点位标签的 Y 轴坐标("textY_X"、"textY_A"、"textY_B"、"textY_C"、"textY_D"),保证高点的标签显示在价格上方,低点标签显示在价格下方。

然后调用 "DrawTextEx" 为 X、A、B、C、D 枢轴点生成文本标签;每个标签都使用 "signalPrefix" 搭配 "_Text_X" 这类后缀作为对象名,显示对应点位字母;定位在枢轴点时间与经过偏移调整后的 Y 坐标,颜色 "clrBlack",字号 11,并依据枢轴点的 "isHigh" 状态设置锚点。最后计算中心标签位置:"centralTime" 取 "X.time" 与 "B.time" 的中间时刻,"centralPrice" 使用 "D.price";调用ObjectCreate创建文本对象,对象名称为 "signalPrefix + '_Text_Center'";根据 "patternType" 把OBJPROP_TEXT文本内容设置为 "Bullish Crab" 或者 "Bearish Crab";再通过ObjectSetString与 "ObjectSetInteger" 配置属性:"OBJPROP_COLOR" 设为 "clrBlack","OBJPROP_FONTSIZE" 字号 11,"OBJPROP_FONT" 字体为 "Arial Bold","OBJPROP_ALIGN" 对齐方式为 "ALIGN_CENTER" 居中。以上操作可以在图表上完整展示螃蟹形态的结构与形态类型。运行程序后,我们可以得到如下可视化效果。

带边线与点位标签的螃蟹形态

从图像中可以看出,我们已经为形态添加了边框和标签,使其展示效果更完整,也更具说明性。接下来我们需要做的是确定该形态的交易价位。

datetime lineStart = D.time;                                     //--- Set start time for trade level lines
datetime lineEnd = D.time + PeriodSeconds(_Period) * 2;          //--- Set end time for trade level lines
double entryPriceLevel, TP1Level, TP2Level, TP3Level, tradeDiff; //--- Declare trade level variables
if (patternType == "Bullish") {                                  //--- Handle bullish trade levels
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_ASK);      //--- Set entry at ask price
   TP3Level = C.price;                                           //--- Set TP3 at C pivot price
   tradeDiff = TP3Level - entryPriceLevel;                       //--- Calculate total trade distance
   TP1Level = entryPriceLevel + tradeDiff / 3;                   //--- Set TP1 at 1/3 of distance
   TP2Level = entryPriceLevel + 2 * tradeDiff / 3;               //--- Set TP2 at 2/3 of distance
} else {                                                         //--- Handle bearish trade levels
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_BID);      //--- Set entry at bid price
   TP3Level = C.price;                                           //--- Set TP3 at C pivot price
   tradeDiff = entryPriceLevel - TP3Level;                       //--- Calculate total trade distance
   TP1Level = entryPriceLevel - tradeDiff / 3;                   //--- Set TP1 at 1/3 of distance
   TP2Level = entryPriceLevel - 2 * tradeDiff / 3;               //--- Set TP2 at 2/3 of distance
}
DrawDottedLine(signalPrefix + "_EntryLine", lineStart, entryPriceLevel, lineEnd, clrMagenta);  //--- Draw entry level line
DrawDottedLine(signalPrefix + "_TP1Line", lineStart, TP1Level, lineEnd, clrForestGreen);       //--- Draw TP1 level line
DrawDottedLine(signalPrefix + "_TP2Line", lineStart, TP2Level, lineEnd, clrGreen);             //--- Draw TP2 level line
DrawDottedLine(signalPrefix + "_TP3Line", lineStart, TP3Level, lineEnd, clrDarkGreen);         //--- Draw TP3 level line
datetime labelTime = lineEnd + PeriodSeconds(_Period) / 2;                                     //--- Set time for trade level labels
string entryLabel = patternType == "Bullish" ? "BUY (" : "SELL (";                             //--- Start entry label text
entryLabel += DoubleToString(entryPriceLevel, _Digits) + ")";                                  //--- Append entry price
DrawTextEx(signalPrefix + "_EntryLabel", entryLabel, labelTime, entryPriceLevel, clrMagenta, 11, true); //--- Draw entry label
string tp1Label = "TP1 (" + DoubleToString(TP1Level, _Digits) + ")";                           //--- Create TP1 label text
DrawTextEx(signalPrefix + "_TP1Label", tp1Label, labelTime, TP1Level, clrForestGreen, 11, true); //--- Draw TP1 label
string tp2Label = "TP2 (" + DoubleToString(TP2Level, _Digits) + ")";                           //--- Create TP2 label text
DrawTextEx(signalPrefix + "_TP2Label", tp2Label, labelTime, TP2Level, clrGreen, 11, true);     //--- Draw TP2 label
string tp3Label = "TP3 (" + DoubleToString(TP3Level, _Digits) + ")";                           //--- Create TP3 label text
DrawTextEx(signalPrefix + "_TP3Label", tp3Label, labelTime, TP3Level, clrDarkGreen, 11, true); //--- Draw TP3 label

此处我们继续为已识别出的形态定义并可视化交易价位。首先将 "lineStart" 赋值为 D 枢轴点的时间 "D.time",借助 "PeriodSeconds(_Period) * 2"把"lineEnd"设置为往后两个周期的时刻;同时声明交易计算所需变量:"entryPriceLevel"、"TP1Level"、"TP2Level"、"TP3Level"以及"tradeDiff"。对于看涨形态("patternType == 'Bullish'"),通过SymbolInfoDouble将 "entryPriceLevel" 设置为当前 Ask 价,"TP3Level" 赋值为 C 枢轴点价格;"tradeDiff" 计算为 "TP3Level - entryPriceLevel";"TP1Level" 与 "TP2Level" 分别为入场价加上 "tradeDiff" 的 1/3、2/3。若是看跌形态,则取用当前 Bid 价,"TP3Level" 同样设为 C 点价格,"tradeDiff" 等于 "entryPriceLevel - TP3Level";"TP1Level"、"TP2Level" 由入场价分别减去价差的 1/3、2/3 得到。

接下来调用 "DrawDottedLine" 绘制四条水平虚线:品红色的入场价位线位于 "entryPriceLevel";止盈线分别在 "TP1Level"(森林绿)、"TP2Level"(绿色)、"TP3Level"(深绿色);所有线条时间范围从 "lineStart" 延伸至 "lineEnd"。最后将 "labelTime" 设置为 "lineEnd" 再加上半个周期;通过DoubleToString格式化价格生成标签文本,例如入场位显示 “BUY (价格)” 或 “SELL (价格)”、止盈位显示 “TP1 (价格)” 等;调用 "DrawTextEx" 在 "labelTime" 位置绘制标签,使用对应颜色、字号 11,标签锚定在各价位线上方。编译完成后,我们得到了以下结果。

看跌形态:

看跌

看涨形态:

看涨

从图像中可以看出,我们已经正确地绘制了交易价位。现在我们需要做的就是启动实际交易仓位,仅此而已。

int currentBarIndex = Bars(_Symbol, _Period) - 1;       //--- Retrieve current bar index
if (g_patternFormationBar == -1) {                      //--- Check if no pattern is locked
   g_patternFormationBar = currentBarIndex;             //--- Lock current bar as formation bar
   g_lockedPatternX = X.time;                           //--- Lock X pivot time
   Print("Pattern detected on bar ", currentBarIndex, ". Waiting for confirmation on next bar."); //--- Log detection
   return;                                              //--- Exit function
}
if (currentBarIndex == g_patternFormationBar) {         //--- Check if still on formation bar
   Print("Pattern is repainting; still on locked formation bar ", currentBarIndex, ". No trade yet."); //--- Log repainting
   return;                                              //--- Exit function
}
if (currentBarIndex > g_patternFormationBar) {          //--- Check if new bar after formation
   if (g_lockedPatternX == X.time) {                    //--- Verify same X pivot for confirmation
      Print("Confirmed pattern (locked on bar ", g_patternFormationBar, "). Opening trade on bar ", currentBarIndex, "."); //--- Log confirmed pattern
      g_patternFormationBar = currentBarIndex;          //--- Update formation bar to current
      if (AllowTrading && !PositionSelect(_Symbol)) {   //--- Check trading allowed and no open position
         double entryPriceTrade = 0, stopLoss = 0, takeProfit = 0; //--- Declare trade parameters
         point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Update point value
         bool tradeResult = false;                      //--- Initialize trade result flag
         if (patternType == "Bullish") {                //--- Process bullish trade
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Set entry at ask price
            double diffTrade = TP2Level - entryPriceTrade; //--- Calculate trade distance
            stopLoss = entryPriceTrade - diffTrade * 3; //--- Set stop loss (3x distance)
            takeProfit = TP2Level;                      //--- Set take profit at TP2
            tradeResult = obj_Trade.Buy(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Crab Signal"); //--- Execute buy trade
            if (tradeResult) {                          //--- Check trade success
               Print("Buy order opened successfully."); //--- Log successful buy
            } else {                                    //--- Handle trade failure
               Print("Buy order failed: ", obj_Trade.ResultRetcodeDescription()); //--- Log failure reason
            }
         } else if (patternType == "Bearish") {         //--- Process bearish trade
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Set entry at bid price
            double diffTrade = entryPriceTrade - TP2Level; //--- Calculate trade distance
            stopLoss = entryPriceTrade + diffTrade * 3; //--- Set stop loss (3x distance)
            takeProfit = TP2Level;                      //--- Set take profit at TP2
            tradeResult = obj_Trade.Sell(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Crab Signal"); //--- Execute sell trade
            if (tradeResult) {                          //--- Check trade success
               Print("Sell order opened successfully."); //--- Log successful sell
            } else {                                    //--- Handle trade failure
               Print("Sell order failed: ", obj_Trade.ResultRetcodeDescription()); //--- Log failure reason
            }
         }
      } else {                                          //--- Trading not allowed or position exists
         Print("A position is already open for ", _Symbol, ". No new trade executed."); //--- Log no trade
      }
   } else {                                            //--- Pattern has changed
      g_patternFormationBar = currentBarIndex;         //--- Update formation bar
      g_lockedPatternX = X.time;                       //--- Update locked X pivot
      Print("Pattern has changed; updating lock on bar ", currentBarIndex, ". Waiting for confirmation."); //--- Log pattern change
      return;                                          //--- Exit function
   }
}
} else {                                               //--- No valid pattern detected
   g_patternFormationBar = -1;                         //--- Reset formation bar
   g_lockedPatternX = 0;                               //--- Reset locked X pivot
}

首先通过 "Bars(_Symbol, _Period) - 1"获取当前 K 线索引,并存入"currentBarIndex"。如果尚未锁定任何形态("g_patternFormationBar == -1"),就将 "g_patternFormationBar" 赋值为 "currentBarIndex",把 X 枢轴点时间 "X.time" 锁定保存到 "g_lockedPatternX",打印日志提示等待形态确认,随后退出。如果仍然处在形态形成的同一根 K 线("currentBarIndex == g_patternFormationBar"),打印日志说明形态存在重绘,直接退出,避免过早触发交易。

当已经生成新 K 线("currentBarIndex > g_patternFormationBar"),并且 X 枢轴点与 "g_lockedPatternX" 匹配时,确认该形态并输出日志,更新 "g_patternFormationBar";再通过 "AllowTrading" 检查是否允许交易,并借助PositionSelect确认当前没有持仓。对于看涨形态,将 "entryPriceTrade" 设置为当前 Ask 价,计算 "diffTrade" 等于 "TP2Level - entryPriceTrade",把止损位设置为该价差的三倍距离下方,止盈设置为 "TP2Level";调用 "obj_Trade.Buy" 执行买入,传入手数 "LotSize" 与注释 "Crab Signal",打印交易成功或失败日志。对于看跌形态,则取用当前 Bid 价,止损设置为价差三倍距离的上方,调用 "obj_Trade.Sell" 执行卖出。如果不允许交易或者已有持仓,则输出日志说明不执行交易。如果形态发生变化,则更新锁定信息继续等待。如果没有识别到形态,则重置 "g_patternFormationBar" 与 "g_lockedPatternX"。以此保证只有经过确认的螃蟹形态才会触发交易,并配套严谨的风险管控。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   ObjectsDeleteAll(0, "CR_");                    //--- Remove all chart objects with "CR_" prefix
   ArrayResize(pivots, 0);                        //--- Clear pivots array
   g_patternFormationBar = -1;                    //--- Reset pattern formation bar index
   g_lockedPatternX = 0;                          //--- Reset locked pattern X pivot time
   ChartRedraw(0);                                //--- Redraw chart to reflect changes
}

此处我们实现OnDeinit事件处理函数,保证 EA 从图表移除时可以完成正确的资源清理工作。首先使用ObjectsDeleteAll删除所有以 "CR_" 为前缀的图表对象,清除螃蟹形态相关的三角形、趋势线、标签等可视化元素。然后通过ArrayResize将 "pivots" 数组大小置为 0,清空已存储的拐点数据。接下来把 "g_patternFormationBar" 重置为‑1、"g_lockedPatternX" 重置为 0,清空形态跟踪变量。最后调用ChartRedraw刷新图表,确保图表同步体现所有对象与数据已被清除。以此实现干净的退出逻辑,释放资源,避免产生残留图表元素。编译完成后,我们得到了以下结果。

看跌信号:

看跌信号

看涨信号:

看涨信号

从截图可以看到,我们能够绘制出谐波形态,并且在形态确认后执行对应的交易,由此完成了对该形态识别、绘图以及自动化交易的设计目标。剩下的事情就是对该程序进行回测,这将在下一节中处理。


回测

经过彻底的回测后,我们得到以下结果。

回测结果图形:

图形

回测报告:

报告


结论

总而言之,我们在 MQL5 中开发完成了一套螃蟹形态交易系统,依托价格行为,结合精确的斐波那契比率识别看涨与看跌螃蟹谐波形态;系统自动执行交易,计算入场位、止损位与多级止盈位,并通过三角形、趋势线这类动态图表对象完成形态可视化展示。

免责声明:本文仅用于教学目的。交易存在重大财务风险,市场波动可能导致亏损。在将本程序应用于实盘交易前,充分的回测与严谨的风险管理至关重要。

借助本文介绍的思路与代码实现,你可以将这套螃蟹形态交易系统改造适配为符合自身交易风格的版本,以此完善你的算法交易策略。祝您交易愉快! 

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

附加的文件 |
Crab_Pattern_EA.mq5 (47.93 KB)
最近评论 | 前往讨论 (7)
Maxim Kuznetsov
Maxim Kuznetsov | 15 5月 2026 在 12:12
Andrey F. Zelinsky #:
“螃蟹”谐波形态与“哈特利蝴蝶”有何区别

根本没什么区别 :-)

右侧突起部分扩大了1.68倍,也就是说,它和蝴蝶形态本质上是一样的,只是额外增加了一个斐波那契位。如果你要操作的话,代码完全一样。

Andrey F. Zelinsky
Andrey F. Zelinsky | 15 5月 2026 在 13:17
Maxim Kuznetsov #:

根本没什么 :-)

右侧突起扩大了1.68倍,也就是说还是那个蝴蝶形态,只是额外增加了一个斐波那契位。如果你要实现的话,代码完全一样。

任何形态的关键不在于代码,而在于信号。

不同形态的信号类型各不相同——这意味着一种形态适用于某种情况,而另一种形态则适用于另一种情况。

Vitaly Muzichenko
Vitaly Muzichenko | 15 5月 2026 在 13:45
Andrey F. Zelinsky #:

任何模式都不是关于代码的,而是关于信号的。

模式的信号类型各不相同——这意味着一种模式适用于某种情况,而另一种模式则适用于另一种情况。

……而结果却始终如一 = 50/50
Andrey F. Zelinsky
Andrey F. Zelinsky | 15 5月 2026 在 13:59
Vitaly Muzichenko #:
……而结果依然是50/50

这篇文章讲的是交易模式,而不是信号的哲学。

信号哲学早已被阐述、演绎并极其通俗易懂地解释过,例如在经典电影《华尔街之狼》中,马克·汉恩与乔丹·贝尔福特的对话就体现了这一点:

俄语配音版视频请见此处:https://www.youtube.com/watch?v=fRVwyZ6t7Fk&t=112s

引文:“没人知道股价是会上涨、下跌、横盘还是打转——这一切都是胡默尔,是海默拉,只有胡默尔才能理解它。

Andrey F. Zelinsky
Andrey F. Zelinsky | 15 5月 2026 在 14:16
Vitaly Muzichenko #:
……而结果依然是50/50

只是除了该模式发出的一个信号之外——还有一系列不同类型的信号(进场/出场信号、过滤信号),还有资金管理策略——最终,实际结果的概率远非50/50

利用深度强化学习优化Ilan智能交易系统 利用深度强化学习优化Ilan智能交易系统
本研究对经典Ilan网格交易策略进行改进,通过在MQL5中集成Q-learning算法,构建出适用于MetaTrader 5平台的自适应版本。本文展示如何定义状态特征、将连续状态空间映射为Q-table可处理的离散值、使用ε-贪婪算法选择操作,以及针对平均持仓收益与平仓事件设计奖励机制。您将实现Q-table的存储与加载功能、通过参数调优优化学习效率,并在策略测试器中对EURUSD(欧元兑美元)/AUDUSD(澳元兑美元)进行测试,以评估稳定性和回撤风险。
价格行为分析工具包开发(第三十六部分):实现Python直接读取MetaTrader 5行情数据流 价格行为分析工具包开发(第三十六部分):实现Python直接读取MetaTrader 5行情数据流
借助Python的数据科学生态以及MetaTrader 5官方客户端库,充分挖掘MetaTrader 5终端的全部能力。本文演示如何完成身份校验,将实时Tick数据与分钟K线数据流直接写入Parquet存储;利用Ta技术指标库与Prophet时间序列预测工具完成高阶特征工程,并训练具备时间感知能力的梯度提升模型。接下来,我们部署轻量Flask服务,对外实时输出交易信号。无论您正在搭建混合量化框架,还是希望借助机器学习增强EA策略,都将掌握一套稳健的端到端流水线,用于数据驱动的算法交易。
开发多币种 EA(第 28 部分):添加平仓管理器 开发多币种 EA(第 28 部分):添加平仓管理器
在并行运行多个策略时,您可能希望定期关闭所有持仓,并重新启动策略。现有代码仅允许通过手动操作来实现这一行为。让我们尝试将这一部分自动化。
MQL5自动化交易策略(第二十六部分):构建针形K线均价加仓的多持仓交易系统 MQL5自动化交易策略(第二十六部分):构建针形K线均价加仓的多持仓交易系统
在本文中,我们将在MQL5中开发一套针形K线(Pin Bar)均价加仓系统:识别针形K线形态作为开仓触发信号,采用均价加仓策略实现多持仓管理,并搭配移动止损与盈亏平衡调整功能增强策略。系统内置可自定义参数,同时配备信息面板,可对持仓与盈利情况进行实时监控。