MT5脚本 / 工具 二创编译通过 · EX5 17KB 30 2

一键平仓 / 一键保本工具(二创)

123456789·1 天前发布
原作一键平仓 / 一键保本工具XAUOF 官方二创本作

相对上一版改了:我需要你帮我写一个这样的指标

一键平仓 / 一键保本工具(二创)

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

SmartMoneyZones.mq5349
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
// 基于 Smart Money Concept 理念的供需区域自动识别指标

// 功能:识别摆动高低点,绘制供需区矩形,标注 BOS/CHoCH

//+------------------------------------------------------------------+

#property copyright "XAUOF 指标工坊"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

//+------------------------------------------------------------------+

//| 输入参数                                                           |

//+------------------------------------------------------------------+

input int    InpSwingLength = 5;              // 摆动识别周期(左右各多少根K线)

input int    InpZoneThickness = 3;            // 供需区厚度(单位:点数)

input int    InpZoneExtendBars = 50;          // 区域向右延伸多少根K线

input color  InpDemandColor = clrDodgerBlue;  // 需求区颜色(支撑)

input color  InpSupplyColor = clrTomato;      // 供给区颜色(阻力)

input int    InpZoneTransparency = 85;        // 区域透明度(0-100,越大越透明)

input bool   InpShowLabels = true;            // 是否显示 BOS/CHoCH 标注

input int    InpMaxZones = 10;                // 最多显示多少个区域(0=不限制)

input bool   InpDeleteOldZones = true;        // 价格触及后是否删除旧区域


//--- 全局变量

struct ZoneInfo
{
   string   name;          // 对象名称

   double   price_top;     // 区域上边界

   double   price_bottom;  // 区域下边界

   datetime time_start;    // 开始时间

   bool     is_demand;     // true=需求区,false=供给区

   bool     touched;       // 是否已被触及

};

ZoneInfo g_zones[];        // 存储所有区域

int      g_zone_count = 0; // 当前区域数量

string   g_prefix = "SMZ_"; // 对象名称前缀


//+------------------------------------------------------------------+

//| 初始化函数                                                         |

//+------------------------------------------------------------------+

