Projeto do assessor - página 4

 
Vitaly Muzichenko:

Eu também, mas há muito tempo cheguei à conclusão de que o código deveria ser compacto em lugares onde ele nunca é visto, nunca é corrigido e nunca será corrigido.

Espalhar o código do usuário com todos estes slots é uma dor de cabeça adicional, porque você precisará arrastar e soltar arquivos em diferentes terminais ou compartilhá-los. É claro que você pode transferir os incluídos para todos os terminais, mas se você mudar ou acrescentar algo em um terminal, então todos eles devem ser substituídos por um novo.

Os Conselheiros Especialistas e os indicadores são tão pequenos que não adianta afastá-los do corpo do programa. Para ser mais correto, eles não são pequenos, são um único arquivo, não é como um site com 10 000 páginas onde não se pode prescindir de classe e de ludes. Além disso, existem estruturas agora, e elas são suficientes para escrever códigos compactos e 100% viáveis.

Aqui vamos.... Você conhece os links simbólicos para pastas?http://skesov.ru/sozdanie-simvolnoy-ssyilki-dlya-papki/

Tenho todas as minhas bibliotecas em uma pasta, e em um monte de terminais, há dezenas delas, em mql****, e em pastas há links simbólicos para esta pasta real. Nada precisa ser arrastado para qualquer lugar.

Além disso, eu utilizo ativamente o armazenamento, se eu mantiver lá tudo o que é importante, posso baixá-lo para outro terminal em 5 segundos. Mas para as libs os vínculos simbólicos são mais convenientes, sempre com total sincronização.

Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
Создание символьной ссылки для папки в Windows 8.1, 8, 7, Vista
  • 2013.07.24
  • skesov.ru
Доброго времени суток! Сегодня рассмотрим интересную тему под названием «Символьные ссылки». Вариантов использования данного инструмента не так уж много. К примеру, если вы используете часть оперативной памяти как RAM-диск, можно перенести какую-либо игру или её часть (скажем папки с графикой) и создать символьную ссылку. Это значительно...
 
Alexey Navoykov:
Recomendo o uso de links simbólicos ou links de junção para a pasta MQL. Todos os terminais procurarão na mesma pasta.

E compartilhá-lo com outra pessoa?

 
Vitaly Muzichenko:

Compartilhar com alguém?

Bem, você tem que decidir o que é mais importante para você, verificar ou dirigir). A facilidade de compartilhar com alguém é mais importante para você do que o conforto da codificação? Se, digamos, você encontrou um erro em alguma função que é usada por muitos Expert Advisors, então você terá que entrar no código de cada um deles e reescrever esta função - isso não o incomoda como programador?

 
Obrigado a todos por discutirem minha pergunta.
Decidiu olhar para o OOP em mqlx para começar, manter suas funções (repetíveis) em um arquivo(s) separado(s). E não seja preguiçoso para comentar.

E outro + para o link simbólico no Windows! Eu o usei em linux mas o esqueci no Windows. Vou ter que tentar...
 
Alexey Navoykov:

Você tem que decidir o que é mais importante para você, verificar ou dirigir). A simplicidade do processo de compartilhamento com alguém é mais importante para você do que o conforto da codificação? Se, por exemplo, você encontrar um erro em uma função, que é usada por muitos Conselheiros Especialistas, então você terá que entrar no código de cada um deles e reescrever esta função - isso não o incomoda como programador?

Tive um caso assim apenas uma vez, há cerca de um mês, mas tive que acrescentar a verificação da abertura do mercado lá, há todas as verificações, exceto esta, e ela apareceu pela primeira vez desde que a utilizo.

Se algo precisa ser adicionado, eu o adiciono ao programa atual, e depois disso uso o arquivo como um modelo para o próximo programa. Como resultado, em poucos anos o modelo tem tudo, bem, ou quase tudo, de modo que um bot de qualquer complexidade pode ser escrito em meia hora.

