
如何将“聪明钱”概念(OB)与斐波那契指标相结合,实现最优进场策略
引言
“聪明钱”的概念(SMC)与订单块是图表上机构交易者通常执行大额买卖指令的核心区域。这些区域往往标志着重要价格波动的起点,对于希望与机构资金行为保持一致的交易者至关重要。理解这些关键价位如何影响价格走势,可为散户提供更深入的市场洞察,帮助他们预判高概率行情。
若将订单块与斐波那契回撤工具结合,交易者可进一步优化进场策略。斐波那契回撤可测量近期波段高点与低点之间的潜在回调位,判断价格在延续趋势前可能回撤的幅度。通过把机构订单流与市场关注区域对齐,该方法帮助交易者精确定位最佳进场点,提高交易准确度。
EA的逻辑
看涨订单块:
在图表上,当一根看跌K线之后出现一根看涨K线,且该看涨K线完全吞没前一根看跌K线,即构成看涨订单块,标志着强劲看涨动能的起点。要确认为有效看涨订单块,看跌K线之后必须至少出现两根或更多连续看涨K线。正确交易看涨订单块的策略是:等待价格回撤并重新进入已识别的看涨订单块区域,再执行买入。
看涨斐波那契回撤:
在确认看涨订单块后,使用斐波那契回撤工具寻找最佳做多入场点。先定位该订单块对应的近期波段高点与波段低点。然后从“低点→高点”绘制斐波那契回撤。若订单块位于或低于 61.8% 回撤位,即与机构买盘区域重合,视为高概率买入机会。
看跌订单块:
当一根看涨 K 线被随后一根看跌 K 线完全吞没,标志强烈下行动量开始。且后续至少出现两根(或一连串)看跌 K 线,即构成看跌订单块。交易时应等待价格回测该看跌订单块区域,再执行卖出。
看跌斐波那契回撤:
对于看跌订单块,同样用斐波那契回撤寻找最佳做空入场点。在定位看跌订单块后,先找出对应波段高点与低点。在此情况下,由于目标为卖出订单,斐波那契回撤应从波段高点画至波段低点。若订单块位于或高于 61.8% 回撤位,即为高概率做空入场区。
让我们开始:
//+------------------------------------------------------------------+ //| 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



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