Simulação de mercado (Parte 03): Uma questão de performance
Introdução
No artigo anterior Simulação de mercado (Parte 02): Cross Order (II), mostrei como fazer para se ter um controle de qual ativo, ou contrato estaríamos observando no Chart Trade. Isto quando estivéssemos fazendo uso, de um sistema, onde não acessaríamos diretamente o contrato negociado. E sim usaríamos um outro ativo, para fazer as tais operações. Tal ativo, na verdade seria o histórico do contrato, e os motivos de se fazer isto, foram explicados no artigo anterior.
Pois bem, apesar de já estarmos em um estágio bem mais avançado no desenvolvimento de todo o sistema. Sempre precisamos selar e cuidar para que as coisas se mantenham dentro de um nível de segurança, confiabilidade e performance adequada. Fazer com que isto aconteça, não é de fato uma tarefa simples. Muito pelo contrário. Isto está longe de ser simples. Mais hora ou menos hora, acabamos nos deparando com algum tipo de falha, ou problema. E é nestas horas que o perfeito entendimento de como todo os sistemas está sendo implementado, será de suma importância. Se você não souber, ou não entender o que cada parte está de fato fazendo, acabará em um beco sem saída. Onde as coisas acabaram se degradando de forma muito rápida.
Por várias semanas, estamos desenvolvendo alguns das aplicações que iremos de fato precisar. Porém, quando comecei a implementar uma nova aplicação, que será vista em breve, os problemas começaram a acontecer. E todo o sistema começou a ter uma performance muito inferior, a que era observada até então. Já que não pretendo construir a coisa de forma monolítica. Ou seja, como se fosse um bloco só. Foi preciso, analisar e verificar quais os motivos que estavam gerando tal degradação na performance.
Mas, ao estudar o fluxograma, acabei notando, que existiam algumas brechas. Tais brechas precisam ser fechadas, isto por que elas mais hora ou menos hora acabaram gerando problemas. Então resolver, e evitar que tais problemas venham a surgir, também faz parte do desenvolvimento da aplicação final. Além destes detalhes, que serão explicados no decorrer do artigo. Existe uma pequena falha, que você pode notar ao usar o indicador de mouse. Tal falha estava passando batida, mas ao efetuar uma análise mais detalhada, a mesma acabou por ser notada. Sendo também corrigida e você, caro leitor, poderá ver do que se trata.
Este tipo de coisa, que muitas das vezes, muitos não mostram, ou explicam. Faz com que você, caro leitor, aspirante a programador profissional, venha a imaginar, erroneamente, que um programador, nunca comete erros. Que todo o código, nasce e se desenvolve sem erros e sem falhas. Quando na verdade, nenhum código, por mais bem pensado que tenha sido, durante a fase de planejamento, está livre de falhas. Então gostaria, que este artigo, além de explicativo, viesse a servir, como uma forma de lhe mostrar o seguinte: Um código, jamais se encontra totalmente pronto. Sempre existe algo, a ser melhorado, ou corrigido dentro do mesmo.
Então dito, tais palavras, vamos ao artigo propriamente dito. Começando com o seguinte tópico.
Melhorando o encapsulamento
Um dos piores problemas que podem acometer um software, que está sendo desenvolvido, é o vazamento de informações. Tal vazamento muitas vezes, se configura de diversas formas. Mas aqui, em questão o problema é bem específico. Já que todo o código está sendo projetado, a fim de fazer uso massivo das classes, e assim da programação orientada em objetos. O vazamento, ocorre, quando uma classe, ou aplicação que está fazendo uso de uma determinada classe, consegue acesso a algo que ele, ou ela não deveria ter acesso. Tal falha, ocorre normalmente por falhas no encapsulamento, ou por que uma função ou procedimento não deveria de fato estar ali. Presente na classe objeto. No nosso caso em particular, existe um procedimento, que não deveria poder ser acessada por outros códigos, senão o código especifico a qual ela está ligada. Mas já que durante todo este tempo, eu não havia me atentado a questão, a falha permanecia.
A falha em questão é a função SetBuffer, presente na classe C_Mouse. Mas por que este procedimento, se caracteriza uma falha? Existe algo errado em ele existir, ali na classe C_Mouse? Eu não diria que está errado. Mas depois de observar com atenção, acabei notando que aquilo não era de fato correto. O problema, não é que um outro programa, seja ele um indicador ou Expert Advisor, possa escrever no buffer do indicador de mouse. Isto de fato não acontece, devido a questões de segurança que da própria implementação do MQL5. Porém não faz sentido algum, tal procedimento estar presente na classe C_Mouse, sendo que o único processo que de fato faz uso dele é o indicador de mouse. Então ao remover da classe C_Mouse, este procedimento, estamos de fato melhorando o encapsulamento da classe C_Mouse. Mas ao mesmo tempo, estamos garantindo, que apenas e somente o indicador de mouse faça algo no seu buffer.
Mas ao remover o procedimento da classe, precisamos garantir que ele continue a funcionar da mesma forma como era antes. Assim garantimos a compatibilidade com qualquer coisa já implementada e desenvolvida, que necessite dos dados vindos do indicador de mouse. Antes de mostrar como a classe C_Mouse ficou, isto por conta que ela sofreu outras mudanças além desta mencionada acima. Vamos ver como o código do indicador de mouse, agora se encontra. Este pode ser visto na íntegra logo abaixo:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "This is an indicator for graphical studies using the mouse." 04. #property description "This is an integral part of the Replay / Simulator system." 05. #property description "However it can be used in the real market." 06. #property version "1.82" 07. #property icon "/Images/Market Replay/Icons/Indicators.ico" 08. #property link "https://www.mql5.com/pt/articles/12580" 09. #property indicator_chart_window 10. #property indicator_plots 0 11. #property indicator_buffers 1 12. //+------------------------------------------------------------------+ 13. double GL_PriceClose; 14. datetime GL_TimeAdjust; 15. //+------------------------------------------------------------------+ 16. #include <Market Replay\Auxiliar\Study\C_Study.mqh> 17. //+------------------------------------------------------------------+ 18. C_Study *Study = NULL; 19. //+------------------------------------------------------------------+ 20. input color user01 = clrBlack; //Price Line 21. input color user02 = clrPaleGreen; //Positive Study 22. input color user03 = clrLightCoral; //Negative Study 23. //+------------------------------------------------------------------+ 24. C_Study::eStatusMarket m_Status; 25. int m_posBuff = 0; 26. double m_Buff[]; 27. //+------------------------------------------------------------------+ 28. int OnInit() 29. { 30. Study = new C_Study(0, "Indicator Mouse Study", user01, user02, user03); 31. if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED; 32. MarketBookAdd((*Study).GetInfoTerminal().szSymbol); 33. OnBookEvent((*Study).GetInfoTerminal().szSymbol); 34. m_Status = C_Study::eCloseMarket; 35. SetIndexBuffer(0, m_Buff, INDICATOR_DATA); 36. ArrayInitialize(m_Buff, EMPTY_VALUE); 37. 38. return INIT_SUCCEEDED; 39. } 40. //+------------------------------------------------------------------+ 41. int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], 42. const double& high[], const double& low[], const double& close[], const long& tick_volume[], 43. const long& volume[], const int& spread[]) 44. { 45. GL_PriceClose = close[rates_total - 1]; 46. if (_Symbol == def_SymbolReplay) 47. GL_TimeAdjust = spread[rates_total - 1] & (~def_MaskTimeService); 48. m_posBuff = rates_total; 49. (*Study).Update(m_Status); 50. 51. return rates_total; 52. } 53. //+------------------------------------------------------------------+ 54. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 55. { 56. uCast_Double info; 57. C_Mouse::st_Mouse sMouse; 58. 59. (*Study).DispatchMessage(id, lparam, dparam, sparam); 60. sMouse = (*Study).GetInfoMouse(); 61. info._8b[0] = (uchar)(sMouse.ExecStudy == C_Mouse::eStudyNull ? sMouse.ButtonStatus : 0); 62. info._16b[1] = (ushort)sMouse.Position.X_Graphics; 63. info._16b[2] = (ushort)sMouse.Position.Y_Graphics; 64. if (m_posBuff > 0) m_Buff[m_posBuff - 1] = info.dValue; 65. 66. ChartRedraw((*Study).GetInfoTerminal().ID); 67. } 68. //+------------------------------------------------------------------+ 69. void OnBookEvent(const string &symbol) 70. { 71. MqlBookInfo book[]; 72. C_Study::eStatusMarket loc = m_Status; 73. 74. if (symbol != (*Study).GetInfoTerminal().szSymbol) return; 75. MarketBookGet((*Study).GetInfoTerminal().szSymbol, book); 76. m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : (symbol == def_SymbolReplay ? C_Study::eInReplay : C_Study::eInTrading)); 77. for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++) 78. if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction; 79. if (loc != m_Status) (*Study).Update(m_Status); 80. } 81. //+------------------------------------------------------------------+ 82. void OnDeinit(const int reason) 83. { 84. MarketBookRelease((*Study).GetInfoTerminal().szSymbol); 85. 86. delete Study; 87. } 88. //+------------------------------------------------------------------+
Código fonte do indicador de Mouse
Note que praticamente, não temos quase nenhuma mudança, frente ao que já era o código original. Você pode ver o código original, nos artigos anteriores. Porém, se você vem acompanhando, e de forma detalhista tais códigos, notará que algo diferente apareceu no procedimento OnChartEvent. O que estamos fazendo ali, era feito antes em um procedimento dentro da classe C_Mouse, Isto no tal SetBuffer, que existia na classe. Mas já que tal procedimento foi removido, precisamos garantir que o buffer, ainda venha a ser escrito. E é justamente isto que fazemos no procedimento OnChartEvent.
Mas por que não fazer isto na função OnCalculate, que seria o mais natural? O motivo é justamente por conta que estamos tratando de eventos ligados ao mouse. OnCalculate, é mais voltada quando estamos tratando de algo relacionado ao preço. Mas como a intenção do indicador de mouse, é justamente fornecer dados referentes ao mouse, precisamos usar uma outra função mais adequada.
Então, praticamente o mesmo código que havia em SetBuffer, agora se encontra entre as linhas 61 e 64. Porém na linha 64, temos que executar um teste, que antes não era feito. O motivo disto, é que foi notado, durante alguns experimentos no sistema de replay/simulador, que acontecia falhas estranhas relacionadas a este indicador de mouse. Porém, ao executar este teste na linha 64, tais falhas deixaram de existir. Então uma modificação que veio em boa hora, já que acabamos por resolver outra falha.
Muito bem, mas além desta melhoria no código do indicador de mouse, existem outras duas mudanças que quero mostrar. Apesar de neste momento elas inda não serem de fato utilizadas. As mesmas já estão sendo implementadas. Isto devido ao fato de que estou unindo outras aplicações a este sistema de replay/simulador. Talvez eu até venha a mostrar as mesmas em alguns artigos no futuro. Mas se isto não ocorrer, não quero ter o trabalho de ter que remover a todo momento, tais códigos. Os mesmos podem, vim a lhe ser uteis em alguns casos. De qualquer forma, se você deseja implementar algo parecido com o que estou mostrando, tais códigos podem, vim a aparecer em algum momento. Se este for o caso, então você já terá acesso a eles.
O primeiro é o arquivo de cabeçalho, que contem códigos de macros.mqh. O mesmo pode ser visto na íntegra logo abaixo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define macroRemoveSec(A) (A - (A % 60)) 05. #define macroGetDate(A) (A - (A % 86400)) 06. #define macroGetSec(A) (A - (A - (A % 60))) 07. #define macroGetTime(A) (A % 86400) 08. #define macroGetMin(A) (int)((A - (A - ((A % 3600) - (A % 60)))) / 60) 09. #define macroGetHour(A) (A - (A - ((A % 86400) - (A % 3600)))) 10. #define macroHourBiggerOrEqual(A, B) ((A * 3600) < (B - (B - ((B % 86400) - (B % 3600))))) 11. #define macroMinusMinutes(A, B) (B - ((A * 60) + (B % 60))) 12. #define macroMinusHours(A, B) (B - (A * 3600)) 13. #define macroAddHours(A, B) (B + (A * 3600)) 14. #define macroAddMin(A, B) (B + (A * 60)) 15. #define macroSetHours(A, B) ((A * 3600) + (B - ((B % 86400)))) 16. #define macroSetMin(A, B) ((A * 60) + (B - (B % 3600))) 17. #define macroSetTime(A, B, C) ((A * 3600) + (B * 60) + (C - (C % 86400))) 18. //+------------------------------------------------------------------+ 19. #define macroColorRGBA(A, B) ((uint)((B << 24) | (A & 0x00FF00) | ((A & 0xFF0000) >> 16) | ((A & 0x0000FF) << 16))) 20. #define macroTransparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0) 21. //+------------------------------------------------------------------+
Arquivo de cabeçalho Macros.mqh
Apesar de grande parte deste arquivo, ser voltado a poder manipular o tempo. Isto quando você estiver fazendo uso do tipo datetime, ou do tipo ulong para informar datas e horário. Tais funções são de grande valia para uma infinidade de trabalhos relacionados a tais manipulações. Isto por conta que é mais rápido para o processador, fazer um calculo, do que chamar uma função da biblioteca do MQL5, para efetuar o mesmo trabalho. Talvez você não consiga realmente notar a utilidade destes cálculos. Mas de qualquer forma, agora o arquivo macros.mqh terá este conteúdo.
O outro arquivo de cabeçalho, que também sofreu mudanças, foi o arquivo defines.mqh. Apesar de que a mudança, que de fato correu foi apenas a adição de uma única linha no arquivo. Mas mesmo assim o mesmo pode ser visto na íntegra logo abaixo:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_VERSION_DEBUG 05. //+------------------------------------------------------------------+ 06. #ifdef def_VERSION_DEBUG 07. #define macro_DEBUG_MODE(A) \ 08. Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A)); 09. #else 10. #define macro_DEBUG_MODE(A) 11. #endif 12. //+------------------------------------------------------------------+ 13. #define def_SymbolReplay "RePlay" 14. #define def_MaxPosSlider 400 15. #define def_MaskTimeService 0xFED00000 16. #define def_IndicatorTimeFrame (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96)))) 17. #define def_IndexTimeFrame 4 18. //+------------------------------------------------------------------+ 19. union uCast_Double 20. { 21. double dValue; 22. long _long; // 1 Information 23. datetime _datetime; // 1 Information 24. uint _32b[sizeof(double) / sizeof(uint)]; // 2 Informations 25. ushort _16b[sizeof(double) / sizeof(ushort)]; // 4 Informations 26. uchar _8b [sizeof(double) / sizeof(uchar)]; // 8 Informations 27. }; 28. //+------------------------------------------------------------------+ 29. enum EnumEvents { 30. evTicTac, //Event of tic-tac 31. evHideMouse, //Hide mouse price line 32. evShowMouse, //Show mouse price line 33. evHideBarTime, //Hide bar time 34. evShowBarTime, //Show bar time 35. evHideDailyVar, //Hide daily variation 36. evShowDailyVar, //Show daily variation 37. evHidePriceVar, //Hide instantaneous variation 38. evShowPriceVar, //Show instantaneous variation 39. evCtrlReplayInit, //Initialize replay control 40. evChartTradeBuy, //Market buy event 41. evChartTradeSell, //Market sales event 42. evChartTradeCloseAll, //Event to close positions 43. evChartTrade_At_EA, //Event to communication 44. evEA_At_ChartTrade //Event to communication 45. }; 46. //+------------------------------------------------------------------+
Arquivo de cabeçalho Defines.mqh
A tal linha referida é a linha 30, mas o fato de ela ter sido adicionada no início da enumeração, muda tudo. Este evento, o tal de tic-tac é um evento de sincronismo. No momento atual, ele ainda não é de fato usado ainda no código do sistema de replay/simulador. Mas no momento em que for preciso fazer a sincronização de diversas coisas, este evento será muito requisitado. Por conta de algumas aplicações, que já estão sendo desenvolvidas, isto para meu uso pessoal, necessitar de tal sincronização, este evento foi adicionado. Porém, isto é devido, ao fato que comentei acima. Onde estou unificando todos os meus projetos para dentro deste ligado ao sistema de replay/simulador.
Então este evento Tic-Tac, é justamente uma das partes que você, caro leitor, não irá de fato entender o porquê faz parte do código do replay/simulador. Mas não se preocupe, quem sabe, futuramente, eu não venha a mostrar tais aplicações que fazem uso de tal evento.
Com isto, finalizamos esta parte. Podemos passar para a próxima parte. Onde o real interesse é o de evitar a degradação da performance do sistema. Isto por conta que ela de fato começou a se degradar. Mas para separar as coisas, vamos a um novo tópico.
Diminuindo a degradação da performance
Durante um bom tempo, o sistema tem tido uma performance adequada. Porém ao iniciar uma nova fase, onde começo a fazer uso mais massivo de algumas coisas. Foi notada uma degradação geral na performance do sistema de replay/simulador. Mesmo quando fazemos uso dos programas, desenvolvidos para ele, serem utilizados em conta real ou conta demo. Tal degradação tem motivos bem específicos e aqui mostrarei onde está a causa. E uma solução que se adéqua pelo menos neste primeiro momento.
O principal problema, e este é o responsável pela degradação na performance geral, como um todo, é o indicador de mouse. Mas como assim? Não que o indicador de mouse, esteja consumindo mais recursos do que era previsto. Mas quando começamos a fazer uso da leitura do buffer do indicador, isto de forma um pouco mais intensa, as coisas começam a ficar meio que lentas.
O motivo disto é um procedimento que existe dentro da classe C_Mouse. Na verdade, o problema é que quando vamos ler o buffer do indicador, começamos a acumular pequenos delays. Isto é causado por conta do sistema de tradução de coordenadas gráficas em coordenadas de preço x tempo. Se isto é feito de uma forma muito frequente, temos um retardo perceptível na posição da linha de preço que pertence ao indicador de mouse.
A forma de sanar isto é isolar de alguma maneira tal efeito. Seja lendo menos o buffer do indicador, seja fazendo uso de alguma outra técnica. Infelizmente, precisamos muito do mouse, já que todo o sistema é um sistema gráfico. Porém, existe uma solução para este problema. Isto sem recorrer a uma solução mais extrema.
Tal solução, deriva do fato, de que precisamos realmente é que o indicador de mouse, nos permita diferenciar, quando estamos fazendo um estudo de quando não estamos fazendo tal coisa. Este é o real motivo de sua criação. Quando um estudo está sendo feito, qualquer clique que venha a ocorrer, deverá ser ignorado por nossas aplicações que estiverem no gráfico.
Para que as aplicações, saibam o que o mouse está fazendo, isto referente ao fato de ele estar ou não sendo usado em um estudo. Devemos fazer um teste. Este teste é executado, ao se chamar a função CheckClick. Quando esta função retorna verdadeiro, para o chamador, sabemos que a posição de mouse, poderá ser utilizada. Quando esta função retorna falso, indica que o mouse está em modo de estudo. Então qualquer posição neste caso deverá ser ignorada pela aplicação que queira usar o mouse. Isto quando ela tem o conhecimento do que o mouse está fazendo.
Para isto precisamos do indicador no gráfico. Para resolver todos estes problemas, diversas mudanças foram necessárias. Para não deixar a coisa muito extensa e cansativa de ser vista. Já que em grande parte, o que foi preciso ser feito, é remover algo de uma classe e enviar para outra classe. Não irei de fato entrar em detalhes, minuciosos das mudanças.
O que de fato você precisa entender, é que antes todo o trabalho era feito dentro da classe C_Mouse. Isto para que qualquer aplicação, que viesse a fazer uso do indicador de mouse, pudesse de fato acessar os dados necessários. Agora este trabalho está dividido entre as classes C_Mouse e C_Terminal. Porém, sem que o indicador de mouse esteja de fato no gráfico, não teremos as aplicações que dependem do mouse funcionando de forma integral. Assim sendo, todas as mudanças fizeram com que a nova hierarquia de classes passasse a ser como mostrada na imagem abaixo. Isto já focando nas aplicações já construídas até este momento.

