English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
preview
Desenvolvendo um EA de negociação do zero (Parte 27): Em direção ao futuro (II)

Desenvolvendo um EA de negociação do zero (Parte 27): Em direção ao futuro (II)

MetaTrader 5Exemplos | 9 agosto 2022, 13:40
470 0
Daniel Jose
Daniel Jose

1.0 - Introdução

No artigo anterior Desenvolvendo um EA de negociação do Zero (Parte 26) corrigimos uma falha catastrófica que existia no sistema de ordens, mas ao mesmo tempo começamos a implementar algo que nos dá um ramo inteiro de novas possibilidades no sistema de ordens, mas apesar do sistema inicialmente implementado naquele artigo ser bastante interessante, existe uma falha que torna o sistema um tanto quando pouco viável, e esta falha foi mostrada no final daquele artigo. Esta era causada pelo fato de não termos como trocar, ou melhor dizendo selecionar qual seria o momento de vencimento da ordem ou posição, além de outras questões menores. Lá eu travei o sistema como uma ordem ou posição que deveria ser encerrada no fim do pregão ou do dia corrente, mas as vezes queremos fazer operações com prazos mais longos, e deixar as coisas daquela forma não ajuda muito.

Então neste artigo irei mostrar uma forma de você corrigir, ou melhor dizendo fazer com que o sistema de ordens fique mais intuitivo, de forma que você ao bater os olhos no gráfico identifique de forma imediata e precisa o que cada ordem representa e como ela irá ser processada, e qual o tipo de movimento esperado.

A questão é tão interessante e ao mesmo tempo tão simples e intuitiva que você depois de ver a coisa funcionando, não irá querer operar sem este sistema. E isto que irei mostrar neste artigo é só uma de diversas possibilidades que você pode implementar no sistema de ordens, talvez eu venha a mostrar outras coisas no futuro, mas isto que iremos ver durante o artigo já lhe dará uma grande base para construir outras modificações igualmente uteis e interessantes, para o seu caso especifico, já que aqui nestes artigos estou tentando deixar a coisa o mais genérica, tanto quanto for possível ser feito.


2.0 - Um modelo intuitivo de operar

Nosso sistema de indicação de ordens até o presente momento estava sendo feito da seguinte forma:

Os indicadores de Take e Stop tem este formado, isto é bastante intuitivo, já que a cor verde indica que o valor irá ser creditado na nossa conta, e a cor vermelha será debitado da conta, isto é bem simples e claro. já quando temos o indicador de Stop como mostrado abaixo, teremos a indicação de que o acionamento do stop irá gerar um valor a ser creditado em nossa conta. Ou seja não precisamos mexer nos indicadores de limites, pelo menos no presente momento, pode ser que no futuro eu mude alguma coisa neles, mas no momento eles já são suficientimente adequados ao uso.

Esta forma de usar os indicadores de limites, é algo bastante simples e intuitiva de ser analisada por qualquer operador. Mas temos algo que ainda não é tão claro, o primeiro é o indicador de ponto de entrada de uma ordem pendente.

Responda sinceramente: Você consegue saber se esta ordem pendente é uma venda ou compra ?!?! E mais: Você consegue dizer se esta ordem pendente irá encerra no fim do dia, ou se abrirá uma posição de prazo mais longo ?!?! Complicado não é mesmo ... agora vamos supor que já temos posições abertas, o indicador será como o mostrado abaixo:

Novamente temos os mesmos problemas que o indicador de ordem pendente. Ao olhar no gráfico e ver estes indicadores, você não sabe dizer ao certo se a posição irá finalizar no fim do dia, ou se ela irá perdurar por mais tempo. E caso ela venha a ter seu encerramento no fim do dia, você não vai querer que a corretora te stop de forma compulsória, pois isto será cobrado de você, e sair encerrando as ordens sem nenhum tipo de critério não é algo muito sensato de ser feito, mesmo a caixa de ferramentas no MetaTrader  , não nos dá esta informação, então tê-la no gráfico usando um indicador, é show.

