English Русский 中文 Español Deutsch 日本語 Português Français Italiano Türkçe
preview
트레이딩 전문가 어드바이저를 처음부터 개발하기(27부): 다음을 향해(II)

트레이딩 전문가 어드바이저를 처음부터 개발하기(27부): 다음을 향해(II)

MetaTrader 5 | 4 4월 2024, 10:48
296 0
Daniel Jose
Daniel Jose

소개

이전 글인 Expert Advisor 처음부터 개발하기(26부)에서 우리는 주문 시스템에 존재하던 치명적인 오류를 수정했습니다. 우리는 또한 새로운 주문 시스템을 운영할 수 있도록 변경 사항을 구현하기 시작했습니다. 이 시리즈에서 원래 구현된 시스템은 매우 흥미롭지만 작동이 불가능한 결함이 있습니다. 이 결함은 이전 글의 마지막 부분에 설명되어 있습니다. 그 이유는 다른 작은 문제 외에도 거래 방법 특히 주문 또는 포지션의 만기 시간을 선택하는 방법을 우리가 몰랐기 때문입니다. 시스템은 거래 세션이 끝나거나 당일에 청산해야 하는 주문 또는 포지션인 것으로 고정되었습니다. 하지만 우리는 때로는 장기적인 트레이딩을 하고 싶을 때가 있습니다. 그러므로 모든 것을 그대로 두는 것은 큰 도움이 되지 않습니다.

따라서 이 글에서 저는 그 해결 방법을 보여드리겠습니다. 우리는 각 주문에 대해 이 주문들이 각각 무엇인지, 어떻게 처리되는지, 어떤 종류의 움직임이 예상되는지 즉각적이고 정확하게 파악할 수 있도록 주문 시스템을 보다 직관적으로 만드는 방법을 살펴볼 것입니다.

이 시스템은 매우 흥미롭고 간단하며 직관적이어서 일단 작동하는 모습을 보고 나면 여러분은 이 시스템 없이는 일하고 싶은 생각이 들지 않을 것입니다. 제가 이 글에서 보여드릴 내용은 주문 시스템에서 여러분이 구현할 수 있는 여러 가지 가능성들 중에 하나에 불과합니다. 나중에 다른 것들을 보여드리겠지만 우리가 이 기사에서 살펴볼 내용이 다른 경우에 유용하고 흥미로운 여러분만의 수정 기능을 만드는 데 훌륭한 기초가 될 수 있습니다. 어쨌든 저는 이 글의 모든 내용을 가능한 한 일반적인 것으로 유지하려고 노력합니다.


2.0. 직관적인 모델

지금까지 우리는 다음과 같이 주문을 처리해 왔습니다:

수익실현 및 손절가 표시가 눈에 잘 띄는 형태로 되어 있어 매우 직관적입니다. 녹색은 계좌에 입금되는 금액을, 빨간색은 차감되는 금액을 표시합니다. 모든 것이 명확합니다. 아래와 같이 손절매 표시가 있는 경우에도 손절매가 활성화되면 이 금액이 계좌에 더해 진다는 것을 보여줍니다. 다시 말해 스톱 오더 레벨의 경우 적어도 지금은 우리가 수정할 이유가 없습니다. 아마도 미래에는 무언가를 변경하고 싶을 수도 있지만 현재로서는 사용하기에 매우 적합합니다.

스톱 레벨을 사용하는 이 방법은 모든 트레이더가 분석 하기에 매우 간단하고 직관적입니다. 하지만 명확하지 않은 부분이 있습니다. 첫 번째는 펜딩 오더의 진입점 표시입니다.

우리는 펜딩 오더가 매수 주문인지 매도 주문인지 알 수 있을까요? 그리고 한 가지 더: 이 지정가 주문이 하루가 끝날 때 청산될지 아니면 장기 포지션으로 개설될지 알 수 있을까요? 간단합니다. 이제 우리에게 이미 오픈 포지션이 있다면 지표는 아래와 같이 표시됩니다:

펜딩 중인 주문 지표와 동일한 문제가 다시 발생합니다. 차트를 보고 이러한 지표를 볼 때 여러분은 포지션이 하루가 끝날 때 청산될지 아니면 더 오래 지속될지 확신할 수 없습니다. 하루가 끝날 때 청산되면 여러분은 비용을 지불해야 하므로 브로커가 포지션을 강제로 청산하는 것을 원하지 않을 것입니다. MetaTrader 도구 상자조차도 이 정보를 표시하지 않기 때문에 기준없이 주문을 청산하는 것은 그다지 합리적이지 않습니다. 그러므로 지표를 통해 차트에 표시하는 것이 좋습니다.

