MQL5 언어를 처음부터 자가 학습 - 페이지 72

 
Vasiliy Sokolov :

완벽함에는 제한이 없으므로 코드에 몇 가지 설명을 더 삽입하겠습니다.

중요하지 않은 두 곳을 노란색으로 강조 표시했습니다.

1) 코드는 첫 번째 if와 다음 else에서 반복됩니다. 차이점은 마지막 줄과 최종 조치(OpenBUY, OpenSell)에만 있습니다.

2) else 블록에 들어가는 조건이 명확하지 않습니다. 풍요로움 때문일까요?? 조회되지 않습니다. 사실, 그들은 마지막 줄에만 의존합니다:

이것은 여기에 기능이 없다는 확실한 신호입니다.

포지션을 여는 시간이 지정된 시간과 일치하면 true를 반환하는 함수를 작성해야 합니다(조금 후에 작성하겠습니다)

예, Vasily, 맞습니다. 함수를 작성하는 것이 정말 필요했습니다.

안부 인사를 전합니다. 블라디미르.

 
Vasiliy Sokolov :
그건 그렇고, 프로그램의 전체 크기에주의하십시오. 이미 중요합니다. 잘 지내고 있나요? 그건 그렇고, 초보자는 더 이상 그런 양의 코드를 작성할 수 없습니다. 변수가 혼란스러워지기 시작하고, 괄호의 중첩이 스케일을 벗어나고, 컴파일 오류가 비가 온 후에 버섯처럼 나타나기 시작합니다. 이 크기의 프로그램을 컴파일한 후에는 실패하기 시작하고 아무도 무엇이 잘못되었는지 이해할 수 없습니다. 그리고 모든 것이 어떤 이유로 당신을 위해 작동합니다), 기능 측면에서 구조적으로 어떤 일이 어떻게 일어나고 있는지 명확합니다. 한 마디로 아름다움.

고마워, 바실리! 여기 당신의 작업의 큰 부분이 있습니다. 왜냐하면. 후행 중지 템플릿은 귀하가 제공했습니다. 함수를 코드로 채우기만 하면 됩니다. 지금은 트롤 작업을 하고 있습니다. 이미 무언가가 완료되었지만 모든 사람이 볼 수 있는 최종 버전의 어드바이저를 제공하기 위해 아직 파악해야 할 사항이 있습니다.

안부 인사를 전합니다. 블라디미르.

 

몇 가지 기능을 추가했습니다. 나는이 코드를 얻었다 :

 //+------------------------------------------------------------------+
//| 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;
}
마법으로 작업하는 것은 여전히 나에게 명확하지 않습니다. 그물에 그것은 의미가 없습니다. 어쨌든 이 검사는 하나의 기능에서만 수행되기 때문에 쉽게 제거할 수 있습니다.
파일:
MrBrooklin.mq5  38 kb
 

onInit 블록도 과도하게 엔지니어링되었으며 여전히 올바르게 작성되지 않았습니다. 첫째, 숫자가 아닌 식별자를 쓰도록 노력해야 합니다. -1이 아니라 INIT_FAILED를 반환하지만 INIT_SUCCEEDED는 0이 아닙니다. 둘째, 스위치는 여기에서 중복됩니다. if 또는 switch를 작성해야 합니다. 다른 하나는 버터 오일이기 때문에 한 가지를 먼저 쓰십시오.

셋째, 계정 유형에 대한 모든 옵션을 제어해야 합니다. 데모가 있고 실제가 있습니다. 그리고 콘테스트도 있습니다. 그러나 세 번째 계정이 없더라도 다른 모든 옵션을 잡을 수 있는 스텁이 있어야 합니다.

 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 нулевое значение означающее "удачная инициализация"
   }
  }
 

글쎄, 케이크에 체리 : 코멘트. 함수를 작성할 때 많은 공간이 있고 손 자체가 필요한 코드 조각에 주석을 제공하기 위해 손을 뻗습니다.

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

동일한 코드가 일부 주요 기능의 본문에 "동시에" 작성되면 주석 없이 가능한 한 짧아집니다.

 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();}

따라서 더 많은 기능을 작성하고 필요한 주석을 작성하도록 권장하며 코드를 더 장황하게 만들지만 더 이해하기 쉽습니다.

세 번째 요점: 여기에 다음과 같이 씁니다.

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

다른 유형의 댓글을 추가하는 이유는 무엇입니까? 블록에 있는 내용을 주석으로 바꾸세요.

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

댓글은 댓글을 위한 것이 아니라 회원님을 위한 것임을 기억하세요. 이전 것을 삭제하고 대신 자신의 것을 추가하십시오. 형식을 고수하십시오 - 함수의 헤더에 이 함수가 수행하는 작업과 어떤 경우에 어떤 값이 반환되는지 간략하지만 명확하게 설명합니다.

 

그건 그렇고, 시간별 포지션 청산 조건을 별도의 함수로 분리했을 때 잘못 쓰여진 것이 분명해졌습니다.

 //+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| времени для закрытия позиции. В противном случае возвращает ложь.|                                                 |
//+------------------------------------------------------------------+     
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 ;
}

귀하의 코드에서 내부 내용을 가져왔습니다. 포지션은 1분 이내에만 청산될 것이 분명합니다. 23시 50분. 이 코드는 작동하지만 23:50에 문제가 발생하면 위치는 23:51에 계속 정지됩니다. 따라서 최소한 다음과 같이 작성해야 합니다.

