从头开始开发智能交易系统(第 21 部分):新订单系统 (IV)·进阶篇
📘

从头开始开发智能交易系统(第 21 部分):新订单系统 (IV)·进阶篇

第 2/3 篇

用 Edit 对象把数值正负直接染出来

EA 如果只把数字丢在图表上却不告诉交易员正负,那跟没显示差不多。C_Object_Edit 这个类现在先保持最简形态:它负责把数值推到 OBJ_EDIT 控件,并用背景色标出正负,省得人眼去猜那个数是赚还是亏。 Create 里有一行把 OBJPROP_READONLY 设成 true,当前版本下交易员不能直接改框里的数;这处以后会放开,但眼下够用。字体写死 Lucida Console、尺寸 10、居中,只是为了让数字对齐好看。 SetTextValue 的关键在背景色分支:如果调用方没传颜色,就按 InfoValue<0 取 def_ColorNegative,否则取 def_ColoPositive。文本本身永远用 IntegerToString(绝对值) 输出,所以框里看到的数恒为正,正负全靠底色。 GetTextValue 反过来读:取文本转整型后,再乘一个符号因子——底色等于 def_ColorNegative 就乘 -1,否则乘 1。这样 EA 回读时不会把亏损误判成盈利。 颜色常量定义在类内部,你想换配色可以改,但正负两色必须明显不同,否则符号判断会错,EA 的分析链路可能整体偏掉。外汇和贵金属波动剧烈,这类显示错误在高杠杆下会放大误判风险。

MQL5 / C++
class="type">void Create(class="type">class="kw">string szObjectName, class="type">class="kw">color cor, class="type">int InfoValue)
{
	C_Object_Base::Create(szObjectName, OBJ_EDIT);
	ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_FONT, "Lucida Console");
	ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_FONTSIZE, class="num">10);
	ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_ALIGN, ALIGN_CENTER);
	ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, clrBlack);
	ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BORDER_COLOR, clrBlack);
	ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_READONLY, true);
	SetTextValue(szObjectName, InfoValue, cor);
}
class="type">void SetTextValue(class="type">class="kw">string szObjectName, class="type">int InfoValue, class="type">class="kw">color cor = clrNONE)
{
	class="type">class="kw">color clr;
	clr = (cor != clrNONE ? cor  : (InfoValue < class="num">0 ? def_ColorNegative : def_ColoPositive));
	ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT, IntegerToString(InfoValue < class="num">0 ? -(InfoValue) : InfoValue));
	ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR, clr);
}
class="type">long GetTextValue(class="type">class="kw">string szObjectName) class="kw">const
{
	class="kw">return (StringToInteger(ObjectGetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT)) * 
		(ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR) == def_ColorNegative ? -class="num">1 : class="num">1));
};

◍ 独立文本标签类为何值得单列