따라서 현재 상황을 더 잘 이해하기 위해 우리는 특히 포지션 진입 지점을 표시하는 지표를 변경해야 합니다.


2.0.1. 지표에 새 정보를 추가하는 방법

차트에서 많은 공간을 차지하지 않고 새로운 정보를 추가하는 가장 쉬운 방법은 비트맵을 사용하는 것입니다. 왜냐하면 비트맵은 이해하기 쉽고 대표성이 높기 때문입니다. 따라서 우리는 추가 코드를 삽입하지 않고도 EA에 4개의 새 비트맵을 추가할 수 있습니다. 이는 C_IndicatorTradeView 클래스에서 확인할 수 있습니다.

#define def_BtnClose            "Images\\NanoEA-SIMD\\Btn_Close.bmp"
#define def_BtnCheckEnabled     "Images\\NanoEA-SIMD\\CheckBoxEnabled.bmp"
#define def_BtnCheckDisabled    "Images\\NanoEA-SIMD\\CheckBoxDisabled.bmp"
#define def_BtnDayTrade         "Images\\NanoEA-SIMD\\Inf_DayTrade.bmp"
#define def_BtnSwing            "Images\\NanoEA-SIMD\\Inf_Swing.bmp"
#define def_BtnInfoBuy          "Images\\NanoEA-SIMD\\Inf_Buy.bmp"
#define def_BtnInfoSell         "Images\\NanoEA-SIMD\\Inf_Sell.bmp"
//+------------------------------------------------------------------+
#resource "\\" + def_BtnClose
#resource "\\" + def_BtnCheckEnabled
#resource "\\" + def_BtnCheckDisabled
#resource "\\" + def_BtnDayTrade
#resource "\\" + def_BtnSwing
#resource "\\" + def_BtnInfoBuy
#resource "\\" + def_BtnInfoSell

또한 우리는 주문 시스템에서 두 개의 새 객체만 구현하면 됩니다.

//+------------------------------------------------------------------+
enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PENDING, IT_RESULT};
enum eEventType {EV_NULL, EV_GROUND = 65, EV_LINE, EV_CLOSE, EV_EDIT, EV_PROFIT, EV_MOVE, EV_CHECK, EV_TYPE, EV_DS};
//+------------------------------------------------------------------+
C_Object_BackGround     m_BackGround;
C_Object_TradeLine      m_TradeLine;
C_Object_BtnBitMap      m_BtnClose,
                        m_BtnCheck,
                        m_BtnInfoType,
                        m_BtnInfo_DS;
C_Object_Edit           m_EditInfo1,
                        m_EditInfo2;
C_Object_Label          m_BtnMove;

새 객체를 추가할 때마다 객체에 연결된 EVENT를 추가하여 객체가 고유한 이름을 갖도록 해야 합니다.

이제 프로그래밍의 가장 흥미로운 부분이 시작됩니다. 가장 먼저 해야 할 일은 유령들을 돌보는 것입니다. 유령들이 정보를 보관할 수 있도록 하기 위해 우리는 이 유령들을 업데이트해야 합니다. 물론 삭제할 수도 있지만 저는 기본 데이터를 남겨두는 것이 좋다고 생각합니다. 다음 코드를 살펴보세요:

#define macroSwapName(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorGhost, A, B));
                void CreateGhostIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                if (GetInfosTradeServer(m_Selection.ticket = ticket) != 0)
                                {
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                        macroSwapName(it, EV_LINE);
                                        macroSwapName(it, EV_GROUND);
                                        macroSwapName(it, EV_MOVE);
                                        macroSwapName(it, EV_EDIT);
                                        macroSwapName(it, EV_CLOSE);
                                        if (it == IT_PENDING)
                                        {
                                                macroSwapName(it, EV_CHECK);
                                                macroSwapName(it, EV_TYPE);
                                                macroSwapName(it, EV_DS);
                                        }
                                        m_TradeLine.SetColor(macroMountName(def_IndicatorGhost, it, EV_LINE), def_IndicatorGhostColor);
                                        m_BackGround.SetColor(macroMountName(def_IndicatorGhost, it, EV_GROUND), def_IndicatorGhostColor);
                                        m_BtnMove.SetColor(macroMountName(def_IndicatorGhost, it, EV_MOVE), def_IndicatorGhostColor);
                                        ObjectDelete(Terminal.Get_ID(), macroMountName(def_IndicatorGhost, it, EV_CLOSE));
                                        m_TradeLine.SpotLight();
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                                        m_Selection.it = it;
                                }else m_Selection.ticket = 0;
                        }
#undef macroSwapName

