English Русский Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
preview
从头开始开发智能交易系统(第 9 部分):概念上的飞跃  (II)

从头开始开发智能交易系统(第 9 部分):概念上的飞跃 (II)

MetaTrader 5指标 | 30 六月 2022, 09:25
1 963 0
Daniel Jose
Daniel Jose

概述

之前的部分里,我们创建了一个基本系统,允许在浮动窗口中使用模板。 虽然我们已完成了很多修改,但代码依旧尚未完成。 之前这样做是为了保持解释的简单,因为虽然在浮动窗口中使用模板确实相当简单,但对象的使用则要复杂得多。 故此,准备好迎接一份全新的工作吧。

事实上,相对于我们利用对象在浮动窗口里创建图表交易界面,最大的困难是 MetaTrader 5 本意并非出于此目的。 一些读者可能会说,我们可以使用标准库来创建图表交易窗口。 但我喜欢把事情复杂化,我想让每个人都能像几篇文章之前讨论的那样自行创建界面。 然而,在那篇文章中,一切都还很简单。 现在我们需要了解 MetaTrader 5 中的限制,以便绕过它们。


计划

我们需从基础开始。 以下代码的行为将符合我们的预期:

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
int OnInit()
{
        long id = ChartID();
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand();
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        
  
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+


这里并无什么复杂的事情,因为影响完全符合预期。 但是 MQL5 允许您更进一步,尽管若您尝试做一些超出系统最初设计目标的事情时,可能会遇到困难。 因此,如果我们将上面的代码更改为下面的代码,事情就会变得有趣。

int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}

我们已将高亮显示的行添加到代码之中。 如果您在图表上运行它,结果如下:


发生了什么? 我们在图表上又放了一个图表。 我们可以在此放置任何对象,因为 MQL5 允许这样做,但这样的话优缺点兼备。 查看代码中的下一处修改,以便理解该步骤的优点。

int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTED, true);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}

高亮显示的附加行产生了以下结果:


这意味着对象内的所有内容都将保留在对象内。 当我们使用浮动窗口时,这是必需的,因为这能大大简化控制逻辑。 但并非一切都是完美的:MetaTrader 5 最初并不是为此而设计的。 故此,当一个对象处于另一个对象内部时,就会出现一个问题 — 我们无法向内部对象发送事件。 为了理解这一点,我们对代码进行更多的修改。 最终代码如下:

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTED, true);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(handle, sz1, OBJPROP_SELECTED, true);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (id == CHARTEVENT_OBJECT_CLICK) Print(sparam);
}
//+------------------------------------------------------------------+

此处是在平台中运行该代码的结果:


请注意,当我们单击内部对象时,实际上是在单击外部对象,这就是事情变得复杂的所在。 但程序员总是努力成为解决问题的专家:你必须解决问题才能得到想要的结果。 通过运用相关知识,我们将构建该系统,从而在浮动窗口中创建图表交易,并确保其功能正常,且具有个性化外观。

在该规划过程中,还有最后一个阶段。 虽然这一部分对于现代计算机来说并不那么尖锐,但它仍然需要加以考虑:处理时间的优化。 该问题与处理器必须执行的操作数量有关,而非与处理信息所需的时间有关。 拟议中的浮动窗口系统包含四个对象,它们应该能够响应您的动作从而四处移动。 相应地,在观察窗口中的任何信息都将会受到窗口自身修改的影响。 至少图表交易会增加对象的数目。 虽然没有相应的计算成本,但代码变得令人不快,似乎优化得很差劲。 我们可以简单地添加一个控制系统,如此来解决问题。 但有一个更优雅的建议。 虽然它看起来更耗时、更费力,但实际上减少了所需维护和操控的对象数量。


实现

首先,我们把浮动窗口的创建过程分为几个步骤,以便支持代码重用。 接下来,我们将在 C_ChartFloating 对象类中创建两个新函数:

//+------------------------------------------------------------------+
bool StageLocal01(string sz0, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1)
{
        m_LimitX = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS);
        m_LimitY = (int)ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS);
        if (m_MaxCounter >= def_MaxFloating) return false;
        CreateBarTitle();
        CreateCaption(sz0);
        CreateBtnMaxMin();
        CreateRegion(TimeFrame, Scale);
	m_Win[m_MaxCounter].handle = ObjectGetInteger(Terminal.Get_ID(), m_Win[m_MaxCounter].szRegionChart, OBJPROP_CHART_ID);
                                
        return true;
}
//+------------------------------------------------------------------+
void StageLocal02(int x, int y, int w, int h)
{
        y = (y < 0 ? m_MaxCounter * def_SizeBarCaption : y);                            
        m_Win[m_MaxCounter].PosX        = -1;
        m_Win[m_MaxCounter].PosY        = -1;
        m_Win[m_MaxCounter].PosX_Minimized = m_Win[m_MaxCounter].PosX_Maximized = x;
        m_Win[m_MaxCounter].PosY_Minimized = m_Win[m_MaxCounter].PosY_Maximized = y;
        SetDimension(w, h, true, m_MaxCounter);
        SetPosition(x, y, m_MaxCounter);
        ChartRedraw(m_Win[m_MaxCounter].handle);
        m_MaxCounter++;
}
//+------------------------------------------------------------------+

添加浮动窗口的新代码如下所示:

bool AddIndicator(string sz0, int x = 0, int y = -1, int w = 300, int h = 200, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1)
{
	if (!StageLocal01(sz0, TimeFrame, Scale)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, sz0 + ".tpl");   
        m_Win[m_MaxCounter].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
        ObjectCreate(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJ_VLINE, 0, 0, 0);
        ObjectSetInteger(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJPROP_COLOR, clrBlack);
        StageLocal02(x, y, w, h);

        return true;
}

这不会影响已组装完毕的系统,但提供了更佳功能。 注意高亮显示的行:现在我们将创建一个函数来使用我们的 IDE。 开头如下图所示:

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        StageLocal02(x, y, w, h);
        return true;
}

请注意,高亮显示的行与我们在前面的代码中所用的行相同。 即,我们正在重用代码,只有我们需要调整的地方才会不同。 现在我们可以通知我们的系统,我们已经有了管理 IDE 的工具。 我们修改 C_TemplateChart 对象类来实现这一点。 下面的代码精准示意了函数中将要修改的内容,因此从现在起,我们就可致力于利用 IDE 实现一个浮动窗口,因为所有必要的支持都将正常工作。

void AddTemplate(void)
{

// .... Function code....

        if (h == 0)
        {
                SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w);
                if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl");
        }
        if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD)
        {
                if ((h > 0) && (w > 0)) Add_RAD_IDE(m_Params.Param[TEMPLATE], 0, -1, w, h); else
                {
                        C_Chart_IDE::Create(GetIdSubWinEA());
                        m_Info[m_Counter - 1].szVLine = "";
                }
        }else
        {
                if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else
                {
                        m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
                        ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0);
                        ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack);
                }
        }
}

我们看看如何配置代码,令其尽可能灵活。 这也会防止系统变成弗兰肯斯坦(Frankenstein)。 当我们修改代码时,请始终记住这一点 — 不需要从头编写代码,也不需要多次验证相同的内容。 始终尝试只验证一次。 然后,您可以在进行新测试之前尽可能多地使用和探索这些内容。 因此,系统就会在良好的前提下成长,而代码将随着时间的推移保持可连贯性和可扩展性。

如果您现在运行该系统,它将在图表上显示一些内容。 但我们至少需要它来显示我们之前编写的界面。 因此,我们需要很对代码进行额外的修改。 现在我们有以下代码:

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
        StageLocal02(x, y, w, h);
        return true;
}

以下是应用程序发布的结果:


如果能够访问模板中的对象(模板加载在上面代码中高亮显示的行中),那将是非常良好和漂亮的。 然而,这是不可能的。 此处有一个重要的细节:我们只应创建将被操纵的对象,来替代我们之前研究时那样创建对象! 当我们必须移动窗口时,这将节省大量的处理时间。 我们还遇到另一个问题,但我们先要解决处理问题,并令系统正常工作。 实际上,这部分已经完成了,只需做一些调整即可。

我们从修改类之间的继承序列开始。 我们不得不如此做,因为我们没有多重继承,所以新结构将如下所示:


