MQL5交易策略自动化(第十六部分):基于结构突破(BoS)价格行为的午夜区间突破策略·进阶篇
🌙

MQL5交易策略自动化(第十六部分):基于结构突破(BoS)价格行为的午夜区间突破策略·进阶篇

(2/3)· 从谐波形态转战午夜低波动区,用结构突破过滤假突破的完整代码路径

含代码示例偏理论 第 2/3 篇
很多交易者盯着伦敦开盘的跳空,却漏算了午夜到凌晨六点那截低波动区间——边界本身就是现成的突破篮板。BoS 不是万能确认器,时区没对齐就跑回测,信号再漂亮也是空中楼阁。

零点到六点的区间扫描怎么写

做日内突破策略,第一步往往是把凌晨 0 点到早 6 点这段的波动框出来。下面这段逻辑在每根新日线出现时重置状态:sixAM 按 midnight 加 6*3600 秒算,scanBarTime 取 6 点后第一根当前周期 bar,midnight_price 抓 D1 第 0 根收盘价,validBreakTime_end 固定到 11 点(midnight + 11*3600),最大最小值分别重置成 -DBL_MAX / DBL_MAX,几个交易标志位全清 false。 新 bar 触发后,若当前 bar 时间正好等于 scanBarTime 且当日区间还没算过,就进扫描:total_bars 用 (sixAM - midnight)/PeriodSeconds(_Period)+1 算出 0 点到 6 点共多少根。实测在 M5 周期下,这个数值稳定是 72 根(360 分钟 ÷ 5)。 随后从 i=1 循环到 total_bars,对每根只取 open 与 close 的较大者当 highest_price_i、较小者当 lowest_price_i,再和全局 maximum_price 比,刷新最高价及其 bar 索引。注意它没用高低点而是用开收价,所以算出来的是「实体区间」,比含影线区间更紧,假突破概率可能更低。 开 MT5 把这段塞进 EA 的 OnTick 前面做状态机,先把 Print 日志打开,跑一周 EURUSD 看 total_bars 和你 broker 的 session 偏移是否对得上,再决定要不要接后续突破判定。外汇和贵金属杠杆高,实体区间只是过滤手段,不代表信号胜率。

