HistorySelect/HistoryDealGetDoubleで勝率を計算する記事では、
過去の約定履歴から後追いで勝率や損益を集計する方法を解説しました。
ですがこの方法には弱点があります。
MT5端末の履歴データが何らかの理由で欠けていたり、別の端末に乗り換えたりすると、
過去の記録を再現できません。
この記事では、決済が起きた瞬間にその場でCSVファイルへ1行ずつ追記していく、
自分だけのログを残す方法を解説します。
`FileOpen`・`FileWrite`・`FileClose`という
MQL5のファイル操作関数の基本と、
MQL4版と比べて何が変わったのかまで扱います。
なぜ自動売買にファイル出力のログが必要なのか
バックテストのレポートは、テスト終了後にまとめて生成されるものであり、
実運用中にリアルタイムで内容を確認することはできません。
またEAが`Print()`や`Alert()`で出力する内容は、
MT5端末を閉じたりログの保持件数を超えたりすると消えてしまいます。
ファイルへの書き出しなら、EAを再起動してもMT5を再インストールしても、
CSVファイル自体をコピーして残しておく限り記録が消えません。
この記事で作るもの: ポジションが決済されるたびに、
決済時刻・銘柄・方向・ロット数・決済損益の5項目を1行として、
CSVファイルの末尾に自動で追記するEAです。
FileOpenでファイルを開く:引数とフラグの意味
ファイルへの読み書きは、必ず`FileOpen()`でファイルを「開く」
ところから始まります。
公式リファレンス(FileOpen)によると、シグネチャは次の通りです。
int FileOpen( string file_name, //ファイル名 int open_flags, //開き方を指定するフラグの組み合わせ short delimiter='\t', //CSVモードでの区切り文字(省略時はタブ) uint codepage=CP_ACP //文字コード(省略時は既定のANSI) );
戻り値は、成功すればファイルハンドル(`int`)、
失敗すれば`INVALID_HANDLE`です。
2番目の引数`open_flags`は、
複数のフラグを`|`(ビットOR)でつなげて指定します。
この記事で使うのは次の組み合わせです。
FILE_WRITE | FILE_CSV | FILE_ANSI
用語: FILE_CSVは
「区切り文字で区切られたテキスト」として読み書きするモードです。
FILE_WRITEと組み合わせると、FileWrite()で渡した複数の値を
自動的にカンマ区切り+改行で書き込んでくれます。
`FILE_ANSI`は文字コードを1バイト文字(ANSI)として扱う指定です。
これを付けずに書き込むと、既定の`FILE_UNICODE`扱いとなり、
Excelなどの一部のソフトで文字化けして見えることがあるため、
日本語を含むログでは明示的に`FILE_ANSI`を付けることを推奨します。
FileWriteで1行書き込む仕組み
ファイルを開いたら、`FileWrite()`で実際にデータを書き込みます。
uint FileWrite(int file_handle, ...);
第1引数はハンドル、それ以降は書き込みたい値を
カンマ区切りで好きなだけ並べられます(最大63個)。
`FILE_CSV`モードでは、渡した値の間に自動でカンマ(区切り文字)が挿入され、
呼び出しの最後に自動で改行(`\r\n`)が付きます。
つまり「この`FileWrite()`呼び出し1回=CSVの1行」
と考えれば分かりやすいです。
//この1回の呼び出しで、CSVの1行分が書き込まれる FileWrite(handle, "2026.09.25 10:00:00", "USDJPY", "買い決済", "0.10", "1250.00");
既存ファイルへの追記と新規作成を両立する書き方
ログは1回書いて終わりではなく、決済のたびに追記していく必要があります。
そのために使うのが`FileIsExist()`と`FileSeek()`です。
bool FileIsExist(string file_name, int common_flag=0); bool FileSeek(int file_handle, long offset, int origin);
`FileIsExist()`でファイルがまだ存在しないと分かった初回だけ、
見出し行(ヘッダー)を書き込みます。
2回目以降は`FileSeek(handle, 0, SEEK_END)`で
ファイルの末尾に移動してから、`FileWrite()`で1行追記します。
注意: FILE_WRITEだけを指定してFileOpen()すると、
開くたびにファイルの中身が空にリセットされます。
追記したい場合は必ずFILE_READ|FILE_WRITEを組み合わせ、
FileSeek()で末尾に移動してから書き込んでください。
これを忘れると、EAを再起動するたびに過去のログが消える事故になります。
実際に動くコード全体:決済のたびにCSVへ1行追記するEA
OnTradeTransactionの記事で解説した、
取引イベントを検知する仕組みと組み合わせます。
決済の約定(Deal)が発生した瞬間だけを捉えて、
CSVへ1行追記する実際にMetaEditorでコンパイルできるコードです。
//+------------------------------------------------------------------+
//| DealLogWriterMql5.mq5 |
//| Copyright 2026, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link "https://creator.fx-ea-system-project.com/"
#property version "1.00"
input string LogFileName = "deal_log.csv";//保存するCSVファイル名
//+------------------------------------------------------------------+
//| ログファイルの先頭にヘッダー行を書き込む(初回のみ) |
//+------------------------------------------------------------------+
void WriteHeaderIfNeeded()
{
if(FileIsExist(LogFileName))
return;//既にファイルがあればヘッダーは書かない
int handle = FileOpen(LogFileName, FILE_WRITE|FILE_CSV|FILE_ANSI, ',');
if(handle == INVALID_HANDLE)
{
Print("ログファイルのヘッダー作成に失敗しました。エラーコード=", GetLastError());
return;
}
FileWrite(handle, "決済時刻", "銘柄", "方向", "ロット", "決済損益");
FileClose(handle);
}
//+------------------------------------------------------------------+
//| 決済済みの1件を末尾に追記する |
//+------------------------------------------------------------------+
void AppendDealLog(datetime dealTime, string symbol, string direction, double lots, double profit)
{
int handle = FileOpen(LogFileName, FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI, ',');
if(handle == INVALID_HANDLE)
{
Print("ログファイルを開けませんでした。エラーコード=", GetLastError());
return;
}
FileSeek(handle, 0, SEEK_END);//末尾に移動してから追記する
FileWrite(handle, TimeToString(dealTime, TIME_DATE|TIME_SECONDS), symbol, direction,
DoubleToString(lots, 2), DoubleToString(profit, 2));
FileClose(handle);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
WriteHeaderIfNeeded();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| 約定(Deal)が追加されるたびに呼ばれる |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(trans.type != TRADE_TRANSACTION_DEAL_ADD)
return;
if(!HistoryDealSelect(trans.deal))
return;
long entry = HistoryDealGetInteger(trans.deal, DEAL_ENTRY);
if(entry != DEAL_ENTRY_OUT)
return;//新規エントリーではなく決済の約定だけを記録する
string symbol = HistoryDealGetString(trans.deal, DEAL_SYMBOL);
double lots = HistoryDealGetDouble(trans.deal, DEAL_VOLUME);
double profit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT);
long dealType = HistoryDealGetInteger(trans.deal, DEAL_TYPE);
//DEAL_TYPE_SELLの決済取引は「買いポジションを閉じた」ことを意味する(逆も同様)
string direction = (dealType == DEAL_TYPE_SELL) ? "買い決済" : "売り決済";
AppendDealLog(TimeCurrent(), symbol, direction, lots, profit);
}
//+------------------------------------------------------------------+
MetaEditorで実際に動かす手順
- MT5の「ツール」→「MetaQuotes Language Editor」でMetaEditorを開く
- 「ファイル」→「新規作成」→「Expert Advisor(テンプレートを使用)」を選び、名前を「DealLogWriterMql5」にして作成する
- テンプレートの中身を全て削除し、上のコードを丸ごと貼り付ける
- F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
- デモ口座のチャートへドラッグ&ドロップし、成行注文を1回出して決済し、「ファイル」→「データフォルダを開く」→「MQL5/Files」に
deal_log.csvが作成されているか確認する
MQL4版との違い:既定の区切り文字がそもそも違う
MQL4にも同名の`FileOpen()`・`FileWrite()`があり、
`FILE_READ`・`FILE_WRITE`・`FILE_CSV`・
`FILE_ANSI`・`FILE_UNICODE`といったフラグ自体は、
MQL4/MQL5でほぼ共通です。
見落としやすいのは、`delimiter`(区切り文字)引数を省略したときの既定値です。
| 項目 | MQL4 | MQL5 |
|---|---|---|
| FileOpenの引数 | file_name, open_flags, delimiter=’;’, codepage=CP_ACP | file_name, open_flags, delimiter=’\t’, codepage=CP_ACP |
| delimiter省略時の既定値 | セミコロン( ; ) | タブ( \t ) |
| 共有端末間でのファイル共有 | FILE_COMMONで対応 | FILE_COMMONで対応(考え方は共通) |
考え方自体はMQL4とMQL5で大きくは変わっていませんが、
区切り文字を明示的に指定せずに`FileOpen()`を呼ぶと、
MQL4版とMQL5版で列の区切られ方が変わってしまいます。
この記事のコードのように、常に`FileOpen()`の第3引数へ
`,`(カンマ)を明示的に渡しておけば、
この既定値の違いに引っかかることはありません。
コピペしてコンパイルが通らない・ログが出力されないときに確認すること
- コンパイルエラーになる: MQL5では`FileOpen()`の第2引数に古いMQL4形式の定数(数値そのもの)を渡すとエラーになる。必ず`FILE_WRITE`等の定数を`|`でつなげて渡す
- ファイルは作られるが中身が空になる: `FILE_READ`を付けずに`FILE_WRITE`だけで開いていないか確認する。この場合、開くたびに中身がリセットされる
- ログが1行しか残らない・上書きされる: `FileSeek(handle, 0, SEEK_END)`を呼ばずに書き込んでいないか確認する
- Excelで開くと日本語が文字化けする: `FILE_ANSI`を付けているか、またはExcel側の文字コード認識設定を確認する
- 「決済のたびに追記」のはずが記録されない: `DEAL_ENTRY`が`DEAL_ENTRY_OUT`(決済)ではなく`DEAL_ENTRY_IN`(新規)のまま判定していないか確認する
よくある質問
ファイル操作でよく検索されている疑問を、
3つにまとめて回答します。
保存したCSVファイルはどこにありますか?
MT5端末の「ファイル」→「データフォルダを開く」から、
MQL5/Filesフォルダの中に保存されます。
FILE_COMMONフラグを付けた場合は、
共通フォルダ(Terminal/Common/Files)に保存されます。
EAではなくインジケーターからファイルに書き込めますか?
できますが、インジケーターは複数のチャートに重複して読み込まれることがあり、
同じファイルへ同時に書き込もうとして競合する可能性があります。
ログ出力の用途では、口座全体の取引を管理しやすいEA側で実装する方が安全です。
FileWriteとFileWriteStringは何が違いますか?
FileWrite()はCSV/TXTモードで、
複数の値を区切り文字付きで一括して書き込みます。
FileWriteString()は1つの文字列だけをそのまま書き込む、
より単純な関数です。
この記事のように「1行=複数項目」を書きたい場合は
FileWrite()が適しています。
あわせて読みたい: 【MQL4】決済ログを自動でCSVに残す。FileOpen/FileWriteの使い方(MQL5版のOnTradeTransactionとの違い)
【MQL5入門】EAの決済ログを自動でCSVに残す。FileOpen/FileWrite/FileCloseの使い方(MQL4版との違い):まとめ
この記事では、MQL5のFileOpen・FileWrite・FileCloseを使って、
決済が起きるたびにCSVファイルへ1行ずつログを追記するEAを解説しました。
ポイントは、FILE_READ|FILE_WRITEで開いたうえで
FileSeek()で末尾に移動してから書き込むことと、
日本語を含むログではFILE_ANSIを明示することです。
このログの仕組みは、HistorySelect/HistoryDealGetDoubleで勝率を計算する記事と組み合わせることで、
過去の履歴データに頼らず、
自分自身の手でリアルタイムに取引記録を残す仕組みとして活用できます。
コメント