강조 표시된 선은 객체를 고스트에게 전달하며 매우 간단하고 명확합니다. 또 다른 간단한 코드는 지표를 보류 중인 상태에서 부동 상태로 변환합니다.

#define macroSwapAtFloat(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorFloat, A, B));
                bool PendingAtFloat(ulong ticket)
                        {
                                eIndicatorTrade it;
                                
                                if (macroGetLinePrice(def_IndicatorFloat, IT_PENDING) > 0) return false;
                                macroSwapAtFloat(IT_PENDING, EV_CHECK);
                                macroSwapAtFloat(IT_PENDING, EV_TYPE);
                                macroSwapAtFloat(IT_PENDING, EV_DS);
                                for (char c0 = 0; c0 < 3; c0++)
                                {
                                        switch(c0)
                                        {
                                                case 0: it = IT_PENDING;        break;
                                                case 1: it = IT_STOP;           break;
                                                case 2: it = IT_TAKE;           break;
                                                default:
                                                        return false;
                                        }
                                        macroSwapAtFloat(it, EV_CLOSE);
                                        macroSwapAtFloat(it, EV_MOVE);
                                        macroSwapAtFloat(it, EV_EDIT);
                                        macroSwapAtFloat(it, EV_GROUND);
                                        macroSwapAtFloat(it, EV_LINE);
                                        m_EditInfo1.SetOnlyRead(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT), false);
                                }
                                return true;
                        }
#undef macroSwapAtFloat

강조 표시된 선은 객체를 부동 지표로 변환하여 우리가 필요한 작업을 나중에 수행할 수 있도록 합니다. 이제 우리는 지표를 만드는 코드에서 몇 가지 변경 사항을 구현해야 합니다. 여러분이 마음에 들 때까지 테스트하고 조정할 수 있는 기능입니다. 기본적으로 아래 코드에서 강조 표시된 부분이 변경되었습니다:

#define macroCreateIndicator(A, B, C, D)        {                                                                               \
                m_TradeLine.Create(ticket, sz0 = macroMountName(ticket, A, EV_LINE), C);                                        \
                m_BackGround.Create(ticket, sz0 = macroMountName(ticket, A, EV_GROUND), B);                                     \
                m_BackGround.Size(sz0, (A == IT_RESULT ? 100 : (A == IT_PENDING ? 144 : 92)), (A == IT_RESULT ? 34 : 22));      \
                m_EditInfo1.Create(ticket, sz0 = macroMountName(ticket, A, EV_EDIT), D, 0.0);                                   \
                m_EditInfo1.Size(sz0, 60, 14);                                                                                  \
                if (A != IT_RESULT)     {                                                                                       \
                        m_BtnMove.Create(ticket, sz0 = macroMountName(ticket, A, EV_MOVE), "Wingdings", "u", 17, C);            \
                        m_BtnMove.Size(sz0, 21, 23);                                                                            \
                                        }else                   {                                                               \
                        m_EditInfo2.Create(ticket, sz0 = macroMountName(ticket, A, EV_PROFIT), clrNONE, 0.0);                   \
                        m_EditInfo2.Size(sz0, 60, 14);  }                                                                       \
                                                }
                                                                                                                
#define macroInfoBase(A)        {                                                                                               \
                m_BtnInfoType.Create(ticket, sz0 = macroMountName(ticket, A, EV_TYPE), def_BtnInfoBuy, def_BtnInfoSell);        \
                m_BtnInfoType.SetStateButton(sz0, m_Selection.bIsBuy);                                                          \
                m_BtnInfo_DS.Create(ticket, sz0 = macroMountName(ticket, A, EV_DS), def_BtnDayTrade, def_BtnSwing);             \
                m_BtnInfo_DS.SetStateButton(sz0, m_Selection.bIsDayTrade);                                                      \
                                }

                void CreateIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                string sz0;
                                
                                switch (it)
                                {
                                        case IT_TAKE    : macroCreateIndicator(it, clrForestGreen, clrDarkGreen, clrNONE); break;
                                        case IT_STOP    : macroCreateIndicator(it, clrFireBrick, clrMaroon, clrNONE); break;
                                        case IT_PENDING:
                                                macroCreateIndicator(it, clrCornflowerBlue, clrDarkGoldenrod, def_ColorVolumeEdit);
                                                m_BtnCheck.Create(ticket, sz0 = macroMountName(ticket, it, EV_CHECK), def_BtnCheckEnabled, def_BtnCheckDisabled);
                                                m_BtnCheck.SetStateButton(sz0, true);
                                                macroInfoBase(IT_PENDING);
                                                break;
                                        case IT_RESULT  :
                                                macroCreateIndicator(it, clrSlateBlue, clrSlateBlue, def_ColorVolumeResult);
                                                macroInfoBase(IT_RESULT);
                                                break;
                                }
                                m_BtnClose.Create(ticket, macroMountName(ticket, it, EV_CLOSE), def_BtnClose);
                        }
