こんにちは、潤奈です( ゚Д゚)!
これまでの連載で、「注文」「決済」「インジケーターの数値取得」という、EA(エキスパートアドバイザー)を開発するための3大要素をすべて学んできました。
今回は、これまでの集大成として、「実際に動くゴールデンクロス・デッドクロスEA」を構築します!
今回作成するコードは、MetaEditorにコピペすればそのままMT5のバックテストで稼働させることができます。
パーツをどうやって組み合わせて1つのEAにするのか、バグの起きないプロ仕様の書き方を分かりやすく解説しますね!
1. 今回作成するゴールデンクロスEAの設計図
プログラムを書く前に、EAがどのようなルールで動くのかを整理しておきましょう。
今回は、最もシンプルで王道なテクニカル分析手法である「2本の移動平均線の交差(クロス)」をシグナルにします。
- 短期移動平均線(期間10)と、長期移動平均線(期間30)を使用。
- 買いシグナル(ゴールデンクロス):
短期MAが長期MAを「下から上に」突き抜けた瞬間。
👉 すでに売りポジションがあればすべて決済し、新しく買いポジションを0.1ロット持つ。 - 売りシグナル(デッドクロス):
短期MAが長期MAを「上から下に」突き抜けた瞬間。
👉 すでに買いポジションがあればすべて決済し、新しく売りポジションを0.1ロット持つ。
⚠️ 超重要:シグナル判定は必ず「確定足」で行う!
EA開発における非常に重要な鉄則があります。それは、「現在動いている足(インデックス0)ではなく、すでに確定した足(1本前と2本前の足)でクロスを判定する」という点です。
現在進行形の足で判定してしまうと、値動きによって「クロスした!やっぱり戻した!」とシグナルが点滅し、1つの足の中で何度も注文と決済を繰り返してしまう大損バグ(チャタリング)が発生します。
そのため、今回は以下のように判定します。
- ゴールデンクロスの条件:
2本前の短期MA <= 2本前の長期MAかつ1本前の短期MA > 1本前の長期MA - デッドクロスの条件:
2本前の短期MA >= 2本前の長期MAかつ1本前の短期MA < 1本前の長期MA
2. 登場パーツを合体させる手順
これまで個別に作ってきたパーツを、以下のようにEAのライフサイクルに合わせて配置します。
- グローバル領域:
注文ライブラリ(CTrade)のインクルード、マジックナンバーやロット数の設定、インジケーターハンドル、およびデータ格納用配列の宣言。 OnInit()(初期化):
短期・長期MAのハンドル作成、格納用配列の時系列化(ArraySetAsSeries)。OnDeinit()(終了処理):
作成した短期・長期MAハンドルの解放(IndicatorRelease)。OnTick()(チック毎の処理):CopyBufferで短期・長期MAの最新3本分のデータを配列に取得します(配列を時系列順に並び替えているため、最新の0番足から3本分コピーすれば、1本前と2本前の確定足を過不足なく取得できます)。確定足データを元に「ゴールデンクロス」または「デッドクロス」を判定。条件が揃ったら、反対のポジションを決済し、新規に発注します。さらに、無駄な連続発注を防ぐための「新バー判定」もここに組み込みます。
3. コピペで動く!完成EAソースコード
MQL5/MT5特有の挙動(環境やタイミングによるポジション同期の不安定さ、毎チック処理が走るOnTickの特性)を考慮し、今回紹介するEAには「新バーの開始時のみ売買ロジックを実行する新バー判定」と、「ポジション情報を確実に同期・取得するための明示的なチケット選択(PositionSelectByTicket)」という、現場で必須となる2大安全対策を組み込んだ「プロ仕様」の設計にしています!
それでは、MetaEditorを開き、新規作成したEAに以下のコードを丸ごと貼り付けてコンパイル(F7)してみてください!
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
//+------------------------------------------------------------------+ //| GoldenCrossEA.mq5 | //| 潤奈FX | //+------------------------------------------------------------------+ #property copyright "潤奈FX" #property link "https://zyunafx.com" #property version "1.00" // --- 注文ライブラリの読み込み --- #include <Trade\Trade.mqh> CTrade trade; // --- 入力パラメータ設定 --- input group "--- EAの設定 ---" input int MagicNumber = 777777; // マジックナンバー input double TradeLots = 0.1; // 取引ロット数 input int Slippage = 100; // 許容スリッページ (ポイント) input group "--- インジケーターの設定 ---" input int FastMAPeriod = 10; // 短期MA期間 input int SlowMAPeriod = 30; // 長期MA期間 // --- グローバル変数 --- int fastMAHandle; // 短期MAのハンドル int slowMAHandle; // 長期MAのハンドル double fastMAValues[]; // 短期MAのデータ格納用配列 double slowMAValues[]; // 長期MAのデータ格納用配列 datetime lastBarTime; // 最後に処理したバーの時間(新バー判定用) //+------------------------------------------------------------------+ //| 初期化関数 | //+------------------------------------------------------------------+ int OnInit() { // CTradeの初期設定 trade.SetExpertMagicNumber(MagicNumber); trade.SetDeviationInPoints(Slippage); // ① 短期MAハンドルの作成 fastMAHandle = iMA(_Symbol, _Period, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE); if(fastMAHandle == INVALID_HANDLE) { Print("短期MAハンドルの作成に失敗しました。"); return(INIT_FAILED); } // ① 長期MAハンドルの作成 slowMAHandle = iMA(_Symbol, _Period, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE); if(slowMAHandle == INVALID_HANDLE) { Print("長期MAハンドルの作成に失敗しました。"); return(INIT_FAILED); } // ② 配列を時系列順に並び替え(最新の足がインデックス0) ArraySetAsSeries(fastMAValues, true); ArraySetAsSeries(slowMAValues, true); Print("EAが正常に初期化されました。"); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 後片付け関数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // メモリリークを防ぐため、ハンドルを解放する IndicatorRelease(fastMAHandle); IndicatorRelease(slowMAHandle); Print("EAが停止し、ハンドルが解放されました。"); } //+------------------------------------------------------------------+ //| チック毎の処理関数 | //+------------------------------------------------------------------+ void OnTick() { // 新しい足が確定したかチェック(新バー開始時のみ実行してチャタリングを防止) datetime currentBarTime = iTime(_Symbol, _Period, 0); if(currentBarTime == lastBarTime) return; // 同じ足なら処理をスキップ // ③ CopyBufferで最新3本分のデータを配列にコピー if(CopyBuffer(fastMAHandle, 0, 0, 3, fastMAValues) <= 0) { Print("短期MAデータのコピーに失敗しました。"); return; } if(CopyBuffer(slowMAHandle, 0, 0, 3, slowMAValues) <= 0) { Print("長期MAデータのコピーに失敗しました。"); return; } // 確定足の値を取得(1 = 1本前の足、2 = 2本前の足) double fastMA_1 = fastMAValues[1]; double fastMA_2 = fastMAValues[2]; double slowMA_1 = slowMAValues[1]; double slowMA_2 = slowMAValues[2]; // 現在のポジション保有状況を確認する bool hasBuyPosition = false; bool hasSellPosition = false; // ヘッジングシステムに対応したポジション確認ループ for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); // PositionGetTicketだけでなく、PositionSelectByTicketを明示的に呼んで同期を安定させる(プロ仕様) if(ticket > 0 && PositionSelectByTicket(ticket)) { // 通貨ペアとマジックナンバーがこのEAのものか確認 if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber) { long type = PositionGetInteger(POSITION_TYPE); if(type == POSITION_TYPE_BUY) hasBuyPosition = true; if(type == POSITION_TYPE_SELL) hasSellPosition = true; } } } // --- A. ゴールデンクロスの判定(買い) --- if(fastMA_2 <= slowMA_2 && fastMA_1 > slowMA_1) { Print("ゴールデンクロス発生!"); // 売りポジションがあれば決済する if(hasSellPosition) { ClosePosition(POSITION_TYPE_SELL); } // 買いポジションをまだ持っていなければ新規発注 if(!hasBuyPosition) { trade.Buy(TradeLots); } } // --- B. デッドクロスの判定(売り) --- if(fastMA_2 >= slowMA_2 && fastMA_1 < slowMA_1) { Print("デッドクロス発生!"); // 買いポジションがあれば決済する if(hasBuyPosition) { ClosePosition(POSITION_TYPE_BUY); } // 売りポジションをまだ持っていなければ新規発注 if(!hasSellPosition) { trade.Sell(TradeLots); } } // 正常に処理が完了したら、現在のバーの時間を記憶 lastBarTime = currentBarTime; } //+------------------------------------------------------------------+ //| 特定のポジションタイプを一括決済する関数(逆順ループ) | //+------------------------------------------------------------------+ void ClosePosition(long posType) { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); // PositionSelectByTicketを明示的に呼び出して同期を確実に安定させる if(ticket > 0 && PositionSelectByTicket(ticket)) { if(PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetInteger(POSITION_TYPE) == posType) { Print("ポジションをクローズします。チケット: ", ticket); trade.PositionClose(ticket); } } } } //+------------------------------------------------------------------+ |
4. MT5の「ストラテジーテスター」で動かしてみよう!
作成したEAをバックテストで走らせる手順を簡単に解説します。
- MT5を開き、キーボードの
Ctrl + Rを押して「ストラテジーテスター」を開きます。 - 「設定」タブで、作成した
GoldenCrossEAを選択します。 - 通貨ペア(例: USDJPY)、時間足(例: 1時間足)、テスト期間を指定します。
- 右下の「スタート」ボタンをクリックします。
- テスト完了後、「グラフ」や「バックテスト」タブを開くと、EAがゴールデンクロスとデッドクロスでしっかりと自動売買を繰り返している様子が視覚的に確認できます!
5. まとめ&次のステップ
今回は、これまでの連載で学んだスキルをすべて組み合わせて、実際に動く「ゴールデンクロスEA」を作成しました。
- シグナルの点滅を防ぐため、判定には必ず「確定足(1本前と2本前)」のデータを使う。
- 反対のシグナルが出たら、既存のポジションを決済(ドテン)してから新しい注文を出す。
- MQL5のベストプラクティス(グローバル配列、ハンドルの解放、逆順決済ループ)を組み込むことで、エラーの起きない強固なEAになる。
ついにご自身の手で自動売買EAの基盤を作ることができました!おめでとうございます!
次のステップとしては、急激な相場の逆行から資金を守るための「ストップロス(損切り)とテイクプロフィット(利食い)の自動設定方法」について解説します。
これができるようになれば、実戦に耐えうるEAへとさらに進化させることができます!
今回のコードを動かしてみて「エラーが出てコンパイルできない」「バックテストが始まらない」といったトラブルがあれば、YouTubeのコメント欄やXでお気軽にご連絡くださいね!


コメント