一张图表上的多个指标(第 06 部分)

我们的 IDE 变更如下图所示:

          

正如您所看到的,设计本身有一些小的变化。 添加了两个新区域:一个将接收资产名称;另一个将接收当天的累计值。 好吧,这些东西对于我们来讲都是非刚需,它们不会影响我们的决定。 但无论如何,它们都很有趣。 我将展示往 IDE 中添加功能的最简单和正确的方法。 如此,在新界面中打开对象列表。 它如下显现:

这两个被圈起来的对象没有与之关联的事件,这意味着它们在 IDE 中不起作用。 所有其它对象均已某些事件正确关联,当这些事件在 EA 中发生时,MetaTrader 5 可以强制它们正确执行。 这就是说,我们可以根据自己的意愿修改 IDE 界面,但如果功能尚未实现,MetaTrader 5 则只在图表上显示对象。 我们需要那个 “EDIT 00” 对象来接收我们正在交易的资产名称,该名称应会显示在对象的中心。 “EDIT 01” 对象会接收某个时段内的累计值。 我们将采用日线时段来了解我们在日线中是盈利还是亏损。 如果该值为负值,则会以一种颜色显示;如果为正值,将采用另一种颜色。

这两个值显然不能由用户更改,因此您可以将其属性保留为只读,如下图所示。

但是,请记住,无法指定信息的显示方式,也就是说,我们无法对齐文本,使其显示在对象的中心。 如若需要,可以用代码完成此操作,因为有一个属性可以将文本设置为居中对齐。 参见 "对象属性" 了解更多详情。 请注意 表格里的 ENUM_ALIGN_MODE — 在它包含的对象上您可以使用对齐文本。

因此,无论我们要实现什么样的修改,我们需要做的第一件事就是准备一个计划:定义新特性、它们的表示形式、以及用户与它们交互的方式。 这允许 MetaTrader 5 通过自己的界面尽可能多地选择正确的对象,并配置它。 结果就是,我们将有一个现成的 IDE,我们只需要通过 MQL5 代码对其进行调整,这样 IDE 最终将 100% 正常工作。 如此,我们来继续修改。

修改

为了防止代码成为真正的弗兰肯斯坦式悲剧(Frankenstein,科学怪人),我们必须尽可能地自我组织,检查哪些功能已经存在,哪些实际需求尚将实现。 在许多情况下,只需对现有代码进行略微修改,得到新代码,并对其进行测试就足够了。 这段新代码会在我们即将实现的代码中重用,唯一要测试的就是为了创建全新功能,由我们后来添加的小控制函数。 优秀的程序员总是这样做:他们尝试通过向现有代码添加控制点,以某种方式重用现有代码。

修改 1. 添加资产名称

为了实现这一部分,我们不需要进行实质性的更改,但这些更改应该在正确的地方实现。 首先,我们为枚举添加一个新值,源代码如下所示:

enum eObjectsIDE {eRESULT, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};


以下是新代码;高亮显示的部分是已添加的部分。 请注意,我不会将新值添加到枚举的开头或结尾。 这样做是为了避免与其它已经存在,并正在运行的代码部分发生冲突。

enum eObjectsIDE {eRESULT, eLABEL_SYMBOL, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};


如果您在枚举的开头或结尾添加新值,则必须查找并更改所有引用这些极值的位置。 在许多情况下,可能由于健忘而导致遗漏,这将引发难以预料的错误。 您可能会认为这些错误看起来是由于新添加的内容引起的,而实际上是由于遗忘。 因此,我们应在极值之间添加修改。

之后,我们需要立即向系统添加一条消息,否则会出现运行时错误。 因此,在源代码中添加以下行。

static const string C_Chart_IDE::szMsgIDE[] = {
                                                "MSG_RESULT",
                                                "MSG_NAME_SYMBOL",
                                                "MSG_BUY_MARKET",
                                                "MSG_SELL_MARKET",
                                                "MSG_DAY_TRADE",
                                                "MSG_CLOSE_POSITION",
                                                "MSG_LEVERAGE_VALUE",
                                                "MSG_TAKE_VALUE",
                                                "MSG_STOP_VALUE"
                                              };


正如您所看到的,我们把它加在同一个地方,从而保持了顺序。 但目前常量可加在任何点位,这不会有任何区别,因为它只用于检查哪个对象会接收消息。 出于结构化目的,我们在加入时把它作为第二条消息。

现在,我们回到 MetaTrader 5,并进行如下更改:

          

