从头开始开发智能交易系统(第 20 部分):新订单系统 (III)·综合运用
(3/3)·从对象规划到图表交互,弄懂MetaTrader 5里每笔订单背后那15个对象的来龙去脉
◍ 挂单与止损对象的图形着色和清理
在 MT5 自定义指标里给交易对象画图形时,先按类型分颜色:止损单(IT_STOP)用珊瑚红配栗色,挂单(IT_PENDING)及缺省情况用金色配暗金菊色,这样肉眼扫图就能区分订单性质。 代码片段里用 m_TradeLine.Create 按 MountName 生成边界线,若 ticket 等于 def_IndicatorTicket0 还会调用 SpotLight 高亮挂单线;背景块 m_BackGround 的尺寸随类型变:止盈止损(IT_TAKE/IT_STOP)是 92×22,挂单是 110×22,多出的宽度给文字留空间。 删除时用宏 macroDelete 一口气清掉背景和线两个对象,再在 RemoveIndicatorTrade 里关掉 CHART_EVENT_OBJECT_DELETE 避免误触发。外汇和贵金属市场波动剧烈,这类图形对象只是辅助,实际成交风险依旧很高。 开 MT5 把这段塞进你的指标 EA,改 IT_PENDING 的 clrGold 为别的色值,就能验证挂单高亮是否按预期显示。
case IT_STOP: cor1 = clrCoral; cor2 = clrMaroon; class="kw">break; case IT_PENDING: class="kw">default: cor1 = clrGold; cor2 = clrDarkGoldenrod; class="kw">break; } m_TradeLine.Create(MountName(ticket, it, EV_LINE), cor2); if (ticket == def_IndicatorTicket0) m_TradeLine.SpotLight(MountName(ticket, IT_PENDING, EV_LINE)); m_BackGround.Create(sz0 = MountName(ticket, it, EV_GROUND), cor1); class="kw">switch (it) { case IT_TAKE: case IT_STOP: m_BackGround.Size(sz0, class="num">92, class="num">22); class="kw">break; case IT_PENDING: m_BackGround.Size(sz0, class="num">110, class="num">22); class="kw">break; } } class="macro">#define macroDelete(A) { \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND)); \ ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE)); \ } class="kw">inline class="type">void RemoveIndicatorTrade(class="type">class="kw">ulong ticket, eIndicatorTrade it = IT_NULL) { ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, class="kw">false);
「挂单与指示器的清理和坐标轴重定位」
这段逻辑处理交易对象删除时的图形清理:若当前指示器指针有效且不是挂单占位(IT_PENDING)或结果占位(IT_RESULT),就直接删掉它;否则把挂单、结果、止盈(IT_TAKE)、止损(IT_STOP)四类占位全部 macroDelete 清掉,再打开图表的对象删除事件开关。 清理完之后用宏重设坐标轴。macroSetAxleY(A) 把名为 MountName(ticket, A, EV_GROUND) 的背景对象和 EV_LINE 类型的交易线同时移到纵坐标 y;macroSetAxleX(A, B) 则把同类两对象的横坐标挪到 B。这样订单图形和背景锚点始终同步,不会在拖拽时脱节。 PositionAxlePrice 函数接收 price、ticket、交易类型 it 以及止盈止损点数、杠杆、买卖方向,是上述宏的实际调用入口。外汇与贵金属杠杆波动剧烈,重绘坐标时若 ticket 对应的挂单已被部分成交,可能触发 IT_PENDING 残留,建议开 MT5 在策略测试器里用 EURUSD 1 分钟数据跑一遍删除流程验证。
if ((it != NULL) && (it != IT_PENDING) && (it != IT_RESULT)) macroDelete(it) else { macroDelete(IT_PENDING); macroDelete(IT_RESULT); macroDelete(IT_TAKE); macroDelete(IT_STOP); } ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true); class="macro">#undef macroDelete class="macro">#define macroSetAxleY(A) { \ m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND), y); \ m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE), y); \ } class="macro">#define macroSetAxleX(A, B) { \ m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND), B); \ m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE), B); \ } class="kw">inline class="type">void PositionAxlePrice(class="type">class="kw">double price, class="type">class="kw">ulong ticket, eIndicatorTrade it, class="type">int FinanceTake, class="type">int FinanceStop, class="type">int Leverange, class="type">bool isBuy) { class="type">class="kw">double ad;
挂单止盈止损的画布坐标换算
在 MT5 自定义指标或 EA 面板里,把挂单的触发价、止盈价、止损价映射到屏幕坐标,靠的是 ChartTimePriceToXY 这个函数。它接收图表 ID、时间(这里传 0 表示用当前最右可见 bar)、价格,以及两个引用型 int 变量 x、y,调用后把像素位置写回这两个变量。 下面这段逻辑只处理挂单(IT_PENDING)。先判断杠杆 Leverange 是否为 0,是就直接 return,避免除零。接着用 Terminal.GetAdjustToTrade() 除以(杠杆 × 最小交易量)算出每最小手的价位调整单位 ad,买挂单和卖挂单通过 isBuy 三元表达式翻转方向。 止盈坐标在基准 X 上偏移 +120 像素,止损再偏移 +220 像素,Y 轴则分别用 Take / Stop 对应的价格走一次 ChartTimePriceToXY 重算。这样挂单线、TP 线、SL 线在面板里就能横向错开,不挤在同一列。外汇与贵金属杠杆品种波动剧烈,坐标算错可能把止损画到反方向,实盘前务必在策略测试器里用 1 小时图肉眼核对一次。
class="type">int x, y; ChartTimePriceToXY(Terminal.Get_ID(), class="num">0, class="num">0, price, x, y); macroSetAxleY(it); macroSetAxleX(it, m_PositionMinimalAlxeX); if (Leverange == class="num">0) class="kw">return; if (it == IT_PENDING) { ad = Terminal.GetAdjustToTrade() / (Leverange * Terminal.GetVolumeMinimal()); ChartTimePriceToXY(Terminal.Get_ID(), class="num">0, class="num">0, price + Terminal.AdjustPrice(FinanceTake * (isBuy ? ad : (-ad))), x, y); macroSetAxleY(IT_TAKE); macroSetAxleX(IT_TAKE, m_PositionMinimalAlxeX + class="num">120); ChartTimePriceToXY(Terminal.Get_ID(), class="num">0, class="num">0, price + Terminal.AdjustPrice(FinanceStop * (isBuy ? (-ad) : ad)), x, y); macroSetAxleY(IT_STOP); macroSetAxleX(IT_STOP, m_PositionMinimalAlxeX + class="num">220); } class="macro">#undef macroSetAxleX class="macro">#undef macroSetAxleY
◍ 一点提醒
这套订单系统目前还差最后一块拼图:把完整订单直接画到图表上。但这一步牵动代码其它位置的改动,幅度不小,所以留到后续篇章单独拆。 若你在改系统时报错,就回退一步重来,直到跑出自己顺手的状态。外汇与贵金属杠杆高、滑点猛,任何自定义 EA 都先在策略测试器用历史数据验证再上真仓。 下一篇会给出带图表订单显示的系统雏形,表面看改动不大,底层逻辑却要动不少。到时照着 ZIP 里的 EA_-_Sistema_de_ordens_k_III_1.zip(约 11.7 MB)比对即可。