手工制图表和交易工具箱(第三部分)。 优化和全新工具·进阶篇
🛠️

手工制图表和交易工具箱(第三部分)。 优化和全新工具·进阶篇

(2/3)·从智能交易系统改指标后,40 个图表同开也不再卡顿,这篇讲清重构与新增工具

新手友好 第 2/3 篇

很多交易者把键盘快捷键脚本挂在 EA 上,却没意识到多图表同时跑键盘检查会悄悄拖慢终端。当选项卡开到三四十个,击键响应变钝,制图节奏直接被打断。其实只需让非活动图表闭嘴,就能把负载压回单窗口。

◍ 用鼠标点图拉出一段自由趋势线

在 MT5 自定义指标里,想靠鼠标点两下就把一段趋势线画出来,核心落在这个 DrawFreeLine 函数。它不负责画整条线,只管「找极值点 + 记录第一击坐标 + 清掉旧标记」,真正的连线逻辑在别处。 函数入口四个参数:_bar 是搜索起点柱序,_isUp 决定找顶还是找底,_fractalSizeRight 和 _fractalSizeLeft 默认都是 1,代表极值左右各比对 1 根 K 线。fractalForFirstSearch 用 MathMax 取二者较大值再乘 2,给首次环绕搜索留了缓冲柱数。 第一次点击时 m_Clicks_Count 为 0,代码把它置 1,并按 _isUp 用 iHigh 或 iLow 抓 selectedBar 的 prices,时间轴用 iTime 取。之后还调了 DeepPointSearch 往更高精度时间层蹭一下极值时间,最后 DrawFirstPointMarker 画个起点标记。 第二次点击走 else 分支:先 ObjectDelete 删掉第一次的标记物,再按同样逻辑取 countedPrice / countedTime。到这里两段坐标齐了,外汇和贵金属波动大、滑点频繁,这种手动锚点方式在 M5 以下周期可能偏滞后,建议开 MT5 用 EURUSD 的 H1 实测两次点击的偏移量。