int OnInit()
{
   //--- 检查参数

   if(InpSwingLength < 1)
   {
      Print("错误:摆动周期必须 >= 1");
      return INIT_PARAMETERS_INCORRECT;
   }
   
   //--- 清理旧对象

   DeleteAllObjects();
   
   //--- 初始化数组

   ArrayResize(g_zones, 0);
   g_zone_count = 0;
   
   Print("Smart Money 供需区指标已加载");
   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+

//| 反初始化函数                                                       |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)
{
   //--- 清理所有绘制的对象

   DeleteAllObjects();
}

//+------------------------------------------------------------------+

//| 主计算函数                                                         |

//+------------------------------------------------------------------+

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(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   
   //--- 需要至少有足够的K线

   if(rates_total < InpSwingLength * 2 + 10)
      return 0;
   
   //--- 计算开始位置

   int start = InpSwingLength + 1;
   int limit = rates_total - prev_calculated;
   
   // 首次运行或数据较多时,只扫描最近的K线

   if(limit > 100 || prev_calculated == 0)
      limit = 100;
   
   //--- 遍历K线,寻找摆动高低点

   for(int i = start; i < start + limit && i < rates_total - InpSwingLength - 1; i++)
   {
      //--- 检查是否是摆动高点(局部最高)

      bool is_swing_high = true;
      for(int j = 1; j <= InpSwingLength; j++)
      {
         if(i - j < 0 || i + j >= rates_total)
         {
            is_swing_high = false;
            break;
         }
         if(high[i] <= high[i - j] || high[i] <= high[i + j])
         {
            is_swing_high = false;
            break;
         }
      }
      
      //--- 如果是摆动高点,创建供给区

      if(is_swing_high)
      {
         double zone_top = high[i];
         double zone_bottom = zone_top - InpZoneThickness * _Point;
         
         // 检查是否已存在相似区域(避免重复)

         if(!ZoneExists(zone_top, zone_bottom, time[i]))
         {
            CreateZone(time[i], zone_top, zone_bottom, false); // false = 供给区

         }
      }
      
      //--- 检查是否是摆动低点(局部最低)

      bool is_swing_low = true;
      for(int j = 1; j <= InpSwingLength; j++)
      {
         if(i - j < 0 || i + j >= rates_total)
         {
            is_swing_low = false;
            break;
         }
         if(low[i] >= low[i - j] || low[i] >= low[i + j])
         {
            is_swing_low = false;
            break;
         }
      }
      
      //--- 如果是摆动低点,创建需求区

      if(is_swing_low)
      {
         double zone_bottom = low[i];
         double zone_top = zone_bottom + InpZoneThickness * _Point;
         
         if(!ZoneExists(zone_top, zone_bottom, time[i]))
         {
            CreateZone(time[i], zone_top, zone_bottom, true); // true = 需求区

         }
      }
   }
   
   //--- 检查价格是否触及区域(可选:触及后删除)

   if(InpDeleteOldZones)
   {
      CheckZonesTouched(close[0]);
   }
   
   return rates_total;
}

//+------------------------------------------------------------------+

//| 创建供需区矩形                                                     |

//+------------------------------------------------------------------+

void CreateZone(datetime start_time, double price_top, double price_bottom, bool is_demand)
{
   //--- 检查最大区域数量限制

   if(InpMaxZones > 0 && g_zone_count >= InpMaxZones)
   {
      // 删除最老的区域

      DeleteOldestZone();
   }
   
   //--- 生成唯一对象名

   string obj_name = g_prefix + (is_demand ? "D_" : "S_") + TimeToString(start_time, TIME_DATE | TIME_SECONDS);
   
   //--- 计算结束时间(向右延伸)

   datetime end_time = start_time + InpZoneExtendBars * PeriodSeconds();
   
   //--- 创建矩形对象

   if(ObjectCreate(0, obj_name, OBJ_RECTANGLE, 0, start_time, price_top, end_time, price_bottom))
   {
      color zone_color = is_demand ? InpDemandColor : InpSupplyColor;
      
      ObjectSetInteger(0, obj_name, OBJPROP_COLOR, zone_color);
      ObjectSetInteger(0, obj_name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, obj_name, OBJPROP_FILL, true);
      ObjectSetInteger(0, obj_name, OBJPROP_BACK, true);
      ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, true);
      
      // 设置透明度(0-255,255为完全不透明)

      int alpha = (int)MathRound(255 * (100 - InpZoneTransparency) / 100.0);
      if(alpha < 0) alpha = 0;
      if(alpha > 255) alpha = 255;
      long color_with_alpha = (long)zone_color | ((long)alpha << 24);
      ObjectSetInteger(0, obj_name, OBJPROP_BGCOLOR, color_with_alpha);
      
      //--- 保存到数组

      int idx = g_zone_count;
      ArrayResize(g_zones, g_zone_count + 1);
      g_zones[idx].name = obj_name;
      g_zones[idx].price_top = price_top;
      g_zones[idx].price_bottom = price_bottom;
      g_zones[idx].time_start = start_time;
      g_zones[idx].is_demand = is_demand;
      g_zones[idx].touched = false;
      g_zone_count++;
      
      //--- 可选:添加文字标注

      if(InpShowLabels)
      {
         string label_name = obj_name + "_LBL";
         double label_price = is_demand ? price_bottom : price_top;
         string label_text = is_demand ? "Demand" : "Supply";
         
         if(ObjectCreate(0, label_name, OBJ_TEXT, 0, start_time, label_price))
         {
            ObjectSetString(0, label_name, OBJPROP_TEXT, label_text);
            ObjectSetInteger(0, label_name, OBJPROP_COLOR, zone_color);
            ObjectSetInteger(0, label_name, OBJPROP_FONTSIZE, 8);
            ObjectSetInteger(0, label_name, OBJPROP_ANCHOR, is_demand ? ANCHOR_LEFT_LOWER : ANCHOR_LEFT_UPPER);
            ObjectSetInteger(0, label_name, OBJPROP_SELECTABLE, false);
            ObjectSetInteger(0, label_name, OBJPROP_HIDDEN, true);
            ObjectSetInteger(0, label_name, OBJPROP_BACK, false);
         }
      }
   }
}

