English Русский Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
preview
从头开始开发智能交易系统(第 23 部分):新订单系统 (VI)

从头开始开发智能交易系统(第 23 部分):新订单系统 (VI)

MetaTrader 5交易 | 17 十月 2022, 10:27
1 295 0
Daniel Jose
Daniel Jose

概述

在上一篇文章从头开始开发交易系统(第 22 部分)中,我们开发了一个移动挂单和持仓破位价的系统。 尽管这种方法相对安全(因为它反映的是交易服务器上的内容),然而它不是快速移动价位的最佳方式。

问题在于,每次我们用鼠标更改某些内容时,该事件都会发送到服务器,然后我们必须等待响应。 该问题与每次跳价时都要发送事件这一事实有关,也就是说,如果在某个时刻由于若干次跳价我们需要移动一次价位,我们不得不遍历所有中间值,这令整个过程非常缓慢。 这就是我们要如何实现代码的修改,令其更灵活且更快速地修改价位。


1.0. 计划

为了实现修改,我们需要做一件非常简单的事情:不要通知服务器所有更改,只通知所需的变更。 即便只是这样做,业已令一切操作正常,尽管我们不能绝对确保一切都恰如我们正在做的一样。

现在我们来看看我们需要在哪里修改代码。 我们所用的单独函数如下所示:

#define macroGetPrice(A) StringToDouble(ObjectGetString(Terminal.Get_ID(), MountName(ticket, A, EV_LINE), OBJPROP_TOOLTIP))
                void MoveSelection(double price, uint keys)
                        {
                                static string memStr = NULL;
                                static ulong ticket = 0;
                                static eIndicatorTrade it;
                                eEventType ev;
                                double tp, sl, pr;
                                bool isPending;
                                
                                string sz0 = m_TradeLine.GetObjectSelected();
                                
                                if (sz0 != NULL)
                                {
                                        if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev);
                                        isPending = OrderSelect(ticket);
                                        switch (it)
                                        {
                                                case IT_TAKE:
                                                        if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP));
                                                        else ModifyPosition(ticket, price, macroGetPrice(IT_STOP));
                                                        break;
                                                case IT_STOP:
                                                        if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price);
                                                        else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price);
                                                        break;
                                                case IT_PENDING:
                                                        pr = macroGetPrice(IT_PENDING);
                                                        tp = macroGetPrice(IT_TAKE);
                                                        sl = macroGetPrice(IT_STOP);
                                                        ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr));
                                                        break;
                                        }
                                };
                        }
#undef macroGetPrice

但是,伴随函数内部的修改,我们还需要实现一些与鼠标事件相关的修改。 这就是我们首先要关注的。

高亮显示的片段需要替换为其它内容,从而表示将会用到新仓位。 但是,对于用户来说,所有修改都应该能够理解。

我已找到了一种有效的方式,可令所有内容易于理解,同时又可以运作,而且无需对整个代码结构进行重大更改。 这就是创建一个幻影指示标签,其在需要之前将不可见。 幸运的是,MetaTrader 5 提供了一种非常简单的途径来做到这一点。 如此,对于那些已熟悉本系列早期文章中的素材的人来说,很容易就能理解本文内容。


2.0. 实现

为了实现一个幻影标签,我们简单地按照真实标签来创建它。 这它是真实标签的精确阴影,直到我们继续按照上一篇文章中展示的方式操控价格。 在此刻,幻影标签就会随着真实标签的移动而出现在图表上。 这将令您能够轻松比较正在发生的事情,并了解是否要进行更改。


2.0.1. 创建一个幻影指示标签

所有修改都在 C_IndicatorTradeView 类中实现。 我们从定义三条新指令开始:

#define def_IndicatorGhost      "G"
#define def_IndicatorReal       "R"
#define def_IndicatorGhostColor clrDimGray

这可令 MetaTrader 5 为我们工作。 该规则是这样的:首先我们创建一个幻影标签,然后我们创建一个真实标签。 因此,MetaTrader 5 会注意不显示幻影标签,直到我们真正需要看到它的那一刻。 由于这是由 MetaTrader 5 自身完成的,我们在编程时就节省了大量精力。