#undef macroInfoBase
#undef macroCreateIndicator

macroInfoBase는 지표에 사용되는 객체를 생성하지만 이러한 객체는 포지션 개시 및 포지션 결과 지표에만 생성되며 다른 지표에서는 이러한 객체를 생성할 필요가 없습니다. 그러나 객체를 만든 위치에 객체를 배치하지 않는다는 점에 유의하세요. 이 작업은 다른 위치에서 수행됩니다. 다음에 표시된 위치입니다.

#define macroSetAxleY(A)                {                                                                               \
                m_BackGround.PositionAxleY(macroMountName(ticket, A, EV_GROUND), y);                                    \
                m_TradeLine.PositionAxleY(macroMountName(ticket, A, EV_LINE), y);                                       \
                m_BtnClose.PositionAxleY(macroMountName(ticket, A, EV_CLOSE), y);                                       \
                if (A != IT_RESULT)m_BtnMove.PositionAxleY(macroMountName(ticket, A, EV_MOVE), y, 1);                   \
                else m_EditInfo2.PositionAxleY(macroMountName(ticket, A, EV_PROFIT), y, 1);                             \
                m_EditInfo1.PositionAxleY(macroMountName(ticket, A, EV_EDIT), y, (A == IT_RESULT ? -1 : 0));            \
                if (A == IT_PENDING) m_BtnCheck.PositionAxleY(macroMountName(ticket, A, EV_CHECK), y);                  \
                if ((A == IT_PENDING) || (A == IT_RESULT))      {                                                       \
                        m_BtnInfoType.PositionAxleY(macroMountName(ticket, A, EV_TYPE), y + (A == IT_PENDING ? 0 : 8)); \
                        m_BtnInfo_DS.PositionAxleY(macroMountName(ticket, A, EV_DS), y - (A == IT_PENDING ? 0: 8));     \
                                                                }                                                       \
                                        }
                                                                        
#define macroSetAxleX(A, B)             {                                                                                               \
                m_BackGround.PositionAxleX(macroMountName(ticket, A, EV_GROUND), B);                                                    \
                m_TradeLine.PositionAxleX(macroMountName(ticket, A, EV_LINE), B);                                                       \
                m_BtnClose.PositionAxleX(macroMountName(ticket, A, EV_CLOSE), B + 3);                                                   \
                m_EditInfo1.PositionAxleX(macroMountName(ticket, A, EV_EDIT), B + 21);                                                  \
                if (A != IT_RESULT) m_BtnMove.PositionAxleX(macroMountName(ticket, A, EV_MOVE), B + 80 + (A == IT_PENDING ? 52 : 0));   \
                else m_EditInfo2.PositionAxleX(macroMountName(ticket, A, EV_PROFIT), B + 21);                                           \
                if (A == IT_PENDING) m_BtnCheck.PositionAxleX(macroMountName(ticket, A, EV_CHECK), B + 82);                             \
                if ((A == IT_PENDING) || (A == IT_RESULT))      {                                                                       \
                        m_BtnInfoType.PositionAxleX(macroMountName(ticket, A, EV_TYPE), B + (A == IT_PENDING ? 100 : 82));              \
                        m_BtnInfo_DS.PositionAxleX(macroMountName(ticket, A, EV_DS), B + (A == IT_PENDING ? 118 : 82));                 \
                                                                }                                                                       \
                                        }
//---
        void ReDrawAllsIndicator(void)
                        {
                                C_IndicatorTradeView::st00 Local;
                                int             max = ObjectsTotal(Terminal.Get_ID(), -1, OBJ_EDIT);
                                ulong           ticket;
                                eIndicatorTrade it;
                                eEventType ev;
                                
                                Local = m_Selection;
                                m_Selection.ticket = 0;
                                for (int c0 = 0; c0 <= max; c0++)
                                   if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, OBJ_EDIT), ticket, it, ev))
                                      if ((it == IT_PENDING) || (it == IT_RESULT))
                                      {
                                        PositionAxlePrice(ticket, IT_STOP, macroGetLinePrice(ticket, IT_STOP));
                                        PositionAxlePrice(ticket, IT_TAKE, macroGetLinePrice(ticket, IT_TAKE));
                                        PositionAxlePrice(ticket, it, macroGetLinePrice(ticket, it));
                                        }
                                m_Selection = Local;
                                ChartRedraw();
                        }