Desta forma teremos que fazer mudanças aqui, e principalmente nos indicadores que mostram o ponto de abertura de uma posição, de forma a termos um melhor entendimento do que esta acontecendo.


2.0.1 - Adicionando novas informações aos indicadores

A forma mais simples de adicionar novas informações e de forma a não ocupar muito espaço no gráfico é com o uso de bitmaps. Eles são simples e podem ser bastante representativos, então sem adicionar nenhum código extra, já adicionamos 4 novos bitmaps ao EA, isto pode ser visto ao observar a classe 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

Feito isto agora teremos apenas que adicionar 2 novos objetos ao nosso sistema de ordens

//+------------------------------------------------------------------+
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;

Vejam que sempre que formos adicionar um novo objeto, temos também que adicionar um EVENTO que irá estar ligado ao objeto, isto para garantir que cada objeto tenha um nome único, isto é algo do qual você sempre devera estar atento ao adicionar novos objetos ao indicador.

Bom agora é a parte divertida da programação, a primeira coisa que de fato temos que tomar cuidado é com relação aos fantasmas, temos que atualizar o modo como eles serão criados, no caso iremos manter as informações, mas poderíamos fazer com que elas fossem retiradas do fantasma, mas acredito ser melhor deixar as informações básicas neles, caso você deseje pode pedir para elas serem removidas, e isto é feito no fragmento abaixo:

#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

As linhas em destaque transferem os objetos para dentro do fantasma, é algo bem simples mesmo nada complicado. A mesma simplicidade é feita no código que transforma os indicadores de pendentes para flutuantes.

#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

As linhas em destaque transferem os objetos para o indicador flutuante, desta forma poderemos fazer diversas coisas depois. Agora no código que cria o indicador teremos que tomar alguns cuidados especiais, mas é algo que você vai testando e ajustando até ficar do seu agrado, basicamente as mudanças foram feitas nos pontos em destaque no código abaixo:

#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

Mas observe que temos a macroInfoBase que cria os objetos que são usados no indicador, mas estes objetos serão criados apenas nos indicadores de abertura de posição e de resultado da posição, não existe a necessidade de criar estes objetos em outros indicadores. Mas reparem que não posicionamos os objetos no local que criamos eles, isto é feito em outro local, que no caso é visto logo a seguir.

#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

Notem que não gosto de fazer mudanças drásticas e radicais ao código, basicamente as únicas mudanças estão e foram destacadas no código acima.


2.0.2 - Problemas a vista

Apesar de tudo funcionar perfeitamente bem, temos um problema, procurei em toda a documentação do MQL5, mas não encontrei nenhuma forma de resolver a coisa de uma maneira simples. O problema é como saber se uma posição aberta recentemente é do tipo Day Trade ( operações curtas dentro do mesmo dia ) ou Swing Trade ( operações mais longas ), no caso de uma posição mais antiga, aberta no dia anterior é bastante simples de ser feito este tipo de analise, pois bastaria você comparar o dia atual com o dia em que a posição foi aberta, e caso seja diferentes, pronto a resposta é que a posição é um Swing Trade, mas e se o EA for encerrado e você o iniciar no mesmo dia em que a posição foi aberta ?!?! Pronto agora você não tem como saber se uma posição é de Day Trade ou de Swing Trade.

Mas este problema não existe no caso de ordens pendentes, neste caso especifico temos uma forma de testar isto. Ao executar uma chamada a OrderGetInteger usando o parâmetro: ORDER_TYPE_TIME, teremos o retorno de um enumerador  ENUM_ORDER_TYPE_TIME e este nos indica se a ordem é do tipo Day Trade ou Swing Trade, mas o mesmo não acontece para as posições.

Por este motivo, a minha solução para este caso é fazer com que algo na ordem ou posição indique para o EA o período de duração da operação, independentemente de qualquer outra informação, mas isto não é uma solução perfeita, resolve em vários casos, mas não em todos, já que o operador pode desejar modificar o sistema que o EA estará usando para identificar se uma operação é de Swing ou Day Trade, isto antes do período de necessário para analise ter passado.

Para entender melhor vejamos como a solução foi implementada.

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;
}

Notem que no caso da ordem pendente a solução é super simples, usamos uma chamada a OrderGetInteger e obtemos o valor que precisamos, já no caso da posição a coisa é um pouco mais complicada, o que é feito é o seguinte: Testamos o dia de abertura da posição, com o dia atual do servidor de negociação, caso ambos sejam iguais, iremos verificar o comentário presente na ordem, caso o comentário indique a string que é usada na classe C_Router para informar que em caso de abertura da posição ela será do tipo Day Trade, o EA irá interpretar e informar isto no indicador de posição, mas é necessário que o comentário não seja alterado até o fim do dia, pois se isto acontecer o EA poderá informar que uma posição de Day Trade na verdade seria de Swing Trade, e a falha não estará no EA e sim no fato do operador ter mudado o comentário antes da hora.

Esta é a falha nesta solução, mas se alguém tiver alguma ideia de como saber se uma posição é ou não de Day Trade apenas observando os dados da posição, por favor deixe nos comentários, pois isto será muito bem vindo.

A forma como a coisa se apresenta no caso de ordens pendentes é vista no video abaixo:


Agora já temos quase tudo pronto, só falta de fato fazer mais algumas adições ao código para que o EA fique bastante interessante de ser usado.


2.0.3 - Respondendo as Mensagens da plataforma

Todo o nosso sistema de ordens se baseia em mensagens enviadas pelo MetaTrader 5 para o EA poder saber o que deve ou não ser feito, e por conta disto saber como implementar o sistema de mensagens é algo tão importante quanto ter a ideia de como fazer o sistema ser modelado.

Todo o código de mensagens pode ser visto abaixo

#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;    //Clique esquerdo
                                                bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT Pressionada
                                                bKeySell = (mKeys & 0x08) == 0x08;    //CTRL Pressionada
                                                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

Não se assuste ao olhar este código, apesar de ele parecer grande e complicado, ele é em essência bastante simples, mas irei focar nos pontos em destaque para explicar as novidades neste sistema de tratamento de mensagens.

A primeira coisa nova no código é o tratamento do evento CHARTEVENT_OBJECT_ENDEDIT este é disparado pelo MetaTrader 5 toda a vez que finalizamos a edição do conteúdo presente em um objeto EDIT, bem mas o que isto importa para nos ?!?! Importa e muito, já que se você não tratar este evento e tentar manipular os dados dos indicadores de limite depois de editar o valor de alavancagem, irá ter inconsistência nos valores, se bem que o EA irá forçar o valor a retornar ao seu original, mas tratando este evento conforme mostrado no código, este problema não existirá e você poderá operar tranquilamente com o novo dado de alavancagem. Lembrando que quando você pede para o EA lhe permitir fazer estes ajustes você estará querendo de fato verificar se é ou não uma boa ideia entrar mais ou menos alavancado na operação, desta forma você pode fazer o estudo sem se arriscar, pois o EA somente irá enviar a ordem para o servidor quando você pedir para ele fazer isto, e o momento que isto se dará é a ativação do checkbox.

Agora vamos observar com um pouco mais de atenção o evento  CHARTEVENT_OBJECT_CLICK, para isto vamos tirar o fragmento marcado no código acima e trazer ele para mais próximo.

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;

// ... Restante do código ....

O que este código esta de fato fazendo ?!?! Você tem alguma ideia ?!?! Bem, no videos deste artigo é demonstrado isto, mas você conseguiria entender como é feito aquele tipo de coisa ?!?! Muitos iriam imaginar que se tratava de um código extremamente complexo, mas ele esta ai ... logo acima ...

Existem dois tratamentos que precisamos fazer, o primeiro é que quando um objeto BitMap recebe um clique o status dele muda, e temos que verificar se o ticket é de algo já no servidor, ou é algo que esta apenas no gráfico, isto é feito pelos pontos destacados em verde caso o ticket seja de algo que esta no servidor, esta mudança de status deve ser desfeita, então o EA irá corrigir isto fazendo a mudança para nos.

Agora reparem no trecho destacado em amarelo, o que está acontecendo ali ?!?! A ideia ali é baseada no seguinte: Por que tenho que colocar uma outra ordem no gráfico, se ela já existe no gráfico, apenas quero inverter a mão, ou seja, se estava comprando, agora quero vender, e se estava vendendo agora quero comprar ... Pois bem o trecho em amarelo faz justamente isto, quando o BitMap responsável por indicar se estamos comprando ou vendendo recebe um clique, ocorre a mudança de forma automática da mão, um detalhe é que isto só pode ser feito em uma ordem flutuante, em algo que já se encontra no servidor, isto é proibido pelo EA.

Bem, com todas estas mudanças os novos indicadores ficaram conforme mostrado abaixo:

Indicador de ordem pendente:

 

Indicador de posição:

Notem como ficou bem mais simples identificar o que uma ordem pendente ou posição aberta estará fazendo, você consegue sem duvidas saber qual o movimento esperado, ou período de vida da posição. A seta apontando para cima e verde, indica uma posição de compra, já se ela for vermelha e apontando para baixo, estaremos esperando um movimento de queda. Agora a letra D representa uma operação de Day Trade, ou seja que irá ser encerrada no final do dia, já se no lugar desta tivermos uma letra S teremos a representação de um Swing Trade, e a operação não necessariamente irá finalizada no fim do dia.

No video abaixo, mostro como o novo sistema de ordem funciona. É verdade que dei um foco as ordens pendentes, isto por que elas podem sofrer mais modificações, já os indicadores de posição não podem ser modificados, eles apenas irão representar o que o servidor esta nos dizendo com relação a posição aberta. Vejam com atenção ao como as coisas funcionam antes de experimentar em uma conta real, pois este sistema é bem prático, mas é preciso se familiarizar com ele para tirar o máximo proveito de sua funcionalidade.




3.0 - Conclusão

Bem nosso sistema de ordens agora esta bastante versátil, podendo fazer diversas coisas, e nos ajudar bastante, mas ainda falta um detalhe crucial que será implementado no próximo artigo, então até lá...


DoEasy. Controles (Parte 5): Objeto base WinForms, controle Painel, parâmetro AutoSize DoEasy. Controles (Parte 5): Objeto base WinForms, controle Painel, parâmetro AutoSize
Neste artigo, criaremos um objeto que serve de base para todos os objetos WinForms da biblioteca e começaremos a preparar a propriedade AutoSize do objeto WinForms "Painel", que dimensiona automaticamente o objeto de acordo com seu conteúdo.
Redes neurais de maneira fácil (Parte 14): Agrupamento de dados Redes neurais de maneira fácil (Parte 14): Agrupamento de dados
Devo confessar que já se passou mais de um ano desde que o último artigo foi publicado. Em um período tão longo como esse, é possível reconsiderar muitas coisas, desenvolver novas abordagens. E neste novo artigo, gostaria de me afastar um pouco do método de aprendizado supervisionado usado anteriormente, e sugerir um pouco de mergulho nos algoritmos de aprendizado não supervisionado. E, em particular, desejaria analisar um dos algoritmos de agrupamento, o k-médias (k-means).
Como desenvolver um sistema de negociação baseado no indicador SAR Parabólico Como desenvolver um sistema de negociação baseado no indicador SAR Parabólico
Neste artigo, nós continuaremos nossa série sobre como projetar um sistema de negociação usando os indicadores mais populares. Neste artigo, nós aprenderemos detalhadamente sobre o indicador SAR Parabólico e como nós podemos projetar um sistema de negociação para ser usado na MetaTrader 5 usando algumas estratégias simples.
Como desenvolver um sistema de negociação baseado no indicador ATR Como desenvolver um sistema de negociação baseado no indicador ATR
Neste artigo, nós aprenderemos uma nova ferramenta técnica que pode ser usada na negociação, como continuação da série em que aprendemos a projetar sistemas de negociação simples. Desta vez, nós trabalharemos com outro indicador técnico popular: Average True Range (ATR).