MQL4のFileOpenでFX自動売買のCSVを保存・読込する方法

MQL4のFileOpenでCSVを保存し読み込む説明用イメージ

MQL4のFileOpenは、CSVなどのファイルを開き、読み書きに使うハンドルを受け取る関数です。FX自動売買の検証値や独自の設定値を保存するときに使えますが、保存先の制約と開き方を決めずに書くと、ファイルが見つからない、既存の内容が消えるといった原因になります。

最初に区別したいのは、読込専用、内容を作り直す書込み、既存の内容を残す追記です。CSVという拡張子だけでは、区切り文字や文字コードも決まりません。保存する側と読む側で、同じ形式を決めておく必要があります。

この記事ではWindows版MT4のMQL4を対象に、保存先とフラグの意味を整理し、専用の学習用CSVを書いて読み戻す例を示します。注文処理や口座情報の出力は含めず、既存のユーザーファイルを学習対象にしない構成です。

この記事のポイント
  • 通常・テスター・FILE_COMMONの保存先を区別する
  • 再作成と追記をフラグとファイル位置で分ける
  • 区切り文字とUTF-8の指定を保存側と読込側でそろえる
  • 学習用CSVを読み戻し開く失敗と内容の不備を分ける
目次

MQL4のFileOpenで保存先と開き方を決める

MQL4でCSVを入出力する説明用イメージ
説明用の生成イメージ

ファイルは許可された保存先で扱う

MQL4の通常のファイル操作には、扱える場所を限定するファイルサンドボックスがあります。普段使っているデスクトップや任意のフォルダの絶対パスをFileOpenへ渡せばよい、という仕組みではありません。まず、そのプログラムが動く環境の保存先を確認します。

スクロールできます
実行環境・指定基本の保存先確認する点
通常の端末内での操作データフォルダのMQL4\Files実際に動かしているMT4のデータフォルダか
MT4のストラテジーテスターTester\Files通常側へ置いたファイルと混同していないか
FILE_COMMONを指定Terminal\Common\Files共通領域を使う目的と対象端末が明確か

保存先の根拠はMQL4公式のファイル関数一覧とFileOpenの仕様です。通常側の場所はMT4の「ファイル」から「データフォルダを開く」で確認し、その中のMQL4、Filesへ進みます。複数のMT4を使っていると、別の端末の同名ファイルを見ていることがあるので注意してください。

FileOpenへは、その保存領域からの相対的な名前を渡します。たとえば学習用サブフォルダのsample.csvを使うなら、コード上では”fileopen-learning\\sample.csv”という形です。文字列内でバックスラッシュを二つ書くのは、区切りとして一つのバックスラッシュを表すためです。

書込みとして開く場合、名前に含めたサブフォルダが存在しなければ作成されると公式資料に説明されています。ただし、何でも任意の外部フォルダへ作成できるという意味ではありません。通常・テスター・共通のどの領域を基準にしているかを固定してから、相対名を決めましょう。

通常とテスターと共通保存先の説明用イメージ
説明用の生成イメージ

読込と再作成ではフラグが異なる

FileOpenの第二引数には、ファイルをどう開くかを表すフラグを指定します。読込・書込の権限と、CSVや文字コードの指定は役割が違います。まずデータを残したいのか、作り直したいのかを決め、その目的に合う組み合わせにします。

スクロールできます
主な指定存在するファイル存在しないファイル
FILE_READ読込用に開く開けずに失敗する
FILE_WRITEサイズ0で再作成する新しく作成する
FILE_READ|FILE_WRITE読書き用に開く新しく作成する

特にFILE_WRITE単独の扱いが重要です。「書けるようにするだけ」と考えて既存CSVへ指定すると、内容を保持する開き方にはなりません。公式のファイルオープンフラグでは、FILE_WRITEはファイルをサイズ0で再作成すると説明されています。学習例を試すときに、普段使う設定ファイル名へ置き換えないでください。

FILE_READ|FILE_WRITEなら、既存ファイルを開くか、新規に作成します。ただし、それだけで末尾への追記が指定されたわけではありません。どこへ書くかはファイル位置も含めて管理します。存在する内容を残す目的なら、フラグと位置の両方を確認する必要があります。

FILE_CSVはCSVとして扱う形式の指定です。FILE_READやFILE_WRITEの代わりにはならないため、CSVで読み込むならFILE_READ|FILE_CSVのように組み合わせます。拡張子が.csvだから、必要な権限や形式がすべて自動で決まるわけではありません。

追記するときは末尾へ移動する

