本ページはプロモーションが含まれています

ホーム › EAプログラミング

EAプログラミング

FX EAの自作・作り方|MQL4で始める初心者向けEA開発入門

↻

トム

トム

現役エンジニア(本業はJavaバックエンド)。以前はMT4のEAを自作してFX自動売買をしていました(現在は引退)。今はポイ活を「時給」で検証・攻略しています。

FXのEA(自動売買プログラム)は、MT4に付属するMetaEditorとMQL4を使えば、無料で自作できます。必要なのはWindowsパソコンとMT4だけです。

手順は「ひな形作成→コード記述→コンパイル→バックテスト」の順です。売買条件が1〜2個の単純なEAなら、変数・if文・OrderSend()を覚えれば未経験からでも書けます。ただし、自作EAで儲かるかはロジック次第です。

かつては私もプログラミングが分からず、MQLを1から勉強した身です。

コードが難しければ、ノーコードツール・ChatGPT・作成代行も選べます(後半で比較)。プログラミング経験者は基礎知識の節を飛ばして構いません。

この記事でわかること

  • MetaEditorで新規EAファイル(.mq4)を作り、コンパイルするまでの手順
  • OnInit()・OnTick()・OnDeinit()の役割とOrderSend()の使い方
  • ボリンジャーバンド逆張りEAの全コードと、pipsから損切り価格を計算する方法
  • EAが動かないときのエラー番号の見方と、ツール・ChatGPT・代行との比較

FXのEAを自作する5つのステップ

EAの自作は、次の5つの手順で進みます。コードを書く前に売買ルールを決めておくと、途中で「何を書けばいいのか」と迷わずに済みます。

  1. 売買ルールを決める(例:ボリンジャーバンドの±2σに触れたら逆張り)
  2. MetaEditorで新規EAファイル(.mq4)を作る
  3. MQL4でエントリー・決済・損切りのコードを書く
  4. コンパイルして実行ファイル(.ex4)を作る
  5. ストラテジーテスターでバックテストし、デモ口座で動作を確かめる

2〜4は実際のコードで順に確認し、5はバックテストの解説記事で手順を補います。

EA自作に必要なもの(MT4・MetaEditor)

今回はMT4(MetaTrader 4)の環境構築に必要な準備物についてご説明します。以下の2つが必要となります。

  • Windowsのパソコン
  • MT4※(EAを書くMetaEditorはMT4に同梱されています)

※MT4とは、MetaQuotesが開発した外国為替取引(FX)のチャートソフトウェアです。リアルタイムで価格の動き表示し、自動売買が可能なツールです。MT4のダウンロードはこちらから行えます。

MT4を稼働させるためには、Windowsが搭載されたパソコンが必要となります。もちろん、Mac版のMT4も存在します。しかし、今回はWindowsでの使用を前提として進めていきます。Macの方は、Windows環境を整備するか、専用のMac版MT4をご利用ください。

プログラミングの知識は、最初はこの記事で扱う変数・関数・if文・for文の範囲で足ります。売買ルールを「どの条件で買い、どこで決済するか」の文章にできれば、コードに置き換えられます。

MetaEditorで新規EAファイルを作る手順

まずはEAの原型となるファイルを作っていきます。

MT4を開いたら、ツールバーにあるMetaEditor(MetaQuotes Language Editor)の起動ボタンを押します。

MT4のツールバーにあるMetaEditorの起動ボタン

MetaEditorが開いたら「New」(新規作成)ボタンを押します。

MetaEditorのツールバーにある「New」ボタン

EAを作成するのでエキスパートアドバイザーを選択します。

MQL4ウィザードで「Expert Advisor (template)」を選ぶ画面

「Experts¥」の後ろに入力した文字がEAの名前です。好きな名前(例: test)を入力して「次へ」ボタンを押します。

MQLウィザードのName欄に「Experts¥test」とEA名を入力する画面

「次へ」ボタンを押して進めます。

MQLウィザードのイベントハンドラ選択画面で「次へ」を押す

「完了」ボタンを押します。

MQLウィザードのテスターイベントハンドラ画面で「完了」を押す

すると下のようなEAの元となるソースが作成されます。

ウィザードで自動生成されたEAのひな形ソース(test.mq4)

説明のため、ひな形からOnInit()だけを残したソースで見ていきます。OnTick()・OnDeinit()の役割は、後半の「3つのイベント関数」で説明します。

OnInit関数だけを残して簡略化したEAのソースコード

MQL4プログラミングの基礎知識(変数・関数・引数)

