情報ストレージとその表示

16 2月 2016, 09:10
Andrey Khatimlianskii
0
642

1. はじめに

ログファイルにエキスパートにより以前記述された重要な情報を見つけようと何時間もかけて過ごしたことはありますか?もしくは、おそらく、Comment()関数により表示される一色の小さい文字を見つめるのに飽き飽きしていますか?それらは、トレーディングにおいて非常に重要なことになりえます。もしどういうことか知っているようであれば、この記事はあなたに向けられているようなものです。

以下は、この記事を記述するために書き出した主な点です。

  • ログファイルに情報を書き込むためにエキスパートによって使用される関数を作成するにはどうすれば良いのか?
  • 表示される情報をより良い視覚的なインパクトを持つようにするツールを作成するにはどうすれば良いのか?:色やフォントのサイズを変えるのか。

2. エキスパートネィティブログファイル

以前述べた通り、Print()関数は、処理中にエキスパートにより情報を記述する上で必ずしも便利ではありません。これは、いくつかのエキスパートが同じターミナル上で同時にトレードする際、特に明白になります。それぞれのエキスパートは、ログファイルに独自の情報を書き込み、そこで何かを探すことはかなり難しいです。この場合、そのような情報の建設的な分析は、確実に論外です;とても困難で、やっかいです。

この問題は実にシンプルな方法で解決することができます。すべてのエキスパートはそれぞれにログファイルを持ちます。そして、情報がそれぞれのエキスパートの個別のログファイルに記述されます。できる限り簡単いこの機会を利用することができるよう、コードを関数として記述しましょう。

関数が何をする必要があるかは以下に記載されています。

  • 一意性のある名前でファイルを作成します
  • その中に情報を記述し、必要な際に
  • エクスパートが処理を終了すればファイルを閉じるようにし、ファイルがその他のアプリケーションにも使用できるようにします。

すべて簡単です。唯一論じうる点は、記録の度にファイルを閉じる必要性です。一方で、これはエキスパートの処理中にその他のアプリケーションがファイルを開くことを許可します。しかし、もしエキスパートがファイルがその他のアプリケーションに使用されているためファイルを開くことができなければ問題が生じます。この場合、情報を損失してしまします。MetaTraderで読み取り専用のファイルを開くことができるプログラムがあるので、これは避けなければなりません。

ファイルのオープンやクローズは一度のみしか実行されないので、それに対するコードは、init()、deinit()関数にそれぞれ配置されます。最小限のスペースで済ませるために、関数として記述しましょう:

int log_handle = -1;
 
//+------------------------------------------------------------------+
// void log_open( string ExpertName = "Expert" )
//
// Function that opens the expert's separate log file.
// Directory, in which the file will be created:
// "...\MetaTrader 4\experts\files\logs\ExpertName\"
// The file name is the date of the file record appeared as "YYYY.MM.DD"
//+------------------------------------------------------------------+
void log_open( string ExpertName = "Expert" )
 {
     string log_name = "logs\\" + ExpertName + " (" + Symbol() + ", " + 
                   strPeriod( Period() ) + ")\\" + TimeToStr( LocalTime(),
                   TIME_DATE ) + ".txt";
  log_handle = FileOpen ( log_name, FILE_READ | FILE_WRITE, " " );
    
  if ( log_handle < 0 )
     { 
         int _GetLastError = GetLastError();
    Print( "FileOpen( ", log_name, ", FILE_READ | FILE_WRITE, \" \" ) - Error #", 
                     _GetLastError );
    return(-1);
   }
 }
string strPeriod( int intPeriod )
 {
     switch ( intPeriod )
     {
          case PERIOD_MN1: return("Monthly");
    case PERIOD_W1:  return("Weekly");
    case PERIOD_D1:  return("Daily");
    case PERIOD_H4:  return("H4");
    case PERIOD_H1:  return("H1");
    case PERIOD_M30: return("M30");
    case PERIOD_M15: return("M15");
    case PERIOD_M5:  return("M5");
    case PERIOD_M1:  return("M1");
    default:        return("UnknownPeriod");
   }
 }
 
//+------------------------------------------------------------------+
// log_close()
//
// Function that closes the expert's native log file.
//+------------------------------------------------------------------+
void log_close()
 {
     if ( log_handle > 0 ) FileClose( log_handle );
 }


