見出し画像

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

ChatGPT担当のナナミです。

前回までの動画では、ChatGPTを使いEAを開発する方法をご紹介しました。

今回の新連載からは、EAクリエイターのまさやんが開発したGPTsを使い、EA開発を行っていこうと思います。

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

このGPTsは、簡単な条件を指定するだけで高性能なEAを生成できるジェネレーターで、GPT Storeに掲載済みです。

GPTsの名前は「Masayan EA Generator for MT4 Ver1.01」です。

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

試しにEAを作成してみましょう。

ゴールデンクロスでロング、デッドクロスでショートのEAを作成して下さいと入力してみます。

出力されたコードでEAを動かしますが、完成したEAには、ゴールデンクロスとデッドクロスが頻繁に入れ替わるタイミングでエントリーとエグジットが連発する不具合があります。
修正は簡単です。
少し余裕を持ってエントリーするようにロジックを変えてくださいとプロンプトを送信するだけです。

これで改善されました。

ゴールデンクロスでロング、デッドクロスでショートのEAのドル円5分足のバックテスト結果

次に、逆張り型のロジックを追加してみましょう。

サーバー時間の23時から5時までは、逆張りのロジックで売買するようにプログラムを書き換えてください。

extern int Bands = 70;// ボリンジャーバンドの計算期間で範囲10~180で指定
extern int Deviation = 1;// ボリンジャーバンドの上方バンドと下方バンドの値で範囲1~3で指定
extern int RSI_highs = 70;// RSIの上限値を入力で範囲60~90で指定
extern int RSI_lows = 30;// RSIの下限値を入力で範囲10~40で指定
extern int RSI_Period = 14;// RSIの期間を入力で範囲6~14で指定
上記のパラメータ値で逆張りのロジックを考えてください。
サーバー時間の5時~23時のみローソク足が反転したタイミングで新規エントリーします。


完成したコードでEAのバックテストを行います。ドル円5分足チャート


//+------------------------------------------------------------------+
//|                                    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 = 50;
extern int LongMovingPeriod = 200;
extern int ConfirmBars = 3; // クロス確認のためのバー数

extern int Bands = 70; // ボリンジャーバンドの計算期間で範囲10~180で指定
extern int Deviation = 1; // ボリンジャーバンドの上方バンドと下方バンドの値で範囲1~3で指定
extern int RSI_highs = 70; // RSIの上限値を入力で範囲60~90で指定
extern int RSI_lows = 30; // RSIの下限値を入力で範囲10~40で指定
extern int RSI_Period = 14; // RSIの期間を入力で範囲6~14で指定
//ここまで改変可能

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 < LongMovingPeriod + ConfirmBars || Bars < Bands)
     {
      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 upperBand = iBands(NULL, 0, Bands, Deviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
   double lowerBand = iBands(NULL, 0, Bands, Deviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
   double rsi = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE, 0);

   int currentHour = Hour();

   if((currentHour >= 23 || currentHour < 5))
     {
      // 逆張りロジック
      if(Close[1] > upperBand && rsi > RSI_highs)
        {
         ShortSign = true;
         LongSign = false;
        }
      else if(Close[1] < lowerBand && rsi < RSI_lows)
        {
         LongSign = true;
         ShortSign = false;
        }
      else
        {
         LongSign = false;
         ShortSign = false;
        }
     }
   else
     {
      // 順張りロジック
      double shortMA = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
      double longMA = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
      double prevShortMA = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, ConfirmBars);
      double prevLongMA = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, ConfirmBars);

      if(prevShortMA <= prevLongMA && shortMA > longMA)
        {
         LongSign = true;
         ShortSign = false;
        }
      else if(prevShortMA >= prevLongMA && shortMA < longMA)
        {
         ShortSign = true;
         LongSign = false;
        }
      else
        {
         LongSign = false;
         ShortSign = false;
        }
     }
//ここまで改変可能

   if(MaxSpread < Info_Spread)
     {
      LongSign = false;
      ShortSign = false;
      spread_msg = "Max spread Order\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
        {
         // ポジション保有している場合
         CloseShortPosition(); // ショートポジションがある場合のみ決済ロング処理
        }
     }
   if(ShortSign == true)
     {
      if(CalculateCurrentOrders() == 0)
        {
         // ポジション保有していない場合
         CheckForOpenShort(); // 新規ショートオーダー処理を行う
         e ++;
        }
      else
        {
         // ポジション保有している場合
         CloseLongPosition(); // ロングポジションがある場合のみ決済ショート処理
        }
     }

   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 CloseShortPosition()
  {
   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 CloseLongPosition()
  {
   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のバックテスト結果(ドル円5分足チャート)

ちなみに、ナンピン設定はうまく動作しないみたいです。
また、生成されたコードにはバグが多数含まれていることがあります。

このGPTsの最大の特徴はバグ対策が施された、まさやんオリジナルのテンプレートを使用し、EAコードを生成できる点にあります。
また、独自のPips計算を行うことで、金やJP225、ビットコインなどに対応できる仕様となっております。

エントリー即決済の無限ループや証拠金不足等でのエラーコード131によるサーバー負荷問題、こうした不具合は必ず対策しなければいけません。

残念ながら、ChatGPTはそのままの設定ではバグ対策は行ってくれません。

Masayan EA Generatorを使うことで、安全なコードを生成してくれるので、万が一エラーが発生しても被害を最小限にとどめることが出来ます。

エラー回避の内容についてご説明いたします。

まず、エラー回避のプログラムとして、小さすぎるストップ逆指値とリミット注文の値があります。
入力できるStopLossRequestとTakeProfitRequestの最小値を0.1、つまり10pipsとすることで、エントリー即決済の無限ループを防ぎます。
この対策が出来ていないEAだと、このような不具合が起きます。
例えば1pipsで利確か損切りをする設定にした場合、エントリーしてから1pips動いて決済、そしてすぐに新規エントリーしてまた決済というように、エントリーと決済の連続ループが起こります。

エントリー即決済、そしてまたエントリーのループ

最低でも決済と損切りの幅を10pipsとすることで、ある程度レートが動いたら決済となることから、無限ループにはなりません。

また、証拠金不足等でオーダーが弾かれた場合でも、連続エントリーが100回を超えるとチャート画面に「Continuous order count limit」と表示され、オーダーがストップします。
連続オーダーのカウント値はサーバー時間の4時、11時、18時にリセットされます。

わたしはこれまで、ソースコードは公開してきませんでした。

しかしながら、今回GPTsを作成するにあたりEAテンプレートの一部を公開することになりました。

このテンプレートを使用しEA開発を行うことで、世の中に優秀なEAが出回ってしまうことでしょう。

EAを販売する立場の人間からすれば、自分で自分の首を絞めているようなもの。

ですが、生成AIが誕生したことでプログラマーとしてコーディングを行う機会は減りました。

今後のEAクリエイターの仕事は、ロジックの考案とパラメータの調整になることでしょう。

いずれ誰かが優秀なGPTsを公開するでしょうから、わたしもいち早くこの分野に挑戦することにしました。

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

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

次回第2回の動画から、このGPTsを使用して色々と試してみようと思います。

そう、わたしが唯一作ることが出来なかったEA、秒スキャ、分スキャEAの開発など、難易度MAXのEAをChatGPTの力をかりて成功させたいです。

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