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

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

EAプログラミング

ChatGPTでEA作成する手順|MT4用プロンプト例と注意点

↻

トム

トム

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

EA開発もここまできたか…ついにAIでプログラミングする時代に突入し、EAもAIで作れそうな時代になってきました。

結論から言うと、ChatGPTでMT4のEAは作成できます。ただし一度の指示で完成することはまれで、「条件を絞って指示する→コンパイルエラーを直してもらう→バックテストで確かめる」を何度か繰り返す必要があります。

筆者は2023年4〜5月に、ChatGPT(当時の有料プランで使えたGPT-4)で実際にEAを作りました。その手順を、プロンプトの画面・生成されたコード・バックテスト結果とあわせて紹介します。

あわせて、AIが書いたMQL4コードで見落としやすいロット計算の誤りと、2026年時点でEA作成に使えるAIについても解説します。

この記事でわかること

  • ChatGPTでMT4のEAを作る4ステップと、実際に使ったプロンプト
  • コンパイルを通すためのプロンプトのコツ
  • AIが書いたコードのロット計算の誤りと修正例
  • 2026年9月時点でEA作成に使えるAIとプラン

ChatGPTでMT4のEAを作成する4ステップ

EA(Expert Advisor)とは、MT4上で売買を自動で行うプログラムのことで、MQL4という専用の言語で書かれています。ChatGPTに作ってもらうのは、このMQL4のソースコードです。

作成は次の4ステップで進めます。

ChatGPTでEAを作成する4ステップ

1
戦略のアイデアを明確にする
2
ChatGPTで必要な情報を集める
3
トレード戦略のルールを決める
4
コーディングとテスト

ステップ1:トレード戦略のアイデアを明確にする

ChatGPTに「MT4のEAを作成するにあたり、有効なトレード戦略をいくつか教えてください」と質問し、トレンドフォローやブレイクアウトなどの戦略が返ってきた画面

ChatGPTの回答の続き。指標ベース・ファンダメンタル分析・センチメント分析の戦略と、バックテストやフォワードテストを勧める説明

まず始めに、どのようなトレーディング戦略をEAに実装したいかを明確にしましょう。具体的な目標や戦略を決めると、目的のEAが完成しやすいです。

たとえば「トレンドフォローか逆張りか」「損切りは何pipsか」「どの通貨ペアで動かすか」などです。

ステップ2:ChatGPTで必要な情報を集める

戦略がイメージしづらい場合、ChatGPTを使って、トレーディング戦略に関連する情報やアイデアを集めます。たとえば、利用したいインジケーターやパラメータの最適化方法、リスク管理手法などを調べられます。

また、ChatGPTの検索機能を使えば、Web上の解説記事や他のトレーダーの手法を調べてもらうこともできます。ただし回答が正しいとは限らないので、元の情報源も確認しましょう。

ステップ3:ルールを決めてEAのコードを書いてもらう

ChatGPTから得た情報をもとに、トレード戦略の具体的なルールを決定します。

エントリーやエグジットのタイミング、ストップロスやテイクプロフィットの設定、ポジションサイズの調整など、EAがどのように動作するかを定めましょう。戦略のルールが決まったら、次のステップであるプログラミングに進みましょう。

試しに思いつくままにChatGPTに命令をして、EAを作成してもらってみました。

トレンドフォロー・1時間足・USDJPYなど11個の条件を付けて、ChatGPTにMT4のEAのソースコードを依頼した画面

返ってきたソースコードが以下です。

//+------------------------------------------------------------------+
//|                                             TrendFollowingEA.mq4 |
//|                                Copyright © 2023, Your Name Here |
//|                                                      example.com |
//+------------------------------------------------------------------+
// Input parameters
input int    FastMA_Period = 10;
input int    SlowMA_Period = 20;
input int    ADX_Period = 14;
input double RiskPercentage = 2; // Risk % of the account balance per trade
input double StopLoss_ATR_Multiplier = 2; // Stop Loss in terms of ATR
input double TakeProfit_ATR_Multiplier = 4; // Take Profit in terms of ATR
input int    ATR_Period = 14; // ATR period for stop loss and take profit calculation
// Global variables
int FastMA_Handle, SlowMA_Handle, ADX_Handle, ATR_Handle;
double Point;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Set up handles for the indicators
   FastMA_Handle = iMA(_Symbol, PERIOD_H1, FastMA_Period, 0, MODE_SMA, PRICE_CLOSE);
   SlowMA_Handle = iMA(_Symbol, PERIOD_H1, SlowMA_Period, 0, MODE_SMA, PRICE_CLOSE);
   ADX_Handle = iADX(_Symbol, PERIOD_H1, ADX_Period);
   ATR_Handle = iATR(_Symbol, PERIOD_H1, ATR_Period);
   
   // Get the point value for the current symbol
   Point = _Point;
   
   // If any errors occur during initialization, return an error code
   if(FastMA_Handle == INVALID_HANDLE || SlowMA_Handle == INVALID_HANDLE ||
      ADX_Handle == INVALID_HANDLE || ATR_Handle == INVALID_HANDLE)
     {
      Print("Error: Unable to create indicator handles.");
      return INIT_FAILED;
     }
   
   return INIT_SUCCEEDED;
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // Release the indicator handles
   IndicatorRelease(FastMA_Handle);
   IndicatorRelease(SlowMA_Handle);
   IndicatorRelease(ADX_Handle);
   IndicatorRelease(ATR_Handle);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // Check if there is an open order
   if(PositionSelect(_Symbol))
     {
      // Check if the market conditions have changed and if it's necessary to close the position
      if(CheckExitConditions())
        {
         PositionClose(_Symbol);
        }
     }
   else
     {
      // If no open positions, check for entry conditions and place a new order
      int orderType = CheckEntryConditions();
      if(orderType != -1)
        {
         PlaceOrder(orderType);
        }
     }
  }
//+------------------------------------------------------------------+
//| Check for entry
//+------------------------------------------------------------------+
int CheckEntryConditions()
{
double FastMA_Current = iMAOnArray(MABuffer,0,0);
double FastMA_Previous = iMAOnArray(MABuffer,0,1);
double SlowMA_Current = iMAOnArray(MABuffer,1,0);
double SlowMA_Previous = iMAOnArray(MABuffer,1,1);
double ADX_Main = iADXOnArray(ADXBuffer,0,0);
double ADX_Positive = iADXOnArray(ADXBuffer,1,0);
double ADX_Negative = iADXOnArray(ADXBuffer,2,0);
// Buy entry condition: Fast MA crosses above Slow MA and ADX indicates a strong trend
if(FastMA_Current > SlowMA_Current && FastMA_Previous <= SlowMA_Previous && ADX_Main > 25 && ADX_Positive > ADX_Negative)
{
return OP_BUY;
}
// Sell entry condition: Fast MA crosses below Slow MA and ADX indicates a strong trend
else if(FastMA_Current < SlowMA_Current && FastMA_Previous >= SlowMA_Previous && ADX_Main > 25 && ADX_Positive < ADX_Negative)
{
return OP_SELL;
}
// No entry conditions met
else
{
return -1;
}
}
//+------------------------------------------------------------------+
//| Check for exit conditions |
//+------------------------------------------------------------------+
bool CheckExitConditions()
{
// Retrieve the current position type
int positionType = PositionGetInteger(POSITION_TYPE);
// Calculate the moving averages
double FastMA_Current = iMAOnArray(MABuffer,0,0);
double SlowMA_Current = iMAOnArray(MABuffer,1,0);
// Check if the moving averages have crossed in the opposite direction
if((positionType == POSITION_TYPE_BUY && FastMA_Current < SlowMA_Current) ||
(positionType == POSITION_TYPE_SELL && FastMA_Current > SlowMA_Current))
{
return true;
}
else
{
return false;
}
}
//+------------------------------------------------------------------+
//| Place an order with appropriate stop loss, take profit, and lot size |
//+------------------------------------------------------------------+
void PlaceOrder(int orderType)
{
// Calculate stop loss and take profit based on ATR
double ATR = iATR(_Symbol, PERIOD_H1, ATR_Period, 0);
double stopLoss = ATR * StopLoss_ATR_Multiplier;
double takeProfit = ATR * TakeProfit_ATR_Multiplier;
// Calculate the entry price, stop loss and take profit levels
double entryPrice = (orderType == OP_BUY) ? Ask : Bid;
double stopLossLevel = (orderType == OP_BUY) ? entryPrice - stopLoss : entryPrice + stopLoss;
double takeProfitLevel = (orderType == OP_BUY) ? entryPrice + takeProfit : entryPrice - takeProfit;
// Calculate the appropriate lot size based on risk management
double lotSize = CalculateLotSize(stopLoss);
// Place the order
ulong ticket;
if(orderType == OP_BUY)
{
ticket = OrderSend(_Symbol, OP_BUY, lotSize, Ask, 10, stopLossLevel, takeProfitLevel);
}
else
{
ticket = OrderSend(_Symbol, OP_SELL, lotSize, Bid, 10, stopLossLevel, takeProfitLevel);
}
// Checkfor errors and print a message if necessary
if(ticket == 0)
{
Print("Error placing order: ", GetLastError());
}
else
{
Print("Order placed successfully: Ticket #", ticket);
}
}
//+------------------------------------------------------------------+
//| Calculate the lot size based on risk management |
//+------------------------------------------------------------------+
double CalculateLotSize(double stopLoss)
{
double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE);
double riskAmount = accountBalance * (RiskPercentage / 100);
double lotSize = riskAmount / (stopLoss * Point * AccountInfoInteger(ACCOUNT_LEVERAGE));
// Adjust the lot size to the minimum and maximum lot size allowed by the broker
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
lotSize = NormalizeDouble(lotSize, 2);
lotSize = MathMin(MathMax(lotSize, minLot), maxLot);
return lotSize;
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[])
{
ArraySetAsSeries(close, true);
// Calculate the moving averages
int fastShift = 0, slowShift = 0;
while(fastShift < rates_total && slowShift < rates_total)
{
MABuffer[0][fastShift] = iMAOnArray(close, FastMA_Period, fastShift);
MABuffer[1][slowShift] = iMAOnArray(close, SlowMA_Period, slowShift);
fastShift++;
slowShift++;
}
// Calculate the ADX values
int adxShift = 0;
while(adxShift < rates_total)
{
ADXBuffer[0][adxShift] = iADXOnArray(high, low, close, ADX_Period, adxShift, MODE_MAIN);
ADXBuffer[1][adxShift] = iADXOnArray(high, low, close, ADX_Period, adxShift, MODE_PLUSDI);
ADXBuffer[2][adxShift] = iADXOnArray(high, low, close, ADX_Period, adxShift, MODE_MINUSDI);
adxShift++;
}
return rates_total;
}
//+------------------------------------------------------------------+

できたソースコードをそのままコンパイルしてみると、コンパイルエラーとなってしまいました。

どうやら生成してもらったソースコードをそのまま使用するのは難しそうです。

コンパイルできない理由
このコードにはMQL5の書き方が混ざっています。原因は後半の「コンパイルが通らない原因はMQL5の書き方の混在」で解説しています。

あわせて読むFX EAの自作・作り方|MQL4で始める初心者向けEA開発入門FXのEAはMT4付属のMetaEditorとMQL4で無料で自作できます。必要なもの・難しさ、ひな形作成からコンパイル・バックテストまでの手順と全コード、未経験者向けのノーコードツール・ChatGPT・作成代行も解説します。

ステップ4:条件を絞ってコードを作り直しテストする

さきほどChatGPTにEAのソースコードを丸々作成してもらいましたが、もうちょっと工夫して作成していきましょう。

先ほどは条件を入れすぎたので、今度は5つに絞り、まずEAの原型となるコードを作ってもらいます。

条件を5個に絞り「MQL4で作成してください」を加えて、EAのソースコードを依頼し直したプロンプト

//+------------------------------------------------------------------+
//|                                              TrendFollowingEA.mq4 |
//|                                  Copyright 2023, Company-Name.com |
//|                                                    Your Name Here |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Company-Name.com"
#property link      "http://www.company-name.com"
#property version   "1.00"
#property strict
// Input parameters
extern double StopLoss = 50;
extern double TakeProfit = 100;
extern int Slippage = 3;
extern int MagicNumber = 12345;
extern double LotSize = 0.1;
extern int FastEMA = 12;
extern int SlowEMA = 26;
extern int SignalEMA = 9;
int OnInit()
{
   // Initialization
   return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
   // Deinitialization
}
void OnTick()
{
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   
   datetime CurrentTime = TimeCurrent();
   
   // Check if there are open orders for this EA
   bool hasOpenOrders = false;
   for(int i = 0; i < OrdersTotal(); i++) {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
         if(OrderSymbol() == _Symbol && OrderMagicNumber() == MagicNumber) {
            hasOpenOrders = true;
            break;
         }
      }
   }
   
   if(!hasOpenOrders) {
      // Calculate EMA values
      double fastEMA = iMA(_Symbol, PERIOD_H1, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
      double slowEMA = iMA(_Symbol, PERIOD_H1, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
      
      // Calculate MACD values
      double mainMACD = iMACD(_Symbol, PERIOD_H1, FastEMA, SlowEMA, SignalEMA, PRICE_CLOSE, MODE_MAIN, 1);
      double signalMACD = iMACD(_Symbol, PERIOD_H1, FastEMA, SlowEMA, SignalEMA, PRICE_CLOSE, MODE_SIGNAL, 1);
      
      // Determine the trade signal
      bool buySignal = fastEMA > slowEMA && mainMACD > signalMACD;
      bool sellSignal = fastEMA < slowEMA && mainMACD < signalMACD;
      
      // Open buy order
      if(buySignal) {
         double buyStopLoss = Bid - StopLoss * _Point;
         double buyTakeProfit = Bid + TakeProfit * _Point;
         OrderSend(_Symbol, OP_BUY, LotSize, Ask, Slippage, buyStopLoss, buyTakeProfit, "Buy Order", MagicNumber, 0, clrBlue);
      }
      
      // Open sell order
      if(sellSignal) {
         double sellStopLoss = Ask + StopLoss * _Point;
         double sellTakeProfit = Ask - TakeProfit * _Point;
         OrderSend(_Symbol, OP_SELL, LotSize, Bid, Slippage, sellStopLoss, sellTakeProfit, "Sell Order", MagicNumber, 0, clrRed);
      }
   }
}
//+------------------------------------------------------------------+

今度はコンパイルが上手く通りましたが、このままだとバックテストは撃沈でした。

あわせて読むMT4バックテストのやり方【初心者向け】EAの設定と結果の見方MT4でEAをバックテストするやり方を画像付きで解説。ヒストリカルデータの準備、ストラテジーテスターのモデル・スプレッド設定、モデリング品質90%の意味、PF・最大ドローダウンなど結果の見方までわかります。

ChatGPTでEAのバックテスト成績を改善する方法

成績の改善は、コードを貼って修正を頼み、返ってきたコードをバックテストで確かめる作業の繰り返しです。先ほどはバックテストが撃沈でしたが、もうちょっと改良をしてもらいましょう。

次の指示を出し、返ってきたコードでバックテストする作業を繰り返しました。

「以下のEAのソースコードのバックテストの成績が良くなるように修正をしてください」とChatGPTに依頼したプロンプト

何回か繰り返した結果のコードとバックテストです。

//+------------------------------------------------------------------+
//|                                              TrendFollowingEA.mq4 |
//|                                  Copyright 2023, Company-Name.com |
//|                                                    Your Name Here |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Company-Name.com"
#property link      "http://www.company-name.com"
#property version   "1.00"
#property strict
// Input parameters
extern double StopLoss = 50;
extern double TakeProfit = 100;
extern int Slippage = 3;
extern int MagicNumber = 12345;
extern double FixedLotSize = 0.1;
extern double RiskPercentage = 2.0;
extern int FastEMA = 12;
extern int SlowEMA = 26;
extern int SignalEMA = 9;
extern int RSI_Period = 14;
extern int RSI_Level = 50;
extern int ATR_Period = 14;
int OnInit()
{
   // Initialization
   return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
   // Deinitialization
}
void OnTick()
{
   double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   
   datetime CurrentTime = TimeCurrent();
   
   // Check if there are open orders for this EA
   bool hasOpenOrders = false;
   for(int i = 0; i < OrdersTotal(); i++) {
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) {
         if(OrderSymbol() == _Symbol && OrderMagicNumber() == MagicNumber) {
            hasOpenOrders = true;
            break;
         }
      }
   }
   
   if(!hasOpenOrders) {
      // Calculate EMA values
      double fastEMA = iMA(_Symbol, PERIOD_H1, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
      double slowEMA = iMA(_Symbol, PERIOD_H1, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 1);
      double fastEMA_previous = iMA(_Symbol, PERIOD_H1, FastEMA, 0, MODE_EMA, PRICE_CLOSE, 2);
      double slowEMA_previous = iMA(_Symbol, PERIOD_H1, SlowEMA, 0, MODE_EMA, PRICE_CLOSE, 2);
      
      // Calculate MACD values
      double mainMACD = iMACD(_Symbol, PERIOD_H1, FastEMA, SlowEMA, SignalEMA, PRICE_CLOSE, MODE_MAIN, 1);
      double signalMACD = iMACD(_Symbol, PERIOD_H1, FastEMA, SlowEMA, SignalEMA, PRICE_CLOSE, MODE_SIGNAL, 1);
      
      // Calculate RSI values
      double currentRSI = iRSI(_Symbol, PERIOD_H1, RSI_Period, PRICE_CLOSE, 1);
      double previousRSI = iRSI(_Symbol, PERIOD_H1, RSI_Period, PRICE_CLOSE, 2);
      
      // Calculate ATR values
      double currentATR = iATR(_Symbol, PERIOD_H1, ATR_Period, 1);
  
      // Determine the trade signal
      bool buySignal = fastEMA > slowEMA && fastEMA_previous <= slowEMA_previous && mainMACD > signalMACD && currentRSI > RSI_Level && previousRSI <= RSI_Level;
      bool sellSignal = fastEMA < slowEMA && fastEMA_previous >= slowEMA_previous && mainMACD < signalMACD && currentRSI < RSI_Level && previousRSI >= RSI_Level;
      // Calculate the dynamic lot size based on the account balance and risk percentage
      double dynamicLotSize = NormalizeDouble((AccountBalance() * (RiskPercentage / 100)) / (StopLoss * 10), 2);
      // Calculate dynamic StopLoss and TakeProfit based on ATR
      double dynamicStopLoss = StopLoss * _Point + currentATR;
      double dynamicTakeProfit = TakeProfit * _Point + currentATR;
      // Open buy order
      if(buySignal) {
         double buyStopLoss = Bid - dynamicStopLoss;
         double buyTakeProfit = Bid + dynamicTakeProfit;
         OrderSend(_Symbol, OP_BUY, MathMax(FixedLotSize, dynamicLotSize), Ask, Slippage, buyStopLoss, buyTakeProfit, "Buy Order", MagicNumber, 0, clrBlue);
      }
  
      // Open sell order
      if(sellSignal) {
         double sellStopLoss = Ask + dynamicStopLoss;
         double sellTakeProfit = Ask - dynamicTakeProfit;
         OrderSend(_Symbol, OP_SELL, MathMax(FixedLotSize, dynamicLotSize), Bid, Slippage, sellStopLoss, sellTakeProfit, "Sell Order", MagicNumber, 0, clrRed);
      }
   }
}

このコードをそのまま使わないでください
ロット計算の式に単位の誤りがあり、円口座では1回の損切りで残高の大半を失う計算になります。詳しくは後半の「ロット計算は口座通貨と損切り幅の単位を検算する」を参照してください。

ChatGPTで改良したEAのMT4ストラテジーテスターの残高曲線。約130回の取引で残高が増えた後、後半は横ばい

バックテスト

ChatGPTにパラメータの最適化範囲を聞く

EAの成績は、パラメータの設定で大きく変わります。筆者はコードを貼り付けて「パラメータの最適化の有効な範囲を教えてください」と頼み、各パラメータの範囲を出してもらいました。

「以下のソースのパラメータの最適化の有効な範囲を教えてください」とChatGPTに質問したプロンプト

ChatGPTが回答したStopLossやFastEMAなど10個のパラメータの最適化範囲と、過学習への注意

最適化の目安はわかりました。合っているかは分かりませんが、この範囲を参考に手動で最適化していきます。最適化の作業だけは、どうしても手動になってしまいます。

あわせて読むカーブフィッティングとは?EAの過剰最適化を見抜く方法と回避策8つカーブフィッティング(過剰最適化)とは、最適化のしすぎで過去の相場にだけ合ったEAになることです。起きる仕組みと8つの回避方法、フォワードテストや販売ページで見抜く確認方法を、目安の数値つきで解説します。

最終的にできたEAとバックテスト結果

適当に最適化し、今回できたEAをバックテストしてみます。まだ本番では使えなさそうですが、1時間もかからず、ここまでのEAを作れました。恐るべきChatGPT!

今回作ったEA(最適化前)は以下からダウンロード可能です。絶対本番では動かさないでください。

ダウンロード

中身は2023年5月2日にコンパイルしたex4ファイルだけで、ソースコードは同梱していません。後半で解説するロット計算などの確認をしていない検証用です。

手動で最適化したEAのバックテスト残高曲線。約160回の取引で増減しながら右肩上がり

ChatGPTでEAを作るときのプロンプトのコツ

2023年の検証では、同じ「トレンドフォローのEA」を頼んでも、指示の出し方でコンパイルが通るかどうかが分かれました。

条件を11個並べた1回目は、コンパイルエラーになるコードが返ってきました。条件を5個に絞り、使う言語を指定し直した2回目は、そのままコンパイルが通っています。この差から分かったコツを3つにまとめます。

条件は一度に詰め込まず段階的に足す

1回目の指示には、時間足や通貨ペアのほかにも条件を詰め込んでいました。「ストップロスとロットをボラティリティに合わせて変動」「ペイオフレシオは1以上」「資金管理機能」「相場に合わせて決済」などです。

条件が増えるほどコードは長くなり、1か所の誤りでコンパイル全体が止まります。まず売買ロジックだけの原型を作り、コンパイルが通ったら損切りやロット計算を1つずつ足していくほうが、どこで壊れたかを追えます。

「MQL4で作成して」と言語を明記する

MT4のEAはMQL4、MT5のEAはMQL5で書きます。名前は似ていますが、インジケーターの値の取り方や注文の出し方が違う別の言語です。

1回目のコードは、インジケーターを「ハンドル」で扱う、ポジションをPositionSelect()で調べるなど、MQL5の書き方が混ざっていました。2回目は条件に「MQL4で作成してください」を加えたところ、MQL4のOrderSelect()やOrderSend()を使うコードになり、コンパイルが通りました。

エラー文やバックテストの結果を渡して直してもらう

コンパイルエラーが出たら、MetaEditorに表示されたエラーメッセージを行番号ごとコピーし、コードと一緒にAIへ渡します。「このエラーを修正して」と頼めば、原因の説明と修正版が返ってきます。

成績の改善も同じ考え方です。筆者は「以下のEAのソースコードのバックテストの成績が良くなるように修正をしてください」と頼み、返ってきたコードをバックテストする作業を何度か繰り返しました。

ただし、AIは修正のたびに頼んでいない箇所まで書き換えることがあります。前のコードと見比べて、ロット計算や損切り幅に意図しない変更が入っていないかを確認してください。

ChatGPTが書いたEAはそのまま使える?3つの確認ポイント

AIが書いたコードは、コンパイルが通っても正しく動くとは限りません。この記事で紹介した3本のコードを読み直すと、動かない理由と、動いても危ない箇所がそれぞれ見つかりました。

自分のEAとして使う前に確認したい点を、3つに分けて説明します。

コンパイルが通らない原因はMQL5の書き方の混在

条件を11個並べた1本目のコードを、MQL4の公式リファレンスと照らし合わせると、次の食い違いがありました。

  • iMA()・iADX()・iATR()はMQL4では値そのものを返す関数なのに、MQL5のように「ハンドル」を受け取る書き方をしている(引数の数も足りない)
  • IndicatorRelease()やPositionSelect()、PositionGetInteger()など、MQL4にない関数を呼んでいる
  • 宣言していない配列MABuffer・ADXBufferを使い、MQL4に存在しないiADXOnArray()を呼んでいる
  • MQL4で定義済みの変数Pointを、同じ名前で宣言し直している

エラーがこれだけ多いと、1つずつ直してもらうより、条件を減らし言語を明記して作り直してもらうほうが早いこともあります。2回目のコードはこの方法でコンパイルが通りました。

ロット計算は口座通貨と損切り幅の単位を検算する

最終版の3本目は、残高の2%をリスクにする「資金管理」を入れたつもりのコードです。ロット数は残高 × 2% ÷ (StopLoss × 10)という1行で計算しています。

この式は「1ロットが1pips動くと10ドル」「StopLossはpips単位」という前提でしか成り立ちません。実際のStopLoss=50はポイント単位(5桁・3桁表示の業者では5pips)で、損切り幅にはATRも足されています。口座通貨の違いも考慮していません。

ドル円の1時間足で、ATRを0.15円と仮定した計算例です。損切り幅は0.05円+0.15円=0.20円(20pips)になります。

口座残高計算されるロット損切り時の損失残高に対する割合
円口座100万円40ロット約80万円約80%
ドル口座(1ドル=150円と仮定)1万ドル0.4ロット約53ドル約0.5%

円口座では、2%のつもりが1回の損切りで残高の約8割を失う計算です。ドル口座では逆にリスクが小さすぎます。さらにMathMax(FixedLotSize, dynamicLotSize)としているため、計算結果が小さくても0.1ロットを下回りません。

ロット数は、MT4が返す「1ロットが1ティック動いたときの損益」(MODE_TICKVALUE)を使い、実際の損切り幅から逆算するのが安全です。考え方はリスク1%を守るロット計算の解説記事と同じです。

以下は、この記事のために書き直した修正例です(2023年の検証で動かしたコードではありません)。紙上で計算を確かめたものなので、使う前にデモ口座で発注ロットを確認してください。

// 残高に対するリスク%と損切り幅(価格差)から、発注ロット数を逆算する
double CalcLotByRisk(double riskPercent, double slDistance)
{
   if(slDistance <= 0) return 0;                                 // 0除算を防ぐ
   double riskAmount = AccountBalance() * riskPercent / 100.0;   // 1回の許容損失額(口座通貨)
   double tickValue  = MarketInfo(Symbol(), MODE_TICKVALUE);     // 1ロットが1ティック動いたときの損益(口座通貨)
   double tickSize   = MarketInfo(Symbol(), MODE_TICKSIZE);      // 1ティックの値幅
   double step       = MarketInfo(Symbol(), MODE_LOTSTEP);       // ロットの刻み幅
   double minLot     = MarketInfo(Symbol(), MODE_MINLOT);
   double maxLot     = MarketInfo(Symbol(), MODE_MAXLOT);
   if(tickValue <= 0 || tickSize <= 0 || step <= 0) return 0;
   double lossPerLot = slDistance / tickSize * tickValue;        // 1ロットで損切りされたときの損失額
   double lot = riskAmount / lossPerLot;
   lot = MathFloor(lot / step + 0.0000001) * step;               // 刻み幅に切り捨て(微小値は浮動小数点の誤差対策)
   if(lot < minLot) lot = minLot;  // 最小ロットに引き上げた場合、実際のリスクは指定より大きくなる
   if(lot > maxLot) lot = maxLot;
   return NormalizeDouble(lot, 2);
}

// 使い方:OnTick() の発注前に、実際の損切り幅(dynamicStopLoss)を渡す
// double lot = CalcLotByRisk(RiskPercentage, dynamicStopLoss);
// if(lot <= 0) return;  // 計算できないときは発注しない

円口座・残高100万円・リスク2%・損切り幅0.20円で計算すると、1ロットあたりの損失は0.20÷0.001×100円=2万円です。ロット数は2万円÷2万円=1.00ロットになり、元の式の40ロットとは40倍の差があります。

バックテストの後はデモ口座でフォワードテストする

パラメータの最適化範囲を尋ねたとき、ChatGPT自身も注意を添えていました。「過剰な最適化は過学習に繋がる」「フォワードテストも行う」という内容です(最適化の節の回答画面)。

過去データに合わせたパラメータが、これからの相場でも通用するとは限りません。バックテストが右肩上がりでもすぐ本番口座には入れず、デモ口座で動かして、取引回数や最大ドローダウンがバックテストと大きくずれないかを確かめてください。

あわせて読むEAのバックテストとフォワードテスト|正しい評価方法5種と合格基準EAはバックテストだけで評価すると本番で崩れがちです。アウトオブサンプル・ウォークフォワード・モンテカルロ・リアルフォワードの5つの検証法と、フォワード期間や取引回数など合格の目安を計算例つきで解説します。

2026年9月時点でEA作成に使えるAIとプラン

この記事の検証は、2023年4〜5月に当時のChatGPTの有料プランで使えたGPT-4で行ったものです。2026年9月時点では、ChatGPTのモデルもプランも当時から入れ替わっています。

ChatGPT以外の対話型AIでも、MQL4のコードは依頼できます。主な選択肢は次のとおりです。

  • ChatGPT(OpenAI):無料プランに加えて、有料のGo・Plus(月20ドル)・Proがあります。無料プランでもコードは依頼できますが、高性能なモデルやファイル添付などに利用上限があります
  • Claude(Anthropic):無料プランに加えて、Pro(月20ドル、年払いなら月17ドル相当)・Max(月100ドルから)があります。無料プランでもチャットでコードを依頼できます

使えるモデルの名前や利用上限は頻繁に変わるため、契約前にChatGPTの料金ページやClaudeの料金ページで確認してください。

モデルが新しくなっても、生成されたコードが正しいとは限りません。2023年の検証で見つかったMQL5の書き方の混在やロット計算の単位の誤りは、どのAIで作ったコードでも確かめたい点です。

ChatGPTでのEA作成に関するよくある質問

ChatGPTでEAを作るときによく聞かれる質問をまとめました。無料版で足りるか、そのまま使えるか、エラーが出たときの対処などです。詳しい手順や根拠は、各見出しの本文もあわせて確認してください。

Q1. ChatGPTの無料版でもEAは作れますか?

A1. 作れます。無料プランでもMQL4のコードは依頼できます。ただし高性能なモデルやファイル添付などには利用上限があるため、長いコードの修正を何度も繰り返すなら有料プラン(Go・Plusなど)のほうが快適です(2026年9月時点)。

Q2. ChatGPTが作ったEAはそのまま使えますか?

A2. そのまま本番で使うのはおすすめしません。2023年の検証では1本目のコードがコンパイルエラーになり、コンパイルが通った最終版にもロット計算の誤りがありました。ロット数と損切り幅を検算し、デモ口座でフォワードテストしてから判断してください。

Q3. MT4用とMT5用のEAはChatGPTで作り分けられますか?

A3. 作り分けられます。プロンプトに「MQL4で作成して」「MQL5で作成して」と使う言語を明記してください。MQL4とMQL5は注文やインジケーターの書き方が違い、混ざるとコンパイルエラーの原因になります。

Q4. ChatGPTのEAでコンパイルエラーが出たらどうすればいいですか?

A4. MetaEditorに表示されたエラーメッセージを行番号ごとコピーし、コードと一緒にAIへ渡して修正を頼みます。エラーが多いときは、条件を減らして作り直してもらうほうが早いこともあります。

Q5. ChatGPT以外のAIでもEAは作れますか?

A5. 作れます。Claudeなど、コードを書ける対話型AIでもMQL4のコードは依頼できます。どのAIでも一度で正しいコードが返るとは限らないので、コンパイルと検算が必要な点は同じです。

Q6. プログラミング経験がなくてもChatGPTでEAを作れますか?

A6. コード自体はAIが書くので、未経験でも動くEAまではたどり着けます。ただし、エラーの箇所やロット計算の誤りに気づくには、変数・関数・OrderSend()などMQL4の基礎を知っていると早く進みます。

まとめ

ChatGPTを活用してMT4で独自のEAを作成する方法を解説しました。2023年の検証時点では一度で完全なソースコードは作れませんでしたが、ちょっとずつ工夫しながらChatGPTを活用してEAを作れそうです。

  • ChatGPTでEAのコードは作れるが、一度の指示で動くとは限らない
  • 条件は段階的に足し、「MQL4で作成して」と言語を明記する
  • コンパイルエラーの文面やバックテスト結果を渡して修正を繰り返す
  • パラメータの最適化は手動で行い、本番の前にフォワードテストで確かめる
あわせて読むEA作成代行の料金相場は?依頼先比較と失敗しない仕様書の書き方EA作成代行の料金は基本5,000円〜3万円、ソースコード込みで1.5万〜3万円前後(2026年9月時点の公開価格)。ココナラ・代行業者の比較、.mq4と著作権の扱い、失敗しない仕様書テンプレートを解説します。 ※本記事は情報提供を目的としたもので、特定のEA・金融商品の売買を推奨するものではありません。FX(外国為替証拠金取引)は元本が保証されておらず、レバレッジにより預けた証拠金を上回る損失が発生する可能性があります。記事内の成績・数値は過去の実績であり、将来の利益を保証するものではありません。投資の最終判断はご自身の責任でお願いします。

QUEST CLEAR!

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

トム

この記事を書いた人トム

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