但这种变更不应该引起关注:继承序列的变更根本不会修改代码,但会令其近乎就绪。 已变更的部分在下面代码中会高亮显示。

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
	if ((w <= 0) || (h <= 0)) return false;
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
        StageLocal02(x, y, w, h);
        return true;
}
void AddTemplate(void)
{
// ..... Código ....
        if (h == 0)
        {
                SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w);
                if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl");
        }
        if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD)
        {
		C_Chart_IDE::Create(Add_RAD_IDE(m_Params.Param[TEMPLATE], 0, -1, w, h));
                m_Info[m_Counter - 1].szVLine = "";
        }else
        {
                if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else
                {
                        m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
                        ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0);
                        ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack);
                }
        }
}
bool Create(bool bFloat)
{
        m_CountObject = 0;
        if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
        FileReadInteger(m_fp, SHORT_VALUE);
                        
        for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
	m_SubWindow = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
        m_szLine = "";
        while (m_szLine != "</chart>")
        {
                if (!FileReadLine()) return false;
                if (m_szLine == "<object>")
                {
                        if (!FileReadLine()) return false;
                        if (m_szLine == "type")
                        {
                                if (m_szValue == "102") if (!LoopCreating(OBJ_LABEL)) return false;
                                if (m_szValue == "103") if (!LoopCreating(OBJ_BUTTON)) return false;
                                if (m_szValue == "106") if (!LoopCreating(OBJ_BITMAP_LABEL)) return false;
                                if (m_szValue == "107") if (!LoopCreating(OBJ_EDIT)) return false;
                                if (m_szValue == "110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) return false;
                        }
                }
        }
        FileClose(m_fp);
        DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, 0, szMsgIDE[eLABEL_SYMBOL]);
        return true;
}

bool LoopCreating(ENUM_OBJECT type)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
#define macro_SetString(A, B) ObjectSetString(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
        int c0;
        bool b0;
        string sz0 = m_szValue;
        while (m_szLine != "</object>") if (!FileReadLine()) return false; else
        {
                if (m_szLine == "name")
                {
                        b0 = false;
                        StringToUpper(m_szValue);
                        for(c0 = eRESULT; (c0 <= eEDIT_STOP) && (!(b0 = (m_szValue == szMsgIDE[c0]))); c0++);
                        if (!b0 && m_IsFloating) return true; else c0 = (b0 ? c0 : m_CountObject);
                        m_ArrObject[c0].szName = StringFormat("%s%04s>%s", def_HeaderMSG, sz0, m_szValue);

//... The rest of the function...

}

这也许看起来很奇怪。 但这里并无什么复杂的东西。 请记住,当我们不用浮动窗口时,系统已经能够处理 IDE 中运行的事件。 但因为那扇浮动窗口,我们必须重建一切。 然而,我们不必从头开始做这件事 — 我们将修改现有代码,以便在正确的位置加入 IDE。 我们只需要添加接收事件的对象。 这些更改令我们明白,是需要创建所有元素,亦或只需要创建其中的一部分。

在这些修改之后,在图表上我们就有了 IDE 信息。 但是 IDE 对象会造成真正的混乱,因为这些对象与浮动窗口并无关联。 现在就需要解决这个问题。

首先,我们需要得到浮动窗口在图表上的位置。 知道对象在窗口里的呈现点位置就足够了。 但由于坚持面向对象编程,我们需要做一些修改,尽管主要结果是下面的代码。

//+------------------------------------------------------------------+
struct IDE_Struct
{
        int     X,
                Y,
                Index;
        bool    IsMaximized;
};
//+------------------------------------------------------------------+
class C_ChartFloating
{

// ... Class code ...

//+------------------------------------------------------------------+
                void SetPosition(const int X, const int Y, const int c0)
                        {

// ... Function code ...

                                if (c0 == m_IDEStruct.Index)
                                {
                                        m_IDEStruct.X = m_Win[c0].PosX + 3;
                                        m_IDEStruct.Y = m_Win[c0].PosY + def_SizeBarCaption + 3;
                                        m_IDEStruct.IsMaximized = m_Win[c0].IsMaximized;
                                }
                        }
//+------------------------------------------------------------------+

// ... Class code ...

//+------------------------------------------------------------------+
inline IDE_Struct GetIDE_Struct(void) const { return m_IDEStruct; }
//+------------------------------------------------------------------+
                bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
                        {
                                if ((w <= 0) || (h <= 0)) return false;
                                if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
                                ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
                                m_IDEStruct.Index = m_MaxCounter;
                                StageLocal02(x, y, w, h);
                                return true;
                        }
//+------------------------------------------------------------------+

//... The rest of the class code 
}

由于解决方案关注于一个窗口 — 我们的图表交易者 — 在这一点之后,没有必要让事情变得更复杂。 现在,我们可在窗口中正确定位对象,至少最初如此。 这是在以下函数中完成的。

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++) if (m_IsFloating)
        {
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, GetIDE_Struct().X + m_ArrObject[c0].iPosX);
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_YDISTANCE, GetIDE_Struct().Y + m_ArrObject[c0].iPosY);
        }else   ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