MQL5 / C++
class=class="str">"cmt">//| Parameters: |
class=class="str">"cmt">//| class="type">int _bar - bar to start search at |
class=class="str">"cmt">//| class="type">bool _isUp - top or bottom? |
class=class="str">"cmt">//| class="type">int _fractalSizeRight - number of bars to the right of extr |
class=class="str">"cmt">//| class="type">int _fractalSizeLeft - number of bars to the left of extremum|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CGraphics::DrawFreeLine(
  class="type">int _bar,
  class="type">bool _isUp,
  class="type">int _fractalSizeRight=class="num">1,
  class="type">int _fractalSizeLeft=class="num">1
)
{
class=class="str">"cmt">//--- Variables
  class="type">class="kw">double    selectedPrice,countedPrice,trendPrice1,trendPrice2;
  class="type">class="kw">datetime  selectedTime,countedTime,trendTime1,trendTime2;
  class="type">int       selectedBar,countedBar;
  class="type">int       bar1,bar2;
  class="type">class="kw">string trendName="",trendDescription="p2;";
  class="type">int fractalForFirstSearch = MathMax(_fractalSizeRight,_fractalSizeLeft)* class="num">2;
class=class="str">"cmt">//--- Search for a bar that meets the extremum criteria
  selectedBar = CUtilites::GetNearesExtremumSearchAround(
    _bar,
    _isUp,
    _fractalSizeLeft,
    _fractalSizeRight
  );
class=class="str">"cmt">//--- Building the starting marker
  if(class="num">0==m_Clicks_Count)
   {
   m_Clicks_Count=class="num">1;
   if(_isUp)
    {
    m_First_Point_Price=iHigh(NULL,PERIOD_CURRENT,selectedBar);
    }
   else
    {
    m_First_Point_Price=iLow(NULL,PERIOD_CURRENT,selectedBar);
    }
   m_First_Point_Time=iTime(NULL,PERIOD_CURRENT,selectedBar);
   class=class="str">"cmt">//---
   m_First_Point_Time=CUtilites::DeepPointSearch(
                         m_First_Point_Time,
                         _isUp,
                         ENUM_TIMEFRAMES(Period())
                         );
   class=class="str">"cmt">//---
   DrawFirstPointMarker(_isUp);

   }
class=class="str">"cmt">//--- Processing a click on the chart
  else
   {
   ObjectDelete(class="num">0,m_First_Point_Marker_Name);
   if(_isUp)
    {
    countedPrice=iHigh(NULL,PERIOD_CURRENT,selectedBar);
    }
   else
    {
    countedPrice=iLow(NULL,PERIOD_CURRENT,selectedBar);
    }
   countedTime=iTime(NULL,PERIOD_CURRENT,selectedBar);

「在小周期上把趋势线画正」

这段代码干的事很直接:把在大周期找到的拐点,映射回当前图表的小周期坐标,再创建一条向右延伸的趋势线。外汇和贵金属这类高杠杆品种,跨周期画线若时间锚点错位,视觉上就会出现明显偏移,动手前建议先在 EURUSD 的 M15 上跑一遍。 先看时间锚点的处理。DeepPointSearch 把 countedTime 在小周期里向下深挖,保证拐点落在真实存在的 K 线上;随后用 if(countedTime<m_First_Point_Time) 强制让趋势线总是从左画到右——如果你习惯从右往左看结构,可以把这一段到下一个注释之间的代码注释掉。 TrendCreate 的调用里,颜色由 GetTimeFrameColor(GetAllLowerTimeframes()) 自动按小周期取,射线参数写死为 true(向右无限延伸)。线宽和线型走的是外部变量 Trend_Line_Width 与 Trend_Line_Style,想改观感直接调这两个全局量即可。 末尾用 iBarShift 把两个时间点转成 bar 索引,再用 GetTimeInFuture 按 (bar1-bar2)*m_Free_Trend_Length_Coefficient 推算线右端延伸到的未来 bar 位置。系数 m_Free_Trend_Length_Coefficient 设成 1.5 时,线长约为两拐点间距的 1.5 倍,可自行改了看延展是否顺眼。

MQL5 / C++
class=class="str">"cmt">//--- Move a point in time on smaller timeframes
countedTime=CUtilites::DeepPointSearch(countedTime,_isUp,ENUM_TIMEFRAMES(Period()));
class=class="str">"cmt">//--- The line is always drawn from left to right. 
class=class="str">"cmt">//--- If it is not convenient, you can comment this part
class=class="str">"cmt">//---   up to the next comment
if(countedTime<m_First_Point_Time)
 {
  trendTime1=countedTime;
  trendPrice1=countedPrice;
  trendTime2=m_First_Point_Time;
  trendPrice2=m_First_Point_Price;
 }
else
 {
  trendTime2=countedTime;
  trendPrice2=countedPrice;
  trendTime1=m_First_Point_Time;
  trendPrice1=m_First_Point_Price;
 }
class=class="str">"cmt">//--- Set the description for future correction
trendDescription+=TimeToString(trendTime2)+";"+DoubleToString(trendPrice2,Digits());
class=class="str">"cmt">//selectedPrice=CUtilites::EquationDirect(
class=class="str">"cmt">//                     trendTime1,trendPrice1,trendTime2,trendPrice2,selectedTime
class=class="str">"cmt">//          );
trendName=CUtilites::GetCurrentObjectName(allPrefixes[class="num">0],OBJ_TREND);

TrendCreate(
   class="num">0,                   class=class="str">"cmt">// Chart ID
   trendName,           class=class="str">"cmt">// Line name
   class="num">0,                   class=class="str">"cmt">// Subwindow number
   trendTime1,          class=class="str">"cmt">// time of the first point
   trendPrice1,         class=class="str">"cmt">// price of the first point
   trendTime2,          class=class="str">"cmt">// time of the second point
   trendPrice2,         class=class="str">"cmt">// price of the second point
   CUtilites::GetTimeFrameColor(
      CUtilites::GetAllLowerTimeframes()
   ),                   class=class="str">"cmt">// line class="type">class="kw">color
   Trend_Line_Style,    class=class="str">"cmt">// line style
   Trend_Line_Width,    class=class="str">"cmt">// line width
   class="kw">false,               class=class="str">"cmt">// background object
   true,                class=class="str">"cmt">// is the line selected
   true                 class=class="str">"cmt">// ray to the right
);

bar1=iBarShift(NULL,class="num">0,trendTime1);
bar2=iBarShift(NULL,class="num">0,trendTime2);
selectedTime = CUtilites::GetTimeInFuture(
                  class=class="str">"cmt">//iTime(NULL,PERIOD_CURRENT,class="num">0),
                  trendTime1,
                  (class="type">int)((bar1-bar2)*m_Free_Trend_Length_Coefficient),
                  COUNT_IN_BARS

趋势线拖拽后的射线与文本落点

手动拉完趋势线后,代码会把端点时间通过 GetTimeInFuture 推到未来,再取该时刻的价位回填。其中 m_Free_Trend_Length_Coefficient 控制延伸长度,按 (bar1-bar2)*系数 算出的柱数决定射线右端落在第几根 K 线。 ObjectSetInteger 连设 OBJPROP_RAY 与 OBJPROP_RAY_RIGHT,两条都等于 IsRay() 的返回值,意味着左右射线开关被同步绑定到同一个函数结果。若 IsRay() 返回 false,画出来就是段线而非无限延伸线。 ObjectMove 用 selectedTime 和 selectedPrice 把第二点挪到未来位置,随后 m_Clicks_Count 清零并 ToggleFreeLineMode 退出自由绘制。最后 ObjectSetString 写 trendDescription 文本、ChartRedraw 重绘——少一步都可能看到旧标签残留。 开 MT5 把这段塞进你自己的画线脚本,改 m_Free_Trend_Length_Coefficient 从 1.5 调到 3.0,能直接对比射线右端外抛距离的变化。外汇与贵金属杠杆高,脚本仅辅助标线,不构成方向判断。

MQL5 / C++
selectedTime = CUtilites::GetTimeInFuture(
                      class=class="str">"cmt">//iTime(NULL,PERIOD_CURRENT,class="num">0),
                      trendTime1,
                      (class="type">int)((bar1-bar2)*m_Free_Trend_Length_Coefficient),
                      COUNT_IN_BARS
                     );
selectedPrice= ObjectGetValueByTime(class="num">0,trendName,selectedTime);
   m_Clicks_Count=class="num">0;
   ToggleFreeLineMode();

◍ 下钻找点的循环逻辑

画线工具里每个端点都要单独跑一次 DeepPointSearch,这个函数放在实用工具文件里,核心任务是把高时间帧上的某个时间点,下钻到尽可能低的时间帧去定位最精确的日期。 最绕的部分是主搜索片段怎么判定历史里到底有没有目标时间。低周期往往缺高周期的信息,光用 iBars 数柱线不够,因为终端能显示的柱线数有上限。代码里先用 TerminalInfoInteger(TERMINAL_MAXBARS) 拿到这个上限,若 iBars 返回值超过上限就砍到 terminalMaxBars-1,避免越界读历史。 接着用 iTime 取当前周期最老一根柱的时间。若它比_neededTime 还早,说明目标时间在该周期历史范围内,可能已触到最深层;若最老时间晚于目标,则高周期数据更全,直接跳上一层周期继续找。 所有检查通过后,才在更高周期那根烛条范围内找极端点。用 iBarShift 锁定高周期柱号,再按 PeriodSeconds 比值推算低周期应扫多少根柱,配合标准极值函数定出高低点,反推时间收工。 当前实现只支持 T 键和 Q 键调用的指标线,外汇与贵金属波动大、滑点频繁,这类跨周期下钻在高波动时段可能偏位,实盘前建议在 MT5 用历史数据跑一遍校验。下个版本计划放开到全部品种并做单独定制。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Search for a given point on lower timeframes &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Parameters:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span>
<span class="comment">class=class="str">"cmt">//|&nbsp;&nbsp; class="type">class="kw">datetime _neededTime - start time on a higher timeframe    &nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//|&nbsp;&nbsp; class="type">bool _isUp - search by highs or by lows          &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//|&nbsp;&nbsp; ENUM_TIMEFRAMES _higher_TF - the highest period  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Return value:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;          |</span>
<span class="comment">class=class="str">"cmt">//|&nbsp;&nbsp; More accurate date(on the lowest possible timeframe)  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">class="kw">datetime</span> CUtilites::DeepPointSearch(
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">datetime</span> _neededTime,
&nbsp;&nbsp;<span class="keyword">class="type">bool</span> _isUp,
&nbsp;&nbsp;<span class="macro">ENUM_TIMEFRAMES</span> _higher_TF=<span class="macro">PERIOD_CURRENT</span>
)
{
<span class="comment">class=class="str">"cmt">//---</span>
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- As a result it gets the most accurate time available</span>
&nbsp;&nbsp;<span class="keyword">class="type">class="kw">datetime</span> deepTime=<span class="number">class="num">0</span>;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- current timeframe</span>
&nbsp;&nbsp;<span class="macro">ENUM_TIMEFRAMES</span> currentTF;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- The number of the highest timeframe in the list of all available periods</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> highTFIndex = GetTimeFrameIndexByPeriod(_higher_TF);
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- The higher period in seconds</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> highTFSeconds = <span class="functions">PeriodSeconds</span>(_higher_TF);
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- Current interval in seconds</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> currentTFSeconds;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- Counter</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> i;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- Bar number on a higher timeframe</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> highBar=<span class="functions">iBarShift</span>(<span class="macro">NULL</span>,_higher_TF,_neededTime);
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- Bar number on the current timeframe</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> currentBar;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- The total number of bars on the current timeframe</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> tfBarsCount;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- How many bars of a lower TF fit into one bar of a higher TF</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> lowerBarsInHigherPeriod;
&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- Maximum allowed number of bars in the terminal</span>
&nbsp;&nbsp;<span class="keyword">class="type">int</span> terminalMaxBars = <span class="functions">TerminalInfoInteger</span>(<span class="macro">TERMINAL_MAXBARS</span>);
<span class="comment">class=class="str">"cmt">//--- Loop sequentially through all timeframes</span>
&nbsp;&nbsp;<span class="keyword">for</span>(i=<span class="number">class="num">0</span>; i&lt;highTFIndex; i++)
&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">//--- Get a timeframe by a number in the list</span>
&nbsp;&nbsp;&nbsp;&nbsp;currentTF=GetTimeFrameByIndex(i);
<span class="comment">class=class="str">"cmt">//--- Check if this timeframe has the required time.</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-class="type">class="kw">color:rgb(class="num">216, class="num">232, class="num">194);">tfBarsCount=<span class="functions">iBars</span>(<span class="macro">NULL</span>,currentTF);
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(tfBarsCount&gt;terminalMaxBars-<span class="number">class="num">1</span>)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tfBarsCount=terminalMaxBars-<span class="number">class="num">1</span>;
&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp;&nbsp;&nbsp;deepTime=<span class="functions">iTime</span>(<span class="macro">NULL</span>,currentTF,tfBarsCount-<span class="number">class="num">1</span>);
<span class="comment">class=class="str">"cmt">//--- If it has, find it.</span>
&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(deepTime&gt;<span class="number">class="num">0</span> &amp;&amp; deepTime&lt;_neededTime)</span>
&nbsp;&nbsp;&nbsp;&nbsp; {

「跨周期定位极值棒的检索逻辑」

在多周期嵌套扫描里,先拿 PeriodSeconds 算出当前周期秒数,再用高周期秒数除以它得到 lowerBarsInHigherPeriod,也就是一根高周期 K 线覆盖了多少根低周期棒。这个值直接决定了后续 iHighest / iLowest 的搜索窗口宽度。 若标记了向上方向 _isUp,就在高周期上用 iHighest 找 MODE_HIGH 的极值棒;否则用 iLowest 找 MODE_LOW。搜索范围写成 currentBar-lowerBarsInHigherPeriod+1 到 currentBar 之后多取一根(lowerBarsInHigherPeriod+1),确保把高周期那一根完整覆盖的低周期区间都扫进去。 循环里一旦通过 iTime 拿到 deepTime 就 break 退出;若一直走到高周期索引 highTFIndex 才结束,说明目标时间只存在于更高周期,此时直接把 _neededTime 赋给 deepTime 返回。 终端层面还有一道硬限制:TerminalInfoInteger(TERMINAL_MAXBARS) 给出当前 MT5 客户端允许的最大棒数,跨周期回看深度不能超过它,否则 iBarShift 会返回 -1,定位直接失效。外汇与贵金属杠杆品种波动剧烈,这类跨周期计算务必先在策略测试器里用真实品种复盘验证。

MQL5 / C++
currentTFSeconds=PeriodSeconds(currentTF);

class=class="str">"cmt">//--- Search for the required bar only within the higher TF candlestick
lowerBarsInHigherPeriod=highTFSeconds/currentTFSeconds;
currentBar = iBarShift(NULL,currentTF,_neededTime);

if(_isUp)
 {
  currentBar = iHighest(
                    NULL,currentTF,MODE_HIGH,
                    lowerBarsInHigherPeriod+class="num">1,
                    currentBar-lowerBarsInHigherPeriod+class="num">1
                   );
 }
else
 {
  currentBar = iLowest(
                   NULL,currentTF,MODE_LOW,
                   lowerBarsInHigherPeriod+class="num">1,
                   currentBar-lowerBarsInHigherPeriod+class="num">1
                  );
 }
deepTime=iTime(NULL,currentTF,currentBar);
class=class="str">"cmt">//--- Once the required time is found, stop the search
break;
   }
  }
class=class="str">"cmt">//--- If reached the end of the loop
 if(i==highTFIndex)
  {
   class=class="str">"cmt">//--- then the required time is only available on the higher timeframe.
   deepTime=_neededTime;
  }
class=class="str">"cmt">//---
 class="kw">return (deepTime);
}
class=class="str">"cmt">//--- Maximum allowed number of bars in the terminal
 class="type">int terminalMaxBars = TerminalInfoInteger(TERMINAL_MAXBARS);

用描述字段兜住时间缺口

指标线画出去之后,最怕的是标的出现时间跳空:线尾端和中间参考框差出一天多,末端被迫偏移,顶部附近的直线走向也跟着歪。线一缩窄,新的突破就冒出来,策略信号可能因此失真。外汇市场常一周才出现一次峰值跳空,感受不明显;股票端随交易所不同,一天内可能多次发生时间缺口,靠肉眼逐个修线不现实。 思路是把“正确坐标”先藏起来,再按实际缺口回推。我选了直线的描述(OBJPROP_TEXT)来存,因为自动画对象时交易者大多不填描述,不会撞车;线多的话也可改用清单文件或终端全局变量。下面这段把基准点时间价格拼进描述字符串。 [CODE] /* Graphics.mqh */ void CGraphics::DrawFreeLine(//...) { //... string trendDescription="p2;"; //... trendDescription+=TimeToString(trendTime2)+";"+DoubleToString(trendPrice2,Digits()); //... ObjectSetString(0,trendName,OBJPROP_TEXT,trendDescription); [/CODE] 真正校正放在每小时开头跑一次。核心函数先查对象在不在,再拆描述文本,只认以“p2”起头、至少3段的记录;基准点时间从描述第2段还原。股票的高频时间缺口会让末端漂移,外汇和贵金属虽跳空少但杠杆高、风险大,这类校正逻辑在实盘前务必用策略 tester 跑一遍验证。

MQL5 / C++
class=class="str">"cmt">/* Graphics.mqh */
class="type">void CGraphics::DrawFreeLine(class=class="str">"cmt">//...)
{
class=class="str">"cmt">//...
  class="type">class="kw">string trendDescription="p2;";
class=class="str">"cmt">//...
  trendDescription+=TimeToString(trendTime2)+";"+DoubleToString(trendPrice2,Digits());
class=class="str">"cmt">//...
  ObjectSetString(class="num">0,trendName,OBJPROP_TEXT,trendDescription);
class=class="str">"cmt">/* Utilites.mqh */
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Adjusts the position of line end in the future in case of price |
class=class="str">"cmt">//|   gaps                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Parameters:                                                       |
class=class="str">"cmt">//|   class="type">class="kw">string _line_name - the name of the line to be corrected        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CUtilites::CorrectTrendFutureEnd(class="type">class="kw">string _line_name)
{
class=class="str">"cmt">//---
  if(ObjectFind(class="num">0,_line_name)<class="num">0)
   {
     PrintDebugMessage(__FUNCTION__+" _line_name="+_line_name+": Object does not exist");
     class=class="str">"cmt">//--- If there is no object to search, there is nothing more to do.
     class="kw">return;
   }
  class=class="str">"cmt">//--- Get a description
  class="type">class="kw">string line_text=ObjectGetString(class="num">0,_line_name,OBJPROP_TEXT);

  class="type">class="kw">string point_components[]; class=class="str">"cmt">// array for point description fragments
  class="type">class="kw">string name_components[];  class=class="str">"cmt">// array containing line name fragments
  class="type">class="kw">string helpful_name="Helpful line"; class=class="str">"cmt">// the name of the auxiliary line
  class="type">class="kw">string vertical_name=""; class=class="str">"cmt">// the name of the corresponding vertical from the crosshair

  class=class="str">"cmt">//--- Get the point time and price in class="type">class="kw">string form
  class="type">int point_components_count=StringSplit(line_text,StringGetCharacter(";",class="num">0),point_components);

  class="type">class="kw">datetime time_of_base_point; class=class="str">"cmt">// time of the basic point
class="type">class="kw">datetime time_first_point,time_second_point; class=class="str">"cmt">// the time of the first and the second point
class="type">class="kw">datetime time_far_ideal; class=class="str">"cmt">// estimated time in the future
  class="type">class="kw">double price_of_base_point; class=class="str">"cmt">// the price of the basic point
  class="type">class="kw">double price_first_point,price_second_point; class=class="str">"cmt">// the prices of the first and the second point
  class="type">int i; class=class="str">"cmt">// counter
class=class="str">"cmt">//--- Check if the line is needed
  if(line_text=="" || point_components_count<class="num">3 || point_components[class="num">0]!="p2")
   {
     PrintDebugMessage(__FUNCTION__+" Error: the line cannot be used");
     class="kw">return;
   }
class=class="str">"cmt">//--- Get the coordinates of the "basic" point from the line description
  time_of_base_point=StringToTime(point_components[class="num">1]);
交给小布盯盘看盘口
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到多图表负载与活动窗口状态,把重复劳动交给小布,你专注决策。

常见问题

每个 EA 有独立执行线程,互不干扰,多图表处理键盘快捷键时终端不易变慢,但 EA 本职是交易,本程序不交易,故后续迁到指标更顺手。
它负责在图形里精准定位深层交点或极值参考点,减少手动数坐标的误差,具体逻辑见正文小节拆解。
小布盯盘已内置多图表活动状态诊断,虽未直接封装该 mqh 库,但打开品种页可看负载分布,省去自己部署指标的调试成本。
主要修正本地与服务器时间、或绘图坐标中时间轴换算偏差,避免线条落在错误 K 线上,外汇贵金属时段切换时尤其要核对。
Shortcuts 管击键、GlobalVariables 管样式、Mouse 管光标、Utilites 管计算、Graphics 管绘制,当前结构作者仍觉方便,但布局后续可能再调。