Auto-aprendizagem da linguagem MQL5 a partir do zero - página 72

 
Vasiliy Sokolov:

Como não há limite para a perfeição, acrescentarei mais alguns comentários sobre o código:

Destaquei dois lugares não triviais em amarelo.

1) Note que o código é repetido no primeiro se e no próximo se. A única diferença está na última linha e na ação final (OpenBUY, OpenSell).

2) As condições para entrar no outro bloco não são óbvias. Eles não são visíveis devido à abundância de ? Na verdade, eles dependem apenas da última linha:

Este é um sinal seguro de que uma função está faltando aqui.

Precisamos escrever uma função que retorne verdadeiro se o tempo para abrir a posição corresponder ao especificado (eu a escreverei mais tarde).

Sim, Vasily, você está certo, nós realmente deveríamos ter escrito a função.

Cumprimentos, Vladimir.

 
Vasiliy Sokolov:
A propósito, preste atenção ao tamanho total de seu programa. Já é bastante grande. O que você acha disso? A propósito, um novato não pode escrever um código de tamanho tão grande: variáveis se misturam, parênteses se tornam muito grandes, erros de compilação se infiltram como cogumelos depois da chuva. Após a compilação, um programa desse tamanho começa a apresentar falhas e ninguém consegue entender o que está errado. E tudo funciona em seu código por alguma razão), e é claro nas funções o que está acontecendo e como. Em uma palavra, é uma beleza.

Obrigado Vasily! Muito de seu trabalho aqui, já que o modelo de parada de trilha foi fornecido por você. Eu só tenho que preencher as funções com código. Atualmente estou trabalhando na parada de trilha. Algumas delas já foram feitas, mas há algumas coisas que ainda preciso resolver antes de submeter a versão final da EA à revisão pública.

Cumprimentos, Vladimir.

 

Eu acrescentei algumas funções. Eu acabei com um código como este:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Зададим условия для открытия позиций BUY и SELL   
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   price=NormalizeDouble(price,digits);
   //-- Если открытой позиции нет и время для открытия позволяет,
   //-- открывает BUY или SELL в зависимости от положения тика
   if(IsMainPositionOpen() == false && IsTimeForOpen())
   {
      if(TickUP()==(price+point))
         OpenBUY();
      if(TickDOWN()==(price-point))
         OpenSELL();
   }
   //-- Если наступило время закрытия позиции, закрываем все
   if(IsTimeForClose())
      CloseALL();

//+------------------------------------------------------------------+
//| Шаблон трейлинг стопа предоставленный Василием Соколовым         |
//+------------------------------------------------------------------+

//-- Выбираем позиции по текущему символу. Если позиции нет и выбирать нечего, то выходим!
   if(!PositionSelect(Symbol()))
      return;
//-- Стоп-лосс длинной позиции переставляем в безубыток и тралим его
   if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
     {
      SetBreakevenForBuyPosition(); // установить безубыток для Buy позиции
      TrailingStopLossForBuyPosition(); // перетащить Stop Loss для Buy позиции
     }
//-- Стоп-лосс короткой позиции переставляем в безубыток и тралим его
   else
      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
        {
         SetBreakevenForSellPosition(); // установить безубыток для Sell позиции
         TrailingStopLossForSellPosition(); // перетащить Stop Loss для Sell позиции
        }
  }
  
//+------------------------------------------------------------------+
//| Возвращает истину, если позиция торгового эксперта уже открыта.  |
//| Возвращает ложь в противном случае.                              |
//+------------------------------------------------------------------+  
bool IsMainPositionOpen()
{
   //-- Если позиций нет - то и у эксперта позиции нет, возвращаем ложь.
   if(PositionSelect(Symbol()) == false)
      return false;
   //-- Позиция есть и ее мэджик совпадает с мэджиком эксперта - возвращаем истину
   if(PositionGetInteger(POSITION_MAGIC) == Magic_Number)
      return true;
   //-- Позиция есть но ее мэджик не совподает с мэджиком эксперта -
   //-- это чья-то другая позиция, позвращаем ложь
   else
      return false;
}
//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| разрешенного времени для открытия позиции. В противном случае    |
//| возвращает ложь.                                                 |
//+------------------------------------------------------------------+  
bool IsTimeForOpen()
  {
   MqlDateTime time_current,time_open,time_open1;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 09:00:00'),time_open);
   TimeToStruct((D'1970.01.01 09:01:00'),time_open1);
   if(time_current.hour == time_open.hour &&
      time_current.min >= time_open.min &&
      time_current.min < time_open1.min
      )
      return true;
   else
      return false;
   }
//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| времени для закрытия позиции. В противном случае возвращает ложь.|                                                 |
//+------------------------------------------------------------------+     
bool IsTimeForClose()
{
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 23:50:00'), time_close);
   if(time_current.hour==time_close.hour && 
      time_current.min==time_close.min)
      return true;
   else
      return false;
}
Eu ainda não entendo o trabalho mágico. Na rede não faz sentido. Em qualquer caso, você pode remover facilmente esta verificação, pois ela é feita apenas em uma função.
Arquivos anexados:
MrBrooklin.mq5  38 kb
 