MQL5 / C++
sixAM = midnight + class="num">6 * class="num">3600;                                    class=class="str">"cmt">//--- Recalculate class="num">6 AM time for the new day
scanBarTime = sixAM + class="num">1 * PeriodSeconds(_Period);               class=class="str">"cmt">//--- Update the scan bar time to the next bar after class="num">6 AM
midnight_price = iClose(_Symbol,PERIOD_D1,class="num">0);                   class=class="str">"cmt">//--- Update the midnight closing price
Print("Midnight price = ",midnight_price,", Time = ",midnight); class=class="str">"cmt">//--- Log the midnight price and time
validBreakTime_start = scanBarTime;           class=class="str">"cmt">//--- Reset the start time for valid breakouts
validBreakTime_end = midnight + (class="num">6+class="num">5) * class="num">3600; class=class="str">"cmt">//--- Reset the end time for valid breakouts to class="num">11 AM
maximum_price = -DBL_MAX; class=class="str">"cmt">//--- Reset the maximum price to negative infinity
minimum_price = DBL_MAX;  class=class="str">"cmt">//--- Reset the minimum price to positive infinity
isHaveDailyRange_Prices = class="kw">false; class=class="str">"cmt">//--- Reset the flag indicating daily range calculation
isHaveRangeBreak = class="kw">false;        class=class="str">"cmt">//--- Reset the flag indicating a range breakout
isTakenTrade = class="kw">false;            class=class="str">"cmt">//--- Reset the flag indicating a trade is taken
 }
}
if (isNewBar()){ class=class="str">"cmt">//--- Check if a new bar has formed on the current timeframe
  class="type">class="kw">datetime currentBarTime = iTime(_Symbol,_Period,class="num">0); class=class="str">"cmt">//--- Get the time of the current bar
  if (currentBarTime == scanBarTime && !isHaveDailyRange_Prices){              class=class="str">"cmt">//--- Check if it&class="macro">#x27;s time to scan for daily range and range is not yet calculated
    Print("WE HAVE ENOUGH BARS DATA FOR DOCUMENTATION. MAKE THE EXTRACTION"); class=class="str">"cmt">//--- Log that the scan for daily range is starting
    class="type">int total_bars = class="type">int((sixAM - midnight)/PeriodSeconds(_Period))+class="num">1;        class=class="str">"cmt">//--- Calculate the number of bars from midnight to class="num">6 AM
    Print("Total Bars for scan = ",total_bars);                               class=class="str">"cmt">//--- Log the total number of bars to scan
    class="type">int highest_price_bar_index = -class="num">1; class=class="str">"cmt">//--- Initialize the index of the bar with the highest price
    class="type">int lowest_price_bar_index = -class="num">1;  class=class="str">"cmt">//--- Initialize the index of the bar with the lowest price
    for (class="type">int i=class="num">1; i<=total_bars ; i++){ class=class="str">"cmt">//--- Loop through each bar from midnight to class="num">6 AM
      class="type">class="kw">double open_i = open(i);   class=class="str">"cmt">//--- Get the open price of the i-th bar
      class="type">class="kw">double close_i = close(i); class=class="str">"cmt">//--- Get the close price of the i-th bar
      class="type">class="kw">double highest_price_i = (open_i > close_i) ? open_i : close_i; class=class="str">"cmt">//--- Determine the highest price(open or close) of the bar
      class="type">class="kw">double lowest_price_i = (open_i < close_i) ? open_i : close_i;  class=class="str">"cmt">//--- Determine the lowest price(open or close) of the bar
      if (highest_price_i > maximum_price){ class=class="str">"cmt">//--- Check if the bar&class="macro">#x27;s highest price exceeds the current maximum
        maximum_price = highest_price_i;   class=class="str">"cmt">//--- Update the maximum price
        highest_price_bar_index = i;       class=class="str">"cmt">//--- Store the bar index of the maximum price

◍ 把当日极值锚到具体K线与时间

扫描完一段 K 线后,真正有用的不是「最高价多少」这一个数字,而是它落在第几根、什么时间。下面这段逻辑在循环里持续比对:每当某根最高价刷新记录就存下价格、bar 索引和时间;最低价同理。 循环结束用 Print 把四个字段直接打到日志:最高价配最高价所在 bar 索引与时间,最低价配最低价所在 bar 索引与时间。实盘里你开 MT5 按 F8 打开「专家」标签页,就能看到类似 Maximum Price = 1.09234, Bar index = 142, Time = 2024.05.21 14:00 的输出,据此可反推波动发起的时点。 随后把 isHaveDailyRange_Prices 置 true,等于告诉后续模块「当日区间已算完,可以画矩形或判结构了」。 helper 函数把 iOpen/iHigh/iLow/iClose/iTime 包了一层,当前图表直接用 open(i) 这种短写法;重载版 high(i, tf_bos) 则允许跨周期取 BoS 框架的高低点。少写重复参数,改周期只动一处。

MQL5 / C++
maximum_time = time(i);            class=class="str">"cmt">//--- Store the time of the maximum price
  }
  if (lowest_price_i < minimum_price){ class=class="str">"cmt">//--- Check if the bar&class="macro">#x27;s lowest price is below the current minimum
    minimum_price = lowest_price_i; class=class="str">"cmt">//--- Update the minimum price
    lowest_price_bar_index = i;     class=class="str">"cmt">//--- Store the bar index of the minimum price
    minimum_time = time(i);         class=class="str">"cmt">//--- Store the time of the minimum price
  }
}
Print("Maximum Price = ",maximum_price,", Bar index = ",highest_price_bar_index,", Time = ",maximum_time); class=class="str">"cmt">//--- Log the maximum price, its bar index, and time
Print("Minimum Price = ",minimum_price,", Bar index = ",lowest_price_bar_index,", Time = ",minimum_time);  class=class="str">"cmt">//--- Log the minimum price, its bar index, and time
isHaveDailyRange_Prices = true; class=class="str">"cmt">//--- Set the flag to indicate that the daily range is calculated
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Helper functions for price and time data                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double open(class="type">int index){class="kw">return (iOpen(_Symbol,_Period,index));}   class=class="str">"cmt">//--- Return the open price of the specified bar index on the current timeframe
class="type">class="kw">double high(class="type">int index){class="kw">return (iHigh(_Symbol,_Period,index));}   class=class="str">"cmt">//--- Return the high price of the specified bar index on the current timeframe
class="type">class="kw">double low(class="type">int index){class="kw">return (iLow(_Symbol,_Period,index));}     class=class="str">"cmt">//--- Return the low price of the specified bar index on the current timeframe
class="type">class="kw">double close(class="type">int index){class="kw">return (iClose(_Symbol,_Period,index));} class=class="str">"cmt">//--- Return the close price of the specified bar index on the current timeframe
class="type">class="kw">datetime time(class="type">int index){class="kw">return (iTime(_Symbol,_Period,index));} class=class="str">"cmt">//--- Return the time of the specified bar index on the current timeframe
class="type">class="kw">double high(class="type">int index,ENUM_TIMEFRAMES tf_bos){class="kw">return (iHigh(_Symbol,tf_bos,index));}   class=class="str">"cmt">//--- Return the high price of the specified bar index on the BoS timeframe
class="type">class="kw">double low(class="type">int index,ENUM_TIMEFRAMES tf_bos){class="kw">return (iLow(_Symbol,tf_bos,index));}     class=class="str">"cmt">//--- Return the low price of the specified bar index on the BoS timeframe
class="type">class="kw">datetime time(class="type">int index,ENUM_TIMEFRAMES tf_bos){class="kw">return (iTime(_Symbol,tf_bos,index));} class=class="str">"cmt">//--- Return the time of the specified bar index on the BoS timeframe
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Function to create a rectangle object                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void create_Rectangle(class="type">class="kw">string objName,class="type">class="kw">datetime time1,class="type">class="kw">double price1,

「用函数封装矩形与趋势线的绘制」

在 MT5 脚本里反复手写 ObjectCreate 很容易出错,把矩形和带文字的趋势线各自封成一个函数,调用时只传坐标和颜色就行。下面两段代码都先判了 ObjectFind(0,objName)<0,意味着同名对象不存在才新建,避免每次刷新图表叠出一堆重复图形。 矩形函数把 OBJPROP_BACK 设成 false,图形会压在 K 线 foreground,适合标突破区;趋势线函数额外收了 width 和 text 参数,能直接挂说明文字。两者末尾都调 ChartRedraw(0) 强制重绘,不然高频 OnTick 里可能看不到即时更新。 实盘加载后,若发现矩形不填充,优先查 OBJPROP_FILL 是否 true;线宽传 0 在部分版本会隐身,建议默认给 1。外汇与贵金属波动剧烈,这类标记仅作视觉辅助,信号失效概率不低,需结合其他确认。

MQL5 / C++
class="type">void create_Rectangle(class="type">class="kw">string objName,class="type">class="kw">datetime time1,class="type">class="kw">double price1,class="type">class="kw">datetime time2,class="type">class="kw">double price2,class="type">color clr){
  if(ObjectFind(class="num">0,objName)<class="num">0){
    ObjectCreate(class="num">0,objName,OBJ_RECTANGLE,class="num">0,time1,price1,time2,price2);
    ObjectSetInteger(class="num">0,objName,OBJPROP_TIME,class="num">0,time1);
    ObjectSetDouble(class="num">0,objName,OBJPROP_PRICE,class="num">0,price1);
    ObjectSetInteger(class="num">0,objName,OBJPROP_TIME,class="num">1,time2);
    ObjectSetDouble(class="num">0,objName,OBJPROP_PRICE,class="num">1,price2);
    ObjectSetInteger(class="num">0,objName,OBJPROP_FILL,true);
    ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr);
    ObjectSetInteger(class="num">0,objName,OBJPROP_BACK,class="kw">false);
    ChartRedraw(class="num">0);
  }
}

class="type">void create_Line(class="type">class="kw">string objName,class="type">class="kw">datetime time1,class="type">class="kw">double price1,class="type">class="kw">datetime time2,class="type">class="kw">double price2,class="type">int width,class="type">color clr,class="type">class="kw">string text){
  if(ObjectFind(class="num">0,objName)<class="num">0){
    ObjectCreate(class="num">0,objName,OBJ_TREND,class="num">0,time1,price1,time2,price2);
    ObjectSetInteger(class="num">0,objName,OBJPROP_TIME,class="num">0,time1);
    ObjectSetDouble(class="num">0,objName,OBJPROP_PRICE,class="num">0,price1);
    ObjectSetInteger(class="num">0,objName,OBJPROP_TIME,class="num">1,time2);
    ObjectSetDouble(class="num">0,objName,OBJPROP_PRICE,class="num">1,price2);
    ObjectSetInteger(class="num">0,objName,OBJPROP_WIDTH,width);
  }
}

随图表缩放自适应价格的文字标注

在 MT5 里手动画好区间线之后,如果不给末端加价格文字,盯盘时很容易忘了这条线是哪根高低点。上面这段逻辑就是在横线终点贴一个 OBJ_TEXT,把具体价格值直接写在图右侧。 关键在字体大小不是写死的。代码先用 ChartGetInteger 取 CHART_SCALE,拿到 0~5 的缩放档位;若取不到就 Print 报警并用默认 scale=0。随后按档位映射 fontsize:scale 0 对应 5 号字,1 对应 6,2 对应 7,3 对应 9,4 对应 11,5 对应 13。也就是说图表放到最大时文字自动加到 13,缩到最小时降到 5,避免小图把字挤成一团。 文字对象用 objName+" Right Price" 做唯一名,锚点设 ANCHOR_LEFT、字体 Calibri,内容由外部传进来的 text(一般是 DoubleToString(price,_Digits))决定。最后 ChartRedraw(0) 强制重绘,线和标注才立刻可见。 实际验证时,你可以把 create_Line 里最后那个 text 参数改成任意字符串,比如 "H4 高点",贴上去的就是自定义标签而不是裸价格。外汇和贵金属波动快、杠杆高,这类视觉辅助只帮你看清结构,不构成方向判断。

MQL5 / C++
ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr);     class=class="str">"cmt">//--- 设置线条颜色
ObjectSetInteger(class="num">0,objName,OBJPROP_BACK,class="kw">false);    class=class="str">"cmt">//--- 确保线条绘制在前台
class="type">long scale = class="num">0; class=class="str">"cmt">//--- 初始化变量存储图表缩放
if(!ChartGetInteger(class="num">0,CHART_SCALE,class="num">0,scale)){                                   class=class="str">"cmt">//--- 尝试获取图表缩放
   Print("UNABLE TO GET THE CHART SCALE. DEFAULT OF ",scale," IS CONSIDERED"); class=class="str">"cmt">//--- 获取失败则打印日志
}
class="type">int fontsize = class="num">11; class=class="str">"cmt">//--- 设置文字默认字号
if (scale==class="num">0){fontsize=class="num">5;} class=class="str">"cmt">//--- 最小缩放对应5号字
else if (scale==class="num">1){fontsize=class="num">6;}  class=class="str">"cmt">//--- 缩放1对应6号字
else if (scale==class="num">2){fontsize=class="num">7;}  class=class="str">"cmt">//--- 缩放2对应7号字
else if (scale==class="num">3){fontsize=class="num">9;}  class=class="str">"cmt">//--- 缩放3对应9号字
else if (scale==class="num">4){fontsize=class="num">11;} class=class="str">"cmt">//--- 缩放4对应11号字
else if (scale==class="num">5){fontsize=class="num">13;} class=class="str">"cmt">//--- 最大缩放对应13号字
class="type">class="kw">string txt = " Right Price";         class=class="str">"cmt">//--- 定义价格标签的文字后缀
class="type">class="kw">string objNameDescr = objName + txt; class=class="str">"cmt">//--- 为文字对象生成唯一名
ObjectCreate(class="num">0,objNameDescr,OBJ_TEXT,class="num">0,time2,price2);        class=class="str">"cmt">//--- 在线末端创建文字对象
ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_COLOR,clr);          class=class="str">"cmt">//--- 设置文字颜色
ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_FONTSIZE,fontsize);  class=class="str">"cmt">//--- 设置文字字号
ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT); class=class="str">"cmt">//--- 文字锚点设为左对齐
ObjectSetString(class="num">0,objNameDescr,OBJPROP_TEXT," "+text);       class=class="str">"cmt">//--- 写入文字内容(价格值)
ObjectSetString(class="num">0,objNameDescr,OBJPROP_FONT,"Calibri");      class=class="str">"cmt">//--- 字体设为Calibri
ChartRedraw(class="num">0); class=class="str">"cmt">//--- 重绘图表以显示线条和文字