一个重要的细节:如果您打算更改幻影的颜色,简单地更改在所选部件中指定的颜色即可。

因此,下一步是修改函数,只允许生成唯一的名称。

inline string MountName(ulong ticket, eIndicatorTrade it, eEventType ev, bool isGhost = false)
{
        return StringFormat("%s%c%c%c%llu%c%c%c%s", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)(isGhost ? ev + 32 : ev), def_SeparatorInfo, (isGhost ? def_IndicatorGhost : def_IndicatorReal));
}

来自先前版本的高亮显示部分已经过添加或修改。 我们让 MetaTrader 5 生成唯一的名称。 如此,我们就不需要再担心这一点。 请注意,我已向事件中添加了一些数值。 我如此做是为了防止幻影接收事件,并试图控制。

下一步是显而易见的 — 我们必须创建标签本身:

inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it)
                        {
                                color cor1, cor2, cor3;
                                string sz0, sz1;
                                
                                switch (it)
                                {
                                        case IT_TAKE    :
                                                cor1 = clrForestGreen;
                                                cor2 = clrDarkGreen;
                                                cor3 = clrNONE;
                                                break;
                                        case IT_STOP    :
                                                cor1 = clrFireBrick;
                                                cor2 = clrMaroon;
                                                cor3 = clrNONE;
                                                break;
                                        case IT_PENDING:
                                                cor1 = clrCornflowerBlue;
                                                cor2 = clrDarkGoldenrod;
                                                cor3 = def_ColorVolumeEdit;
                                                break;
                                        case IT_RESULT  :
                                        default:
                                                cor1 = clrDarkBlue;
                                                cor2 = clrDarkBlue;
                                                cor3 = def_ColorVolumeResult;
                                                break;
                                }
                                m_TradeLine.Create(ticket, MountName(ticket, it, EV_LINE, true), def_IndicatorGhostColor);
                                m_TradeLine.Create(ticket, MountName(ticket, it, EV_LINE), cor2);
                                if (ticket == def_IndicatorTicket0) m_TradeLine.SpotLight(MountName(ticket, IT_PENDING, EV_LINE));
                                if (it != IT_RESULT) m_BackGround.Create(ticket, sz0 = MountName(ticket, it, EV_GROUND, true), def_IndicatorGhostColor);
                                m_BackGround.Create(ticket, sz1 = MountName(ticket, it, EV_GROUND), cor1);
                                switch (it)
                                {
                                        case IT_TAKE:
                                        case IT_STOP:
                                        case IT_PENDING:
                                                m_BackGround.Size(sz0, 92, 22);
                                                m_BackGround.Size(sz1, 92, 22);
                                                break;
                                        case IT_RESULT:
                                                m_BackGround.Size(sz1, 84, 34);
                                                break;
                                }
                                m_BtnClose.Create(ticket, MountName(ticket, it, EV_CLOSE), def_BtnClose);
                                m_EditInfo1.Create(ticket, sz0 = MountName(ticket, it, EV_EDIT, true), def_IndicatorGhostColor, 0.0);
                                m_EditInfo1.Create(ticket, sz1 = MountName(ticket, it, EV_EDIT), cor3, 0.0);
                                m_EditInfo1.Size(sz0, 60, 14);
                                m_EditInfo1.Size(sz1, 60, 14);
                                if (it != IT_RESULT)
                                {
                                        m_BtnMove.Create(ticket, sz0 = MountName(ticket, it, EV_MOVE, true), "Wingdings", "u", 17, def_IndicatorGhostColor);
                                        m_BtnMove.Create(ticket, sz1 = MountName(ticket, it, EV_MOVE), "Wingdings", "u", 17, cor2);
                                        m_BtnMove.Size(sz1, 21, 21);
                                }else
                                {
                                        m_EditInfo2.Create(ticket, sz1 = MountName(ticket, it, EV_PROFIT), clrNONE, 0.0);
                                        m_EditInfo2.Size(sz1, 60, 14);
                                }
                        }