Todo o código executável cabe em uma tela, embora o arquivo contenha um pouco mais de 4.000 linhas, mas eu olho para lá muito raramente, nem que seja apenas o que eu precise acrescentar. De funções em loops recusadas, utilizou apenas duas, uma em aberto coleta informações, a segunda na história, e tudo isso na estrutura na parte inferior do código. Tudo é muito simples e próximo um do outro. O código principal é comentado. O projeto se estende muito fácil e rapidamente, sem nenhuma perda.

 
Alexey Volchanskiy:

Parece bom, podemos ver também TRACE_**** e ASSERT?

Bem... Para o autor de uma aula magistral sobre sedução de mulheres, a quem invejo com uma inveja negra, você é bem-vindo.

A versão de depuração é ativada automaticamente em meu código, se a macro do sistema correspondente for definida. Entretanto, se não estiver habilitado, você também pode habilitar afirmações e rastrear por definições:

#define _FORCETRACE 1
#define _FORCEASSERT 1

Neste caso - independentemente das configurações do sistema - são gerados traços de depuração e afirmações de depuração.

Tenho uma diretiva para conectar estas macros:

#include <MyLib\DebugOrRelease\DebugSupport.mqh>

Esta diretriz reúne todos os arquivos e definições necessários. Tenho todos eles em uma pasta separada DebugOrRelease. Anexando-as aqui. (O código foi escrito há muito tempo, em sua maioria "apressadamente", portanto não é tão bonito quanto as interfaces e a classe de história). As afirmações e traços próprios para a versão de depuração estão nos arquivos AssertD e TraceD, as funções reais são PerformAssert() e PerformTrace().

Além destes arquivos e macros usar arquivo de log global (se a saída para o arquivo de log estiver definida), eu já o publiquei uma vez, mas, mais uma vez. O arquivo de log está em minha pasta "Comum".

Arquivos anexados:
 
Andrey Kisselyov:

Bom trabalho, eu gosto, mas não gosto de OOP e tento fazer sem ele. Não gosto de processadores com divisão de fluxo (por exemplo, 4 núcleos e 8 fios). Deve ficar claro que a divisão e qualquer virtualização é uma perda de performance e perda de tempo de máquina para sua implementação, seja ela divisão de fluxo no kernel ou virtualização de funções no código.

brevidade é a irmã do talento, eu acho que soa melhor.

Aprendi há muito tempo que a manutenção e reutilização do código é muito mais importante do que a redução de desempenho.

OOP - isso me ajuda muito quando volto ao código depois de algum tempo para modificá-lo. Para não mencionar a reutilização.

Mas, concordo, está longe de ser sempre necessário usar o OOP.

Digamos, eu tenho CDataProvider:pulic CDataProviderI class - provedor de dados, que fornece especialistas com séries de tempos, indicadores, dados de terminal e ambiente. Um Expert Advisor pode conter muitos TPs - cada um deles receberia indicações de timeseries e indicadores do provedor de dados (cada TP não precisa criar timeseries - o provedor de dados forneceria indicações para as timeseries necessárias se elas já existirem, e apenas criaria timeseries, que ainda não foram criadas).

Quando você precisar receber um indicador do provedor de dados - preencha a estrutura de descrição do indicador, e então solicite um indicador do provedor, apontando para esta estrutura.

Assim, cada indicador dentro do provedor de dados deve ser capaz de identificar sua estrutura (o provedor de dados "conhece" apenas a classe base abstrata da estrutura) e de acordo com ela criar o objeto indicador pronto, que será criado pelo provedor de dados.

Mas não é razoável fazer tudo isso apenas com o propósito de verificar uma nova idéia de um indicador. Como resultado, para esses novos indicadores tudo é "caseiro", sem nenhum OOP. Entretanto, se eu vejo que um indicador é útil para Consultores Especialistas, ele é escrito "adequadamente" - com total apoio do OOP e criação dentro de um fornecedor de dados.

P.S.

