如何将聪明钱概念(OB)与斐波那契指标相结合,实现最优进场策略·进阶篇
📘

如何将聪明钱概念(OB)与斐波那契指标相结合,实现最优进场策略·进阶篇

第 2/2 篇

用左右各5根K线卡出波段点

EA 加载时 OnInit 只做一件事:给 trade 对象挂上 MagicNumber,作为这批订单的唯一标签,不在这里碰任何价格逻辑。 波段识别的核心在一段循环里:len 固定为 5,bar_Now 取倒数第 5 根 K 线,用 getHigh / getLow 把当前 K 线与左右各 5 根比一遍。只要右侧出现更高或相等高点,或左侧有更高高点,isSwingH 就翻 false;低点同理,任一侧更低或相等就否掉 isSwingL。 留到最后的 true 才是真波段:高点写进 fib_high 和 fib_t1,低点写进 fib_low 和 fib_t2,后面直接喂给斐波那契对象。你在 MT5 里把 len 改成 3 或 8,波段灵敏度会明显不同,可立刻回看图表验证。 订单块扫描靠静态变量 prevDay 卡每日只跑一次:在 Time1Hstrt 到 Time1Hend 时段内,三根 K 线组合(阳-阳-阴为看涨,阴-阴-阳为看跌)确认后才 new 一个 COrderBlock,赋好方向、时间、高低价和颜色;日期没变就跳过,避免重复计算吃 CPU。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Expert initialization function                                 |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> <span class="functions">OnInit</span>(){
   trade.SetExpertMagicNumber(MagicNumber);
   <span class="keyword">class="kw">return</span>(<span class="macro">INIT_SUCCEEDED</span>);
}
<span class="keyword">const</span> <span class="keyword">class="type">int</span> len = <span class="number">class="num">5</span>;
<span class="keyword">class="type">int</span> left_bars, right_bars;
<span class="keyword">class="type">int</span> bar_Now = len;
<span class="keyword">class="type">bool</span> isSwingH = <span class="keyword">true</span>, isSwingL = <span class="keyword">true</span>;
<span class="keyword">for</span>(<span class="keyword">class="type">int</span> i = <span class="number">class="num">1</span>; i &lt;= len; i++){
   right_bars = bar_Now - i;
   left_bars = bar_Now + i;
<span class="keyword">if</span>((getHigh(bar_Now) &lt;= getHigh(right_bars)) ||(getHigh(bar_Now) &lt; getHigh(left_bars))){
   isSwingH = <span class="macro">class="kw">false</span>;
}
<span class="keyword">if</span>((getLow(bar_Now) &gt;= getLow(right_bars)) || getLow(bar_Now) &gt; getLow(left_bars)){
   isSwingL = <span class="macro">class="kw">false</span>;
}
<span class="keyword">if</span>(isSwingH){
   <span class="functions">Print</span>(<span class="class="type">class="kw">string">"We have a swing high at index: "</span>, bar_Now, <span class="class="type">class="kw">string">"at price: "</span>, getHigh(bar_Now));
   fib_high = getHigh(bar_Now);
   fib_t1 = getTime(bar_Now);
}
<span class="keyword">if</span>(isSwingL){
   <span class="functions">Print</span>(<span class="class="type">class="kw">string">"We have a swing low at index: "</span>, bar_Now,<span class="class="type">class="kw">string">" at price: "</span>, getLow(bar_Now));
   fib_low = getLow(bar_Now);
   fib_t2 = getTime(bar_Now);
}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//|              Function to find OB              |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> getOrderB(){
   <span class="keyword">class="kw">static</span> <span class="keyword">class="type">int</span> prevDay = <span class="number">class="num">0</span>;

   <span class="predefines">class="type">MqlDateTime</span> structTime;
   <span class="functions">TimeCurrent</span>(structTime);
   structTime.min = <span class="number">class="num">0</span>;
   structTime.sec = <span class="number">class="num">0</span>;

   structTime.hour = Time1Hstrt;
   <span class="keyword">class="type">class="kw">datetime</span> timestrt = <span class="functions">StructToTime</span>(structTime);

   structTime.hour = Time1Hend;
   <span class="keyword">class="type">class="kw">datetime</span> timend = <span class="functions">StructToTime</span>(structTime);
   <span class="keyword">if</span>(<span class="functions">TimeCurrent</span>() &gt;= timestrt &amp;&amp; <span class="functions">TimeCurrent</span>() &lt; timend){
      <span class="keyword">if</span>(prevDay != structTime.day_of_year){
         <span class="keyword">class="kw">delete</span> OB;

◍ 用三根 K 线结构锁定订单块

订单块(Order Block)的识别不靠肉眼猜,可以用连续三根偏移 K 线的收开盘关系来判定。下面这段 MT5 逻辑遍历最近 100 根柱,找的是看涨 OB:第 i 根收高于开,隔两根的 i+2 也收高于开,而 i+3 必须收低于开且开在 i+2 收盘下方,此时把 i+3 的时空与高低记为 OB 对象。 循环从 i=1 起扫,是因为索引 0 是当前未闭合柱,参与回看会引入未来函数式的偏差;上限 100 根约覆盖常规 H1 图表 4 个交易日,足够抓日内机构痕迹。

看跌 OB 是镜像条件:i 与 i+2 均收低于开,i+3 收高于开且开低于 i+2 收盘。打印确认时间用 TIME_DATETIME_MINUTES,能直接落到分钟级,方便你对照小布盯盘的成交流。

外汇与贵金属杠杆高,OB 被扫损概率不低,上述结构只提示「倾向」存在供需失衡,实盘须配合止损与多周期确认。

MQL5 / C++
for(class="type">int i = class="num">1; i < class="num">100; i++){
   if(getOpen(i) < getClose(i)){ class=class="str">"cmt">// index is i since the loop starts from i which is = class="num">1 "for(class="type">int i = class="num">1)..."
      if(getOpen(i + class="num">2) < getClose(i + class="num">2)){
         if(getOpen(i + class="num">3) > getClose(i + class="num">3) && getOpen(i + class="num">3) < getClose(i + class="num">2)){
            Print("Bullish Order Block confirmed at: ", TimeToString(getTime(i + class="num">2), TIME_DATE||TIME_MINUTES));
            class=class="str">"cmt">//isBullishOB = true;
            OB = new COrderBlock();
            OB.direction = class="num">1;
            OB.time = getTime(i + class="num">3);
            OB.high = getHigh(i + class="num">3);
            OB.low = getLow(i + class="num">3);
            isBullishOB = true;

            OBClr = isBullishOB ? BullOB : BearOB;

            class=class="str">"cmt">// specify strt time
            T1 = OB.time;

            class=class="str">"cmt">// reset BULL OB flag
            isBullishOB = class="kw">false;
            prevDay = structTime.day_of_year;
            class="kw">break;

            class="kw">delete OB;
         }
      }
   }
   if(getOpen(i) > getClose(i)){
      if(getOpen(i + class="num">2) > getClose(i + class="num">2)){
         if(getOpen(i + class="num">3) < getClose(i + class="num">3) && getOpen(i + class="num">3) < getClose(i + class="num">2)){
            Print("Bearish Order Block confirmed at: ", TimeToString(getTime(i + class="num">2), TIME_DATE||TIME_MINUTES));
            class=class="str">"cmt">//isBearishOB = true;

「熊块实例化与K线取值封装」

下面这段逻辑在扫描到看跌订单块时,会现场 new 一个 COrderBlock 对象,把方向写死为 -1,并取 i+3 这根 K 线的时间、最高、最低作为 OB 的边界。注意 OB.time 被同步赋给 T1,随后 isBearishOB 复位为 false,prevDay 记成当前 structTime.day_of_year,最后 break 跳出循环并 delete OB——这套动作意味着每个新交易日只捕获一次熊块,避免重复标记。 OBClr 用三元表达式在 BearOB / BullOB 之间切换,配色与方向绑定。外汇与贵金属市场波动剧烈,订单块失效概率不低,实盘前务必在 MT5 策略测试器用历史数据回放验证标记频率。 isNewBar() 靠静态变量 last_time 对比 SERIES_LASTBAR_DATE 判断新柱,首次调用直接返回 false 不触发扫描。getHigh / getLow / getOpen / getClose / getTime 五个小函数只是 iHigh、iLow、iOpen、iClose、iTime 的薄封装,调用时传 index 即可,省去每次写 _Symbol、_Period 的冗余。 OnTick 里只在 isNewBar() 为真时才跑 getOrderB(),也就是说订单块识别被钉在每根新 K 线开盘那一瞬,盘中重复报价不会重算,回测时这点直接影响信号触发次数。

MQL5 / C++
OB = new COrderBlock();
OB.direction = -class="num">1;
OB.time = getTime(i + class="num">3);
OB.high = getHigh(i + class="num">3);
OB.low = getLow(i + class="num">3);
isBearishOB = true;

OBClr = isBearishOB ? BearOB : BullOB;

T1 = OB.time;

class=class="str">"cmt">// reset the BEAR OB flag
isBearishOB = class="kw">false;
prevDay = structTime.day_of_year;
class="kw">break;

class="kw">delete OB;
}
					}
				}
			}
		}
	}
}
class="type">bool isNewBar() {
  class=class="str">"cmt">// Memorize the time of opening of the last bar in the class="kw">static variable
  class="kw">static class="type">class="kw">datetime last_time = class="num">0;

