1. FileWriteStruct関数の概要と実務での活用法
MQL5で効率的なシステムトレード(シストレ)を開発する際、避けて通れないのが「データの保存」です。FileWriteStructは、ユーザーが定義した構造体(struct)の内容を、そのままバイナリ形式でファイルに書き出す関数です。
実務開発において、変数を一つずつFileWriteDoubleやFileWriteStringで保存するのは非効率であり、コードも煩雑になります。例えば、「現在保有中のポジションの独自管理データ」や「バックテスト用のカスタム指標データ」を保存する場合、関連する複数の変数を一つの構造体にまとめ、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を使用する上で、最も注意すべきは構造体のメンバ変数に「ポインタ」や「動的オブジェクト」を含めないことです。
- string型はNG:
MQL5のstring型は内部的にデータへの「ポインタ(参照先アドレス)」を保持しています。FileWriteStructでそのまま書き込むと、データの本体ではなく、その時のメモリ上の住所だけが保存されます。次にファイルを読み込んだ時には、その住所にデータは存在しないため、EAがクラッシュする原因になります。文字列を保存したい場合は、サンプルコードのようにucharやcharの固定長配列を使用してください。 - 動的配列はNG:
double[]などの動的配列も同様です。配列のサイズが可変なものは、構造体の一部として一括保存できません。 - FILE_BIN モードの必須:
FileOpen時にFILE_CSVやFILE_TXTを指定していると、この関数は正しく動作しません。必ずバイナリモードを選択してください。 - 構造体のアライメント:
異なる環境やOS間でファイルを共有する場合、コンパイラによる構造体の詰め物(パディング)の影響で、データのズレが生じることがあります。高度な開発ではpack(push, 1)等を意識する必要がありますが、通常のEA開発であれば、同じPC/ターミナル内で完結するため、それほど神経質になる必要はありません。
5. 【重要】自動売買における約定スピードと環境の罠
シストレ開発において、コードの最適化と同じか、それ以上に重要なのが「実行環境」です。どれほど洗練されたロジックを FileWriteStruct で高速に処理したとしても、自宅のPCや一般的な光回線から注文を出している限り、プロの世界では勝負になりません。
FXの注文がブローカーのサーバーに届くまでには、物理的な距離に起因する「ネットワーク遅延(レイテンシ)」が必ず発生します。自宅PCからの注文は、プロバイダや複数のハブを経由するため、数ミリ秒から数百ミリ秒のロスが生じます。この遅延は、スリッページ(注文価格と約定価格の差)を誘発し、バックテストでは利益が出ていても、リアル口座では損失が積み重なる「環境の罠」を生みます。約定スピードを極限まで高め、物理的な優位性を確保するには、ブローカーのデータセンターに近接した専用のVPS(仮想専用サーバー)の利用が不可欠です。安定した電源と高速なバックボーンを持つVPS環境こそが、シストレエンジニアにとっての真の戦場といえます。
💡 この記事の内容を実運用で活かすには?
この記事の内容を実運用で活かすには、正しい環境が必要です。
特にVPSを使わないと、このロジックは再現できません。

コメント