MT5指标编译通过 · EX5 17KB 41 13

订单块(供需区)自动识别

XAUOF 官方·1 天前发布

自动画出最近的看涨、看跌订单块矩形,价格回到区域内时弹窗提醒

订单块(供需区)自动识别

效果示意图 · AI 根据源码绘制

OrderBlock_Indicator.mq5330
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#property copyright "Order Block Indicator"
#property link      ""
#property version   "1.00"
#property indicator_chart_window
#property indicator_plots 0

// ========== 可调参数 ==========
input int    MaxBlocks = 5;              // 最多显示订单块数量(推荐3-5个)
input int    ExtendBars = 50;            // 矩形向右延伸K线数(推荐30-100)
input double BreakoutMultiplier = 1.5;   // 突破强度最小倍数(突破K线长度需≥订单块K线×此倍数,推荐1.5-3.0)
input bool   AlertOnReturn = true;       // 价格回到区域时是否弹窗提醒
input color  BullishColor = clrLimeGreen;// 看涨订单块颜色
input color  BearishColor = clrRed;      // 看跌订单块颜色
input int    Transparency = 60;          // 矩形透明度(0-100,越大越透明,推荐 50-70)

// ========== 订单块结构 ==========
struct OrderBlock {
   int type;              // 1=看涨,-1=看跌,0=无效
   double priceTop;       // 矩形上边界
   double priceBottom;    // 矩形下边界
   datetime timeStart;    // 起始时间
   int barIndex;          // 订单块K线索引
   bool alerted;          // 是否已提醒
   string objectName;     // 矩形对象名称
};

OrderBlock blocks[];      // 存储所有订单块
int blockCount = 0;       // 当前订单块数量

//+------------------------------------------------------------------+
//| 初始化函数                                                          |
//+------------------------------------------------------------------+
int OnInit() {
   // 设置定时器,每秒检查一次价格回归提醒
   if(AlertOnReturn) {
      EventSetTimer(1);
   }
   
   // 初始化订单块数组
   ArrayResize(blocks, MaxBlocks);
   for(int i = 0; i < MaxBlocks; i++) {
      blocks[i].type = 0;
      blocks[i].alerted = false;
      blocks[i].objectName = "";
   }
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| 反初始化函数                                                        |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // 删除所有订单块矩形
   for(int i = 0; i < MaxBlocks; i++) {
      if(blocks[i].objectName != "") {
         ObjectDelete(0, blocks[i].objectName);
      }
   }
   
   // 删除定时器
   EventKillTimer();
}

