【MQL5】FileWriteStruct関数の使い方と自動売買実装コード

1. FileWriteStruct関数の概要と実務での活用法

MQL5で効率的なシステムトレード(シストレ)を開発する際、避けて通れないのが「データの保存」です。FileWriteStructは、ユーザーが定義した構造体(struct)の内容を、そのままバイナリ形式でファイルに書き出す関数です。

実務開発において、変数を一つずつFileWriteDoubleFileWriteStringで保存するのは非効率であり、コードも煩雑になります。例えば、「現在保有中のポジションの独自管理データ」や「バックテスト用のカスタム指標データ」を保存する場合、関連する複数の変数を一つの構造体にまとめ、FileWriteStructで一括保存するのが定石です。

しかし、初心者がつまずきやすいポイントとして、「バイナリ形式であるため、メモ帳で開いても中身が読めないこと」や、「動的なデータ(string型や動的配列)を構造体に含めると正しく保存できないこと」が挙げられます。この関数の真価は、高速な読み書きと、データ構造を維持したままの保存にあります。

2. 構文と戻り値

FileWriteStructの構文は非常にシンプルです。

uint FileWriteStruct(
   int          file_handle,   // ファイルハンドル
   const void&  struct_object, // 構造体のインスタンス
   int          size=-1        // 書き込むサイズ(通常は省略)
);

パラメーター

  • file_handle: FileOpen関数によって返されたファイルハンドルを指定します。この際、FILE_BIN(バイナリモード)で開かれている必要があります。
  • struct_object: ファイルに保存したい構造体変数の参照を渡します。
  • size: 書き込むバイト数です。デフォルトの -1 を指定すると、構造体のサイズ(sizeof)が自動的に適用されます。通常は省略して問題ありません。

戻り値

  • 成功した場合、書き込まれたバイト数を返します。
  • 失敗した場合は 0 を返します。詳細なエラーを確認するには GetLastError() を呼び出します。

3. 具体的な使い方・実践サンプルコード

以下の例では、EAが持つ特定のトレード情報を構造体にまとめ、バイナリファイルとして保存する流れを示します。

// --- 保存したい構造体の定義
struct MyTradeLog {
   long     ticket;      // チケット番号
   double   open_price;  // エントリー価格
   datetime open_time;   // エントリー時間
   char     note[32];    // メモ(stringではなく固定長char配列を使うのがコツ)
};

// --- 実践的な保存関数
void SaveTradeData(MyTradeLog &data) {
   string fileName = "TradeData.bin";

   // ファイルを書き込み、バイナリモードで開く
   int fileHandle = FileOpen(fileName, FILE_WRITE | FILE_BIN);

   if(fileHandle != INVALID_HANDLE) {
      // 構造体を丸ごと書き込み
      uint bytesWritten = FileWriteStruct(fileHandle, data);

      if(bytesWritten > 0) {
         Print("データ保存成功: ", bytesWritten, " バイト書き込みました。");
      } else {
         Print("データ保存失敗。エラーコード: ", GetLastError());
      }

      // 必ずファイルを閉じる
      FileClose(fileHandle);
   } else {
      Print("ファイルオープン失敗。");
   }
}

// EAのOnStartなどで使用
void OnStart() {
   MyTradeLog log;
   log.ticket = 12345678;
   log.open_price = 145.50;
   log.open_time = TimeCurrent();
   StringToCharArray("Breakout Trade", log.note); // 文字列をchar配列に変換

   SaveTradeData(log);
}

4. 使用上の注意点とよくあるエラー

FileWriteStructを使用する上で、最も注意すべきは構造体のメンバ変数に「ポインタ」や「動的オブジェクト」を含めないことです。

  1. string型はNG:
    MQL5の string 型は内部的にデータへの「ポインタ(参照先アドレス)」を保持しています。FileWriteStructでそのまま書き込むと、データの本体ではなく、その時のメモリ上の住所だけが保存されます。次にファイルを読み込んだ時には、その住所にデータは存在しないため、EAがクラッシュする原因になります。文字列を保存したい場合は、サンプルコードのように ucharchar の固定長配列を使用してください。
  2. 動的配列はNG:
    double[] などの動的配列も同様です。配列のサイズが可変なものは、構造体の一部として一括保存できません。
  3. FILE_BIN モードの必須:
    FileOpen 時に FILE_CSVFILE_TXT を指定していると、この関数は正しく動作しません。必ずバイナリモードを選択してください。
  4. 構造体のアライメント:
    異なる環境やOS間でファイルを共有する場合、コンパイラによる構造体の詰め物(パディング)の影響で、データのズレが生じることがあります。高度な開発では pack(push, 1) 等を意識する必要がありますが、通常のEA開発であれば、同じPC/ターミナル内で完結するため、それほど神経質になる必要はありません。

5. 【重要】自動売買における約定スピードと環境の罠

シストレ開発において、コードの最適化と同じか、それ以上に重要なのが「実行環境」です。どれほど洗練されたロジックを FileWriteStruct で高速に処理したとしても、自宅のPCや一般的な光回線から注文を出している限り、プロの世界では勝負になりません。

FXの注文がブローカーのサーバーに届くまでには、物理的な距離に起因する「ネットワーク遅延(レイテンシ)」が必ず発生します。自宅PCからの注文は、プロバイダや複数のハブを経由するため、数ミリ秒から数百ミリ秒のロスが生じます。この遅延は、スリッページ(注文価格と約定価格の差)を誘発し、バックテストでは利益が出ていても、リアル口座では損失が積み重なる「環境の罠」を生みます。約定スピードを極限まで高め、物理的な優位性を確保するには、ブローカーのデータセンターに近接した専用のVPS(仮想専用サーバー)の利用が不可欠です。安定した電源と高速なバックボーンを持つVPS環境こそが、シストレエンジニアにとっての真の戦場といえます。

💡 この記事の内容を実運用で活かすには?

この記事の内容を実運用で活かすには、正しい環境が必要です。
特にVPSを使わないと、このロジックは再現できません。

コメント

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