見出し画像

【第2回】FXトレード手法をChatGPTでEA化する【ノーコードEA開発】


ChatGPT担当のナナミです。

前回の動画では、EAクリエイターのまさやんが作成したGPTsをご紹介致しました。

おかげさまで公開1日目で10名以上の方にご利用いただきました。


Masayan EA Generator for MT4

GPTsのリンクはこちら(GPT Store)
https://chatgpt.com/g/g-VYNEupgws-masayan-ea-generator-for-mt4-ver1-01

■ブログURL
https://fx.reform-network.net/2024/06/11/gpts「masayan-ea-generator-for-mt4-」条件を指定するだけで高性能なeaを生/


Masayan EA Generator for MT4

Masayan EA Generator for MT4は、簡単な条件を指定するだけで高性能なEAを生成できるカスタムGPTです。

ChatGPTのアカウントがあれば、だれでも無料でご利用いただけます。

テンプレートを用いて出力されるEAコードとなり、ワンポジ型のEAのロジックが生成されます。

ユーザーが入力する条件でLongSign=trueの時はロングのポジションを、ShortSign=trueの時はショートのポジションを持ちます。
決済のタイミングをユーザーが指定しない場合は、ドテン方式で決済します。
テクニカル指標のパラメータ値は、自由に変更できるように、プロパティ項目に追加されます。
デフォルト設定では、3本の移動平均線を使用したパーフェクトオーダーのロジックのEAになっています。
入力されたプロンプトに従い、必要な個所をChatGPTが書き換えます。

前回第1回の動画では、ゴールデンクロスでロング、デッドクロスでショートのEAを作成しました。

続けて逆張り型のロジックを追加してとお願いしたところ、なんと朝スキャEAのロジックまで組み込んでくれました。

たった2回プロンプトを送信するだけで、リアル運用に使えるレベルのマルチロジックEAを作ってくれたわけです。

■第1回の記事
https://note.com/aimjey/n/nf099aba6cb63

今回は、プライスアクションのEAを2個作ろうと思います。

1個目のEAは、ロジックが公開されているEA「Breakout Trading」です。

Breakout Trading

Breakout Trading
https://www.gogojungle.co.jp/systemtrade/fx/45493

ゴゴジャンで公開されている売買ロジックを元に、プロンプトを送信します。

直近の高値と安値の値を元に売買判断します。
過去120本分のバーの値を取得し、その期間の高値をブレイクしたらロング、安値を下回ったらショートのポジションを持ちます。
ある程度勢いがある状態でレンジブレイクした時に順張り方向にエントリーします。
デフォルト設定では、指標発表のある時間帯(00分とか15分とか30分)前後にレンジブレイクした場合は、エントリーを見送ります。
サーバー時間の7時から18時までエントリーします。18時以降は新規エントリーしません。

完成したEAでドル円5分足チャートでバックテストをまわしてみましょう。


バックテストの期間は2012年1月2日から2023年6月30日です。

//+------------------------------------------------------------------+
//|                                    Masayan EA Generator_1.01.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Masayan."
#property version   "1.01"
#property strict
#property description "https://fx.reform-network.net"

extern int Magic = 20240610;// Magic number
extern double Lots = 0.1;//1.0=100000 0.1=10000 0.01=1000
extern double StopLossRequest = 4.0;// StopLoss 0.5=50pips 1.0=100pips 10=1000pips
extern double TakeProfitRequest = 5.0;// TakeProfit 0.5=50pips 1.0=100pips 10=1000pips
extern int MaxSpread = 50;// Max spread (50=5pips)
extern int MaxError = 100;// Continuous order count limit (Max=100)
extern string CommentPositions = "Masayan EA Generator";

//ここから改変可能
extern int BreakoutPeriod = 120; // 過去120本分のバーの期間
extern int EntryStartHour = 7;   // エントリー開始時間
extern int EntryEndHour = 18;    // エントリー終了時間