time_current.min>=time_close.min)

그리고 이것은 이상적이지 않습니다. 더 강력한 솔루션은 거래 모드를 사용하는 것입니다. 그러나 이것은 다음 수준의 기술입니다. 지금까지는 이 디자인이 작동할 것입니다.

 
MrBrooklin :

안녕하세요 바실리님! 적시에 조언과 지원을 주셔서 대단히 감사합니다. 함수의 역할과 프로그램 코드 구성 원칙에 대한 귀하의 메시지는 MQL5 프로그래밍 언어를 배우는 데 많은 도움이 되었습니다.

  1. https://www.mql5.com/en/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

이제 머리에 있는 정보가 이미 구조화된 형태를 취했을 때 나뿐만 아니라 다른 프로그래머가 작성한 코드를 훨씬 더 쉽게 인식할 수 있습니다. 이 주제가 모든 초보자가 MQL5 프로그래밍 언어를 처음부터 배우는 데 좋은 도움이 되기를 바랍니다.

안부 인사를 전합니다. 블라디미르.

잘했어요. 좋은 코더는 일반적으로 좋은 알고리즘을 가지고 있고, 작업 담당자와 골키퍼가 있습니다. 모든 것은 올바른 목표와 올바른 작업에서 시작됩니다. 바로 집을 짓고 물을 찾을 수 있습니다. 먼저 물을 찾아 주변 물을 고려하여 집을 지을 수 있습니다. 목표/목적, 그리고 문제 진술은 가능성에서 .... 당신은 즉시 알고리즘으로 이동 .... 하지만 일반적으로, 아주 심지어!

 
Vasiliy Sokolov :

그건 그렇고, 시간별 포지션 청산 조건을 별도의 함수로 분리했을 때 잘못 쓰여진 것이 분명해졌습니다.

귀하의 코드에서 내부 내용을 가져왔습니다. 포지션은 1분 이내에만 청산될 것이 분명합니다. 23시 50분. 이 코드는 작동하지만 23:50에 문제가 발생하면 위치는 23:51에 계속 정지됩니다. 따라서 최소한 다음과 같이 작성해야 합니다.

그리고 이것조차 이상적이지 않습니다. 더 강력한 솔루션은 거래 모드를 사용하는 것입니다. 그러나 이것은 다음 수준의 기술입니다. 지금까지는 이 디자인이 작동할 것입니다.

Vasily, 당신은 훌륭한 선생님입니다!

입문서나 교과서에서는 그러한 설명을 찾을 수 없습니다. 당신이 제안한 모든 것을 조언자의 최종 버전에서 확실히 구현할 것입니다.

안부 인사를 전합니다. 블라디미르.

 

주제에서 조금 벗어나 선생님(선생님)에 대한 제 삶의 이야기를 할게요. 우리 연구소에서는 이미 전문화가 진행 중일 때 훌륭한 사람이 가르쳤습니다. 그 당시 우리는 논리 대수학을 공부하고 있었습니다. 많은 학생들이 1+1 이 어떻게 1 과 같을 수 있는지 오랫동안 이해하지 못했습니다. 1x1 이면 당연히 1 과 같습니다. 그리고 여기 너!!! 이 선생님은 간단한 예를 사용하여 논리적 "OR"이 무엇인지, 논리적 "AND" 가 무엇인지에 대해 설명해 주셨고 평생 기억했습니다.

교사는 아침에 수업을 위해 연구소에 가야한다고 상상해보십시오. 무궤도 전차 또는 트램으로 연구소에 갈 수 있습니다. 정류장에 왔지만 트롤리 버스(조건이 false 이거나 0 과 동일)도 없고 트램(조건이 거짓 이거나 0 과 동일)도 없습니다. 당연히 학원에 올 수 없을 것입니다(조건 false 또는 0 과 동일). 0+0=0 확인 중. 놀라운! 정류장에 왔는데 무궤도 전차( 조건 또는 1 과 같음), 또는 트램( 조건 또는 1 과 동일), 또는 무궤도 전차와 트램이 함께 있으면 반드시 연구소에 도착할 것입니다. 조건이 이거나 1 과 같습니다! 1+0=1 , 0+1=11+1=1 을 확인합니다. 모든 것이 적합합니다!

무궤도 전차와 전차에 대한 동일한 예를 사용하여 그는 논리적 AND 가 무엇인지 설명했습니다.

이것이 바로 교사의 재능의 힘이 의미하는 것입니다! 평생 기억하겠습니다!

안부 인사를 전합니다. 블라디미르.

 
Valeriy Yastremskiy :

잘했어요. 좋은 코더는 일반적으로 좋은 알고리즘을 가지고 있고, 작업 담당자와 골키퍼가 있습니다. 모든 것은 올바른 목표와 올바른 작업에서 시작됩니다. 바로 집을 짓고 물을 찾을 수 있습니다. 먼저 물을 찾아 주변 물을 고려하여 집을 지을 수 있습니다. 목표/목적, 그리고 문제 진술은 가능성에서 .... 당신은 즉시 알고리즘으로 이동 .... 하지만 일반적으로, 아주 심지어!

Valery씨, 이 주제와 건설적인 대화에 참여해주셔서 감사합니다.

안부 인사를 전합니다. 블라디미르.