MQL4とは、MT4のEAやインジケータを作るためのMetaQuotes社のプログラミング言語です。文法はC言語(C++)に近く、変数・関数・if文・for文といった基本はC言語とほぼ同じ書き方です。

C言語に触れたことがあれば読みやすいはずです。ここではプログラミングがまったく分からない方向けに説明するので、安心してください。

MQLでEAを作成していく前に、まずはプログラミングで知っておきたい基本的な知識を紹介します。

変数、型とは?

変数とは、情報(値)を格納するための「箱」のようなものです。

数字や文字などの情報を変数に保存し、その変数を使って処理を行います。変数を作るときは、どんな情報を入れるかを表す「型」と、変数を呼ぶための「名前」を決めます。

型とは、変数に入れる情報の種類を示すラベルです。型を見れば、変数にどんなデータが入っているかがすぐに分かります。たとえば型が「int」の変数には、整数だけを入れられます。

型の主な種類としては以下のようなものがあります:

  • int(整数型)
  • string(文字列型)
  • double(小数点を含む数値型)

型と変数名を組み合わせた「型 + 変数名」の形で変数を作ります。

型 + 変数名

と書けば変数を作れます。

文字列型の変数なら string mojihensu、整数型の変数なら int suutihensu と書きます。

変数を作ったら、その「箱」に値を入れます。値の入れ方は次のとおりです。

string mojihensu;    // 文字列型の変数を作る
int    suutihensu;   // 整数型の変数を作る
mojihensu  = "文字列";
suutihensu = 1;

これにより、mojihensuという変数には"文字列"という文字データが、suutihensuという変数には1という数値データがそれぞれ格納されます。

コメントとは?

コードを書いていると、自分がどんな意図で書いたのかを忘れることがあります。コメント(メモ)を残しておけば、あとで読み返したときに意図を思い出せます。

コメントは、2つのスラッシュ(//)の後ろに書きます。//から行末までは、プログラムとして実行されません。

例)

// 数値変数に1を入れるよ
suutihensu = 1;

関数とは?

関数とは、値を入れると処理をして結果を返してくれる仕組みです。スマートスピーカーに話しかけると答えが返ってくるのと同じイメージです。

関数は次の形で書きます。

型 + 関数名  + (入力) + { + }

先頭の型は、関数が返す値の型です。値を返さない関数では、型の部分に void と書きます。

値を返す関数では、{}の中で return を使って結果を返します。

例)
入力された文字をそのまま返す関数(オウム返しみたいな関数)

string mojiwokaesuyo(string nyuuryoku) {
   return nyuuryoku;
}

何も返さず、処理だけ実行する関数の例です。受け取った文字を表示するだけで値を返さないので、型は void です。

void mojiwohyouji(string nyuuryoku) {
   Print(nyuuryoku);   // 受け取った文字を「エキスパート」タブに表示するだけ
}

引数とは?

先ほどのオウム返しの関数では、「string nyuuryoku」の部分が引数です。

string mojiwokaesuyo(string nyuuryoku) {
   return nyuuryoku;
}

EAの基本となる3つのイベント関数(OnInit・OnTick・OnDeinit)

EAのプログラムは、上から順に1回だけ実行されるわけではありません。MT4は「EAを起動した」「新しい価格(ティック)が届いた」「EAを外した」といった出来事(イベント)のたびに、決まった名前の関数を呼び出します。EAづくりでは、この3つのイベント関数のどこに処理を書くかを最初に押さえておきます。

初期化関数 OnInit()

int OnInit()
{
   // EAの起動時に1回だけ実行したい処理を書く
   return(INIT_SUCCEEDED);   // 初期化に成功したことをMT4に伝える
}

EAをチャートに載せて起動したとき、最初に呼び出される関数を初期化関数と言います。関数名はOnInit()です。時間足・通貨ペアやパラメータを変えたときも、OnDeinit()のあとにもう一度呼び出されます。

変数の初期化など、最初にやっておきたい処理を書きます。return(INIT_SUCCEEDED);で初期化の成功をMT4に伝えます。古いMQL4ではinit()という名前で、公式リファレンスのとおり互換性のため現在も使えます。

ティックごとに動く関数 OnTick()

void OnTick()
{
   // 新しいティック(価格の更新)が届くたびに実行される
}

新しいティック(価格の更新)が届くたびに呼び出される関数がOnTick()です。古い書き方のstart()に当たります。

注文や決済など、EAのメインの処理は基本的にこの関数に書きます。

終了関数 OnDeinit()

void OnDeinit(const int reason)
{
   // EAをチャートから外したときなどに実行される後片付けの処理
}

EAを終了するときに呼び出される関数が、終了関数のOnDeinit()です(古い書き方ではdeinit())。

EAをチャートから外したときだけでなく、時間足や通貨ペアを切り替えたとき、パラメータを変更したときにも呼ばれます。チャートに描いたオブジェクトの削除など後片付けに使い、ポジションの決済には使わないほうが安全です。

あわせて読むMQL4のOnTick・OnTimer等イベント関数の使い方MQL4のOnTick・OnTimer・OnInit・OnDeinitなどイベント関数が呼ばれるタイミングと書き方を一覧表とコードで解説。足の確定ごとの判定、MQL5(MT5)での違いやOnTradeTransaction等の固有関数もまとめました。

注文を出す関数 OrderSend()

int OrderSend(string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment=NULL, int magic=0, datetime expiration=0, color arrow_color=clrNONE);

OrderSend()を呼ぶと注文を出せます。引数の意味は、後半の「エントリー条件を決める」で詳しく説明します。

EAの3つの基本関数が呼ばれる順番

1
初期化関数 OnInit()
EAを起動するときに最初に呼ばれる
2
ティック関数 OnTick()
チャートのティックが動くたびに呼ばれる。注文などのメイン処理を書く
3
終了関数 OnDeinit()
EAを終了させるときに呼ばれる

最小のサンプルEA(買い注文を出すだけ)

ここまで説明した知識を使って注文を出すEAを作成してみましょう。

#property strict

double Lots     = 0.1;  // 売買ロット数
int    Slippage = 30;   // 許容スリッページ(ポイント単位。5桁・3桁表示の業者なら3pips)
int    MagicNo  = 1;    // マジックナンバー(このEAの注文を見分ける番号)

void OnTick()
{
   // 買い注文を出す(成功するとチケット番号、失敗すると-1が返る)
   int ticket = OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0, "buy", MagicNo, 0, clrBlue);
   if(ticket < 0)
   {
      Print("注文に失敗しました。エラー番号: ", GetLastError());
   }
}

これだけで、ティックが届くたびに買い注文を出し続けるEAが完成します。OrderSend()は失敗すると-1を返すので、戻り値を確認してエラー番号を表示しています。動作確認はストラテジーテスターかデモ口座だけで行い、本番口座では絶対に動かさないでください。

スリッページはpipsではなく「ポイント」単位の整数で指定します。5桁・3桁表示の業者なら30で3pipsです。int型に0.3のような小数を入れると0に切り捨てられ、スリッページを一切許容しない設定になってしまいます。

コンパイルしてEA(.ex4)を作る

ソースコード(.mq4)を書いたら、MT4が実行できるファイル(.ex4)に変換するコンパイルを行います。コンパイルしないと、書いたEAはMT4で動きません。

MetaEditorのツールバーにある「コンパイル」ボタン

「コンパイル」ボタンを押すとコンパイルできますが、このときソースの記述ミスがあった場合エラーとなります。

エラーがでた場合はソースを修正して再度コンパイルしましょう。エラーが解消されない限りコンパイルは成功しません。

よくあるコンパイルエラー

コンパイルエラーは、MetaEditor下部の[エラー]タブに行番号付きで表示されます。初心者がつまずきやすい原因は次のとおりです。

原因例直し方
全角スペース・全角記号が混ざるint Lots(間が全角スペース)すべて半角で書き直す
型名の大文字・小文字の違いString name;MQL4の文字列型は小文字のstring
行末のセミコロン忘れint a = 1文の終わりに;を付ける
型の不一致int Slippage = 0.3;整数型に小数を入れると0に切り捨てられる(警告)。整数で書く
宣言していない変数OrderClose(..., pSlippage, ...)宣言した変数名(Slippage)に揃える
括弧の閉じ忘れ// 処理 for(...){(コメントの後ろにコードを続ける)//から行末まではコメントになるため、コードは改行して書く

ボリンジャーバンド逆張りEAを自作する

基本の形がわかったら、売買ルールを持ったEAを作ってみましょう。題材は、1本前の足がボリンジャーバンドの+2σに届いたら売り、-2σに届いたら買い、反対側のバンドに届いたら決済する逆張りEAです。

外部パラメータ→インジケータの取得→エントリー→損切り→決済の順に部品を作り、最後に1つのファイルにまとめます。

外部パラメータ(extern)を定義する

extern int    MagicNo      = 18907; // マジックナンバー(このEAの注文を見分ける番号)
extern int    Slippage     = 30;    // 許容スリッページ(ポイント。5桁・3桁表示の業者で3pips)
extern double LotSize      = 0.1;   // 売買ロット数
extern int    BandsPeriod  = 20;    // ボリンジャーバンドの期間
extern double BandsDev     = 2.0;   // ボリンジャーバンドの偏差(σ)
extern int    StopLossPips = 30;    // 損切り幅(pips)

変数の前に extern を付けると、その変数は外部パラメータになります。外部パラメータは、EAをチャートに載せるときの設定画面で値を変えられます。

iBandsでボリンジャーバンドの値を取得する

インジケータの値は、MQL4に用意された専用の関数を呼ぶだけで取得できます。

今回はボリンジャーバンドの値を、iBands()という関数で取得します。iBands()の書き方と引数は次のとおりです。

double iBands(string symbol,int timeframe,int period,double deviation,int bands_shift,int applied_price,int mode,int shift);
引数意味
symbol通貨ペア。NULLを指定すると現在のチャートの通貨ペア
timeframe時間足。0を指定すると現在のチャートの時間足
periodボリンジャーバンドの期間
deviation標準偏差の倍率(2なら±2σ)
bands_shiftバンドを右にずらす本数
applied_price計算に使う価格(PRICE_CLOSEなら終値)
mode取得するライン(MODE_UPPER:上バンド、MODE_LOWER:下バンド)
shift何本前の足の値を取得するか(0が現在の足、1が1本前)

実際にボリンジャーバンドの値を取得してみましょう。

// 1本前の足(shift=1)のボリンジャーバンド+2σ(上バンド)の値
double upper = iBands(NULL, 0, BandsPeriod, BandsDev, 0, PRICE_CLOSE, MODE_UPPER, 1);
// 同じく-2σ(下バンド)の値
double lower = iBands(NULL, 0, BandsPeriod, BandsDev, 0, PRICE_CLOSE, MODE_LOWER, 1);

エントリー条件を決める

エントリーは逆張りです。1本前の足の高値が+2σ以上なら売り、安値が-2σ以下なら買いを入れます。損切り価格の計算に使うPipPoint()は、次の「損切り価格をpipsから計算する」で作る関数です。

int ticket;
if(High[1] >= upper)        // 1本前の高値が+2σ以上 → 売り(逆張り)
{
   double sl = NormalizeDouble(Ask + StopLossPips * PipPoint(), Digits);
   ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, sl, 0, "sell", MagicNo, 0, clrRed);
   if(ticket < 0) Print("売り注文に失敗: ", GetLastError());
}
else if(Low[1] <= lower)    // 1本前の安値が-2σ以下 → 買い(逆張り)
{
   double sl = NormalizeDouble(Bid - StopLossPips * PipPoint(), Digits);
   ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, sl, 0, "buy", MagicNo, 0, clrBlue);
   if(ticket < 0) Print("買い注文に失敗: ", GetLastError());
}

High[1]は、1本前の足の高値です。

if文は、括弧の中の条件を満たしたときだけ{}の中の処理を実行します。

注文はOrderSend()で出します。OrderSend()の書き方と引数は次のとおりです。

int OrderSend(string symbol,int cmd,double volume,double price,int slippage,double stoploss,double takeprofit,string comment=NULL,int magic=0,datetime expiration=0,color arrow_color=clrNONE);
引数意味
symbol通貨ペア。現在のチャートの通貨ペアで注文するならSymbol()を渡す
cmd注文の種類(OP_BUY:成行買い、OP_SELL:成行売り など)
volumeロット数
price注文価格(成行の買いはAsk、売りはBid)
slippage許容スリッページ(ポイント単位の整数)
stoploss損切り価格(0なら設定しない)
takeprofit利確価格(0なら設定しない)
comment注文につけるコメント
magicマジックナンバー。EAが自分の注文を見分けるための番号
expiration有効期限(予約注文のみ。成行注文は0)
arrow_colorチャートに表示する矢印の色(clrNONEなら表示しない)

OrderSend()は、成功するとチケット番号、失敗すると-1を返します(MQL4公式リファレンス)。失敗の理由はGetLastError()でエラー番号として取得できます。

損切り価格をpipsから計算する

損切り(SL)と利確(TP)は、OrderSendに「価格」で渡します。価格に直接0.5を足すと、ドル円では0.5円=50pips、ユーロドルでは0.5ドル=5,000pipsになり、通貨ペアによって幅がまったく変わってしまいます。

そこで幅はpipsで決め、1pipsの値幅を掛けて価格に直します。5桁・3桁表示の業者では、最小単位のPoint(ユーロドルなら0.00001)は1pipsの10分の1です。

// 1pipsの値幅を返す(5桁・3桁表示の業者はPointの10倍)
double PipPoint()
{
   if(Digits == 3 || Digits == 5) return(Point * 10);
   return(Point);
}

// ---- 以下は OnTick() の中で使う ----
// 売りの損切り価格:決済に使うAskから30pips上に置き、価格の桁数に丸める
double slSell = NormalizeDouble(Ask + 30 * PipPoint(), Digits);
// 買いの損切り価格:決済に使うBidから30pips下
double slBuy  = NormalizeDouble(Bid - 30 * PipPoint(), Digits);

表示桁数ごとの換算は次のとおりです。PipPoint()はこの表の「1pips」を返すので、どの業者でも30pipsが同じ値幅になります。

価格の表示桁数例Point(最小単位)1pips30pipsの値幅
3桁(円絡み)USDJPY 150.1230.0010.01(Point×10)0.30円
5桁(ドルストレート)EURUSD 1.081230.000010.0001(Point×10)0.0030ドル
2桁(円絡み)USDJPY 150.120.010.01(Point×1)0.30円
4桁(ドルストレート)EURUSD 1.08120.00010.0001(Point×1)0.0030ドル

OrderSendのスリッページ(slippage)も同じPoint単位で指定します。5桁・3桁表示の業者で30を渡すと3pips、2桁・4桁表示の業者では30pipsの許容幅になります。

あわせて読むMT4開発者必見!リスク1%を守るロット計算コードの完全実装ガイドMT4 EA開発でリスク1%を守るロット計算コードを完全解説。MarketInfoを使った実装例や通貨ペアごとの計算方法、落とし穴を紹介します。

決済(クローズ)条件を決める

決済は、反対側のバンドに届いたときです。買いポジションは+2σ、売りポジションは-2σに届いたら決済します。マジックナンバーと通貨ペアが一致しない注文は、別のEAや手動の注文なので対象外にします。

// 保有中の注文を最後の番号から順に確認する
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
   if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
   // 別のEAや別の通貨ペアの注文は対象外
   if(OrderMagicNumber() != MagicNo || OrderSymbol() != Symbol()) continue;

   // 買いポジション:1本前の高値が+2σに届いたら決済
   if(OrderType() == OP_BUY && High[1] >= upper)
   {
      if(!OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrWhite))
         Print("買いポジションの決済に失敗: ", GetLastError());
   }
   // 売りポジション:1本前の安値が-2σに届いたら決済
   else if(OrderType() == OP_SELL && Low[1] <= lower)
   {
      if(!OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrWhite))
         Print("売りポジションの決済に失敗: ", GetLastError());
   }
}

for文を使うと、同じ処理を繰り返せます。

for(int i = OrdersTotal() - 1; i >= 0; i--)は、保有中の注文を最後の番号から0番まで1つずつ処理するという意味です。つまり、今ある注文すべてを順に確認します。後ろから処理するのは、決済すると注文の番号が詰まり、前から数えると確認漏れが出るためです。

全体のコード

部品を1つのファイルにまとめると次のようになります。新しい足ができたときだけ判定し、このEAのポジションがないときだけ新規注文を出します。

#property strict

extern int    MagicNo      = 18907; // マジックナンバー(このEAの注文を見分ける番号)
extern int    Slippage     = 30;    // 許容スリッページ(ポイント。5桁・3桁表示の業者で3pips)
extern double LotSize      = 0.1;   // 売買ロット数
extern int    BandsPeriod  = 20;    // ボリンジャーバンドの期間
extern double BandsDev     = 2.0;   // ボリンジャーバンドの偏差(σ)
extern int    StopLossPips = 30;    // 損切り幅(pips)

//+------------------------------------------------------------------+
//| 1pipsの値幅を返す(5桁・3桁表示の業者はPointの10倍)              |
//+------------------------------------------------------------------+
double PipPoint()
{
   if(Digits == 3 || Digits == 5) return(Point * 10);
   return(Point);
}

//+------------------------------------------------------------------+
//| このEAが保有しているポジション数を数える                          |
//+------------------------------------------------------------------+
int CountMyPositions()
{
   int count = 0;
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      if(OrderMagicNumber() == MagicNo && OrderSymbol() == Symbol()) count++;
   }
   return(count);
}

//+------------------------------------------------------------------+
//| ティックごとに呼ばれる関数                                        |
//+------------------------------------------------------------------+
void OnTick()
{
   // 新しい足ができたときだけ処理する(同じ足で何度も注文しないため)
   static datetime lastBarTime = 0;   // static変数は定数でしか初期化できない
   if(lastBarTime == Time[0]) return;
   lastBarTime = Time[0];

   if(!IsTradeAllowed()) return;

   // 1本前の足のボリンジャーバンド(上:+2σ、下:-2σ)
   double upper = iBands(NULL, 0, BandsPeriod, BandsDev, 0, PRICE_CLOSE, MODE_UPPER, 1);
   double lower = iBands(NULL, 0, BandsPeriod, BandsDev, 0, PRICE_CLOSE, MODE_LOWER, 1);

   // ---- 決済:反対側のバンドに届いたら決済する ----
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      if(OrderMagicNumber() != MagicNo || OrderSymbol() != Symbol()) continue;

      if(OrderType() == OP_BUY && High[1] >= upper)
      {
         if(!OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrWhite))
            Print("買いポジションの決済に失敗: ", GetLastError());
      }
      else if(OrderType() == OP_SELL && Low[1] <= lower)
      {
         if(!OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrWhite))
            Print("売りポジションの決済に失敗: ", GetLastError());
      }
   }

   // ---- 新規注文:このEAのポジションがないときだけ ----
   if(CountMyPositions() > 0) return;

   // 損切り幅がブローカーの最小距離(ストップレベル)より狭いとエラー130になる
   double slWidth = StopLossPips * PipPoint();
   if(slWidth < MarketInfo(Symbol(), MODE_STOPLEVEL) * Point)
   {
      Print("損切り幅がストップレベルより狭いため発注しません");
      return;
   }

   int ticket;
   if(High[1] >= upper)        // +2σに届いた → 売り(逆張り)
   {
      double sl = NormalizeDouble(Ask + slWidth, Digits);
      ticket = OrderSend(Symbol(), OP_SELL, LotSize, Bid, Slippage, sl, 0, "sell", MagicNo, 0, clrRed);
      if(ticket < 0) Print("売り注文に失敗: ", GetLastError());
   }
   else if(Low[1] <= lower)    // -2σに届いた → 買い(逆張り)
   {
      double sl = NormalizeDouble(Bid - slWidth, Digits);
      ticket = OrderSend(Symbol(), OP_BUY, LotSize, Ask, Slippage, sl, 0, "buy", MagicNo, 0, clrBlue);
      if(ticket < 0) Print("買い注文に失敗: ", GetLastError());
   }
}

決済した足で反対側の条件も満たしていれば、そのまま逆方向のポジションを持ちます(ドテン)。損切り幅がブローカーのストップレベル(MarketInfo()のMODE_STOPLEVEL)より狭いとエラー130になるため、発注前に確認しています。コードをMetaEditorに貼り付けてコンパイルすれば完成です。

完成したらバックテストで確かめる

EAが完成したらバックテストをしましょう。
バックテストの方法はこちらに記載しております。

自作EAが動かない・注文が通らないときは何を確認する?

コンパイルが通っても、チャートに載せたEAが注文を出さないことがあります。原因の多くは、MT4側の設定か、注文の値(価格・損切り幅・ロット)の誤りです。まずMT4下部の[ターミナル]→[エキスパート]タブに出るメッセージとエラー番号を確認します。

  • ツールバーの自動売買ボタンがオンになっているか、EAのプロパティで自動売買を許可しているか
  • エラー130(ERR_INVALID_STOPS):損切り・利確が現在値に近すぎる、または価格の桁数に丸めていない
  • エラー129(ERR_INVALID_PRICE):買いにBid、売りにAskを渡している、または丸めていない価格を渡している
  • エラー131(ERR_INVALID_TRADE_VOLUME):ロット数が業者の最小ロット・刻みに合っていない
  • エラー134(ERR_NOT_ENOUGH_MONEY):証拠金が足りない

エラー番号の一覧はMQL4公式リファレンスのエラーコード表で確認できます。

プログラミング未経験でもEAを作る方法(ノーコード・ChatGPT・代行・書籍)

ここまでのコードを読んで「自分で全部書くのは大変そうだ」と感じた方もいると思います。EAの作り方はMQL4で手書きする方法だけではありません。プログラミングなしで作れるツール、ChatGPTにコードを書かせる方法、書籍で体系的に学ぶ方法、作成代行に頼む方法があり、かけられる時間と予算で選べます。

作り方費用の目安(2026年9月時点)向いている人注意点
MQL4で自作無料(MT4・MetaEditor)ロジックを細かく作り込みたい習得に時間がかかる
EA作成ツール(EAつくーる)29,800円(GogoJungle販売ページ)コードを書かずに作りたいツールで設定できる条件の範囲で作る
ChatGPT無料プランから利用可売買条件を文章で説明できるコードの誤りを見抜く基礎知識が要る
作成代行シンプルなEAでソースコード込み1.5万〜3万円前後時間がなく仕様が固まっている仕様を文章にまとめる必要がある

作成代行の料金はEA作成代行の料金相場の記事で、公開価格をもとに比較しています。

プログラミングなしで作れるEA作成ツール(EAつくーる)

コードの理解が難しい場合は、ソースコードを自動生成してくれるEAつくーるというソフトをおすすめします。EAつくーる(MT4・MT5対応、2026年9月時点でGogoJungleで販売中)を使えば、自分の作りたい条件でEAをプログラミングなしで作れます。

できあがったソースコードを読んでEA作成を学ぶ使い方もできます。プログラミングができない人でも、EAを作れます。

実は、私自身はプログラミングの知識があるため、このツールには頼らなかったのですが、後になってその効率の良さに驚きました。挫折しそうになる前に、この商品を使ってみてください。

また、一通りの知識が身についた後も、このツールはコード作成の時短ツールとして役立ちます。プログラミング学習の加速には欠かせない商品ですよ。

ノーコードで誰でも簡単EA開発、完全自動・半自動取引であなたのトレードを少しでも楽に!ノーコードで誰でも簡単EA開発、完全自動・半自動取引であなたのトレードを少しでも楽に! | GogoJungle

使い方も動画で解説されているので安心です。

ChatGPTでEAのコードを作る

ChatGPTに売買条件を伝えて、MQL4のコードを書かせる方法もあります。一度で完成することはまれで、「条件を絞って指示する→コンパイルエラーを貼って直してもらう→バックテストで確かめる」を繰り返すのが基本です。

指示に「MQL4で作成してください」と入れないとMQL5の書き方が混ざり、コンパイルが通らないことがあります。この記事の基礎知識があれば、返ってきたコードのどこが売買ルールなのかを自分で読めます。

あわせて読むChatGPTでEA作成する手順|MT4用プロンプト例と注意点ChatGPTでMT4のEAを作成する手順を、2023年にGPT-4で試したプロンプト・生成コード・バックテスト結果で解説。コンパイルエラーの直し方、AIが間違えやすいロット計算の確認ポイント、Claudeなど他のAIでの作り方も紹介します。

書籍でMQL4を体系的に学ぶ

あわせて読むMT4(MQL4) EA開発&運用を学ぶためのオススメ書籍、本MT4(MQL4)のEA開発と運用を学ぶオススメ書籍をレベル別に紹介。書籍選びで失敗しない3つの視点や学習リソースも解説します。

もし1からしっかりと学びたいならば、入門として「FXメタトレーダー入門 現代の錬金術師シリーズ」という書籍の購入をおすすめします↓

私も初心者時代にお世話になった書籍です。こちらの書籍をマスターした後に次のステップに進みましょう。

2008年1月発行の書籍のため、MetaEditorの画面や関数名(OnInit()・OnTick()など)は現在の仕様と異なる部分があります。本記事のコードと照らし合わせながら読むと迷いません。

時間がない場合はEA作成代行に依頼する

EA作成代行は、作りたいEAを代わりに作ってくれるサービスです。

スキルや時間はなくてもアイデアがある方は、依頼するだけでEAを作ってもらえます。

ただし、代行を頼むには作りたいEAの仕様を文章で正確に伝える必要があります。まったくの初心者がいきなり依頼するのは、少しハードルが高いかもしれません。

あわせて読むEA作成代行の料金相場は?依頼先比較と失敗しない仕様書の書き方EA作成代行の料金は基本5,000円〜3万円、ソースコード込みで1.5万〜3万円前後(2026年9月時点の公開価格)。ココナラ・代行業者の比較、.mq4と著作権の扱い、失敗しない仕様書テンプレートを解説します。

FXのEA自作に関するよくある質問

FXのEAを自作するときに、初心者からよく聞かれる質問をまとめました。費用、MQL4とMQL5の違い、保存場所、注文エラーの原因など、コードを書く前後でつまずきやすい点を中心に答えています。詳しい手順やコードは、各見出しの本文もあわせて確認してください。

Q1. FXのEA自作は、プログラミング未経験の初心者でもできますか?

A1. できます。買い注文を出すだけの数行のEAから始め、変数・関数・if文・OrderSend()の使い方を覚えれば、この記事のボリンジャーバンド逆張りEAのような単純なロジックは書けます。コードを書くのが難しければ、EA作成ツールやChatGPTを併用する方法もあります。

Q2. EAの自作にお金はかかりますか?

A2. MQL4で自作するなら、MT4とMetaEditorは無料で使えるため費用はかかりません。EA作成ツールのEAつくーるは29,800円(2026年9月時点のGogoJungle販売価格)、作成代行はシンプルなEAでソースコード込み1.5万〜3万円前後が目安です。

Q3. MQL4とMQL5のどちらでEAを作ればいいですか?

A3. 使う取引ツールで決まります。MT4ならMQL4、MT5ならMQL5で作ります。MQL4で書いたEAはMT5ではそのまま動かないため、口座を開く業者がどちらに対応しているかを先に確認してください。

Q4. 作ったEA(.mq4・.ex4)はどこに保存されますか?

A4. MT4のデータフォルダ内にある MQL4\Experts フォルダに保存されます。MT4のメニュー[ファイル]→[データフォルダを開く]で開けます。コンパイルに成功すると、同じフォルダに実行ファイルの .ex4 ができます。

Q5. 自作EAの注文がエラー130になるのはなぜですか?

A5. エラー130(ERR_INVALID_STOPS)は、損切り・利確の価格が現在値に近すぎるか、価格の桁数に丸められていないときに出ます。損切り幅をpipsで決めて1pipsの値幅を掛け、NormalizeDouble()で桁数に丸めたうえで、ブローカーのストップレベル(MarketInfo()のMODE_STOPLEVEL)より離して指定します。

Q6. init()・start()とOnInit()・OnTick()は何が違いますか?

A6. init()・start()・deinit()は古いMQL4の関数名で、互換性のために現在も使えます。現在のMetaEditorのひな形はOnInit()・OnTick()・OnDeinit()で作られるため、新しく作るEAはこちらで書くのがおすすめです。

Q7. 自作EAはすぐに本番口座で動かしてもいいですか?

A7. すぐには使わず、ストラテジーテスターでのバックテストとデモ口座での動作確認を先に行ってください。発注条件の誤りやエラーは、実際に動かしてみないと見つからないことが多いためです。

Q8. 自作したEAで儲かりますか?

A8. 自作しただけで儲かるわけではありません。損益を決めるのはコードではなく売買ロジックで、バックテストの成績が良くても、過去の値動きに合わせすぎた(過剰最適化)EAは実運用で成績が崩れることがあります。長い期間のバックテストとデモ口座での動作確認を経てから、少額で動かしてください。

まとめ:FXのEA自作はMetaEditorとMQL4で始められる

FXのEAは、MetaEditorで.mq4ファイルを作り、MQL4でコードを書いてコンパイルすれば自作できます。押さえておきたい点は次の5つです。

  • 処理はOnInit(起動時)・OnTick(ティックごと)・OnDeinit(終了時)の3つのイベント関数に書き分ける
  • 注文はOrderSendで出し、戻り値(成功時はチケット番号、失敗時は-1)を確認する
  • 損切り・利確はpipsで幅を決め、1pipsの値幅(5桁・3桁表示の業者はPointの10倍)を掛けて価格に直す
  • 完成したらバックテストとデモ口座で動作を確かめてから本番口座で使う
  • 自分で書くのが大変なら、EA作成ツール・ChatGPT・作成代行も選べる
※本記事は情報提供を目的としたもので、特定のEA・金融商品の売買を推奨するものではありません。FX(外国為替証拠金取引)は元本が保証されておらず、レバレッジにより預けた証拠金を上回る損失が発生する可能性があります。記事内の成績・数値は過去の実績であり、将来の利益を保証するものではありません。投資の最終判断はご自身の責任でお願いします。

QUEST CLEAR!

最後まで読んでくれて、ありがとうございます。
つぎのクエストは、この下から選べます。

トム

この記事を書いた人トム

・現役エンジニア(本業はJavaバックエンド)
・以前はMT4のEAを自作してFX自動売買をしていました(現在は引退)
・今はポイ活を「時給」で検証・攻略しています