一键平仓 / 一键保本工具(二创)
相对上一版改了:我需要你帮我写一个这样的指标
效果示意图 · AI 根据源码绘制
// 基于 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);
}
}
//+------------------------------------------------------------------+作品说明
从图上识别到的特征:
- 多个浅蓝色横向矩形区域,标记着关键的供需区/订单区块(Order Block),覆盖历史的支撑/阻力位
- 文字标注:BOS(结构突破)、CHoCH(趋势转换)、strong Low 等,这是 Smart Money Concept 的标注方式
- 虚线连接:连接某些关键的摆动高低点
- 整体是一个Smart Money Concept (SMC) 风格的供需区识别指标
下面给你写一个核心功能版本:自动识别摆动高低点,在这些位置绘制供需区矩形,并可选地标注市场结构变化。
使用说明
安装方法
- 将代码保存为
SmartMoneyZones.mq5,放入MQL5/Indicators文件夹 - 在 MetaEditor 中编译
- 拖动到图表即可使用
参数说明
- 摆动识别周期(默认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.
评论
还没有人评论,来抢个沙发。