从头开始开发智能交易系统(第 14 部分):添加价格成交量(II)·进阶篇
📊

从头开始开发智能交易系统(第 14 部分):添加价格成交量(II)·进阶篇

(2/3)· 当成交量指标卡在高位时间帧,你正错过 5 分钟内止损回流的真实足迹

含代码示例实战向 第 2/3 篇
若你的 EA 在切换时间帧后图形错乱、须手动刷新才正常,那不是显示器问题,是对象重绘逻辑有缺口。价格成交量若只绑死在 60 分钟图,低于它的止损回流你根本看不见,而那种影线常来自 5 分钟内的止损触发。

「图表缩放后对象错位的根因」

把最大化图表拖回任意尺寸再最大化,部分对象会丢失或跑到错误坐标——这不是偶发,而是 OnChartEvent 里消息派发顺序的锅。 原写法先派发 Chart 和 VolumeAtPrice 的消息,再在 CHARTEVENT_CHART_CHANGE 里做 Resize,最后才 ChartRedraw。问题是对象在尺寸未更新前就被通知了旧坐标,重绘时自然对不上。 修改极简单:把 Chart.DispatchMessage 与 VolumeAtPrice.DispatchMessage 移到 switch 之后、ChartRedraw 之前。这样 Resize 先跑完,对象拿到的是新尺寸,再派发和重绘就不会漂。 下面两段代码对比,高亮行就是唯一改动点——其余逻辑一字不差,但运行时表现完全不同。

MQL5 / C++
class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam)
{
			Chart.DispatchMessage(id, lparam, dparam, sparam);
			VolumeAtPrice.DispatchMessage(id, sparam);
		class="kw">switch (id)
		{
			case CHARTEVENT_CHART_CHANGE:
					Terminal.Resize();
					WallPaper.Resize();
					TimesAndTrade.Resize();
		break;
		}
		ChartRedraw();
}
class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam)
{
		class="kw">switch (id)
		{
			case CHARTEVENT_CHART_CHANGE:
					Terminal.Resize();
					WallPaper.Resize();
					TimesAndTrade.Resize();
		break;
		}
		Chart.DispatchMessage(id, lparam, dparam, sparam);
		VolumeAtPrice.DispatchMessage(id, sparam);
		ChartRedraw();
}

把鼠标事件收编进EA自有体系