在面板对象体系里,C_Object_Label 是这一阶段收尾要补的一个类。原本它和 C_Object_BtnBitMap 行为接近,作者一度不想单独写;但为了让文本信息脱离 C_Object_Edit 的输入逻辑独立存在,还是新建了它。 这个类本身极薄,只包了一层 Create 方法,其余全交给底层对象。下面这段就是它的全部家当,继承自有编辑框能力的基类,只重设字体、文本、字号和颜色。 [CODE] <span class="preprocessor">#property </span><span class="macro">copyright</span> <span class="string">"Daniel Jose"</span> <span class="comment">//+------------------------------------------------------------------+</span> <span class="preprocessor">#include </span><span class="string">"C_Object_Edit.mqh"</span> <span class="comment">//+------------------------------------------------------------------+</span> <span class="keyword">class</span> C_Object_Label : <span class="keyword">public</span> C_Object_Edit { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">public</span>&nbsp;&nbsp;: <span class="comment">//+------------------------------------------------------------------+</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">void</span> Create(<span class="keyword">string</span> szObjectName, <span class="keyword">string</span> Font = <span class="string">"Lucida Console"</span>, <span class="keyword">string</span> szTxt = <span class="string">""</span>, <span class="keyword">int</span> FontSize = <span class="number">10</span>, <span class="keyword">color</span> cor = <span class="macro">clrBlack</span>) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;C_Object_Base::Create(szObjectName, <span class="macro">OBJ_LABEL</span>); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetString</span>(Terminal.Get_ID(), szObjectName, <span class="macro">OBJPROP_FONT</span>, Font); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetString</span>(Terminal.Get_ID(), szObjectName, <span class="macro">OBJPROP_TEXT</span>, szTxt); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(Terminal.Get_ID(), szObjectName, <span class="macro">OBJPROP_FONTSIZE</span>, FontSize); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(Terminal.Get_ID(), szObjectName, <span class="macro">OBJPROP_COLOR</span>, cor); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}; <span class="comment">//+------------------------------------------------------------------+</span> }; <span class="keyword">virtual</span> <span class="keyword">void</span> PositionAxleY(<span class="keyword">string</span> szObjectName, <span class="keyword">int</span> Y<span style="background-color:rgb(177, 210, 143);">, <span class="keyword">int</span> iArrow = <span class="number">0</span></span>) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">int</span> desl = (<span class="keyword">int</span>)<span class="functions">ObjectGetInteger</span>(Terminal.Get_ID(), szObjectName, <span class="macro">OBJPROP_YSIZE</span>); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">ObjectSetInteger</span>(Terminal.Get_ID(), szObjectName, <span class="macro">OBJPROP_YDISTANCE</span>, <span style="background-color:rgb(177, 210, 143);">(iArrow == <span class="number">0</span> ? Y - (<span class="keyword">int</span>)(desl / <span class="number">2</span>) : (iArrow == <span class="number">1</span> ? Y : Y - desl))</span>); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp>}; [/CODE] 逐行看:第 1 行声明版权信息;第 3 行引入 C_Object_Edit 头文件,标签类由此继承。类体内 Create 方法先调基类生成 OBJ_LABEL 对象,随后用 ObjectSetString 设字体与文本,ObjectSetInteger 设字号(默认 10)与颜色(默认 clrBlack)。 下面高亮的那段是基类 C_Object_Base 的改动:PositionAxleY 多收一个 iArrow 参数,默认 0。函数内先取对象 Y 方向尺寸 desl,再按 iArrow 决定 YDISTANCE——0 表示居中(Y 减半高),1 表示顶对齐,其他值表示底对齐(Y 减全高)。实测这样改后,解释面板里显示大数值更顺眼:上部放合约数或杠杆倍数,下部放持仓结果。 OOP 在这里的价值很直白:类拆得越细,相似对象的重复代码越少。下一步就是拿这个标签类去动 C_ObjectsTrade 的显示了。

MQL5 / C++
class="macro">#class="kw">property copyright "Daniel Jose"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "C_Object_Edit.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class C_Object_Label : class="kw">public C_Object_Edit
{
      class="kw">public  :
class=class="str">"cmt">//+------------------------------------------------------------------+
            class="type">void Create(class="type">class="kw">string szObjectName, class="type">class="kw">string Font = "Lucida Console", class="type">class="kw">string szTxt = "", class="type">int FontSize = class="num">10, class="type">class="kw">color cor = clrBlack)
              {
                  C_Object_Base::Create(szObjectName, OBJ_LABEL);
                  ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_FONT, Font);
                  ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT, szTxt);
                  ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_FONTSIZE, FontSize);
                  ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, cor);
              };
class=class="str">"cmt">//+------------------------------------------------------------------+
};
class="kw">virtual class="type">void PositionAxleY(class="type">class="kw">string szObjectName, class="type">int Y, class="type">int iArrow = class="num">0)
              {
                  class="type">int desl = (class="type">int)ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YSIZE);
                  ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YDISTANCE, (iArrow == class="num">0 ? Y - (class="type">int)(desl / class="num">2) : (iArrow == class="num">1 ? Y : Y - desl)));
              };

「给交易面板补齐可交互对象」

在图表上把信息层真正画出来,关键不在于多写几百行,而在于先定义对象要响应的事件类型。下面的枚举里,EV_GROUND 从 65 开始编号,后续手动追加了 EV_CLOSE、EV_EDIT、EV_VOLUME、EV_MOVE 四种事件,这意味着面板从只能看,变成能接收关闭、编辑、成交量显示、拖动等指令。 对象的创建顺序必须严格按图层叠放来:背景 C_Object_BackGround 最先,再往上压交易线、按钮位图、编辑框和标签。代码中新增的 m_BtnClose、m_EditInfo、m_InfoVol、m_BtnMove 如果写错位置,图表上就会被背景盖住,调一下声明次序即可。 宏替换在这里省掉了大量重复代码——删除整块面板对象时,只写高亮部分就能清空 4 个面板里的 6 个对象。若不用宏,手撸同样逻辑出错概率会明显上升。 编译后界面已经能显示,但控件仍点不动:事件函数还没绑定。没有事件,这套 UI 只是把旧信息换了个画法,对外汇、贵金属这类高波动品种没有任何实操价值,下一步必须给每个对象挂上响应。

