Autoapprendimento del linguaggio MQL5 da zero - pagina 72

 
Vasiliy Sokolov:

Poiché non c'è limite alla perfezione, aggiungerò qualche altro commento sul codice:

Ho evidenziato in giallo due posti non banali.

1) Notate che il codice è ripetuto nel primo if e nel successivo else. L'unica differenza è nell'ultima riga e nell'azione finale (OpenBUY, OpenSell).

2) Le condizioni per entrare nel blocco else non sono ovvie. Non sono visibili a causa dell'abbondanza di ?? Infatti, dipendono solo dall'ultima linea:

Questo è un segno sicuro che qui manca una funzione.

Dobbiamo scrivere una funzione che restituisca vero se il tempo di apertura della posizione corrisponde a quello specificato (la scriverò più tardi).

Sì, Vasily, hai ragione, avremmo dovuto davvero scrivere la funzione.

Saluti, Vladimir.

 
Vasiliy Sokolov:
A proposito, fate attenzione alla dimensione totale del vostro programma. È già abbastanza grande. Come ti piace? A proposito, un principiante non può scrivere un codice così grande: le variabili si confondono, le parentesi diventano troppo grandi, gli errori di compilazione si insinuano come funghi dopo la pioggia. Dopo la compilazione, un programma di tali dimensioni inizia a fare glitch e nessuno riesce a capire cosa c'è che non va. E tutto funziona nel tuo codice per qualche motivo), ed è chiaro nelle funzioni cosa sta succedendo e come. In una parola, è una bellezza.

Grazie Vasily! Molto del tuo lavoro qui, dato che il modello di trailing stop è stato fornito da te. Devo solo riempire le funzioni con del codice. Attualmente sto lavorando sul trailing stop. In parte è già stato fatto, ma ci sono alcune cose che devo ancora capire per mostrare la versione finale EA al pubblico.

Saluti, Vladimir.

 

Ho aggiunto alcune funzioni. Mi sono ritrovato con un codice come questo:

//+------------------------------------------------------------------+
//| 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;
}
Ancora non capisco il lavoro di magia. Sulle reti non ha senso. In ogni caso si può facilmente rimuovere questo controllo, poiché è fatto solo in una funzione.
File:
MrBrooklin.mq5  38 kb
 

Anche il blocco OnInit è sovrapensiero e non è ancora scritto in modo corretto. Prima di tutto, dovresti cercare di scrivere identificatori, non numeri. Restituisce INIT_SUCCEED invece di -1. In secondo luogo, l'interruttore è eccessivo qui. Si dovrebbe usare o 'if' o 'switch'. Prima deve essere scritto uno e poi l'altro sarà obsoleto.

In terzo luogo, dobbiamo monitorare tutti i tipi di account. Abbiamo Demo e poi abbiamo Real. E poi c'è il Concorso. Ma anche se non ci fosse un terzo conto, ci dovrebbe essere uno stub che catturi tutte le altre varianti:

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 la ciliegina sulla torta: i commenti. Quando scriviamo funzioni, c'è molto spazio, la nostra mano è portata a mettere commenti nei pezzi di codice giusti:

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

Quando lo stesso codice è scritto "contemporaneamente" nel corpo di qualche funzione principale, diventa il più breve possibile e senza commenti:

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

Quindi scrivete più funzioni, vi incoraggiano a scrivere i giusti commenti, e anche se rendono il codice più prolisso, lo rendono anche più chiaro.

Il terzo punto: qui stai scrivendo:

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

Perché aggiungere un altro tipo di commento? Basta sostituire quello che c'è nel blocco con il tuo commento:

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

Ricordate, i commenti sono per voi, non voi per i commenti. Cancellate i vecchi e aggiungete i vostri al loro posto. Attieniti al formato - nell'intestazione della funzione, dichiara brevemente ma chiaramente nei commenti cosa fa la funzione e quali valori restituisce in quali casi.

 

A proposito, quando le condizioni del tempo di chiusura della posizione sono state separate in una funzione separata, è diventato chiaro che non era scritto correttamente:

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

Ho preso il contenuto interno dal tuo codice. È chiaro che la posizione sarà chiusa solo entro un minuto. В 23:50. Questo codice funzionerà, ma se qualcosa va storto alle 23:50, la posizione rimarrà sospesa alle 23:51. Per questo bisogna almeno scriverlo:

time_current.min>=time_close.min)