//---
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                macroSetLinePrice(ticket, it, price);
                                macroSetAxleY(it);
                                switch (it)
                                {
                                        case IT_TAKE: desl = 160; break;
                                        case IT_STOP: desl = 270; break;
                                        default: desl = 0;
                                }
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2));
                        }
#undef macroSetAxleX
#undef macroSetAxleY

또한 제가 코드를 급격하고 급진적으로 변경하는 것을 좋아하지 않는다는 점을 강조하고 싶습니다. 기본적으로 위의 코드에는 변경 사항만 강조 표시되어 있습니다.


2.0.2. 눈에 보이는 문제

모든 것이 완벽하게 작동하지만 문제가 있습니다. 제가 모든 MQL5 문서를 검색했지만 간단한 방법으로 해결할 수 있는 방법을 찾지 못했습니다. 문제는 최근에 개설한 포지션이 데이 트레이딩(당일 내 단기매매)인지 스윙 트레이딩(장기매매)인지를 확인하는 방법입니다. 전날 개설한 오래된 포지션의 경우 현재일과 포지션 개설일을 비교하는 것으로 충분하므로 이러한 유형의 분석을 수행하는 것은 매우 간단합니다: 서로 다른 경우 해당 포지션은 스윙 트레이딩입니다. 하지만 포지션이 개시된 당일에 EA가 청산되면 어떻게 될까요? 이 경우 포지션이 데이 트레이딩인지 스윙 트레이딩인지 알 수 있는 방법은 없습니다.

펜딩 주문의 경우 이를 확인할 수 있는 방법이 있으므로 이 문제는 발생하지 않습니다. ORDER_TYPE_TIME 매개 변수를 사용하여 OrderGetInteger를 호출하면 주문이 주간 트레이딩인지 스윙 트레이딩인지를 나타내는 ENUM_ORDER_TYPE_TIME 열거형 값이 반환됩니다. 하지만 포지션에는 이러한 일이 발생하지 않습니다.

따라서 이 경우 저의 해결책은 다른 정보와 관계없이 주문 또는 포지션에 무언가를 추가하여 EA에 작업 기간을 알리는 것입니다. 그러나 이것은 몇 가지 경우에는 문제를 해결하지만 아닌 경우도 있기 때문에 완벽한 해결책은 아닙니다. 트레이더는 분석에 필요한 기간 전에 거래가 스윙 트레이딩인지 데이 트레이딩인지를 식별하기 위해 EA에서 사용하는 시스템을 수정할 수 있기 때문입니다.

좀 더 이해하기 위해 솔루션이 어떻게 구현되는지 살펴보겠습니다.

inline char GetInfosTradeServer(ulong ticket)
{
        long info;
                                
        if (ticket == 0) return 0;
        if (OrderSelect(ticket))
        {
                if (OrderGetString(ORDER_SYMBOL) != Terminal.GetSymbol()) return 0;
                info = OrderGetInteger(ORDER_TYPE);
                m_Selection.bIsBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                m_Selection.pr = OrderGetDouble(ORDER_PRICE_OPEN);
                m_Selection.tp = OrderGetDouble(ORDER_TP);
                m_Selection.sl = OrderGetDouble(ORDER_SL);
                m_Selection.vol = OrderGetDouble(ORDER_VOLUME_CURRENT);
                m_Selection.bIsDayTrade = ((ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME) == ORDER_TIME_DAY);
                                        
                return -1;
        }
        if (PositionSelectByTicket(ticket))
        {
                if (PositionGetString(POSITION_SYMBOL) != Terminal.GetSymbol()) return 0;
                m_Selection.bIsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
                m_Selection.pr = PositionGetDouble(POSITION_PRICE_OPEN);
                m_Selection.tp = PositionGetDouble(POSITION_TP);
                m_Selection.sl = PositionGetDouble(POSITION_SL);
                m_Selection.vol = PositionGetDouble(POSITION_VOLUME);
                if (macroGetDate(PositionGetInteger(POSITION_TIME)) == macroGetDate(TimeTradeServer()))
                        m_Selection.bIsDayTrade = PositionGetString(POSITION_COMMENT) == def_COMMENT_TO_DAYTRADE;
                else m_Selection.bIsDayTrade = false;
                                        
                return 1;
        }
        return 0;
}

위에서 언급했듯이 펜딩 오더의 경우 OrderGetInteger를 호출하여 필요한 값을 가져오는 것으로 충분합니다. 포지션은 조금 더 복잡합니다. 거래 서버의 포지션 개시일과 현재일을 확인합니다. 둘 다 같은 경우 순서대로 코멘트를 확인합니다. 코멘트가 포지션이 개설되면 데이 트레이딩이 될 것임을 나타내기 위해 C_Router 클래스에서 사용되는 문자열을 나타내는 경우 EA는 이를 해석하여 포지션 지표에 표시합니다. 그러나 코멘트는 하루가 끝날 때까지 변경되지 않아야 합니다. 만약에 변경이 된다면 EA는 데이 트레이딩 포지션이 실제로 스윙 트레이딩이라고 보고할 수도 있으며 이 경우 이는 EA의 잘못이 아니라 트레이더가 코멘트를 너무 빨리 변경했기 때문일 수 있기 때문입니다.