//+------------------------------------------------------------------+
//| 主计算函数                                                          |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[]) {
   
   // 设置数组为时间序列(索引0是最新K线)
   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   
   // 至少需要3根K线才能识别订单块
   if(rates_total < 3) return(0);
   
   // 从最近的K线开始扫描(跳过正在形成的K线0)
   int start = 1;
   if(prev_calculated > 0) {
      start = 1; // 每次只检查最近的几根K线
   }
   
   // 首次计算回看 300 根历史把已有的订单块找出来;之后每根新 K 线只查最近几根。
   // 从旧到新扫:AddOrderBlock 把新块插到最前、挤掉最旧的,这样留下的正好是最近的几个
   int lookback = (prev_calculated == 0) ? MathMin(300, rates_total - 1) : MathMin(10, rates_total - 1);
   for(int i = lookback - 1; i >= start; i--) {
      // 检查看涨订单块:i-1是下跌K线,i是强势上涨突破前高
      if(i >= 1 && i < rates_total - 1) {
         double prevOpen = open[i];
         double prevClose = close[i];
         double prevHigh = high[i];
         double prevLow = low[i];
         
         double currOpen = open[i-1];
         double currClose = close[i-1];
         double currHigh = high[i-1];
         double currLow = low[i-1];
         
         // 看涨订单块识别:prev是下跌K线,curr是上涨K线且突破前高
         if(prevClose < prevOpen) { // prev是下跌K线
            double prevBodySize = prevOpen - prevClose;
            double currBodySize = MathAbs(currClose - currOpen);
            
            // 检查curr是否强势突破(上涨且突破前高,且强度足够)
            if(currClose > currOpen && 
               currHigh > prevHigh &&
               prevBodySize > 0 &&
               currBodySize >= prevBodySize * BreakoutMultiplier) {
               
               // 检查是否已存在相同位置的订单块
               bool exists = false;
               for(int j = 0; j < MaxBlocks; j++) {
                  if(blocks[j].type == 1 && 
                     MathAbs(blocks[j].timeStart - time[i]) < PeriodSeconds(PERIOD_CURRENT) * 2) {
                     exists = true;
                     break;
                  }
               }
               
               if(!exists) {
                  // 创建新的看涨订单块(用 prev 整根 K 线的高低点做区间,比只用实体更贴近常见的订单块定义,也更醒目)
                  AddOrderBlock(1, prevHigh, prevLow, time[i], i);
               }
            }
         }
         
         // 看跌订单块识别:prev是上涨K线,curr是下跌K线且跌破前低
         if(prevClose > prevOpen) { // prev是上涨K线
            double prevBodySize = prevClose - prevOpen;
            double currBodySize = MathAbs(currClose - currOpen);
            
            // 检查curr是否强势跌破(下跌且跌破前低,且强度足够)
            if(currClose < currOpen && 
               currLow < prevLow &&
               prevBodySize > 0 &&
               currBodySize >= prevBodySize * BreakoutMultiplier) {
               
               // 检查是否已存在相同位置的订单块
               bool exists = false;
               for(int j = 0; j < MaxBlocks; j++) {
                  if(blocks[j].type == -1 && 
                     MathAbs(blocks[j].timeStart - time[i]) < PeriodSeconds(PERIOD_CURRENT) * 2) {
                     exists = true;
                     break;
                  }
               }
               
               if(!exists) {
                  // 创建新的看跌订单块(同样用整根 K 线的高低点)
                  AddOrderBlock(-1, prevHigh, prevLow, time[i], i);
               }
            }
         }
      }
   }
   
   // 更新所有订单块矩形位置和检查是否被突破
   UpdateOrderBlocks(time, close);
   
   return(rates_total);
}

//+------------------------------------------------------------------+
//| 添加新订单块                                                        |
//+------------------------------------------------------------------+
void AddOrderBlock(int type, double top, double bottom, datetime startTime, int barIdx) {
   // 确保top > bottom
   if(top < bottom) {
      double temp = top;
      top = bottom;
      bottom = temp;
   }
   
   // 查找空位或替换最老的订单块
   int idx = -1;
   for(int i = 0; i < MaxBlocks; i++) {
      if(blocks[i].type == 0) {
         idx = i;
         break;
      }
   }
   
   // 如果没有空位,删除最老的
   if(idx == -1) {
      idx = MaxBlocks - 1;
      if(blocks[idx].objectName != "") {
         ObjectDelete(0, blocks[idx].objectName);
      }
      
      // 向后移动数组
      for(int i = MaxBlocks - 1; i > 0; i--) {
         blocks[i] = blocks[i-1];
      }
      idx = 0;
   }
   
   // 创建新订单块
   blocks[idx].type = type;
   blocks[idx].priceTop = top;
   blocks[idx].priceBottom = bottom;
   blocks[idx].timeStart = startTime;
   blocks[idx].barIndex = barIdx;
   blocks[idx].alerted = false;
   blocks[idx].objectName = "OB_" + IntegerToString(type) + "_" + TimeToString(startTime, TIME_DATE|TIME_SECONDS);
   
   // 创建矩形对象
   CreateRectangle(blocks[idx]);
}

//+------------------------------------------------------------------+
//| 创建矩形对象                                                        |
//+------------------------------------------------------------------+
void CreateRectangle(OrderBlock &block) {
   datetime endTime = block.timeStart + PeriodSeconds(PERIOD_CURRENT) * ExtendBars;
   
   color rectColor = (block.type == 1) ? BullishColor : BearishColor;
   
   // 创建矩形
   ObjectCreate(0, block.objectName, OBJ_RECTANGLE, 0, block.timeStart, block.priceTop, endTime, block.priceBottom);
   ObjectSetInteger(0, block.objectName, OBJPROP_COLOR, rectColor);
   ObjectSetInteger(0, block.objectName, OBJPROP_STYLE, STYLE_SOLID);
   ObjectSetInteger(0, block.objectName, OBJPROP_WIDTH, 1);
   ObjectSetInteger(0, block.objectName, OBJPROP_FILL, true);
   ObjectSetInteger(0, block.objectName, OBJPROP_BACK, true);
   
   // 设置透明度(0-255,255是完全透明)
   int alpha = (int)((Transparency / 100.0) * 255);
   ObjectSetInteger(0, block.objectName, OBJPROP_BGCOLOR, ColorWithAlpha(rectColor, alpha));
   
   // 设置描述
   string desc = (block.type == 1) ? "看涨订单块" : "看跌订单块";
   ObjectSetString(0, block.objectName, OBJPROP_TOOLTIP, desc);
}

