订单块(供需区)自动识别
自动画出最近的看涨、看跌订单块矩形,价格回到区域内时弹窗提醒
效果示意图 · AI 根据源码绘制
#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;
}
}
}
//+------------------------------------------------------------------+作品说明
思路说明
订单块(Order Block)是 Smart Money 概念中的重要支撑/阻力区域。本指标通过识别「冲动突破前的最后一根反向K线」来标记订单块:
- 看涨订单块:下跌K线被后续强势上涨突破 → 该下跌K线实体是潜在支撑
- 看跌订单块:上涨K线被后续强势下跌突破 → 该上涨K线实体是潜在阻力
用半透明矩形标记这些区域并向右延伸,当价格回到区域时可选弹窗提醒,区域被完全突破后自动删除,始终保留最近的有效订单块。
使用说明
安装方法
- 将代码保存为
OrderBlock_Indicator.mq5,放入 MT5 的MQL5/Indicators文件夹 - 在 MetaEditor 中编译(F7),确保无错误
- 在图表上右键 → "技术指标列表" → "自定义" → 选择
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.
评论
还没有人评论,来抢个沙发。