Esta imagem acima, mostra exatamente como as classes passaram a ser estruturadas. Sendo assim, você as deve imaginar como é visto acima. Com um detalhe. Cada um dos retângulos indicam uma aplicação diferente. Ou seja, mesmo que a classe C_ChartFloatingRAD, herde a classe C_Mouse. O indicador Chart Trade, não irá de fato ser funcional, sem que o indicador de mouse esteja presente no gráfico.
Isto talvez possa soar estranho, para muitos que ao olhar esta hierarquia de classes. Pense que o fato da classe C_ChartFloatingRAD, herdar a classe C_Mouse, faria com que o indicador Chart Trade, não viesse a precisar do indicador de mouse no mesmo gráfico. Porém, a forma como tais indicadores foram construídos, faz com que o indicador Chart Trade, de fato dependa do indicador de mouse. Se este último não estiver presente no gráfico, o Chart Trade ficará inacessível ao usuário.
Este tipo de comportamento, foi pensado e admitido como adequado. Mesmo que para muitos isto não faça de fato sentido. Mas observe que tanto o teclado, quando o mouse, se ligam de fato a classe C_Terminal e a classe C_Mouse. Porém a construção de ambas as classes, foi feita de maneira a garantir uma menor degradação da performance geral. Mas mesmo assim, se for necessário, o buffer do indicador de mouse, irá de fato conter os dados que poderão ser acessados, pelo indicador Chart Trade. Isto não é uma exclusividade do indicador Chart Trade.
Na verdade, a ideia é que qualquer coisa dentro do terminal gráfico, tenha um acesso que seja o mais rápido quanto for possível dos dados presentes na classe C_Mouse. Mas se você desejar transferir tais informações por outros aplicativos, ou acessá-las em um serviço, poderá ler o buffer do indicador e terá as mesmas informações. Porém, neste caso, o peso será retirado do indicador, e passará para a outra aplicação. Não degradando desta maneira a performance geral do sistema que está sendo criado.
Além deste fato, que foi mencionado acima, foram feitas diversas mudanças a fim de reduzir o número de chamadas a determinadas funções. O motivo disto, é que muitas vezes, alguns valores, podem ser armazenados dentro de alguma variável privativa da classe. Quando isto é possível e o valor desta variável será constante durante todo o tempo de vida da aplicação. Podemos e devemos fazer uso preferencialmente de uma variável, em vez de executar uma chamada a fim de obter o mesmo valor, a todo momento.
Desta forma, você, caro leitor, irá de fato notar que muitas chamadas a fim de obter o ID do gráfico, foram removidas para dentro do constructor da classe. No lugar de tais chamadas, o que passou a ser feito, foi referenciar uma dada variável que contém este mesmo valor de ID do gráfico. Visto que este valor, não será modificado durante todo o tempo em que o gráfico permanecer aberto. Este tipo de coisa, apesar de economizar alguns poucos ciclos de processador, pode de fato fazer muita diferença. Ainda mais quando o número de chamadas a uma dada função for relativamente alto.
Muito provavelmente você não notará grande melhoria no desempenho geral. Isto por ter tomado apenas e somente esta medida. Mas não subestime estas pequenas perdas, pois conforme elas se acumulam, acabam se tornando significativas.
Muito bem, então vamos ver como os novos códigos de fato ficaram. Como o intuito destes artigos, não é de fato fornecer códigos fontes, e sim passar conhecimento. Irei manter as coisas como estão sendo feitas, já a algum tempo. Ou seja, não teremos código fonte no anexo. Mas você os terá na íntegra logo abaixo. Isto por que tais aplicações, fazem uso de mais de um arquivo, e como você pode notar na imagem acima, existe uma ligação entre eles. Assim, se eu postar o código não no anexo, mas sim no artigo. Quem desejar apenas usar o código, sem de fato entender programação, ficará impedido de fazer isto. Pois não irá de fato, conseguir construir a estrutura de diretórios para que o código seja compilado, gerando assim uma aplicação.
Para aqueles que estão começando me desculpe, por fazer isto e desta maneira. Porém, quero que você não seja do tipo que gosta de usar CTRL + C e CTRL + V. Muita gente simplesmente faz isto, e acaba fazendo grandes porcarias, e depois pedindo para que alguém as ajude a fazer o código funcionar. Não quero e não desejo incentivar ninguém a isto. Quero que você aprenda e compreenda o que está acontecendo no programa que está sendo desenvolvido. Sendo assim, para aqueles que gostariam do código no anexo, peço desculpas. Mas se você já tem uma certa noção de programação, conseguirá obter os fontes rapidamente. Então vamos começar o a classe C_Terminal. A nova classe pode ser vista na íntegra logo abaixo:
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "Macros.mqh" 005. #include "..\Defines.mqh" 006. //+------------------------------------------------------------------+ 007. class C_Terminal 008. { 009. //+------------------------------------------------------------------+ 010. public : 011. //+------------------------------------------------------------------+ 012. struct st_Mouse 013. { 014. struct st00 015. { 016. short X_Adjusted, 017. Y_Adjusted, 018. X_Graphics, 019. Y_Graphics; 020. double Price; 021. datetime dt; 022. }Position; 023. uchar ButtonStatus; 024. bool ExecStudy; 025. }; 026. //+------------------------------------------------------------------+ 027. protected: 028. enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance}; 029. //+------------------------------------------------------------------+ 030. struct st_Terminal 031. { 032. ENUM_SYMBOL_CHART_MODE ChartMode; 033. ENUM_ACCOUNT_MARGIN_MODE TypeAccount; 034. long ID; 035. string szSymbol; 036. int Width, 037. Height, 038. nDigits, 039. SubWin, 040. HeightBar; 041. double PointPerTick, 042. ValuePerPoint, 043. VolumeMinimal, 044. AdjustToTrade; 045. }; 046. //+------------------------------------------------------------------+ 047. void CurrentSymbol(bool bUsingFull) 048. { 049. MqlDateTime mdt1; 050. string sz0, sz1; 051. datetime dt = macroGetDate(TimeCurrent(mdt1)); 052. enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER; 053. 054. sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3); 055. for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS); 056. switch (eTS) 057. { 058. case DOL : 059. case WDO : sz1 = "FGHJKMNQUVXZ"; break; 060. case IND : 061. case WIN : sz1 = "GJMQVZ"; break; 062. default : return; 063. } 064. sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS))); 065. for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0)) 066. if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break; 067. } 068. //+------------------------------------------------------------------+ 069. inline void DecodeMousePosition(int xi, int yi) 070. { 071. int w = 0; 072. 073. xi = (xi > 0 ? xi : 0); 074. yi = (yi > 0 ? yi : 0); 075. ChartXYToTimePrice(m_Infos.ID, m_Mouse.Position.X_Graphics = (short)xi, m_Mouse.Position.Y_Graphics = (short)yi, w, m_Mouse.Position.dt, m_Mouse.Position.Price); 076. m_Mouse.Position.dt = AdjustTime(m_Mouse.Position.dt); 077. m_Mouse.Position.Price = AdjustPrice(m_Mouse.Position.Price); 078. ChartTimePriceToXY(m_Infos.ID, w, m_Mouse.Position.dt, m_Mouse.Position.Price, xi, yi); 079. yi -= (int)ChartGetInteger(m_Infos.ID, CHART_WINDOW_YDISTANCE, m_Infos.SubWin); 080. m_Mouse.Position.X_Adjusted = (short) xi; 081. m_Mouse.Position.Y_Adjusted = (short) yi; 082. } 083. //+------------------------------------------------------------------+ 084. private : 085. st_Terminal m_Infos; 086. st_Mouse m_Mouse; 087. struct mem 088. { 089. long Show_Descr, 090. Show_Date; 091. bool AccountLock; 092. }m_Mem; 093. //+------------------------------------------------------------------+ 094. inline void ChartChange(void) 095. { 096. int x, y, t; 097. 098. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 099. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 100. ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t); 101. ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y); 102. m_Infos.HeightBar = (int)(t - y) / 100; 103. } 104. //+------------------------------------------------------------------+ 105. public : 106. //+------------------------------------------------------------------+ 107. C_Terminal(const long id = 0, const uchar sub = 0) 108. { 109. m_Infos.ID = (id == 0 ? ChartID() : id); 110. m_Mem.AccountLock = false; 111. m_Infos.SubWin = (int) sub; 112. CurrentSymbol(false); 113. ZeroMemory(m_Mouse); 114. m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR); 115. m_Mem.Show_Date = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE); 116. ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false); 117. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true); 118. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true); 119. ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false); 120. m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS); 121. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 122. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 123. m_Infos.PointPerTick = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE); 124. m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE); 125. m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP); 126. m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick; 127. m_Infos.ChartMode = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE); 128. if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)); 129. ChartChange(); 130. } 131. //+------------------------------------------------------------------+ 132. ~C_Terminal() 133. { 134. ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date); 135. ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr); 136. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false); 137. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false); 138. } 139. //+------------------------------------------------------------------+ 140. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg) 141. { 142. if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true; 143. m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING); 144. } 145. //+------------------------------------------------------------------+ 146. inline const st_Terminal GetInfoTerminal(void) const 147. { 148. return m_Infos; 149. } 150. //+------------------------------------------------------------------+ 151. inline const st_Mouse GetPositionsMouse(void) const 152. { 153. return m_Mouse; 154. } 155. //+------------------------------------------------------------------+ 156. const double AdjustPrice(const double arg) const 157. { 158. return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits); 159. } 160. //+------------------------------------------------------------------+ 161. inline datetime AdjustTime(const datetime arg) 162. { 163. int nSeconds= PeriodSeconds(); 164. datetime dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0); 165. 166. return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt))); 167. } 168. //+------------------------------------------------------------------+ 169. inline double FinanceToPoints(const double Finance, const uint Leverage) 170. { 171. double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1)); 172. 173. return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade))); 174. }; 175. //+------------------------------------------------------------------+ 176. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 177. { 178. static string st_str = ""; 179. 180. switch (id) 181. { 182. case CHARTEVENT_CHART_CHANGE: 183. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 184. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 185. ChartChange(); 186. break; 187. case CHARTEVENT_MOUSE_MOVE: 188. DecodeMousePosition((int)lparam, (int)dparam); 189. break; 190. case CHARTEVENT_OBJECT_CLICK: 191. if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false); 192. if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true) 193. ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true); 194. break; 195. case CHARTEVENT_OBJECT_CREATE: 196. if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false); 197. st_str = sparam; 198. break; 199. } 200. } 201. //+------------------------------------------------------------------+ 202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) const 203. { 204. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false); 205. ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0); 206. ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n"); 207. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false); 208. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor); 209. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false); 210. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false); 211. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder); 212. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true); 213. } 214. //+------------------------------------------------------------------+ 215. bool IndicatorCheckPass(const string szShortName) 216. { 217. string szTmp = szShortName + "_TMP"; 218. 219. IndicatorSetString(INDICATOR_SHORTNAME, szTmp); 220. m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin); 221. if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE) 222. { 223. ChartIndicatorDelete(m_Infos.ID, 0, szTmp); 224. Print("Only one instance is allowed..."); 225. SetUserError(C_Terminal::ERR_NoMoreInstance); 226. 227. return false; 228. } 229. IndicatorSetString(INDICATOR_SHORTNAME, szShortName); 230. 231. return true; 232. } 233. //+------------------------------------------------------------------+ 234. };
Arquivo de cabeçalho C_Terminal.mqh
Quem está visualizando este código pela primeira vez, pode imaginar que ele é grande e complexo. Porém grande parte dele se manteve intacto. Se bem, que ocorreram mudanças, devido ao fato, de que agora ele ajudará a classe C_Mouse a executar suas tarefas. Por isto que agora temos no procedimento DispatchMessage, o evento CHARTEVENT_MOUSE_MOVE. Isto por que, grande parte do trabalho será feito aqui.
Observe que na linha 12 agora temos uma estrutura para fornecer os dados do mouse. No entanto, o procedimento DecodeMousePosition, presente na linha 69 não irá de fato preencher os dados referentes a questão dos botões. Tal coisa será de responsabilidade da classe C_Mouse executar. Sendo assim a próxima coisa que deveremos ver, é justamente a classe C_Mouse, que se encontra logo abaixo.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "C_Terminal.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_MousePrefixName "MouseBase" + (string)GetInfoTerminal().SubWin + "_" 007. #define macro_NameObjectStudy (def_MousePrefixName + "T" + (string)ObjectsTotal(0)) 008. //+------------------------------------------------------------------+ 009. class C_Mouse : public C_Terminal 010. { 011. public : 012. enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay}; 013. enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10}; 014. //+------------------------------------------------------------------+ 015. protected: 016. //+------------------------------------------------------------------+ 017. void CreateObjToStudy(int x, int w, string szName, color backColor = clrNONE) const 018. { 019. if (!m_Mem.IsInitOk) return; 020. CreateObjectGraphics(szName, OBJ_BUTTON); 021. ObjectSetInteger(m_Mem.id, szName, OBJPROP_STATE, true); 022. ObjectSetInteger(m_Mem.id, szName, OBJPROP_BORDER_COLOR, clrBlack); 023. ObjectSetInteger(m_Mem.id, szName, OBJPROP_COLOR, clrBlack); 024. ObjectSetInteger(m_Mem.id, szName, OBJPROP_BGCOLOR, backColor); 025. ObjectSetString(m_Mem.id, szName, OBJPROP_FONT, "Lucida Console"); 026. ObjectSetInteger(m_Mem.id, szName, OBJPROP_FONTSIZE, 10); 027. ObjectSetInteger(m_Mem.id, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 028. ObjectSetInteger(m_Mem.id, szName, OBJPROP_XDISTANCE, x); 029. ObjectSetInteger(m_Mem.id, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1); 030. ObjectSetInteger(m_Mem.id, szName, OBJPROP_XSIZE, w); 031. ObjectSetInteger(m_Mem.id, szName, OBJPROP_YSIZE, 18); 032. } 033. //+------------------------------------------------------------------+ 034. private : 035. enum eStudy {eStudyNull, eStudyCreate, eStudyExecute}; 036. struct st01 037. { 038. st_Mouse Data; 039. color corLineH, 040. corTrendP, 041. corTrendN; 042. eStudy Study; 043. }m_Info; 044. struct st_Mem 045. { 046. bool CrossHair, 047. IsFull, 048. IsInitOk; 049. datetime dt; 050. string szShortName, 051. szLineH, 052. szLineV, 053. szLineT, 054. szBtnS; 055. long id; 056. }m_Mem; 057. //+------------------------------------------------------------------+ 058. void GetDimensionText(const string szArg, int &w, int &h) 059. { 060. TextSetFont("Lucida Console", -100, FW_NORMAL); 061. TextGetSize(szArg, w, h); 062. h += 5; 063. w += 5; 064. } 065. //+------------------------------------------------------------------+ 066. void CreateStudy(void) 067. { 068. if (m_Mem.IsFull) 069. { 070. CreateObjectGraphics(m_Mem.szLineV = macro_NameObjectStudy, OBJ_VLINE, m_Info.corLineH); 071. CreateObjectGraphics(m_Mem.szLineT = macro_NameObjectStudy, OBJ_TREND, m_Info.corLineH); 072. ObjectSetInteger(m_Mem.id, m_Mem.szLineT, OBJPROP_WIDTH, 2); 073. CreateObjToStudy(0, 0, m_Mem.szBtnS = macro_NameObjectStudy); 074. } 075. m_Info.Study = eStudyCreate; 076. } 077. //+------------------------------------------------------------------+ 078. void ExecuteStudy(const double memPrice) 079. { 080. double v1 = GetPositionsMouse().Position.Price - memPrice; 081. int w, h; 082. 083. if (!CheckClick(eClickLeft)) 084. { 085. m_Info.Study = eStudyNull; 086. ChartSetInteger(m_Mem.id, CHART_MOUSE_SCROLL, true); 087. if (m_Mem.IsFull) ObjectsDeleteAll(m_Mem.id, def_MousePrefixName + "T"); 088. }else if (m_Mem.IsFull) 089. { 090. string sz1 = StringFormat(" %." + (string)GetInfoTerminal().nDigits + "f [ %d ] %02.02f%% ", 091. MathAbs(v1), Bars(GetInfoTerminal().szSymbol, PERIOD_CURRENT, m_Mem.dt, GetPositionsMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0)); 092. GetDimensionText(sz1, w, h); 093. ObjectSetString(m_Mem.id, m_Mem.szBtnS, OBJPROP_TEXT, sz1); 094. ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP)); 095. ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_XSIZE, w); 096. ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_YSIZE, h); 097. ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_XDISTANCE, GetPositionsMouse().Position.X_Adjusted - w); 098. ObjectSetInteger(m_Mem.id, m_Mem.szBtnS, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - (v1 < 0 ? 1 : h)); 099. ObjectMove(m_Mem.id, m_Mem.szLineT, 1, GetPositionsMouse().Position.dt, GetPositionsMouse().Position.Price); 100. ObjectSetInteger(m_Mem.id, m_Mem.szLineT, OBJPROP_COLOR, (memPrice > GetPositionsMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP)); 101. } 102. m_Info.Data.ButtonStatus = eKeyNull; 103. } 104. //+------------------------------------------------------------------+ 105. public : 106. //+------------------------------------------------------------------+ 107. C_Mouse(const long id, const string szShortName) 108. :C_Terminal(id) 109. { 110. m_Mem.IsInitOk = false; 111. m_Mem.id = GetInfoTerminal().ID; 112. m_Mem.szShortName = szShortName; 113. } 114. //+------------------------------------------------------------------+ 115. C_Mouse(const long id, const string szShortName, color corH, color corP, color corN) 116. :C_Terminal(id) 117. { 118. m_Mem.id = GetInfoTerminal().ID; 119. if (!(m_Mem.IsInitOk = IndicatorCheckPass(m_Mem.szShortName = szShortName))) return; 120. m_Mem.CrossHair = (bool)ChartGetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL); 121. ChartSetInteger(m_Mem.id, CHART_EVENT_MOUSE_MOVE, true); 122. ChartSetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL, false); 123. ZeroMemory(m_Info); 124. m_Info.corLineH = corH; 125. m_Info.corTrendP = corP; 126. m_Info.corTrendN = corN; 127. m_Info.Study = eStudyNull; 128. if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE)) 129. CreateObjectGraphics(m_Mem.szLineH = (def_MousePrefixName + (string)ObjectsTotal(0)), OBJ_HLINE, m_Info.corLineH); 130. ChartRedraw(m_Mem.id); 131. } 132. //+------------------------------------------------------------------+ 133. ~C_Mouse() 134. { 135. if (!m_Mem.IsInitOk) return; 136. ChartSetInteger(m_Mem.id, CHART_EVENT_OBJECT_DELETE, false); 137. ChartSetInteger(m_Mem.id, CHART_EVENT_MOUSE_MOVE, ChartWindowFind(m_Mem.id, m_Mem.szShortName) != -1); 138. ChartSetInteger(m_Mem.id, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair); 139. ObjectsDeleteAll(m_Mem.id, def_MousePrefixName); 140. } 141. //+------------------------------------------------------------------+ 142. inline bool CheckClick(const eBtnMouse value) 143. { 144. return (m_Info.Data.ButtonStatus & value) == value; 145. } 146. //+------------------------------------------------------------------+ 147. inline const st_Mouse GetInfoMouse(void) 148. { 149. if (!m_Mem.IsInitOk) 150. { 151. double Buff[]; 152. uCast_Double loc; 153. int handle = ChartIndicatorGet(m_Mem.id, 0, m_Mem.szShortName); 154. 155. ZeroMemory(m_Info.Data); 156. if (CopyBuffer(handle, 0, 0, 1, Buff) == 1) 157. { 158. loc.dValue = Buff[0]; 159. DecodeMousePosition((int)loc._16b[1], (int)loc._16b[2]); 160. m_Info.Data = GetPositionsMouse(); 161. m_Info.Data.ButtonStatus = loc._8b[0]; 162. } 163. IndicatorRelease(handle); 164. } 165. return m_Info.Data; 166. } 167. //+------------------------------------------------------------------+*/ 168. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 169. { 170. int w = 0; 171. static double memPrice = 0; 172. 173. C_Terminal::DispatchMessage(id, lparam, dparam, sparam); 174. switch (id) 175. { 176. case (CHARTEVENT_CUSTOM + evHideMouse): 177. if (m_Mem.IsFull) ObjectSetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR, clrNONE); 178. break; 179. case (CHARTEVENT_CUSTOM + evShowMouse): 180. if (m_Mem.IsFull) ObjectSetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR, m_Info.corLineH); 181. break; 182. case CHARTEVENT_MOUSE_MOVE: 183. m_Info.Data = GetPositionsMouse(); 184. if (m_Mem.IsFull) ObjectMove(m_Mem.id, m_Mem.szLineH, 0, 0, m_Info.Data.Position.Price); 185. if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(m_Mem.id, m_Mem.szLineV, 0, m_Info.Data.Position.dt, 0); 186. m_Info.Data.ButtonStatus = (uchar) sparam; 187. if (CheckClick(eClickMiddle) && (m_Info.Study != eStudyCreate)) 188. if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(m_Mem.id, m_Mem.szLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy(); 189. if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate)) 190. { 191. ChartSetInteger(m_Mem.id, CHART_MOUSE_SCROLL, false); 192. if (m_Mem.IsFull) ObjectMove(m_Mem.id, m_Mem.szLineT, 0, m_Mem.dt = m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price); 193. m_Info.Study = eStudyExecute; 194. } 195. if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice); 196. m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute; 197. break; 198. case CHARTEVENT_OBJECT_DELETE: 199. if ((m_Mem.IsFull) && (sparam == m_Mem.szLineH)) 200. CreateObjectGraphics(m_Mem.szLineH, OBJ_HLINE, m_Info.corLineH); 201. break; 202. } 203. } 204. //+------------------------------------------------------------------+ 205. }; 206. //+------------------------------------------------------------------+ 207. #undef macro_NameObjectStudy 208. //+------------------------------------------------------------------+
Arquivo de cabeçalho C_Mouse.mqh
Apesar de aparentemente, esta classe parecer igual, ao que era antes, a mesma conta com algumas mudanças. Tais mudanças, tem como objetivo tornar o uso do indicador de mouse, mais evidente. Porém você pode ver que algumas mudanças aconteceram. Deixo como tarefa, para os entusiastas e aspirantes a programador profissional, analisar e encontrar tais diferenças deste código visto acima, com o antigo código desta classe C_Mouse. Acredite vai ser algo bastante divertido, e que irá lhe mostrar diversas coisas interessantes sobre herança de classes. Apesar disto, quero lhe deixar uma pulga atrás da orelha, apenas para aguçar a sua curiosidade. Quero que olhe a linha 187 e se pergunte: Por que durante a criação de um estudo, estou testando para ver se existe um estudo sendo feito?
Como não podia deixar de passar, o código da classe C_Study também passou por mudanças. No entanto, tais mudanças, visam apenas evitar que um valor constante, que no caso é a ID do gráfico, seja a todo momento buscado via uma chamada de função. Assim o novo código da classe C_Study pode ser visto logo abaixo, também na íntegra.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\C_Mouse.mqh" 005. //+------------------------------------------------------------------+ 006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_" 007. //+------------------------------------------------------------------+ 008. class C_Study : public C_Mouse 009. { 010. private : 011. //+------------------------------------------------------------------+ 012. struct st00 013. { 014. eStatusMarket Status; 015. MqlRates Rate; 016. string szInfo, 017. szBtn1, 018. szBtn2, 019. szBtn3; 020. color corP, 021. corN; 022. int HeightText; 023. bool bvT, bvD, bvP; 024. long id; 025. }m_Info; 026. //+------------------------------------------------------------------+ 027. void Draw(void) 028. { 029. double v1; 030. 031. if (m_Info.bvT) 032. { 033. ObjectSetInteger(m_Info.id, m_Info.szBtn1, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 18); 034. ObjectSetString(m_Info.id, m_Info.szBtn1, OBJPROP_TEXT, m_Info.szInfo); 035. } 036. if (m_Info.bvD) 037. { 038. v1 = NormalizeDouble((((GetPositionsMouse().Position.Price - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2); 039. ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1); 040. ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 041. ObjectSetString(m_Info.id, m_Info.szBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 042. } 043. if (m_Info.bvP) 044. { 045. v1 = NormalizeDouble((((GL_PriceClose - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2); 046. ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1); 047. ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP)); 048. ObjectSetString(m_Info.id, m_Info.szBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1))); 049. } 050. } 051. //+------------------------------------------------------------------+ 052. inline void CreateObjInfo(EnumEvents arg) 053. { 054. switch (arg) 055. { 056. case evShowBarTime: 057. C_Mouse::CreateObjToStudy(2, 110, m_Info.szBtn1 = (def_ExpansionPrefix + (string)ObjectsTotal(0)), clrPaleTurquoise); 058. m_Info.bvT = true; 059. break; 060. case evShowDailyVar: 061. C_Mouse::CreateObjToStudy(2, 53, m_Info.szBtn2 = (def_ExpansionPrefix + (string)ObjectsTotal(0))); 062. m_Info.bvD = true; 063. break; 064. case evShowPriceVar: 065. C_Mouse::CreateObjToStudy(58, 53, m_Info.szBtn3 = (def_ExpansionPrefix + (string)ObjectsTotal(0))); 066. m_Info.bvP = true; 067. break; 068. } 069. } 070. //+------------------------------------------------------------------+ 071. inline void RemoveObjInfo(EnumEvents arg) 072. { 073. string sz; 074. 075. switch (arg) 076. { 077. case evHideBarTime: 078. sz = m_Info.szBtn1; 079. m_Info.bvT = false; 080. break; 081. case evHideDailyVar: 082. sz = m_Info.szBtn2; 083. m_Info.bvD = false; 084. break; 085. case evHidePriceVar: 086. sz = m_Info.szBtn3; 087. m_Info.bvP = false; 088. break; 089. } 090. ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, false); 091. ObjectDelete(m_Info.id, sz); 092. ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, true); 093. } 094. //+------------------------------------------------------------------+ 095. public : 096. //+------------------------------------------------------------------+ 097. C_Study(long IdParam, string szShortName, color corH, color corP, color corN) 098. :C_Mouse(IdParam, szShortName, corH, corP, corN) 099. { 100. ZeroMemory(m_Info); 101. m_Info.id = GetInfoTerminal().ID; 102. if (_LastError >= ERR_USER_ERROR_FIRST) return; 103. m_Info.corP = corP; 104. m_Info.corN = corN; 105. CreateObjInfo(evShowBarTime); 106. CreateObjInfo(evShowDailyVar); 107. CreateObjInfo(evShowPriceVar); 108. ResetLastError(); 109. } 110. //+------------------------------------------------------------------+ 111. void Update(const eStatusMarket arg) 112. { 113. int i0; 114. datetime dt; 115. 116. if (m_Info.Rate.close == 0) 117. m_Info.Rate.close = iClose(NULL, PERIOD_D1, ((_Symbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(NULL, PERIOD_D1, 0))) ? 0 : 1)); 118. switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status)) 119. { 120. case eCloseMarket : 121. m_Info.szInfo = "Closed Market"; 122. break; 123. case eInReplay : 124. case eInTrading : 125. i0 = PeriodSeconds(); 126. dt = (m_Info.Status == eInReplay ? (datetime) GL_TimeAdjust : TimeCurrent()); 127. m_Info.Rate.time = (m_Info.Rate.time <= dt ? (datetime)(((ulong) dt / i0) * i0) + i0 : m_Info.Rate.time); 128. if (dt > 0) m_Info.szInfo = TimeToString((datetime)m_Info.Rate.time - dt, TIME_SECONDS); 129. break; 130. case eAuction : 131. m_Info.szInfo = "Auction"; 132. break; 133. default : 134. m_Info.szInfo = "ERROR"; 135. } 136. Draw(); 137. } 138. //+------------------------------------------------------------------+ 139. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 140. { 141. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 142. switch (id) 143. { 144. case CHARTEVENT_CUSTOM + evHideBarTime: 145. RemoveObjInfo(evHideBarTime); 146. break; 147. case CHARTEVENT_CUSTOM + evShowBarTime: 148. CreateObjInfo(evShowBarTime); 149. break; 150. case CHARTEVENT_CUSTOM + evHideDailyVar: 151. RemoveObjInfo(evHideDailyVar); 152. break; 153. case CHARTEVENT_CUSTOM + evShowDailyVar: 154. CreateObjInfo(evShowDailyVar); 155. break; 156. case CHARTEVENT_CUSTOM + evHidePriceVar: 157. RemoveObjInfo(evHidePriceVar); 158. break; 159. case CHARTEVENT_CUSTOM + evShowPriceVar: 160. CreateObjInfo(evShowPriceVar); 161. break; 162. case CHARTEVENT_MOUSE_MOVE: 163. Draw(); 164. break; 165. } 166. ChartRedraw(m_Info.id); 167. } 168. //+------------------------------------------------------------------+ 169. }; 170. //+------------------------------------------------------------------+ 171. #undef def_ExpansionPrefix 172. #undef def_MousePrefixName 173. //+------------------------------------------------------------------+
Arquivo de cabeçalho C_Study.mqh
Bem, se você fizer uso de todos estes códigos fontes que foram vistos até este momento, irá de fato conseguir criar o indicador de mouse. Porém, ainda faltam mais dois códigos para terminar este artigo. Já que não quero deixá-los para um outro artigo. Visto que no próximo artigo o conteúdo será outro. Assim sendo quero já deixar tudo pronto, para evitar voltar neste mesmo assunto. Pelo menos durante um bom tempo. Desta forma, vamos ver os dois últimos arquivos contendo código fonte. Começando com o código da classe C_ChartFloatingRAD, que pode ser visto na íntegra logo abaixo:
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "../Auxiliar/C_Mouse.mqh" 005. #include "C_AdjustTemplate.mqh" 006. //+------------------------------------------------------------------+ 007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", m_Init.id, A) 008. #define macro_CloseIndicator(A) { \ 009. OnDeinit(REASON_INITFAILED); \ 010. SetUserError(A); \ 011. return; \ 012. } 013. //+------------------------------------------------------------------+ 014. class C_ChartFloatingRAD : private C_Mouse 015. { 016. private : 017. enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL}; 018. struct st00 019. { 020. short x, y, minx, miny, 021. Leverage; 022. string szObj_Chart, 023. szObj_Editable, 024. szFileNameTemplate; 025. long WinHandle; 026. double FinanceTake, 027. FinanceStop; 028. bool IsMaximized, 029. IsDayTrade, 030. IsSaveState; 031. struct st01 032. { 033. short x, y, w, h; 034. color bgcolor; 035. int FontSize; 036. string FontName; 037. }Regions[MSG_NULL]; 038. }m_Info; 039. struct st01 040. { 041. short y[2]; 042. bool bOk; 043. long id; 044. }m_Init; 045. //+------------------------------------------------------------------+ 046. void CreateWindowRAD(int w, int h) 047. { 048. m_Info.szObj_Chart = "Chart Trade IDE"; 049. m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit"; 050. ObjectCreate(m_Init.id, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0); 051. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x); 052. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y); 053. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XSIZE, w); 054. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YSIZE, h); 055. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false); 056. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false); 057. m_Info.WinHandle = ObjectGetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_CHART_ID); 058. }; 059. //+------------------------------------------------------------------+ 060. void AdjustEditabled(C_AdjustTemplate &Template, bool bArg) 061. { 062. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_STOP_VALUE; c0++) 063. if (bArg) 064. { 065. Template.Add(EnumToString(c0), "bgcolor", NULL); 066. Template.Add(EnumToString(c0), "fontsz", NULL); 067. Template.Add(EnumToString(c0), "fontnm", NULL); 068. } 069. else 070. { 071. m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor")); 072. m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz")); 073. m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm"); 074. } 075. } 076. //+------------------------------------------------------------------+ 077. inline void AdjustTemplate(const bool bFirst = false) 078. { 079. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade 080. 081. C_AdjustTemplate *Template; 082. 083. if (bFirst) 084. { 085. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(m_Init.id) + ".tpl", true); 086. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 087. { 088. (*Template).Add(EnumToString(c0), "size_x", NULL); 089. (*Template).Add(EnumToString(c0), "size_y", NULL); 090. (*Template).Add(EnumToString(c0), "pos_x", NULL); 091. (*Template).Add(EnumToString(c0), "pos_y", NULL); 092. } 093. AdjustEditabled(Template, true); 094. }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate); 095. if (_LastError >= ERR_USER_ERROR_FIRST) 096. { 097. delete Template; 098. 099. return; 100. } 101. m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage); 102. m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage)); 103. m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage)); 104. (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol); 105. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage)); 106. (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2)); 107. (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2)); 108. (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0")); 109. (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0")); 110. if (!(*Template).Execute()) 111. { 112. delete Template; 113. 114. macro_CloseIndicator(C_Terminal::ERR_FileAcess); 115. }; 116. if (bFirst) 117. { 118. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 119. { 120. m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x")); 121. m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y")); 122. m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x")); 123. m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y")); 124. } 125. m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x; 126. AdjustEditabled(Template, false); 127. }; 128. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? m_Init.y[m_Init.bOk] : m_Info.Regions[MSG_TITLE_IDE].h + 6)); 129. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx)); 130. ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny)); 131. 132. delete Template; 133. 134. ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate); 135. ChartRedraw(m_Info.WinHandle); 136. 137. #undef macro_PointsToFinance 138. } 139. //+------------------------------------------------------------------+ 140. eObjectsIDE CheckMousePosition(short &x, short &y) 141. { 142. int xi, yi, xf, yf; 143. st_Mouse loc; 144. 145. loc = GetPositionsMouse(); 146. x = loc.Position.X_Graphics; 147. y = loc.Position.Y_Graphics; 148. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 149. { 150. xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x; 151. yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y; 152. xf = xi + m_Info.Regions[c0].w; 153. yf = yi + m_Info.Regions[c0].h; 154. if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0; 155. } 156. return MSG_NULL; 157. } 158. //+------------------------------------------------------------------+ 159. inline void DeleteObjectEdit(void) 160. { 161. ChartRedraw(); 162. ObjectsDeleteAll(m_Init.id, m_Info.szObj_Editable); 163. } 164. //+------------------------------------------------------------------+ 165. template <typename T > 166. void CreateObjectEditable(eObjectsIDE arg, T value) 167. { 168. DeleteObjectEdit(); 169. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0); 170. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 171. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3); 172. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w); 173. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h); 174. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor); 175. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER); 176. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1); 177. ObjectSetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName); 178. ObjectSetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value)); 179. ChartRedraw(); 180. } 181. //+------------------------------------------------------------------+ 182. bool RestoreState(void) 183. { 184. uCast_Double info; 185. bool bRet; 186. C_AdjustTemplate *Template; 187. 188. if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue)) 189. { 190. m_Info.x = (short) info._16b[0]; 191. m_Info.y = (short) info._16b[1]; 192. m_Info.minx = (short) info._16b[2]; 193. m_Info.miny = (short) info._16b[3]; 194. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(m_Init.id) + ".tpl"); 195. if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else 196. { 197. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL); 198. (*Template).Add("MSG_TAKE_VALUE", "descr", NULL); 199. (*Template).Add("MSG_STOP_VALUE", "descr", NULL); 200. (*Template).Add("MSG_DAY_TRADE", "state", NULL); 201. (*Template).Add("MSG_MAX_MIN", "state", NULL); 202. if (!(*Template).Execute()) bRet = false; else 203. { 204. m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1; 205. m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1; 206. m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr")); 207. m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr")); 208. m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr")); 209. } 210. }; 211. delete Template; 212. }; 213. 214. GlobalVariablesDeleteAll(macro_NameGlobalVariable("")); 215. 216. return bRet; 217. } 218. //+------------------------------------------------------------------+ 219. public : 220. //+------------------------------------------------------------------+ 221. C_ChartFloatingRAD(string szShortName, const short Leverage, const double FinanceTake, const double FinanceStop) 222. :C_Mouse(0, "") 223. { 224. m_Init.id = GetInfoTerminal().ID; 225. m_Info.IsSaveState = false; 226. if (!IndicatorCheckPass(szShortName)) return; 227. if (!RestoreState()) 228. { 229. m_Info.Leverage = Leverage; 230. m_Info.IsDayTrade = true; 231. m_Info.FinanceTake = FinanceTake; 232. m_Info.FinanceStop = FinanceStop; 233. m_Info.IsMaximized = true; 234. m_Info.minx = m_Info.x = 115; 235. m_Info.miny = m_Info.y = 64; 236. } 237. m_Init.y[false] = 150; 238. m_Init.y[true] = 210; 239. CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]); 240. AdjustTemplate(true); 241. } 242. //+------------------------------------------------------------------+ 243. ~C_ChartFloatingRAD() 244. { 245. ChartRedraw(); 246. ObjectsDeleteAll(m_Init.id, m_Info.szObj_Chart); 247. if (!m_Info.IsSaveState) 248. FileDelete(m_Info.szFileNameTemplate); 249. } 250. //+------------------------------------------------------------------+ 251. void SaveState(void) 252. { 253. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B); 254. 255. uCast_Double info; 256. 257. info._16b[0] = m_Info.x; 258. info._16b[1] = m_Info.y; 259. info._16b[2] = m_Info.minx; 260. info._16b[3] = m_Info.miny; 261. macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue); 262. m_Info.IsSaveState = true; 263. 264. #undef macro_GlobalVariable 265. } 266. //+------------------------------------------------------------------+ 267. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 268. { 269. #define macro_AdjustMinX(A, B) { \ 270. B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x; \ 271. mx = x - m_Info.Regions[MSG_TITLE_IDE].w; \ 272. A = (B ? (mx > 0 ? mx : 0) : A); \ 273. } 274. #define macro_AdjustMinY(A, B) { \ 275. B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y; \ 276. my = y - m_Info.Regions[MSG_TITLE_IDE].h; \ 277. A = (B ? (my > 0 ? my : 0) : A); \ 278. } 279. 280. static short sx = -1, sy = -1, sz = -1; 281. static eObjectsIDE obj = MSG_NULL; 282. short x, y, mx, my; 283. double dvalue; 284. bool b1, b2, b3, b4; 285. ushort ev = evChartTradeCloseAll; 286. 287. C_Mouse::DispatchMessage(id, lparam, dparam, sparam); 288. switch (id) 289. { 290. case CHARTEVENT_CUSTOM + evEA_At_ChartTrade: 291. if (m_Init.bOk = ((lparam >= 0) && (lparam < 2))) 292. CurrentSymbol((bool)lparam); 293. AdjustTemplate(true); 294. break; 295. case CHARTEVENT_CHART_CHANGE: 296. if (!m_Init.bOk) 297. EventChartCustom(m_Init.id, evChartTrade_At_EA, 0, 0, ""); 298. x = (short)ChartGetInteger(m_Init.id, CHART_WIDTH_IN_PIXELS); 299. y = (short)ChartGetInteger(m_Init.id, CHART_HEIGHT_IN_PIXELS); 300. macro_AdjustMinX(m_Info.x, b1); 301. macro_AdjustMinY(m_Info.y, b2); 302. macro_AdjustMinX(m_Info.minx, b3); 303. macro_AdjustMinY(m_Info.miny, b4); 304. if (b1 || b2 || b3 || b4) AdjustTemplate(); 305. break; 306. case CHARTEVENT_MOUSE_MOVE: 307. if (CheckClick(C_Mouse::eClickLeft)) 308. { 309. switch (CheckMousePosition(x, y)) 310. { 311. case MSG_MAX_MIN: 312. if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 313. break; 314. case MSG_DAY_TRADE: 315. if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true); 316. break; 317. case MSG_LEVERAGE_VALUE: 318. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage); 319. break; 320. case MSG_TAKE_VALUE: 321. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake); 322. break; 323. case MSG_STOP_VALUE: 324. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop); 325. break; 326. case MSG_TITLE_IDE: 327. if (sx < 0) 328. { 329. ChartSetInteger(m_Init.id, CHART_MOUSE_SCROLL, false); 330. sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx); 331. sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny); 332. } 333. if ((mx = x - sx) > 0) ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx); 334. if ((my = y - sy) > 0) ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my); 335. if (m_Info.IsMaximized) 336. { 337. m_Info.x = (mx > 0 ? mx : m_Info.x); 338. m_Info.y = (my > 0 ? my : m_Info.y); 339. }else 340. { 341. m_Info.minx = (mx > 0 ? mx : m_Info.minx); 342. m_Info.miny = (my > 0 ? my : m_Info.miny); 343. } 344. break; 345. case MSG_BUY_MARKET: 346. ev = evChartTradeBuy; 347. case MSG_SELL_MARKET: 348. ev = (ev != evChartTradeBuy ? evChartTradeSell : ev); 349. case MSG_CLOSE_POSITION: 350. if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk)) 351. { 352. string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'), 353. m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage)); 354. PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp); 355. EventChartCustom(m_Init.id, ev, 0, 0, szTmp); 356. } 357. break; 358. } 359. if (sz < 0) 360. { 361. sz = x; 362. AdjustTemplate(); 363. if (obj == MSG_NULL) DeleteObjectEdit(); 364. } 365. }else 366. { 367. sz = -1; 368. if (sx > 0) 369. { 370. ChartSetInteger(m_Init.id, CHART_MOUSE_SCROLL, true); 371. sx = sy = -1; 372. } 373. } 374. break; 375. case CHARTEVENT_OBJECT_ENDEDIT: 376. switch (obj) 377. { 378. case MSG_LEVERAGE_VALUE: 379. case MSG_TAKE_VALUE: 380. case MSG_STOP_VALUE: 381. dvalue = StringToDouble(ObjectGetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_TEXT)); 382. if (obj == MSG_TAKE_VALUE) 383. m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue); 384. else if (obj == MSG_STOP_VALUE) 385. m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue); 386. else 387. m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue)); 388. AdjustTemplate(); 389. obj = MSG_NULL; 390. ObjectDelete(m_Init.id, m_Info.szObj_Editable); 391. break; 392. } 393. break; 394. case CHARTEVENT_OBJECT_DELETE: 395. if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown); 396. break; 397. } 398. ChartRedraw(); 399. } 400. //+------------------------------------------------------------------+ 401. }; 402. //+------------------------------------------------------------------+ 403. #undef macro_NameGlobalVariable 404. #undef macro_CloseIndicator 405. //+------------------------------------------------------------------+
Arquivo de cabeçalho C_ChartFloatingRAD.mqh
Apesar deste código, aparentar ser bem extenso. Para quem vem acompanhando esta sequência sobre o replay/simulação. Irá de fato notar, que grande parte do código visto acima, permanece inalterado. Não precisando assim ser de todo modificado, para quem está acompanhando e estudando os códigos. No entanto, você irá de fato perceber, que muitas das chamadas a GetInfoTerminal foram modificadas para m_Init.id. O fato de isto ter ocorrido, é justamente por conta que acessar uma variável é consideravelmente mais rápido do que acessar uma função.
Então na linha 224, você pode ver onde o valor da ID do gráfico é capturada, para ser usada, posteriormente por toda a classe. Mas mesmo assim, a grande questão aqui se dá no evento CHARTEVENT_MOUSE_MOVE, presente na linha 306. Observe que apesar de na linha 307, fazermos uma chamada a classe C_Mouse, está de fato retornará um valor falso, caso o indicador de mouse não esteja presente. Porém quando o indicador estiver presente, poderemos ter um valor verdadeiro sendo retornado.
Neste caso você deverá se atendar ao fato de que na linha 309, o procedimento CheckMousePosition não usará os dados de lparam e dparam. Este seriam os pontos onde estaria os valores de posição do mouse. Mas ao olharmos este procedimento, que se encontra na linha 140, você poderá notar que a linha 145 irá de fato buscar os dados de posição. Estes foram interpretados na classe C_Terminal. Então nas linhas 145 e 146, dizemos qual é o valor que desejamos usar. Neste caso estamos usando os valores absolutos. Mas também poderíamos usar os valores relativos.
De qualquer forma, os valores estão vindo da interpretação presente na classe C_Terminal. E não do buffer do indicador de mouse. Este tipo de coisa faz com que a execução seja mais eficiente e por conta disto mais rápida. Mas mesmo assim, estes mesmos valores estarão no buffer do indicador de mouse.
E para finalizar, vamos ver o último arquivo de código fonte. Já que ele também passou por mudanças. Isto graças as mudanças no constructor da classe C_ChartFloatingRAD. Estou falando do arquivo contendo o código fonte do indicador Chart Trade. Este pode ser visto logo abaixo:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Chart Trade Base Indicator." 04. #property description "See the articles for more details." 05. #property version "1.82" 06. #property icon "/Images/Market Replay/Icons/Indicators.ico" 07. #property link "https://www.mql5.com/pt/articles/12580" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh> 12. //+------------------------------------------------------------------+ 13. #define def_ShortName "Indicator Chart Trade" 14. //+------------------------------------------------------------------+ 15. C_ChartFloatingRAD *chart = NULL; 16. //+------------------------------------------------------------------+ 17. input ushort user01 = 1; //Leverage 18. input double user02 = 100.1; //Finance Take 19. input double user03 = 75.4; //Finance Stop 20. //+------------------------------------------------------------------+ 21. int OnInit() 22. { 23. chart = new C_ChartFloatingRAD(def_ShortName, user01, user02, user03); 24. 25. if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED; 26. 27. return INIT_SUCCEEDED; 28. } 29. //+------------------------------------------------------------------+ 30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 31. { 32. return rates_total; 33. } 34. //+------------------------------------------------------------------+ 35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 36. { 37. if (_LastError < ERR_USER_ERROR_FIRST) 38. (*chart).DispatchMessage(id, lparam, dparam, sparam); 39. } 40. //+------------------------------------------------------------------+ 41. void OnDeinit(const int reason) 42. { 43. switch (reason) 44. { 45. case REASON_INITFAILED: 46. ChartIndicatorDelete(ChartID(), 0, def_ShortName); 47. break; 48. case REASON_CHARTCHANGE: 49. (*chart).SaveState(); 50. break; 51. } 52. 53. delete chart; 54. } 55. //+------------------------------------------------------------------+
Código fonte do indicador Chart Trade
Note que a única mudança que ocorreu foi na linha 23. Mas para quem não viu as versões antigas. Poderá apreciar o código na íntegra.
Considerações finais
Neste artigo, apresentei todas as mudanças que aconteceram no código, visando dar uma melhor sustentabilidade ao mesmo. No próximo artigo trataremos de um outro assunto. No anexo deste artigo, você terá acesso as aplicações já compiladas, do indicador de Mouse e do indicador Chart Trade. Isto para que possa acessar a versão mais recente dos mesmos. Já que você já pode estar fazendo uso particular dos mesmos em suas negociações. Então nos vemos no próximo artigo.
| Arquivo | Descrição |
|---|---|
| Experts\Expert Advisor.mq5 | Demonstra a interação entre o Chart Trade e o Expert Advisor (É necessário o Mouse Study para interação) |
| Indicators\Chart Trade.mq5 | Cria a janela para configuração da ordem a ser enviada (É necessário o Mouse Study para interação) |
| Indicators\Market Replay.mq5 | Cria os controles para interação com o serviço de replay/simulador (É necessário o Mouse Study para interação) |
| Indicators\Mouse Study.mq5 | Permite interação entre os controles gráficos e o usuário (Necessário tanto para operar o replay simulador, quanto no mercado real) |
| Services\Market Replay.mq5 | Cria e mantem o serviço de replay e simulação de mercado (Arquivo principal de todo o sistema) |
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Redes neurais em trading: Rede neural espaço-temporal (STNN)
MetaTrader 5 no macOS
Do básico ao intermediário: Template e Typename (II)
Do básico ao intermediário: Template e Typename (I)
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso