Discussão do artigo "Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXI): classes de negociação - objeto básico de negociação multiplataforma"

 

Novo artigo Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXI): classes de negociação - objeto básico de negociação multiplataforma foi publicado:

Neste artigo, iniciaremos uma nova seção da biblioteca, nomeadamente as classes de negociação, e consideraremos a criação de um único objeto básico de negociação para as plataformas MetaTrader 5 e MetaTrader 4. Tal objeto de negociação implicará que, ao enviar uma consulta ao servidor, para ele terão sido enviados os parâmetros da solicitação de negociação já verificados e corretos.

É muito bom ter muitos dados diversos e obter acesso fácil a eles a qualquer momento. Mas a disponibilidade desses dados não fará muito sentido se não pudermos reagir a eles de acordo com o propósito a que se destinam, que é negociar. Certamente, juntamente com a funcionalidade existente, também precisamos de recursos de negociação.
Esta seção será extensa e faremos tudo passo a passo.

  • Precisamos poder enviar quaisquer ordens de negociação a partir de qualquer plataforma (MetaTrader 5 ou MetaTrader 4). Ao mesmo tempo, sem pensar com qual plataforma as estamos enviando, pois será o mesmo.
  • Precisamos primeiro verificar se as solicitações de negociação são corretas, para não carregar o servidor com consultas que são conhecidas por serem erradas.
  • Precisamos considerar e processar corretamente os códigos de retorno do servidor de negociação. Afinal, o que faz um EA quando envia uma ordem ao servidor? Ele dialoga com o servidor na forma de consultas-respostas. Para que o EA possa se comunicar com o servidor, nossa tarefa é prover corretamente esse "canal de comunicação", isto é, criar métodos de processamento de respostas do servidor de negociação.
  • Precisamos criar vários métodos de processamento de respostas de servidor, afinal, às vezes, necessitamos abrir posições "de preferência a qualquer custo". Para fazer isso, é necessário fornecer o reenvio da ordem ao servidor se, quando colocada, ela for recusada, nesse caso, podemos quer ajustar os parâmetros da ordem de negociação e reenviá-la, quer deixar todos os parâmetros inalterados aguardando o momento adequado em que a ordem com esses parâmetros seja aceite e imediatamente enviá-la. Além disso, temos que considerar o nível de preços, para não reenviar a ordem ao preço que conhecido por ser o pior.
    Às vezes, porém, precisamos apenas enviar a ordem de negociação e, independentemente do resultado da solicitação, continuar trabalhando.
  • Precisamos trabalhar com classes de negociação de forma que, ao colocar um programa criado com base na biblioteca, no Mercado mql5, não haja problemas, uma vez que tal programa deve passar todas as verificações sem quaisquer dificuldades.
Por enquanto, temos esse pequenos planos em relação às classes de negociação.

Autor: Artyom Trishkin

 

Li mais da metade do texto com muita atenção. Mais adiante, minha consciência começou a se desligar. O volume do artigo é extremamente grande.

Anatoly Kozharsky sempre apresentava um esquema geral da biblioteca no final de seus artigos, por meio do qual era possível ter uma ideia clara de sua estrutura. Não vi esse esquema em seus artigos e, portanto, me sinto como uma "formiga" rastejando no corpo de um "elefante". Estou construindo uma ideia concisa da biblioteca, mas ao ler seus artigos é difícil inventá-la. Há muitas informações "mastigadas" na forma de detalhes, explicações de código, referências a outros artigos, mas não há uma descrição integral e concisa e um esquema da biblioteca.

É claro que o nível de implementação é muito alto. Tudo é muito profissional, e isso fica evidente. Porém, eu questionaria a necessidade de tantos "wrappers". Por exemplo: O método " CTradeObj::SetOrder" tem os seguintes wrappers:

//--- Define a ordem pendente (1) BuyStop, (2) BuyLimit, (3) BuyStopLimit
   bool                 PlaceBuyStop(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceBuyLimit(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceBuyStopLimit(const double volume,
                                     const string symbol,
                                     const double price_stop,
                                     const double price_limit,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- Define a ordem pendente (1) SellStop, (2) SellLimit, (3) SellStopLimit
   bool                 PlaceSellStop(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceSellLimit(const double volume,
                                     const string symbol,
                                     const double price,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
   bool                 PlaceSellStopLimit(const double volume,
                                     const string symbol,
                                     const double price_stop,
                                     const double price_limit,
                                     const double sl=0,
                                     const double tp=0,
                                     const ulong magic=ULONG_MAX,
                                     const string comment=NULL,
                                     const datetime expiration=0,
                                     const ENUM_ORDER_TYPE_TIME type_time=ORDER_TIME_GTC);
//--- 
                        

Todos eles não são muito diferentes, mas ocupam muito espaço. Com esse "envolvimento profundo", o material e os códigos crescerão como uma "levedura" e o uso da funcionalidade se tornará mais complicado proporcionalmente ao crescimento do número de entidades, que se multiplicará exponencialmente.

Não, não sou contra isso. Acho que é disso que se trata o polimorfismo- quando um método gera muitos invólucros, cada um com sua própria especificidade. O problema é que a estrutura da solução com essa abordagem cresce muito rápido e deixa de ser útil na prática. Precisamos limitar o crescimento do material, caso contrário, ele se tornará cada vez mais difícil de entender e usar.

 
Реter Konow:

Li mais da metade do texto com muita atenção. Mais adiante, minha consciência começou a se desligar. O volume do artigo é extremamente grande.

Anatoly Kozharsky sempre apresentava um esquema geral da biblioteca no final de seus artigos, por meio do qual era possível ter uma ideia clara de sua estrutura. Não vi esse esquema em seus artigos e, portanto, sinto-me como uma "formiga" rastejando no corpo de um "elefante". Estou construindo uma ideia concisa da biblioteca, mas ao ler seus artigos é difícil inventá-la. Há muitas informações "mastigadas" na forma de detalhes, explicações de código, referências a outros artigos, mas não há uma descrição e um esquema completos e concisos da biblioteca.

Obviamente, o nível de implementação é muito alto. Tudo é muito profissional, e isso fica evidente. Porém, eu questionaria a necessidade de tantos "wrappers". Por exemplo: O método " CTradeObj::SetOrder" tem os seguintes wrappers:

Todos eles não são muito diferentes, mas ocupam muito espaço. Com esse tipo de "envolvimento profundo", o material e os códigos crescerão como uma "levedura", e o uso da funcionalidade se tornará mais complicado proporcionalmente ao crescimento do número de entidades, que se multiplicará exponencialmente.

Não, não sou contra isso. Acho que é disso que se trata o polimorfismo- quando um método gera muitos invólucros, cada um com sua própria especificidade. O problema é que a estrutura da solução com essa abordagem cresce muito rápido e deixa de ser útil na prática. Precisamos limitar o crescimento do material, caso contrário, ele se tornará cada vez mais difícil de entender e usar.

Obrigado por sua avaliação, Peter. Você deve usar somente o que está disponível no CEngine, que, por sua vez, está disponível no programa.

Todo o resto é a implementação de métodos para os quais todos os parâmetros necessários são passados. Esses métodos não são necessários para uso prático em seus próprios programas - eles são necessários para a própria biblioteca. Portanto, para o usuário final, nem sempre há tantos métodos, e isso é mostrado nos consultores de teste.

Naturalmente, tudo será descrito. E a estrutura e todos os métodos disponíveis para o usuário. E as funções de usuário serão criadas, ocultando a implementação de OOP e não exigindo o uso de acesso explícito aos objetos da biblioteca.

Mas tudo está à nossa frente - precisamos compor a funcionalidade principal, que pode ser descrita tanto estrutural quanto tabularmente, de modo que a descrição seja completa e não exija modificações.

A cada artigo subsequente, a diferença de métodos aumentará drasticamente. Este artigo é apenas o início do trabalho nas classes comerciais. E, por favor, dê uma olhada em OrderSend() para cada tipo de ordem a ser enviada - quais parâmetros são necessários. Todos esses parâmetros são passados para os métodos. É claro que seria possível passar parâmetros por meio de estruturas descritas, como nos indicadores, mas isso seria desnecessário.
E você poderia fazer com que o usuário preenchesse as estruturas necessárias, mas é mais fácil e mais amigável passar os parâmetros diretamente para o método do que preencher a estrutura antecipadamente e depois passá-la para o método. Isso é feito apenas para a conveniência de uso pelo usuário final, não para a conveniência de analisar os componentes da biblioteca.
 
Artyom Trishkin:

Obrigado por sua avaliação, Peter. É necessário usar apenas o que está disponível no CEngine, que, por sua vez, está disponível no programa.

Todo o resto é a implementação de métodos para os quais todos os parâmetros necessários são passados. Esses métodos não são necessários para uso prático em seus próprios programas - eles são necessários para a própria biblioteca. Portanto, para o usuário final, nem sempre há tantos métodos, e isso é mostrado nos consultores de teste.

Naturalmente, tudo será descrito. E a estrutura e todos os métodos disponíveis para o usuário. E as funções do usuário serão criadas, ocultando a implementação de OOP e não exigindo o uso de acesso explícito aos objetos da biblioteca.

Mas tudo está à nossa frente - precisamos compor a funcionalidade principal, que pode ser descrita tanto estrutural quanto tabularmente, de modo que a descrição seja completa e não exija modificações.

A cada artigo subsequente, a diferença nos métodos aumentará drasticamente. Este artigo é apenas o início do trabalho com classes comerciais.

Para não ser infundado, eu sugeriria a seguinte medida para limitar o "embrulho":

Reescreva o método " CTradeObj::SetOrder" de modo que ele receba um parâmetro adicional - o tipo de ordem a ser definido. Além disso, o próprio método determinará como fazer isso. Ele inicializará a estrutura necessária, definirá os valores necessários e chamará os métodos necessários. Assim, você compactará todos os seus wrappers em um parâmetro adicional enviado ao método " CTradeObj::SetOrder". Ele receberá PlaceBuyStop, PlaceSellStopLimit ou PlaceBuyLimit como parâmetro e, em seguida, fará o trabalho necessário.

Obviamente, essa opção é mais complicada de implementar, mas você garantirá a "compressão" da funcionalidade da biblioteca e a facilidade de uso.

Boa sorte.

 
Реter Konow:

Para não ser infundado, eu sugeriria a seguinte medida para limitar o "envolvimento":

O método " CTradeObj::SetOrder" deve ser reescrito de modo que receba um parâmetro adicional - o tipo de ordem a ser definido. Além disso, o próprio método determinará como fazer isso. Ele inicializará a estrutura necessária, definirá os valores necessários e chamará os métodos necessários. Assim, você compactará todos os seus wrappers em um parâmetro adicional enviado ao método " CTradeObj::SetOrder". Ele receberá PlaceBuyStop, PlaceSellStopLimit ou PlaceBuyLimit como parâmetro e, em seguida, fará o trabalho necessário.

Obviamente, essa opção é mais complicada de implementar, mas você garantirá a "compressão" da funcionalidade da biblioteca e a facilidade de uso.

Boa sorte.

Esse método não é necessário para o usuário da biblioteca. Ele é necessário para a própria biblioteca.

Peter. Tente abrir uma posição definindo apenas seu tipo. Diretamente no terminal. Depois, veja o que mais é exigido de você. O terminal não decidirá por você em que símbolo abrir a posição, com que lote abri-la, que níveis de stop-orders definir, etc.

 
Artyom Trishkin:

...

E, por favor, dê uma olhada em OrderSend() para cada tipo de pedido enviado - quais parâmetros são necessários. Todos esses parâmetros são passados para os métodos. É claro que seria possível passar parâmetros por meio de estruturas descritas, como nos indicadores, mas isso seria desnecessário.

E você poderia fazer com que o usuário preenchesse as estruturas necessárias, mas é mais fácil e mais amigável passar os parâmetros diretamente para o método do que preencher a estrutura antecipadamente e depois passá-la para o método. Isso é feito para a conveniência do usuário final, não para a conveniência de analisar os componentes da biblioteca.
Do meu ponto de vista, a opção mais amigável seria apenas um wrapper para OrderSend(). Esse wrapper aceitaria os parâmetros do usuário e decidiria como formatar a chamada. Eu faria dessa forma. Mas isso fica a seu critério.
 
Реter Konow:
Do meu ponto de vista, a opção mais amigável seria apenas um wrapper para OrderSend(). Esse wrapper aceitaria os parâmetros do usuário e decidiria como formatar a chamada. Eu faria dessa forma. Mas você sabe melhor.

Eu faço dessa forma - os métodos aceitam parâmetros do usuário.

 
Artyom Trishkin:

Eu o faço desta forma: os métodos aceitam parâmetros do usuário.

Sim, eu entendo. Essa é uma implementação "multicamadas" do processamento de pedidos. O problema está na "espessura" dos invólucros. Eu confundi quais métodos se referem a mecanismos e quais métodos se referem à interface de usuário da biblioteca. Desculpe-me. Se houvesse menos invólucros, seria mais fácil de entender, mas a escolha é sua.
 
Реter Konow:
Sim, entendi. Essa é uma implementação de "várias camadas" do processamento de pedidos. O problema está na "espessura" dos invólucros. Eu confundi quais métodos se referem a mecanismos e quais métodos se referem à interface de usuário da biblioteca. Desculpe-me. Se houvesse menos invólucros, seria mais fácil de entender, mas a escolha é sua.

Veja, Peter, eu tento fornecer não apenas um método de trabalho, mas muitas possibilidades.

Neste artigo, o acesso de baixo nível às funções de negociação é organizado. Tudo o que o objeto de negociação faz é organizar corretamente os parâmetros recebidos na função de envio de uma ordem para o servidor. Para enviar uma ordem de negociação, é necessário acessar a coleção de símbolos, obter um objeto do símbolo desejado e, a partir desse objeto, obter um objeto de negociação. Em seguida, trabalhar com o objeto de negociação. Aqui, facilitei para o usuário o preenchimento da ordem de negociação - basta passar os parâmetros necessários. Mas esses parâmetros devem ser verificados independentemente quanto à exatidão, pois o objeto de negociação considera que todos os parâmetros passados a ele já estão corretos.

Isso não é conveniente. Mas existe esse acesso. Quem precisar dele verificará tudo por conta própria e enviará uma ordem de negociação. Não há processamento de resposta do servidor. Essa é a primeira coisa que foi feita.

O próximo artigo descreverá uma classe de negociação, que terá o mesmo conjunto de métodos de negociação com os mesmos parâmetros, mas agora tudo será verificado quanto à exatidão e retornado ao programa se os parâmetros não estiverem corretos (ou um deles, ou se não houver possibilidade de negociação). Esse é o segundo estágio - já existem duas formas aqui - a primeira, que já existe, e a segunda - quando a própria biblioteca verifica a exatidão dos parâmetros e, se todos estiverem corretos, a ordem é enviada e, se pelo menos um deles não estiver correto ou não houver possibilidade de negociação, ela simplesmente retorna ao programa de controle. Aqui será organizado um acesso de nível superior com verificações de correção.

Mas isso não é tudo. Precisamos reagir aos erros. E no próximo artigo iremos ainda mais longe - processaremos os erros recebidos nos parâmetros das ordens de negociação (mesmo antes de enviá-las ao servidor). Afinal de contas, você pode se recusar a abrir uma posição em caso de erro e pode corrigir os parâmetros e enviar a solicitação novamente. Esse já é o terceiro nível de trabalho com objetos de negociação.

E então haverá uma abordagem de nível ainda mais alto - tudo funcionará automaticamente de acordo com as configurações necessárias. E chegaremos às solicitações diferidas - quando o Expert Advisor aguardará o momento certo para enviar a ordem necessária novamente ou simplesmente saberá em que situação precisa enviar uma solicitação. Todas essas solicitações pendentes estarão em seu cofrinho, aguardando o momento certo para enviar a solicitação.

Portanto, Peter, é prematuro julgar qualquer coisa ainda.

Você sugeriu fazer um único acesso às funções de negociação, mas eu quero oferecer uma variedade de métodos - para todos, de acordo com suas necessidades. Embora seja impossível agradar a todos, é claro.

 

Artyom Trishkin:

...

Gostei de sua explicação e, por causa disso, surgiu uma contradição interna.

1. Por um lado, dar aos usuários acesso a todos os níveis do mecanismo da biblioteca por meio da construção de uma estrutura de "invólucro" em camadas, em que, em vez de algoritmos comprimidos, há relações expandidas, e a "estrutura" algorítmica é dimensionada para facilitar a percepção, proporcionando uma compreensão confortável da estrutura das soluções. É bonito, estético e elegante. É claro que se trata de uma questão de design e facilidade de uso. Mais precisamente, a facilidade para os pesquisadores, pois os usuários se preocupam mais com os resultados e a eficiência. Agora vamos analisar a solução de uma perspectiva diferente.

2- A eficiência implica em concisão, um conjunto mínimo de entidades e invólucros, com o acesso mais curto possível aos dados. Tudo deve estar próximo, de modo a não "esticar" e não transferir uma "carga" de parâmetros de um wrapper para outro. Eficiência e um estilo implantado, em camadas e elegante, com "vagar" entre "esquadrões" de parâmetros de wrappers são coisas estranhas e quase incongruentes.

//-----------------------------------

Em vista dessa contradição, eu sugeriria um meio-termo. Limitar parcialmente o crescimento do número de wrappers aumentando a concisão das soluções. Em princípio, um wrapper é suficiente para a função OrderSend(). Em um caso extremo, vários. Mas, nesse caso, a solução será difícil de ser entendida pelos "pesquisadores", e o acesso às "camadas" se tornará mais complicado. Portanto, tente combinar blocos compactados e eficientes e "vinhas" de OOP-jungle, tão apreciadas pelos programadores. E você encontrará o meio-termo.

 
Artyom Trishkin:

...

E então haverá uma abordagem de nível ainda mais alto - tudo funcionará no modo automático de acordo com as configurações necessárias. E chegaremos às solicitações diferidas - quando o Expert Advisor aguardará o momento certo para enviar a ordem necessária novamente ou simplesmente saberá em que situação precisa enviar uma solicitação. Todas essas solicitações pendentes estarão em seu cofrinho e estarão aguardando o momento certo para enviar a solicitação.

Portanto, Peter, é prematuro julgar qualquer coisa.

Você sugeriu fazer um único acesso às funções de negociação, mas eu quero oferecer uma variedade de métodos - para todos, de acordo com suas necessidades. Embora seja impossível agradar a todos, é claro.

Dividir a biblioteca em níveis e permitir que os usuários trabalhem neles é muito bom. Mas não se esqueça de que a quantidade pode acabar com a qualidade. Quanto mais métodos um usuário tiver, maior será a probabilidade de ele se confundir. Isso é algo a se ter em mente.

Fora isso, eu concordo. Uma abordagem em camadas para as funções de negociação é uma coisa boa.