O bloco do OnInit também é muito pensado e ainda não foi escrito de forma muito correta. Antes de tudo, você deve tentar escrever identificadores, não números. Retornar INIT_SUCCEEDED ao invés de -1. Em segundo lugar, a troca é excessiva aqui. Este código deve conter ou se ou trocar. Primeiro você tem que escrever um e depois o outro - apenas óleo.

Em terceiro lugar, precisamos monitorar todos os tipos de contas. Temos Demo e depois temos Real. E depois há o Concurso. Mas mesmo que não houvesse uma terceira conta, deveria haver um toco que capturaria todas as outras variantes:

int OnInit()
  {
//--- Определим тип счёта на который устанавливаем советник: демо или реальный счет
   ENUM_ACCOUNT_TRADE_MODE account_type=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
//--- теперь превратим значение перечисления в понятный вид
   string trade_mode;               //создадим переменную для торгового режима
   ACCOUNT_TRADE_MODE_CONTEST
   if(account_type==ACCOUNT_TRADE_MODE_REAL) //если торговый режим счёта - реальный
     {
      //--- выводим окно сообщений на торговом терминале и закрываем советник
      MessageBox("Работа на реальном счете запрещена, выходим!","Советник запущен на реальном счете");
      return(INIT_FAILED); //возвращаем для функции OnInit ненулевое значение означающее "неудачная инициализация"
     }
   if(account_type==ACCOUNT_TRADE_MODE_DEMO) //если торговый режим счёта - демо
     {
      //--- выводим окно сообщений на торговом терминале и продолжаем работу советника
      MessageBox("Работа на демо-счете разрешена!","Советник запущен на демо-счете");
      trade_mode="Счёт REAL";
      return(INIT_SUCCEEDED); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
     }
//-- Заглушка на случай непредвиденных вариантов. Всегда должна быть даже если варианта явно два.
   else
   {
      MessageBox("Неизвестный тип счета. Работа невозможна!");
      return(INIT_FAILED); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
   }
  }
 

E a cereja no bolo: comentários. Quando escrevemos funções, há muito espaço, nossa mão é desenhada para colocar comentários nos pedaços de código certos:

//+------------------------------------------------------------------+
//| Возвращает истину, если позиция торгового эксперта уже открыта.  |
//| Возвращает ложь в противном случае.                              |
//+------------------------------------------------------------------+  
bool IsMainPositionOpen()
{
   //-- Если позиций нет - то и у эксперта позиции нет, возвращаем ложь.
   if(PositionSelect(Symbol()) == false)
      return false;
   //-- Позиция есть и ее мэджик совпадает с мэджиком эксперта - возвращаем истину
   if(PositionGetInteger(POSITION_MAGIC) == Magic_Number)
      return true;
   //-- Позиция есть но ее мэджик не совподает с мэджиком эксперта -
   //-- это чья-то другая позиция, позвращаем ложь
   else
      return false;
}

Quando o mesmo código é escrito "ao mesmo tempo" no corpo de alguma função principal, ele se torna tão curto quanto possível e sem comentários:

if(PositionSelect(Symbol())==false
      && PositionGetInteger(POSITION_MAGIC)!=Magic_Number
      && time_current.hour==time_open.hour
      && time_current.min>=time_open.min
      && time_current.min<time_open1.min
      && (TickUP()==(price+point)))
     {OpenBUY();}

Portanto, escreva mais funções, eles o encorajam a escrever os comentários certos e, embora tornem o código mais verboso, eles também o tornam mais claro.

O terceiro ponto: aqui você está escrevendo:

//+------------------------------------------------------------------+
/ | Expert initialization function                                   |
//+------------------------------------------------------------------+
/* Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции
   return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то
   это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше
   можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.
   "удачная инициализация".
*/
int OnInit()
  {
  ...
  }

Por que acrescentar outro tipo de comentário? Basta substituir o que está no bloco por seu comentário:

//+------------------------------------------------------------------------------------------------------+
//| Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции    |
//| return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то   |
//| это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше      |
//| можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.  |
//| "удачная инициализация".                                                                             |
//+------------------------------------------------------------------------------------------------------+
int OnInit()
  {
  ...
  }

Lembre-se, os comentários são para você, não para você. Elimine os antigos e acrescente os seus próprios. Mantenha-se fiel ao formato - no cabeçalho da função, diga brevemente mas claramente nos comentários o que a função faz e quais os valores que ela retorna em que casos.

 

A propósito, quando as condições de tempo de fechamento da posição foram separadas em uma função separada, ficou claro que ela não estava escrita corretamente:

//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| времени для закрытия позиции. В противном случае возвращает ложь.|                                                 |
//+------------------------------------------------------------------+     
bool IsTimeForClose()
{
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 23:50:00'), time_close);
   if(time_current.hour==time_close.hour && 
      time_current.min==time_close.min)
      return true;
   else
      return false;
}