上述函数可提供对象的正确初始定位,结果如下所示:


除了正确的初始定位之外,对象已经可以对事件做出反应。 然而,该系统仍然存在漏洞,需要修复。 此处是我们要修改的第一件事:


如您所见,窗口已最小化,但对象仍保留在屏幕上。 这是因为对象不在窗口区域内(我曾在前面解释了原因)。 故在我们继续之前,需要先修复它。 这可以通过如下所示的简单代码来完成修改:

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++) if (m_IsFloating)
        {
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, GetIDE_Struct().X + m_ArrObject[c0].iPosX + (GetIDE_Struct().IsMaximized ? 0 : Terminal.GetWidth()));
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_YDISTANCE, GetIDE_Struct().Y + m_ArrObject[c0].iPosY + (GetIDE_Struct().IsMaximized ? 0 : Terminal.GetHeight()));
        }else   ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

结果如下:

现在,我们来修复移动的问题。 可以由以下代码解决此问题

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        static double AccumulatedRoof = 0.0;
        bool            b0;
        double  d0;
        static int px = -1, py = -1;
                                
        C_ChartFloating::DispatchMessage(id, lparam, dparam, sparam);
        if (m_CountObject < eEDIT_STOP) return;
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        if ((GetIDE_Struct().X != px) || (GetIDE_Struct().Y != py))
                        {
                                px = GetIDE_Struct().X;
                                py = GetIDE_Struct().Y;
                                Resize(-1);
                        }
                        break;

//... The rest of the function ...

最终结果如下:

 


结束语

看看编程有多棒:我们从一个问题开始,然后,在不对代码进行重大更改的情况下,我们一个接一个地解决了问题,最后我们用尽可能少的代码编写出工作代码。


重要!如果您在图表交易背景上看到奇怪的信息,这是由于 MT5 的更新 3228,它会令用到 clrNONE 颜色的对象不透明。 可利用附带的 IDE 文件修复此问题。


本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/10363

附加的文件 |
EA_1.09.zip (3617.03 KB)
IDE.tpl (10.13 KB)
DoEasy. 控件(第 4 部分):面板控件,Padding(填充)和 Dock(驻靠)参数 DoEasy. 控件(第 4 部分):面板控件,Padding(填充)和 Dock(驻靠)参数
在本文中,我将实现处理 Padding(填充,元素所有侧边的内部缩进/边距)和 Dock(驻靠)参数(对象在其容器中的定位方式)。
DoEasy. 控件 (第 3 部分): 创建绑定控件 DoEasy. 控件 (第 3 部分): 创建绑定控件
在本文中,我将创建绑定到基准元素的从属控件。 开发任务将使用基准控件功能执行。 此外,我还会稍微修改一下图形元素阴影对象,因为把它应用于任何有阴影的对象时会遇到一些逻辑错误。
如何掌握机器学习 如何掌握机器学习
查看这些有用的资料选集,它们可以辅助交易者提高他们的算法交易知识。 简约算法时代正在成为过去,如果不运用机器学习技术和神经网络,成功变得越来越困难。
学习如何设计基于 CCI 的交易系统 学习如何设计基于 CCI 的交易系统
来自我们的《学习如何设计交易系统》系列的新篇章中,我将介绍商品通道指数(CCI),解释其细节,并与您分享如何基于此指标创建交易系统。