所有高亮显示的行都会产生幻影。 有一件事也许看起来很奇怪:为什么我们不重现所有的元素。 事实上,幻影并不是真实标签的精确复制品,而只是它的影子。 因此,我们不需要创建所有元素。 真实标签定义了应该发生的事情,而幻影只作为参考,即交易服务器将看到的内容。

现在进入需要更详细研究的部分,其中 MetaTrader 5 将真正努力工作。 您也许会认为在正确的位置排列对象需要做很多工作,但来看看源代码中实际更改的内容。

#define macroSetAxleY(A, B)     {                                                                       \
                m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND, B), y);                              \
                m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE, B), y);                                 \
                m_BtnClose.PositionAxleY(MountName(ticket, A, EV_CLOSE, B), y);                                 \
                m_EditInfo1.PositionAxleY(MountName(ticket, A, EV_EDIT, B), y, (A == IT_RESULT ? -1 : 0));      \
                m_BtnMove.PositionAxleY(MountName(ticket, A, EV_MOVE, B), (A == IT_RESULT ? 9999 : y));         \
                m_EditInfo2.PositionAxleY(MountName(ticket, A, EV_PROFIT, B), (A == IT_RESULT ? y : 9999), 1);  \
                                }
                                                                        
#define macroSetAxleX(A, B, C)  {                                                       \
                m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND, C), B);      \
                m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE, C), B);         \
                m_BtnClose.PositionAxleX(MountName(ticket, A, EV_CLOSE, C), B + 3);     \
                m_EditInfo1.PositionAxleX(MountName(ticket, A, EV_EDIT, C), B + 21);    \
                m_BtnMove.PositionAxleX(MountName(ticket, A, EV_MOVE, C), B + 80);      \
                m_EditInfo2.PositionAxleX(MountName(ticket, A, EV_PROFIT, C), B + 21);  \
                                }                                                                               
//+------------------------------------------------------------------+
inline void ReDrawAllsIndicator(void)
                        {
                                int             max = ObjectsTotal(Terminal.Get_ID(), -1, -1);
                                ulong           ticket;
                                double          price;
                                eIndicatorTrade it;
                                eEventType      ev;
                                
                                for (int c0 = 0; c0 <= max; c0++) if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, -1), ticket, price, it, ev))
                                        PositionAxlePrice(ticket, it, price);
                        }
//+------------------------------------------------------------------+
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                ObjectSetString(Terminal.Get_ID(), MountName(ticket, it, EV_LINE), OBJPROP_TOOLTIP, DoubleToString(price));
                                macroSetAxleY(it, true);
                                macroSetAxleY(it, false);
                                switch (it)
                                {
                                        case IT_TAKE: desl = 110; break;
                                        case IT_STOP: desl = 220; break;
                                        default: desl = 0;
                                }
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2), true);
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2), false);
                        }
#undef macroSetAxleX
#undef macroSetAxleY

全部就这些吗? 我们只需简单地更改宏? 是的,没有必要重新创建整个代码,我们只需调整它即可。 我们真正需要做的所有就是告诉 MetaTrader 5 我们正在操作的对象名称,而 MetaTrader 5 将为我们完成剩下的工作。 我们不需要为此创建一系列函数。 只需添加高亮显示的部分即可。

要更改的另一个函数在此如下所示:

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;
                                
        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        value0 = value0 / Terminal.GetVolumeMinimal();
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0, def_ColorVolumeEdit);
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), value0, def_IndicatorGhostColor);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), finance, def_IndicatorGhostColor);
                        break;
        }
}

于此,我们已添加了选定的片段。 这就是我们的幻影如何被选择的。 它精准地反映了真实标签正在发生的事情。 事实上,它反映得太好了,以至于我们不得不再实现一些东西,才能让它正常工作。 代码没有错,但幻影与真实标签的关系太紧密,而这我们实际上应该避免。

此处最后一个要更改的函数如下所示:

inline void RemoveIndicator(ulong ticket, eIndicatorTrade it = IT_NULL)
                        {
#define macroDestroy(A, B)      {                                                                               \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND, B));                            \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE, B));                              \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_CLOSE, B));                             \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_EDIT, B));                              \
                if (A != IT_RESULT)     ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_MOVE, B));      \
                else ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_PROFIT, B));                       \
                                }

                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                if ((it == IT_NULL) || (it == IT_PENDING) || (it == IT_RESULT))
                                {
                                        macroDestroy(IT_RESULT, true);
                                        macroDestroy(IT_RESULT, false);
                                        macroDestroy(IT_PENDING, true);
                                        macroDestroy(IT_PENDING, false);
                                        macroDestroy(IT_TAKE, true);
                                        macroDestroy(IT_TAKE, false);
                                        macroDestroy(IT_STOP, true);
                                        macroDestroy(IT_STOP, false);
                                } else
                                {
                                        macroDestroy(it, true);
                                        macroDestroy(it, false);
                                }
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
#undef macroDestroy
                        }

高亮显示的片段已经过更改,这里没什么特别的。


2.0.2. 将幻影自真实剥离

上一章节中所做的修改会创建一个幻影。 但我们遇到一个问题 — 它太靠近真实对象了。 有些事乍一看,很难实现。 但当我们查看代码时,我们看到已经有了一个解决方案。 不过,此解决方案位于错误的位置。 我们需要改变解决方案所处的位置,并令其在整个类中更加明显。

该解决方案的代码如下所示:

                void DispatchMessage(int id, long lparam, double dparam, string sparam)
                        {
                                ulong   ticket;
                                double  price, tp, sl;
                                bool            isBuy,
                                                        bKeyBuy,
                                                        bKeySell,
                                                        bEClick;
                                long            info;
                                datetime        dt;
                                uint            mKeys;
                                eIndicatorTrade         it;
                                eEventType                      ev;
                                
                                static bool bMounting = false, bIsDT = false, bIsMove = false;
                                static double leverange = 0, valueTp = 0, valueSl = 0, memLocal = 0;
                                
                                switch (id)
                                {
                                        case CHARTEVENT_MOUSE_MOVE:

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

高亮显示的代码就是该解决方案。 但这怎么可能呢? 请记住,当您下挂单时,系统会设法创建和操纵数据,如此最终您在图表上得到一个表述标签,指示下订单的所在。 这是通过以下代码完成的:

case CHARTEVENT_MOUSE_MOVE:
        Mouse.GetPositionDP(dt, price);
        mKeys   = Mouse.GetButtonStatus();
        bEClick  = (mKeys & 0x01) == 0x01;    //left mouse click
        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT pressed
        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
        if (bKeyBuy != bKeySell)
        {
                if (!bMounting)
                {
                        Mouse.Hide();
                        bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl);
                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange);
                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange);
                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                        bMounting = true;
                }
                tp = price + (bKeyBuy ? valueTp : (-valueTp));
                sl = price + (bKeyBuy ? (-valueSl) : valueSl);
                UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy);
                if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT);
                }else if (bMounting)
                {
                        UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false);
                        Mouse.Show();
                        memLocal = 0;
                        bMounting = false;

//... Rest of the code...

取决于您是按 SHIFT 买入,亦或是按 CTRL 卖出,系统依据正在创建的内容生成挂单的表示形式。 它将直接显示在图表上。 在 Chart Trade 中捕获止盈或止损的数值,然后您可将表述移动到下订单的位置,之后,单击鼠标左键,告诉系统必须在那里下订单。 一旦鼠标再次移动,则表述订单的标签就会被删除,而订单指示将予以保留。

到目前为止,没有什么特别的。 但是,如果您查看代码,您将看到以下内容:

// ... CHARTEVENT_MOUSE_MOVE code....

        }else if ((!bMounting) && (bKeyBuy == bKeySell))
        {
                if (bEClick)
                {
                        bIsMove = false;
                        m_TradeLine.SpotLight();
                }
                MoveSelection(price, mKeys);
        }
break;

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

如此,当我们不创建挂单,并释放键时,我们会将鼠标价格位置发送到可能选中的某个标签。 这是在高亮显示的行中完成的。 一旦我们点击左键,我们就结束转移,指标会被取消选择,这将改变当前局部变量的状态,bIsMove。 但我们来改变它。 该变量的状态仅由 DispatchMessage 函数内部的另一个事件进行修改。 该事件如下所示:

// ... Code ....

        case EV_MOVE:
                if (bIsMove)
                {
                        m_TradeLine.SpotLight();
                        bIsMove = false;
                }else
                {
                        m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                        bIsMove = true;
                }
        break;

该段代码将更改 bIsMove 变量的状态,同时更改标签,从而示意它是否被选中。

那么,如果我们令该变量在整个类中可见,我们就可把幻影与真实标签分开,并且有可能只操纵真实标签或幻影 — 这取决于您觉得哪个更有趣。 但在这此,我们将更改真实标签,而幻影将显示交易服务器之所见。

以此方式,我们就不必过多地搅乱代码,只需调整一些细节即可。 一旦左键点击完成,且对象已被移动,则发送更改挂单或止损位的订单。

我们看看这在实践中是如何做到的。 首先,我们创建一个私密变量。

bool    m_bIsMovingSelect;

这反映了我上面解释的内容。 但它需要初始化。

C_IndicatorTradeView() : m_bIsMovingSelect(false) {}

现在,我们转到DispatchMessage,并用它来替代 bIsMove。

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;

// ... Internal code...

        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:

// ... Internal code...

                                }else if ((!bMounting) && (bKeyBuy == bKeySell))
                                {
                                        if (bEClick)
                                        {
                                                m_bIsMovingSelect = false;
                                                m_TradeLine.SpotLight();
                                        }
                                        MoveSelection(price, mKeys);
                                }
                                break;

// ... Internal code...

                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {

// ... Internal code....

                                case EV_MOVE:
                                        if (m_bIsMovingSelect)
                                        {
                                                m_TradeLine.SpotLight();
                                                m_bIsMovingSelect = false;
                                        }else
                                        {
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                                m_bIsMovingSelect = true;
                                        }
                                        break;
                        }
                        break;
                }
}

这些修改都以高亮显示。 因此,现在整个类都知道我们何时依据用户选择的某个标签操作。 以这种方式,我们可以将幻影与真实标签分开,并拥有更准确的表示。


2.0.3. 只移动所要的材料

在之前两个主题中,我们创建并修复了系统,从而能用一个幻影指代标签。 现在我们需要移动组件,为此,我们将不得不做一些工作。 第一步是创建一个结构来存储我们所需的各种信息,从而避免函数之间的过多调用。 该结构如下图所示:

struct st00
{
        eIndicatorTrade it;
        bool            bIsMovingSelect,
                        bIsBuy;
        ulong           ticket;
        double          vol,
                        pr,
                        tp,
                        sl;
}m_InfoSelection;

高亮显示的片段以前就存在于代码当中,但现在它作为结构的一部分。 不用担心。 随着我们走得更远,结构为什么拥这些元素将变得更加清晰。

在此,我们研究已修改并需要解释的函数。 第一处是设置文本值。

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;

        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        value0 = value0 / Terminal.GetVolumeMinimal();
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0, def_ColorVolumeEdit);
                        if (!m_InfoSelection.bIsMovingSelect) m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), value0, def_IndicatorGhostColor);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        if (!m_InfoSelection.bIsMovingSelect) m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT, true), finance, def_IndicatorGhostColor);
                        break;
        }
}

当我们移动某些东西时,我们不希望幻影跟随新数据变更,我们希望它保持原样,以便展示标签曾经的位置。 这可以通过添加高亮显示的点轻松做到。 因此,当标签自由移动时,幻影立定不动。 在此,我们并没有移动其本身,而是服务器上的数值指示。

后跟的移动代码如下所示:

void MoveSelection(double price)
{
        double tp, sl;
                                
        if (!m_InfoSelection.bIsMovingSelect) return;
        switch (m_InfoSelection.it)
        {
                case IT_TAKE:
                        UpdateInfosIndicators(0, m_InfoSelection.ticket, m_InfoSelection.pr, price, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
                        break;
                case IT_STOP:
                        UpdateInfosIndicators(0, m_InfoSelection.ticket, m_InfoSelection.pr, m_InfoSelection.tp, price, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
                        break;
                case IT_PENDING:
                        tp = (m_InfoSelection.tp == 0 ? 0 : price + m_InfoSelection.tp - m_InfoSelection.pr);
                        sl = (m_InfoSelection.sl == 0 ? 0 : price + m_InfoSelection.sl - m_InfoSelection.pr);
                        UpdateInfosIndicators(0, m_InfoSelection.ticket, price, tp, sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
                        break;
        }
}

请注意高亮显示的部分。 它调整移动,如此系统便可正确指示数据。 这也许看起来有些奇怪,但 UpdateINfosIndicators 函数还有另一处修复。 如果我们现在不这样做,我们以后会遇到问题。 其它函数非常简单,无需解释。

void SetPriceSelection(double price)
{
        bool isPending;
        if (!m_InfoSelection.bIsMovingSelect) return;
        isPending = OrderSelect(m_InfoSelection.ticket);
        m_InfoSelection.bIsMovingSelect = false;
        m_TradeLine.SpotLight();
        switch (m_InfoSelection.it)
        {
                case IT_TAKE:
                        if (isPending) ModifyOrderPendent(m_InfoSelection.ticket, m_InfoSelection.pr, price, m_InfoSelection.sl);
                        else ModifyPosition(m_InfoSelection.ticket, price, m_InfoSelection.sl);
                        break;
                case IT_STOP:
                        if (isPending) ModifyOrderPendent(m_InfoSelection.ticket, m_InfoSelection.pr, m_InfoSelection.tp, price);
                        else ModifyPosition(m_InfoSelection.ticket, m_InfoSelection.tp, price);
                        break;
                case IT_PENDING:
                        ModifyOrderPendent(m_InfoSelection.ticket, price, (m_InfoSelection.tp == 0 ? 0 : price + m_InfoSelection.tp - m_InfoSelection.pr), (m_InfoSelection.sl == 0 ? 0 : price + m_InfoSelection.sl - m_InfoSelection.pr));
                        break;
        }
}

上述函数将通知服务器正在发生的事情,以及新数据是什么。 请注意,必须在高亮显示的行中选择一些指标,否则将不会向服务器发出请求。

最后推荐的函数如下所示:

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;
        double  price;
        bool    bKeyBuy,
                bKeySell,
                bEClick;
        datetime dt;
        uint    mKeys;
        char    cRet;
        eIndicatorTrade         it;
        eEventType              ev;
                                
        static bool bMounting = false, bIsDT = false;
        static double valueTp = 0, valueSl = 0, memLocal = 0;
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse click
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //Pressed SHIFT
                        bKeySell = (mKeys & 0x08) == 0x08;    //Pressed CTRL
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        Mouse.Hide();
                                        bIsDT = Chart.GetBaseFinance(m_InfoSelection.vol, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_InfoSelection.vol);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_InfoSelection.vol);
                                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                                        m_InfoSelection.it = IT_PENDING;
                                        m_InfoSelection.ticket = def_IndicatorTicket0;
                                        m_InfoSelection.bIsMovingSelect = true;
                                        m_InfoSelection.pr = price;
                                        bMounting = true;
                                }
                                m_InfoSelection.tp = m_InfoSelection.pr + (bKeyBuy ? valueTp : (-valueTp));
                                m_InfoSelection.sl = m_InfoSelection.pr + (bKeyBuy ? (-valueSl) : valueSl);
                                m_InfoSelection.bIsBuy = bKeyBuy;
                                MoveSelection(price);
                                if ((bEClick) && (memLocal == 0))
                                {
                                        MoveSelection(0);
                                        m_InfoSelection.bIsMovingSelect = false;
                                        CreateOrderPendent(m_InfoSelection.vol, bKeyBuy, memLocal = price,  price + m_InfoSelection.tp - m_InfoSelection.pr, price + m_InfoSelection.sl - m_InfoSelection.pr, bIsDT);
                                }
                        }else if (bMounting)
                        {
                                MoveSelection(0);
                                m_InfoSelection.bIsMovingSelect = false;
                                Mouse.Show();
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell))
                        {
                                if (bEClick) SetPriceSelection(price); else MoveSelection(price);
                        }
                        break;
                case CHARTEVENT_OBJECT_DELETE:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev))
                        {
                                CreateIndicatorTrade(ticket, it);
                                GetInfosTradeServer(ticket);
                                m_InfoSelection.bIsMovingSelect = false;
                                UpdateInfosIndicators(0, ticket, m_InfoSelection.pr, m_InfoSelection.tp, m_InfoSelection.sl, m_InfoSelection.vol, m_InfoSelection.bIsBuy);
                        }
                        break;
                case CHARTEVENT_CHART_CHANGE:
                        ChartSetInteger(ChartID(), CHART_SHOW_OBJECT_DESCR, false);
                        ReDrawAllsIndicator();
                        break;
                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {
                                case EV_CLOSE:
                                        if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it)
                                        {
                                                case IT_PENDING:
                                                case IT_RESULT:
                                                        if (cRet < 0) RemoveOrderPendent(ticket); else ClosePosition(ticket);
                                                        break;
                                                case IT_TAKE:
                                                case IT_STOP:
                                                        m_InfoSelection.bIsMovingSelect = true;
                                                        SetPriceSelection(0);
                                                        break;
                                        }
                                        break;
                                case EV_MOVE:
                                        if (m_InfoSelection.bIsMovingSelect)
                                        {
                                                m_TradeLine.SpotLight();
                                                m_InfoSelection.bIsMovingSelect = false;
                                        }else
                                        {
                                                m_InfoSelection.ticket = ticket;
                                                m_InfoSelection.it = it;
                                                if (m_InfoSelection.bIsMovingSelect = (GetInfosTradeServer(ticket) != 0))
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                        }
                                        break;
                        }
                        break;
        }
}