现在,MetaTrader 5 已经将 IDE 中的对象识别为接收消息的对象,剩下的只是创建发送消息的过程。 消息中的文本只应添加一次,只要 MetaTrader 5 把 IDE 放在图表上,它就可以被发送。 这可由简单地将所需代码添加到对象类的 Create 函数末尾来实现。 但为了不让代码变成满面疤痕的弗兰肯斯坦,我们将在 DispatchMessage 函数内添加新代码。 原始函数如下所示:

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if (szArg == szMsgIDE[eRESULT])
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:

// ... The rest of the code...

        }
}


以下是完成相关修改后的代码:

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if (szArg == szMsgIDE[eRESULT])
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                        }else if (szArg == szMsgIDE[eLABEL_SYMBOL])
                        {
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_TEXT, Terminal.GetSymbol());
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_ALIGN, ALIGN_CENTER);
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:

// ... The rest of the code

        }
}


创建 send 函数后,我们可以选择发送此消息的点。 最好的位置实际上是在对象类的 Create 函数的末尾,因此最终的代码如下所示:

bool Create(int nSub)
{
        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 = nSub;
        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, szMsgIDE[eLABEL_SYMBOL]);
        return true;
}

添加的实际内容以绿色高亮显示。 请注意,在几乎不做任何更改的情况下,我们就已经有了一个 100% 实现的消息流,我们可以移步到需要实现的下一条消息。

修改2. 添加当天的累计值(覆盖点)

同样,我们遵循与添加资产名称时相同的逻辑,因此新代码如下所示:

enum eObjectsIDE {eRESULT, eLABEL_SYMBOL, eROOF_DIARY, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};

// ... Rest of the code

static const string C_Chart_IDE::szMsgIDE[] = {
                                                "MSG_RESULT",
                                                "MSG_NAME_SYMBOL",
                                                "MSG_ROOF_DIARY",
                                                "MSG_BUY_MARKET",
                                                "MSG_SELL_MARKET",
                                                "MSG_DAY_TRADE",
                                                "MSG_CLOSE_POSITION",
                                                "MSG_LEVERAGE_VALUE",
                                                "MSG_TAKE_VALUE",
                                                "MSG_STOP_VALUE"
                                              };


之后,我们用一条新消息更改 IDE:

          

我们的新 IDE 已经准备就绪。 现在,我们将实现代码,来创建包含当天累计值的消息。 我们应该首先决定在哪个类中实现这个功能。 许多人可能会在 C_Chart_IDE 类中创建此函数,但出于组织原因,最好将其与处理订单的函数放在一起。 因此,代码是在 C_OrderView 类中实现的。 其代码示意如下:

double UpdateRoof(void)
{
        ulong   ticket;
        int     max;
        string  szSymbol = Terminal.GetSymbol();
        double  Accumulated = 0;
                                
        HistorySelect(macroGetDate(TimeLocal()), TimeLocal());
        max = HistoryDealsTotal();
        for (int c0 = 0; c0 < max; c0++) if ((ticket = HistoryDealGetTicket(c0)) > 0)
                if (HistoryDealGetString(ticket, DEAL_SYMBOL) == szSymbol)
                        Accumulated += HistoryDealGetDouble(ticket, DEAL_PROFIT);
                                                
        return Accumulated;
}


现在代码已经实现,我们需要将消息添加到系统中。 为了让操盘手的生活更轻松,我已经添加了代码来报告已完成的结果。 此为其代码:

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        static double AccumulatedRoof = 0.0;
        bool    b0;
        double  d0;

        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if ((b0 = (szArg == szMsgIDE[eRESULT])) || (szArg == szMsgIDE[eROOF_DIARY]))
                        {
                                if (b0)
                                {
                                        ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                                }else
                                {
                                        AccumulatedRoof = dValue;
                                        dValue = 0;
                                }
                                d0 = AccumulatedRoof + dValue;
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eROOF_DIARY].szName, OBJPROP_TEXT, DoubleToString(MathAbs(d0), 2));
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eROOF_DIARY].szName, OBJPROP_BGCOLOR, (d0 >= 0 ? clrForestGreen : clrFireBrick));
                        }else   if (szArg == szMsgIDE[eLABEL_SYMBOL])
                        {
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_TEXT, Terminal.GetSymbol());
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eLABEL_SYMBOL].szName, OBJPROP_ALIGN, ALIGN_CENTER);
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:

// .... The rest of the code....

        }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值