English Русский Español Deutsch 日本語
preview
如何将“聪明钱”概念(OB)与斐波那契指标相结合,实现最优进场策略

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

MetaTrader 5交易系统 |
151 3
Hlomohang John Borotho
Hlomohang John Borotho

引言

“聪明钱”的概念(SMC)与订单块是图表上机构交易者通常执行大额买卖指令的核心区域。这些区域往往标志着重要价格波动的起点,对于希望与机构资金行为保持一致的交易者至关重要。理解这些关键价位如何影响价格走势,可为散户提供更深入的市场洞察,帮助他们预判高概率行情。

若将订单块与斐波那契回撤工具结合,交易者可进一步优化进场策略。斐波那契回撤可测量近期波段高点与低点之间的潜在回调位,判断价格在延续趋势前可能回撤的幅度。通过把机构订单流与市场关注区域对齐,该方法帮助交易者精确定位最佳进场点,提高交易准确度。


EA的逻辑

看涨订单块:

在图表上,当一根看跌K线之后出现一根看涨K线,且该看涨K线完全吞没前一根看跌K线,即构成看涨订单块,标志着强劲看涨动能的起点。要确认为有效看涨订单块,看跌K线之后必须至少出现两根或更多连续看涨K线。正确交易看涨订单块的策略是:等待价格回撤并重新进入已识别的看涨订单块区域,再执行买入。

Bullish OB

看涨斐波那契回撤:

在确认看涨订单块后,使用斐波那契回撤工具寻找最佳做多入场点。先定位该订单块对应的近期波段高点与波段低点。然后从“低点→高点”绘制斐波那契回撤。若订单块位于或低于 61.8% 回撤位,即与机构买盘区域重合,视为高概率买入机会。

Bullish FIBO

看跌订单块:

当一根看涨 K 线被随后一根看跌 K 线完全吞没,标志强烈下行动量开始。且后续至少出现两根(或一连串)看跌 K 线,即构成看跌订单块。交易时应等待价格回测该看跌订单块区域,再执行卖出。

Bearish OB

看跌斐波那契回撤:

对于看跌订单块,同样用斐波那契回撤寻找最佳做空入场点。在定位看跌订单块后,先找出对应波段高点与低点。在此情况下,由于目标为卖出订单,斐波那契回撤应从波段高点画至波段低点。若订单块位于或高于 61.8% 回撤位,即为高概率做空入场区。

Bearish FIBO

让我们开始:

//+------------------------------------------------------------------+
//|                                                       FIB_OB.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include  <Trade/Trade.mqh>
CTrade trade;

#define BullOB clrLime
#define BearOB clrRed

#include <Trade/Trade.mqh> 这行代码引入了 MQL5 的交易库,它内建了管理交易、订单与持仓的函数。文件 Trade.mqh 中预定义了一系列类和函数,可大幅简化开仓、平仓、修改订单等操作。我们创建了一个名为 trade 的 CTrade 实例。CTrade 类封装了所有交易动作(下买单、卖单、平仓、改单等)。通过该对象,即可在EA中以面向对象的方式轻松管理交易。

随后,我们定义两个颜色常量,用于在图表上可视化看涨订单块与看跌订单块。BullOB 设为 clrLime(绿色),标记看涨订单块,暗示机构买盘可能介入的区域;BearOB 设为 clrRed(红色),标记看跌订单块,暗示机构卖盘可能介入的区域。

//+------------------------------------------------------------------+
//|                           Global vars                            |
//+------------------------------------------------------------------+
double Lots = 0.01;
int takeProfit = 170;
//int stopLoss = 200;
int length = 100;

input double stopLoss = 350;
input double Mgtn = 00.85;

bool isBullishOB = false; 
bool isBearishOB = false;

input int Time1Hstrt = 3;
input int Time1Hend = 4;

isBullishOB 与 isBearishOB 是两个布尔标志,用于追踪是否已检测到看涨或看跌的订单块(OB)。在全局变量中,我们还定义了时间设置相关的输入参数 Time1Hstrt 与 Time2Hend。Time1Hstrt:指定交易时段的起始小时,本例中对应纽约交易时段的开始时间;Time1Hend:指定该交易时段的结束小时。