MQL5 / C++
<span class="keyword">enum</span> eEventType {EV_GROUND = <span class="number">class="num">65</span>, EV_LINE<span style="background-class="type">class="kw">color:rgb(class="num">177, class="num">210, class="num">143);">, EV_CLOSE, EV_EDIT, EV_VOLUME, EV_MOVE</span>};
C_Object_BackGround&nbsp;&nbsp;&nbsp;&nbsp; m_BackGround;
C_Object_TradeLine&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_TradeLine;
<span style="background-class="type">class="kw">color:rgb(class="num">177, class="num">210, class="num">143);">C_Object_BtnBitMap&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_BtnClose;</span>
<span style="background-class="type">class="kw">color:rgb(class="num">177, class="num">210, class="num">143);">C_Object_Edit&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_EditInfo,</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-class="type">class="kw">color:rgb(class="num">177, class="num">210, class="num">143);">m_InfoVol;</span>
<span style="background-class="type">class="kw">color:rgb(class="num">177, class="num">210, class="num">143);">C_Object_Label&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_BtnMove;</span>
<span class="keyword">class="kw">inline</span> <span class="keyword">class="type">void</span> CreateIndicatorTrade(<span class="keyword">class="type">class="kw">ulong</span> ticket, eIndicatorTrade it)
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">color</span> cor1, cor2, cor3;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span> sz0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">int</span> infoValue;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">switch</span> (it)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">case</span> IT_TAKE&nbsp;&nbsp;&nbsp;&nbsp;:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;infoValue = m_BaseFinance.FinanceTake;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor1 = <span class="macro">clrForestGreen</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor2 = <span class="macro">clrDarkGreen</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor3 = <span class="macro">clrNONE</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">break</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">case</span> IT_STOP&nbsp;&nbsp;&nbsp;&nbsp;:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;infoValue = - m_BaseFinance.FinanceStop;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor1 = <span class="macro">clrFireBrick</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor2 = <span class="macro">clrMaroon</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor3 = <span class="macro">clrNONE</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">break</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">case</span> IT_PENDING:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;infoValue = m_BaseFinance.Leverange;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor1 = <span class="macro">clrCornflowerBlue</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor2 = <span class="macro">clrDarkGoldenrod</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor3 = <span class="macro">clrLightBlue</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">break</span>;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-class="type">class="kw">color:rgb(class="num">177, class="num">210, class="num">143);"><span class="keyword">case</span> IT_RESULT&nbsp;&nbsp;:</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">default</span>:

挂单面板的尺寸与控件分支

这段逻辑在订单面板绘制时给不同元素定尺寸和颜色:止盈、止损、挂单的背景块统一用 92×22 像素,而结算块 IT_RESULT 的背景放大到 84×34,留出更多空间显示盈亏数字。 颜色上,杠杆数值 infoValue 和两条边框线都走深蓝 clrDarkBlue,编辑框文字用银色 clrSilver,视觉层级一眼能分清。 非结算状态下会生成一个 Wingdings 字体、字符 'u'、字号 17 的拖拽按钮 m_BtnMove;一旦 it == IT_RESULT,就改挂 m_InfoVol 成交量标签,尺寸 60×14,和编辑框对齐。 在 MT5 里把这段嵌进你的 CPanel 类,调一下 92、22、84、34 这几个像素值,就能直接看挂单和结算块在图表上的占位差异。外汇和贵金属杠杆高,面板只是辅助,实际仓位仍以券商风控为准。

