MT5指标编译通过 · EX5 16KB 自动发布 4 0

订单块自动识别与回测提醒指标

lm152632·2 小时前发布

自动标记看涨看跌订单块矩形区域,价格回测时弹窗提醒

订单块自动识别与回测提醒指标
OrderBlock_Alert.mq5407
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
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
#property copyright   "XAUOF指标工坊"
#property version     "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

//+------------------------------------------------------------------+
//| 输入参数
//+------------------------------------------------------------------+
input int    MaxOrderBlocks = 5;            // 最多显示订单块数量(1-20)
input int    BreakoutCandles = 3;           // 突破确认K线数(判断趋势强度)
input double BreakoutRatio = 1.5;           // 突破K线实体倍数(相对前一根)
input color  BullishColor = clrDodgerBlue;  // 看涨订单块颜色
input color  BearishColor = clrCrimson;     // 看跌订单块颜色
input int    RectTransparency = 85;         // 矩形透明度(0-100,越大越透明)
input bool   EnableAlert = true;            // 启用回测提醒
input bool   ShowOnlyUntested = true;       // 仅显示未回测区域

//+------------------------------------------------------------------+
//| 订单块结构体
//+------------------------------------------------------------------+
struct OrderBlock
{
   datetime time;        // 订单块形成时间
   double   high;        // 区域上沿
   double   low;         // 区域下沿
   bool     is_bullish;  // true=看涨, false=看跌
   bool     is_tested;   // 是否已回测
   string   rect_name;   // 矩形对象名称
};

OrderBlock g_blocks[];          // 订单块数组
int        g_block_count = 0;   // 当前订单块数量
datetime   g_last_alert_time;   // 上次提醒时间(避免重复)

//+------------------------------------------------------------------+
//| 初始化函数
//+------------------------------------------------------------------+
int OnInit()
{
   // 参数校验
   if(MaxOrderBlocks < 1 || MaxOrderBlocks > 20)
   {
      Print("错误:订单块数量必须在1-20之间");
      return INIT_PARAMETERS_INCORRECT;
   }
   
   ArrayResize(g_blocks, MaxOrderBlocks);
   g_block_count = 0;
   g_last_alert_time = 0;
   
   // 清理旧对象
   ObjectsDeleteAll(0, "OB_");
   
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| 反初始化函数
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   // 清理所有订单块矩形
   ObjectsDeleteAll(0, "OB_");
}

//+------------------------------------------------------------------+
//| 指标计算函数
//+------------------------------------------------------------------+
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为最新)
   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   
   // 至少需要 BreakoutCandles+2 根K线才能识别
   if(rates_total < BreakoutCandles + 2)
      return 0;
   
   // 每次重新扫描最近的订单块(从索引3开始,避免未完成K线)
   ScanForOrderBlocks(time, open, high, low, close, rates_total);
   
   // 检查价格是否回测订单块
   if(EnableAlert)
      CheckRetestAlert(close[0]);
   
   // 更新矩形显示
   UpdateRectangles();
   
   return rates_total;
}

//+------------------------------------------------------------------+
//| 扫描订单块
//+------------------------------------------------------------------+
void ScanForOrderBlocks(const datetime &time[],
                        const double &open[],
                        const double &high[],
                        const double &low[],
                        const double &close[],
                        int rates_total)
{
   // 临时存储新发现的订单块
   OrderBlock temp_blocks[];
   ArrayResize(temp_blocks, 0);
   
   // 从第 BreakoutCandles+1 根开始扫描(跳过最新3根避免未完成)
   for(int i = BreakoutCandles + 1; i < MathMin(rates_total - 10, 500); i++)
   {
      // 检查看涨订单块:大阳线突破前面的下跌
      if(IsBullishOrderBlock(i, open, high, low, close))
      {
         OrderBlock ob;
         ob.time = time[i];
         ob.high = high[i];
         ob.low = low[i];
         ob.is_bullish = true;
         ob.is_tested = IsPriceRetested(i, close, high[i], low[i], true);
         ob.rect_name = "OB_Bull_" + TimeToString(time[i]);
         
         // 检查是否已存在
         if(!IsBlockExists(ob))
         {
            int size = ArraySize(temp_blocks);
            ArrayResize(temp_blocks, size + 1);
            temp_blocks[size] = ob;
         }
      }
      
      // 检查看跌订单块:大阴线突破前面的上涨
      if(IsBearishOrderBlock(i, open, high, low, close))
      {
         OrderBlock ob;
         ob.time = time[i];
         ob.high = high[i];
         ob.low = low[i];
         ob.is_bullish = false;
         ob.is_tested = IsPriceRetested(i, close, high[i], low[i], false);
         ob.rect_name = "OB_Bear_" + TimeToString(time[i]);
         
         if(!IsBlockExists(ob))
         {
            int size = ArraySize(temp_blocks);
            ArrayResize(temp_blocks, size + 1);
            temp_blocks[size] = ob;
         }
      }
   }
   
   // 按时间排序,保留最新的 MaxOrderBlocks 个
   SortAndLimitBlocks(temp_blocks);
}