class COrderBlock : public CObject {
public:
   int direction;
   datetime time;//[]
   double high;
   double low;
   
   void draw(datetime tmS, datetime tmE, color clr){
      string objOB = " OB REC" + TimeToString(time);
      ObjectCreate( 0, objOB, OBJ_RECTANGLE, 0, time, low, tmS, high);
      ObjectSetInteger( 0, objOB, OBJPROP_FILL, true);
      ObjectSetInteger( 0, objOB, OBJPROP_COLOR, clr);
      
      string objtrade = " OB trade" + TimeToString(time);
      ObjectCreate( 0, objtrade, OBJ_RECTANGLE, 0, tmS, high, tmE, low); // trnary operator
      ObjectSetInteger( 0, objtrade, OBJPROP_FILL, true);
      ObjectSetInteger( 0, objtrade, OBJPROP_COLOR, clr);
   }  
};

我们定义一个名为 COrderBlock 的类,用来在交易图表中建模“订单块”(Order Block,OB)。该类包含方向、时间、最高价、最低价等属性,以及一个 draw 方法,用于在图表上绘制订单块。COrderBlock 继承自 MQL5 的通用基类 CObject,CObject是MQL5中用于创建对象的通用类。因此可以直接使用 CObject 的所有方法与属性。

成员变量(属性)direction(int):订单块方向。1 表示看涨订单块(bullish),-1 表示看跌订单块(bearish)。time(datetime):订单块形成或被检测到的时间。它存储订单块事件的时间戳。high(double):订单块最高价(区域上边界)。low(double):订单块最低价(区域下边界)。这四个变量是订单块的核心属性。

字符串变量 objOB 用于保存将在图表上表示订单块的矩形对象的唯一名称。名称由前缀 "OB_REC" 与订单块时间(用 TimeToString() 转换)拼接而成。ObjectCreate() 函数负责在图表上创建矩形对象(OBJ_RECTANGLE)。参数 0 代表当前图表;objOB 是矩形对象的名称。time, low:矩形左下角坐标(起始时间与最低价)。tmS, high:矩形右上角坐标(结束时间与最高价)。

COrderBlock* OB;
color OBClr;
datetime T1;
datetime T2;

  • COrderBlock* OB:指向“订单块”对象的指针,用于在图表上管理与绘制订单块。
  • color OBClr:存储订单块颜色的变量,根据检测到的是看涨还是看跌订单块来决定具体颜色。
  • datetime T1:表示订单块起始时间的变量。
  • datetime T2:表示订单块结束时间的变量。
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){
   trade.SetExpertMagicNumber(MagicNumber);

   return(INIT_SUCCEEDED);
}

OnInit() 函数在 EA 首次加载到图表时被调用。在此函数里,我们只设置 EA 的 Magic 数字,作为该 EA 所有订单的唯一标识。

const int len = 5;
int left_bars, right_bars;
int bar_Now = len;
bool isSwingH = true, isSwingL = true;
  • len:设定左右两侧各需比较的 K 线根数。
  • left_bars, right_bars:用于存储当前 K 线左右两侧 K 线的索引。
  • bar_Now = len:当前被分析的 K 线索引设为 len(即 5 根 K 线前)。
  • isSwingH, isSwingL:布尔变量,初始为 true,用于判断当前 K 线是否为波段高点或波段低点。