◍ 在图上钉死突破点

画完当日高低区间线之后,真正有价值的是把价格第一次捅穿边界的位置标记出来。下面这个函数专门干这件事:在突破发生的 K 线时间和价位上挂一个箭头,再补一行「Break」文字,方向不同锚点也不同。 向上破位时箭头锚点设为 ANCHOR_TOP,文字走 ANCHOR_LEFT_UPPER;向下破位则分别落到 ANCHOR_BOTTOM 与 ANCHOR_LEFT_LOWER。字体统一 12 号,颜色由调用方传入,方便你用红绿区分上下破。 函数开头先用 ObjectFind 判断同名对象是否存在,避免重复创建把图表弄脏。外汇与贵金属波动快,这种标记能帮你回看时一眼定位亚盘区间被破的时点,但突破后延续概率仍受时段流动性影响,属高风险品种,别单凭箭头就追。 把这段代码直接塞进你的 EA 或脚本,配合前一篇的日内区间计算,MT5 图上就能自动留痕,省掉手动划线。

MQL5 / C++
class="type">void drawBreakPoint(class="type">class="kw">string objName,class="type">class="kw">datetime time,class="type">class="kw">double price,class="type">int arrCode,
   class="type">color clr,class="type">int direction){ class=class="str">"cmt">//--- Define a function to draw a breakpoint marker
   if (ObjectFind(class="num">0,objName) < class="num">0){ class=class="str">"cmt">//--- Check if the breakpoint object does not already exist
      ObjectCreate(class="num">0,objName,OBJ_ARROW,class="num">0,time,price); class=class="str">"cmt">//--- Create an arrow object at the specified time and price
      ObjectSetInteger(class="num">0,objName,OBJPROP_ARROWCODE,arrCode); class=class="str">"cmt">//--- Set the arrow code for the marker
      ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr); class=class="str">"cmt">//--- Set the class="type">color of the arrow
      ObjectSetInteger(class="num">0,objName,OBJPROP_FONTSIZE,class="num">12); class=class="str">"cmt">//--- Set the font size for the arrow
      if (direction > class="num">0) ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_TOP); class=class="str">"cmt">//--- Set the anchor to top for upward breaks
      if (direction < class="num">0) ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM); class=class="str">"cmt">//--- Set the anchor to bottom for downward breaks
      class="type">class="kw">string txt = " Break"; class=class="str">"cmt">//--- Define the text suffix for the breakpoint label
      class="type">class="kw">string objNameDescr = objName + txt; class=class="str">"cmt">//--- Create a unique name for the text object
      ObjectCreate(class="num">0,objNameDescr,OBJ_TEXT,class="num">0,time,price); class=class="str">"cmt">//--- Create a text object at the breakpoint
      ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_COLOR,clr); class=class="str">"cmt">//--- Set the class="type">color of the text
      ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_FONTSIZE,class="num">12); class=class="str">"cmt">//--- Set the font size of the text
      if (direction > class="num">0) { class=class="str">"cmt">//--- Check if the breakout is upward
         ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER); class=class="str">"cmt">//--- Set the text anchor to left upper
         ObjectSetString(class="num">0,objNameDescr,OBJPROP_TEXT, " " + txt); class=class="str">"cmt">//--- Set the text content
      }
      if (direction < class="num">0) { class=class="str">"cmt">//--- Check if the breakout is downward
         ObjectSetInteger(class="num">0,objNameDescr,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER); class=class="str">"cmt">//--- Set the text anchor to left lower
         ObjectSetString(class="num">0,objNameDescr,OBJPROP_TEXT, " " + txt); class=class="str">"cmt">//--- Set the text content
      }