  class=class="str">"cmt">// Get current time
  class="type">class="kw">datetime lastbar_time = (class="type">class="kw">datetime)SeriesInfoInteger(Symbol(), Period(), SERIES_LASTBAR_DATE);
  class=class="str">"cmt">// First call
  if (last_time == class="num">0) {
    last_time = lastbar_time;
    class="kw">return class="kw">false;
  }
  class=class="str">"cmt">// If the time differs(new bar)
  if (last_time != lastbar_time) {
    last_time = lastbar_time;
    class="kw">return true;
  }
  class=class="str">"cmt">// If no new bar, class="kw">return class="kw">false
  class="kw">return class="kw">false;
}
class="type">class="kw">double getHigh(class="type">int index) {
    class="kw">return iHigh(_Symbol, _Period, index);
}
class="type">class="kw">double getLow(class="type">int index) {
    class="kw">return iLow(_Symbol, _Period, index);
}
class="type">class="kw">double getOpen(class="type">int index){
  class="kw">return iOpen(_Symbol, _Period, index);
}
class="type">class="kw">double getClose(class="type">int index){
  class="kw">return iClose(_Symbol, _Period, index);
}
class="type">class="kw">datetime getTime(class="type">int index) {
    class="kw">return iTime(_Symbol, _Period, index);
}
class="type">void OnTick(){
  if(isNewBar()){
    getOrderB();

订单块触发后的多空入场逻辑

在抓取摆动点之后,程序先取当前品种的买价与卖价:Bid 用 SymbolInfoDouble(_Symbol, SYMBOL_BID) 拿,Ask 用 SYMBOL_ASK 拿,这是后续判断触发条件的基准。 对于看涨订单块(OB.direction > 0),只有当卖价 Ask 仍低于 OB 区间上沿 OB.high 时才可能介入。此时入场价直接挂 Ask,止盈取 OB 形成时间之后最高价所在 K 线的高点,止损则落在 OB.low 减去缓冲量 Mgtn 的位置,并用 _Digits 做小数位归一。 代码里还会画一条斐波那契对象,按 Fib_Trade_lvls 百分比反推一个 entLvl 参考线。若 OB.high 不高于这条线,才真正画出 OB 并调用 trade.Buy 市价买入;否则直接 delete OB 放弃。外汇与贵金属杠杆高,这类市价单滑点可能吞掉缓冲,实盘前务必在 MT5 策略测试器跑一遍。 看跌分支完全对称:OB.direction < 0 且买价 Bid 高于 OB.low 时,入场价取 Bid,止盈取 OB 时间后最低价所在 K 线低点,止损为 OB.high 加 Mgtn。entLvl 由 fib_low 往上推,OB.low 不低于它才 trade.Sell,否则删块。最后一行把斐波那契线颜色设为黑色,纯视觉处理。

MQL5 / C++
      getSwings();
      
      class="type">class="kw">double Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      class="type">class="kw">double Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      
      if(CheckPointer(OB) != POINTER_INVALID && OB.direction > class="num">0 && Ask < OB.high){
         class="type">class="kw">double entry = Ask;
         class="type">class="kw">double tp = getHigh(iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, iBarShift(_Symbol, PERIOD_CURRENT, OB.time)));
         class="type">class="kw">double sl = NormalizeDouble(OB.low - Mgtn, _Digits);
         class=class="str">"cmt">// class="type">class="kw">double sl = getLow(iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, class="num">2,iBarShift(_Symbol, PERIOD_CURRENT, OB.time)));
         
         ObjectCreate( class="num">0, FIBO_OBJ, OBJ_FIBO, class="num">0, fib_t1, fib_low, fib_t2, fib_high);
         class="type">class="kw">double entLvl = fib_high - (fib_high - fib_low) * Fib_Trade_lvls / class="num">100; class=class="str">"cmt">// check this if non
         
         if(OB.high <= entLvl){
            T2  = getTime(class="num">0);
            OB.draw(T1, T2, OBClr);
            trade.Buy(Lots, _Symbol, entry, sl, tp, "OB buy");
            class="kw">delete OB;
         }else{
            class="kw">delete OB;
         }         
      }
      if(CheckPointer(OB) != POINTER_INVALID && OB.direction < class="num">0 && Bid > OB.low){
         class="type">class="kw">double entry = Bid;
         class="type">class="kw">double tp = getLow(iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, iBarShift(_Symbol, PERIOD_CURRENT, OB.time)));
         class="type">class="kw">double sl = NormalizeDouble(OB.high + Mgtn, _Digits);
         class=class="str">"cmt">// class="type">class="kw">double sl = getHigh(iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, iBarShift(_Symbol, PERIOD_CURRENT, OB.time)));
         