关于该函数,有些事情需要注意,即要关注与以前版本的区别:我们在此重用了更多的代码。 故此,若在其它地方,我们的代码中有错误,我们就可以快速察觉它,并予以修复。 以前,该函数代码有点不稳定,其它部分存在一些修复和缺陷。 其中一个要点就是,当我们下挂单时,有一个单独的系统用于移动指标;现在我们有一个单一的系统,与在图表上下订单的系统相同。 它也能用于移动对象,即现在相同的 CHARTEVENT_MOUSE_MOVE 事件既用于下挂单,又可用于移动对象。 它也许看起来像小事一桩,但它令代码的任何更改均可见,如果我们遇到问题,它就会尽可能长地显示出来,如同我们所用的鼠标事件。


结束语

请观看下面的视频,以便理清思路,了解所做的更改会发生什么。 您将看到,现在我们只需稍多细节即可令 EA 在订单系统方面全部完成。


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

附加的文件 |
神经网络变得轻松(第二十部分):自动编码器 神经网络变得轻松(第二十部分):自动编码器
我们继续研究无监督学习算法。 一些读者可能对最近发表的与神经网络主题的相关性有疑问。 在这篇新文章中,我们回到了对神经网络的研究。
DoEasy. 控件 (第 10 部分): WinForms 对象 — 动画界面 DoEasy. 控件 (第 10 部分): WinForms 对象 — 动画界面
现在是时候实现动画图形界面功能,方便用户与对象的交互了。 为了让更复杂的对象能正确工作,还需要新功能。
DoEasy. 控件 (第 11 部分): WinForms 对象 — 群组,CheckedListBox WinForms 对象 DoEasy. 控件 (第 11 部分): WinForms 对象 — 群组,CheckedListBox WinForms 对象
本文将讨论 WinForms 对象群组,及创建 CheckBox 对象列表对象。
价格走势模型及其主要规定(第 1 部分):概率价格域演化方程与发生的可观测随机游走 价格走势模型及其主要规定(第 1 部分):概率价格域演化方程与发生的可观测随机游走
本文研究的是概率价格域演化方程,与即将到来的价格尖峰准则。 它还揭示了图表上价格数值的本质,以及这些数值随机游走的发生机制。