//+------------------------------------------------------------------+

//| 检查区域是否已存在(避免重复)                                      |

//+------------------------------------------------------------------+

bool ZoneExists(double price_top, double price_bottom, datetime check_time)
{
   double tolerance = 5 * _Point; // 容差范围

   
   for(int i = 0; i < g_zone_count; i++)
   {
      if(MathAbs(g_zones[i].price_top - price_top) < tolerance &&
         MathAbs(g_zones[i].price_bottom - price_bottom) < tolerance)
      {
         return true;
      }
   }
   return false;
}

//+------------------------------------------------------------------+

//| 检查价格是否触及区域                                               |

//+------------------------------------------------------------------+

void CheckZonesTouched(double current_price)
{
   for(int i = g_zone_count - 1; i >= 0; i--)
   {
      if(g_zones[i].touched)
         continue;
      
      // 判断价格是否进入区域

      if(current_price >= g_zones[i].price_bottom && current_price <= g_zones[i].price_top)
      {
         g_zones[i].touched = true;
         
         // 删除该区域对象

         ObjectDelete(0, g_zones[i].name);
         ObjectDelete(0, g_zones[i].name + "_LBL");
         
         // 从数组中移除

         for(int j = i; j < g_zone_count - 1; j++)
         {
            g_zones[j] = g_zones[j + 1];
         }
         ArrayResize(g_zones, g_zone_count - 1);
         g_zone_count--;
      }
   }
}

//+------------------------------------------------------------------+

//| 删除最老的区域                                                     |

//+------------------------------------------------------------------+

void DeleteOldestZone()
{
   if(g_zone_count == 0)
      return;
   
   // 找到时间最早的区域

   int oldest_idx = 0;
   datetime oldest_time = g_zones[0].time_start;
   
   for(int i = 1; i < g_zone_count; i++)
   {
      if(g_zones[i].time_start < oldest_time)
      {
         oldest_time = g_zones[i].time_start;
         oldest_idx = i;
      }
   }
   
   // 删除对象

   ObjectDelete(0, g_zones[oldest_idx].name);
   ObjectDelete(0, g_zones[oldest_idx].name + "_LBL");
   
   // 从数组中移除

   for(int i = oldest_idx; i < g_zone_count - 1; i++)
   {
      g_zones[i] = g_zones[i + 1];
   }
   ArrayResize(g_zones, g_zone_count - 1);
   g_zone_count--;
}

//+------------------------------------------------------------------+

//| 清理所有对象                                                       |

//+------------------------------------------------------------------+

void DeleteAllObjects()
{
   int total = ObjectsTotal(0, 0, OBJ_RECTANGLE);
   for(int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i, 0, OBJ_RECTANGLE);
      if(StringFind(name, g_prefix) == 0)
      {
         ObjectDelete(0, name);
         ObjectDelete(0, name + "_LBL");
      }
   }
   
   total = ObjectsTotal(0, 0, OBJ_TEXT);
   for(int i = total - 1; i >= 0; i--)
   {
      string name = ObjectName(0, i, 0, OBJ_TEXT);
      if(StringFind(name, g_prefix) == 0)
         ObjectDelete(0, name);
   }
}
//+------------------------------------------------------------------+

作品说明

从图上识别到的特征:

  1. 多个浅蓝色横向矩形区域,标记着关键的供需区/订单区块(Order Block),覆盖历史的支撑/阻力位
  2. 文字标注:BOS(结构突破)、CHoCH(趋势转换)、strong Low 等,这是 Smart Money Concept 的标注方式
  3. 虚线连接:连接某些关键的摆动高低点
  4. 整体是一个Smart Money Concept (SMC) 风格的供需区识别指标

下面给你写一个核心功能版本:自动识别摆动高低点,在这些位置绘制供需区矩形,并可选地标注市场结构变化。

使用说明

安装方法

  1. 将代码保存为 SmartMoneyZones.mq5,放入 MQL5/Indicators 文件夹
  2. 在 MetaEditor 中编译
  3. 拖动到图表即可使用

参数说明

  • 摆动识别周期(默认5):左右各对比多少根K线来判断高低点,越大识别越严格
  • 供需区厚度(默认3点):矩形区域的垂直高度,根据品种波动调整
  • 区域向右延伸(默认50根):矩形向未来延伸多少根K线
  • 需求区颜色(默认蓝色):支撑区域的颜色
  • 供给区颜色(默认红色):阻力区域的颜色
  • 区域透明度(0-100):数值越大越透明,推荐70-90
  • 显示标注:是否在区域旁边显示 "Demand/Supply" 文字
  • 最多显示区域:限制图表上同时显示的区域数量,0=不限制
  • 价格触及后删除:开启后,价格触及区域会自动删除该区域

信号解读

  • 蓝色矩形:需求区(买方力量强),价格回踩时可能反弹
  • 红色矩形:供给区(卖方力量强),价格上探时可能回落
  • 结合趋势使用:上升趋势关注需求区做多,下降趋势关注供给区做空

注意事项

  • 此指标基于历史高低点绘制,不保证未来一定有效
  • 建议结合其他确认信号(如突破、成交量)使用
  • 摆动周期越大,识别的区域越少但越可靠
  • 不同周期(H1/H4/D1)识别的区域级别不同,大周期更有参考价值

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

这是什么

这是一个基于 Smart Money Concept(聪明钱概念)的 MT5 供需区自动识别指标。它扫描历史 K 线,找出摆动高点和低点,在这些位置画出矩形区域:摆动低点画蓝色需求区(支撑),摆动高点画红色供给区(阻力)。用来帮你快速定位潜在的反转区域,省去手动画线的麻烦。

逻辑原理

指标用「摆动识别周期」参数,对比每根 K 线左右各 N 根的高低点。如果某根 K 线的最高价比左右各 N 根都高,就是摆动高点,在这里画一个供给区矩形,上边界是该 K 线的最高价,下边界往下延伸设定的点数。摆动低点同理,最低价比左右各 N 根都低,就画需求区矩形。

矩形向右延伸设定的 K 线数量,形成一个「等待区域」。代码会检查当前价格是否进入这些区域,如果开启了「触及删除」,价格穿过区域后会自动移除该矩形和标签。为了不让图表太乱,可以限制最多显示多少个区域,超出时自动删除最老的。

首次运行时只扫描最近 100 根 K 线,避免加载时卡顿。绘制的区域不会重绘,因为它基于已经完成的摆动点,不会因为后续 K 线改变而移动。

参数说明

  • 摆动识别周期(默认 5):左右各对比多少根 K 线来判断高低点。数值越大,识别越严格,画出的区域越少但越可靠。小周期图表用 3-5,大周期可以用 7-10。
  • 供需区厚度(默认 3 点):矩形区域的垂直高度,单位是点数(Point)。XAUUSD 波动大可以设 5-10,EURUSD 可以用 2-5,根据品种调。
  • 区域向右延伸(默认 50 根):矩形向未来延伸多少根 K 线。H1 图可以设 30-50,H4 可以设 20-30。
  • 需求区颜色(默认蓝色):支撑区域的颜色,代码里是 clrDodgerBlue。
  • 供给区颜色(默认红色):阻力区域的颜色,代码里是 clrTomato。
  • 区域透明度(默认 85):0 完全不透明,100 完全透明。85 左右比较舒服,既能看到又不挡 K 线。
  • 显示标注(默认开启):在区域旁边显示 "Demand" 或 "Supply" 文字,方便区分。
  • 最多显示区域(默认 10):限制图表上同时显示的区域数量,0 表示不限制。设太多会乱,10-20 比较合适。
  • 价格触及后删除(默认开启):价格进入区域后自动删除该矩形。关闭的话区域会一直保留。

怎么用

把代码保存为 SmartMoneyZones.mq5,放进 MT5 数据文件夹的 MQL5/Indicators 目录,在 MetaEditor 里编译。然后从导航器窗口拖到你想分析的图表上,调整参数后确认。

图表上会自动画出蓝色和红色矩形。蓝色需求区是历史的摆动低点,价格回踩时可能反弹;红色供给区是摆动高点,价格上探时可能承压。结合趋势方向使用:上升趋势关注需求区找多单机会,下降趋势关注供给区找空单机会。不建议逆势操作。

可以叠加其他工具确认,比如趋势线、均线、成交量放大等。单纯依赖供需区不够,要看当时的市场环境。

适用场景

适合波动较大、流动性好的品种,比如黄金(XAUUSD)、原油、主要货币对(EURUSD、GBPUSD)。小品种或冷门交叉盘摆动点不明显,效果会差。

推荐用在 H1、H4、D1 这种较大周期上,识别出来的区域级别更高,参考价值更大。M5、M15 噪音多,摆动点太频繁,容易被假突破。

适合趋势行情的回调进场:趋势形成后,价格回踩需求区或上探供给区时配合其他信号入场。不适合宽幅震荡或无明确趋势的市场,因为供需区会被反复穿透,失去参考意义。

风险提示

这类指标基于历史摆动点,不保证未来价格一定在这些区域反转。市场结构变化、重大新闻、流动性枯竭时,历史支撑阻力可能直接失效。

区域厚度是固定点数,不会根据当时的波动率或成交量调整,可能与实际供需强度不符。代码里没有对突破强度的判断,单纯画区域,需要你自己结合 K 线形态、量能确认。

摆动识别周期设太小会画出很多杂乱区域,设太大会漏掉一些次级支撑阻力。需要根据交易周期和品种反复测试,找到合适的参数组合。

触及删除功能只看价格是否进入区域,不管是快速穿透还是在区域内盘整。如果价格只是扫一下就走,删除后可能后续还会回来测试。建议配合价格行为(Pin Bar、吞没等)或其他指标过滤信号,不要看到区域就盲目入场。

常见问题

这个指标会重绘吗?
不会。它基于已经完成的摆动高低点绘制区域,历史 K 线确定后不会改变,所以画出的区域位置是固定的,不会因为后续 K 线而移动或消失(除非你开启了触及删除功能)。
摆动周期默认值 5 适合什么图表周期?
默认值 5 比较适合 H1 和 H4 图表。如果你用 M15 或 M30,可以降到 3;用 D1 或 W1,可以提高到 7-10,避免识别出太多次要摆动点。
区域厚度 3 点对 XAUUSD 够用吗?
XAUUSD 波动大,3 点可能偏薄。建议根据当前价格和 ATR 调整,一般设 5-10 点比较合理。EURUSD、GBPUSD 这种可以用 2-5 点。代码里是固定点数,不同品种要手动调。
价格触及后删除有什么用?
开启后,价格进入区域会自动移除该矩形,让图表保持清爽,只留未测试的区域。关闭的话所有历史区域都会保留,适合你想回看价格在哪些位置发生过反转。根据个人习惯选。
可以用在 XAUUSD 以外的品种吗?
可以。代码里没有品种限制,任何品种都能用。但效果取决于品种的波动特性和流动性,主要货币对、黄金、原油效果较好,冷门品种或小周期噪音多的不太适合。

Smart Money Supply & Demand Zones Indicator for MT5

An MT5 indicator that automatically identifies swing highs and lows, then draws supply and demand zone rectangles on the chart. Based on Smart Money Concept, it helps traders spot potential reversal areas with adjustable sensitivity, zone thickness, and auto-deletion on touch.

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

评论

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

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