追記では、ファイルを読書き用に開いたあと、書く位置を末尾へ移します。位置を変更するのがFileSeekで、末尾から0バイトの位置ならFileSeek(handle, 0, SEEK_END)という指定です。移動に成功したことを確認してからFileWriteへ進みます。

// h is a valid handle opened with FILE_READ|FILE_WRITE.
ResetLastError();
if(!FileSeek(h, 0, SEEK_END))
{
   Print("Seek failed: ", GetLastError());
   // Close h and stop this operation.
}

これは追記位置を示す部分例で、単独で動くプログラムではありません。FileSeekの公式仕様では、位置の移動はバイト単位で、成功・失敗をboolで返します。CSVの「3行目」を指定する関数ではないため、文字数をそのままバイト位置と考える使い方は避けます。

ヘッダーは新規ファイルの最初に一度だけ書きます。追記のたびにヘッダーも加えると、読み込む側で途中の行がデータなのか列名なのかを区別しなければなりません。また、既存ファイルが想定した形式か分からない場合は、末尾へ足す前にヘッダー・列数・区切り文字を確認してください。

読込と再作成と追記を区別する説明用イメージ
説明用の生成イメージ

区切り文字と文字コードをそろえる

FileOpenでCSVの区切り文字を省略すると、既定値はセミコロンです。名前にCSVと付くから常にカンマ区切りになる、とは考えません。カンマ区切りにしたい場合は、第三引数にカンマの文字を明示し、読込時にも同じ指定を使います。

UTF-8で保存・読込する例では、FILE_CSVとFILE_ANSIを組み合わせ、第四引数にCP_UTF8を指定します。FILE_ANSIという名前だけを見るとUTF-8と違うように感じますが、ここではコードページで変換する指定と組み合わせて使います。公式のコードページ一覧ではCP_UTF8がUTF-8として定義されています。

// Read a comma-delimited UTF-8 CSV.
int h = FileOpen(name, FILE_READ|FILE_CSV|FILE_ANSI, ',', CP_UTF8);

FILE_UNICODEを付けた場合や、文字コードのフラグを省略した場合の既定動作とは分けます。FILE_ANSIとFILE_UNICODEを同時に付けて調整するのではなく、読みたいファイルの仕様を先に決めてください。既存ファイルは、先頭の識別情報などによって読み方が影響される場合もあり、文字コードの変更を推測で繰り返さない方が確実です。

列が一つにつながる場合は区切り文字、文字が崩れる場合は文字コードというように、症状を分けます。ただし、表計算ソフトが自動判定した見た目だけでMQL4の入出力成功とは判断しません。CSVの実データと、読み込む側が指定した形式を照合します。表示側の復旧はMT4の文字化けを直す確認順で別に扱っています。

区切り文字と文字コードをそろえる説明用イメージ
説明用の生成イメージ

ハンドルを確認してから処理を進める

FileOpenが成功すると、後続の読書きで使うファイルハンドルが返ります。失敗したときはINVALID_HANDLEです。返ってきた値を確認せずFileReadStringやFileWriteへ進めると、最初に開けなかった問題と、その後の読書きエラーを混ぜてしまいます。

操作前にResetLastErrorを呼び、失敗を検出した直後にGetLastErrorを読むと、どの操作の問題かを整理しやすくなります。ハンドルは取得できたが列名が違う場合は「開く失敗」ではなく「内容の形式が想定と違う」問題です。両者を同じエラーメッセージへまとめないようにします。

有効なハンドルを受け取ったあと、途中で処理を中断する場合にもFileCloseを呼びます。成功時だけ閉じるコードにすると、後片付けを忘れやすくなります。FileCloseの公式説明が示すように、閉じる対象はFileOpenで得たハンドルです。ファイル名の文字列を渡すものではありません。

MQL4のFileOpenをFX自動売買のCSVで試す

既存ファイルから分けた学習用CSVの説明用イメージ
説明用の生成イメージ

専用の学習用CSVへ書き込む

ここからは、一度だけ実行するMQL4スクリプトで書込みと読み戻しを試します。EAのOnTick内で繰り返し実行するコードではありません。二つのコードブロックを同じmq4ファイルへ入れる構成で、最初のOnStartから、後に示すReadLearningCsvを呼びます。

例では専用サブフォルダと実行時の一時名を使い、同名ファイルがあれば停止します。開いたあとも空であることを確かめ、既存データへ書き足しません。同時に複数起動しない学習用途で使い、任意のユーザーファイル名を入力する構成にはしていません。存在確認は、複数の書込み元を調停する排他制御の代わりにはなりません。