MQL5 / C++
infoValue = m_BaseFinance.Leverange;
cor1 = clrDarkBlue;
cor2 = clrDarkBlue;
cor3 = clrSilver;
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:
				case IT_PENDING:
						m_BackGround.Size(sz0, class="num">92, class="num">22);
						class="kw">break;
				case IT_RESULT:
						m_BackGround.Size(sz0, class="num">84, class="num">34);
						class="kw">break;
				}
				m_BtnClose.Create(MountName(ticket, it, EV_CLOSE), def_BtnClose);
				m_EditInfo.Create(sz0 = MountName(ticket, it, EV_EDIT), cor3, infoValue);
				m_EditInfo.Size(sz0, class="num">60, class="num">14);
				if (it != IT_RESULT) m_BtnMove.Create(MountName(ticket, it, EV_MOVE), "Wingdings", "u", class="num">17, cor2);
				else
				{
					m_InfoVol.Create(sz0 = MountName(ticket, it, EV_VOLUME), clrNONE, infoValue);
					m_InfoVol.Size(sz0, class="num">60, class="num">14);

◍ 用宏批量清理挂单图形对象

在 MT5 的 EA 里,一笔订单可能挂载了基准线、地平线、收盘价线、编辑点、移动线、成交量块等多类图形对象。手动逐个 ObjectDelete 既冗长又容易漏,用宏把同 ticket 下的多后缀对象一次性抹掉更稳。 macroDelete(A) 这个宏接收指标交易类型 A,连续调用 ObjectDelete 删除 EV_GROUND、EV_LINE、EV_CLOSE、EV_EDIT 四类固定对象;当 A 不等于 IT_RESULT 时再删 EV_MOVE,否则改删 EV_VOLUME,避免结果单误清成交量标记。 RemoveIndicatorTrade 函数先关闭图表的对象删除事件广播(CHART_EVENT_OBJECT_DELETE 置 false),再判断 it 参数:若不是 NULL、IT_PENDING、IT_RESULT 就走宏清理单类,否则进入大括号做分支处理。外汇与贵金属杠杆高,对象残留可能引发鼠标误触或事件重入,清场逻辑建议回测时逐单验证。

MQL5 / C++
class="macro">#define macroDelete(A)                                                                             \
   ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND));                                 \
   ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE));                                   \
   ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_CLOSE));                                  \
   ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_EDIT));                                   \
   if (A != IT_RESULT)                                                                               \
      ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_MOVE));                                \
   else                                                                                              \
      ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_VOLUME));                              \

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);
               if ((it != NULL) && (it != IT_PENDING) && (it != IT_RESULT)) macroDelete(it)
               else
               {

「挂单组件销毁与 Y 轴重定位的收尾处理」

在挂单图形对象被用户删除时,先把四类关联宏对象一次性清掉:待办挂单、结果标签、止盈线与止损线。对应的 macroDelete 调用连续执行四次,随后用 ChartSetInteger 打开 CHART_EVENT_OBJECT_DELETE 事件开关,让图表能捕获删除动作,这一串操作若漏掉任一行,MT5 上就可能残留不可见的锚点对象。 清理完之后立刻切换思路做重绘,用 macroSetAxleY 这个宏统一把背景、交易线、平仓按钮按新 Y 坐标 y 重定位。结果标签 IT_RESULT 会带 -1 的纵向偏移,其余类型偏移为 0,这是为了让成交信息块和价格线在视觉上错开半行,避免文字压住线体。 宏里对 IT_RESULT 做了分支:非结果类才挂移动按钮 m_BtnMove,结果类则改挂成交量标签 m_InfoVol 并取偏移 1。你在 MT5 里改这套面板时,直接调 y 变量就能整体上下挪动挂单控件组,比逐个 ObjectSetInteger 改坐标省事得多,但外汇与贵金属波动剧烈,这类自定义面板只帮你看单,不替你管风险。

MQL5 / C++
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); \
   m_BtnClose.PositionAxleY(MountName(ticket, A, EV_CLOSE), y); \
   m_EditInfo.PositionAxleY(MountName(ticket, A, EV_EDIT), y, (A == IT_RESULT ? -class="num">1 : class="num">0)); \
   if (A != IT_RESULT) \
      m_BtnMove.PositionAxleY(MountName(ticket, A, EV_MOVE), y); \
   else \
      m_InfoVol.PositionAxleY(MountName(ticket, A, EV_VOLUME), y, class="num">1); \

常见问题

用 Edit 对象配合条件判断,正数值设绿色、负数值设红色,直接在控件属性里改 Color,不必另写文本。
独立类能隔离坐标、字体、点击事件等状态,后续加按钮或挂单控件时不用重写底层绘制逻辑,维护更省事。
可以。把面板截图或描述发给小布,它能按常见尺寸规则标出可能重叠的控件区域,并给出重排建议。
按订单类型用独立尺寸常量加 switch 分支,把宽高计算收进各分支头部,避免在主循环里堆 if-else。
销毁时记录被删对象 Y 轴坐标,对其下所有同面板对象做统一 Y 偏移重定位,用宏清理残留句柄防内存漏。