开发回放系统(第30部分):EA交易项目C_Mouse类(四)·综合运用
📘

开发回放系统(第30部分):EA交易项目C_Mouse类(四)·综合运用

第 3/3 篇

◍ 鼠标拖拽测距时的扩展斐波那契绘制逻辑

当鼠标事件触发且 mouse.GetInfoMouse().ExecStudy 为真时,脚本进入测量态:若本地 m_Info.ExecStudy 尚未置位就先 CreateStudy() 建对象,随后用当前鼠标价与记忆价算出价差 v1 和百分比偏离 v2v2 的公式 MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0)) 给出的是相对记忆价的回撤/扩张幅度,例如记忆价 1.1000、鼠标价 1.1110 时,v2 约为 1.00%。这个数值会拼进 sz1 文本并通过 GetDimensionText 拿到宽高 wh。 对象属性写入很直白:扩展斐波那契的价、时间锚到鼠标位置,颜色按 v1 正负在 corN/corP 间切换;按钮文本、背景色、尺寸与坐标偏移都依鼠标点修正,负向价差时 Y 偏移取 1 而非 h。 鼠标测量结束分支里,若 m_Info.ExecStudy 曾为真则隐藏斐波那契、删按钮,按 ClearStudy 决定是否连斐波那契也删掉,最后把 ExecStudy 复位。v1v2 在块外又算了一遍,说明这两行是后续标签刷新的公共前置。 别把正负色当方向确认 颜色只是 v1 符号的视觉映射,不代表行情必然延续;外汇与贵金属杠杆高,鼠标测距仅辅助读图,下单前仍要以实盘结构为准。

MQL5 / C++
if (mouse.GetInfoMouse().ExecStudy)
{
   if (!m_Info.ExecStudy) CreateStudy();
   v1 = def_InfoMousePos.Price - m_Info.MemPrice;
   v2 = MathAbs(class="num">100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * class="num">100.0));
   sz1 = StringFormat(" %." + (class="type">class="kw">string)def_InfoTerminal.nDigits + "f [ %d ] %class="num">02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - class="num">1, v2);
   GetDimensionText(sz1, w, h);
   ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, class="num">0, def_InfoMousePos.Price);
   ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, class="num">0, def_InfoMousePos.dt);
   ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, (v1 < class="num">0 ? m_Info.corN : m_Info.corP));
   ObjectSetString(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_TEXT, sz1);
   ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_BGCOLOR, (v1 < class="num">0 ? m_Info.corN : m_Info.corP));
   ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XSIZE, w);
   ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YSIZE, h);
   ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XDISTANCE, def_InfoMousePos.X - w);
   ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, def_InfoMousePos.Y - (v1 < class="num">0 ? class="num">1 : h));
}
else if (m_Info.ExecStudy)
{
   ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, clrNONE);
   ObjectDelete(def_InfoTerminal.ID, def_ExpansionBtn1);
   if (m_Info.ClearStudy) ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
   m_Info.ExecStudy = class="kw">false;
}
break;
}
}
v1 = def_InfoMousePos.Price - m_Info.MemPrice;
v2 = MathAbs(class="num">100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * class="num">100.0));

「把鼠标位置换算成带小数位的K线序号」

在 MT5 的鼠标交互面板里,常需要把光标对应的时间反推成「距当前第几根 K 线」并附带价格精度。下面这段拼接逻辑直接吃终端的小数位参数 nDigits,避免黄金(XAUUSD)这类 2 位或 5 位报价品种显示错位。 代码先取鼠标时间 m_Info.MemDT 与当前品种,用 Bars() 算从记忆时间到光标时间之间有多少根 K 线,再减 1 得到相对索引;同时用 MathAbs 取 v1 绝对值并按 nDigits 格式化,v2 以百分比两位小数附在末尾。外汇与贵金属杠杆高、滑点大,这类界面数值仅作定位参考,不构成方向判断。 实盘验证时把 def_InfoTerminal.nDigits 打印出来:EURUSD 通常 5,XAUUSD 常见 2 或 5,若硬编码 '.2f' 会在 5 位品种上丢精度。

MQL5 / C++
sz1 = StringFormat(" %." + (class="type">class="kw">string)def_InfoTerminal.nDigits + "f [ %d ] %class="num">02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - class="num">1, v2);

把继承层次当成你的代码保险箱

前面几节铺开的思路,落到实处的做法就是:别急着删改老代码,用类的继承把新功能挂上去。MT5 里很多被认为「做不到」的事,瓶颈不在平台,而在写代码的人没试过用层次结构去扩展。 你可以打开本文附带的 Files_-_FOREX.zip(3743.96 KB)和 Market_Replay_-_30.zip(60.95 KB),里面是已验证可用的完整工程。复制一份到 MT5 的 MQL5 目录,编译跑通后,试着给现有类加一个子类,不碰父类源码——这种改法在外汇、贵金属这类高波动品种上尤其稳,不会因为小改动搞崩整套回放逻辑。 下一部分作者打算把部分逻辑收进 C_Mouse 类,若真合进去了,前文其实都已拆过代码,你直接对照着看差异就行。外汇与贵金属杠杆风险极高,任何代码策略都只是概率优势,实盘前务必在策略测试器里跑够样本。

常见问题

在拖拽结束事件里读取起止坐标,换算成价格与K线序号后调用绘制函数,按扩展斐波那契比例数组逐条画线即可。
用鼠标X坐标减去图表左侧偏移再除以单根K线像素宽,得到的值直接保留小数,不必取整。
可以,小布能读取你当前图表上的对象数据并比对比例与坐标换算结果,标出异常线段。
继承层次把通用方法和专用逻辑分开,改一处不影响其他功能,相当于给代码上了保险箱。
有影响,忽略小数会让起止点偏移半根K线以上,保留小数能明显提升测距和斐波那契贴合度。