コードは説明用で、この記事の作成時点では実機でのコンパイル・動作を未確認です。デモ環境で内容を読んでから試し、既存のEAへそのまま追記しないでください。口座番号、認証情報、注文内容は出力せず、固定の学習用文字列と仮の値だけを扱います。

#property strict
void OnStart()
{
   string name = "fileopen-learning\\sample-"
      + IntegerToString((int)TimeLocal()) + "-"
      + IntegerToString((int)GetTickCount()) + ".csv";
   if(FileIsExist(name))
   { Print("Name already exists. Stop."); return; }

   ResetLastError();
   int h = FileOpen(name, FILE_READ|FILE_WRITE|FILE_CSV|FILE_ANSI,
                    ',', CP_UTF8);
   if(h == INVALID_HANDLE)
   { Print("Open failed: ", GetLastError()); return; }
   if(FileSize(h) != 0)
   { Print("Not empty. Stop."); FileClose(h); return; }

   if(FileWrite(h, "item", "value") == 0
      || FileWrite(h, "sample_a", "12.50") == 0
      || FileWrite(h, "sample_b", "9.00") == 0)
   {
      Print("Write failed: ", GetLastError());
      FileClose(h);
      return;
   }
   FileFlush(h);
   FileClose(h);
   Print("Learning file: ", name);
   ReadLearningCsv(name);
}
CSVへ列と値を書き込む説明用イメージ
説明用の生成イメージ

FILE_WRITE単独で既存ファイルを再作成する例にはせず、読書き用に開いて空であることを条件にしています。専用の一時名は、普段使うCSVと切り離すためです。出力名が変わるので、実行後は表示された名前と、実際に確認しているファイル名を合わせてください。

FileWriteには、一つの行の項目を別々の引数で渡します。ここではitem,valueというヘッダーと、二つのデータ行を書きます。FileWriteの公式仕様では、区切り文字と行末の改行が付与され、戻り値は書き込んだバイト数、失敗時は0です。例でもこの戻り値を確認しています。

12.50と9.00は学習用の文字列で、相場の記録やEAの推奨設定ではありません。実際の小数を出力する場合、必要な桁数を決めてDoubleToStringなどで形式を明示します。保存できたことだけで、他のプログラムが同じ型・精度として正しく読めるとは判断しないようにします。

同じ形式で読み戻して列を確認する

次が同じファイルへ追加する読込用関数です。書込みを閉じてから、FILE_READで開き直します。ヘッダーはitem,valueの二列という前提で確認し、その後は二列ずつ読みます。任意のCSVへ対応する汎用パーサーではなく、直前のコードが作った単純な学習用ファイルが対象です。

void ReadLearningCsv(const string name)
{
   ResetLastError();
   int h = FileOpen(name, FILE_READ|FILE_CSV|FILE_ANSI, ',', CP_UTF8);
   if(h == INVALID_HANDLE)
   { Print("Read open failed: ", GetLastError()); return; }

   string first = FileReadString(h);
   if(FileIsLineEnding(h) || FileIsEnding(h))
   { Print("Header columns missing."); FileClose(h); return; }
   string second = FileReadString(h);
   if(first != "item" || second != "value" || !FileIsLineEnding(h))
   { Print("Header mismatch."); FileClose(h); return; }

   while(!FileIsEnding(h))
   {
      string item = FileReadString(h);
      if(FileIsLineEnding(h) || FileIsEnding(h))
      { Print("Value column missing."); break; }
      string value = FileReadString(h);
      if(!FileIsLineEnding(h))
      { Print("Unexpected extra column."); break; }
      Print("item=", item, " / value=", value);
   }
   FileClose(h);
}

FILE_CSVでのFileReadStringは、原則として次の区切り文字か行末までを読みます。一回呼ぶだけで常に一行全体が取れるわけではありません。FileReadStringの公式仕様に沿って、列の数に対応した読取りを行います。行末はFileIsLineEnding、ファイルの終端はFileIsEndingで確認します。

この例はヘッダーと基本の列数を確認しますが、valueを数値として採用する処理はありません。実用の設定値を読むなら、値が空でないか、数値の表記が正しいか、許容範囲内かまで検証してから使います。読込失敗や欠損値を、正しいゼロという設定へ黙って置き換えないでください。

また、区切り文字や改行を含む自由文の引用・エスケープ規則まで、この例で対応できるとは考えません。外部サービスや表計算ソフトが作ったCSVは、列順、改行、引用符の扱いも仕様として確認します。まず単純な二列のファイルで保存・読込の対応を確かめてから、形式を広げましょう。

CSVを読み戻して列と値を確認する説明用イメージ
説明用の生成イメージ

Flushと共有フラグの役割を分ける

FileWriteした内容は、入出力のバッファに残る場合があります。FileFlushは残った内容をディスクへ書き出す関数です。例では書込みの区切りで呼び、その後に閉じています。FileCloseでも残ったバッファは書き出されますが、長時間開く設計ではどこで保存を確定させたいかを考えます。

FileFlushの公式説明には、頻繁な呼出しが動作速度へ影響し得ることも書かれています。毎ティック無条件に呼べばよい、という話ではありません。どの単位のデータを失いたくないか、入出力の頻度、読書きを切り替える箇所を整理し、必要な区切りで使います。

FILE_COMMONは保存場所を共通領域へ変える指定です。一方、FILE_SHARE_READやFILE_SHARE_WRITEは、複数プログラムからの共有アクセスに関するフラグです。共通フォルダへ置くだけで同時書込みが整列したり、行の重複や衝突が防げたりするわけではありません。共有フラグも読書き権限の代わりにはなりません。

複数のEAが同じCSVへ書く運用なら、誰が書込みを担当するか、どの単位で書くか、読込中の更新をどう扱うかを別に設計します。最初の学習では一つのプログラム、一つのファイルに限定すると、文字コードや位置の問題と、共有アクセスの問題を混ぜずに確認できます。

失敗を保存先と形式と操作で切り分ける

スクロールできます
症状先に確認すること避けたい判断
INVALID_HANDLEが返る通常・テスター・共通の別、相対名、読書きフラグ、エラー情報すぐに任意フォルダの絶対パスへ変える
既存の内容が消えたFILE_WRITE単独で開いていないか追記用のフラグだと思い込む
追記にならないREAD|WRITEとSEEK_ENDの成功開けたから末尾へ書くはずと考える
列が合わないdelimiter、ヘッダー、列数、CSV形式文字コードだけを変え続ける
文字が崩れるFILE_ANSI/UNICODEとコードページ、読込側の条件拡張子だけでUTF-8と決める
書けたが読めないFlush/Close、読込先、形式、共有アクセスFileOpen成功を内容の成功と同一視する

デモ環境で確認する順序は、コードを読みコンパイルする、専用の学習用ファイルを一度作る、出力名と保存先を確認する、同じ形式で読み戻す、の流れです。今回の例は実機未検証なので、エラーや警告を解消してから、ヘッダーと二行の値が一致するか確かめてください。

通常のチャートで成功しても、EAをテスターで使う場合はテスター側の保存先を改めて確認します。読み込むファイルを通常側へ置いたまま、同じ名前がテスターにもあると仮定しないでください。FILE_COMMONを使うなら、書込み側と読込側で同じ場所の指定になっているかも照合します。

取引履歴の保存が目的なら、自作CSVのコードを書く前にMT4の標準機能で取引履歴を表示・保存する方法を確認できます。EAの入力設定をそのまま保存したい場合はMT4のsetファイルの保存と読込も用途が異なります。本記事は自分で項目を定めるCSV入出力の手順です。

保存先と入出力結果を順に確認する説明用イメージ
説明用の生成イメージ

学習が終わったら、作成された専用のファイル名と内容を確認し、不要な学習用CSVだけを片付けます。コード例は自動削除を行いません。普段使う設定、他のEAが使う共有ファイル、出所が分からないCSVをまとめて削除する操作へ広げないでください。

MQL4のFileOpenは開き方と読み戻しで確認する

MQL4のFileOpenでCSVを扱うときは、保存先、読書きの権限、書く位置、区切り文字、文字コードを一組で決めます。FILE_WRITE単独の再作成と、FILE_READ|FILE_WRITEで開いたあとに末尾へ追記する操作を分けることが、既存データを取り違えないための基本です。

FX自動売買の検証値や設定値へ使う前に、専用の学習用CSVで、開く、書く、保存する、閉じる、読み戻すという流れを確認しましょう。INVALID_HANDLEと内容の形式不一致を区別し、読めた値も必要な型や範囲を検証してから採用します。

最初の確認は単純な二列で十分です。保存先にあるファイルと読み戻したヘッダー・値が対応することを確かめ、共有アクセスや複雑なCSV形式は別の設計として段階的に追加してください。

よかったらシェアしてね!
目次