         ObjectCreate( class="num">0, FIBO_OBJ, OBJ_FIBO, class="num">0, fib_t2, fib_high, fib_t1, fib_low);
         class="type">class="kw">double entLvl = fib_low + (fib_low - fib_high) * Fib_Trade_lvls / class="num">100;
         
         if(OB.low >= entLvl){
            T2 = getTime(class="num">0);
            OB.draw(T1, T2, OBClr);
            trade.Sell(Lots, _Symbol, entry, sl, tp, "OB sell");
            class="kw">delete OB;
         }else{
            class="kw">delete OB;
         }         
      }
      
      ObjectSetInteger( class="num">0, FIBO_OBJ, OBJPROP_COLOR, clrBlack);

◍ 批量改斐波那契水平线颜色

在 MT5 里用循环遍历斐波那契对象的全部水平线,是批量改样式的直接办法。下面这段 MQL5 针对名为 FIBO_OBJ 的对象,把每一层的颜色刷成黑色。 代码逻辑并不复杂:先用 ObjectGetInteger 取该对象的总层数 OBJPROP_LEVELS,再从 0 到层数减一循环,对每一层调用 ObjectSetInteger 写入 OBJPROP_LEVELCOLOR 为 clrBlack。

MQL5 / C++
for(class="type">int i = class="num">0; i < ObjectGetInteger(class="num">0, FIBO_OBJ, OBJPROP_LEVELS); i++){
    ObjectSetInteger(class="num">0, FIBO_OBJ, OBJPROP_LEVELCOLOR, i, clrBlack);
}
逐行拆解: 第1行:定义整型 i 从 0 开始,循环上限是 FIBO_OBJ 的水平线总数,由 ObjectGetInteger 在图表 0 上读取 OBJPROP_LEVELS 得到。 第2行:对第 i 层设置颜色属性 OBJPROP_LEVELCOLOR,值为 clrBlack。 第3行:循环体结束。 开 MT5 自建一个斐波那契画线对象,把 FIBO_OBJ 换成你的实际对象名,跑这段就能看到所有水平线秒变黑色,省去手动一层层点。外汇与贵金属画线分析属高风险辅助手段,颜色调整不改变任何价格概率。

MQL5 / C++
for(class="type">int i = class="num">0; i < ObjectGetInteger(class="num">0, FIBO_OBJ, OBJPROP_LEVELS); i++){
    ObjectSetInteger(class="num">0, FIBO_OBJ, OBJPROP_LEVELCOLOR, i, clrBlack);
}

「记住这一条就够了」

把订单块、波段高低点与斐波那契回撤塞进同一个 OnTick 循环,本质是用代码把机构介入区和大单回撤位做成可重复的过滤网。EA 在每根新 K 线生成时重算看涨/看跌订单块,并用斐波那契水平卡住进场区,止损靠 mgtn 缓冲带挂在区块外侧,这套逻辑让散户和资金流保持同向的概率更高,但不是免死金牌。 外汇与贵金属杠杆高、滑点狠,XAUUSD 一天波个 20~30 美元是常事,EA 再顺结构也扛不住黑天鹅。真要跑 FIB_OB.mq5,先在策略测试器用 2023 年至今的 M15 数据跑一遍,看回撤是否落在你容忍的区间内,再谈实盘。 代码里波段点判定写死左右各 5 根 Bar,主结构抓得稳,但微调结构得把 len 调小,否则小级别假突破会直接被忽略。

常见问题

只在明显波段处数左右5根K线,若两侧K线高低杂乱、无单边延伸,就跳过;可先肉眼圈出波段再让代码校验。
不必须吞没,关键是第三根反向收盘且成交量或幅度放大;前两根合并区间即为订单块区域,用作后续触发参考。
可以,小布盯盘的AIGC会按你设的K线结构识别订单块并叠加斐波那契水平,打开对应品种页直接看标注,省去手绘。
价格回踩订单块且恰落在61.8%或50%斐波内才视为有效触发;跌破区块下沿并远离则倾向假信号,放弃入场。
手动易漏且乱,批量脚本一键统一色系能快速区分关键位;建议把50%、61.8%设醒目色,其余淡色,盘面更干净。