//+------------------------------------------------------------------+
//| 颜色透明度处理                                                      |
//+------------------------------------------------------------------+
color ColorWithAlpha(color baseColor, int alpha) {
   // MQL5的颜色格式:0xAARRGGBB
   int r = (baseColor & 0xFF);
   int g = ((baseColor >> 8) & 0xFF);
   int b = ((baseColor >> 16) & 0xFF);
   
   return (color)((alpha << 24) | (b << 16) | (g << 8) | r);
}

//+------------------------------------------------------------------+
//| 更新订单块并检查是否被突破                                            |
//+------------------------------------------------------------------+
void UpdateOrderBlocks(const datetime &time[], const double &close[]) {
   double currentPrice = close[0];
   
   for(int i = 0; i < MaxBlocks; i++) {
      if(blocks[i].type == 0) continue;
      
      // 更新矩形右边界时间
      datetime endTime = TimeCurrent() + PeriodSeconds(PERIOD_CURRENT) * ExtendBars;
      ObjectSetInteger(0, blocks[i].objectName, OBJPROP_TIME, 1, endTime);
      
      // 检查是否被完全突破
      bool breached = false;
      if(blocks[i].type == 1) {
         // 看涨订单块被向下突破
         if(currentPrice < blocks[i].priceBottom) {
            breached = true;
         }
      } else if(blocks[i].type == -1) {
         // 看跌订单块被向上突破
         if(currentPrice > blocks[i].priceTop) {
            breached = true;
         }
      }
      
      // 如果被突破,删除订单块
      if(breached) {
         ObjectDelete(0, blocks[i].objectName);
         blocks[i].type = 0;
         blocks[i].objectName = "";
      }
   }
}

//+------------------------------------------------------------------+
//| 定时器函数:检查价格是否回到订单块区域                                 |
//+------------------------------------------------------------------+
void OnTimer() {
   if(!AlertOnReturn) return;
   
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   
   for(int i = 0; i < MaxBlocks; i++) {
      if(blocks[i].type == 0) continue;
      
      // 检查价格是否在订单块范围内
      bool inZone = (currentPrice >= blocks[i].priceBottom && currentPrice <= blocks[i].priceTop);
      
      // 如果价格回到区域且未提醒过
      if(inZone && !blocks[i].alerted) {
         string alertMsg = "";
         if(blocks[i].type == 1) {
            alertMsg = "价格回到看涨订单块区域!\n价格: " + DoubleToString(currentPrice, _Digits) +
                      "\n区域: " + DoubleToString(blocks[i].priceBottom, _Digits) + " - " + DoubleToString(blocks[i].priceTop, _Digits);
         } else {
            alertMsg = "价格回到看跌订单块区域!\n价格: " + DoubleToString(currentPrice, _Digits) +
                      "\n区域: " + DoubleToString(blocks[i].priceBottom, _Digits) + " - " + DoubleToString(blocks[i].priceTop, _Digits);
         }
         
         Alert(alertMsg);
         blocks[i].alerted = true;
      }
      
      // 如果价格离开区域,重置提醒标志
      if(!inZone) {
         blocks[i].alerted = false;
      }
   }
}
//+------------------------------------------------------------------+
真实 MT5 运行截图服务器把编译好的 EX5 挂到黄金 H1 图表上截的,上面的封面就是照着它画的
订单块(供需区)自动识别 在 MT5 上的真实截图

作品说明

思路说明