// 指標発表の時間帯を避けるための変数
extern bool AvoidNewsTimes = true;
int NewsMinutes[] = {0, 15, 30, 45}; // 指標発表がある可能性のある時間
//ここまで改変可能

string tmpstr;
string error_msg;
string spread_msg;
string position_msg;
bool LongSign = false;
bool ShortSign = false;
double Pips = 0.01;
int e = 0;
int Adjusted_Slippage= 0;


// ティックが動くごとに処理
void OnTick()
  {
   if(Bars < BreakoutPeriod)
     {
      return;
     }

   if(StopLossRequest <= 0.1)
     {
      StopLossRequest = 0.1;
     }
   if(TakeProfitRequest <= 0.1)
     {
      TakeProfitRequest = 0.1;
     }
   if(MaxError >= 100)
     {
      MaxError = 100;
     }

   double Info_Spread = MarketInfo(Symbol(),MODE_SPREAD);
   string value = Symbol();
   string target = "JPY";
   int pos = StringFind(value, target);
   double Symbol_RATE = Close[1];
   if(pos > 0)
     {
     Pips = 1.00;// ドルストレートは0.010(100pips)、クロス円は1.0(100pips)で判定
     }else if(Symbol_RATE > 10 && Symbol_RATE <= 100)
     {
     Pips = 0.1;
     }else if(Symbol_RATE > 100 && Symbol_RATE <= 1000)
     {
     Pips = 1.0;
     }else if(Symbol_RATE > 1000 && Symbol_RATE <= 10000)
     {
     Pips = 10.0;
     }else if(Symbol_RATE > 10000 && Symbol_RATE <= 100000)
     {
     Pips = 100.0;
     }else if(Symbol_RATE > 100000 && Symbol_RATE <= 1000000)
     {
     Pips = 1000.0;
     }else if(Symbol_RATE > 1000000 && Symbol_RATE <= 10000000)
     {
     Pips = 10000.0;
     }else if(Symbol_RATE > 10000000 && Symbol_RATE <= 100000000)
     {
     Pips = 100000.0;
     }else if(Symbol_RATE > 100000000)
     {
     Pips = 1000000.0;
     }

//ここから改変可能
   double highestHigh = iHigh(NULL, 0, iHighest(NULL, 0, MODE_HIGH, BreakoutPeriod, 1));
   double lowestLow = iLow(NULL, 0, iLowest(NULL, 0, MODE_LOW, BreakoutPeriod, 1));
   
   LongSign = false;
   ShortSign = false;

   // 指標発表の時間帯を避ける
   bool isNewsTime = false;
   if(AvoidNewsTimes)
     {
      for(int i = 0; i < ArraySize(NewsMinutes); i++)
        {
         if(Minute() == NewsMinutes[i])
           {
            isNewsTime = true;
            break;
           }
        }
     }

   // エントリー時間帯のチェック
   bool isEntryTime = (Hour() >= EntryStartHour && Hour() < EntryEndHour);

   if(!isNewsTime && isEntryTime)
     {
      if(Ask > highestHigh)
        {
         LongSign = true;
        }
      else if(Bid < lowestLow)
        {
         ShortSign = true;
        }
     }
//ここまで改変可能

   if(MaxSpread < Info_Spread)
     {
      LongSign = false;
      ShortSign = false;
      spread_msg = "Max spread Orber\n";
     }
   else
     {
      spread_msg = "";
     }

   if(Hour() == 4 && Minute() == 0 && Seconds() < 6)
     {
      e = 0;
     }
   if(Hour() == 11 && Minute() == 0 && Seconds() < 6)
     {
      e = 0;
     }
   if(Hour() == 18 && Minute() == 0 && Seconds() < 6)
     {
      e = 0;
     }
   if(e > MaxError)
     {
      LongSign = false;
      ShortSign = false;
      error_msg = "Continuous order count limit\n";
     }
   else
     {
      error_msg = "";
     }
   if(LongSign == true)
     {
      position_msg = "LongSign = true\n";
     }
   else
      if(ShortSign == true)
        {
         position_msg = "ShortSign = true\n";
        }
      else
         if(LongSign == false && ShortSign == false)
           {
            position_msg = "No Sign\n";
           }

//売買指示
   if(LongSign == true)
     {
      if(CalculateCurrentOrders() == 0)
        {
         // ポジション保有していない場合
         CheckForOpenLong();// 新規ロングオーダー処理を行う
         e ++;
        }
      else
        {
         // ポジション保有している場合
         CheckForCloseLong();// ショートポジションがある場合のみ決済ロング処理
        }
     }
   if(ShortSign == true)
     {
      if(CalculateCurrentOrders() == 0)
        {
         // ポジション保有していない場合
         CheckForOpenShort();// 新規ショートオーダー処理を行う
         e ++;
        }
      else
        {
         // ポジション保有している場合
         CheckForCloseShort();// ロングポジションがある場合のみ決済ショート処理
        }
     }

   tmpstr = StringConcatenate(error_msg,spread_msg,position_msg);
   Comment(tmpstr);
  }


