以前の記事で、1日の取引回数に上限を設けるだけのシンプルなEAを作りましたが、
あのコードは1つのEAだけで回数を数えていました。
同じ口座で複数のEAを同時に動かしている場合、
「口座全体で1日5回まで」のように、EAをまたいで回数を合算したいことがあります。
static変数やグローバル変数(プログラムの外側で宣言した変数)は、
そのEA・インジケーター1つの中でしか値を保持できないため、
この用途には使えません。
この記事では、MT4の端末(ターミナル)そのものに値を保存し、
動いているすべてのEA・インジケーター・スクリプトから読み書きできる
「グローバル変数」の使い方を、複数EAでの回数共有を例に解説します。
MQL4のグローバル変数が持つ4つの基本関数
datetime GlobalVariableSet(string name, double value); double GlobalVariableGet(string name); bool GlobalVariableCheck(string name); bool GlobalVariableDel(string name);
名前(文字列)をキーにして、double型の値を1つだけ保存できる、
というシンプルな仕組みです。
`GlobalVariableSet()`で保存し、
`GlobalVariableGet()`で読み出します。
存在しない名前を`GlobalVariableGet()`で読むと0が返るため、
「本当に0が保存されているのか、まだ一度も保存されていないのか」
を区別したい場合は、先に`GlobalVariableCheck()`で存在を確認します。
設計思想: 日付を変数名に含めて『1日ごとの自動リセット』を実現する
複数EAで1日の取引回数を共有する場合、
素朴に実装すると「日付が変わったら0に戻す」処理を、
すべてのEAに個別に書く必要が出てきます。
この記事では、変数名そのものに日付を含めることで、
明示的なリセット処理を書かずに済ませます。
string DailyCounterName()
{
return("DailyTradeCount_" + TimeToString(TimeCurrent(), TIME_DATE));
}
日付が変われば`DailyCounterName()`が返す文字列も自動的に変わるため、
新しい日には存在しない変数名になり、
`GlobalVariableCheck()`がfalseを返して、
自然と0からカウントが始まります。
前日以前の変数は消さない限りそのまま端末に残りますが、
これは次の「消し忘れの罠」の節で扱います。
実際に動くコード全体
同じ接頭辞(`CounterPrefix`)を使う複数のEAで、
1日の新規エントリー回数を合算してカウントし、
上限に達したらそのEAは新規エントリーを見送る、
実際にMetaEditorでコンパイルできるEAです。
//+------------------------------------------------------------------+
//| GlobalDailyTradeLimit.mq4 |
//| 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"
#property strict
input int MagicNumber = 56789;//マジックナンバー(このEA固有)
input double Lots = 0.01;//ロット数
input int TakeProfit = 20;//利確(pips)
input int StopLoss = 20;//損切り(pips)
input int Slippage = 3;//スリッページ
input int MaxDailyTrades = 5;//1日あたりの新規エントリー上限(同じ接頭辞を使う全EA合算)
input string CounterPrefix = "DailyTradeCount_";//グローバル変数の接頭辞
//+------------------------------------------------------------------+
//| 今日の日付を含むグローバル変数名を作る |
//+------------------------------------------------------------------+
string DailyCounterName()
{
return(CounterPrefix + TimeToString(TimeCurrent(), TIME_DATE));
}
//+------------------------------------------------------------------+
//| 今日すでに何回エントリーしたかを取得する(なければ0) |
//+------------------------------------------------------------------+
int GetTodayTradeCount()
{
string name = DailyCounterName();
if(!GlobalVariableCheck(name))
return(0);
return((int)GlobalVariableGet(name));
}
//+------------------------------------------------------------------+
//| 今日のエントリー回数を1増やす |
//+------------------------------------------------------------------+
void IncrementTodayTradeCount()
{
string name = DailyCounterName();
GlobalVariableSet(name, GetTodayTradeCount()+1);
}
//+------------------------------------------------------------------+
//| このEA(マジックナンバー)のポジションを保有しているか判定する |
//+------------------------------------------------------------------+
bool HasPosition()
{
for(int i=OrdersTotal()-1; i>=0; i--)
{
if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
return(true);
}
return(false);
}
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(HasPosition())
return;
if(GetTodayTradeCount() >= MaxDailyTrades)
return;//このEA単体ではなく、同じ接頭辞を使う他のEAの分も合算された回数
//ここは説明用のシンプルな条件。実際にはお好みのエントリーロジックに置き換える
double ma50 = iMA(NULL,0,50,0,MODE_SMA,PRICE_CLOSE,0);
double ma100 = iMA(NULL,0,100,0,MODE_SMA,PRICE_CLOSE,0);
if(ma50 > ma100)
{
double sl = Ask-StopLoss*Point*10;
double tp = Ask+TakeProfit*Point*10;
int ticket = OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,sl,tp,"Global Limit Buy",MagicNumber,0,Blue);
if(ticket >= 0)
IncrementTodayTradeCount();
else
Print("買い注文に失敗しました。エラーコード=",GetLastError());
}
}
//+------------------------------------------------------------------+
このコードを、マジックナンバーだけを変えて複数のチャートにセットすれば、
`MaxDailyTrades`という同じ上限を、すべてのEAで共有できます。
MetaEditorで実際に動かす手順
- MT4のMetaEditorで「ファイル」→「新規作成」→「Expert Advisor(テンプレートを使用)」を選び、名前を「GlobalDailyTradeLimit」にして作成する
- テンプレートの中身を全て削除し、上のコードを丸ごと貼り付ける
- F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
- デモ口座で、マジックナンバーだけを変えたコピーを2枚のチャートにセットし、片方でエントリーした回数がもう片方の残り回数にも反映されることを確認する
- MT4上部メニューの「表示」→「グローバル変数一覧」で、`DailyTradeCount_`から始まる変数が作られていることも確認できる
『消し忘れ』の罠: グローバル変数は端末に残り続ける
グローバル変数は、EAをチャートから外しても、
MT4を再起動しても消えません。
端末のデータフォルダに保存された状態のまま残り続けます
(4週間まったく読み書きされないと自動的に削除される仕様ですが、
毎日アクセスがある変数は消えません)。
このため、テスト用に作った変数を消し忘れると、
翌日以降のカウントに古いテスト値が混ざり込む事故が起こり得ます。
明示的に消したい場合は`GlobalVariableDel(name)`を、
複数まとめて消したい場合は、
`GlobalVariablesDeleteAll(prefix)`を使います。
//DailyTradeCount_で始まる変数をすべて削除する例(手動クリーンアップ用)
GlobalVariablesDeleteAll("DailyTradeCount_");
また、グローバル変数は「今ログインしている口座」ではなく、
「今使っている端末(ターミナル)」単位で保存されます。
同じ端末で複数の口座を切り替えて検証すると、
口座を切り替えても前の口座で作った変数がそのまま残っている点に注意してください。
コピペして動かない・意図と違うときに確認すること
- `GlobalVariableGet()`が常に0を返す: 変数名のタイプミス、または一度も`GlobalVariableSet()`されていない可能性がある。`GlobalVariableCheck()`で存在を先に確認する
- 複数EAで回数が合算されない: `CounterPrefix`の値が各EAで完全に一致しているか確認する(1文字でも違うと別の変数として扱われる)
- 日をまたいでも回数がリセットされない: サーバー時刻(`TimeCurrent()`)が日付をまたぐタイミングと、自分が期待する「1日の区切り」が一致しているか確認する。MT4のサーバー時刻はブローカーごとに異なる
- ストラテジーテスターでの回数が実運用と食い違う: グローバル変数はテスターと実運用の端末で完全に独立しているとは限らない。テスト後は`GlobalVariableDel()`で明示的に削除するか、テスト専用の接頭辞を使い、正確な独立性は自分の環境で確認することを推奨する
まとめ
MQL4のグローバル変数
(GlobalVariableSet/GlobalVariableGet)は、
static変数と違い、
同じ端末で動くすべてのEA・インジケーター・スクリプトから読み書きできます。
変数名に日付を含めることで、
明示的なリセット処理なしに『1日ごとの自動リセット』を実現できる一方、
端末に残り続けるため、
消し忘れが翌日以降の挙動に影響する『罠』もあわせて理解しておく必要があります。
コメント