for(int i = 1; i <= len; i++){
   right_bars = bar_Now - i;
   left_bars = bar_Now + i;

for 循环遍历 len(5)根 K 线,分别检查当前 K 线(bar_Now)左右两侧的价格。

  • right_bars = bar_Now - i:指向当前 K 线右侧第 i 根 K 线。
  • left_bars = bar_Now + i:指向当前 K 线左侧第 i 根 K 线。
if((getHigh(bar_Now) <= getHigh(right_bars)) ||(getHigh(bar_Now) < getHigh(left_bars))){
   isSwingH = false;
}

此条件检查当前 K 线高点是否小于或等于左右两侧任意一根 K 线的高点。若任意一侧出现更高或相等的高点,则当前 K 线不是波段高点,将 isSwingH 设为 false。

if((getLow(bar_Now) >= getLow(right_bars)) || getLow(bar_Now) > getLow(left_bars)){
   isSwingL = false;
}

与波段高点逻辑类似,此条件检查当前 K 线低点是否大于或等于左右两侧任意一根 K 线的低点。若任意一侧出现更低或相等的低点,则当前 K 线不是波段低点,将 isSwingL 设为 false。

if(isSwingH){
   Print("We have a swing high at index: ", bar_Now, "at price: ", getHigh(bar_Now));
   fib_high = getHigh(bar_Now);
   fib_t1 = getTime(bar_Now);
}

若 isSwingH 仍为 true,说明检测到波段高点。函数打印该 K 线索引及波段高点价格。并将波段高点价存入全局变量 fib_high,对应时间存入 fib_t1。这些值稍后作为斐波那契对象的参数。

if(isSwingL){
   Print("We have a swing low at index: ", bar_Now," at price: ", getLow(bar_Now));
   fib_low = getLow(bar_Now);
   fib_t2 = getTime(bar_Now);
}

同理,若 isSwingL 仍为 true,说明检测到波段低点。函数打印该 K 线索引及波段低点价格。并将波段低点价存入全局变量 fib_low,对应时间存入 fib_t2。这些值稍后作为斐波那契对象的参数。

//+------------------------------------------------------------------+
//|                      Function to find OB                         |
//+------------------------------------------------------------------+
void getOrderB(){

   static int prevDay = 0;
   
   MqlDateTime structTime;
   TimeCurrent(structTime);
   structTime.min = 0;
   structTime.sec = 0;
   
   structTime.hour = Time1Hstrt;
   datetime timestrt = StructToTime(structTime);
   
   structTime.hour = Time1Hend;
   datetime timend = StructToTime(structTime);

   if(TimeCurrent() >= timestrt && TimeCurrent() < timend){
      if(prevDay != structTime.day_of_year){
         delete OB;
         
         for(int i = 1; i < 100; i++){
            if(getOpen(i) < getClose(i)){ // index is i since the loop starts from i which is = 1 "for(int i = 1)..."
               if(getOpen(i + 2) < getClose(i + 2)){
                  if(getOpen(i + 3) > getClose(i + 3) && getOpen(i + 3) < getClose(i + 2)){
                     Print("Bullish Order Block confirmed at: ", TimeToString(getTime(i + 2), TIME_DATE||TIME_MINUTES));
                     //isBullishOB = true;
                     OB = new COrderBlock();
                     OB.direction = 1;
                     OB.time = getTime(i + 3);
                     OB.high = getHigh(i + 3);
                     OB.low = getLow(i + 3);
                     isBullishOB = true;
                     
                     OBClr = isBullishOB ? BullOB : BearOB;
                     
                     // specify strt time
                     T1 = OB.time;
                     
                     // reset BULL OB flag
                     isBullishOB = false;
                     prevDay = structTime.day_of_year;
                     break;
                     
                     delete OB;
                  }
               }
            }
            if(getOpen(i) > getClose(i)){
               if(getOpen(i + 2) > getClose(i + 2)){
                  if(getOpen(i + 3) < getClose(i + 3) && getOpen(i + 3) < getClose(i + 2)){
                     Print("Bearish Order Block confirmed at: ", TimeToString(getTime(i + 2), TIME_DATE||TIME_MINUTES));
                     //isBearishOB = true;
                     OB = new COrderBlock();
                     OB.direction = -1;
                     OB.time = getTime(i + 3);
                     OB.high = getHigh(i + 3);
                     OB.low = getLow(i + 3);
                     isBearishOB = true;
                     
                     OBClr = isBearishOB ? BearOB : BullOB;
                     
                     T1 = OB.time;
                     
                     // reset the BEAR OB flag
                     isBearishOB = false;
                     prevDay = structTime.day_of_year;
                     break;
                     
                     delete OB;
                  }
               }
            }
         }
      }
   }
}

该函数在指定时间段(由 Time1Hstrt 至 Time1end 限定)的价格数据中查找看涨与看跌订单块(Order Block)。一旦识别成功,便创建一个 COrderBlock 对象,并赋予方向、时间、最高价、最低价等属性。随后设置颜色与标志以便可视化及后续处理。prevDay 是一个静态变量,其值在函数调用间保持不变。确保每日仅执行一次订单块检测。

若当前日期已被处理过(prevDay 已记录),则直接跳过检测,避免重复计算。当日期更替时自动重置。函数通过以下价格行为规则判断订单块:

  • 看涨订单块的一系列条件:第一根 K 线为阳线(开盘价 < 收盘价)。第二根 K 线亦为阳线,作为确认。第三根 K 线为阴线,且其开盘价低于第二根 K 线的收盘价。
  • 若上述条件全部满足,则确认看涨订单块。
  • 随即创建新的 COrderBlock 对象,设置方向为看涨、记录时间与高低价等。
  • 同样,看跌订单块条件:第一根 K 线为阴线(开盘价 > 收盘价)。第二根 K 线亦为阴线,作为确认。第三根 K 线为阳线,且其开盘价低于第二根 K 线的收盘价。
  • 满足条件后确认看跌订单块,并创建对应对象。
  • 处理完毕后,删除 OB 对象释放内存。
  • 最后更新 prevDay,确保函数每日仅运行一次。
bool isNewBar() {
   // Memorize the time of opening of the last bar in the static variable
   static datetime last_time = 0;
   
   // Get current time
   datetime lastbar_time = (datetime)SeriesInfoInteger(Symbol(), Period(), SERIES_LASTBAR_DATE);

   // First call
   if (last_time == 0) {
      last_time = lastbar_time;
      return false;
   }

   // If the time differs (new bar)
   if (last_time != lastbar_time) {
      last_time = lastbar_time;
      return true;
   }

   // If no new bar, return false
   return false;
}

该函数用于检测图表上是否出现了新的 K 线,以便“每根 K 线仅执行一次”相关操作。

double getHigh(int index) {
    return iHigh(_Symbol, _Period, index);
}

double getLow(int index) {
    return iLow(_Symbol, _Period, index);
}

double getOpen(int index){
   return iOpen(_Symbol, _Period, index);
}

double getClose(int index){
   return iClose(_Symbol, _Period, index);
}

datetime getTime(int index) {
    return iTime(_Symbol, _Period, index);
}

本节代码提供了一组工具函数,用于根据给定 index 获取单根 K 线的具体价格数据与时间信息。每个函数均通过 MQL5 内置的 iHigh()、iLow()、iOpen()、iClose() 与 iTime() 等接口,访问指定品种和周期的对应值。

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

      if(CheckPointer(OB) != POINTER_INVALID && OB.direction < 0 && Bid > OB.low){
         double entry = Bid;
         double tp = getLow(iLowest(_Symbol, PERIOD_CURRENT, MODE_LOW, iBarShift(_Symbol, PERIOD_CURRENT, OB.time)));
         double sl = NormalizeDouble(OB.high + Mgtn, _Digits);
        // double sl = getHigh(iHighest(_Symbol, PERIOD_CURRENT, MODE_HIGH, iBarShift(_Symbol, PERIOD_CURRENT, OB.time)));
         
         ObjectCreate( 0, FIBO_OBJ, OBJ_FIBO, 0, fib_t2, fib_high, fib_t1, fib_low);
         double entLvl = fib_low + (fib_low - fib_high) * Fib_Trade_lvls / 100;
         
         if(OB.low >= entLvl){
            T2 = getTime(0);
            OB.draw(T1, T2, OBClr);
            trade.Sell(Lots, _Symbol, entry, sl, tp, "OB sell");
            delete OB;
         }else{
            delete OB;
         }         
      }
      
      ObjectSetInteger( 0, FIBO_OBJ, OBJPROP_COLOR, clrBlack);
      for(int i = 0; i < ObjectGetInteger( 0, FIBO_OBJ, OBJPROP_LEVELS); i++){
         ObjectSetInteger( 0, FIBO_OBJ, OBJPROP_LEVELCOLOR, i, clrBlack);
      }
    }   
}