A propósito, no caso de indicadores e provedores de dados vemos a vantagem da herança de virtualização. Temos a interface básica dos parâmetros de indicadores CIndicatorParametersI, o sucessor desta interface são os parâmetros reais do indicador necessário. Ao solicitar o indicador, declaramos estes parâmetros e passamos ao fornecedor de dados um ponteiro para a interface abstrata. Assim, o próprio fornecedor de dados nem sabe qual indicador é solicitado - ele é definido em uma função, na qual o indicador é criado de acordo com o novo tipo. E somente este indicador sabe quais parâmetros foram passados, ele recupera os parâmetros necessários do objeto passado.

O truque é que em quase todo lugar dentro do provedor de dados existe uma classe básica simples de parâmetros (ou indicadores) - apenas as funções mais simples e comuns das interfaces básicas estão disponíveis para o provedor de dados. Isto simplifica a modificação do código (quando necessário), e não cria a tentação de "adulterar" o código de indicadores do fornecedor de dados. Se você quiser mudar um indicador, isso é feito apenas dentro do indicador, o fornecedor de dados é apenas um armazenamento de indicadores, o máximo que ele pode fazer é criar um novo indicador.

 
George Merts:

A propósito, fico muito nervoso quando o ninho é mais de dois níveis. Tento nunca escrevê-lo dessa maneira, espalhando o código sobre as funções.

E mesmo quando há dois níveis de aninhamento - eu sempre escrevo um comentário após cada parêntese de fechamento, que bloqueia o fechamento (por exemplo, cabeçalho de laço duplicado).

Quanto ao estilo, aqui está meu código para selecionaruma posição histórica para MT5 (por mágico especificado, símbolo, com intervalo de datas especificado):

A própria classe de história é descendente da interface abstrata CTradeHistoryI:

Ao selecionar o histórico necessário - você pode recalcular seus componentes (posições para MT5 ou pedidos para MT4), e obter uma interface para qualquer componente como uma interface abstrata:

Para o MT4 existem classes de história correspondentes que também herdam dessas interfaces - assim, a natureza transversal é fornecida ao mesmo tempo - uma EA não precisa descobrir onde trabalha, todo o trabalho com a história é feito através de interfaces abstratas.


Não é uma grande crítica:

class CTradePosComponentI: public CMyObject
{
...
}

Por que reinventar a roda na forma de CMyObject, quando existe um CObject padrão que todos entendem?

class class CTradeHistoryI: public CMyObject
{
// Расширенный интерфейс
   virtual void Sort(ESortTPCMode stmMode = STM_BY_OPEN_TIME_A) = 0;
}

A funcionalidade do CObject e CArrayObj é claramente copiada aqui. Por quê? A triagem rápida é construída em recipientes de dados padrão. Você deve usá-los.

class CTradePosComponentI: public CMyObject
{
public:
   void CTradePosComponentI() {    SetMyObjectType(MOT_TRADEPOS_COMPONENT_I); };
   virtual void ~CTradePosComponentI() {};
}

Se a classe tem uma interface - é melhor esconder seu construtor na seção protegida. Então, seu objeto não pode ser criado diretamente.

Definir um construtor vazio? Eu não sei. Eu não faria isso. É melhor não mencionar o destruidor se você não precisar dele.

for(iI=0;iI<iHistoryDealsTotal; ++iI)
...

Incremento não padrão iI, iteração não padrão ++iI, iHistóriaDealsTotal - definido em algum lugar lá fora, muito antes do loop. Mais simples:

for(int i = 0; i < HistoryDealsTotal();i++)

É tão rápido quanto a versão anterior, mas muito mais óbvio.

virtual bool               IsTPCInUnloss() const { if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE) return(false); if(GetTPCType() == POSITION_TYPE_BUY) { if(GetTPCStopLoss() >= GetTPCOpenPrice()) return(true); } else { if(GetTPCStopLoss() <= GetTPCOpenPrice())return(true); }; return (false); };

Os próprios programadores parecem ser contra tais textos, mas eles mesmos os escrevem em algum lugar. Ninguém quer investigar tais disparates. O que os impediu de escrevê-lo dessa forma:

virtual bool IsTPCInUnloss() const
{
   if(GetTPCStopLoss() <= 0 || GetTPCStopLoss() == EMPTY_VALUE)
      return(false);
   if(GetTPCType() == POSITION_TYPE_BUY)
   { 
      if(GetTPCStopLoss() >= GetTPCOpenPrice())
         return(true);
   } 
   else
   {
     if(GetTPCStopLoss() <= GetTPCOpenPrice())
        return(true);
   }; 
   return (false);
};

E ';' no final dos colchetes - isso é obsoleto, não se deve fazer isso agora.

Um método Select gigante consiste em um gigante para loop:

for(iI=0;iI<iHistoryDealsTotal; ++iI)
      {
      ulCurTicket = HistoryDealGetTicket(iI);
      
      if(ulCurTicket == 0)
         return(WRONG_VALUE);
      
      // Получим направление сделки   
      if(HistoryDealGetInteger(ulCurTicket,DEAL_ENTRY,lCurEntry)!=true)
         {
         TRACE_INTEGER("Не удалось получить направление сделки ! Тикет: ",ulCurTicket);
         continue;
         };
      
      // Проверим направление сделки
      if(lCurEntry != DEAL_ENTRY_OUT)
         continue;
      
      // Получим магик сделки
      if(HistoryDealGetInteger(ulCurTicket,DEAL_MAGIC,lCurMagic)!=true)
         {
         TRACE_INTEGER("Не удалось получить магик сделки ! Тикет: ",ulCurTicket);
         continue;
         };
         
      // Проверим магик
      if(ulMagic != NULL && lCurMagic != ulMagic)
         {
         //TRACE_INTEGER("Сделка не подходит ! Имеет неверный магик ! Magic сделки: ",lCurMagic);
         //TRACE_INTEGER("Требуемый Magic : ",ulMagic);
         continue;
         };
      ...
}

Obviamente, todas as verificações para a conformidade da transação com o Expert Advisor atual devem ser feitas em um método separado, por exemplo, como este:

for(iI=0;iI<iHistoryDealsTotal; ++iI)
{
   if(!CheckDeal(iI))
      continue;
   ...
}

E em geral, a seleção deve ser dividida em 3-4 mais métodos para deixar claro o que está acontecendo nela.

George Merts
O próprio Expert Advisor consiste em cinco linhas. Neste arquivo, o objeto da própria fábrica de peças da EA é declarado e as inclusões são incluídas.

A fábrica é um padrão muito controverso. É bom usá-lo, mas eu não recomendo fazer tudo através de uma fábrica.

George Merts
E mesmo quando há dois níveis de aninhamento, é obrigatório escrever comentários após cada parêntese de fechamento, que bloqueiam o enterramento (digamos, cabeçalho de laço duplicado).

Bem, é por isso que você escreve entre parênteses, da maneira feia da MQL. Se você o escreveu dessa forma:

if(OrderSelect())
{
   ...
}

Você sempre verá qual parêntese fecha qual bloco de código.

Você poderia ter encontrado mais de uma dúzia de avisos em seu código. Claro, o código não é perfeito, mas você pode sentir o gosto do autor pela beleza :))

 
Vasiliy Sokolov:

Um pouco de crítica:

О. Esse é o tipo de discussão que eu adoro. Portanto.

Por que reinventar a roda na forma de CMyObject, quando existe um CObject padrão que todos entendem?

A funcionalidade de CObject e CArrayObj é obviamente copiada aqui. Por quê? A triagem rápida é construída em recipientes de dados padrão. Utilize-os.

CMyObject é um herdeiro do CObject padrão, todas as listas e matrizes em meu código são descendentes de CArray (e outras matrizes de biblioteca padrão). Quase nunca utilizo arrays [] de matriz padrão.

E, é claro, ordenar e trabalhar com listas utiliza a funcionalidade básica do CObject.

