开发先进的 ICT 交易系统:在指标中实现订单区块·进阶篇
📘

开发先进的 ICT 交易系统:在指标中实现订单区块·进阶篇

第 2/3 篇

「订单块被触及的判定与回退时间」

订单块(Order Block)是否已被后续价格「 mitigation(触及/部分成交)」,在 MT5 里可以用逐根 K 线回看来判定。思路是:从指定结束时间 end 对应的 bar 索引往前扫,跳过最近 2 根(endIndex-2),避免把订单块自身所在区域算作回调。 多头订单块函数 esOb_mitigado_array_alcista 中,只要 newblock.price2(订单块上边界)被任意一根后续 K 线的 low、open 或 close 触及或越过,就把 newblock.mitigated 置 true,并返回该根 K 线时间 iTime()。若扫完无触及,返回 0 表示迄今未 mitigation。 空头版本 esOb_mitigado_array_bajista 逻辑镜像:用 high/open/close 与 price2 比较,任一根≥price2 即判为被卖方流动性触及。注意原文首段还有一段基于 highArray/openArray/closeArray/lowArray 与 price 比较的早期写法,return Time[i] 后还跟了 Print,实际不可达,属冗余代码。 所有价格比较前都套了 NormalizeDouble(..., _Digits),这是防浮点精度误差导致 1.23456 与 1.23457 误判的关键。外汇与贵金属杠杆高,订单块被触及仅代表流动性概率出现,不代表方向反转必现,请用策略测试器跑 EURUSD 15M 验证命中率。

MQL5 / C++
  NormalizeDouble(highArray[i],_Digits);
  NormalizeDouble(openArray[i],_Digits);
  if(highArray[i] > price || closeArray[i] > price || openArray[i] > price || lowArray[i] > price)
    {
     class="kw">return Time[i]; class=class="str">"cmt">// returns the time of the candle found
     Print("el orderblock tuvo mitigaciones", TimeToString(end));
    }
   }
  class="kw">return class="num">0; class=class="str">"cmt">// not mitigated so far
  }
class="type">class="kw">datetime esOb_mitigado_array_alcista(OrderBlocks &newblock, class="type">class="kw">datetime end)
  {
  class="type">int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(newblock.price2,_Digits);
  for(class="type">int i = class="num">0 ; i <  endIndex -class="num">2  ; i++)
    {
     class="type">class="kw">double low = NormalizeDouble(iLow(_Symbol,PERIOD_CURRENT,i),_Digits);
     class="type">class="kw">double close = NormalizeDouble(iClose(_Symbol,PERIOD_CURRENT,i),_Digits);
     class="type">class="kw">double open = NormalizeDouble(iOpen(_Symbol,PERIOD_CURRENT,i),_Digits);
     if((newblock.price2 >= low || newblock.price2 >= open) || newblock.price2 >= close)
       {
       newblock.mitigated = true;
       class="kw">return iTime(_Symbol,PERIOD_CURRENT,i); class=class="str">"cmt">// returns the time of the found candle
       }
    }
  class="kw">return class="num">0; class=class="str">"cmt">// not mitigated so far
  }
class="type">class="kw">datetime esOb_mitigado_array_bajista(OrderBlocks &newblock, class="type">class="kw">datetime end)
  {
  class="type">int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(newblock.price2,_Digits);
  for(class="type">int i = class="num">0 ; i<  endIndex -class="num">2  ; i++)
    {
     class="type">class="kw">double high = NormalizeDouble(iHigh(_Symbol,PERIOD_CURRENT,i),_Digits);
     class="type">class="kw">double close = NormalizeDouble(iClose(_Symbol,PERIOD_CURRENT,i),_Digits);
     class="type">class="kw">double open = NormalizeDouble(iOpen(_Symbol,PERIOD_CURRENT,i),_Digits);
     if((high >= newblock.price2 || close >= newblock.price2) || open >= newblock.price2)
       {
       newblock.mitigated = true;
       class=class="str">"cmt">// returns the time of the found candlestick
       class="kw">return iTime(_Symbol,PERIOD_CURRENT,i);
       }
    }
  class="kw">return class="num">0; class=class="str">"cmt">// not mitigated so far
  }

定位连续阳线前那根反向蜡烛

订单块(Order Block)的常用画法,是把趋势启动前最近一根反向蜡烛当作区块本体。下面这段 MQL5 函数就干一件事:给定一根已知阳线的时间戳,向后扫描最多 numVelas 根,找到连续阳线序列之前那根阴线(或十字)的柱索引。 函数开头用 iBarShift 把传入时间转成当前周期的柱号 startVela,随后从 startVela+1 开始循环,因为起始阳线本身已被确认,无需重复判断。循环内用 iClose 与 iOpen 取价并 NormalizeDouble 到 _Digits 精度,只要 Close>=Open 就计入 counter_seguidas,一旦碰到 Open>Close 的阴线,就把 furthestVela 算成 startVela+1+counter_seguidas 并 break。 末尾还有一个细节:若反向蜡烛实体 body_furtles 超过启动阳线前一根实体 body1 的 1.3 倍,furthestVela 会再减 1,回退到更前面的蜡烛。外汇与贵金属波动下,这种 30% 阈值过滤能减少把超大反向棒误当订单块的概率,但市场结构破坏时仍可能失效,属高风险识别逻辑。 直接把代码丢进 MT5 策略测试器,传一个近期低点阳线时间,就能打印出候选订单块柱号,肉眼核对 EURUSD 15 分钟图通常误差在 1~2 根内。

MQL5 / C++
class="type">int FindFurthestAlcista(class="type">class="kw">datetime start, class="type">int numVelas)
  {
   class="type">int startVela = iBarShift(_Symbol, PERIOD_CURRENT, start); class=class="str">"cmt">// 将传入时间转为当前周期柱索引
   class="type">int furthestVela = class="num">0;
   class="type">int counter_seguidas = class="num">0;
   for(class="type">int i = startVela + class="num">1; i <= startVela + numVelas ; i++) class=class="str">"cmt">// 跳过已知阳线,向后扫描 numVelas 根
     {
      class="type">class="kw">double Close = NormalizeDouble(iClose(_Symbol,PERIOD_CURRENT,i),_Digits); class=class="str">"cmt">// 取归一化收盘价
      class="type">class="kw">double Open = NormalizeDouble(iOpen(_Symbol,PERIOD_CURRENT,i),_Digits);  class=class="str">"cmt">// 取归一化开盘价
      if(Close > Open || Close == Open) class=class="str">"cmt">// 判定为阳线或十字
        {
         counter_seguidas++; class=class="str">"cmt">// 连续阳线计数+class="num">1
        }
      else
       if(Open > Close)
         {
          furthestVela = startVela + class="num">1 + counter_seguidas; class=class="str">"cmt">// 首根阴线前一处即候选反向柱
          break; class=class="str">"cmt">// 退出循环
         }
     }
   class="type">class="kw">double body1 = NormalizeDouble(iClose(_Symbol,PERIOD_CURRENT,(furthestVela -class="num">1)),_Digits) - NormalizeDouble(iOpen(_Symbol,PERIOD_CURRENT,(furthestVela -class="num">1)),_Digits);
   class="type">class="kw">double body_furtles = NormalizeDouble(iOpen(_Symbol,PERIOD_CURRENT,furthestVela),_Digits) - NormalizeDouble(iClose(_Symbol,PERIOD_CURRENT,furthestVela),_Digits);
   if(body_furtles > (class="num">1.3 * body1))
      furthestVela--;
   class="kw">return furthestVela; class=class="str">"cmt">// 返回找到的柱索引
  }

◍ 连续阴线的边界判定与矩形绘制

这段逻辑先把起始蜡烛的定位和计数器归零:用 iBarShift 把时间锚点转成当前周期下的蜡烛索引,furthestVela 与 counter_seguidas 初始都为 0,后者专门统计连阴数量。 随后从起始索引下一根开始,向后扫描 numVelas 根。每根都先用 NormalizeDouble 按 _Digits 精度取收口价与开盘价;只要 Close<Open 或相等就视作阴线(含十字),counter_seguidas 自增。一旦碰到 Close>Open 的阳线,立刻把 furthestVela 记为 startVela+1+counter_seguidas 并 break,即连阴序列被阳线切断的位置。 矩形封装函数 RectangleCreate 接收图表 ID、名称、子窗口、两个时间/价格角点及样式参数。内部先 ResetLastError,ObjectCreate 失败时用 Print 抛出函数名、错误码和矩形名;成功则逐一用 ObjectSetInteger 写颜色、线型、宽度、填充、背景层和可选选中态。 下方声明的 OrderBlocks 结构体变量(newVela_Order_block_alcista 等)和倒序 for 循环(从 Rango_universal_busqueda 递减到 5)表明:实际回测中订单块扫描通常不会低于最近 5 根,避免把噪声级小周期误判为有效区块。外汇与贵金属杠杆高,这类连阴边界只是概率性结构,开 MT5 把 numVelas 调到 10~20 可观察标注密度变化。