이 부분이 이 솔루션의 단점이지만 포지션 데이터만 보고 데이 트레이딩인지 아닌지 판단하는 방법에 대한 아이디어가 있는 분이 있다면 댓글로 공유해 주세요.

펜딩 오더의 경우 아래 동영상에서 확인할 수 있습니다:


이제 거의 모든 것이 준비되었습니다. EA를 재미있게 사용할 수 있도록 코드에 몇 가지를 추가하기만 하면 됩니다.


2.0.3. 플랫폼 메시지에 응답하기

우리의 전체 주문 시스템은 MetaTrader 5가 보낸 메시지를 기반으로 하므로 EA는 해야 할 일과 하지 말아야 할 일이 무엇인지 알 수 있습니다. 그렇기 때문에 메시지 시스템을 구현하는 방법을 아는 것이 매우 중요합니다.

전체 메시징 관련 코드는 아래와 같습니다:

#define macroGetDataIndicatorFloat      {                                                                                                               \
                m_Selection.vol = m_EditInfo1.GetTextValue(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT)) * Terminal.GetVolumeMinimal();      \
                m_Selection.bIsBuy = m_BtnInfoType.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_TYPE));                             \
                m_Selection.pr = macroGetLinePrice(def_IndicatorFloat, IT_PENDING);                                                                     \
                m_Selection.sl = macroGetLinePrice(def_IndicatorFloat, IT_STOP);                                                                        \
                m_Selection.tp = macroGetLinePrice(def_IndicatorFloat, IT_TAKE);                                                                        \
                m_Selection.bIsDayTrade = m_BtnInfo_DS.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_DS));                           \
                                        }
                                                                                                
                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;
                                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;    //SHIFT pressed
                                                bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
                                                if (bKeyBuy != bKeySell)
                                                {
                                                        if (!bMounting)
                                                        {
                                                                m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
                                                                valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                                                valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                                                m_Selection.it = IT_PENDING;
                                                                m_Selection.pr = price;
                                                        }
                                                        m_Selection.tp = m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp));
                                                        m_Selection.sl = m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl);
                                                        m_Selection.bIsBuy = bKeyBuy;
                                                        m_BtnInfoType.SetStateButton(macroMountName(def_IndicatorTicket0, IT_PENDING, EV_TYPE), bKeyBuy);
                                                        if (!bMounting)
                                                        {
                                                                IndicatorAdd(m_Selection.ticket = def_IndicatorTicket0);
                                                                bMounting = true;
                                                        }
                                                        MoveSelection(price);
                                                        if ((bEClick) && (memLocal == 0)) SetPriceSelection(memLocal = price);
                                                }else if (bMounting)
                                                {
                                                        RemoveIndicator(def_IndicatorTicket0);
                                                        memLocal = 0;
                                                        bMounting = false;
                                                }else if ((!bMounting) && (bKeyBuy == bKeySell) && (m_Selection.ticket > def_IndicatorGhost))
                                                {
                                                        if (bEClick) SetPriceSelection(price); else MoveSelection(price);
                                                }
                                                break;
                                        case CHARTEVENT_OBJECT_DELETE:
                                                if (GetIndicatorInfos(sparam, ticket, it, ev))
                                                {
                                                        if (GetInfosTradeServer(ticket) == 0) break;
                                                        CreateIndicator(ticket, it);
                                                        if ((it == IT_PENDING) || (it == IT_RESULT))
                                                                PositionAxlePrice(ticket, it, m_Selection.pr);
                                                        ChartRedraw();
                                                        m_TradeLine.SpotLight();
                                                        m_Selection.ticket = 0;
                                                        UpdateIndicators(ticket, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                }
                                                break;
                                        case CHARTEVENT_OBJECT_ENDEDIT:
                                                macroGetDataIndicatorFloat;
                                                m_Selection.ticket = 0;
                                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                break;
                                        case CHARTEVENT_CHART_CHANGE:
                                                ReDrawAllsIndicator();
                                                break;
                                        case CHARTEVENT_OBJECT_CLICK:
                                                if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev)
                                                {
                                                        case EV_TYPE:
                                                                if (ticket == def_IndicatorFloat)
                                                                {
                                                                        macroGetDataIndicatorFloat;
                                                                        m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1)));
                                                                        m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1)));
                                                                        m_Selection.ticket = 0;
                                                                        UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                                } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam));
                                                                break;
                                                        case EV_DS:
                                                                if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam));
                                                                break;
                                                        case EV_CLOSE:
                                                                if (ticket == def_IndicatorFloat) RemoveIndicator(def_IndicatorFloat, it);
                                                                else 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_Selection.ticket = ticket;
                                                                                m_Selection.it = it;
                                                                                SetPriceSelection(0);
                                                                                break;
                                                                }
                                                                break;
                                                        case EV_MOVE:
                                                                        if (ticket == def_IndicatorFloat)
                                                                        {
                                                                                macroGetDataIndicatorFloat;
                                                                                m_Selection.ticket = ticket;
                                                                                m_Selection.it = it;
                                                                        }else   CreateGhostIndicator(ticket, it);
                                                                break;
                                                        case EV_CHECK:
                                                                if (ticket != def_IndicatorFloat)
                                                                {
                                                                        if (PendingAtFloat(ticket)) RemoveOrderPendent(ticket);
                                                                        else m_BtnCheck.SetStateButton(macroMountName(ticket, IT_PENDING, EV_CHECK), true);
                                                                } else
                                                                {
                                                                        macroGetDataIndicatorFloat;
                                                                        m_Selection.ticket = def_IndicatorTicket0;
                                                                        m_Selection.it = IT_PENDING;
                                                                        SetPriceSelection(m_Selection.pr);
                                                                        RemoveIndicator(def_IndicatorFloat);
                                                                }
                                                                break;
                                                }
                                                break;
                                }
                        }