订单块(Order Block)是 Smart Money 概念中的重要支撑/阻力区域。本指标通过识别「冲动突破前的最后一根反向K线」来标记订单块:

  • 看涨订单块:下跌K线被后续强势上涨突破 → 该下跌K线实体是潜在支撑
  • 看跌订单块:上涨K线被后续强势下跌突破 → 该上涨K线实体是潜在阻力

用半透明矩形标记这些区域并向右延伸,当价格回到区域时可选弹窗提醒,区域被完全突破后自动删除,始终保留最近的有效订单块。

使用说明

安装方法

  1. 将代码保存为 OrderBlock_Indicator.mq5,放入 MT5 的 MQL5/Indicators 文件夹
  2. 在 MetaEditor 中编译(F7),确保无错误
  3. 在图表上右键 → "技术指标列表" → "自定义" → 选择 OrderBlock_Indicator

参数调节

  • 最多显示订单块数量:建议 3-5 个,太多会让图表杂乱
  • 矩形向右延伸K线数:根据交易周期调整,H1 建议 50-100,M15 建议 30-50
  • 突破强度最小倍数:默认 1.5 表示突破K线实体长度需≥订单块K线的 1.5 倍,数值越大过滤越严格
  • 价格回到区域时弹窗提醒:开启后,当价格首次回到订单块区域时会弹窗提示(可能是潜在入场机会)
  • 颜色和透明度:可根据个人喜好调整,透明度建议 70-90 之间

信号解读

  • 绿色矩形(看涨订单块):机构可能在此区域布局多单,价格回到该区域可能获得支撑
  • 红色矩形(看跌订单块):机构可能在此区域布局空单,价格回到该区域可能遇到阻力
  • 订单块被完全突破后会自动删除,说明该区域已失效
  • 最佳入场时机:价格首次回到订单块区域时(配合其他信号确认)

注意事项

  • 订单块不是绝对支撑/阻力,需结合趋势、成交量等多维度分析
  • 在震荡市中订单块可能频繁失效,建议在趋势明确时使用
  • 弹窗提醒只在价格首次进入区域时触发,避免频繁打扰
  • 较小周期(如 M1/M5)订单块失效快,建议用 M15 及以上周期

AI 解读根据源码逐行分析写成,供参考,以实际运行为准

这是什么

这是一个基于 Smart Money 概念的订单块(Order Block)识别指标,用于在图表上自动标记机构可能布局的关键支撑和阻力区域。看涨订单块(绿色矩形)代表潜在支撑,看跌订单块(红色矩形)代表潜在阻力。当价格回到这些区域时,指标会弹窗提醒你可能出现的交易机会。

逻辑原理

订单块的识别逻辑是:找到「被强势突破前的最后一根反向 K 线」。

看涨订单块:前一根是下跌 K 线(收盘低于开盘),当前 K 线是上涨 K 线且最高价突破了前一根的高点,并且当前 K 线实体长度 ≥ 前一根实体长度 × 突破强度倍数(默认 1.5 倍)。此时前一根 K 线的高低点区间被标记为看涨订单块,用整根 K 线(含上下影线)的高低点画矩形。

看跌订单块:前一根是上涨 K 线(收盘高于开盘),当前 K 线是下跌 K 线且最低价跌破了前一根的低点,并且当前 K 线实体长度 ≥ 前一根实体长度 × 突破强度倍数。前一根 K 线的高低点区间被标记为看跌订单块。

矩形向右延伸指定数量的 K 线。当价格完全突破订单块(看涨区间被向下突破或看跌区间被向上突破)时,该矩形自动删除,表示区域已失效。指标始终保留最近的有效订单块(最多保留参数设定的数量)。

定时器每秒检查一次,如果价格回到某个订单块区域内且之前未提醒过,会弹出提示窗口告诉你潜在的入场机会。

参数说明

  • MaxBlocks(默认 5):最多在图表上显示几个订单块。数值越大图表越杂乱,建议 3-5 个。
  • ExtendBars(默认 50):矩形向右延伸多少根 K 线。H1 周期建议 50-100,M15 建议 30-50,根据你的交易周期调整。
  • BreakoutMultiplier(默认 1.5):突破强度的最小倍数。表示突破 K 线的实体长度必须 ≥ 订单块 K 线实体长度的多少倍才算有效突破。数值越大过滤越严格,假信号越少但订单块也会更少。建议 1.5-3.0。
  • AlertOnReturn(默认 true):价格回到订单块区域时是否弹窗提醒。开启后可能打断你的操作,关闭后需要自己盯盘。
  • BullishColor(默认亮绿色):看涨订单块(支撑区)的颜色。
  • BearishColor(默认红色):看跌订单块(阻力区)的颜色。
  • Transparency(默认 60):矩形的透明度,0-100 范围,数值越大越透明。建议 50-70,既能看清区域又不遮挡 K 线。

怎么用

把代码保存为 OrderBlock_Indicator.mq5,放到 MT5 的 MQL5/Indicators 文件夹,在 MetaEditor 里按 F7 编译,确保无错误。然后在图表上右键 → 技术指标列表 → 自定义 → 选择 OrderBlock_Indicator,双击或拖到图表上。

绿色矩形是看涨订单块,价格回到这里可能获得支撑;红色矩形是看跌订单块,价格回到这里可能遇到阻力。矩形被完全突破后会自动消失。如果开启了弹窗提醒,价格首次进入某个区域时会跳出提示框,告诉你可能的入场时机。最佳入场点通常是价格首次回测订单块区域时,结合其他确认信号(比如趋势、K 线形态)再动手。

适用场景

适合有明确趋势的行情,比如 XAUUSD、EURUSD、指数等流动性好的品种。建议用在 M15 及以上周期,H1 和 H4 效果较好。订单块在趋势行情中作为回调入场的参考位置比较有用,上涨趋势中找看涨订单块做多,下跌趋势中找看跌订单块做空。

不适合震荡市和超短周期。在震荡行情里订单块可能频繁失效,假突破多,信号质量差。M1 和 M5 周期的订单块生命周期太短,噪音大,不建议使用。

风险提示

订单块不是绝对的支撑或阻力,它只是一个参考区域。价格可能在区域内反转,也可能直接穿透。不要单独依赖订单块做交易决策,需要结合趋势、成交量、K 线形态等多维度确认。

突破强度的判断是基于 K 线实体长度,不考虑成交量和市场环境,可能出现假突破导致的假订单块。BreakoutMultiplier 参数可以过滤一部分,但无法完全避免。

弹窗提醒只在价格首次进入区域时触发,如果你错过了提醒或者价格在区域内反复震荡,之后不会再提醒(除非价格离开区域后再次回来)。

订单块被突破后会自动删除,但删除的判断是「价格完全突破区域边界」,如果价格只是稍微刺破边界又回来,可能会误删有效的订单块。这是所有订单块指标的通病。

代码里用的是 Bid 价格做判断,点差大的品种可能会有偏差。首次加载时会回看 300 根历史 K 线寻找订单块,如果历史数据不足或者行情波动剧烈,可能找不到足够的订单块。

常见问题

这个指标会重绘吗
不会重绘。订单块一旦生成,矩形的价格区间不会改变,只有被完全突破时才会删除。
可以用在 XAUUSD 以外的品种吗
可以。代码没有品种限制,适合所有流动性好的品种,比如外汇、贵金属、指数。低流动性品种可能假突破多,需自行测试。
MT4 能用吗
不能。这是 MQL5 代码,只能在 MetaTrader 5 平台使用。MT4 需要用 MQL4 重写。
参数默认值适合什么周期
默认参数(ExtendBars=50, BreakoutMultiplier=1.5)比较适合 H1 周期。M15 可以把 ExtendBars 降到 30-40,H4 可以提高到 80-100。BreakoutMultiplier 在震荡市可以提高到 2.0-3.0 过滤假信号。
订单块为什么会突然消失
订单块被完全突破后会自动删除。看涨订单块被价格向下突破(收盘价低于区域下边界),看跌订单块被价格向上突破(收盘价高于区域上边界),说明该区域已经失效。

Order Block Indicator for MT5

Auto-detects bullish and bearish order blocks based on Smart Money Concept. Draws semi-transparent rectangles for supply/demand zones, alerts when price returns, and removes invalidated blocks automatically.

Source code is shown above. Compiled EX5 files are available to VIP members.

评论

后可以留言,比如反馈用着怎么样、想加什么功能。

还没有人评论,来抢个沙发。