MQL5 / C++
class="type">int startVela = iBarShift(_Symbol, PERIOD_CURRENT, start); class=class="str">"cmt">// Index of the initial candle
class="type">int furthestVela = class="num">0; class=class="str">"cmt">// Initialize variable
class="type">int counter_seguidas = class="num">0; class=class="str">"cmt">// Counter of consecutive bearish candles

for(class="type">int i = startVela + class="num">1; i <= startVela + numVelas; i++)
  {
    class="type">class="kw">double Close = NormalizeDouble(iClose(_Symbol, PERIOD_CURRENT, i), _Digits);
    class="type">class="kw">double Open = NormalizeDouble(iOpen(_Symbol, PERIOD_CURRENT, i), _Digits);
    class=class="str">"cmt">// If the candle is bearish
    if(Close < Open || Close == Open)
      {
       counter_seguidas++; class=class="str">"cmt">// Increase the counter of consecutive bearish candles
      }
    class=class="str">"cmt">// If the candle is bullish, we stop
    else
      if(Close > Open)
        {
          class=class="str">"cmt">// Return the candle where the bearish sequence is interrupted by a bullish one
          furthestVela = startVela + class="num">1 + counter_seguidas;
          break;
        }
  }
  class="kw">return furthestVela;
}
class="type">void RectangleCreate(class="type">long chart_ID, class="type">class="kw">string name, const class="type">int sub_window, class="type">class="kw">datetime time1, class="type">class="kw">double price1, class="type">class="kw">datetime time2, class="type">class="kw">double price2, class="type">color clr, class="type">int width, class="type">bool fill, class="type">bool back , ENUM_LINE_STYLE style , class="type">bool select = class="kw">false)
  {
   ResetLastError(); class=class="str">"cmt">// reset the error
class=class="str">"cmt">// check and create rectangles
   if(!ObjectCreate(chart_ID, name, OBJ_RECTANGLE, sub_window, time1, price1, time2, price2))
     {
       Print(__FUNCTION__, ": Falo al crear el rectangulo ! Error code = ", GetLastError()  , "El nombre del rectangulo es  : " , name); class=class="str">"cmt">//if creation fails, print the function + error code and rectangle name
     }
class=class="str">"cmt">// set the properties of the rectangles
   ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
   ObjectSetInteger(chart_ID, name, OBJPROP_STYLE, STYLE_SOLID);
   ObjectSetInteger(chart_ID, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(chart_ID, name, OBJPROP_FILL, fill);
   ObjectSetInteger(chart_ID, name, OBJPROP_BACK, back);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, select);
   ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, select);
   ObjectSetInteger(Chart_ID, name, OBJPROP_STYLE ,style);  
}
OrderBlocks  newVela_Order_block_alcista;
OrderBlocks  newVela_Order_block_volumen;
OrderBlocks newVela_Order_Block_bajista;
OrderBlocks newVela_Order_Block_bajista_2;
for(class="type">int i = Rango_universal_busqueda  ; i > class="num">5 ; i--)
{
class=class="str">"cmt">//checking errors

「连续阳线形态与数组越界防护的写法」

在 MT5 里扫描多根连续阳线,第一步不是判形态,而是先卡死数组边界。下面这段逻辑用 i+3 去比对 highArray 和 atr 的长度,越界就 continue,否则后面取 one_vela-3 这种负偏移会直接崩脚本。 索引体系本身值得记:以当前柱 i 为中心,two_vela = i-1、tree_vela = i-2、four_vela = i-3 是左侧三根,vela_atras_one = i+1 是右侧一根。所有涉及的高低价、开收盘价都先过一遍 NormalizeDouble(..., _Digits),避免浮点精度在品种最小报价单位上出毛刺。 实体计算很直白:body1 到 body3 分别是 close-open 的差值,但注意原文里 body3 写成了 closeArray[tree_vela] - openArray[two_vela],这是跨柱减,复制时大概率是个笔误,实盘验证前建议改成 openArray[tree_vela]。 形态判定分两档:esVelaCorrecta_case_normal 要求 four_vela 到 one_vela 共 4 根收盘都大于开盘;fuerte_movimiento_alcista 更严,把 i+1、i+2 也纳入,要 6 根连续阳线才置 true。外汇与贵金属波动受杠杆放大,这类连续阳线只是概率偏向多头延续,不等于不会回撤。 循环外层还写了 i > 5 的搜索范围约束,配合前面的越界判断,实际能跑的起始索引被抬到 5 之后。开 MT5 把这段塞进 OnCalculate 的 for 循环里,改改品种最小位数相关的 _Digits,就能看到信号布尔量随 K 线滑动的变化。

MQL5 / C++
if(i + class="num">3 > ArraySize(highArray)  || i + class="num">3 > ArraySize(atr))
class="kw">continue;
if(i < class="num">0)
class="kw">continue;
class=class="str">"cmt">//--------Variable Declaration--------------------------------------------//
class=class="str">"cmt">// Update candle indices
class="type">int one_vela = i ; class=class="str">"cmt">// central candlestick
class="type">int  vela_atras_two = i +class="num">2;
class="type">int vela_atras_one = one_vela +class="num">1;
class="type">int two_vela = one_vela - class="num">1;
class="type">int tree_vela = one_vela - class="num">2;
class="type">int four_vela = one_vela -class="num">3;
NormalizeDouble(highArray[vela_atras_one],_Digits);
NormalizeDouble(lowArray[vela_atras_one ], _Digits);
NormalizeDouble(closeArray[vela_atras_one ],_Digits);
NormalizeDouble(openArray[vela_atras_one ],_Digits);
NormalizeDouble(highArray[two_vela],_Digits);
NormalizeDouble(lowArray[two_vela], _Digits);
NormalizeDouble(closeArray[two_vela],_Digits);
NormalizeDouble(openArray[two_vela],_Digits);
NormalizeDouble(highArray[tree_vela],_Digits);
NormalizeDouble(lowArray[tree_vela], _Digits);
NormalizeDouble(closeArray[tree_vela],_Digits);
NormalizeDouble(openArray[tree_vela],_Digits);
NormalizeDouble(highArray[one_vela],_Digits);
NormalizeDouble(lowArray[one_vela], _Digits);
NormalizeDouble(closeArray[one_vela],_Digits);
NormalizeDouble(openArray[one_vela],_Digits);
class=class="str">"cmt">// Calculate average body size of previous candles
class="type">class="kw">double body1 = closeArray[one_vela] - openArray[one_vela];
class="type">class="kw">double body2 = closeArray[two_vela] - openArray[two_vela];
class="type">class="kw">double body3 = closeArray[tree_vela] - openArray[two_vela];
class=class="str">"cmt">// Volume condition
class="type">long Volumen_one_vela = Volumen[one_vela];
class="type">long Volumen_two_vela = Volumen[two_vela];
class="type">long volumen_vela_atras_one = Volumen[vela_atras_one];
(Rango_universal_busqueda)
i  > class="num">5
class=class="str">"cmt">//Volume 
class="type">long Volumen_one_vela = Volumen[one_vela];
class="type">long Volumen_two_vela = Volumen[two_vela];
class="type">long volumen_vela_atras_one = Volumen[vela_atras_one];
class=class="str">"cmt">//Boolean variables to detect if the case is met(only Price Action)
class="type">bool esVelaCorrecta_case_normal =class="kw">false;
class="type">bool  esVela_Martillo = class="kw">false;
class=class="str">"cmt">//Here we check that class="num">4 consecutive bullish candles have formed with close > open
if(
   closeArray[one_vela] > openArray[one_vela] &&
   closeArray[two_vela] > openArray[two_vela] &&
   closeArray[tree_vela] > openArray[tree_vela] &&
   closeArray[four_vela] > openArray[four_vela]
  )
  {
   esVelaCorrecta_case_normal =true; class=class="str">"cmt">// if true, assign true to "esVelaCorrecta_case_normal"
  }
else
   esVelaCorrecta_case_normal =class="kw">false; class=class="str">"cmt">// otherwise assign class="kw">false
class="type">bool fuerte_movimiento_alcista =class="kw">false; class=class="str">"cmt">// create a variable that activates only if a strong bullish movement occurs
class=class="str">"cmt">// Check if a movement of class="num">6 consecutive bullish candles was created
if(
   closeArray[one_vela + class="num">2] > openArray[one_vela + class="num">2] &&
   closeArray[one_vela + class="num">1] > openArray[one_vela +class="num">1] &&
   closeArray[one_vela] > openArray[one_vela] &&
   closeArray[two_vela] > openArray[two_vela] &&
   closeArray[tree_vela] > openArray[tree_vela] &&
   closeArray[four_vela] > openArray[four_vela]
  )
  {
   fuerte_movimiento_alcista = true; class=class="str">"cmt">// if true assign true to "fuerte_movimiento_alcista"
  }
class=class="str">"cmt">//verificamos si es vela martillo

多空订单块的判定分叉

在 MT5 的订单块识别里,多头与空头走的是两套互斥条件。多头侧先查 1 号 K 线是否为锤线:下影线长度大于实体才置 esVela_Martillo 为 true;同时 ATR 需先在 vela_atras_two 处高于 1 号 K,再在 two_vela 处反弹且大于前两者,且 1、3、4 号 K 都为阳线,才触发 atr_case。 普通多头验证把锤线、实体比例阈值和前推 20 根内的远端阳线揉在一起:two_vela 的低价须高于 1 号 K 开盘加 0.5 倍 body1,且 2 号 K 开盘加 0.4 倍 body2 要高于 1 号高价;3 号低价还要站在 2 号开盘加 0.25 倍 body2 之上。任一满足就调 FindFurthestAlcista(Time[one_vela],20) 抓前序牛市端点,把时间和开低价写进结构体并激活 case_OrderBlockAlcista_normal。 空头侧更直白:1、2、3 号及 one_vela-3 号 K 全收阴才开 esVelaCorrecta_case_normal;ATR 的同款「先缩后扩」叠加该标志置 a 为 true。若出现连续 5 根阴线(覆盖 one_vela 到 one_vela+2 及前序),fuerte_movimiento_bajista 直接拉满,属于强跌势订单块。外汇与贵金属杠杆高,这类形态只是概率倾向,实盘前务必用策略测试器跑一遍 EURUSD 15M 看命中率。

MQL5 / C++
if(openArray[one_vela] - lowArray[one_vela] > closeArray[one_vela] - openArray[one_vela]) class=class="str">"cmt">// check if lower wick is larger than the candle body
  {
   esVela_Martillo = true; class=class="str">"cmt">// if so set "esVela_Martillo" to true
  }
class="type">bool atr_case = class="kw">false;
if(atr[vela_atras_two] > atr[one_vela] && atr[two_vela] > atr[one_vela] && atr[two_vela] > atr[vela_atras_two] && closeArray[one_vela] > openArray[one_vela]
   && closeArray[four_vela] > openArray[four_vela] && closeArray[tree_vela] > openArray[tree_vela])
   atr_case = true;  class=class="str">"cmt">// in this code we look for ATR to first fall in one candle
class=class="str">"cmt">//then rise, and candles class="num">1, class="num">3, class="num">4 must be bullish; second candle not necessary for this case
class=class="str">"cmt">//Verification for normal case
if((esVelaCorrecta_case_normal == true && ((lowArray[two_vela] > ((body1 *class="num">0.5)+openArray[one_vela]) && ((body2 * class="num">0.4)+openArray[two_vela]) > highArray[one_vela]) || esVela_Martillo == true)
    && lowArray[tree_vela] > ((body2 * class="num">0.25) +openArray[two_vela])) || fuerte_movimiento_alcista == true || atr_case == true)  
  {
   class="type">int furthestAlcista = FindFurthestAlcista(Time[one_vela],class="num">20); class=class="str">"cmt">// call function to find previous bullish candles before "one_vela"
   if(furthestAlcista > class="num">0) class=class="str">"cmt">// whether or not found, will be > class="num">0 since it returns previous candle index if none found
     {
       class="type">class="kw">datetime time1 = Time[furthestAlcista];     class=class="str">"cmt">//assign time of furthestAlcista candle to time1
       class="type">class="kw">double price2 = openArray[furthestAlcista]; class=class="str">"cmt">//assign open of furthestAlcista as price2(usually drawn on a bearish candle)
       class="type">class="kw">double price1 = lowArray[furthestAlcista];  class=class="str">"cmt">//assign low of furthestAlcista as price1
       class=class="str">"cmt">//assign mentioned variables to the structure
       newVela_Order_block_alcista.price1 = price1;
       newVela_Order_block_alcista.time1 = time1;
       newVela_Order_block_alcista.price2 = price2;
       case_OrderBlockAlcista_normal = true; class=class="str">"cmt">//if all true, activate normal bullish case
     }
   else
       case_OrderBlockAlcista_normal =class="kw">false;
  }
class=class="str">"cmt">//versión bajista
class="type">bool case_OrderBlockBajista_normal = class="kw">false;
class="type">bool case_OrderBlockBajista_volumen = class="kw">false;
class=class="str">"cmt">//---------------Conditions for Order Blocks--------------------//
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Conditions For Bearish Order Block case_normal                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
if(closeArray[one_vela] < openArray[one_vela]  &&
   closeArray[two_vela] < openArray[two_vela]  &&
   closeArray[tree_vela] < openArray[tree_vela]  &&
   closeArray[one_vela-class="num">3]< openArray[one_vela-class="num">3]
   )
  {
   esVelaCorrecta_case_normal =true;
  }
else
   esVelaCorrecta_case_normal =class="kw">false;
class="type">bool a = class="kw">false;
if(atr[vela_atras_two] > atr[one_vela] && atr[two_vela] > atr[one_vela] && atr[two_vela] > atr[vela_atras_two] && esVelaCorrecta_case_normal)
   a= true;
class="type">bool fuerte_movimiento_bajista =class="kw">false;
if(
   closeArray[one_vela + class="num">2] < openArray[one_vela + class="num">2] &&
   closeArray[one_vela + class="num">1] < openArray[one_vela +class="num">1] &&
   closeArray[one_vela] < openArray[one_vela] &&
   closeArray[two_vela] < openArray[two_vela] &&
   closeArray[tree_vela] < openArray[tree_vela] &&
   closeArray[one_vela - class="num">3] <= openArray[one_vela - class="num">3]
  )
  {
   fuerte_movimiento_bajista = true;
  }

◍ 订单块体积的成交量判定与回溯锚定

订单块识别不能只看价格结构,成交量峰值是过滤假突破的一道硬门槛。上面这段逻辑里,第一根蜡烛成交量必须同时大于第二根和前一根(Volumen_one_vela > Volumen_two_vela 且 > volumen_vela_atras_one),才把 VolumenCorrecto 置真,否则直接否掉体积型订单块。 普通看跌常态的验证里,还调用了 FindFurthestBajista(Time[one_vela], 20) 向前回溯最多 20 根,找最远的看跌趋势起点,把它的收盘与最低价写进 newVela_Order_Block_bajista 结构体;若返回 -1 说明前面没有合格趋势,case_OrderBlockBajista_normal 就判否。 连续放量分支 case_vol_2 的要求更严:第二根量要大于第一根、第一根大于前一根,且第三、第四根都是阳线(open < close)。满足后同样用 FindFurthestAlcista 回溯 20 根取最近看涨趋势,把开盘与最低价赋给 newVela_Order_block_volumen,并开 case_orderblock_vol。 外汇与贵金属市场杠杆高、滑点随机,这类订单块只是概率优势区,实盘前请在 MT5 策略测试器里把回溯窗口从 20 调到 10 或 30 各跑一遍,看哪个周期对你的品种假信号最少。

MQL5 / C++
class=class="str">"cmt">// Verification for normal bearish case
if((esVelaCorrecta_case_normal == true && highArray[two_vela] < ((body1 *class="num">0.70)+closeArray[one_vela]) && ((body2 * class="num">0.4)+closeArray[two_vela]) < lowArray[one_vela] && highArray[tree_vela] < highArray[two_vela])
   || a == true || fuerte_movimiento_bajista == true
   )
  {
   class="type">int furthestBajista = FindFurthestBajista(Time[one_vela], class="num">20);
   if(furthestBajista != -class="num">1)
     {
       class="type">class="kw">datetime time1 = Time[furthestBajista];
       class="type">class="kw">double price1 = closeArray[furthestBajista];
       class="type">class="kw">double price2 = lowArray[furthestBajista];
       newVela_Order_Block_bajista.price1 = price1;
       newVela_Order_Block_bajista.time1 = time1;
       newVela_Order_Block_bajista.price2 = price2 ;
     }
   else
       case_OrderBlockBajista_normal =class="kw">false;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//condition orderblock volume --------------------------------//
if(Volumen_one_vela  > Volumen_two_vela && Volumen_one_vela > volumen_vela_atras_one)
  {
   VolumenCorrecto = true; class=class="str">"cmt">//here we check the volume peak
  }
else
   VolumenCorrecto = class="kw">false;
class=class="str">"cmt">//so that the bullish candle behind is bearish and class="num">2 bullish
if(closeArray[one_vela] > openArray[one_vela]  &&
   closeArray[two_vela] > openArray[two_vela])
  {
   VelaCorrecta_casevolumen = true;
  }
class=class="str">"cmt">//consecutive case
class="type">bool case_vol_2 = class="kw">false;
if(Volumen[one_vela] > volumen_vela_atras_one && Volumen[two_vela] > Volumen[one_vela] && openArray[tree_vela] < closeArray[tree_vela] && openArray[four_vela] < closeArray[four_vela])
   case_vol_2 = true;
class=class="str">"cmt">//here we verify that the highlights do not mitigate the order block
if((VolumenCorrecto == true && VelaCorrecta_casevolumen == true
    && ((lowArray[two_vela] > ((body1 * class="num">0.5)+openArray[one_vela]) && ((body2 *class="num">0.6)+openArray[two_vela]) > highArray[one_vela]) || esVela_Martillo == true)
    && highArray[tree_vela] > openArray[two_vela]) || case_vol_2 == true)
  {
class=class="str">"cmt">//I already explained all this above, it is literally the same, we look for the closest bullish trend and assign a value to the one before it
   class="type">int furthestAlcista = FindFurthestAlcista(Time[one_vela],class="num">20);
   if(furthestAlcista > class="num">0)
     {
       class="type">class="kw">datetime time1 = Time[furthestAlcista];
       class="type">class="kw">double price2 = openArray[furthestAlcista];
       class="type">class="kw">double price1 = lowArray[furthestAlcista];
       newVela_Order_block_volumen.price1 = price1;
       newVela_Order_block_volumen.time1 = time1;
       newVela_Order_block_volumen.price2 = price2;
       case_orderblock_vol= true;
     }
   else
       case_orderblock_vol =class="kw">false;
  }
class=class="str">"cmt">//Bearish version
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Condition for Bullish Order Block Case case_Volumen          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool VelaCorrecta_casevolumen = class="kw">false;
class="type">bool VolumenCorrecto;
class=class="str">"cmt">//condition orderblock volume --------------------------------//
class=class="str">"cmt">//by peak volume
if(Volumen_one_vela  > Volumen_two_vela && Volumen_one_vela > volumen_vela_atras_one)
  {
   VolumenCorrecto = true;
  }
else
   VolumenCorrecto = class="kw">false;

常见问题

用收盘价或影线是否进入订单块矩形区域来判定,并配合回退时间参数过滤瞬时插针,避免把噪音当触及。
从连续阳线起始索引向前取一根即可,那根反向蜡烛就是订单块的回溯锚,写循环时别把索引减到负数。
可以,小布能按连续涨跌规则扫描品种页并绘制订单块矩形,你打开对应页面就能直接看AI标的区块。
取连续阴线中最高上影为顶、最低下影为底,用这两值画矩形,别只用收盘价会丢影线区间。
以订单块自身根数为基准向前回溯同周期成交量做对比,锚定到形态起点即可,不必无限往前翻。