手工图表和交易工具包(第二部分)。 图表图形绘图工具·综合运用
📘

手工图表和交易工具包(第二部分)。 图表图形绘图工具·综合运用

第 3/3 篇

「给斐波那契对象批量写层级标注」

在 MT5 里手动给斐波那契工具每层改文字很烦,尤其画完扇形或回撤后层级一多就容易漏。把标注逻辑收进一个图形类,用循环统一写 OBJPROP_LEVELTEXT,能省掉重复操作。 下面这段类方法 SetFiboDescriptions 接收对象名和描述数组,先取真实层级数 levels_count,再比对数组成员的 array_size。循环里若数组还有对应项就写进去,不够就留空串,最后 ChartRedraw(0) 刷新。 实盘注意:外汇与贵金属杠杆高、滑点大,这类辅助绘图不改变信号本身,仅提升读图效率,任何标注都不构成方向保证,行情可能反向。

MQL5 / C++
class CGraphics
  {
  class=class="str">"cmt">//--- Fields
class="kw">private:
  class=class="str">"cmt">// ...
  class="type">color       m_Fibo_Default_Color;
  ENUM_LINE_STYLE  m_Fibo_Default_Style;
  class=class="str">"cmt">// ...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Default constructor                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
CGraphics::CGraphics(class="type">void)
  {
  class=class="str">"cmt">//...
  m_Fibo_Default_Color = Fibo_Default_Color;
  m_Fibo_Default_Style = VFun_Levels_Style;
  }
  class=class="str">"cmt">// ...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Sets descriptions of levels in any Fibonacci object              |
class=class="str">"cmt">//|  _object_name - the name of the Fibonacci object                 |
class=class="str">"cmt">//|  _levels_descriptions[] - array of level descriptions            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CGraphics::SetFiboDescriptions(
  class="type">class="kw">string _object_name,               class=class="str">"cmt">// Object name
  class="kw">const class="type">class="kw">string &_levels_descriptions[] class=class="str">"cmt">// Array of descriptions
)
  {
  class="type">int i,                                     class=class="str">"cmt">// Current level counter
       levels_count=(class="type">int)ObjectGetInteger(class="num">0,_object_name,OBJPROP_LEVELS), class=class="str">"cmt">// The real number of levels
       array_size=ArraySize(_levels_descriptions);                        class=class="str">"cmt">// The number of received descriptions
class=class="str">"cmt">//--- Loop  through all levels
  for(i=class="num">0; i<levels_count; i++)
    {
    if(array_size>class="num">0 && i<array_size) class=class="str">"cmt">// Choose a description from the array
      {
      class=class="str">"cmt">//--- and write it to the level
      ObjectSetString(class="num">0,_object_name,OBJPROP_LEVELTEXT,i,_levels_descriptions[i]);
      }
    else class=class="str">"cmt">// If the descriptions are not enough,
      {
      ObjectSetString(class="num">0,_object_name,OBJPROP_LEVELTEXT,i,""); class=class="str">"cmt">// leave the description empty
      }
    }
class=class="str">"cmt">//--- Redraw the chart before finishing
  ChartRedraw(class="num">0);
  }

◍ 用鼠标位置决定斐波那契扇形取点方向

在 MT5 里动态画斐波那契扇形,核心是先判定光标在价格上还是下,再决定抓波段低点还是高点。下面这段代码把这套逻辑落到了具体变量和函数调用上,复制进 EA 就能直接验证。 double levels_values[]; 声明存放扇形比例水平的数组,比如常见的 0.382、0.618 都从这里读。string levels_descriptions[] = {}; 是水平线的文字说明容器,初始为空。int p1=0, p2=0; 记录扇形起止两根 K 线的序号,double price1=0, price2=0; 对应两个端点的价格。 fun_name 通过 CUtilites::GetCurrentObjectName(allPrefixes[3],OBJ_FIBOFAN) 自动生成扇形对象名,避免重复创建冲突;fun_0_name 则给零号射线单独建一条 OBJ_TREND 线,主要为了兼容 MT4 的显示习惯。 CUtilites::StringToDoubleArray(VFun_Levels,levels_values); 把参数字符串解析成双精度数组,这一步决定了扇形有几条射线。随后用 CMouse::Below() 和 CMouse::Above() 分流:光标在价格下方就取两段 iLow 最低价,上方则取 iHigh 最高价,SetExtremumsBarsNumbers 负责找离鼠标最近的极值棒。 ObjectCreate 用 iTime 转换出的时间轴坐标加 price1/price2 画出 OBJ_FIBOFAN;TrendCreate 再补一条带颜色的下周期零射线。最后 SetFiboLevels(fun_name,levels_values) 把水平比例写进对象。外汇与贵金属波动剧烈,这类自动画线仅作辅助参考,实际进出场仍需结合风控。

MQL5 / C++
class="type">class="kw">double levels_values[];                      class=class="str">"cmt">// Array of level values
class="type">class="kw">string levels_descriptions[] = {};             class=class="str">"cmt">// Array of level descriptions
class="type">int p1=class="num">0,                                   class=class="str">"cmt">// Bar number for the fan starting point
    p2=class="num">0;                                   class=class="str">"cmt">// Bar number for the fan ending point
class="type">class="kw">double price1=class="num">0,                            class=class="str">"cmt">// First point price
      price2=class="num">0;                             class=class="str">"cmt">// Second point price
class="type">class="kw">string fun_name =                           class=class="str">"cmt">// Fan name
   CUtilites::GetCurrentObjectName(allPrefixes[class="num">3],OBJ_FIBOFAN),
   fun_0_name =
     CUtilites::GetCurrentObjectName(allPrefixes[class="num">3]+"0_",OBJ_TREND);
class=class="str">"cmt">//--- Get data for the fan from the parameter class="type">class="kw">string
   CUtilites::StringToDoubleArray(VFun_Levels,levels_values);
class=class="str">"cmt">//--- Find the extreme points closest to the mouse
   if(CMouse::Below())     class=class="str">"cmt">// If the mouse cursor is below the price
     {
      CUtilites::SetExtremumsBarsNumbers(class="kw">false,p1,p2);
      price1=iLow(Symbol(),PERIOD_CURRENT,p1);
      price2=iLow(Symbol(),PERIOD_CURRENT,p2);
     }
   else
     if(CMouse::Above())  class=class="str">"cmt">// If the mouse cursor is above the price
       {
        CUtilites::SetExtremumsBarsNumbers(true,p1,p2);
        price1=iHigh(Symbol(),PERIOD_CURRENT,p1);
        price2=iHigh(Symbol(),PERIOD_CURRENT,p2);
       }
class=class="str">"cmt">//--- Create the fan object
   ObjectCreate(
      class="num">0,fun_name,OBJ_FIBOFAN,class="num">0,
      iTime(Symbol(),PERIOD_CURRENT,p1),
      price1,
      iTime(Symbol(),PERIOD_CURRENT,p2),
      price2
   );
class=class="str">"cmt">//--- The zero ray of this object is denoted by a colored line(for compatibility with MT4)
   TrendCreate(
      class="num">0,
      fun_0_name,
      class="num">0,
      iTime(Symbol(),PERIOD_CURRENT,p1),
      price1,
      iTime(Symbol(),PERIOD_CURRENT,p2),
      price2,
      CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()),
      class="num">0,class="num">1,class="kw">false,true,true
   );
class=class="str">"cmt">//--- Describe the fan levels
   SetFiboLevels(fun_name,levels_values);

斐波那契对象的装饰与快捷键绘制落点

在创建斐波那契工具后,先用 SetFiboDescriptions 写入层级文字,再用 CurrentObjectDecorate 设定默认颜色与时间帧属性,否则对象在图表上会缺失标注或颜色错乱。 替代射线(substitute ray)的装饰不能套用主对象颜色,而要用 CUtilites::GetTimeFrameColor 配合 GetAllLowerTimeframes 取到的下级周期色,这样在多头时间帧叠加时仍能区分来源。 快捷键响应里,当 lparam 匹配 VFun_Key 对应的操作字符,就触发 m_graphics.DrawVFan 画斐波那契扇面;注意 ChartRedraw(0) 必须放在对象属性全部设完之后,否则 MT5 可能渲染上一帧的半成品。外汇与贵金属图表叠加多周期工具属高风险操作,参数误设会导致视觉误导。

MQL5 / C++
  SetFiboDescriptions(fun_name, levels_descriptions);
class=class="str">"cmt">//--- Set standard parameters(such as timeframes and selection after creation)
  CurrentObjectDecorate(fun_name,m_Fibo_Default_Color);
class=class="str">"cmt">//--- Also make out the "substitute" ray
  CurrentObjectDecorate(
     fun_0_name,
     CUtilites::GetTimeFrameColor(
        CUtilites::GetAllLowerTimeframes()
     )
  );
class=class="str">"cmt">//---
  ChartRedraw(class="num">0);
}
CUtilites::StringToDoubleArray(VFun_Levels,levels_values);
class=class="str">"cmt">//--- Draw a Fibonacci fan(VFun)
      if(CUtilites::GetCurrentOperationChar(VFun_Key) == lparam)
        {
         m_graphics.DrawVFan();
        }
      break;

「三类草叉同屏的画法逻辑」

安德鲁草叉在实战里不是单用一种,而是常规、席夫、反转三套一起挂。常规草叉端点直接落在极值价上;席夫草叉的端点1放在趋势方向上1-2距离的一半处,中心线斜率更小,若价格贴合这类草叉,走势倾向横盘调整;反转草叉端点1逆势偏移与1-2等距,专门应对快速行情,存续时间短但价格跨度大。 把三种都丢到图表上,关键点和未来极端位会清楚很多。作者用两个函数实现:一个按坐标画任意类型草叉,另一个先算好点1/2/3基准坐标再循环调用。草叉类型用枚举定义,基准点收进 PitchforkPoints base 结构里,调绘图函数时传参更干净。 快捷键映射写在 Shortcuts.mqh,编译后图表上按 P 键即可呼出草叉集合。外汇和贵金属波动剧烈,草叉仅作概率参考,实际触发仍受高风险事件影响。 下面这段是绘制函数的核心骨架,按类型切换价格与样式后交给 ObjectCreate 生成 OBJ_PITCHFORK:

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Creates Andrews&class="macro">#x27; pitchfork using specified coordinates            |
class=class="str">"cmt">//| Parameters:                                                      |
class=class="str">"cmt">//|    _name - the name of created pitchfork                         |
class=class="str">"cmt">//|    _base - the structure containing coordinates of  basic points |
class=class="str">"cmt">//|    _type - pitchfork type(SIMPLE,SHIFF,REVERCE)                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void  CGraphics::MakePitchfork(
   class="type">class="kw">string _name,                 class=class="str">"cmt">// The name of the created object
   PitchforkPoints &_base,       class=class="str">"cmt">// Structure describing pitchfork base points
   PitchforkType _type           class=class="str">"cmt">// Pitchfork type(SIMPLE,SHIFF,REVERCE)
  )
  {
class=class="str">"cmt">//---
   class="type">class="kw">double price_first;           class=class="str">"cmt">// The price of the first point(depends on the type)
   class="type">color pitchfork_color;        class=class="str">"cmt">// Pitchfork class="type">color (depends on the type)
   class="type">int pitchfork_width;          class=class="str">"cmt">// Line width(depends on the type)
   ENUM_LINE_STYLE pitchfork_style; class=class="str">"cmt">// Line style(depends on the type)
   class="type">class="kw">double fibo_levels[] = {class="num">1};   class=class="str">"cmt">// Add external levels(only for MQL5)
   class="type">class="kw">string fibo_descriptions[] = {""}; class=class="str">"cmt">// Level description(only for MQL5)
class=class="str">"cmt">//--- Set type dependent parameters:
   if(_type == SHIFF)            class=class="str">"cmt">// Schiff pitchfork
     {
      price_first = _base.shiffMainPointPrice;
      pitchfork_color = Pitchfork_Shiff_Color;
      pitchfork_width = Pitchfork_Shiff_Width;
      pitchfork_style = Pitchfork_Shiff_Style;
     }
   else
     if(_type == REVERCE)        class=class="str">"cmt">// "Reverse" pitchfork
       {
        price_first = _base.reverceMainPointPrice;
        pitchfork_color = Pitchfork_Reverce_Color;
        pitchfork_width = Pitchfork_Reverce_Width;
        pitchfork_style = Pitchfork_Reverce_Style;
       }
     else
       {
        class=class="str">"cmt">// "classic" pitchfork
        price_first =_base.mainPointPrice;
        pitchfork_color = Pitchfork_Main_Color;
        pitchfork_width = Pitchfork_Main_Width;
        pitchfork_style = Pitchfork_Main_Style;
       }
class=class="str">"cmt">//--- Draw
   ObjectCreate(class="num">0,_name,OBJ_PITCHFORK,class="num">0,
                 _base.time1,price_first,
                 _base.time2,_base.secondPointPrice,

◍ 一套三型 pitchfork 的鼠标取点逻辑

在 MT5 里批量绘制 Andrews 干草叉,常见做法是把常规、Schiff、反向 Schiff(也叫 micmed 通道)三种类型放在同一个基准上一次画出。代码里的 DrawPitchforksSet 就是干这事:它先读鼠标位置决定方向,再回溯两个极值棒,作为干草叉的第一、第二基点。 方向判断靠 CMouse::Below() 和 CMouse::Above()。若鼠标落在 K 线实体上(既不在上方也不在下方),函数直接 return 并提示「把点设在棒极值价的上方或下方」——这意味着手动取点时,光标必须脱离实体,否则什么也画不出来。 基点不是随手取,而是用 CUtilites::GetNearestExtremumBarNumber 在 dropped_bar 左右各搜 Pitchfork_First_Point_Left_Bars / Right_Bars 根棒找最近极值。第二基点从 bar_first-1 开始反向搜,保证两个基点不重叠。外汇与贵金属波动剧烈,这种极值回溯窗口设太小,叉线可能对噪音过度敏感,实盘前建议在策略测试器里调参验证。 下面这段是取点与方向判定的核心片段,逐行看: bool up=true; // 方向标记,鼠标在价下方则转 false double dropped_price = CMouse::Price(); // 鼠标所在「起点」价格 int dropped_bar = CMouse::Bar(); // 起点棒号 string name = ""; // 当前对象名暂空 PitchforkPoints base; // 存基准坐标的结构体 if(CMouse::Below()) { up=false; } // 鼠标在价下,方向向下 else { if(!CMouse::Above()) { // 在实体上 if(Print_Warning_Messages) Print(DEBUG_MESSAGE_PREFIX,": Set a point above or below the bar extreme price"); return; // 不画,退出 } }

MQL5 / C++
  class="type">bool up=true;                      class=class="str">"cmt">// direction(mouse below or above the price)
  class="type">class="kw">double dropped_price = CMouse::Price();   class=class="str">"cmt">// "Starting point" price
  class="type">int dropped_bar = CMouse::Bar();          class=class="str">"cmt">// Starting point bar number
  class="type">class="kw">string name = "";                         class=class="str">"cmt">// The name of the current object
  PitchforkPoints base;                     class=class="str">"cmt">// Structure for the base coordinates
class=class="str">"cmt">//---
  if(CMouse::Below())
    {
      up=class="kw">false;
    }
  else
    {
      if(!CMouse::Above()) class=class="str">"cmt">// If the mouse pointer is on the candlestick, do nothing
        {
          if(Print_Warning_Messages)
            {
              Print(DEBUG_MESSAGE_PREFIX,": Set a point above or below the bar extreme price");
            }
          class="kw">return;
        }
    }

三极值点定位与三种叉形基线赋值

pitchfork 的骨架来自三个极值点:首点、次点、第三点。代码里用 CUtilites::GetNearestExtremumBarNumber 按左右搜索窗口(Pitchfork_Second_Point_Left_Bars / Right_Bars 等参数)回看 K 线号,up 标志决定找顶还是找底。 若任一极点返回负值(bar_first<0 等),说明当前品种与周期不满足条件,直接 Print 警告并 return,不会强行画线。外汇与贵金属波动结构差异大,EURUSD 与 XAUUSD 在同一周期下可能一个出点一个不出点,属正常现象。 基线价格按方向填充:上升叉中 mainPointPrice 取首点 iHigh,secondPointPrice 取次点 iLow,thirdPointPrice 取第三点 iHigh;下降叉反之。Schiff 与 reverse 变体的首点价格,则用主点减去(主点-次点)/2 或加回该半差得出。 开 MT5 把 Pitchfork_Third_Point_Left_Bars 从默认 20 改到 50,观察 XAUUSD 的 H1 上第三点是否从近端摆动顶变为更早的高点——这直接改变整条中轨斜率。

MQL5 / C++
                                          Pitchfork_Second_Point_Left_Bars,
                                          Pitchfork_Second_Point_Right_Bars
                                        );
   class="type">int bar_third = CUtilites::GetNearestExtremumBarNumber(
                                          bar_second-class="num">1,
                                          true,
                                          up,
                                          Pitchfork_Third_Point_Left_Bars,
                                          Pitchfork_Third_Point_Right_Bars
                                        );
class=class="str">"cmt">//--- If not found, report an error
   if(bar_first<class="num">0||bar_second<class="num">0||bar_third<class="num">0)
     {
       if(Print_Warning_Messages)
         {
          Print(DEBUG_MESSAGE_PREFIX,": Could not find points that match all conditions.");
         }
       class="kw">return;
     }
class=class="str">"cmt">//--- Fill the structure for basic control points
   base.mainPointPrice = up ?                                          class=class="str">"cmt">// Price - first basic point
                                          iHigh(Symbol(),PERIOD_CURRENT,bar_first)
                                        : iLow(Symbol(),PERIOD_CURRENT,bar_first);
   base.secondPointPrice = up ?                                         class=class="str">"cmt">// Price - second basic point
                                          iLow(Symbol(),PERIOD_CURRENT,bar_second)
                                        : iHigh(Symbol(),PERIOD_CURRENT,bar_second);
   base.thirdPointPrice = up ?                                           class=class="str">"cmt">// Price - third basic point
                                          iHigh(Symbol(),PERIOD_CURRENT,bar_third)
                                        : iLow(Symbol(),PERIOD_CURRENT,bar_third);
   base.shiffMainPointPrice = base.mainPointPrice-                       class=class="str">"cmt">// Price - first point of Schiff pitchfork
                                          (base.mainPointPrice-base.secondPointPrice)/class="num">2;
   base.reverceMainPointPrice = base.mainPointPrice+                     class=class="str">"cmt">// Price - first point of "reverse" pitchfork
                                          (base.mainPointPrice-base.secondPointPrice)/class="num">2;

「用三点坐标驱动三种叉形线」

在 MT5 自定义指标里画 Andrews 叉形线,核心是先抓三个基准 K 线的时间戳。下面三行把第一、二、三点的时间分别写进结构体,iTime 取的是当前图表周期 PERIOD_CURRENT 下对应 bar 的开盘时间,bar_first / bar_second / bar_third 是外部算好的偏移量。 base.time1 = iTime(Symbol(),PERIOD_CURRENT,bar_first); // 第一点时间 base.time2 = iTime(Symbol(),PERIOD_CURRENT,bar_second); // 第二点时间 base.time3 = iTime(Symbol(),PERIOD_CURRENT,bar_third); // 第三点时间 拿到时间后,靠三个布尔开关分别绘制普通叉、Schiff 叉、反向叉。每个分支用 CUtilites::GetCurrentObjectName 拼出带前缀的对象名,再调 MakePitchfork 并传入枚举类型 SIMPLE / SHIFF / REVERCE;若三个开关全开,图表上会叠加三种叉形,外汇与贵金属品种在高波动时段可能出现线条密集、互相穿插的视觉噪声,需手动关掉其中一两个。 PitchforkType 枚举只列了三个值,对应绘制函数里的分支;PitchforkPoints 结构体则把三个点的价格(mainPointPrice、shiffMainPointPrice、reverceMainPointPrice 及另两个辅助价)和三个时间捆在一起。开 MT5 把这段塞进你的指标源码,改 allPrefixes[4] 的前缀,就能在 EURUSD 的 M15 上直接验证三种叉是否按预期生成。

MQL5 / C++
base.time1 = iTime(Symbol(),PERIOD_CURRENT,bar_first);   class=class="str">"cmt">// Time of the first point
base.time2 = iTime(Symbol(),PERIOD_CURRENT,bar_second);  class=class="str">"cmt">// Time of the second point
base.time3 = iTime(Symbol(),PERIOD_CURRENT,bar_third);   class=class="str">"cmt">// Time of the third point
class=class="str">"cmt">//--- Draw "regular" pitchfork
if(Pitchfork_Show_Main)
  {
  name =CUtilites::GetCurrentObjectName(allPrefixes[class="num">4]+"_main",OBJ_PITCHFORK);
  MakePitchfork(name,base,SIMPLE);
  }
class=class="str">"cmt">//--- Draw Schiff pitchfork
if(Pitchfork_Show_Shiff)
  {
  name =CUtilites::GetCurrentObjectName(allPrefixes[class="num">4]+"_shiff",OBJ_PITCHFORK);
  MakePitchfork(name,base,SHIFF);
  }
class=class="str">"cmt">//--- Draw "reverse" pitchfork
if(Pitchfork_Show_Reverce)
  {
  name =CUtilites::GetCurrentObjectName(allPrefixes[class="num">4]+"_reverce",OBJ_PITCHFORK);
  MakePitchfork(name,base,REVERCE);
  }
class=class="str">"cmt">//---
class=class="str">"cmt">//ChartRedraw(class="num">0); not needed here as it is called when drawing each object
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Possible Andrews&class="macro">#x27; pitchfork types                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum PitchforkType
  {
  SIMPLE,
  SHIFF,
  REVERCE
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|  Structure describing a "base" for the Andrews&class="macro">#x27; pitchfork         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">struct PitchforkPoints
  {
  class="type">class="kw">double      mainPointPrice;        class=class="str">"cmt">// Price - first base point
  class="type">class="kw">double      shiffMainPointPrice;   class=class="str">"cmt">// Price - second base point
  class="type">class="kw">double      reverceMainPointPrice; class=class="str">"cmt">// Price - third base point
  class="type">class="kw">double      secondPointPrice;      class=class="str">"cmt">// Price - first point of Schiff pitchfork
  class="type">class="kw">double      thirdPointPrice;       class=class="str">"cmt">// Price - first point of "reverse" pitchfork
  class="type">class="kw">datetime    time1;                 class=class="str">"cmt">// Time of the first point
  class="type">class="kw">datetime    time2;                 class=class="str">"cmt">// Time of the second point
  class="type">class="kw">datetime    time3;                 class=class="str">"cmt">// Time of the third point
  };

◍ 用按键事件触发安德鲁音叉重绘

在 MT5 自定义指标或 EA 里,把安德鲁音叉(Andrews Pitchfork)的绘制挂到键盘事件上,比每次手动刷图更顺手。核心思路是捕获 WM_KEYDOWN 的 lparam,跟预设的 Pitchfork_Key 做比对。 上面这段片段里,CUtilites::GetCurrentOperationChar(Pitchfork_Key) 取出当前按键对应的操作字符,若等于消息参数 lparam,就调用 m_graphics.DrawPitchforksSet() 重绘整组音叉,随后 break 退出分支。 实盘验证时建议把 Pitchfork_Key 设成不常用的组合键,避免和 MT5 默认快捷键冲突;外汇与贵金属波动剧烈、杠杆高风险,音叉仅作通道概率参考,不代表方向确定性。

MQL5 / C++
  class=class="str">"cmt">//...
class=class="str">"cmt">//--- Draw Andrews&class="macro">#x27; Pitchfork
      if(CUtilites::GetCurrentOperationChar(Pitchfork_Key) == lparam)
        {
         m_graphics.DrawPitchforksSet();
        }
      break;
  class=class="str">"cmt">//...

未来端点的趋势线在 MT5 里怎么画才不塌

MT5 的趋势线绘图依赖价格和时间双坐标,当你在周五画一条右端指向“下周一”的直线时,终端会先假设周日存在、再在周一发现周日不可交易而丢弃两天,导致按时间坐标拉伸的线比预期短一截。图上看得很清楚:同样的逻辑,若想量出固定根数 K 线距离的斜线,用日历算日期就会踩坑。 解决办法是把日期从“日历”改成“点位”。鼠标在图表上落一个点,相邻烛台间的像素距离可算(见前文“相邻柱线距离”一节),按需要的柱数往后数,再用标准 ChartXYToTimePrice 把屏幕坐标转回时间和价格。但要避开周日塌陷,线得在周一画,而不是周五。 空间限制是另一处暗雷。MT5 允许画线延伸的画布范围有限,线若太靠近右边框,终端可能把端点坐标转不出来,直接置为 0,对应 1970-01-01。我曾加一条斜线,结果它反向逆转、右端点被推到超前近六个月的位置。若线段纯按日期绘制则不会出这种怪象。 外汇与贵金属杠杆高、滑点大,这类绘图偏差会直接干扰级别判断,实盘前务必在 MT5 历史数据里复现一次。结论很直接:我们需要一个能算出尚未确定的未来日期的函数,来稳当地画这种一端伸向未来的直线。

「按像素或柱线推算未来时间的工具函数」

在 MT5 图表上做比例缩放相关的画线时,常需要从一个已知时间点往后推一段距离取未来日期。多数情况我们用柱线偏移就够了,但当视图按像素缩放后,用屏幕像素数反推时间会更贴合肉眼看到的距离。 作者把这类计算做成了纯工具函数,不绘图、不依赖鼠标,放进了 Utilities 类。早期版本在边界条件下算不准,后来重写得简单很多,核心就是 GetTimeInFuture()。 枚举 ENUM_FUTURE_COUNT 提供两种模式:COUNT_IN_BARS 按柱线数算,COUNT_IN_PIXELS 按像素数算。两者都是整数输入,函数靠这个枚举区分该把 _length 当什么单位。 调用时传参考时间 _start_time、间隔长度 _length,以及可选的计数类型(默认按柱线)。若按像素算失败、时间超出范围,函数会退化为按日期累加并跳过周日的近似结果。外汇与贵金属杠杆高,这类时间推算只用于辅助定位,实际信号须结合行情确认。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 GlobalVariables.mqh |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The enumeration describes possible options for calculating the   |
class=class="str">"cmt">//|   time of the next bar                                            |
class=class="str">"cmt">//|     COUNT_IN_BARS - calculate date by the number of bars         |
class=class="str">"cmt">//|     COUNT_IN_PIXELS - calculate date by the number of pixels     |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_FUTURE_COUNT {
   COUNT_IN_BARS,     class=class="str">"cmt">// By bars
   COUNT_IN_PIXELS    class=class="str">"cmt">// By pixel
};
class=class="str">"cmt">//...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Utilites.mqh |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//...
class CUtilites
  {
class="kw">public:
class=class="str">"cmt">//...
   class=class="str">"cmt">//--- Calculates a date in the future relative to the start date with the _length interval, specified in pixels or bars
   class="kw">static class="type">class="kw">datetime          GetTimeInFuture(
      class="kw">const class="type">class="kw">datetime _start_time,                                          class=class="str">"cmt">// Reference time based on which the future bar is calculated
      class="kw">const class="type">int _length,                                                   class=class="str">"cmt">// Interval length(in bars or pixels)
      class="kw">const ENUM_FUTURE_COUNT _count_type=COUNT_IN_BARS                   class=class="str">"cmt">// Interval type(pixels or bars).
   );
class=class="str">"cmt">//...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The function tries to calculate date in the future using the     |
class=class="str">"cmt">//|   screen x and y coordinates                                       |
class=class="str">"cmt">//| If calculation is unsuccessful(time exceeds limits), calculates |
class=class="str">"cmt">//|   time with an error: as a sum of dates excluding Sundays.        |
class=class="str">"cmt">//| Parameters:                                                        |
class=class="str">"cmt">//|   _current_time,                 Source time,                      |
class=class="str">"cmt">//|   _length,                       Interval length                   |

◍ 把未来时间换算成屏幕坐标的两种量法

在 MT5 自定义指标里做预测射线或虚线区间时,常需要由某个已知时间往后推一段距离取「未来时间」。这段距离可以有两种量法:按 K 线根数(COUNT_IN_BARS)或按屏幕像素(COUNT_IN_PIXELS),二者在坐标换算上的处理完全不同。 核心函数 GetTimeInFuture 接收起始时间 _start_time、间隔长度 _length 和量法枚举 _count_type。它先通过 ChartTimePriceToXY 把起始时间转成图表的 x 像素坐标,再根据量法决定 future_x 怎么加:按根数就乘上相邻两根 K 线的像素距 bar_distance,按像素就直接加。 最后用 ChartGetInteger(0,CHART_WIDTH_IN_PIXELS) 判断 future_x 是否没超出图表右边界——若超出,换算出来的未来时间就没有意义,调用方应丢弃该值。外汇与贵金属行情跳空频繁,bar_distance 会随缩放改变,实测中切换周期后像素距可能从 7 变到 3,射线落点会明显偏移,建议每次重绘前重取。

MQL5 / C++
class="type">class="kw">datetime CUtilites::GetTimeInFuture(
  class="kw">const class="type">class="kw">datetime _start_time,                      class=class="str">"cmt">// Reference time based on which the future bar is calculated
  class="kw">const class="type">int _length,                               class=class="str">"cmt">// Interval length(in bars or pixels)
  class="kw">const ENUM_FUTURE_COUNT _count_type=COUNT_IN_BARS class=class="str">"cmt">// Interval type(pixels or bars).
)
  {
class=class="str">"cmt">//---
  class="type">class="kw">datetime
    future_time;       class=class="str">"cmt">// Variable for result
  class="type">int
    bar_distance =  GetBarsPixelDistance(),        class=class="str">"cmt">// Distance in pixels between two adjacent bars
    current_x,                                     class=class="str">"cmt">// The x coordinate of the starting point
    future_x,                                      class=class="str">"cmt">// The x coordinate of the result
    current_y,                                     class=class="str">"cmt">// The y coordinate, does not affect the result; needed for the conversion function
    subwindow = class="num">0;                                 class=class="str">"cmt">// Subwindow index
  class="type">class="kw">double current_price;                            class=class="str">"cmt">// Any initial price, does not affect the result

class=class="str">"cmt">//--- Convert the time passed in parameters into the screen coordinate x
  ChartTimePriceToXY(class="num">0,subwindow,_start_time,CMouse::Price(),current_x,current_y);
class=class="str">"cmt">//--- Calculate a point in the future in screen coordinates
  if(COUNT_IN_BARS == _count_type) class=class="str">"cmt">// If the length is specified in bars,
    {
    class=class="str">"cmt">// then the interval size should be converted to pixels.
    future_x = current_x + _length*bar_distance;
    }
  else class=class="str">"cmt">// ... If the length is in pixels,
    {
    class=class="str">"cmt">// use it as is
    future_x = current_x + _length;
    }
class=class="str">"cmt">//--- Convert screen coordinates into time
  if(ChartGetInteger(class="num">0,CHART_WIDTH_IN_PIXELS)>=future_x) class=class="str">"cmt">// If successful,
    {

越界时的时间回退算法

当预测坐标超出图表可视范围,ChartXYToTimePrice 无法换算时间,代码转而用秒数硬算:以起始时间 _start_time 为基准,加上区间长度乘当前周期秒数 PeriodSeconds()。若按 K 线数计数(COUNT_IN_BARS == _count_type),加 _length 根 K 线的时间;若按像素距离,则除 bar_distance 后再乘周期秒数。 辅助函数 GetBarsPixelDistance 直接返回 2 的 CHART_SCALE 次方作为相邻两根 K 线的像素间距。比如缩放级别 4 时,两根 bar 相隔 16 像素;缩放 5 则是 32 像素。这个值在越界分支里被用来把像素投影还原成 bar 数。 开 MT5 把这段塞进 EA 的 utility 头文件,改 _length 和 CHART_SCALE 就能看到预测线在边界处的回退行为。外汇与贵金属杠杆高,这类坐标推算仅作界面辅助,不代表任何方向判断。

MQL5 / C++
  ChartXYToTimePrice(class="num">0,future_x,current_y,subwindow,future_time,current_price);  class=class="str">"cmt">// convert the resulting value
   }
  else class=class="str">"cmt">// Otherwise, if time cannot be calculated because it exceeds limits
   {
   future_time =       class=class="str">"cmt">// Calculate time as usual, in seconds
     _start_time       class=class="str">"cmt">// To the starting time
     +(               class=class="str">"cmt">// add
       ((COUNT_IN_BARS == _count_type) ? _length : _length/bar_distance) class=class="str">"cmt">// interval size in bars
       *PeriodSeconds()   class=class="str">"cmt">// multiplied by the number of seconds in the current period
     );
   }
class=class="str">"cmt">//--- Return the resulting value
   class="kw">return future_time;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                     Utilites.mqh |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//...
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Calculates a distance in pixels between two adjacent bars        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int       CUtilites::GetBarsPixelDistance(class="type">void)
  {
class=class="str">"cmt">//--- Calculate the distance
   class="kw">return ((class="type">int)MathPow(class="num">2,ChartGetInteger(class="num">0,CHART_SCALE)));
  }

「用像素锁死水平线的视觉长度」

画水平级别时,第一点跟着鼠标走,第二点怎么算才是关键。程序先判断线段长度要不要随图表缩放变,再按像素算出坐标,最后反推回价格和时间。这样在不同周期下,线覆盖的烛条数会自然变化,但屏幕上的物理长度可控。 参数 Short_Level_Length_In_Pixels 决定度量方式:为 true 时,按 Short_Level_Length_Pix 里的像素值画短线段,按 S 键触发;为 false 时,长度以烛条数计,取 Short_Level_Length。按 L 键则长度乘 Long_Level_Multiplicator,得到扩展线。 下面这段来自 Graphics.mqh 的绘制函数,把两种长度逻辑写得很直白。注意 level_length 在像素模式下直接用参数,非像素模式要乘上每根烛条的像素距 GetBarsPixelDistance(),外汇和贵金属图表缩放频繁,这种换算偏差可能让级别对不准你想标的价格。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Graphics.mqh |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draws a horizontal level                                          |
class=class="str">"cmt">//| Parameters:                                                       |
class=class="str">"cmt">//|   _multiplicator - multiplier for determining the length          |
class=class="str">"cmt">//|                  of the larger level(how many times higher)      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//---
class="type">void CGraphics::DrawHorizontalLevel(
   class="type">class="kw">double _multiplicator class=class="str">"cmt">// Multiplier for the level length
  )
  {
class=class="str">"cmt">//--- Description of variables
   class="type">class="kw">datetime p2_time;           class=class="str">"cmt">// Time of point class="num">2
   class="type">class="kw">string Level_Name ="";      class=class="str">"cmt">// Level name
class=class="str">"cmt">//Color of the current line(equal to  the general class="type">color of the current time interval)
   class="type">color Level_Color=CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes());
   class="type">int window = class="num">0;             class=class="str">"cmt">// The index of the subwindow in which the line is drawn
   ENUM_LINE_STYLE Current_Style = STYLE_SOLID; class=class="str">"cmt">// Line style
   class="type">int Current_Width=class="num">1;                                  class=class="str">"cmt">// Line width
   class="type">int level_length = class="num">0;                                 class=class="str">"cmt">// Line length
class=class="str">"cmt">//--- Get the length(in pixels)
   if(Short_Level_Length_In_Pixels)
     {
     class=class="str">"cmt">// If EA parameters instruct to measure in pixels,
     level_length = Short_Level_Length_Pix; class=class="str">"cmt">// ...Use the length from parameters
     }
   else
     {
     class=class="str">"cmt">// Otherwise the number of candlesticks is specified in parameters
     level_length = Short_Level_Length * CUtilites::GetBarsPixelDistance();
     }
class=class="str">"cmt">//--- Set level parameters
   if(_multiplicator>class="num">1) class=class="str">"cmt">// If the level is extended
     {
     Level_Name = CUtilites::GetCurrentObjectName(allPrefixes[class="num">7]);
     Current_Style = Long_Level_Style;
     Current_Width = Long_Level_Width;
     }
   else                 class=class="str">"cmt">// An if the level is class="type">class="kw">short
     {
     Level_Name = CUtilites::GetCurrentObjectName(allPrefixes[class="num">6]);
     Current_Style = Short_Level_Style;
     Current_Width = Short_Level_Width;

◍ 用鼠标坐标画趋势线的收尾逻辑

在鼠标事件回调的末尾,先通过 CUtilites::GetTimeInFuture 把像素长度换算成未来时间坐标,再调用 TrendCreate 以鼠标所在时间、价格为起止点画出水平趋势线。 代码里 level_length*_multiplicator 决定线向右延伸的像素倍数,COUNT_IN_PIXELS 标志位说明这是按屏幕像素而非竹节数计算。改 _multiplicator 能直接改变画线长度,开 MT5 挂上 EA 按快捷键就能看到差异。 Short_Level_Key 与 Long_Level_Key 分别对应短、长水平位的快捷键,DrawHorizontalLevel 传入 1 或 Long_Level_Multiplicator 控制延伸倍数。外汇与贵金属波动剧烈,这类手动画线仅作参考,实际进出场仍需结合风控。

MQL5 / C++
   }
class=class="str">"cmt">//--- Calculate real coordinates(price and time) for the second point
   p2_time = CUtilites::GetTimeInFuture(CMouse::Time(),level_length*_multiplicator,COUNT_IN_PIXELS);
class=class="str">"cmt">//--- Draw a line using the known coordinates
   TrendCreate(class="num">0,
               Level_Name,
               class="num">0,
               CMouse::Time(),
               CMouse::Price(),
               p2_time,
               CMouse::Price(),
               Level_Color,
               Current_Style,
               Current_Width
               );
class=class="str">"cmt">//---

   ChartRedraw(class="num">0);
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Shortcuts.mqh |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// ...
class=class="str">"cmt">//--- Draw a class="type">class="kw">short limited level
         if(CUtilites::GetCurrentOperationChar(Short_Level_Key) == lparam)
         {
            m_graphics.DrawHorizontalLevel(class="num">1);
         }
class=class="str">"cmt">//--- Draw an extended limited level
         if(CUtilites::GetCurrentOperationChar(Long_Level_Key) == lparam)
         {
            m_graphics.DrawHorizontalLevel(Long_Level_Multiplicator);
         }
class=class="str">"cmt">// ...

把趋势线改成带时间边界的估价通道

对角线趋势线在MT5上手绘时默认是射线,会无限延伸,反而掩盖了「价格×时间」双重约束。把它改成受两端极值柱线限制的线段后,既能看速率边界,也能当级别参照,比矩形更直观。 修改后的 DrawTrendLine 不再只连两点,而是按 (p2-p1)*Trend_Length_Coefficient 把线段向未来延长,系数在EA参数里配。柱线编号向右递增,所以两点间柱数直接是 p1-p2 的差值,无需额外数K线。 方向判定藏在 price1 + (tmp_price - price1) 里:线向下时 price1 高于第二点,括号为负,距离从价格里扣;线向上时括号为正,距离加上去。初学者常问加减谁定,其实就是这个表达式唯一决定,不用管连的是高点还是低点——开头已做过检查。 算完价格必须用 NormalizeDouble 规范小数位,否则和图表报价位数不一致会画错。Shortcuts.mqh 不用动,按 T 键调这个函数就能画。外汇和贵金属波动大,这类自制线只是辅助,实盘仍要防假突破。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  Graphics.mqh |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draws a trend line using two nearest extreme points.              |
class=class="str">"cmt">//|   Extremum length(number of bars on left and right) is set      |
class=class="str">"cmt">//|   by parameters Fractal_Size_Left and Fractal_Size_Right         |
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//| There is a "Trend_Points" variable in the global parameters.     |
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//| If the variable value is equal to "TREND_DOTS_EXTREMUMS",        |
class=class="str">"cmt">//|   end points of the straight line will lie strictly at extrema.  |
class=class="str">"cmt">//| If the values is "TREND_DOTS_HALF", the line will be            |
class=class="str">"cmt">//|   extended into the future by a distance of                     |
class=class="str">"cmt">//|   (p2-p1)*Trend_Length_Coefficient                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void                CGraphics::DrawTrendLine(class="type">void)
  {
   class="type">int dropped_bar_number=CMouse::Bar(); class=class="str">"cmt">// candlestick number under the mouse
   class="type">int p1=class="num">0,p2=class="num">0;                       class=class="str">"cmt">// numbers of the first and seconds points
   class="type">class="kw">string trend_name =                 class=class="str">"cmt">// trend line name
      CUtilites::GetCurrentObjectName(allPrefixes[class="num">0],OBJ_TREND);
   class="type">class="kw">double
      price1=class="num">0,   class=class="str">"cmt">// price of the first point
      price2=class="num">0,   class=class="str">"cmt">// price of the second point
      tmp_price;  class=class="str">"cmt">// variable for temporary storing of the price
   class="type">class="kw">datetime
      time1=class="num">0,    class=class="str">"cmt">// time of the first point
      time2=class="num">0,    class=class="str">"cmt">// time of the second point
      tmp_time;   class=class="str">"cmt">// a variable to store time

「鼠标位置决定趋势线的极值点取法」

在 MT5 里用鼠标交互画趋势线时,光标相对 K 线的位置直接决定取的是 Lower Low 还是 Higher High。如果光标落在某根 K 线最低价下方,脚本会调用 SetExtremumsBarsNumbers(false, p1, p2) 找两根向下的极值棒,再用 iLow 取对应低价;若光标在最高价上方,则走 true 分支取 iHigh,二者逻辑对称。 当 Trend_Points 设为 TREND_POINTS_HALF 时,第二点不直接用历史极值,而是按 (p1-p2)*Trend_Length_Coefficient 把时间推到未来,价格按相同系数从 point1 向原 point2 延伸。这样画出来的是半射线,并顺手补一条 OBJ_HLINE 与 OBJ_VLINE 做边界参照。 最后 TrendCreate 用 GetAllLowerTimeframes 对应颜色绘线,ChartRedraw(0) 强制重绘。外汇与贵金属杠杆高,这类交互画线只帮你定位结构,进出场仍要结合实时波动判断,亏损概率不低。

MQL5 / C++
class=class="str">"cmt">//--- Setting initial parameters
  if(CMouse::Below()) class=class="str">"cmt">// If a mouse cursor is below the candlestick Low
    {
      class=class="str">"cmt">//--- Find two extreme points below
      CUtilites::SetExtremumsBarsNumbers(class="kw">false,p1,p2);
      class=class="str">"cmt">//--- Determine point prices by Low
      price1=iLow(Symbol(),PERIOD_CURRENT,p1);
      price2=iLow(Symbol(),PERIOD_CURRENT,p2);
    }
  else class=class="str">"cmt">// otherwise
    if(CMouse::Above()) class=class="str">"cmt">// If a mouse cursor is below the candlestick High
      {
       class=class="str">"cmt">//--- Find two extreme points above
       CUtilites::SetExtremumsBarsNumbers(true,p1,p2);
       class=class="str">"cmt">//--- Determine point prices by High
       price1=iHigh(Symbol(),PERIOD_CURRENT,p1);
       price2=iHigh(Symbol(),PERIOD_CURRENT,p2);
      }
      else
        {
         class="kw">return;
        }
class=class="str">"cmt">//--- The time of the first and second points does not depend on the direction
  time1=iTime(Symbol(),PERIOD_CURRENT,p1);
  time2=iTime(Symbol(),PERIOD_CURRENT,p2);
class=class="str">"cmt">//--- If the line should be extended to the right
  if(Trend_Points == TREND_POINTS_HALF)
    {
      class=class="str">"cmt">//--- Temporarily save the coordinates of point class="num">2
      tmp_price = price2;
      tmp_time = time2;
      class=class="str">"cmt">//--- Calculate the time of the second point
      time2 = CUtilites::GetTimeInFuture(time1,(p1-p2)*Trend_Length_Coefficient);
      class=class="str">"cmt">//--- Calculate the price of the second point
      price2 = NormalizeDouble(price1 + (tmp_price - price1)*Trend_Length_Coefficient,Digits());
      class=class="str">"cmt">//--- Draw boundary levels by price and time
      DrawSimple(OBJ_HLINE,time2,price2);
      DrawSimple(OBJ_VLINE,time2,price2);
    }
class=class="str">"cmt">//--- Draw the line
  TrendCreate(class="num">0,trend_name,class="num">0,
             time1,price1,time2,price2,
             CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()),
             class="num">0,Trend_Line_Width,class="kw">false,true,m_Is_Trend_Ray
             );
class=class="str">"cmt">//--- Redrawing the chart
  ChartRedraw(class="num">0);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Shortcuts.mqh |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//...
class=class="str">"cmt">//--- Draw a trend line
           if(CUtilites::GetCurrentOperationChar(Trend_Line_Key) == lparam)
             {
              m_graphics.DrawTrendLine();
             }
class=class="str">"cmt">//...

◍ 用 7/8 和 14/8 画垂直测量位

价格突破尖峰柱或较大烛条边缘后,后续移动距离常与所测柱线高度相近,随后反转的概率不低;但不少能判断方向的大户会在到达 100% 测量位前就平仓,所以期待值经常落空。实战里更常用的局部位是 7/8,也就是取刚过去柱线高度的 7/8 处作为参考反转区。 这套绘制逻辑有两个硬约束。级别的时间跨度永远以柱线根数计,根数来自 Short_Level_Length 变量,你改这个数就能控制向右延伸多远;价格计算只锚定一个基点,方向由 direction 参数决定,鼠标在烛身上方或下方会自动切换正负号,避免写两套重复代码。 下面这段加到 Shortcuts.mqh,键盘绑 V 即可触发。它先抓当前柱的 High/Low,线长设为 (High-Low)*2,再按鼠标位置定 direction:上方则取 High 且 direction=-1 向下画,下方反之。7/8 级别名由前缀拼出,右端时间用 GetTimeInFuture 按 Short_Level_Length 推。外汇与贵金属波动剧烈,这类测量位仅作概率参考,实盘须自担高风险。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Graphics.mqh |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Draws a vertical line at levels class="num">7/class="num">8 and class="num">14/class="num">8 of the             |
class=class="str">"cmt">//|   current candlestick size                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CGraphics::DrawVerticalLevels(class="type">void)
  {
class=class="str">"cmt">//--- Description of variables
   class="type">class="kw">string
   Current_Vertical_Name =   class=class="str">"cmt">// The name of the basic vertical line
     CUtilites::GetCurrentObjectName(allPrefixes[class="num">5]),
   Current_Level_Name =       class=class="str">"cmt">// The name of the current level
     CUtilites::GetCurrentObjectName(allPrefixes[class="num">5]+"7_8_");
   class="type">class="kw">double
   Current_Line_Lenth,        class=class="str">"cmt">// The length of the current line(level or vertical)
   Current_Extremum,          class=class="str">"cmt">// Working extremum(High or Low, depending on the mouse position
   Level_Price,               class=class="str">"cmt">// Level price
   High = iHigh(Symbol(),PERIOD_CURRENT,CMouse::Bar()), class=class="str">"cmt">// The High price of the current candlestick
   Low =  iLow(Symbol(),PERIOD_CURRENT,CMouse::Bar());  class=class="str">"cmt">// The Low price of the current candlestick
   class="type">int
   direction=class="num">0;               class=class="str">"cmt">// Price increment sign
   class="type">long timeframes;           class=class="str">"cmt">// List of working timeframes
   class="type">class="kw">datetime
   Current_Date =             class=class="str">"cmt">// Time of the current bar
     iTime(Symbol(),PERIOD_CURRENT,CMouse::Bar()),
   Right_End_Time =           class=class="str">"cmt">// Time of the right border of the level
     CUtilites::GetTimeInFuture(Current_Date,Short_Level_Length);
class=class="str">"cmt">//--- Calculating candlestick length
   Current_Line_Lenth = (High-Low)*class="num">2;
class=class="str">"cmt">//--- Initialization of the main variables depending on the desired drawing direction
   if(CMouse::Above()) class=class="str">"cmt">// If the mouse is above
     {
     Current_Extremum = High;  class=class="str">"cmt">// The main price is High
     direction = -class="num">1;           class=class="str">"cmt">// Drawing direction - downward
     }
   else                class=class="str">"cmt">// Otherwise
     {

鼠标位决定画线与方向

在 MT5 自定义指标里,用 CMouse::Below() 判断鼠标相对 K 线的位置,能直接决定极值引用和画线方向。鼠标在 K 线下方时,把 Low 作为 Current_Extremum,direction 置 1 表示向上画;鼠标落在实体中部则直接 return,不画任何对象。 垂直主线靠 TrendCreate() 生成,终点价格算法是 Current_Extremum + Current_Line_Lenth*2*direction,direction 为 1 时线向上延伸、为 -1 时向下。颜色取 CUtilites::GetTimeFrameColor(GetAllLowerTimeframes()),即关联所有下层周期的统一色,样式宽度由 Vertical_With_Short_Levels_Style / Width 控制。 两条短水平位按 7/8 与 14/8 系数铺:第一级 Level_Price = Extremum + Line_Lenth*Coefficient*direction,第二级再乘 2。实测把 Vertical_Short_Level_Coefficient 从 0.5 调到 1.0,EURUSD 十五分钟图上两线间距会拉宽约一倍,扫到小波动的概率倾向降低。外汇与贵金属杠杆高,这类辅助线仅作结构参考,不代表方向确认。

MQL5 / C++
if(CMouse::Below()) class=class="str">"cmt">// If the mouse is below
  {
   Current_Extremum = Low; class=class="str">"cmt">// The main price is Low
   direction = class="num">1; class=class="str">"cmt">// Drawing direction is upward
  }
else class=class="str">"cmt">// If the mouse is in the middle of the candlestick, exit
  {
   class="kw">return;
  }
class=class="str">"cmt">//--- Vertical line
 TrendCreate(class="num">0,
             Current_Vertical_Name,
             class="num">0,
             Current_Date,
             Current_Extremum,
             Current_Date,
             Current_Extremum+(Current_Line_Lenth*class="num">2)*direction,
             CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()),
             Vertical_With_Short_Levels_Style,
             Vertical_With_Short_Levels_Width
             );
class=class="str">"cmt">//--- First level(class="num">7/class="num">8)
 Level_Price = Current_Extremum+(Current_Line_Lenth*Vertical_Short_Level_Coefficient)*direction;
 TrendCreate(class="num">0,
             Current_Level_Name,
             class="num">0,
             Current_Date,
             Level_Price,
             Right_End_Time,
             Level_Price,
             CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()),
             Short_Level_7_8_Style,
             Short_Level_7_8_Width
             );
class=class="str">"cmt">//--- Second level(class="num">14/class="num">8)
 Current_Level_Name = CUtilites::GetCurrentObjectName(allPrefixes[class="num">5]+"14_8_");
 Level_Price = Current_Extremum+(Current_Line_Lenth*class="num">2*Vertical_Short_Level_Coefficient)*direction;
 TrendCreate(class="num">0,
             Current_Level_Name,
             class="num">0,
             Current_Date,
             Level_Price,
             Right_End_Time,
             Level_Price,
             CUtilites::GetTimeFrameColor(CUtilites::GetAllLowerTimeframes()),
             Short_Level_14_8_Style,
             Short_Level_14_8_Width
             );

「用快捷键触发垂直价位线重绘」

在 MT5 自定义指标或 EA 里,可以把垂直价位线的绘制动作绑定到特定按键消息上,避免每次手动调图。 上面这段逻辑判断当前 ChartEvent 的 lparam 是否等于预设的快捷键标识 Vertical_With_Short_Levels_Key,若匹配就调用 DrawVerticalLevels() 重画短周期水平位对应的竖线。 实际验证时,把 Vertical_With_Short_Levels_Key 设成你常用的组合键值,在 EURUSD 或 XAUUSD 图表上按对应键,可能看到竖线即时刷新;外汇与贵金属波动剧烈,这类辅助线仅作参考,不构成方向判断。

MQL5 / C++
     if(CUtilites::GetCurrentOperationChar(Vertical_With_Short_Levels_Key)  == lparam)
       {
         m_graphics.DrawVerticalLevels();
       }
      break;

◍ 键盘映射直接驱动图表对象

当前函数库把一套单键指令写死在事件回调里,交易者不用点面板,按键即生成对应图形对象。下表是已实现的映射:U/D 切换主时间帧(取自 TFs 面板),Z 切换图表 Z 序,T 基于鼠标最近两个单向极值拉坡度趋势线,R 切射线模式,I 画仅可见垂线,H 画水平线,P 画安德鲁草叉,F 画斐波那契扇形,S 短水平位,L 扩展水平位,V 带标记垂线。 这套映射在 MT5 里属于低延迟交互,适合手动结合价格行为快速标注。外汇与贵金属波动剧烈、杠杆高风险,按键生成的对象仅作参考,不预示方向。 验证方式:把库载入 EA 并挂图,鼠标移到极值附近按 T,应立刻出现坡度线;按 R 后同一条线尾端变为射线延伸。若没反应,先查图表事件捕获是否开启。

画得少,看得清

这套快捷标注工具包默认参数在 MT5 里就能直接跑,但行情结构一变,老画法可能慢慢失真——这不是 bug,是价格行为本身在漂移。 想验证很简单:把 Shortcuts_v2.01 的 MQL5 文件夹丢进数据目录(文件→打开数据文件夹),编译 Experts\Shortcuts\Shortcuts.mq5 主文件,挂上图表就能用手动快捷键拉线。改了 GlobalVariables 里的颜色却没生效,多半是只编译了子文件,主文件不重编不会吸改动。 外汇和贵金属波动大、杠杆高,这类辅助工具只帮你少画冤枉线,不替你判方向。真要顺手,得自己调默认参数去适配品种节律。

常见问题

用循环遍历图表上的斐波那契对象,按水平位数值自动写入对应层级标签;可复制绘图工具包里的批量标注脚本直接跑。
在鼠标事件里读光标坐标相对基准点的左右位置,据此切换扇形起止点顺序,避免画反。
可以,小布能识别三极值点并套用三型 pitchfork 画法生成同屏草叉,省去手动切换基线的麻烦。
用快捷键结合装饰设置面板统一改样式,或在工具包里绑定一键美化宏,落点即生效。
按高点-低点-回抽点顺序取三极值,分别赋给中线、上基线、下基线,同屏时锁住各自基准免互相覆盖。