オープンファイルがありますので、情報をそれに記述することができます。このために:

  • 情報を失わないようにカーソルをファイルの最後に移動させます;
  • その行の最初にレコード時間を記述します;これは分析において役に立ちます;
  • そのテキストをファイルに記載します;
  • ディスクに完成したファイルを保存します;もしそのエキスパートが処理を不規則に終了させた場合、これはデータの損失を防ぎます。

以下の関数は上記から生じたものです:

//+------------------------------------------------------------------+
// log( string text )
//
// The function that writes the text line into the expert's native log file.
//+------------------------------------------------------------------+
void log( string text )
 {
     int _GetLastError = 0;
  if ( log_handle < 0 )
     {
         Print( "Log write error! Text: ", text );
    return(-1);
   }
    
     //---- Move the file pointer to the end of file
    if ( !FileSeek ( log_handle, 0, SEEK_END ) )
      {
         _GetLastError = GetLastError();
    Print( "FileSeek ( " + log_handle + ", 0, SEEK_END ) - Error #", 
          _GetLastError );
    return(-1);
  }
    //---- If the line to be written by the expert is not the line feed character, 
    //---- add the recording time at the beginning of the line
    if( text != "\n" && text != "\r\n" )
         text = StringConcatenate( TimeToStr( LocalTime(), TIME_SECONDS ), 
                             " - - - ", text );
    if( FileWrite ( log_handle, text ) < 0 )
          {
             _GetLastError = GetLastError();
     Print( "FileWrite ( ", log_handle, ", ", text, " ) - Error #", 
           _GetLastError );
     return(-1);
    }
    
    //---- Save the written text on the disk
    FileFlush( log_handle );
 }


さて、そのPrintという単語は、すべてのエキスパートにおいてログという単語と容易に入れ替わられます。log_openと、log_close関数の呼び出しを忘れないようにしましょう。

これは、インクルードファイルのlog.mq4を使用する典型的でシンプルなエキスパートです。

#include <log.mq4>
 
int init()
  {
    log_open( "log_test" );
    log( "The log file has been successfully opened, the expert starts working..." );
    return(0);
  }
int deinit()
  {
    log( "Close the log file, the expert finishes work..." );
    log_close();
    return(0);
  }
int start()
  {
    log( "New tick: Bid = " + DoubleToStr( Bid, Digits ) );
    return(0);
  }

3. 情報の表示

ログファイルの問題を解決したので、表示される情報の「デコレーション」を開始することができます。

まずは、このタスクを実装するいくつかの方法を考察しましょう。MQL4では、Comment()関数が情報の表示を担っていましたが、それは上記の内容において適しません。したがって、その他のソリューションを考案する必要があります。これにおける良い例は、テキストを所持するオブジェクトです。それらのうち二つしかありません: "テキスト"と"テキストラベル". それらの根本的な違いは”テキスト”はチャート座標に固定されており(価格と時間)、”テキストラベル”は、ウィンドウ座標に固定されています。チャートが移動したり、サイズが変化する際にその地点に固定されるため情報が必要なので、我々は”テキストラベル”を使用します。

Objectという単語で始まる名前のオブジェクトを作成しコントロールする関数がMQL4にあります。それらのうちどれが今回の目的において役に立つか見てみましょう;

  • bool ObjectCreate(...) – オブジェクトを作成する;
  • bool ObjectDelete(...) – 使用後、オブジェクトを削除する;
  • bool ObjectSet(...) – アンカ(x,y)のようなオブジェクト属性を変更する
  • bool ObjectSetText(...) – テキストを表示する;
  • void ObjectsRedraw() – テキストが変更されたのち、オブジェクトを再描画する

以下がやらなければならないことです:

  • init()と呼ばれるエキスパート関数にて、情報を表示するためのオブジェクトを作成する;
  • deinit()関数と呼ばれるエキスパート関数が作成されたすべてのオブジェクトを削除します;
  • start()関数にて、すべての作成されたオブジェクトのサイズやテキスト、フォントの色を変更できるようにする;

それぞれの作業をこなす、3つの関数を得ました。


コードを記述する前に、”テキストラベル”の使用に関する不快な制限について言及したいと思います。それは一行ですみます、つまり、フィード文字を含まないということです。しかし、情報はもし数行で表示される場合、よりよく理解されます。そのため、いくつかのオブジェクトを作成し、データをその中で分配するのです。5つの「行」を作成しましたが、どれだけの行を使用しても良いです。

さらに、表示されるテキストの長さに関して制限があります。そのため、2番目の”カラム”を追加しました。つまり、右手にもう5つの行を追加します。

こちらがオブジェクトを作成するinfo_init()関数になります:

/////////////////////////////////////////////////////////////////////////////////
// void info_init()
//
// Creation of objects to display information
/////////////////////////////////////////////////////////////////////////////////
void info_init()
  {
    for( int row = 0; row <= 4; row ++ )
      {
        _LabelCreate( StringConcatenate( "InfoLabel_0", row ),   4, 15 + 15*row );
        _LabelCreate( StringConcatenate( "InfoLabel_1", row ), 270, 15 + 15*row );
      }
  }
 
/////////////////////////////////////////////////////////////////////////////////
// void _LabelCreate ( string _Name, int _XDistance, int _YDistance, int _Corner = 0 )
//
// Creation of "Text Label" named as _Name.
// Coordinates: х = _XDistance, у = _YDistance, corner = _Corner.
/////////////////////////////////////////////////////////////////////////////////
void _LabelCreate ( string _Name, int _XDistance, int _YDistance, int _Corner = 0 )
  {
    int _GetLastError;
 
    if( !ObjectCreate( _Name, OBJ_LABEL, 0, 0, 0 ) )
      {
        _GetLastError = GetLastError();
        if ( _GetLastError != 4200 )
          {
            Print( "ObjectCreate( \"", _Name, "\", OBJ_LABEL,0,0,0 ) - Error #",
                   _GetLastError );
            return(-1);
          }
      }
    if( !ObjectSet( _Name, OBJPROP_CORNER, _Corner ) )
      {
        _GetLastError = GetLastError();
        Print( "ObjectSet( \"", _Name, "\", OBJPROP_CORNER, ", _Corner, 
                                       " ) - Error #", _GetLastError );
      }
    if( !ObjectSet( _Name, OBJPROP_XDISTANCE, _XDistance ) )
      {
        _GetLastError = GetLastError();
        Print( "ObjectSet( \"", _Name, "\", OBJPROP_XDISTANCE, ", _XDistance, 
                                             " ) - Error #", _GetLastError );
      }
    if( !ObjectSet( _Name, OBJPROP_YDISTANCE, _YDistance ) )
      {
        _GetLastError = GetLastError();
        Print( "ObjectSet( \"", _Name, "\", OBJPROP_YDISTANCE, ", _YDistance, 
                                             " ) - Error #", _GetLastError );
      }
    if( !ObjectSetText ( _Name, "", 10 ) )
      {
        _GetLastError = GetLastError();
        Print( "ObjectSetText( \"", _Name, "\", \"\", 10 ) - Error #", _GetLastError );
      }
  }

ご覧の通り、オブジェクトは、"InfoLabel_" + номер объекта (00から04が左側のカラムに、10から14までが右に割り当てられます)と呼ばれます。そのオブジェクトは、上方の左コーナーに固定されます。たくさんのユーザーがOHLCを見ることに慣れるので、スペースが意図的に以前作成されました。15の行の間に垂直のスペースを作成しました。これは標準サイズのテキストには十分です。初期化が終了したので、でイニシャライゼーションを作成しましょう。それらはかなり類似しています。:

/////////////////////////////////////////////////////////////////////////////////
// void info_deinit()
//
// Deletion of objects created by the info_init() function
/////////////////////////////////////////////////////////////////////////////////
void info_deinit()
 {
     int _GetLastError;
   for ( int row = 0; row <= 4; row ++ )
      {
          if ( !ObjectDelete( StringConcatenate( "InfoLabel_0", row ) ) )
           {
                   _GetLastError = GetLastError();
       Print( "ObjectDelete( \"", StringConcatenate( "InfoLabel_0", row ), 
                                        "\" ) - Error #", _GetLastError );
      }
          if( !ObjectDelete( StringConcatenate( "InfoLabel_1", row ) ) )
               {
                  _GetLastError = GetLastError();
       Print( "ObjectDelete( \"", StringConcatenate( "InfoLabel_1", row ), 
                                       "\" ) - Error #", _GetLastError );
      }
       }
 }


最も興味深いものとしては、それ自身を表示する情報です。

したがって、それらの場所にて各々であり、表示されているテキストを所持する準備ができている、10のオブジェクトがあります。表示するための関数の使用をできる限りシンプルにする方法を考えてみましょう。

まず、オブジェクト名ではなく、数字を特定するためによりシンプルなものになります。

次に、パラメーター二つのみが使用される必要があります;オブジェクト数と、表示されるテキスト数です。テキストカラーやフォントサイズが変化される必要がないので、その他のパラメーターはオプションになります。ここで、もしパラメーターがスキップされた場合何をすべきか考える必要があります。二つの選択肢があります:

  • 標準のパラメーターを使用する;
  • もしくは、最後に使用されたパラメーターを使用する。

2番目の選択肢の方が便利であると思います:すべてのエキスパートにおいて標準のパラメーターはコード内で変更される必要があり、最後に使用されたパラメーターは自動的に関数が使用されるごとに保存されます。

したがって、関数は以下のようになります;

void info( int LabelNumber, string Text, color Color = -1, 
              double FontSize = -1.0, string Font = "-1" )
 {
     //---- define the object name
      string LabelName;
  if ( LabelNumber < 10 )
          LabelName = StringConcatenate( "InfoLabel_0", LabelNumber );
  else
         LabelName = StringConcatenate( "InfoLabel_" , LabelNumber );
 
  //---- if the additional parameters were not specified, 
    //---- set the last used values for them
    if ( Color < 0 ) 
          Color = lastusedColor;
  if ( FontSize < 0 ) 
     FontSize = lastusedFontSize;
  if ( Font == "-1" ) 
     Font = lastusedFont;
 
  //---- save the last used values
    lastusedColor = Color;
  lastusedFontSize = FontSize;
  lastusedFont = Font;

最後に使用された変数が値としてnullを含む場合を避けるために、宣言時に直ちに値を割り当てましょう:

color lastusedColor = Black;
double lastusedFontSize = 9.0;
string lastusedFont = "Arial";


最後に使用された変数が関数外で宣言されることに気づいたかもしれません。これは、info関数の呼び出し時すべて値が0になることを防ぎます。

それでは、新しいテキストを表示し、オブジェクトを再描画しましょう。これは即座にオブジェクトが表示されるようにするために施した変更において実行されなければなりません:

 //---- display the new text
    if( !ObjectSetText( LabelName, Text, FontSize, Font, Color ) )
      {
        int _GetLastError = GetLastError();
        Print( "ObjectSetText( \"", LabelName,"\", \"", Text, "\", ", FontSize, ", ", Font, 
                                             ", ", Color, " ) - Error #", _GetLastError );
      }
    //---- redraw the objects
    ObjectsRedraw();
  }

これら3つすべての関数の完全なコードは添付されたinfo.mq4というファイルにて確認できます。

それではここまでをチェックしてみましょう;




これはなかなかよいのではないかと思います。

最後に、もう一つ便利なものを作成します - すべての情報を削除する関数です。info_clear()と名付けられます。その使用方法をご存知かと思います。

void info_clear()
 {
     for ( int n = 0;  n < 5;  n ++ ) 
       info( n, "" );
  for (     n = 10; n < 15; n ++ ) 
       info( n, "" );
 }

4. まとめ

この記事はログファイルを保存し、情報を表示するメソッドを紹介しました。log.mq4とinfo.mq4の二つのインクルードファイルが作成され、「experts/include」フォルダに保存されました: これらは、どのエキスパートからでも使用できます。

MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/1405

添付されたファイル |
info.mq4 (4.5 KB)
info_test_eng.mq4 (1.24 KB)
log.mq4 (3.19 KB)
log_test_eng.mq4 (0.76 KB)
Expert Advisor、スクリプト、インディケータの同期 Expert Advisor、スクリプト、インディケータの同期

本稿では Expert Advisor、スクリプト、インディケータが含まれるバンドルプログラムを作成する必要性と一般的原則について考察します。

メカニカル・トレーディングシステム "スタニスラブの三角形" メカニカル・トレーディングシステム "スタニスラブの三角形"

スタニスラブ・チャバショブ(Stanislav Chuvashov)氏のアイデアに基づいたメカニカル・トレーディングシステムの概要とプログラムコードを提供します。三角形の構造は上部・下部フラクタルからなる2本のラインの交差から成っています。

エキスパートシステム「コメンテーター」MQL4プログラムの組み込みインジケーターの実践的な使用 エキスパートシステム「コメンテーター」MQL4プログラムの組み込みインジケーターの実践的な使用

この記事は、MQL4でのプログラミングにおけるテクニカルインジケーターの使用について紹介しています。

オプティマイザでの遺伝的アルゴリズム vs シンプルな検索 オプティマイザでの遺伝的アルゴリズム vs シンプルな検索

この記事は、遺伝的アルゴリズムとシンプルな検索により取得されるものを用いて、エキスパートアドバイザーの最適化を行った結果とかかった時間を比較します。