MQL5言語をゼロから独学で学ぶ - ページ 72

 
Vasiliy Sokolov:

完璧を求めるとキリがないので、コードに少しコメントを加えておきます。

自明でない2箇所を黄色で強調しました。

1) 最初のifと次のelseでコードが繰り返されていることに注意してください。違いは最後の行とエンドアクション(OpenBUY、OpenSell)だけです。

2)elseブロックに入るための条件が明らかでない。が多いため、目に見えない。実際には、最後の行にしか依存しない。

これは、ここに機能が欠けていることを意味しています。

ポジションを開く 時間が指定したものと一致した場合に真を返す関数を書く必要があります(後で書きます)。

そうですね、Vasilyさんのおっしゃるとおり、本当に関数を書くべきでした。

ウラジミールさん、ありがとうございます。

 
Vasiliy Sokolov:
ちなみに、プログラムの総容量にも気を配ってください。すでにかなり大きくなっています。いかがでしょうか。ちなみに、初心者がこのような大きなコードサイズを書くことはできません。変数が混ざったり、ブラケットが大きくなりすぎたり、コンパイルエラーが雨後のキノコのように忍び寄ったりします。コンパイル後、このサイズのプログラムは不具合を起こし、何が悪いのか誰も理解できない。そして、あなたのコードではなぜかすべてがうまくいくのです)、何がどのように起こっているのか、関数の中で明確にされているのです。一言で言えば、「美しい」です。

Vasilyさん、ありがとうございます。トレーリングストップのテンプレートが提供されたように、ここではあなたの仕事がたくさんあります。あとは、関数にコードを埋め込むだけです。現在、トレーリングストップに取り組んでいるところです。すでにできているものもありますが、EAの最終版をパブリックレビューに出す前に、まだ解決しておかなければならないことがあるのです。

ウラジミールさん、ありがとうございます。

 

少し機能を追加してみました。結局、こんなコードになりました。

//+------------------------------------------------------------------+
//| 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;
}
まだ、マジックワークが理解できていません。ネットでは意味がない。いずれにせよ、このチェックは1つの関数で行われるだけなので、簡単に削除することができます。
ファイル:
MrBrooklin.mq5  38 kb
 

OnInitブロックも考えすぎで、まだかなり正しく書けていません。まず、数字ではなく、識別子を書くようにすることです。1の代わりにINIT_SUCCEEDEDEDを返す。第二に、ここではswitchが過剰である。if'か'switch'のどちらかを使用する必要があります。まず一方が先に書かれていないと、もう一方は陳腐化してしまう。

第三に、すべてのアカウントの種類を監視する必要があります。Demoがあり、Realがある。そして、「コンテスト」です。しかし、たとえ第3のアカウントがなかったとしても、他のすべての変種を 捕らえるスタブがあるはずです。

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

だから、もっと関数を書いてください。正しいコメントを書くように促されますし、コードが冗長になるけれども、明確になるんです。

3点目:ここに書いてありますね。

//+------------------------------------------------------------------+
/ | 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:

Vasilyさん、こんにちは。タイムリーなアドバイスとサポートをどうもありがとうございました。関数の役割やプログラムコード構築の原則についてのコメントは、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

頭の中の情報が構造化されたことで、自分だけでなく、他のプログラマーが書いたコードも理解しやすくなったのです。このトピックが、これからプログラミング言語MQL5をゼロから学び始める方にとって、良い参考になれば幸いです。

ウラジミールさん、ありがとうございます。

ウラジミールさん、お疲れ様でした。優秀なコーダーには、たいてい優秀なアルゴリズム担当者がいて、優秀な目標設定担当者がいる...。すべては、正しい目標、正しい目的から始まるのです。一気に家を建てて、水を探すことも可能です。まず水を見つけ、水を意識した家づくりをすればいいのです。目標/目的、そして目標設定は機会から...。アルゴリズムに直行するんですね...。しかし、一般的にはとても良いものです

 
Vasiliy Sokolov:

ところで、位置閉鎖 時間条件を別の関数に分離したところ、正しく記述されていないことが明らかになった。

あなたのコードから内部コンテンツを取り出しました。1分以内にしかポジションがクローズしないことが明確になっています。В 23:50.このコードは動作しますが、23時50分に何か問題が発生した場合、23時51分になってもポジションはぶら下がったままです。だからこそ、せめて書いておかないと。

そして、この選択肢さえも理想的ではありません。より強力なソリューションは、トレーディングモードを使用することです。しかし、これは次のレベルの話です。このデザインで対応できるのであれば。

Vasilyさん、あなたは素晴らしい先生です。

どんな入門書や教科書にも、そのような説明は載っていない。ご指摘いただいたことは、EAの最終バージョンに必ず反映させます。

ウラジミールさん、ありがとうございます。

 

ちょっと話がそれますが、ある先生のライフストーリーをお話しします。私たちの研究所では、すでに専門化していた頃、素晴らしい先生がいらっしゃいました。当時は、論理学の代数学を勉強していた。多くの学生は、どうして1+1が 1に なるのか、長い間理解できなかった。1x1 であれば、当然1に なります。そして、そこにあるのは!!!この先生は、論理的なORとは 何か、論理的なANDとは 何か、簡単な例を使って説明してくれましたが、これは一生忘れないでしょう。

先生曰く、「朝、研究所に着いて授業を受けなければいけないと想像してください。研究所へは、トロリーバスまたは トラムで行くことができます。バス停まで来たが、トロリーバス(条件不成立 または0と 同じ)もトラム(条件不成立 または0と 同じ)もない。当然、研究所には行けません(条件 偽または 0と 同じ)。0+0=0 を確認する。ワンダフル!バス停に着いて、トロリーバス(条件 1 と同じ)か 路面電車(条件:1 同じ)か、あるいは トロリーバスと路面電車の両方がある場合、あなたは間違いなく研究所に到着し、条件:1同じが 成立します!チェック:1+0=1,0+1=1,1+1=1。すべてがフィットする!

同じようにトロリーバスや路面電車を例にして、論理的なANDとは 何かを説明してくれたのです。

それが先生の才能の力なんです!一生の思い出になります!

敬具 ウラジミール

 
Valeriy Yastremskiy:

お疲れ様でした。優秀なコーダーには、たいてい優秀なアルゴリズム担当者がいて、目標設定担当者がいて...。すべては、正しい目標、正しい目的から始まります。一気に家を建てて、水を探すことも可能です。まず水を見つけ、水を意識した家づくりをすればいいのです。目標/目的、そして目標設定は機会から...。アルゴリズムに直行するんですね...。しかし、一般的にはとても良いものです

Valeryさん、トピックへの参加と建設的な対話に感謝します。

ウラジミールさん、ありがとうございます。