//+------------------------------------------------------------------+
//| 判断看涨订单块
//+------------------------------------------------------------------+
bool IsBullishOrderBlock(int i,
                         const double &open[],
                         const double &high[],
                         const double &low[],
                         const double &close[])
{
   // 第 i 根是阴线(订单块候选)
   if(close[i] >= open[i])
      return false;
   
   double ob_body = open[i] - close[i];
   if(ob_body <= 0)
      return false;
   
   // 后续第 i-1 根是大阳线(突破)
   if(close[i-1] <= open[i-1])
      return false;
   
   double breakout_body = close[i-1] - open[i-1];
   
   // 突破K线实体必须大于订单块实体的 BreakoutRatio 倍
   if(breakout_body < ob_body * BreakoutRatio)
      return false;
   
   // 突破K线的收盘价必须高于订单块的高点
   if(close[i-1] <= high[i])
      return false;
   
   // 检查之前 BreakoutCandles 根是否在下跌
   for(int j = 1; j <= BreakoutCandles; j++)
   {
      if(i + j >= ArraySize(close))
         return false;
      if(close[i+j] < close[i+j-1])  // 至少有一根在下跌
         return true;
   }
   
   return false;
}

//+------------------------------------------------------------------+
//| 判断看跌订单块
//+------------------------------------------------------------------+
bool IsBearishOrderBlock(int i,
                         const double &open[],
                         const double &high[],
                         const double &low[],
                         const double &close[])
{
   // 第 i 根是阳线(订单块候选)
   if(close[i] <= open[i])
      return false;
   
   double ob_body = close[i] - open[i];
   if(ob_body <= 0)
      return false;
   
   // 后续第 i-1 根是大阴线(突破)
   if(close[i-1] >= open[i-1])
      return false;
   
   double breakout_body = open[i-1] - close[i-1];
   
   // 突破K线实体必须大于订单块实体的 BreakoutRatio 倍
   if(breakout_body < ob_body * BreakoutRatio)
      return false;
   
   // 突破K线的收盘价必须低于订单块的低点
   if(close[i-1] >= low[i])
      return false;
   
   // 检查之前 BreakoutCandles 根是否在上涨
   for(int j = 1; j <= BreakoutCandles; j++)
   {
      if(i + j >= ArraySize(close))
         return false;
      if(close[i+j] > close[i+j-1])  // 至少有一根在上涨
         return true;
   }
   
   return false;
}

//+------------------------------------------------------------------+
//| 检查价格是否回测过区域
//+------------------------------------------------------------------+
bool IsPriceRetested(int block_index,
                     const double &close[],
                     double block_high,
                     double block_low,
                     bool is_bullish)
{
   // 检查订单块形成后的K线是否触碰区域
   for(int i = 0; i < block_index; i++)
   {
      if(close[i] >= block_low && close[i] <= block_high)
         return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 检查订单块是否已存在
//+------------------------------------------------------------------+
bool IsBlockExists(const OrderBlock &new_block)
{
   for(int i = 0; i < g_block_count; i++)
   {
      if(g_blocks[i].time == new_block.time)
         return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| 排序并限制订单块数量
//+------------------------------------------------------------------+
void SortAndLimitBlocks(OrderBlock &temp_blocks[])
{
   int temp_count = ArraySize(temp_blocks);
   if(temp_count == 0)
      return;
   
   // 简单冒泡排序(按时间倒序)
   for(int i = 0; i < temp_count - 1; i++)
   {
      for(int j = i + 1; j < temp_count; j++)
      {
         if(temp_blocks[i].time < temp_blocks[j].time)
         {
            OrderBlock temp = temp_blocks[i];
            temp_blocks[i] = temp_blocks[j];
            temp_blocks[j] = temp;
         }
      }
   }
   
   // 保留最新的 MaxOrderBlocks 个
   g_block_count = MathMin(temp_count, MaxOrderBlocks);
   for(int i = 0; i < g_block_count; i++)
   {
      g_blocks[i] = temp_blocks[i];
   }
}

//+------------------------------------------------------------------+
//| 检查回测提醒
//+------------------------------------------------------------------+
void CheckRetestAlert(double current_price)
{
   datetime current_time = TimeCurrent();
   
   // 避免1分钟内重复提醒
   if(current_time - g_last_alert_time < 60)
      return;
   
   for(int i = 0; i < g_block_count; i++)
   {
      // 如果已回测过且设置了仅显示未回测,跳过
      if(g_blocks[i].is_tested && ShowOnlyUntested)
         continue;
      
      // 检查当前价格是否在区域内
      if(current_price >= g_blocks[i].low && current_price <= g_blocks[i].high)
      {
         if(!g_blocks[i].is_tested)  // 首次回测
         {
            string msg = StringFormat("价格回测%s订单块!\n时间:%s\n区域:%.5f - %.5f",
                                    g_blocks[i].is_bullish ? "看涨" : "看跌",
                                    TimeToString(g_blocks[i].time),
                                    g_blocks[i].low,
                                    g_blocks[i].high);
            Alert(msg);
            g_blocks[i].is_tested = true;
            g_last_alert_time = current_time;
            break;
         }
      }
   }
}

//+------------------------------------------------------------------+
//| 更新矩形显示
//+------------------------------------------------------------------+
void UpdateRectangles()
{
   // 先清理所有旧矩形
   ObjectsDeleteAll(0, "OB_");
   
   for(int i = 0; i < g_block_count; i++)
   {
      // 如果设置了仅显示未回测,且已回测,跳过
      if(ShowOnlyUntested && g_blocks[i].is_tested)
         continue;
      
      // 创建矩形
      string name = g_blocks[i].rect_name;
      datetime time_right = TimeCurrent() + PeriodSeconds(PERIOD_CURRENT) * 50;  // 延伸到右侧
      
      ObjectCreate(0, name, OBJ_RECTANGLE, 0,
                   g_blocks[i].time, g_blocks[i].high,
                   time_right, g_blocks[i].low);
      
      // 设置颜色和透明度
      color rect_color = g_blocks[i].is_bullish ? BullishColor : BearishColor;
      ObjectSetInteger(0, name, OBJPROP_COLOR, rect_color);
      ObjectSetInteger(0, name, OBJPROP_FILL, true);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
      ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
      
      // 设置透明度(0-255,255为完全透明)
      int transparency = (int)MathRound(RectTransparency * 2.55);
      ObjectSetInteger(0, name, OBJPROP_BGCOLOR, ColorToARGB(rect_color, transparency));
      
      // 添加文字标签
      string label_name = name + "_Label";
      ObjectCreate(0, label_name, OBJ_TEXT, 0, g_blocks[i].time, g_blocks[i].high);
      ObjectSetString(0, label_name, OBJPROP_TEXT, g_blocks[i].is_bullish ? "OB↑" : "OB↓");
      ObjectSetInteger(0, label_name, OBJPROP_COLOR, rect_color);
      ObjectSetInteger(0, label_name, OBJPROP_FONTSIZE, 8);
      ObjectSetInteger(0, label_name, OBJPROP_BACK, false);
   }
   
   ChartRedraw();
}

//+------------------------------------------------------------------+
//| 颜色转ARGB(带透明度)
//+------------------------------------------------------------------+
uint ColorToARGB(color clr, int alpha)
{
   uint r = (clr & 0xFF);
   uint g = ((clr >> 8) & 0xFF);
   uint b = ((clr >> 16) & 0xFF);
   return (alpha << 24) | (r << 16) | (g << 8) | b;
}
//+------------------------------------------------------------------+
真实 MT5 运行截图服务器把编译好的 EX5 挂到黄金 H1 图表上截的,上面的封面就是照着它画的
订单块自动识别与回测提醒指标 在 MT5 上的真实截图

作品说明

思路说明

订单块(Order Block)是指价格剧烈波动前的最后一根反向K线,代表机构建仓区域。识别逻辑:

  • 看涨OB:下跌后出现大阳线突破,前一根阴线为看涨订单块
  • 看跌OB:上涨后出现大阴线突破,前一根阳线为看跌订单块
  • 用矩形标记区域(高低点),价格回测时弹窗提醒
  • 可设置最多保留几个有效区域,自动清理旧区域

使用说明

安装步骤:

  1. 复制代码到 MetaEditor,保存为 OrderBlock_Alert.mq5
  2. 编译无误后,拖到任意图表即可使用

参数设置:

  • 最多显示订单块数量:建议 3-5 个,太多会混乱
  • 突破确认K线数:默认 3 根,判断趋势强度
  • 突破K线实体倍数:默认 1.5 倍,越大过滤越严格
  • 矩形透明度:0-100,推荐 80-90,既能看清又不遮挡价格
  • 仅显示未回测区域:打钩后,已触及的订单块会自动隐藏

信号解读:

  • 蓝色矩形(OB↑):看涨订单块,价格回测可考虑做多
  • 红色矩形(OB↓):看跌订单块,价格回测可考虑做空
  • 弹窗提醒时说明价格正在回测,结合其他指标确认入场

注意事项:

  1. 订单块不是百分百有效,需结合趋势和成交量判断
  2. 回测时不一定立即反弹,可能穿透或反复测试
  3. 建议在 H1 及以上周期使用,M5/M15 容易产生假信号
  4. 弹窗提醒有 1 分钟冷却,避免频繁弹窗干扰交易

评论

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

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