E anche questa opzione non è ideale. Una soluzione più potente è quella di utilizzare le modalità di trading. Tuttavia, questo è il prossimo livello di eccellenza. Finora questo design andrà bene.

 
MrBrooklin:

Ciao Vasily! Grazie mille per il vostro tempestivo consiglio e supporto. I tuoi messaggi sul ruolo delle funzioni e sui principi di costruzione del codice del programma mi hanno davvero aiutato nell'apprendimento del linguaggio di programmazione 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

Ora che le informazioni nella mia testa sono strutturate, è più facile capire il codice scritto non solo da me, ma anche da altri programmatori. Spero che questo argomento sia un buon aiuto per coloro che stanno iniziando ad imparare il linguaggio di programmazione MQL5 da zero.

Saluti, Vladimir.

Buon lavoro, Vladimir. I buoni codificatori di solito hanno buoni algoritmici, e hanno buoni fissatori di obiettivi... Tutto inizia con i giusti obiettivi e le giuste mete. Si può costruire subito una casa e poi trovare l'acqua. Si può trovare prima l'acqua e costruire la casa pensando all'acqua. Obiettivi / scopo, e la definizione degli obiettivi è da opportunità.... Vai direttamente all'algoritmo .... Ma in generale è molto buono!

 
Vasiliy Sokolov:

A proposito, quando le condizioni del tempo di chiusura della posizione sono state separate in una funzione separata, è diventato chiaro che non era scritto correttamente:

Ho preso il contenuto interno dal tuo codice. È chiaro che la posizione sarà chiusa solo entro un minuto. В 23:50. Questo codice funzionerà, ma se qualcosa va storto alle 23:50, la posizione rimarrà sospesa alle 23:51. Per questo bisogna almeno scriverlo:

E anche questa opzione non è ideale. Una soluzione più potente è quella di utilizzare le modalità di trading. Tuttavia, questo è il prossimo livello di eccellenza. A patto che questo design sia in grado di gestirlo.

Vasily, sei un grande insegnante!

Nessun abbecedario o libro di testo può fornire una tale spiegazione. Tutto quello che hai suggerito lo implementerò sicuramente nella versione finale dell'EA.

Saluti, Vladimir.

 

Vado un attimo fuori tema e vi racconterò una storia di vita di un insegnante. Nel nostro istituto, quando già ci stavamo specializzando, avevamo un'insegnante meravigliosa. All'epoca stavamo studiando l'algebra della logica. Molti studenti non riuscivano a capire per molto tempo come 1+1 potesse essere uguale a 1? Se 1x1, sarebbe ovviamente uguale a 1. Ed ecco fatto!!! Questo insegnante ha usato semplici esempi per darci una spiegazione di ciò che è l'OR logico e ciò che è l'AND logico, che ricorderò per il resto della mia vita.

Immaginate, dice l'insegnante, che dovete arrivare all'istituto la mattina per le lezioni. Si può prendere un filobus OPPURE un tram per arrivare all'istituto. Siete arrivati alla fermata dell'autobus, ma non c'è né un filobus (condizionefalsa o uguale a 0), né un tram (condizione falsa o uguale a 0). Naturalmente, non sarete in grado di raggiungere l'istituto (condizione false o uguale a 0). Controllare 0+0=0. Meraviglioso! Se arrivi alla fermata dell'autobus e c'è un filobus (condizione vera o uguale a 1), OPPURE un tram (condizione vera o uguale a 1), OPPURE sia un filobus che un tram insieme, allora arriverai sicuramente all'istituto e la condizione vera o uguale a 1 sarà valida! Controllo: 1+0=1, 0+1=1 e 1+1=1. Tutto si adatta!

Usando gli stessi esempi con il filobus e il tram, ci ha spiegato cos'è l'AND logico.

Questo è il potere del talento di un insegnante! Lo ricorderò per il resto della mia vita!

Sinceramente, Vladimir.

 
Valeriy Yastremskiy:

Ottimo lavoro. I buoni codificatori di solito hanno buoni algoritmici, e hanno dei fissatori di obiettivi, e hanno dei fissatori di obiettivi... Tutto inizia con i giusti obiettivi e le giuste mete. Si può costruire subito una casa e poi trovare l'acqua. Si può trovare prima l'acqua e costruire la casa pensando all'acqua. Obiettivi / scopo, e la definizione degli obiettivi è da opportunità.... Vai direttamente all'algoritmo .... Ma in generale è molto buono!

Grazie, Valery, per la tua partecipazione all'argomento e al dialogo costruttivo.

Saluti, Vladimir.