手工图表和交易工具包(第二部分)。 图表图形绘图工具·综合运用
- 给斐波那契对象批量写层级标注
- 用鼠标位置决定斐波那契扇形取点方向
- 斐波那契对象的装饰与快捷键绘制落点
- 三类草叉同屏的画法逻辑
- 一套三型 pitchfork 的鼠标取点逻辑
- 三极值点定位与三种叉形基线赋值
- 用三点坐标驱动三种叉形线
- 用按键事件触发安德鲁音叉重绘
- 未来端点的趋势线在 MT5 里怎么画才不塌
- 按像素或柱线推算未来时间的工具函数
- 把未来时间换算成屏幕坐标的两种量法
- 越界时的时间回退算法
- 用像素锁死水平线的视觉长度
- 用鼠标坐标画趋势线的收尾逻辑
- 把趋势线改成带时间边界的估价通道
- 鼠标位置决定趋势线的极值点取法
- 用 7/8 和 14/8 画垂直测量位
- 鼠标位决定画线与方向
- 用快捷键触发垂直价位线重绘
- 键盘映射直接驱动图表对象
- 画得少,看得清
「给斐波那契对象批量写层级标注」
在 MT5 里手动给斐波那契工具每层改文字很烦,尤其画完扇形或回撤后层级一多就容易漏。把标注逻辑收进一个图形类,用循环统一写 OBJPROP_LEVELTEXT,能省掉重复操作。 下面这段类方法 SetFiboDescriptions 接收对象名和描述数组,先取真实层级数 levels_count,再比对数组成员的 array_size。循环里若数组还有对应项就写进去,不够就留空串,最后 ChartRedraw(0) 刷新。 实盘注意:外汇与贵金属杠杆高、滑点大,这类辅助绘图不改变信号本身,仅提升读图效率,任何标注都不构成方向保证,行情可能反向。
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) 把水平比例写进对象。外汇与贵金属波动剧烈,这类自动画线仅作辅助参考,实际进出场仍需结合风控。
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 可能渲染上一帧的半成品。外汇与贵金属图表叠加多周期工具属高风险操作,参数误设会导致视觉误导。
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:
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; // 不画,退出 } }
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 上第三点是否从近端摆动顶变为更早的高点——这直接改变整条中轨斜率。
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 上直接验证三种叉是否按预期生成。
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 默认快捷键冲突;外汇与贵金属波动剧烈、杠杆高风险,音叉仅作通道概率参考,不代表方向确定性。
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,以及可选的计数类型(默认按柱线)。若按像素算失败、时间超出范围,函数会退化为按日期累加并跳过周日的近似结果。外汇与贵金属杠杆高,这类时间推算只用于辅助定位,实际信号须结合行情确认。
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,射线落点会明显偏移,建议每次重绘前重取。
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 就能看到预测线在边界处的回退行为。外汇与贵金属杠杆高,这类坐标推算仅作界面辅助,不代表任何方向判断。
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(),外汇和贵金属图表缩放频繁,这种换算偏差可能让级别对不准你想标的价格。
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 控制延伸倍数。外汇与贵金属波动剧烈,这类手动画线仅作参考,实际进出场仍需结合风控。
} 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 键调这个函数就能画。外汇和贵金属波动大,这类自制线只是辅助,实盘仍要防假突破。
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) 强制重绘。外汇与贵金属杠杆高,这类交互画线只帮你定位结构,进出场仍要结合实时波动判断,亏损概率不低。
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 推。外汇与贵金属波动剧烈,这类测量位仅作概率参考,实盘须自担高风险。
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 十五分钟图上两线间距会拉宽约一倍,扫到小波动的概率倾向降低。外汇与贵金属杠杆高,这类辅助线仅作结构参考,不代表方向确认。
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 图表上按对应键,可能看到竖线即时刷新;外汇与贵金属波动剧烈,这类辅助线仅作参考,不构成方向判断。
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 里的颜色却没生效,多半是只编译了子文件,主文件不重编不会吸改动。 外汇和贵金属波动大、杠杆高,这类辅助工具只帮你少画冤枉线,不替你判方向。真要顺手,得自己调默认参数去适配品种节律。