E a diferença entre eles é a seguinte: Um CObjeto padrão é "um objeto de lista ou array ordenado". Um CMyObject é um CObject que tem um certo tipo, e contém algum valor dado quando foi criado. Eu precisava deste objeto por causa da redução generalizada de objetos à classe abstrata básica - para entender por ponteiro a qual objeto "de fato" aponta. O tipo CMyObject é definido por essa mesma função SetMyObjectType (). Esta função deve necessariamente ser chamada nos construtores de qualquer derivado de CMyObject, para atribuir um identificador à classe à qual o objeto pertence.

Também possui SetUDCreationValue() - definindo um valor definido pelo usuário quando criado. Raramente utilizado. É necessário distinguir diferentes objetos de uma mesma classe.

Se a interface de classe - seu construtor é melhor se esconder na seção protegida. Então, seu objeto não pode ser criado diretamente.

Construtor protegido ???? Sim, acho que é razoável para as interfaces, eu não sabia que era possível.

Definir um construtor vazio? Eu não sei. Eu não faria isso. É melhor não mencionar o destruidor se você não precisar dele.

É um "legado amaldiçoado do passado". Era uma vez um projeto bastante grande e, se não definíssemos um destruidor vazio, levou bastante tempo para remover os objetos por algum motivo. Por isso, tenho feito isso na mosca desde então. De modo geral, o destruidor também deve ser virtual.

Incremento não padrão iI, iteração não padrão ++iI, iHistóriaDealsTotal - definido em algum lugar lá fora, muito antes do loop. É melhor mantê-lo mais simples:

Discorda. O incremento é perfeitamente normal, - i, apenas uma notação padronizada - primeiro com uma letra pequena seu tipo é inteiro, e depois com uma letra maiúscula seu nome é I.

Você parece estar contra tais folhas, mas as escreve em algum lugar. Ninguém quer analisar tais disparates. O que o impediu de escrevê-lo dessa maneira:

Neste caso, tive que escolher entre "visibilidade" da classe e beleza da função. Eu escolhi "visibilidade". A beleza sofreu.

O método Select gigante consiste em um gigante para loop:

Obviamente, todas as verificações para a conformidade da transação com o Expert Advisor atual devem ser feitas em um método separado, por exemplo, como este:

E em geral, a seleção precisa ser dividida em 3-4 mais métodos para deixar claro o que está acontecendo nela.

Eu concordo. Aqui, em princípio, este mesmo ciclo "cresceu", no início não era tão grande assim.

Embora nem sempre seja conveniente implementar verificações menores em funções privadas, pois estas verificações nem sempre são rastreáveis em código.

A fábrica é um padrão muito controverso. O uso é bom, mas eu não recomendo fazer tudo através de uma fábrica.

Agora não me lembro, existiam várias variantes de construção do Expert Advisor. Eu parei na "Fábrica de peças do Expert Advisor". Em princípio, não é mais um puro padrão clássico de "fábrica". Originalmente, pretendia-se que fosse um padrão "clássico", mas agora é mais um "construtor-concentrador" de peças de Expert Advisor. E também é responsável pela remoção dessas peças, o que não é característico de uma fábrica. Mas o nome permaneceu.

É por isso que você o escreve entre parênteses da maneira feia da MQL. Se você o escreveu dessa forma:

Você sempre verá qual parêntese fecha qual bloco de código.

Por que "do jeito feio"?

O cabeçalho do laço, depois o suporte de abertura com recuo, depois o bloco inteiro com o mesmo recuo, e finalmente o suporte de fechamento - também com recuo.

O que você acha que é melhor?

 
Gregory Kovalenko:

Preciso obter lucros com 2 pedidos em aberto. A última ordem aberta, eu chamoOrderProfit2, e a próxima ordem -OrderProfit1.

A primeira ordem é aberta primeiro, depois a segunda ordem, então a primeira ordem no laço é chamada 2)

Onde está o erro?

Você está apenas passando pelas ordens. Em nenhum lugar ele verifica qual deles é o primeiro e qual é o segundo.

Você precisa entrar com um cheque no horário de abertura. Então você pode distinguir entre a ordem que abriu mais cedo e a ordem que abriu mais tarde. Ou talvez eles possam abrir ao mesmo tempo.

Razão: