最終更新: 2026年6月
移動平均線のゴールデンクロスを使ったEAは、EA開発の入門として最初に作るべき題材だ。ロジックがシンプルで理解しやすく、コードに落とし込む過程でMQL言語の基本要素——変数・条件分岐・インジケーター呼び出し・注文——を全て経験できる。
本記事では、MQL4(MT4)とMQL5(MT5)それぞれの実装サンプルを示しながら、実運用に近いコードへの拡張手順まで解説する。
ゴールデンクロスEAの基本ロジック
ゴールデンクロスとは、短期移動平均線が長期移動平均線を下から上に突き抜ける現象だ。上昇トレンドへの転換シグナルとして広く使われる。デッドクロスはその逆で、短期が長期を上から下に割り込む下降シグナルだ。
EAとして実装する場合の骨格は以下になる。
| 条件 | アクション | |---|---| | 前バーで短期MA < 長期MA かつ 現バーで短期MA > 長期MA | 買いエントリー | | 前バーで短期MA > 長期MA かつ 現バーで短期MA < 長期MA | 売りエントリー |
重要な原則: 判定は必ず「確定済みバー(1本前)」と「現在バー」で行う。現在の未確定バーだけで判定すると、同一バー内で何度もシグナルが出る「シグナル重複問題」が発生する。
MQL4での実装
iMA() 関数の基本
MQL4で移動平均線の値を取得するには iMA() 関数を使う。
double iMA(
string symbol, // 通貨ペア(_Symbol で現在のペア)
int timeframe, // 時間足(0で現在の時間足)
int ma_period, // 移動平均の期間
int ma_shift, // シフト(通常0)
int ma_method, // 計算方法(MODE_SMA / MODE_EMA等)
int applied_price, // 適用価格(PRICE_CLOSE等)
int shift // バーのシフト(0=現在、1=1本前)
);
最後の shift 引数が重要だ。shift=0 が現在の(未確定の)バー、shift=1 が1本前の確定済みバーを指す。
ゴールデンクロスEA(MQL4・完全版)
// ゴールデンクロスEA — MQL4版
// パラメーター
extern int FastPeriod = 5; // 短期MA期間
extern int SlowPeriod = 20; // 長期MA期間
extern double Lots = 0.1; // ロット数
extern int StopLoss = 50; // 損切りpips
extern int TakeProfit = 100; // 利確pips
// ポジション有無の確認関数
bool HasOpenPosition(int type) {
for (int i = 0; i < OrdersTotal(); i++) {
if (OrderSelect(i, SELECT_BY_POS) &&
OrderSymbol() == _Symbol &&
OrderType() == type) {
return true;
}
}
return false;
}
void OnTick() {
// 移動平均線の値を取得(現在バーと1本前バー)
double fast_current = iMA(_Symbol, 0, FastPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double fast_prev = iMA(_Symbol, 0, FastPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double slow_current = iMA(_Symbol, 0, SlowPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
double slow_prev = iMA(_Symbol, 0, SlowPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
// ゴールデンクロス検出 — 既にBUYポジションがない場合のみエントリー
if (fast_prev < slow_prev && fast_current > slow_current) {
if (!HasOpenPosition(OP_BUY)) {
double sl = Ask - StopLoss * Point * 10;
double tp = Ask + TakeProfit * Point * 10;
OrderSend(_Symbol, OP_BUY, Lots, Ask, 3, sl, tp, "GC_EA", 0, 0, clrBlue);
}
}
// デッドクロス検出 — 既にSELLポジションがない場合のみエントリー
if (fast_prev > slow_prev && fast_current < slow_current) {
if (!HasOpenPosition(OP_SELL)) {
double sl = Bid + StopLoss * Point * 10;
double tp = Bid - TakeProfit * Point * 10;
OrderSend(_Symbol, OP_SELL, Lots, Bid, 3, sl, tp, "GC_EA", 0, 0, clrRed);
}
}
}
Point * 10 の乗算は5桁表示ブローカー(小数点5桁)対応のためだ。4桁表示のブローカーでは Point をそのまま使う。ブローカー環境に依存するため、実際は Digits == 5 ? Point * 10 : Point で動的に判定するコードにするのが望ましい。
MQL5での実装
MQL5では iMA() ハンドル方式を使う。MQL4と異なり、一度ハンドルを作成してから値を取得する2ステップの手順だ。
// ゴールデンクロスEA — MQL5版
#include <Trade\Trade.mqh>
// パラメーター
input int FastPeriod = 5;
input int SlowPeriod = 20;
input double Lots = 0.1;
input int StopLoss = 50;
input int TakeProfit = 100;
CTrade trade; // トレードオブジェクト
int fast_handle; // 短期MAハンドル
int slow_handle; // 長期MAハンドル
int OnInit() {
// MAハンドルの作成
fast_handle = iMA(_Symbol, PERIOD_CURRENT, FastPeriod, 0, MODE_SMA, PRICE_CLOSE);
slow_handle = iMA(_Symbol, PERIOD_CURRENT, SlowPeriod, 0, MODE_SMA, PRICE_CLOSE);
if (fast_handle == INVALID_HANDLE || slow_handle == INVALID_HANDLE) {
Print("MAハンドル作成失敗");
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason) {
IndicatorRelease(fast_handle);
IndicatorRelease(slow_handle);
}
void OnTick() {
double fast_buf[2], slow_buf[2];
// 最新2本分の値を取得(インデックス0=現在、インデックス1=1本前)
if (CopyBuffer(fast_handle, 0, 0, 2, fast_buf) < 2) return;
if (CopyBuffer(slow_handle, 0, 0, 2, slow_buf) < 2) return;
double fast_current = fast_buf[0];
double fast_prev = fast_buf[1];
double slow_current = slow_buf[0];
double slow_prev = slow_buf[1];
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
double pip = (digits == 5 || digits == 3) ? point * 10 : point;
// ポジション未保有確認
bool no_position = (PositionsTotal() == 0);
// ゴールデンクロス
if (fast_prev < slow_prev && fast_current > slow_current && no_position) {
double sl = ask - StopLoss * pip;
double tp = ask + TakeProfit * pip;
trade.Buy(Lots, _Symbol, ask, sl, tp, "GC_MQL5");
}
// デッドクロス
if (fast_prev > slow_prev && fast_current < slow_current && no_position) {
double sl = bid + StopLoss * pip;
double tp = bid - TakeProfit * pip;
trade.Sell(Lots, _Symbol, bid, sl, tp, "GC_MQL5");
}
}
MQL5の CTrade クラスを使うと、MQL4の OrderSend() より安全かつ簡潔に注文が書ける。このクラスはMQL5標準ライブラリに含まれており、別途インストール不要だ。
実運用に向けた拡張ポイント
上記のコードは動作するが、実際の口座で使うにはいくつかの拡張が必要だ。
1. フィルターの追加
純粋なゴールデンクロスEAは「ダマし」(偽のクロスシグナル)に弱い。トレンドフィルターを加えることでシグナルの精度を上げられる。
よく使う組み合わせ:
- ADXフィルター: ADX > 25 のときのみエントリー(トレンドが強いときのみ)
- 長期MAフィルター: 200期間EMAの上にある場合のみ買いエントリー
- 時間フィルター: ロンドン・NYセッション中のみエントリー
2. 決済ロジックの追加
上記コードは「次のクロスが出るまでポジションを保持する」ロジックになっている。実際は反対クロスでの決済に加え、タイムリミット(例: 10バー経過後に決済)を設けることも有効だ。
3. エラーハンドリング
OrderSend() の戻り値(MQL4)や CTrade::Buy() の結果確認(MQL5)を実装し、注文が通らなかった場合の処理を書く。本番EAでは省略禁止だ。
Claudeと会話しながらインジケータが作れるhedgrow-fxでは、こうしたフィルター追加・エラーハンドリングをAIとの対話で実装できるため、コード拡張の壁打ち相手として活用できる。
バックテスト時の注意点
ゴールデンクロスEAのバックテストで特に気をつけるべき点が2つある。
スプレッドの設定: バックテストのスプレッド設定が実際のブローカーのスプレッドと一致しているかを確認する。スプレッドを0にしたバックテストは「机上の空論」だ。
全ティックデータの使用: 移動平均線クロスEAは「クロスした瞬間のPrice」に依存する。粗いデータ(1分足補完等)ではシグナルのタイミングがずれる。可能な限り「全ティック」モードでバックテストを行うこと。
よくある質問(FAQ)
Q: 短期MAと長期MAの期間は何が最適ですか? A: 「最適な期間」は相場環境・通貨ペア・時間足によって異なります。よく使われる組み合わせは5/20、10/30、25/75ですが、バックテストで自分の取引スタイルに合う期間を検証して選んでください。
Q: SMAとEMAではどちらがゴールデンクロスEAに向きますか? A: EMAは直近の価格変動に反応しやすいため、ゴールデンクロスの発生が早くなります。SMAより早いシグナルはダマしも増えますが、遅延も少なくなります。どちらが優れているかはバックテストで確認するしかありません。
Q: なぜ「現在バーのみ」で判定してはいけないのですか? A: MT4/MT5のOnTick()はティックごとに呼ばれます。未確定バー(現在バー)の値はティックごとに変わるため、「現在バー=1本前バー」の比較だけだと同じバー内で何度もシグナルが出てしまいます(重複エントリー)。確定済みの1本前のバーと比較することで、1バーに1回のシグナル制御ができます。
Q: ストップロスなしでEAを動かせますか? A: 技術的には可能ですが、推奨しません。予期しない急騰・急落時に口座残高を一気に失うリスクがあります。ロスカット(強制決済)が発動する前に手動で対応できるとは限らないため、常にストップロスを設定してください。
免責事項: 本記事のコードは教育目的のサンプルです。実際の取引に使用した際の損失について筆者は責任を負いません。必ずデモ口座での十分な検証を経てから実口座で使用してください。
著者情報: 金融工学専攻・アルゴリズム取引研究者(EA開発歴8年)