#undef macroGetDataIndicatorFloat

이 코드에 겁먹지 마세요. 코드가 크고 복잡해 보이지만 실제로는 매우 간단합니다. 강조 표시된 부분을 중심으로 메시지 처리 코드의 새로운 기능을 설명하겠습니다.

첫 번째 새로운 기능은 CHARTEVENT_OBJECT_ENDEDIT 이벤트 처리 코드에 있습니다. 이 코드는 EDIT 객체에 있는 콘텐츠 편집을 완료할 때마다 MetaTrader 5에서 트리거 됩니다. 이게 무슨 뜻일까요? 만약 우리가 이 이벤트를 처리하지 않고 레버링 값을 편집한 후 스톱 레벨 지표의 데이터를 조작하려고 하면 값에 불일치가 발생할 것입니다. 이 이벤트는 매우 중요합니다. EA가 강제로 값을 원래 값으로 되돌리지만 코드에 표시된 대로 이 이벤트를 처리하면 이 문제는 발생하지 않고 우리는 레버리지 데이터로 원활하게 거래할 수 있습니다. 여러분이 EA에게 이러한 조정을 허용해 달라고 요청할 때는 레버리지를 더 많이 또는 적게 사용하는 것 중 어느것이 좋은 것인지를 확인해야 한다는 점을 명심하세요. 이렇게 하면 EA는 사용자가 요청할 때만 주문을 서버로 보냅니다. 그러므로 여러분은 위험을 감수하지 않고 확인할 수 있으며 확인란이 활성화되는 순간이 바로 이 때입니다.

이제 CHARTEVENT_OBJECT_CLICK 이벤트에 대해 자세히 살펴보겠습니다. 이를 위해 이전 코드에서 강조 표시된 부분을 가져옵니다.

case CHARTEVENT_OBJECT_CLICK:
        if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev)
        {
                case EV_TYPE:
                        if (ticket == def_IndicatorFloat)
                        {
                                macroGetDataIndicatorFloat;
                                m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1)));
                                m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1)));
                                m_Selection.ticket = 0;
                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                        } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam));
                        break;
                case EV_DS:
                        if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam));
                        break;

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

이 코드는 실제로 어떤 기능을 할까요? 혹시 아시나요? 이 글에 있는 동영상이 이를 보여 주지만 어떻게 그런 일이 이루어지는지 여러분은 이해할 수 있을까요? 많은 사람들이 매우 복잡한 코드라고 생각하겠지만 바로 위에 있습니다.

우리가 해야 할 일이 두 가지 있습니다. 첫 번째는 비트맵 객체를 클릭하면 상태가 변경되어 해당 티켓이 서버에 이미 존재하는 티켓인지 아니면 차트에만 있는 티켓인지 확인해야 합니다. 이는 녹색으로 강조 표시된 포인트에 이해 되는 것입니다. 만약 티켓이 서버에 있는 경우 상태 변경을 취소해야 하며 이후 EA가 필요한 변경을 수행하여 이를 바로잡습니다.

