Desenvolvendo um sistema de Replay (Parte 78): Um novo Chart Trade (V)
Introdução
No artigo anterior Desenvolvendo um sistema de Replay (Parte 77): Um novo Chart Trade (IV), expliquei da maneira o mais detalhada possível. E tentando não complicar, o que em muitos casos é bastante complicado. Como você faz, para desenvolver um protocolo de comunicação. Isto para que você possa transferir informações, entre aplicações diferentes, ou programas distintos. Estando estes no mesmo ambiente ou não. No caso específico, estamos querendo fazer, com que o indicador Chart Trade, consiga dizer ao Expert Advisor, o que ele deverá fazer. Ou seja, quando o usuário, disser ao Chart Trade, que deseja vender, o Expert Advisor venderá. Quando o usuário disser que deseja comprar, o Expert Advisor fará a compra a mercado.
Então, nestes últimos artigos, fiquei basicamente focado, em apresentar os motivos pelo qual, deveríamos criar o indicador Chart Trade. Mas principalmente como preparar um protocolo de mensagens. Antes mesmo de tentar codificar qualquer outra coisa. Porém, até o momento, estivemos focados apenas e somente na parte do indicador.
Entretanto, a explicação não estará completa, sem que antes você venha a conhecer a parte do receptor. Sendo que esta parte se encontra de fato presente, no Expert Advisor. Isto por conta que qualquer indicador, é proibido, pelo MetaTrader 5, de enviar ordens. Manipular posições ou fazer qualquer coisa, que esteja diretamente relacionada ao sistema, responsável por comunicar ao servidor de negociação, que algo deverá ocorrer. Isto considerando, que este algo seria, a abertura, fechamento ou mesmo mudança na composição de uma posição ou ordem.
Se bem, que aqui existe algo que alguns poderiam dizer se tratar de uma brecha. Mas que na verdade, se trata de uma outra coisa, que iremos de fato explorar futuramente. Isto para conseguir desenvolver uma outra ferramenta, que iremos de fato precisar, e muito, que ela seja construída. Mas isto será assunto para alguns artigos mais para frente. Já que ainda, estamos nas primeiras etapas do sistema de ordens.
No momento, a nossa preocupação é outra. No caso, a principal preocupação, é tentar entender, a fundo como o Expert Advisor, consegue saber o que está acontecendo. Isto por que o usuário, não irá de forma alguma interagir, diretamente com o Expert Advisor. Ou seja, o usuário, irá realmente interagir com o indicador Chart Trade, a fim de dizer ao Expert Advisor o que deverá ser feito. Pois nada adianta termos o indicador Chart Trade, se o responsável por enviar os pedidos ao mercado, seja para abrir, ou fechar uma posição, não consegue saber o que se passa. Lembre-se de quem realmente tem a permissão de fazer isto é o Expert Advisor. Nenhum outro programa fará isto no MetaTrader 5.
Então neste artigo, o foco principal, será:
Como o Expert Advisor consegue entender o Chart Trade?
Já vamos começar este tópico, já com esta questão em mente. Então se você não faz ideia, de como isto de fato acontece. Aconselho a você, primeiramente entender o artigo anterior. Onde explico como desenhar um protocolo de mensagens. Pois aqui vamos ver como fazer a mágica funcionar. Isto por que nem o indicador Chart Trade, nem o Expert Advisor, sabem da existência um do outro. E nem precisam saber, basta que ambos confiem que a mensagem será entregue e que ela será compreendida.
No entanto, mesmo sem saber que existe um Expert Advisor no gráfico. O indicador Chart Trade, consegue fazer com que o Expert Advisor, venha a saber o que o usuário está desejando fazer. Este tipo de coisa é bastante interessante de ser vista.
Mas principalmente, se você conseguir entender isto, irá de fato conseguir fazer com que seus programas, ou aplicações que serão executadas no MetaTrader 5, venham a ser bem mais versáteis. Além disto, este conhecimento que estou aqui lhe mostrando, vai muito além do MetaTrader 5.
Sim, meu caro leitor. Este tipo de coisa, que é a troca de mensagens entre programas, ou processos, também é usado em sistemas operacionais. Tais como Windows, Linux e MacOS. Todos, e absolutamente todos os sistemas modernos, se baseiam nesta premissa, onde você desenvolve diversos programas mais simples, porém capazes de se comunicar entre si. Com isto você consegue criar um ecossistema, bastante amplo e sustentável.
Isto por que, cada uma das aplicações, pode em essência, ser otimizada para efetuar uma dada tarefa. Mas quando colocadas em um conjunto, elas conseguirão executar qualquer tipo de coisa imaginável. E com um custo bastante baixo, tanto para implementação, quanto para manutenção ou aperfeiçoamento.
Então a partir deste ponto, comece a esquecer, aquela coisa de criar programas, ou aplicações complexas e cheias de coisas que precisaram, ou que venham a mudar com o tempo. Comece a pensar em criar programas mais simples. Porém esta simplicidade sempre vem acompanhada de uma maior agilidade, em melhorar a própria aplicação. Sei que para muitos isto pode ser novidade. Mas acreditem, grande parte da indústria, realmente trabalha assim. Ninguém de fato, cria algo inteiriço, pois fazer isto, além de ser bastante caro e custoso, é muito dispendioso em termos gerais. Principalmente no momento de fazer alguma modificação, ou elaborar e implementar alguma uma melhoria geral no sistema.
Se a aplicação for mais simples, será muito mais fácil resolver qualquer problema. E mesmo fazer alguns ajustes, ou otimizar a forma como ela estará, sendo executada. Por conta disto, o Expert Advisor, será programado, de forma a fazer apenas o que o MetaTrader 5, exige que apenas ele faça. Ou seja, enviar e modificar ordens e posições. Todo, o restante, será executado por outros tipos de programas e aplicações.
Assim sendo, para de fato compreender, como o Expert Advisor, consegue entender o Chart Trade. Vamos primeiramente, montar um código bem simples. Acreditem ele será bem simples mesmo. Não faremos, neste primeiro momento, nenhum envio de ordens. Fechamento de posição, ou virada de mão. Novamente, não faremos isto ainda. Pois isto iria de fato apenas complicar as coisas e a explicação em si.
Precisamos de algo que seja, o mais simples possível, para que você, caro leitor e aspirante a programador profissional, consiga entender como a parte do receptor funciona. Assim você conseguirá ter um maior entendimento, de como o protocolo de mensagens, consegue fazer com que o indicador Chart Trade, que não sabe da existência do Expert Advisor, consiga fazer com que este, faça alguma coisa.
Ao mesmo tempo o Expert Advisor, que desconhece totalmente a existência do Chart Trade, consegue responder ao um pedido do usuário. Lembrando que o usuário não irá em momento algum, interagir diretamente como o Expert Advisor.
Então o código mais simples para fazer isto, pode ser visto logo abaixo. Este código se encontra na íntegra.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Demo version between interaction" 04. #property description "of Chart Trade and Expert Advisor" 05. #property version "1.78" 06. #property link "https://www.mql5.com/pt/articles/11760" 07. //+------------------------------------------------------------------+ 08. #include <Market Replay\Defines.mqh> 09. //+------------------------------------------------------------------+ 10. class C_Decode 11. { 12. private : 13. struct stInfoEvent 14. { 15. EnumEvents ev; 16. string szSymbol; 17. bool IsDayTrade; 18. ushort Leverange; 19. double PointsTake, 20. PointsStop; 21. }info[1]; 22. public : 23. //+------------------------------------------------------------------+ 24. C_Decode() 25. { 26. info[0].szSymbol = _Symbol; 27. } 28. //+------------------------------------------------------------------+ 29. bool Decode(const int id, const string sparam) 30. { 31. string Res[]; 32. 33. if (StringSplit(sparam, '?', Res) != 6) return false; 34. stInfoEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], (bool)(Res[2] == "D"), (ushort) StringToInteger(Res[3]), StringToDouble(Res[4]), StringToDouble(Res[5])}; 35. if ((id == loc.ev) && (loc.szSymbol == info[0].szSymbol)) info[0] = loc; 36. 37. ArrayPrint(info, 2); 38. 39. return true; 40. } 41. //+------------------------------------------------------------------+ 42. }*GL_Decode; 43. //+------------------------------------------------------------------+ 44. int OnInit() 45. { 46. GL_Decode = new C_Decode; 47. 48. return INIT_SUCCEEDED; 49. } 50. //+------------------------------------------------------------------+ 51. void OnTick() {} 52. //+------------------------------------------------------------------+ 53. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 54. { 55. switch (id) 56. { 57. case CHARTEVENT_CUSTOM + evChartTradeBuy : 58. case CHARTEVENT_CUSTOM + evChartTradeSell : 59. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 60. (*GL_Decode).Decode(id - CHARTEVENT_CUSTOM, sparam); 61. break; 62. } 63. } 64. //+------------------------------------------------------------------+ 65. void OnDeinit(const int reason) 66. { 67. delete GL_Decode; 68. } 69. //+------------------------------------------------------------------+
Código fonte do Expert Advisor
Caramba, você está me dizendo que este código acima é simples? Cara. Não estou te entendendo. Este código para mim parece muito complicado. Não estou conseguindo entender praticamente nada dele. Estou aqui a ver navios. Se este código é simples, imagina então algo complicado? Sim, este código realmente é bastante simples. Porém ele está fazendo uso, de algumas coisas que muitos de vocês não usam normalmente. Ou melhor, muitos se quer sabem que é possível usar no MQL5. Então se este é o seu caso, e você de fato, pretende se tornar um programador profissional no futuro. Acompanhe a explicação que irei de fato dar, pois as coisas aqui são bem mais simples, do que possa parecer. E o código é bastante direto e objetivo naquilo que ele tem que fazer.
Mas este conhecimento que irei lhe repassar, pode lhe ajudar a pensar as coisas com mais calma. Além é claro, ele serve como uma forma de se divertir enquanto programa. Pois programar é uma diversão. Se você vê isto como um trabalho chato e sem graça. Difícil e complicado, sugiro que você comece a pensar em fazer outra coisa da vida. E esqueça definitivamente a área de programação e desenvolvimento. Pois quando se está programando ou desenvolvendo algo, você realmente deve se sentir, como uma criança na loja de doces. Nunca sabe exatamente qual o doce, que irá de fato pedir primeiro.
Mas chega de devaneios, e vamos ver como este código de fato funciona. As sete primeiras linhas, acredito não ser problema para ninguém entender, mesmo quem esteja começando, não terá dificuldades em entendê-las. Já a linha oito, é uma velha conhecida de todos nos. Aqui estamos adicionando, ou melhor dizendo, incluído um arquivo ao nosso código. Este arquivo é um arquivo de cabeçalho, onde se encontra as definições que precisaremos em breve. Mas vamos pular, neste primeiro momento, o código que se encontra entre as linhas 10 e 42. Isto por que este código será explicando com mais calma, um pouco mais a frente. Primeiro precisamos entender, alguns pequenos, porém importantes detalhes.
Com isto, vamos a primeira função realmente necessária ao nosso código. Ela é a OnInit, que se inicia na linha 44. Por conta da linha 42, as coisas podem funcionar de uma forma ligeiramente diferente, do que muitos imaginam. Na linha 46, fazemos uso do operador NEW para alocar memória e inicializar a classe C_Decode. Mas por que alocar memória para isto? Não seria mais simples, deixar o compilador fazer isto para nos? Sim. De fato seria mais simples deixar o compilador fazer isto. Porém você deve se acostumar, a controlar quando e até onde uma classe será utilizada. Se você deixar o compilador fazer isto, poderá em alguns momentos, usar a classe com valores diferentes do que você esperava encontrar.
Acredite, não é raro, você usar uma classe com valores diferentes, ainda mais quando o código é bastante extenso e a classe declarada em diversos pontos distintos. Mais hora ou menos hora, você acaba se enrolando. Mas, fazendo uso do conjunto de operadores NEW - DELETE, você com toda a certeza definirá onde começa e onde termina o seu código. Muitos programadores iniciantes não conseguem definir isto. Por mais estranho que possa parecer. Eles não sabem dizer onde o código termina. Quase sempre muitos não sabem onde ele se quer começa. É estranho, mas isto de fato existe.
Então quando a linha 46 for executada, ela irá de fato alocar memória suficiente para conter a classe C_Decode. Ao mesmo tempo chamará a linha 24. Esta linha é o constructor da classe C_Decode. Note que neste constructor, temos a inicialização de apenas e tão somente uma variável. Esta manterá o nome do símbolo, ou melhor dizendo, o nome do ativo onde o Expert Advisor se encontra. Agora preste bastante atenção ao seguinte: Neste modelo, NÃO TEREMOS PERMISSÃO DE CROSS ORDER. Neste momento, acredito que isto não está realmente claro para você. Mas continuem lendo o artigo para entender por que, não teremos permissões de efetuar operações de ordens cruzadas. Porém com algumas pequenas modificações, e estas bem simples, poderemos sim, fazer uso de ordens cruzadas. Mas no momento, não se preocupe com isto. Lembre-se que este código, serve apenas para entender, como o protocolo de mensagens funciona. Ele não tem um propósito de realmente fazer nenhum pedido ao servidor de negociação.
Então voltemos a função OnInit. Depois de ter executado a linha 46, retornamos na linha 48, um valor que indica que a inicialização foi concluída com sucesso. É importante informar isto ao MetaTrader 5. Isto por que, se o valor que estivermos retornando for diferente de INIT_SUCCEEDED, o MetaTrader 5 irá automaticamente tomar medidas para desativar o Expert Advisor. Uma destas medidas é lançar um evento Deinit que chamará o procedimento OnDeinit no código do Expert Advisor.
Caso seu código não contenha este procedimento, o MetaTrader 5 tomará as medidas padrão. De qualquer forma, no final, mesmo que sua aplicação permaneça listada, ela não receberá tempo de processador. Ou seja, o MetaTrader 5, não fará o agendamento da sua aplicação para execução.
E todo e qualquer tipo de informação que estiver no gráfico, e que por ventura, pertencer a aplicação, deverá ser ignorado. Não é raro, que uma aplicação crie e coloque objetos no gráfico. Mas quando o MetaTrader 5, dispara o evento Deinit, o seu código não remove tais objetos e elementos do gráfico. Isto por que, muitos programadores, simplesmente ignoram o procedimento OnDeinit. Então nestes casos o MetaTrader 5, não poderá fazer nada mais. E os elementos e objetos ficam ali no gráfico. Dando muitas vezes falsas indicações ou informações inválidas.
Por conta disto é de boa prática, você sempre se preocupar em implementar o procedimento OnDeinit. Muitas das vezes ele fará muito pouco. Mas no nosso caso específico, ele contém apenas e somente uma única linha, a linha 67. Nesta fazemos uso do operador DELETE a fim de liberar a memória que foi alocada. Neste momento o destructor da classe C_Decode será chamado. Como a nossa classe não tem, explicitamente um destructor declarado. O compilador vendo o uso do operador DELETE na classe, providenciará um destructor implícito. Isto por que o operador DELETE realmente necessita de um destructor. Mas você não precisa se preocupar com isto. Apenas estou explicando este fato, para que você saiba como realmente as coisas funcionam.
Muito bem, agora que já vimos, onde nosso código do Expert Advisor se inicia e onde ele termina. Podemos passar para os demais pontos. Na linha 51, temos o procedimento que será chamado a cada tick que o ativo receber. Este procedimento é obrigatório em todo e qualquer Expert Advisor. Mas, você deve evitar fazer uso, ou melhor dizendo, colocar código dentro deste procedimento. Os motivos disto podem ser visto na sequência de artigos sobre como criar um Expert Advisor que opera de forma 100% automática. Se desejar criar um Expert Advisor, com algum nível de automação, sugiro que você olhe esta sequência.
São ao todo 15 artigos, onde o primeiro pode ser visto aqui Aprendendo a construindo um Expert Advisor que opera de forma automática (Parte 01): Conceitos e estruturas. Realmente ler tal sequência, irá lhe ajudar muito. Mas aqui nesta sequência do sistema de replay/simulação, usaremos algumas coisas que foram mostradas lá na sequência sobre Expert Advisor automático. Mas de qualquer forma, não deixe de dar uma olhada na sequência que mencionei.
Sendo assim, podemos ver o outro procedimento, que se encontra na linha 53. Trata-se do tratador de eventos OnChartEvent. Agora que a coisa de fato fica interessante. Mas para separar as coisas, vamos ver isto em um novo tópico.
Como decodificar uma mensagem, vinda em um evento
Em artigos passados, nesta mesma sequência sobre replay/simulador. Expliquei como EventChartCustom e OnChartEvent se interligam. Isto foi intensamente utilizado, para fazer com que o Serviço de replay/simulador, conseguisse repassar dados ao Indicador de controle. Isto para que o indicador de controle pudesse saber, em que ponto da simulação ou replay estávamos. Se você não viu ou não leu os artigos a este respeito. Isto por estar chegando nesta sequência, justamente neste artigo daqui. Pode conferir e entender como foi feito para que o serviço, conseguisse enviar dados para o indicador de controle, olhando os artigos sobre como dar play no serviço.
Um destes artigos é Desenvolvendo um sistema de Replay (Parte 60): Dando play no serviço (I), são sete os artigos sobre isto. Porém você deve ler os demais artigos também, pois irá lhe ajudar a entender os detalhes envolvidos. Mas principalmente, por que fazer as coisas assim.
Muito bem, lá na comunicação entre o serviço e o indicador de controle, a comunicação estava se dando via parâmetros numéricos. Ok, aquilo era a parte fácil do sistema. Já que a informação está disponível diretamente nos parâmetros. Fosse ele o lparam, ou o dparam. Mas aqui a coisa muda de figura. Neste ponto, vamos usar o valor que estará no parâmetro sparam. Ou seja, a mensagem agora será uma string. E dentro desta mesma string, é que se encontra o que precisamos. Só que a informação está de alguma forma codificada. A forma como esta codificação é feita, é chamada de protocolo de mensagem. No artigo anterior, expliquei sobre isto. Aqui vamos entender como decodificar, aquela mesma mensagem que se encontra dentro do protocolo.
Você deve se lembrar, que no artigo anterior, falhei que a mensagem iria de fato conter o evento. Este tipo de coisa, servirá para que possamos testar a mensagem aqui no receptor. Ou seja, o Expert Advisor, conseguirá verificar, se a mensagem realmente não foi corrompida de alguma forma.
Já que a mensagem poderá ser decodificada, usando o mesmo protocolo. Isto entre os três tipos diferentes. Podemos fazer o que é visto entre as linhas 57 e 59. Observem que ali, temos os três eventos reconhecidos, e que o Expert Advisor, deverá interceptar. Já que todos os três, poderão passar pelo mesmo crivo de decodificação, a única linha realmente usada é a linha 60. Esta linha, irá de fato chamar, o código que se encontra na linha 29. Agora muita, mas muita atenção ao que vou explicar. Se você não conseguir entender a explicação, leia novamente pois é algo muito importante, apesar de ser muito pouco comum.
Observe que na linha 31, declaramos uma variável local, que é um array dinâmico. Sendo dinâmico, o próprio programa, fica responsável por alocar memória para ele. Não precisamos nos preocupar com ele então. Agora atenção ao seguinte fato: Na linha 33, executamos uma chamada a função StringSplit. Esta tem como finalidade, preencher o array dinâmico, com strings que estão separadas por um determinado carácter. No caso é o carácter de interrogação. Mas por que de ser este carácter?
O motivo é visto no artigo passado. Observe que no código visto lá, o carácter de interrogação é usado para separar os conjuntos. Pois bem, durante esta separação, é esperado que exista, um determinado número de strings. Se a mensagem estiver correta, ou minimamente correta, o número de strings será de seis. Ou seja, se StringSplit reportar um número menor ou maior, o teste falhará e teremos um retorno falso. Se for reportado, as seis strings esperadas pela função, passaremos de fato, para a próxima linha.
Agora muita atenção a esta linha 34. Esta linha não é algo muito comum, mas também não é nenhum código alienígena. Para entender o que esta, de fato acontecendo, você deve ver esta linha 34, em conjunto com a estrutura declarada na linha 13. PRESTE MUITA ATENÇÃO. Cada elemento, presente na linha 34, tem um correspondente e está referenciando algo na estrutura stInfoEvent. O que está sendo feito, não é algo comum, mas fiz propositalmente para explicar uma coisa. Observe a ordem, que as variáveis estão sendo declaradas, na estrutura stInfoEvent. Esta observação é muito importante. Agora observe a ordem em que os valores estão sendo colocados na linha 34. Opa. Vejam que eles batem. Então, esta linha 34, seria o equivalente ao seguinte código:
stInfoEvent loc; loc.ev = (EnumEvents) StringToInteger(Res[0]); loc.szSymbol = Res[1]; loc.IsDayTrade = (Res[2] == "D"); loc.Leverange = (ushort) StringToInteger(Res[3]); loc.PointsTake = StringToDouble(Res[4]); loc.PointsStop = StringToDouble(Res[5]);
Fragmento de código
É exatamente a mesma coisa. Mas, porém, todavia e entretanto, existe um risco ao se usar o código como mostrado na linha 34. E este mesmo risco não ocorre, quando se usa o código visto no fragmento. Este risco é a mudança na estrutura stInfoEvent. Mas como assim? Não entendi, por que mudar a estrutura stInfoEvent, é um problema quando codificamos as coisas como visto na linha 34, mas não é quando codificamos como visto no fragmento.
O motivo é bem simples. Talvez seja até mesmo bobo e mesmo assim vai dar muitas dores de cabeça, e horas tentando entender por que as coisas estão dando errado. Vamos imaginar uma mudança bem simples. Você em algum momento, decide trocar a variável, que está declarada na linha 19 com a variável declarada na linha 20. Para um código feito como visto no fragmento, esta mudança não irá de fato afetar em nada as coisas.
Porém, para o código visto na linha 34, a coisa muda completamente de figura. Isto por que o valor que esperamos colocar na variável PointsTake, será colocado no PointsStop. E o valor esperado para ser colocado em PointStop, será colocado em PointsTake. Mas que maluquice é esta? Você está de brincadeira. Só pode. Não me caro leitor, isto não é nenhuma brincadeira, e tão pouco maluquice.
O motivo pelo qual isto acontece, tem tudo a ver com a forma como o compilador organiza as variáveis na memória. Este tipo de coisa, apesar de ajudar em diversos momentos, principalmente quando programamos em C legado. No caso do C legado, muitas vezes, queremos forçar o sistema, a executar certas coisas. Então fazemos uso de uma técnica muito parecida. Porém de forma proposital. Onde forçamos o programa a escrever em certas regiões da memória, e logo depois fazemos um pulo para esta região. Não entrarei em detalhes, de como fazer isto, pois isto foge completamente do escopo e da intenção deste artigo.
Mas se você souber o que está fazendo, pode fazer coisas, que de outra forma não seria possível ser feita. Então, somente use este modo de atribuição de valores a uma estrutura, quando a estrutura, já estiver completa e totalmente elaborada e fechada. E uma vez que ela esteja elaborada, você NÃO DEVERA MAIS MEXER NELA. Caso você venha a mexer, deverá reparar todos os pontos de atribuição, que se parecem com a linha 34. Para entender, exatamente este risco. Utilize este código para testar o sistema. Apenas mudando esta linha 34, ou mudando a ordem em que as variáveis estão declaradas na estrutura stInfoEvent.
Mas vamos continuar a explicação. Quando o código chega na linha 35, verificamos se as coisas correspondem, a algo esperado. Tanto o nome do ativo, quanto o tipo de evento. Se isto for verdadeiro, iremos de fato atribuir o valor a um array estático. ATENÇÃO: Esta atribuição somente ocorrerá nesta fase de teste e demonstração. Isto por que, precisamos que a estrutura stInfoEvent, esteja em um array. O motivo é a linha 37. Nesta linha fazemos uso de uma outra função do MQL5, a ArrayPrint. Esta função imprimirá o array para nos de forma que o resultado do conteúdo do array, será mostrado no terminal. O resultado de uma das execuções, pode ser visto na imagem, logo abaixo.
Conclusão
Por conta dos motivos que mostrei neste artigo. O Expert Advisor, não é capaz de enviar pedidos ainda para o servidor de negociação. Pois se você não entender, antes como o código funciona. Mas principalmente, não entender como o protocolo de comunicação, entre o Chart Trade e o Expert Advisor realmente funciona. Poderia correr o risco de acabar fazendo alguma bobagem. Mas mesmo assim, foi interessante deixar as coisas desta maneira. Pois aproveitei e acabei explicando algo, que você em algum momento, pode acabar se deparando e não entender por que funciona ou não funciona.
De qualquer maneira, já temos as premissas para o que será feito nos próximos artigos. No vídeo, abaixo você pode ver como o sistema se está se comportando. Então até o próximo artigo, pois as coisas serão ainda mais interessantes.
- 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