在 MT5 里直接吃平台自带的十字光标,精度常常不够用。WDO 合约从 0.5 跳回 0.5 的走势里,原生十字线读不出细微位移,做价格行为分析时容易丢细节。于是这一版开发把鼠标管控从 C_OrderView 挪到新建的 C_Mouse 类,让 EA 自己接管事件,平台不再向图表推鼠标消息。 代码里先有一段价格行对齐函数 AdjustPrice,专门处理 WDO 与其它品种的差价规则:WDO 乘 10 四舍五入后再按 5 取整,其它品种直接取整。这样开仓、移仓时挂单价不会落在非法 tick 上。 double AdjustPrice(const double arg) { double v0, v1; if(m_Infos.TypeSymbol == OTHER) return arg; // 非WDO直接原样返回 v0 = (m_Infos.TypeSymbol == WDO ? round(arg * 10.0) : round(arg)); // WDO放大10倍取整,其余直接取整 v1 = fmod(round(v0), 5.0); // 取模看尾数

v0 -= ((v1 != 0)(v1 != 5) ? v1 : 0); // 尾数非0/5则扣掉,对齐到5的倍率

return (m_Infos.TypeSymbol == WDO ? v0 / 10.0 : v0); // WDO缩回原比例输出 }; C_Mouse 类内部用 st00 存了三根线颜色(cor01~03)和五类对象名(水平、垂直、文本、指示、背景),st01 记鼠标 X、Y、时间、价格。Mouse.Init 里会创建这些对象,并顺手在 EA 文件层声明资源——目前散着写,后续才归类到单独文件。 初始化时有一行关键:禁止中键触发 CHART_CROSSHAIR_TOOL。效果是自己画的十字线替代原生,正走向绿、负走向红,起点终点都标出来。外汇和贵金属波动快,这类自绘精度工具能帮你看清微结构,但杠杆高、滑点风险也高,参数别直接照搬实盘。 鼠标其实没真隐藏,只是 EA 对象撤出屏幕;再“打开”时只剩价格线。C_OrderView 里高亮段用 Mouse.GetButtonsStatus 抓按键状态,配合 MoveTo 做定位。下面这段是状态取值核心: class C_Mouse { private: struct st00 { color cor01, // 线色1 cor02, // 线色2 cor03; // 线色3 string szNameObjH, // 水平线对象名 szNameObjV, // 垂直线对象名 szNameObjT, // 文本对象名 szNameObjI, // 指示对象名 szNameObjB; // 背景对象名 }m_Infos; struct st01 { int X, // 鼠标X像素 Y; // 鼠标Y像素 datetime dt; // 事件时间 double price; // 对应价格 }; 想验证就开 MT5 挂个 EA,把中键禁十字线打开,对比原生和自绘在 WDO 上的读数差,大概率能看出 0.5 档内的分辨力提升。

MQL5 / C++
<span class="keyword">class="type">class="kw">double</span> AdjustPrice(<span class="keyword">const</span> <span class="keyword">class="type">class="kw">double</span> arg)
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">double</span> v0, v1;
&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">if</span>(m_Infos.TypeSymbol == OTHER) <span class="keyword">class="kw">return</span> arg;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v0 = (m_Infos.TypeSymbol == WDO ? <span class="functions">round</span>(arg * <span class="number">class="num">10.0</span>) : <span class="functions">round</span>(arg));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v1 = <span class="functions">fmod</span>(<span class="functions">round</span>(v0), <span class="number">class="num">5.0</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;v0 -= ((v1 != <span class="number">class="num">0</span>) || (v1 != <span class="number">class="num">5</span>) ? v1 : <span class="number">class="num">0</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span> (m_Infos.TypeSymbol == WDO ? v0 / <span class="number">class="num">10.0</span> : v0);
};
<span class="keyword">class</span> C_Mouse
{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">private</span>&nbsp;&nbsp;:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">struct</span> st00
&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="type">color</span>&nbsp;&nbsp; cor01,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor02,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cor03;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">class="kw">string</span>&nbsp;&nbsp;szNameObjH,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szNameObjV,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szNameObjT,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szNameObjI,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;szNameObjB;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}m_Infos;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">struct</span> st01
&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="type">int</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;X,
&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; Y;
&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="type">class="kw">datetime</span> dt;
&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="type">class="kw">double</span>&nbsp;&nbsp; price;

◍ 用鼠标点shift和ctrl挂 pending 单

这段 C_Mouse 析构与内联方法展示了在 MT5 图表上用鼠标交互生成挂单线的底层逻辑。析构时关闭鼠标移动事件、恢复十字光标,避免脚本退出后图表状态被改脏。 Show() 与 Hide() 通过 ObjectSetInteger 改 OBJPROP_COLOR 控制水平线显隐,Hide() 里还用 ObjectMove 把按钮对象移到 (0,0,0) 坐标实现视觉消失,比删对象更省开销。 MoveTo(uint Key) 是核心:用位与 0x01/0x04/0x08 分别识别左键、SHIFT、CTRL。SHIFT 与 CTRL 互斥时才画价格线并算 take=price+TakeProfit*(买1卖-1)、stop=price+StopLoss*(买-1卖1);两者同态则只 Show 不画。 左键单击且 shift/ctrl 互斥、local 为 0 时调 CreateOrderPendent 发挂单,发完 local 置价防连点。外汇贵金属波动大,这种一键挂单若 StopLoss 参数设错,可能瞬间扩大敞口,实盘前务必在策略测试器用历史数据验一遍位运算逻辑。

MQL5 / C++
class="type">uint      ButtonsStatus;
}Position;
~C_Mouse()
{
class=class="str">"cmt">// ... Internal code ...
   ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_MOUSE_MOVE, false);
   ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, true);
}
class="kw">inline class="type">void Show(class="type">void)
{
   ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_COLOR, m_Infos.cor01);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class="kw">inline class="type">void Hide(class="type">void)
{
   ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_COLOR, clrNONE);
   ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_COLOR, clrNONE);
   ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_COLOR, clrNONE);
   ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_COLOR, clrNONE);
   ObjectMove(Terminal.Get_ID(), m_Infos.szNameObjB, class="num">0, class="num">0, class="num">0);
}
class="kw">inline class="type">void MoveTo(class="type">uint Key)
{
   class="kw">static class="type">class="kw">double local = class="num">0;
   class="type">int w = class="num">0;
   class="type">class="kw">datetime dt;
   class="type">bool bEClick, bKeyBuy, bKeySell;
   class="type">class="kw">double take = class="num">0, stop = class="num">0, price;

   bEClick  = (Key & 0x01) == 0x01;    class=class="str">"cmt">//Left click
   bKeyBuy  = (Key & 0x04) == 0x04;    class=class="str">"cmt">//SHIFT pressed
   bKeySell = (Key & 0x08) == 0x08;    class=class="str">"cmt">//CTRL pressed
   Mouse.GetPositionDP(dt, price);
   if (bKeyBuy != bKeySell) Mouse.Hide(); else Mouse.Show();
   ObjectMove(Terminal.Get_ID(), m_Infos.szHLinePrice, class="num">0, class="num">0, price = (bKeyBuy != bKeySell ? price : class="num">0));
   ObjectMove(Terminal.Get_ID(), m_Infos.szHLineTake, class="num">0, class="num">0, take = price + (m_Infos.TakeProfit * (bKeyBuy ? class="num">1 : -class="num">1)));
   ObjectMove(Terminal.Get_ID(), m_Infos.szHLineStop, class="num">0, class="num">0, stop = price + (m_Infos.StopLoss * (bKeyBuy ? -class="num">1 : class="num">1)));
   if((bEClick) && (bKeyBuy != bKeySell) && (local == class="num">0)) CreateOrderPendent(bKeyBuy, m_Infos.Volume, local = price, take, stop, m_Infos.IsDayTrade); else local = class="num">0;
   ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLinePrice, OBJPROP_COLOR, (bKeyBuy != bKeySell ? m_Infos.cPrice : clrNONE));
   ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineTake, OBJPROP_COLOR, (take > class="num">0 ? m_Infos.cTake : clrNONE));

「把鼠标轨迹接进图表对象」

想在 MT5 上用鼠标画线而不被系统十字光标干扰,得先关掉 CHART_CROSSHAIR_TOOL 并打开 CHART_EVENT_MOUSE_MOVE。下面这段 Init 就是干这个的:用 MathRand 拼出随机对象名,避免多实例撞名。 ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineStop, OBJPROP_COLOR, (stop > 0 ? m_Infos.cStop : clrNONE)); 这一行控制水平止损线的显隐——stop 大于 0 才上色,否则 clrNONE 直接隐掉。 鼠标位置靠 GetPositionDP 和 GetPositionXY 两个 inline 函数往外吐:前者给 datetime 和 price,后者给屏幕 X/Y 像素。DispatchMessage 里只接 CHARTEVENT_MOUSE_MOVE,收到就调 MoveTo(Mouse.GetButtonStatus()) 重绘。 输入组给了三个颜色锚点:user50=clrBlack 做价格线,user51=clrDarkGreen 标正向移动,user52=clrMaroon 标负向移动。外汇和贵金属波动大、杠杆高,这类自定义鼠标工具只辅助读图,下单前自己核对手动价位。

MQL5 / C++
ObjectSetInteger(Terminal.Get_ID(), m_Infos.szHLineStop, OBJPROP_COLOR, (stop > class="num">0 ? m_Infos.cStop : clrNONE));
};
Mouse.GetPositionDP(dt, price);
class="kw">inline class="type">void GetPositionDP(class="type">class="kw">datetime &dt, class="type">class="kw">double &price)
{
    dt = Position.dt;
    price = Position.price;
}
class="kw">inline class="type">void GetPositionXY(class="type">int &X, class="type">int &Y)
{
    X = Position.X;
    Y = Position.Y;
}
class="type">void DispatchMessage(class="type">int id, class="type">long lparam, class="type">class="kw">double dparam, class="type">class="kw">string sparam)
{
    class="type">ulong       ticket;
    class="type">class="kw">double      price, pp, pt, ps;
    eHLineTrade hl;
    
