FX

MQL4のグローバル変数でFX自動売買の状態を保存・確認する方法

一時的なプログラム変数と端末保存を区別する説明用生成画像

MQL4のグローバル変数には、関数の外に宣言するプログラム内の変数と、MT4端末へ名前を付けて保存する変数があります。FX自動売買の状態を再起動後にも読みたいなら、両者を分けて考える必要があります。関数の外へ宣言するだけでは、端末へ永続保存したことにはなりません。

端末の保存値を使うときも、「読めたから売買を再開してよい」とは限りません。別EAとの名前衝突、保存期間、古い値、実際の保有注文との不一致を確認します。この記事では、二種類の違いから、専用のデモ用キーで書込・読込を確かめる手順まで整理します。

この記事のポイント
  • プログラムの有効範囲と端末保存の違い
  • 他EA・口座・テスターと衝突しない名前の考え方
  • 値が0の場合と読込失敗を区別する方法
  • 再起動後の保存値を注文状態と照合する順序

MQL4のグローバル変数とFX自動売買の保存範囲

プログラム内と端末内で保存範囲が異なることを表す説明用生成画像
変数を使える範囲と、値が残る期間は別の性質です(説明用の生成画像)

関数の外の変数はプログラム内で使う

関数の外で宣言した変数は、そのプログラムの複数の関数から参照できます。たとえばOnInitとOnTickで同じ状態を扱いたいときの置き場所です。一つの関数内だけで使うローカル変数との違いは、まず参照できる範囲にあります。

一方、プログラム内のグローバル変数は、そのMQL4プログラムの生存期間に結び付きます。EAを読み込み直したり端末を終了したりした後も、前回の値がそのまま復元される保存庫ではありません。static変数も、宣言すればディスクへ書かれるという意味ではありません。

int programState = 0;  // プログラム内のグローバル変数

int OnInit()
{
   programState = 1;
   return(INIT_SUCCEEDED);
}

void OnTick()
{
   // 同じプログラムの関数から参照する
   // これだけでは端末への保存にならない
}

上は参照範囲を説明する断片です。端末再起動後も1を読みたいなら、保存と復元を別途設計します。また、同じ名前の変数を別EAに宣言しても、二つのプログラムの変数が自動でつながるわけではありません。宣言位置と端末共有を混同しないようにしましょう。

言語上の定義はMQL4公式のプログラム内グローバル変数を参照してください。入力パラメーター、プログラム実行中の状態、再起動後に復元したい状態を分けると、保存する必要のない値まで共有せずに済みます。

端末の変数は名前付きの数値を保存する

GlobalVariableSetやGlobalVariableGetで扱うのは、クライアント端末のグローバル変数です。名前と数値を対応させて保存し、同じ端末で動くMQL4プログラムから参照できます。プログラムをまたいで少量の状態を受け渡す場合にも使えます。

値はdouble型の数値です。たとえば停止フラグの0と1、決められた状態番号などを表現できます。ただし、長い文章、複雑な設定一式、履歴の一覧をそのまま保存する入れ物ではありません。大きな整数識別子なども、doubleへの変換で精度を失わないか別に確認が必要です。

種類使える範囲再起動後の扱い
関数内のローカル変数その関数やブロック保存を別途作らなければ残らない
関数外のグローバル変数そのMQL4プログラム端末保存とは別
端末のグローバル変数同じ端末のMQL4プログラム保持条件の範囲で保存される
CSVなどのファイル設計した保存先と読込処理形式・配置・アクセス条件を別途管理

表の「同じ端末」は、同じ取引口座という意味ではありません。別のMT4インストールや別のパソコンへ自動で同期される仕組みでもありません。複数の端末を使う場合は、どの端末がその値を持っているのかを明確にします。端末の分け方はMT4の複数起動と保存先の確認も参考になります。

名前に用途と対象を含める

EAごとに名前を分けて保存値の衝突を防ぐ説明用生成画像
保存名は用途と対象を分け、他の値とぶつからないようにします(説明用の生成画像)

端末の変数は同じ端末内で共有されるため、単にflagやstateという名前だけでは対象を区別しにくくなります。別EAや別口座用の値を上書きすると、プログラム自体に構文エラーがなくても、意図しない状態で動く可能性があります。

名前を決める際は、アプリやEAの識別子、運用・検証の区別、口座や銘柄、設定の版、値の用途を必要に応じて組み合わせます。すべてを長く詰め込むのではなく、同じ保存領域で衝突しない最小のルールを決めます。名前の意味を後から説明できることが大切です。

GlobalVariableSetの名前は63文字以内という制約があり、文字コードで変換できない文字は問題になります。配布するプログラムでは、公式が推奨するラテン文字を中心にした名前が扱いやすいですね。長いサーバー名などを連結する場合は、完成した名前の長さも検査します。

また、現在の口座番号が変わっただけで、前の口座用の保存値が自動で消えるとは考えないでください。口座が違う値を読める名前なら、EA側で対象を確認しない限り混同するおそれがあります。保存名と復元時の口座確認を両方用意する設計が役立ちます。

名前の制約と保存時の戻り値はGlobalVariableSetの公式仕様で確認できます。停止状態を示す値には、価格やロットとは別の用途名を付けると、単位の取り違えも減らせます。

四週間と最終アクセスを確認する

保存値にも保持期間があることを表す説明用生成画像
端末の保存値は、無期限の保管庫ではありません(説明用の生成画像)

端末のグローバル変数は、最後のアクセスから四週間が経過すると自動削除されると公式に説明されています。新しい値を書くだけでなく、読み取りもアクセスに含まれます。したがって「一度保存したから何か月後でも必ずある」という前提にはできません。

逆に、定期的に読んでいる値はアクセスが続きます。存在している期間が長いことから、その内容が最近の業務状態へ更新されたとは限りません。最後にアクセスした時刻と、売買状態を確認した時刻は、同じ意味の時刻ではありません。

たとえば古い停止フラグを毎日起動時に読むだけなら、保存され続けても停止理由が解消されたかは分かりません。状態の鮮度が必要な設計では、確認した日時や仕様の版を別途管理し、いつまでその値を信頼するかを決めます。保存の保持条件を、状態の有効期限へそのまま流用しないことです。

保持期間、読み取りの扱い、共有範囲は端末グローバル変数の公式説明を参照してください。変数が見つからないときの初期値と、新規発注を許可してよい条件は、別々に決めておく必要があります。

ファイル保存と使い分ける

少量の数値状態を同じ端末内で共有したいなら、端末変数は候補になります。設定一式を読み込む、行ごとの記録を扱う、外部で確認できる形式へ出力する、といった用途では、ファイルなど別の保存方法も検討します。数値一個へ複雑な情報を無理に押し込まない方が、後から検証しやすくなります。

ファイルを使えばすべてが解決するわけでもありません。保存先の制約、文字コード、上書きや追記、複数プログラムからのアクセスを設計する必要があります。選ぶ基準は、保存する情報の形、共有する範囲、復元の条件です。

CSVの読込・書込が目的なら、MQL4のFileOpenでCSVを保存・読込する方法を参照してください。この記事では端末内の小さな状態を扱い、ファイル形式や履歴の大量保存へ話を広げません。

MQL4のグローバル変数を保存・復元する手順

一つの値を書き込み直後に読み直して照合する説明用生成画像
書込と、その直後の読込確認を別の操作として扱います(説明用の生成画像)

Getの失敗と値の0を区別する

GlobalVariableGetには、値を直接返す形式と、成功・失敗をboolで返して別の変数へ値を渡す形式があります。数値の0を正常値として使うなら、成功・失敗を分けられる後者が読みやすい方法です。読込失敗を正常な0へ置き換えて処理を続けないようにします。

GlobalVariableCheckで存在確認しても、その後の読取まで同じ状態が固定されるわけではありません。共有領域では、その間に他の処理が値を変えたり削除したりする可能性があります。存在確認の結果だけを使い、Getの成否を無視しないことが大切です。

以下は一つの専用キーで動作を確かめる学習用スクリプトです。初期状態は読取だけで、CreateOnceをtrueにしたときだけ、デモ口座で未作成のキーを一回作ります。既存キーは上書きしません。実際の注文を出す関数はありません。

#property strict
input bool CreateOnce = false;

void OnStart()
{
   string key = "BLOG_GV_DEMO_"
                + IntegerToString(AccountNumber()) + "_v1";
   if(StringLen(key) > 63) return;

   if(CreateOnce)
   {
      if(!IsDemo())
      {
         Print("Demo account only. No write.");
         return;
      }
      if(GlobalVariableCheck(key))
      {
         Print("Key already exists. No overwrite: ", key);
         return;
      }
      ResetLastError();
      datetime changed = GlobalVariableSet(key, 42.25);
      if(changed == 0)
      {
         int err = GetLastError();
         Print("Set failed: ", err);
         return;
      }
      GlobalVariablesFlush();
   }

   double value = 0;
   ResetLastError();
   if(!GlobalVariableGet(key, value))
   {
      int err = GetLastError();
      Print("No readable value: ", err, " Key=", key);
      return;
   }
   Print("Key=", key, " Value=", DoubleToString(value, 2));
}

42.25は数値保存を確認するための仮の値で、残高や売買条件ではありません。まずご自身のMetaEditorでコンパイルし、他のEAが使わない検証端末とデモ口座で実行してください。この例は、複数の書込元が競合する初期化まで解決した完成品ではありません。

読取形式の違いはGlobalVariableGetの公式仕様で確認できます。読込に失敗したら、変数の値ではなく処理結果の問題として扱い、どのキーを読もうとしたかとエラー番号を点検します。

F3の一覧と再起動後の値を比べる

通常の再起動後に保存値を確認する説明用生成画像
正常に終了・再起動した後も、同じ名前と値を読み直します(説明用の生成画像)

Windows版MT4では、ツールメニューのグローバル変数、またはF3から一覧を確認できます。専用キーを作成したら、コードが出力した名前と一覧の名前を照合し、42.25が保存されているかを見ます。似た名前の既存キーへ手動で入力しないように注意してください。

次にCreateOnceをfalseへ戻して再実行し、読取だけで同じ値が出るかを確認します。さらに端末を通常終了して起動し直し、同じ端末・同じデモ口座で再度読みます。端末の保存機能を検証する工程と、EAの自動復元処理を検証する工程は分けて考えましょう。

一覧で値を変更できることは、運用中に気軽に変更してよいという意味ではありません。その値を監視するEAがあれば、次の処理で売買判断へ影響する可能性があります。手動編集を試す場合も、使うのは自分が作った検証専用キーだけにし、実際の管理値と混ぜません。

画面操作と端末間の保持はMetaTrader 4公式ヘルプのグローバル変数を参照してください。F3に一覧が出ることから、関数外に宣言したすべての変数を確認できるわけではありません。

Flushと復元可否を分けて判断する

GlobalVariablesFlushは、端末のグローバル変数をディスクへ保存する処理です。特定の一変数だけを保存する引数はなく、戻り値もありません。公式には、端末終了時に保存される一方、突然の障害でデータを失う可能性があるため、この関数で保存を制御できると説明されています。

ここから「Flushを呼んだから、あらゆる障害でも状態が必ず復元する」とまでは言えません。ディスクの障害、別端末での起動、キーの誤り、古い値の読込などは別の問題です。保存処理の呼出と、次回読めたことの確認を一つの成功判定にまとめない方がよいですね。

復元時は、値の存在、値の範囲、対象口座、設定の版、実際の注文状態を確認します。たとえば「発注済み」というフラグが1でも、対象注文を照合できない場合は自動で正しい状態と扱わず、確認が必要な状態へ分けます。逆にフラグが0だから注文も存在しない、と決めないことも重要です。

保存値は補助的な状態であり、サーバー上の注文という事実を置き換えるものではありません。EAを再開するときは、保存された値と現在の注文を照合してから、次の操作を許可する設計にします。Flushの役割は公式リファレンスで確認できます。

共有更新とテスターの干渉を避ける

複数のEAが共有値を同時に変えないよう調整する説明用生成画像
共有する一つの値には、書込順序の取り決めが必要です(説明用の生成画像)

二つのEAが同じ値を読み、どちらも1を足して保存すると、片方の更新が失われることがあります。単純なGetとSetの組合せは、二つの操作をひとまとまりに固定する仕組みではありません。読み取りやすいコードでも、共有更新の順序は別途設計します。

GlobalVariableSetOnConditionは、既存の値が指定した確認値と一致するときだけ更新する関数です。公式では一変数への原子的なアクセスを提供するとされ、同一端末のEA間の排他制御に使うことができます。ただし、まだ存在しないキーをこの関数だけで作成することはできません。

また、一変数の条件付き更新ができても、複数の値の更新や実際の注文送信まで一つの操作になるわけではありません。ロックを使う場合は、作成を誰が行うか、解除を誰が行うか、途中で止まったときにどう復旧するかも決めます。ここが不明なまま、trueになるまで無限に待つ処理へしないでください。

MQL4の公式説明では、端末とストラテジーテスターが共通のグローバル変数を使う点にも注意が示されています。検証用と運用用の名前を明確に分け、前のテストの値を次の結果へ混ぜないようにします。条件付き更新の仕様はGlobalVariableSetOnConditionを参照してください。

MQL4のグローバル変数は復元まで検証する

最終確認は、読める場合だけでなく、未作成の場合、正常な0の場合、想定外の値の場合、再起動後、別口座の場合を分けて行います。それぞれで、読取結果とEAが次に取る動作を照合します。「エラーが表示されなかった」だけで再開条件を満たしたと判断しないことです。

検証用の値を整理するときは、自分が作ったキーを一つずつ確認します。他EAの停止フラグや識別値まで一括削除すると、運用状態を変えてしまうおそれがあります。使い終えたキーの削除は、参照するプログラムがないことを確かめてから行います。

FX自動売買で残したいのは、意味を説明できる必要最小限の状態です。プログラム内だけで使う値はプログラム内へ、端末内で共有・復元する数値は専用の保存名へ分けます。値が読めないときや事実と合わないときは、勝手な既定値で発注へ進めず、確認する工程へ戻しましょう。

MQL4のグローバル変数は、保存する場所を理解し、書いた値を読み直し、再起動後の使い方まで確かめると扱いやすくなります。保存APIの成功と売買状態の正しさを分けることが、状態を持つEAを検証する基本です。

EA作成ツールの検証範囲を確認する

生成コードの動作テスト・コンパイル結果と、その限界 →記事内の戦略や個別のEAの収益性を検証した結果ではありません。

学んだルールを、形にしてみよう。

EA作成ツールを開く ↗