由于 OnTick() 在每次新报价(tick)到来时都会被执行,我们通过 isNewBar() 函数来确认是否已生成新的 K 线。getOrderB() 函数负责识别潜在的看涨或看跌订单块(机构大单集中的区域)。getSwing() 函数则找出价格运行中的波段高低点,用于绘制斐波那契回撤位。

当订单块被确认且价格回撤到该区间内时,首先判断现价是否位于该订单块区域。若位于其中,再进一步校验价格是否恰好落在 61.8% 斐波那契回撤位附近。该水平在机构策略中常被视为强劲反转点。只有当两个条件同时满足,即:价格处于订单块内部;与 61.8% 斐波那契回撤位对齐; 才执行开仓(买入或卖出)。若任一条件不满足,则删除该订单块并放弃此次交易。

看涨订单块确认后:

确认的看涨订单块

看跌订单块确认后:

确认的看跌订单块

系统的核心逻辑建立在“订单块”与斐波那契回撤水平之间的依存关系之上。当检测到订单块时,系统会验证其是否与 61.8% 斐波那契回撤位对齐:对于看涨订单块,价格须跌破 61.8% 回撤位;对于看跌订单块,价格须高于 61.8% 回撤位。若订单块未达到上述斐波那契条件,则不会开仓。即便如此,斐波那契对象仍会被绘制在图表上,以便交易者可视化回撤位,并持续监控潜在机会,直至条件完全满足再行入场。


结论

总而言之,我们将订单块、波段高低点与斐波那契回撤等核心技术概念整合,实现了自动化的交易决策。通过自定义函数识别看涨与看跌订单块——这些区域通常对应机构大单介入的关键价位。借助斐波那契水平,EA 在开仓前进一步确认价格回撤是否落在高概率区域。OnTick() 函数持续监控市场,每当新 K 线形成即评估是否满足开仓条件,并依据实时价格行为自动设定入场、止损与止盈。

最终,这款EA帮助散户交易者与机构资金流保持一致,提供系统化的高概率进场方案。通过识别并响应订单块与价格回撤等关键市场结构,EA 让交易者能够“镜像”大型金融机构的战略动作。从而提升交易准确度、减少情绪干扰,并最终增强盈利能力。

回测结果

净值曲线

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

附加的文件 |
FIB_OB.mq5 (10.31 KB)
最近评论 | 前往讨论 (3)
Fr Ca
Fr Ca | 22 10月 2024 在 15:10

我认为你的压缩文件没有按预期运行,我没有看到图形,但看到了一些关于高低波动的调试信息。

的调试信息。

Hlomohang John Borotho
Hlomohang John Borotho | 24 10月 2024 在 12:19
Fr Ca #:

我认为你的压缩文件没有按预期运行,我没有看到任何图形,但看到了一些调试信息,这些信息涉及到一个

的调试信息。

如果是这样,您可以直接获取 .mq5 文件
MrPopular
MrPopular | 29 10月 2024 在 17:11
mgtn 代表什么?
交易中的神经网络:对比形态变换器(终章) 交易中的神经网络:对比形态变换器(终章)
在本系列的上一篇文章中,我们考察了“原子-基序对比变换器”(AMCT)框架,其用对比学习来发现各个级别的关键形态,从基本元素到复杂结构。在本文中,我们将继续利用 MQL5 实现 AMCT 方式。
名义变量的序数编码 名义变量的序数编码
在本文中,我们将讨论并演示如何使用Python和MQL5将名义预测变量转换为适合机器学习算法的数值格式。
基于主成分的特征选择与降维 基于主成分的特征选择与降维
本文深入探讨了改进型前向选择成分分析(Forward Selection Component Analysis,FSCA)算法的实现,该算法灵感源自Luca Puggini和Sean McLoone在《前向选择成分分析:算法与应用》一文中所提出的研究。
创建一个基于布林带PIRANHA策略的MQL5 EA 创建一个基于布林带PIRANHA策略的MQL5 EA
在本文中,我们将创建一个MQL5 EA,它基于PIRANHA策略,并使用布林带来提升交易表现。我们会系统梳理该策略的核心原理、代码实现细节,以及测试与优化方法。并助您轻松将 EA 部署到实际的交易环境中。