开发回放系统(第30部分):EA交易项目C_Mouse类(四)·综合运用
◍ 鼠标拖拽测距时的扩展斐波那契绘制逻辑
当鼠标事件触发且 mouse.GetInfoMouse().ExecStudy 为真时,脚本进入测量态:若本地 m_Info.ExecStudy 尚未置位就先 CreateStudy() 建对象,随后用当前鼠标价与记忆价算出价差 v1 和百分比偏离 v2。
v2 的公式 MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0)) 给出的是相对记忆价的回撤/扩张幅度,例如记忆价 1.1000、鼠标价 1.1110 时,v2 约为 1.00%。这个数值会拼进 sz1 文本并通过 GetDimensionText 拿到宽高 w、h。
对象属性写入很直白:扩展斐波那契的价、时间锚到鼠标位置,颜色按 v1 正负在 corN/corP 间切换;按钮文本、背景色、尺寸与坐标偏移都依鼠标点修正,负向价差时 Y 偏移取 1 而非 h。
鼠标测量结束分支里,若 m_Info.ExecStudy 曾为真则隐藏斐波那契、删按钮,按 ClearStudy 决定是否连斐波那契也删掉,最后把 ExecStudy 复位。v1、v2 在块外又算了一遍,说明这两行是后续标签刷新的公共前置。
别把正负色当方向确认
颜色只是 v1 符号的视觉映射,不代表行情必然延续;外汇与贵金属杠杆高,鼠标测距仅辅助读图,下单前仍要以实盘结构为准。
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 位品种上丢精度。
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 类,若真合进去了,前文其实都已拆过代码,你直接对照着看差异就行。外汇与贵金属杠杆风险极高,任何代码策略都只是概率优势,实盘前务必在策略测试器里跑够样本。