이제 노란색으로 강조 표시된을 살펴봅시다. 이 아이디어는 다음을 기반으로 합니다: 차트에 이미 주문이 있고 저는 그저 주문의 방향을 바꾸고 싶은데 왜 차트에 또 다른 주문을 넣어야 하나요? 다시 말해 매수 주문이 있었는데 이제는 매수 주문을 매도 주문으로 바꾸고 싶다는 뜻입니다. 노란색 조각이 바로 그 역할을 하는 것입니다. 매수 또는 매도 여부를 담당하는 비트맵을 클릭하면 자동으로 방향이 바뀝니다. 한 가지 세부 사항: 이 작업은 플로팅 주문에 대해서만 가능하며 이미 서버에 있는 주문에 대해서는 금지되어 있습니다.

이러한 모든 변경 사항으로 인해 이제 지표는 다음과 같이 표시됩니다:

펜딩 오더 유형

 

포지션 지표:

이제 여러분은 예상되는 움직임이나 포지션의 수명을 정확히 알 수 있게 되었습니다. 그러므로 펜딩 오더 또는 오픈 포지션이 어떤 상태인지 파악하기가 훨씬 쉬워졌습니다. 위쪽을 가리키는 녹색 화살표는 매수 포지션, 아래쪽을 가리키는 빨간색 화살표는 매도 포지션을 나타냅니다. 문자 D는 당일 거래를 나타내며, 하루가 끝나면 청산됩니다. S인 경우 스윙 트레이딩이며 하루가 끝날 때 반드시 청산되는 것은 아닙니다.

다음 동영상에서는 새로운 주문 시스템이 어떻게 작동하는지 보여드립니다. 포지션 지표는 변경될 수 없지만 펜딩 오더는 추가 수정이 가능하기 때문에 저는 펜딩 오더에 집중했습니다. 서버에서 제공한 위치 관련 데이터만 표시됩니다. 이 시스템은 실용적이지만 기능을 최대한 활용하려면 익숙해져야 하므로 여러분은 라이브 계정에서 사용해보기 전에 모든 기능이 어떻게 작동하는지 자세히 살펴보셔야 합니다.




결론

이제 우리의 주문 시스템은 매우 다양해졌습니다. 우리이 주문 시스템은 여러 가지 작업을 수행할 수 있고 많은 도움이 되지만 아직 한 가지 중요한 세부 사항이 누락되어 있습니다. 이 누락 사항은 다음 글에서 구현할 예정입니다. 그럼 곧 뵙겠습니다...


MetaQuotes 소프트웨어 사를 통해 포르투갈어가 번역됨
원본 기고글: https://www.mql5.com/pt/articles/10630

Expert Advisor 개발 기초부터(28부): 미래를 향해(III) Expert Advisor 개발 기초부터(28부): 미래를 향해(III)
아직 우리의 주문 시스템에는 미흡한 부분이 하나 있습니다. 조만간 해결하도록 하겠습니다. MetaTrader 5는 주문 값을 생성하고 수정할 수 있는 티켓 시스템을 제공합니다. 이 아이디어는 동일한 티켓 시스템을 더 빠르고 효율적으로 만들 수 있는 EA를 만드는 것입니다.
Expert Advisor 개발 기초부터(26부): 미래를 향해(I) Expert Advisor 개발 기초부터(26부): 미래를 향해(I)
오늘은 주문 시스템을 한 단계 더 발전시켜 보겠습니다. 하지만 그 전에 우리는 몇 가지 문제를 해결해야 합니다. 우리가 어떻게 하고 싶은지, 거래일 동안 우리가 어떤 일을 할 것인지와 관련된 몇 가지 질문이 있습니다.
시장과 시장이 보여 주는 글로벌 패턴의 물리학 시장과 시장이 보여 주는 글로벌 패턴의 물리학
이 글에서는 시장에 대한 이해가 조금이라도 있는 시스템이라면 글로벌 규모로 운영 가능하다는 가정을 테스트해 보려고 합니다. 저는 어떤 이론이나 패턴을 발명하지 않을 것이고 알려진 사실만을 사용하며 이러한 사실을 점차 수학적인 분석 언어로 번역할 것입니다.
프랙탈로 트레이딩 시스템 설계하는 방법 알아보기 프랙탈로 트레이딩 시스템 설계하는 방법 알아보기
이 글은 가장 인기 있는 보조지표를 기반으로 트레이딩 시스템을 설계하는 방법에 대한 시리즈의 새로운 글입니다. 우리는 프랙탈 지표인 새로운 지표에 대해 배우고 이를 기반으로 MetaTrader 5 터미널에서 실행될 거래 시스템을 설계하는 방법을 알아볼 것입니다.