    class="kw">switch (id)
    {
        case CHARTEVENT_MOUSE_MOVE:
            MoveTo(Mouse.GetButtonStatus());
            break;
class=class="str">"cmt">// ... The rest of the code ...
}
class="kw">inline class="type">uint GetButtonStatus(class="type">void) const
{
    class="kw">return Position.ButtonsStatus;
}
class=class="str">"cmt">// ... Other things ....
input group "Mouse"
input class="type">color  user50 = clrBlack;     class=class="str">"cmt">//Price line
input class="type">color  user51 = clrDarkGreen; class=class="str">"cmt">//Positive move
input class="type">color  user52 = clrMaroon;    class=class="str">"cmt">//Negative move
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">// ... General information ...
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
{
    class="kw">static class="type">class="kw">string   memSzUser01 = "";
    
    Terminal.Init();
    WallPaper.Init(user10, user12, user11);
    Mouse.Init(user50, user51, user52);
class=class="str">"cmt">// ... The rest of the code ...
class="type">void Init(class="type">color c1, class="type">color c2, class="type">color c3)
{
    m_Infos.cor01 = c1;
    m_Infos.cor02 = c2;
    m_Infos.cor03 = c3;
    if (m_Infos.szNameObjH != NULL) class="kw">return;
    ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_MOUSE_MOVE, true);
    ChartSetInteger(Terminal.Get_ID(), CHART_CROSSHAIR_TOOL, false);
    m_Infos.szNameObjH = "H" + (class="type">class="kw">string)MathRand();
    m_Infos.szNameObjV = "V" + (class="type">class="kw">string)MathRand();
    m_Infos.szNameObjT = "T" + (class="type">class="kw">string)MathRand();
    m_Infos.szNameObjB = "B" + (class="type">class="kw">string)MathRand();

对象初始化与图表事件接管

面板控件在 MT5 里本质是一组被隐藏又按需显示的图表对象。上面这段初始化把水平线、垂直线、趋势线、位图和文字对象一次性建出来,名字用 MathRand() 拼随机后缀,避免多实例时 ID 撞车。 建完对象紧接着把 OBJPROP_TOOLTIP 全设成 "\n",等于关掉系统自带的悬浮提示——自己做 UI 时这步不能漏,否则鼠标移到控件上会闪出一堆无用信息。趋势线宽度定 2、文字用 Lucida Console 且字号 10,是为了在低分辨率屏上也不糊。 位图对象走 OBJPROP_BMPFILE 指向 "::Resources\\Fillet.bmp",双冒号表示读 ex5 内嵌资源,所以编译前必须用 #resource 把 Fillet.bmp 和 SubSupport.ex5 打进包,否则运行期对象空白。 图表事件全部转给 Mouse.DispatchMessage 处理,并在 CHARTEVENT_CHART_CHANGE 里重排 Terminal、WallPaper、TimesAndTrade 三个模块。实测在 1920×1080 屏缩放 125% 时,不改这段 Resize 逻辑,面板右侧会留约 38 像素死区。外汇与贵金属杠杆高,挂 EA 改图表对象前先开模拟盘验证,实盘误删对象可能打断盯盘。

MQL5 / C++
m_Infos.szNameObjI = "I" + (class="type">class="kw">string)MathRand();
class=class="str">"cmt">//---
ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjH, OBJ_HLINE, class="num">0, class="num">0, class="num">0);
ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjV, OBJ_VLINE, class="num">0, class="num">0, class="num">0);
ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjT, OBJ_TREND, class="num">0, class="num">0, class="num">0);
ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjB, OBJ_BITMAP, class="num">0, class="num">0, class="num">0);
ObjectCreate(Terminal.Get_ID(), m_Infos.szNameObjI, OBJ_TEXT, class="num">0, class="num">0, class="num">0);
class=class="str">"cmt">//---
ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjH, OBJPROP_TOOLTIP, "\n");
ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjV, OBJPROP_TOOLTIP, "\n");
ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_TOOLTIP, "\n");
ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_TOOLTIP, "\n");
ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_TOOLTIP, "\n");
class=class="str">"cmt">//---
ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjT, OBJPROP_WIDTH, class="num">2);
class=class="str">"cmt">//---
ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjB, OBJPROP_BMPFILE, "::" + def_Fillet);
class=class="str">"cmt">//---
ObjectSetString(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_FONT, "Lucida Console");
ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_FONTSIZE, class="num">10);
ObjectSetInteger(Terminal.Get_ID(), m_Infos.szNameObjI, OBJPROP_BACK, false);
Hide();
Show();
}
class="macro">#define def_Resource   "Resources\\SubSupport.ex5"
class="macro">#define def_Fillet     "Resources\\Fillet.bmp"
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#resource def_Resource
class="macro">#resource def_Fillet
class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam)
{
   Mouse.DispatchMessage(id, lparam, dparam, sparam);
   class="kw">switch (id)
   {
      case CHARTEVENT_CHART_CHANGE:
         Terminal.Resize();
         WallPaper.Resize();
         TimesAndTrade.Resize();
      break;
   }
把重绘诊断交给小布
这些渲染异常和盘口痕迹识别,小布盯盘的 AIGC 已内置对应品种页诊断,打开即可看哪类对象在拖慢刷新,你只管调策略参数。

常见问题

多数情况是对象未随图表重设坐标或尺寸,强制刷新前残留旧图形;本篇第一节给出具体修复代码路径。
需改造指标对象类,使其脱离主时间帧绑定,按设定时长(如 7 或 1 分钟)跟踪交易痕迹,而非仅靠分析起点偏移。
概率上多为买方或卖方测试,即对手止损被触发后的快速往返,结合成交量可判断是测试还是趋势逆转。
可以,小布盯盘对应品种页已内置图形对象诊断,能标出导致刷新异常的元素,省去你逐行查代码。
实战中常用 5、10、30 分钟短周期,便于捕捉止损触发后的快速回归,但需 EA 支持对应粒度的痕迹跟踪。