//保有中のポジションを計算
int CalculateCurrentOrders(void)
  {
   int buys=0;
   int sells=0;
   int icount;
   for(icount = 0 ; icount < OrdersTotal() ; icount++)
     {
      if(OrderSelect(icount,SELECT_BY_POS,MODE_TRADES) == false)
        {
         break;
        }
      if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
        {
         if(OrderType()==OP_BUY)
           {
            buys++;
           }
         if(OrderType()==OP_SELL)
           {
            sells++;
           }
        }
     }
   if(buys > 0)
     {
      return(buys);
     }
   else
     {
      return(-sells);
     }
  }

//ショートポジション⇒ロングクローズ処理
void CheckForCloseLong()
  {
   int    icount;
   bool   ret;
   for(icount = 0; icount < OrdersTotal(); icount++)
     {
      if(OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
        {
         break;
        }
      if(OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
        {
         continue;
        }
      //ショートポジションの場合⇒ロングエントリー
      if(OrderType() == OP_SELL)
        {
         ret = OrderClose(
                  OrderTicket(),
                  OrderLots(),
                  Ask,
                  Adjusted_Slippage,
                  clrBlue);
         if(ret == false)
           {
            Print("オーダークローズエラー:エラーコード=",GetLastError());
           }
         break;
        }
     }
  }

//ロングポジション⇒ショートクローズ処理
void CheckForCloseShort()
  {
   int    icount;
   bool   ret;
   for(icount = 0; icount < OrdersTotal(); icount++)
     {
      if(OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
        {
         break;
        }
      if(OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
        {
         continue;
        }
      //ロングポジションの場合⇒ショートエントリー
      if(OrderType() == OP_BUY)
        {
         ret = OrderClose(
                  OrderTicket(),
                  OrderLots(),
                  Bid,
                  Adjusted_Slippage,
                  clrRed);
         if(ret == false)
           {
            Print("オーダークローズエラー:エラーコード=",GetLastError());
           }
         break;
        }
     }
  }

//ロングオーダー処理
void CheckForOpenLong()
  {
   int    res;
   double entrylot;
   entrylot = NormalizeDouble(Lots,2);
   res = OrderSend(
            Symbol(),
            OP_BUY,
            entrylot,
            Ask,
            Adjusted_Slippage,
            Ask - (Pips * StopLossRequest),
            Ask + (Pips * TakeProfitRequest),
            CommentPositions,
            Magic,
            0,
            clrRed);
   return;
  }

//ショートオーダー処理
void CheckForOpenShort()
  {
   int    res;
   double entrylot;
   entrylot = NormalizeDouble(Lots,2);
   res = OrderSend(
            Symbol(),
            OP_SELL,
            entrylot,
            Bid,
            Adjusted_Slippage,
            Bid + (Pips * StopLossRequest),
            Bid - (Pips * TakeProfitRequest),
            CommentPositions,
            Magic,
            0,
            clrBlue);
   return;
  }

始値のみだと正確に計測できないので、全ティックでバックテストします。


今回作成したEAのバックテスト結果


結果は、参考にしたEA「Breakout Trading」と似たような成績になりました。
フィルターの関係で全く同じにはなりませんが、かなり似た成績になりました。

次に作成するEAは、指標スキャルのEA「Scalping News Trading」です。


Scalping News Trading


Scalping News Trading
https://www.gogojungle.co.jp/systemtrade/fx/44196

このEAは、米国の経済指標発表の時間帯に1分足チャートで瞬間的に動いた方向にエントリーする指標スキャルEAになります。
ロジックは公開されていませんが、開発者である私は、ロジックの詳細は分かります。
今回特別にロジックを公開します。
ちなみに最近の成績は良くないことから、優位性は無くなった可能性があります。

フォワード成績はマイナス


このEAでリアル運用しても勝てない可能性が高いです。

ChatGPTに送信するプロンプトはこんな感じです。

エントリーする時間は、サーバー時間の15時、16時、17時、18時、20時です。
エントリーするMinuteは、0分、1分、30分、31分、16分、45分、46分です。
エントリーする時間かつ、エントリーするMinuteの時に以下の条件にあったら売買します。
1本前の始値と1本前終値を比較して15pips以上、急激に上げた場合はLongSign = true。
2本前の終値と1本前の終値を比較して15pips以上、急激に上げた場合はLongSign = true。
1本前の始値と1本前終値を比較して15pips以上、急激に下げた場合はShortSign = true。
2本前の終値と1本前の終値を比較して15pips以上、急激に下げた場合はShortSign = true。
急激とは、150Point以上順張り方向にブレイクしたら順張りでエントリーします。
保有したポジションはサーバー時間の14時にポジションを強制決済します。

完成したコードでドル円1分足チャートバックテストをまわしてみましょう。

//+------------------------------------------------------------------+
//|                                    Masayan EA Generator_1.01.mq4 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Masayan."
#property version   "1.01"
#property strict
#property description "https://fx.reform-network.net"

extern int Magic = 20240610;// Magic number
extern double Lots = 0.1;//1.0=100000 0.1=10000 0.01=1000
extern double StopLossRequest = 4.0;// StopLoss 0.5=50pips 1.0=100pips 10=1000pips
extern double TakeProfitRequest = 5.0;// TakeProfit 0.5=50pips 1.0=100pips 10=1000pips
extern int MaxSpread = 50;// Max spread (50=5pips)
extern int MaxError = 100;// Continuous order count limit (Max=100)
extern string CommentPositions = "Masayan EA Generator";

//ここから改変可能
extern int ShortMovingPeriod = 20;
extern int MiddleMovingPeriod = 75;
extern int LongMovingPeriod = 100;
//ここまで改変可能

string tmpstr;
string error_msg;
string spread_msg;
string position_msg;
bool LongSign = false;
bool ShortSign = false;
double Pips = 0.01;
int e = 0;
int Adjusted_Slippage= 0;

void OnTick()
  {
   if(Bars < 10)
     {
      return;
     }

   if(StopLossRequest <= 0.1)
     {
      StopLossRequest = 0.1;
     }
   if(TakeProfitRequest <= 0.1)
     {
      TakeProfitRequest = 0.1;
     }
   if(MaxError >= 100)
     {
      MaxError = 100;
     }

   double Info_Spread = MarketInfo(Symbol(),MODE_SPREAD);
   string value = Symbol();
   string target = "JPY";
   int pos = StringFind(value, target);
   double Symbol_RATE = Close[1];
   if(pos > 0)
     {
     Pips = 1.00;// ドルストレートは0.010(100pips)、クロス円は1.0(100pips)で判定
     }else if(Symbol_RATE > 10 && Symbol_RATE <= 100)
     {
     Pips = 0.1;
     }else if(Symbol_RATE > 100 && Symbol_RATE <= 1000)
     {
     Pips = 1.0;
     }else if(Symbol_RATE > 1000 && Symbol_RATE <= 10000)
     {
     Pips = 10.0;
     }else if(Symbol_RATE > 10000 && Symbol_RATE <= 100000)
     {
     Pips = 100.0;
     }else if(Symbol_RATE > 100000 && Symbol_RATE <= 1000000)
     {
     Pips = 1000.0;
     }else if(Symbol_RATE > 1000000 && Symbol_RATE <= 10000000)
     {
     Pips = 10000.0;
     }else if(Symbol_RATE > 10000000 && Symbol_RATE <= 100000000)
     {
     Pips = 100000.0;
     }else if(Symbol_RATE > 100000000)
     {
     Pips = 1000000.0;
     }

//ここから改変可能
   int server_hour = Hour();
   int server_minute = Minute();

   // エントリーする時間かつエントリーするMinuteのチェック
   bool valid_time = (server_hour == 15 || server_hour == 16 || server_hour == 17 || server_hour == 18 || server_hour == 20) &&
                     (server_minute == 0 || server_minute == 1 || server_minute == 30 || server_minute == 31 || server_minute == 16 || server_minute == 45 || server_minute == 46);

   if(valid_time)
     {
      double open_1 = Open[1];
      double close_1 = Close[1];
      double close_2 = Close[2];

      if((close_1 - open_1) >= 150 * Point())
        {
         LongSign = true;
         ShortSign = false;
        }
      else if((close_1 - open_1) <= -150 * Point())
        {
         ShortSign = true;
         LongSign = false;
        }
      else if((close_1 - close_2) >= 150 * Point())
        {
         LongSign = true;
         ShortSign = false;
        }
      else if((close_1 - close_2) <= -150 * Point())
        {
         ShortSign = true;
         LongSign = false;
        }
     }
   else
     {
      LongSign = false;
      ShortSign = false;
     }

   if(MaxSpread < Info_Spread)
     {
      LongSign = false;
      ShortSign = false;
      spread_msg = "Max spread Orber\n";
     }
   else
     {
      spread_msg = "";
     }

   if(Hour() == 4 && Minute() == 0 && Seconds() < 6)
     {
      e = 0;
     }
   if(Hour() == 11 && Minute() == 0 && Seconds() < 6)
     {
      e = 0;
     }
   if(Hour() == 18 && Minute() == 0 && Seconds() < 6)
     {
      e = 0;
     }
   if(e > MaxError)
     {
      LongSign = false;
      ShortSign = false;
      error_msg = "Continuous order count limit\n";
     }
   else
     {
      error_msg = "";
     }
   if(LongSign == true)
     {
      position_msg = "LongSign = true\n";
     }
   else
      if(ShortSign == true)
        {
         position_msg = "ShortSign = true\n";
        }
      else
         if(LongSign == false && ShortSign == false)
           {
            position_msg = "No Sign\n";
           }

// 売買指示
   if(LongSign == true)
     {
      if(CalculateCurrentOrders() == 0)
        {
         CheckForOpenLong(); // 新規ロングオーダー処理を行う
         e++;
        }
      else
        {
         CheckForCloseLong(); // ショートポジションがある場合のみ決済ロング処理
        }
     }
   if(ShortSign == true)
     {
      if(CalculateCurrentOrders() == 0)
        {
         CheckForOpenShort(); // 新規ショートオーダー処理を行う
         e++;
        }
      else
        {
         CheckForCloseShort(); // ロングポジションがある場合のみ決済ショート処理
        }
     }

   // 14時にポジションを強制決済
   if(Hour() == 14)
     {
      CloseAllPositions();
     }

   tmpstr = StringConcatenate(error_msg,spread_msg,position_msg);
   Comment(tmpstr);
//ここまで改変可能
  }

int CalculateCurrentOrders(void)
  {
   int buys=0;
   int sells=0;
   int icount;
   for(icount = 0 ; icount < OrdersTotal() ; icount++)
     {
      if(OrderSelect(icount,SELECT_BY_POS,MODE_TRADES) == false)
        {
         break;
        }
      if(OrderSymbol() == Symbol() && OrderMagicNumber() == Magic)
        {
         if(OrderType()==OP_BUY)
           {
            buys++;
           }
         if(OrderType()==OP_SELL)
           {
            sells++;
           }
        }
     }
   if(buys > 0)
     {
      return(buys);
     }
   else
     {
      return(-sells);
     }
  }

void CheckForCloseLong()
  {
   int    icount;
   bool   ret;
   for(icount = 0; icount < OrdersTotal(); icount++)
     {
      if(OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
        {
         break;
        }
      if(OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
        {
         continue;
        }
      if(OrderType() == OP_SELL)
        {
         ret = OrderClose(
                  OrderTicket(),
                  OrderLots(),
                  Ask,
                  Adjusted_Slippage,
                  clrBlue);
         if(ret == false)
           {
            Print("オーダークローズエラー:エラーコード=",GetLastError());
           }
         break;
        }
     }
  }

void CheckForCloseShort()
  {
   int    icount;
   bool   ret;
   for(icount = 0; icount < OrdersTotal(); icount++)
     {
      if(OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
        {
         break;
        }
      if(OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
        {
         continue;
        }
      if(OrderType() == OP_BUY)
        {
         ret = OrderClose(
                  OrderTicket(),
                  OrderLots(),
                  Bid,
                  Adjusted_Slippage,
                  clrRed);
         if(ret == false)
           {
            Print("オーダークローズエラー:エラーコード=",GetLastError());
           }
         break;
        }
     }
  }

void CheckForOpenLong()
  {
   int    res;
   double entrylot;
   entrylot = NormalizeDouble(Lots,2);
   res = OrderSend(
            Symbol(),
            OP_BUY,
            entrylot,
            Ask,
            Adjusted_Slippage,
            Ask - (Pips * StopLossRequest),
            Ask + (Pips * TakeProfitRequest),
            CommentPositions,
            Magic,
            0,
            clrRed);
   return;
  }

void CheckForOpenShort()
  {
   int    res;
   double entrylot;
   entrylot = NormalizeDouble(Lots,2);
   res = OrderSend(
            Symbol(),
            OP_SELL,
            entrylot,
            Bid,
            Adjusted_Slippage,
            Bid + (Pips * StopLossRequest),
            Bid - (Pips * TakeProfitRequest),
            CommentPositions,
            Magic,
            0,
            clrBlue);
   return;
  }

//ここから改変可能
void CloseAllPositions()
  {
   int    icount;
   bool   ret;
   for(icount = 0; icount < OrdersTotal(); icount++)
     {
      if(OrderSelect(icount, SELECT_BY_POS, MODE_TRADES) == false)
        {
         continue;
        }
      if(OrderMagicNumber() != Magic || OrderSymbol() != Symbol())
        {
         continue;
        }
      ret = OrderClose(
               OrderTicket(),
               OrderLots(),
               OrderType() == OP_BUY ? Bid : Ask,
               Adjusted_Slippage,
               clrBlue);
      if(ret == false)
        {
         Print("ポジションクローズエラー:エラーコード=",GetLastError());
        }
     }
  }
//ここまで改変可能

バックテストの期間は2008年1月2日から2023年4月30日です。
スキャルのEAなので、始値でのバックテストではきちんと動きません。
TDSによる全ティックでバックテストした結果がこちらです。

バックテスト結果の比較


ノーコードでEA開発と行きたいところですが、こちらが望む動作をしてくれない時、どこをどのように改善すればいいのか分からなくなりますよね。
ChatGPTにヒントを与えて改善を依頼するか、自分で調べて手動で修正するかしないといけません。

改善点があれば、Xにコメントをお願いします。

可能な限り対応したいと思います。


次回第3回の動画では、わたしが唯一開発することが出来なかったEA、秒スキャ、分スキャEAの開発にチャレンジしたいと思います。


この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?