Eu tirei o conteúdo interno de seu código. É claro que a posição será fechada em apenas um minuto. В 23:50. Este código funcionará, mas se algo der errado às 23:50, a posição permanecerá suspensa às 23:51. É por isso que você tem que pelo menos escrevê-lo:

time_current.min>=time_close.min)

E mesmo esta opção não é a ideal. Uma solução mais poderosa é a utilização de modos comerciais. No entanto, este é o próximo nível de excelência. Até o momento, este projeto vai servir muito bem.

 
MrBrooklin:

Olá Vasily! Muito obrigado por seus conselhos e apoio oportuno. Seus comentários sobre o papel das funções e princípios de construção do código do programa realmente me ajudaram no aprendizado da linguagem de programação MQL5:

  1. https://www.mql5.com/ru/forum/352460/page28#comment_18636493
  2. https://www.mql5.com/ru/forum/352460/page28#comment_18637800
  3. https://www.mql5.com/ru/forum/352460/page29#comment_18641729
  4. https://www.mql5.com/ru/forum/352460/page52#comment_18694985

Agora que a informação em minha cabeça está estruturada, é mais fácil entender o código escrito não só por mim, mas também por outros programadores. Espero que este tópico seja uma boa ajuda para aqueles que estão começando a aprender a linguagem de programação MQL5 a partir do zero.

Cumprimentos, Vladimir.

Bom trabalho, Vladimir. Bons codificadores geralmente têm bons algoritmos e têm bons programadores de metas... Tudo começa com as metas certas, e os objetivos certos. Você pode construir uma casa de uma vez e depois encontrar água. Você pode encontrar água primeiro e construir a casa com água em mente. Metas / propósito, e o estabelecimento de metas é de oportunidade.... Você vai direto para o algoritmo.... Mas em geral é muito bom!

 
Vasiliy Sokolov:

A propósito, quando as condições de tempo de fechamento da posição foram separadas em uma função separada, ficou claro que ela não estava escrita corretamente:

Eu tirei o conteúdo interno de seu código. É claro que a posição será fechada em apenas um minuto. В 23:50. Este código funcionará, mas se algo der errado às 23:50, a posição permanecerá suspensa às 23:51. É por isso que você tem que pelo menos escrevê-lo:

E mesmo esta opção não é a ideal. Uma solução mais poderosa é a utilização de modos comerciais. No entanto, este é o próximo nível de excelência. Desde que este projeto seja capaz de lidar com ele.

Vasily, você é um grande professor!

Nenhuma cartilha ou livro didático pode fornecer tal explicação. Tudo o que você sugeriu eu certamente irei implementar na versão final da EA.

Cumprimentos, Vladimir.

 

Vou sair do tópico por um momento e vou contar uma história de vida sobre um professor. Em nosso instituto, quando já estávamos nos especializando, tínhamos um professor maravilhoso. Naquela época, estávamos estudando a álgebra da lógica. Muitos estudantes não conseguiram entender por muito tempo como 1+1 poderia ser igual a 1? Se 1x1, obviamente seria igual a 1. E aí está!!!! Este professor usou exemplos simples para nos dar uma explicação do que é o OU lógico e do que é o E lógico, que eu vou lembrar para o resto da minha vida.

Imagine, diz o professor, que você precisa chegar ao instituto pela manhã para as aulas. Você pode pegar um bonde-ônibus OU um bonde para chegar ao instituto. Você chegou à parada do ônibus, mas não há nem um trólei (condiçãofalsa ou igual a 0), nem um bonde (condição falsa ou igual a 0). Naturalmente, você não conseguirá chegar ao instituto (condição falsa ou a mesma que 0). Verificar 0+0=0. Maravilhoso! Se você chegar na parada do ônibus e houver um trólei (condição verdadeira ou a mesma que 1), OU um bonde (condição verdadeira ou a mesma que 1) , OU ambos um trólei e um bonde juntos, então você definitivamente chegará ao instituto e a condição verdadeira ou a mesma que 1 se manterá! Verificação: 1+0=1, 0+1=1 e 1+1=1. Tudo se encaixa!

Usando os mesmos exemplos com o trólei e o bonde, ele nos explicou o que é lógico AND.

Esse é o poder do talento de um professor! Vou me lembrar disso para o resto da minha vida!

Atenciosamente, Vladimir.

 
Valeriy Yastremskiy:

Bom trabalho. Os bons codificadores geralmente têm bons algorítmicos, e têm programadores de metas, e têm programadores de metas... Tudo começa com as metas certas, e os objetivos certos. Você pode construir uma casa de uma vez e depois encontrar água. Você pode encontrar água primeiro e construir a casa com água em mente. Metas / propósito, e o estabelecimento de metas é de oportunidade.... Você vai direto para o algoritmo.... Mas em geral é muito bom!

Obrigado, Valery, por sua participação no tema e por um diálogo construtivo.

Cumprimentos, Vladimir.

Razão: