【MQL5】FileWriteLong関数について

MQL5リファレンス
スポンサーリンク
スポンサーリンク

FileWriteLong関数の働き・役割

FileWriteLong関数は、バイナリファイルにlong型の値をファイルポインタの現在位置から書き込むために使用されます。この関数を使うことで、指定したファイルハンドルを持つファイルに、64ビットの整数型データを効率的に保存することができます。

FileWriteLong関数は、主にバイナリデータ(0と1の並び)を扱うファイルで使用され、数値データをファイルに書き込んだり、保存したデータを後で他のプログラムや処理に利用する場合に便利です。例えば、通貨ペアや時間軸に対応するデータ(ボリュームや時間)を保存し、後からファイルを読み込んで分析するなどの用途で使用されます。

FileWriteLong関数の引数について

uint  FileWriteLong(
  int  file_handle,    // ファイルハンドル
  long  value            // 書かれる値
  );

FileWriteLong関数は、2つの引数を取ります。

1. file_handle(ファイルハンドル
この引数は、ファイルを操作するための識別子です。FileOpen関数から返されるファイル記述子を指定します。ファイルハンドルは、どのファイルに対して操作を行うかを指示する役割を果たします。

2. value(書かれる値)
この引数は、ファイルに書き込むlong型の値です。long型は64ビットの整数であり、この値がバイナリ形式でファイルに書き込まれます。

FileWriteLong関数の戻り値について

FileWriteLong関数は、正常に動作した場合に書き込まれたバイト数を返します。long型の値は64ビット(8バイト)であるため、成功時の戻り値は常に8です。

もしファイルに値を書き込む際に何らかの問題が発生した場合、FileWriteLong関数はエラーを発生させ、正しい動作を確認するためには、関数実行後にGetLastError関数を使ってエラーコードを取得することが推奨されます。

また、ファイルポインタは、書き込んだバイト数に応じて自動的に次の位置に移動します。

FileWriteLong関数を使ったサンプルコード

//--- スクリプトの起動時に入力パラメータのウィンドウを表示する設定
#property script_show_inputs

//--- 端末からデータを受け取るパラメータを宣言
input string         InpSymbolName="EURUSD";           // 通貨ペア(例:EURUSD)
input ENUM_TIMEFRAMES InpSymbolPeriod=PERIOD_H1;       // 時間軸(例:1時間足)
input datetime       InpDateStart=D'2013.01.01 00:00'; // データのコピー開始日(例:2013年1月1日 0時0分)

//--- データをファイルに書き込むためのパラメータを宣言
input string         InpFileName="Volume.bin";         // 書き込み先のファイル名(例:Volume.bin)
input string         InpDirectoryName="Data";          // ディレクトリ名(例:Dataディレクトリ)

//+------------------------------------------------------------------+
//| スクリプトプログラムのメイン関数                                  |
//+------------------------------------------------------------------+
void OnStart()
{
  //--- 現在のサーバー時刻を取得し、date_finish変数に保存
  datetime date_finish=TimeCurrent();

  //--- ボリュームと時間を保存するためのバッファを定義
  long     volume_buff[];   // ティックボリュームを保存するための配列
  datetime time_buff[];     // 時間データを保存するための配列
  int      size;            // データのサイズ(配列の要素数)

  //--- 最後に発生したエラーをリセットする
  ResetLastError();

  //--- 指定した通貨ペアと時間軸のティックボリュームデータをコピー
  if(CopyTickVolume(InpSymbolName, InpSymbolPeriod, InpDateStart, date_finish, volume_buff)==-1)
  {
    //--- ティックボリュームのコピーに失敗した場合、エラーメッセージをエキスパートログに表示して処理を終了
    PrintFormat("Failed to copy values of the tick volume. Error code = %d", GetLastError());
    return;  // 処理を終了
  }

  //--- 指定した通貨ペアと時間軸の時間データをコピー
  if(CopyTime(InpSymbolName, InpSymbolPeriod, InpDateStart, date_finish, time_buff)==-1)
  {
    //--- 時間データのコピーに失敗した場合、エラーメッセージをエキスパートログに表示して処理を終了
    PrintFormat("Failed to copy time values. Error code = %d", GetLastError());
    return;  // 処理を終了
  }

  //--- コピーしたデータのサイズ(配列の要素数)を取得
  size = ArraySize(volume_buff);

  //--- データを書き込むためにファイルを開く(存在しない場合は新規作成)
  ResetLastError();  // 最後のエラーをリセット
  int file_handle = FileOpen(InpDirectoryName+"//"+InpFileName, FILE_READ|FILE_WRITE|FILE_BIN);
  
  //--- ファイルが正しく開けたかどうか確認
  if(file_handle != INVALID_HANDLE)
  {
    //--- ファイルが正しく開けた場合、メッセージを表示
    PrintFormat("%s file is available for writing", InpFileName);
    PrintFormat("File path: %s\\Files\\", TerminalInfoString(TERMINAL_DATA_PATH));  // ファイルパスを表示

    //--- 最初にデータのサイズ(サンプル数)を書き込む
    FileWriteLong(file_handle, (long)size);

    //--- 時間とティックボリュームデータをファイルに書き込むループ
    for(int i = 0; i < size; i++)
    {
      //--- 時間データをファイルに書き込む
      FileWriteLong(file_handle, (long)time_buff[i]);
      //--- ティックボリュームデータをファイルに書き込む
      FileWriteLong(file_handle, volume_buff[i]);
    }

    //--- データの書き込みが完了したら、ファイルを閉じる
    FileClose(file_handle);
    PrintFormat("Data is written, %s file is closed", InpFileName);  // 書き込み完了メッセージ
  }
  else
  {
    //--- ファイルが開けなかった場合、エラーメッセージを表示
    PrintFormat("Failed to open %s file, Error code = %d", InpFileName, GetLastError());
  }
}

サンプルコード解説1:グローバル領域部分

//--- スクリプトの起動時に入力パラメータのウィンドウを表示する
#property script_show_inputs

//--- 端末からデータを受け取るパラメータ
input string             InpSymbolName="EURUSD";           // 通貨ペアのシンボル名を指定する。初期値は"EURUSD"
input ENUM_TIMEFRAMES    InpSymbolPeriod=PERIOD_H1;        // 時間軸を指定する。初期値は1時間足(H1)
input datetime           InpDateStart=D'2013.01.01 00:00'; // データのコピーを開始する日時を指定する。初期値は2013年1月1日 00:00

//--- データをファイルに書き込むためのパラメータ
input string             InpFileName="Trend.bin";          // ファイル名を指定する。初期値は"Trend.bin"
input string             InpDirectoryName="Data";          // ディレクトリ名を指定する。初期値は"Data"

このセクションでは、スクリプトのグローバル領域部分について解説します。この領域では、スクリプトの設定やユーザー入力パラメータを定義し、スクリプトの動作に必要な情報を指定しています。

#property script_show_inputs

このディレクティブは、スクリプトの実行時に入力パラメータウィンドウを表示するためのものです。スクリプトが実行されると、ユーザーがカスタマイズできる入力パラメータがウィンドウに表示され、実行前に調整可能となります。

input string InpSymbolName="EURUSD";

この部分は、通貨ペアのシンボル(特定の文字や記号)を指定するための入力パラメータを定義しています。
inputキーワードは、ユーザーが実行前に変更できるパラメータであることを示しています。ここでは、初期値として “EURUSD” が設定されています。このパラメータを使って、どの通貨ペアのデータを処理するかを指定します。

input ENUM_TIMEFRAMES InpSymbolPeriod=PERIOD_H1;

この部分は、時間軸を指定するための入力パラメータを定義しています。
ENUM_TIMEFRAMESは時間軸を定義するための列挙型で、PERIOD_H1は1時間足(H1)を表しています。ユーザーが異なる時間軸(例:5分足、日足など)を指定することで、分析の範囲を変更することができます。

input datetime InpDateStart=D'2013.01.01 00:00';

この部分は、データのコピーを開始する日時を指定するための入力パラメータです。
dateTime型は日付と時刻を表します。ここでは、初期値として “2013年1月1日 00:00” が設定されています。ユーザーは、このパラメータを変更することで、データ取得を開始する日付を設定できます。

input string InpFileName="Trend.bin";

この部分は、データを保存するファイルの名前を指定するための入力パラメータです。
初期値として “Trend.bin” というファイル名が設定されています。このファイルには、トレンドデータがバイナリ形式で書き込まれます。ユーザーはファイル名を変更することが可能です。

input string InpDirectoryName="Data";

この部分は、データを書き込むディレクトリの名前を指定するための入力パラメータです。
初期値として “Data” というディレクトリ名が指定されています。スクリプトは、このディレクトリ内にファイルを作成し、データを書き込みます。ディレクトリ名もユーザーが変更することができます。

このグローバル領域では、ユーザーがスクリプトの動作をカスタマイズするためのパラメータが定義されており、これらのパラメータはスクリプトの主要な処理において使用されます。

サンプルコード解説2:OnStart関数部分その1

//+------------------------------------------------------------------+
//| スクリプトプログラムのメイン関数                                  |
//+------------------------------------------------------------------+
void OnStart()
{
  //--- 現在のサーバー時刻を取得し、date_finish変数に保存
  datetime date_finish=TimeCurrent();

  //--- ボリュームと時間を保存するためのバッファを定義
  long     volume_buff[];   // ティックボリュームを保存するための配列
  datetime time_buff[];     // 時間データを保存するための配列
  int      size;            // データのサイズ(配列の要素数)

  //--- 最後に発生したエラーをリセットする
  ResetLastError();

このセクションでは、スクリプトのメイン処理であるOnStart関数の前半部分について詳しく解説します。

datetime型の変数date_finishの取得

まず、TimeCurrent関数により、現在のサーバー時間をdate_finishという変数に保存しています。TimeCurrent関数は、サーバー側で最後に知られている時刻を取得する関数で、MQL5でよく使われる時刻情報の取得方法です。このサーバー時間を基準として、後続のデータ処理やコピー操作が行われます。

バッファの定義

続いて、long型のティックボリュームと時間データを保存するための配列バッファ)が宣言されています。

  • volume_buffは、ティックボリューム(1本のバーに含まれるティックの数)を保存するための配列です。ティックとは、金融市場における価格の変動を指します。
  • time_buffは、各バー(足)の時間データを保存するための配列です。これは各バーが形成された時刻の情報を保持します。

これらのバッファは、後で取得する値を一時的に保存しておくために使用され、後続の処理でファイルに書き込まれるデータとなります。

エラーのリセット

次に、ResetLastError関数を使用して最後に発生したエラー情報をリセットしています。MQL5では、関数が実行される際にエラーが発生した場合、そのエラーコードは内部的に保持されます。このリセット操作は、後続の処理でエラーが発生したかどうかを正確に判断できるようにするための重要な操作です。

これにより、今後のエラー処理が確実に新しいエラーに基づいて行われるようになります。

サンプルコード解説3:OnStart関数部分その2

 //--- 指定した通貨ペアと時間軸のティックボリュームデータをコピー
  if(CopyTickVolume(InpSymbolName, InpSymbolPeriod, InpDateStart, date_finish, volume_buff)==-1)
  {
    //--- ティックボリュームのコピーに失敗した場合、エラーメッセージをエキスパートログに表示して処理を終了
    PrintFormat("Failed to copy values of the tick volume. Error code = %d", GetLastError());
    return;  // 処理を終了
  }

  //--- 指定した通貨ペアと時間軸の時間データをコピー
  if(CopyTime(InpSymbolName, InpSymbolPeriod, InpDateStart, date_finish, time_buff)==-1)
  {
    //--- 時間データのコピーに失敗した場合、エラーメッセージをエキスパートログに表示して処理を終了
    PrintFormat("Failed to copy time values. Error code = %d", GetLastError());
    return;  // 処理を終了
  }

このセクションでは、OnStart関数の中でティックボリュームデータと時間データをコピーする処理について詳しく解説します。

ティックボリュームデータのコピー

最初に、指定された通貨ペアと時間軸に対して、ティックボリュームデータを取得します。この操作は、CopyTickVolume関数を使用して行われます。この関数は、指定された期間内の各バー(足)に対応するティックボリュームデータをコピーし、指定された配列(ここではvolume_buff)に保存します。

もしデータのコピーに失敗した場合、CopyTickVolume関数は-1を返します。その場合、エラーコードをエキスパートログに表示し、処理を終了します。エラーコードはGetLastError関数を使って取得され、問題の原因を特定するために役立ちます。例えば、指定した通貨ペアや時間軸が間違っていた場合や、サーバーに接続できなかった場合などが考えられます。

時間データのコピー

次に、指定された通貨ペアと時間軸に対応する時間データを取得します。この処理は、CopyTime関数を使用して行われ、時間データがtime_buffという配列にコピーされます。

CopyTickVolume関数と同様に、CopyTime関数が-1を返した場合は、時間データのコピーに失敗したことを示します。この場合も、エラーコードをエキスパートログに表示し、処理を終了します。エラーが発生した理由は、前述のように、通貨ペアや時間軸の指定が正しくない場合や、他の通信エラーなどが考えられます。

どちらのデータも正しく取得されないと後続の処理が意味を成さないため、これらのエラーチェックは重要です。

サンプルコード解説4:OnStart関数部分その3


  //--- コピーしたデータのサイズ(配列の要素数)を取得
  size = ArraySize(volume_buff);

  //--- データを書き込むためにファイルを開く(存在しない場合は新規作成)
  ResetLastError();  // 最後のエラーをリセット
  int file_handle = FileOpen(InpDirectoryName+"//"+InpFileName, FILE_READ|FILE_WRITE|FILE_BIN);
  

このセクションでは、取得したデータのサイズを確認し、それをファイルに書き込む準備をする部分について解説します。

データのサイズ取得

まず、取得したティックボリュームデータのサイズ、つまり配列要素数をArraySize関数を使用して取得しています。ここでは、volume_buff配列要素数を取得し、そのサイズをsize変数に格納しています。この操作により、後にファイルに書き込むデータの数がわかります。

ArraySize関数は、配列要素数を返す関数で、データのサイズを確認する際に使用されます。この場合、ティックボリュームと対応する時間データが同じ数であることが前提なので、volume_buff配列のサイズを確認しています。

ファイルのオープン

次に、ファイルを開いてデータを書き込む準備をします。この処理ではFileOpen関数を使っています。FileOpen関数は、指定されたファイルが存在する場合にそれを開き、存在しない場合は新規作成します。この場合、InpDirectoryNameで指定されたディレクトリにInpFileNameで指定されたファイル名でファイルが作成されます。

オープン時には、ファイルがバイナリ形式(FILE_BIN)で読み書き(FILE_READ|FILE_WRITE)されるように指定されています。このバイナリ形式は、数値データを効率的に書き込む場合に使用され、特に大規模なデータの処理や高速な読み書きが必要な場合に適しています。

最後に、FileOpen関数戻り値をfile_handle変数に格納し、後でそのファイルハンドルを使ってデータの書き込みを行います。もしファイルを開くのに失敗した場合、file_handleには無効なハンドルINVALID_HANDLE)が返されます。この後の処理でファイルハンドルが無効かどうかを確認することで、ファイル操作が正常に行えるかどうかを判断します。

ファイルを開く前に、ResetLastError関数を呼び出して、過去のエラー状態をリセットしている点も重要です。これにより、ファイル操作で発生する可能性のあるエラーを正確にキャッチできるようになります。

サンプルコード解説5:OnStart関数部分その4

  if(file_handle != INVALID_HANDLE)
  {
    //--- ファイルが正しく開けた場合、メッセージを表示
    PrintFormat("%s file is available for writing", InpFileName);
    PrintFormat("File path: %s\\Files\\", TerminalInfoString(TERMINAL_DATA_PATH));  // ファイルパスを表示

    //--- 最初にデータのサイズ(サンプル数)を書き込む
    FileWriteLong(file_handle, (long)size);

    //--- 時間とティックボリュームデータをファイルに書き込むループ
    for(int i = 0; i < size; i++)
    {
      //--- 時間データをファイルに書き込む
      FileWriteLong(file_handle, (long)time_buff[i]);
      //--- ティックボリュームデータをファイルに書き込む
      FileWriteLong(file_handle, volume_buff[i]);
    }

    //--- データの書き込みが完了したら、ファイルを閉じる
    FileClose(file_handle);
    PrintFormat("Data is written, %s file is closed", InpFileName);  // 書き込み完了メッセージ
}

このセクションでは、ファイルにデータを書き込む具体的な処理と、その後のファイル操作の終了について解説します。

ファイルのオープン確認

まず、file_handleがINVALID_HANDLEでないことを確認しています。これは、FileOpen関数でファイルが正常に開けたかどうかをチェックする重要な部分です。もしファイルが正しく開けた場合、指定されたファイル名が書き込み可能であることを確認するメッセージを表示しています。

PrintFormat関数を使ってファイルのパスも表示していますが、TerminalInfoString関数を使用して、MetaTrader5のデータフォルダへのパスを取得しています。これにより、ファイルがどこに保存されているかをユーザーに通知します。

データサイズの書き込み

次に、最初にデータのサイズ(配列要素数)を書き込んでいます。これは、後でファイルを読み込む際に、どれだけのデータが保存されているのかを確認するための重要な情報です。
FileWriteLong関数を使用して、long型のデータとしてサイズを書き込みます。この処理により、ファイルの先頭にサンプル数(データのサイズ)が格納されます。

時間データとティックボリュームデータの書き込み

続いて、時間データとティックボリュームデータをループを使って順次ファイルに書き込んでいきます。forループは、データのサイズに応じて繰り返し処理を行い、1つずつデータをファイルに書き込んでいきます。

これにより、各時間に対応するティックボリュームがペアとしてファイルに保存されます。

ファイルのクローズ

すべてのデータを書き込んだ後、FileClose関数を使用してファイルを閉じます。ファイルを開いたら必ず閉じる必要があり、これによりファイルが正しく保存され、他のプログラムやプロセスからアクセス可能になります。

最後に、データが正常に書き込まれたことを示すメッセージをエキスパートログに出力しています。これにより、ユーザーはファイルが正しく書き込まれ、操作が完了したことを確認できます。

サンプルコード解説6:OnStart関数部分その5

 else
  {
    //--- ファイルが開けなかった場合、エラーメッセージを表示
    PrintFormat("Failed to open %s file, Error code = %d", InpFileName, GetLastError());
  }
}

このセクションでは、ファイルのオープンに失敗した場合のエラーハンドリングについて解説します。

ファイルのオープン失敗時の処理

もし、FileOpen関数でファイルのオープンに失敗した場合、file_handleには無効なハンドルINVALID_HANDLE)が格納されます。このケースを考慮し、elseブロックでエラーハンドリングを行います。

エラーハンドリングでは、PrintFormat関数を使用して、ユーザーにファイルが開けなかったことを通知するメッセージをエキスパートログに表示しています。メッセージには、開こうとしたファイル名(InpFileName)と、GetLastError関数を使用して取得したエラーコードが含まれます。

GetLastError関数の役割

GetLastError関数は、直前に発生したエラーコードを取得するための関数です。このエラーコードは、なぜファイルを開けなかったのかを判断するために非常に重要です。例えば、指定したディレクトリが存在しない、ファイル名に問題がある、またはファイルシステムにアクセス権限がないといった原因が考えられます。エラーコードを調べることで、原因を特定し、修正が可能になります。

タイトルとURLをコピーしました