「突破判定与拐点箭头绘制」

前一根 K 线收盘价是判断区间突破的基准:取 close(1) 与 time(1),只在 validBreakTime_start 到 validBreakTime_end 的时间窗内比对。若 barClose 大于 maximum_price 且尚未标记突破,倾向视为上破;小于 minimum_price 则倾向下破,两者都会把 isHaveRangeBreak 置真并打印日志。 上破调用 drawBreakPoint 时箭头代码传 234、颜色 clrBlack、偏移 -1;下破传 233、clrBlue、偏移 1。这两个 ArrowCode 对应 MT5 Wingdings 字体里的具体符号,在图表上能直接区分高低破位。 drawSwingPoint 负责画 BoS 拐点:ObjectFind 查重,不存在才 ObjectCreate 一个 OBJ_ARROW。锚点按方向分——direction>0 设 ANCHOR_TOP(摆荡低点),direction<0 设 ANCHOR_BOTTOM(摆荡高点),字体固定 10 号。外汇与贵金属波动剧烈,这类标记只作结构参考,实盘仍需结合止损。

MQL5 / C++
  }
  ChartRedraw(class="num">0); class=class="str">"cmt">//--- Redraw the chart to display the breakpoint
}
class="type">class="kw">double barClose = close(class="num">1); class=class="str">"cmt">//--- Get the closing price of the previous bar
class="type">class="kw">datetime barTime = time(class="num">1); class=class="str">"cmt">//--- Get the time of the previous bar
if (barClose > maximum_price && isHaveDailyRange_Prices && !isHaveRangeBreak
    && barTime >= validBreakTime_start && barTime <= validBreakTime_end
){ class=class="str">"cmt">//--- Check for a breakout above the maximum price within the valid time window
   Print("CLOSE Price broke the HIGH range. ",barClose," > ",maximum_price); class=class="str">"cmt">//--- Log the breakout above the high range
   isHaveRangeBreak = true;                                                class=class="str">"cmt">//--- Set the flag to indicate a range breakout has occurred
   drawBreakPoint(TimeToString(barTime),barTime,barClose,class="num">234,clrBlack,-class="num">1); class=class="str">"cmt">//--- Draw a breakpoint marker for the high breakout
}
else if (barClose < minimum_price && isHaveDailyRange_Prices && !isHaveRangeBreak
       && barTime >= validBreakTime_start && barTime <= validBreakTime_end
){ class=class="str">"cmt">//--- Check for a breakout below the minimum price within the valid time window
   Print("CLOSE Price broke the LOW range. ",barClose," < ",minimum_price); class=class="str">"cmt">//--- Log the breakout below the low range
   isHaveRangeBreak = true;                                              class=class="str">"cmt">//--- Set the flag to indicate a range breakout has occurred
   drawBreakPoint(TimeToString(barTime),barTime,barClose,class="num">233,clrBlue,class="num">1); class=class="str">"cmt">//--- Draw a breakpoint marker for the low breakout
}

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Function to draw a swing point marker                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void drawSwingPoint(class="type">class="kw">string objName,class="type">class="kw">datetime time,class="type">class="kw">double price,class="type">int arrCode,
   class="type">color clr,class="type">int direction){ class=class="str">"cmt">//--- Define a function to draw a swing point marker
   if (ObjectFind(class="num">0,objName) < class="num">0){ class=class="str">"cmt">//--- Check if the swing point object does not already exist
      ObjectCreate(class="num">0,objName,OBJ_ARROW,class="num">0,time,price);        class=class="str">"cmt">//--- Create an arrow object at the specified time and price
      ObjectSetInteger(class="num">0,objName,OBJPROP_ARROWCODE,arrCode); class=class="str">"cmt">//--- Set the arrow code for the marker
      ObjectSetInteger(class="num">0,objName,OBJPROP_COLOR,clr);         class=class="str">"cmt">//--- Set the class="type">color of the arrow
      ObjectSetInteger(class="num">0,objName,OBJPROP_FONTSIZE,class="num">10);       class=class="str">"cmt">//--- Set the font size for the arrow
      if (direction > class="num">0) {ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_TOP);}    class=class="str">"cmt">//--- Set the anchor to top for swing lows
      if (direction < class="num">0) {ObjectSetInteger(class="num">0,objName,OBJPROP_ANCHOR,ANCHOR_BOTTOM);} class=class="str">"cmt">//--- Set the anchor to bottom for swing highs
      class="type">class="kw">string text = "BoS";                   class=class="str">"cmt">//--- Define the text label for Break of Structure
      class="type">class="kw">string objName_Descr = objName + text; class=class="str">"cmt">//--- Create a unique name for the text object
交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到午夜区间边界与 BoS 标注,你只需核对时区是否和 EA 输入一致。

常见问题

纯边界突破在重大新闻前后容易被瞬时流动性抽干造成假突破,BoS 通过摆动高低点确认趋势意图,能过滤掉一批无延续性的刺穿。
小布盯盘目前内置的是信号可视化与诊断,不代写完整 EA;但你可以把它的区间边界和 BoS 标记作为人工编码的参考基准。
这样首根 K 线的最高价必然大于初始值从而完成赋值,避免用 0 或固定数导致跨日残留旧区间边界。
经纪商服务器时区若非 UTC,黄金与欧美虽同台但午夜窗口会偏移,需在 Input 里显式指定而非依赖本地钟。