見出し画像

専業スキャルパーのヒロシさんのFX手法をカスタマイズして勝てるスキャルのEAを作ってみた【勝てるロジックのEA化】

ChatGPT担当のナナミです。

前回の動画では、FXスキャルパーのヒロシさんの手法をChatGPTでEA化しました。

ですが、単純にボリンジャーバンドとRSIを使用しただけでは勝てないことが分かったので、今回、EAをカスタマイズして勝てるEAに仕上げたいと思います。

もちろん、完成したEAのコードは無料で公開しますので、最後までご視聴、よろしくお願いします。

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

■ブログURL
https://fx.reform-network.net/2024/07/23/【新連載】fx手法をchatgptでea化する【勝てるロジックの/

■前回記事
https://note.com/aimjey/n/n3b2503e4a30f


今回行う修正は、以下の2つです。

まず、決済のロジックを追加します。
次に、売買フィルターを追加します。

単純に10pipsで利食いとか5pipsで損切りとかを設定しても勝てません。
きちんと優位性のあるタイミングで決済を行う必要があります。

裁量トレードであれば、瞬間的な値動きをとらえてプライスアクションで売買判断するといったやり方が考えられますね。
EAトレードの場合、テクニカル指標を用いたエントリーと決済を行います。

次の決済方法について候補を考えてみました。

1つ目、ボリンジャーバンドの中心線にタッチで決済。
2つ目、ボリンジャーバンドの±1シグマにタッチで決済。
3つ目、RSIの50にタッチで決済。

以上の決済方法をそれぞれ検証したいと思います。

決済方法については、手動でプログラムを組みます。

修正の前に、前回の完成したEAプログラムのエントリー箇所に不具合が見つかりました。

不具合の箇所はロングサインとショートサインのif文のところです。

noteの記事リンクは修正済みです。

また、GPTsに関しても決済ロジックを生成できるように修正しました。

保有しているポジションの決済条件を指示する内容のプロンプトを送信されたら、以下の内容を元にプログラムを書き変えます。

if(CalculateCurrentOrders() == 1){// ロングのポジション保有
//ここに決済の条件を入力
CloseLongPosition();// 保有しているロングポジションをショートで決済するという意味
} else if(CalculateCurrentOrders() == -1){// ショートのポジション保有
//ここに決済の条件を入力
CloseShortPosition();// 保有しているショートポジションをロングで決済するという意味
}

簡単なプロンプトを送信するだけで、コーディングしてくれるのがChatGPTの利点です。
学習した内容は新しいセッションには引継ぎされません。

ChatGPTはセッションを超えての学習は行われない

ChatGPTは一度のセッションを超えて記憶を保持することができません。ということです。
というわけで、今回GPTsを直接修正しました。


GPTsを直接修正することで、生成するコードのクセを直せる

それでも決済の処理でたまに間違いが頻発してしまいます。

これはもうどうしようもないですね。

ChatGPTの今後のバージョンアップに期待です。

さて、本題に戻ります。

決済のプログラムを消して、ドテンで売買する条件でバックテストをまわすと、資産チャートは横ばいに推移します。

どうやら下手に決済するより、ドテンの売買のままのほうが良い成績が出せるみたいです。


次に、エントリー条件を絞り込むために、売買フィルターを追加してみましょう。

他のテクニカル指標と組み合わせることで、成績は改善できると思います。

この部分の修正に関しては、ChatGPTで簡単に行うことができます。

とりあえず、移動平均線を用いて長期のトレンドを判断し、順張り方向にエントリーするプログラムを追加してみましょう。

Masayan EA Generator for MT4 で、条件を入力しEAを作ってみます。

完成したEAでバックテストをまわします。

移動平均の期間が1000でRSIで決済の場合、
プロフィットファクターは1.17。
トレード回数が31976回。
利益が10973米ドルです。

移動平均の期間が1000でボリンジャーバンドの中心線にタッチで決済の場合、
プロフィットファクターは1.13。
トレード回数が28794回。
利益が7998米ドルです。

移動平均の期間を1000で±1シグマにタッチで決済の場合、
プロフィットファクターは1.10。
トレード回数が35788回。
利益が5295米ドルです。

移動平均の期間を1000とし、RSIが50にタッチで決済の設定がよさそうです。

ドル円1分足チャートでTDSによる全ティックバックテストをまわしてみます。

2010年から2013年あたりに強烈なドローダウンが発生しています。


その後は右肩上がりに資産が増えていますが、2020年以降は負けています。

改善点として、トレード回数が非常に多いので、有利な時間帯だけエントリーするように修正してみましょう。

サーバー時間の7時から18時までエントリーするように修正してもらいました。

この時間帯だと成績は悪化したので、それ以外の時間のみエントリーするように修正してみます。

プロフィットファクターは1.18で、成績は改善しましたね。

朝スキャEAとして使える可能性があります。

プロパティ設定でエントリーする時間を選べるように修正してもらいましょう。

ですが、ここで問題が発生しました。

ChatGPTがこちらの望むコードを生成してくれません。

仕方ないので、手動で修正します。

新規エントリーを見送る時間帯を総当たりでバックテストします。

いつもはサーバー時間の7時から18時を基準にEAを作っているのですが、朝スキャEAの場合、トレードする時間帯をもう少し短くする必要があります。

また、サーバー時間の0時はスプレッドが拡大するので、0時は新規エントリーを見送るようにします。

エントリーする時間をいろいろ試したのですが、結局いつもの時間サーバー時間の7時から18時になってしまいました。

というわけで、欧州勢が参加し始める時間帯のサーバー時間の7時から、欧州ロンドン勢が引ける時間帯のサーバー時間の17時59分までを新規エントリーしない設定で完成とします。

完成したEAのTDSによる全ティックバックテストの結果です。


サーバー時間の制限有り

上記のEAのソースコードはこちら

//+------------------------------------------------------------------+
//|                                    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 = 1.0;// StopLoss 0.1=10pips 0.5=50pips 1=100pips
extern double TakeProfitRequest = 1.0;// TakeProfit 0.1=10pips 0.5=50pips 1=100pips
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 RsiPeriod = 14; // RSIの期間
extern int BollingerBandsPeriod = 30; // ボリンジャーバンドの期間
extern int BollingerBandsDeviation = 2; // ボリンジャーバンドのシグマ値
extern int LongTermMAPeriod = 1000; // 長期トレンドの移動平均期間
//ここまで改変可能

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.01)
     {
      StopLossRequest = 0.01;
     }
   if(TakeProfitRequest <= 0.01)
     {
      TakeProfitRequest = 0.01;
     }
   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 rsi = iRSI(NULL, 0, RsiPeriod, PRICE_CLOSE, 1);
   double upperBand = iBands(NULL, 0, BollingerBandsPeriod, BollingerBandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
   double lowerBand = iBands(NULL, 0, BollingerBandsPeriod, BollingerBandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
   double currentPrice = Close[0];

   // 長期トレンドの方向を判断するための移動平均
   double longTermMA = iMA(NULL, 0, LongTermMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);

   // 長期トレンドに順張りするロジック
   if(currentPrice > longTermMA && rsi <= 30 && currentPrice <= lowerBand)
     {
      LongSign = true;
      ShortSign = false;
     }
   else if(currentPrice < longTermMA && rsi >= 70 && currentPrice >= upperBand)
     {
      ShortSign = true;
      LongSign = false;
     }
   else
     {
      LongSign = false;
      ShortSign = false;
     }
    
/*
1つ目、MODE_MAIN:中心線にタッチで決済。
   double middleBand = iBands(NULL, 0, BollingerBandsPeriod, BollingerBandsDeviation, 0, PRICE_CLOSE, MODE_MAIN, 1);
            if(CalculateCurrentOrders() == 1 && middleBand < Close[1]){// ポジション保有かつ移動平均を下から上にクロス
            CloseLongPosition();// ロングポジションがある場合のみ決済ショート処理
            } else if(CalculateCurrentOrders() == -1 && middleBand > Close[1]){// ポジション保有かつ移動平均を上から下にクロス
            CloseShortPosition();// ショートポジションがある場合のみ決済ロング処理
            }
2つ目、ボリンジャーバンドの±1シグマにタッチで決済。
   double upperBand1 = iBands(NULL, 0, BollingerBandsPeriod, 1, 0, PRICE_CLOSE, MODE_UPPER, 1);
   double lowerBand1 = iBands(NULL, 0, BollingerBandsPeriod, 1, 0, PRICE_CLOSE, MODE_LOWER, 1);
            if(CalculateCurrentOrders() == 1 && lowerBand1 < Close[1]){// ポジション保有かつ-1シグマにタッチ
            CloseLongPosition();// ロングポジションがある場合のみ決済ショート処理
            } else if(CalculateCurrentOrders() == -1 && upperBand1 > Close[1]){// ポジション保有かつ+1シグマにタッチ
            CloseShortPosition();// ショートポジションがある場合のみ決済ロング処理
            }
3つ目、RSIの50にタッチで決済。
*/
   double rsi2 = iRSI(NULL, 0, RsiPeriod, PRICE_CLOSE, 1);
            if(CalculateCurrentOrders() == 1 && rsi2 > 50){// ポジション保有かつRSIが50にタッチ
            CloseLongPosition();// ロングポジションがある場合のみ決済ショート処理
            } else if(CalculateCurrentOrders() == -1 && rsi2 < 50){// ポジション保有かつRSIが50にタッチ
            CloseShortPosition();// ショートポジションがある場合のみ決済ロング処理
            }

// 新規エントリーの時間制限(7時から18時はエントリーを見送る)
   int currentHour = Hour();
   if(currentHour >= 7 && currentHour < 18)
     {
      LongSign = false;
      ShortSign = false;
     }
   if(currentHour == 0)// 新規エントリーの時間制限(0時はスプレッド拡大の為エントリーを見送る)
     {
      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
        {
         // ポジション保有している場合
         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(サーバー時間の指定なし)のバックテスト結果

サーバー時間の指定なし

結局、今回作成したEAではトレードする時間を変えても意味のないことが分かりました。

それから、このEAの問題点として、トレード回数がまだまだ多すぎますね。

もう少し売買フィルターを追加して、エントリー頻度を抑えると成績が改善する可能性はあります。

例えば、ZigZagやフィボナッチ・リトレースメント、ストキャスティクスなどのテクニカル指標と組み合わせたり、
あるいはレンジブレイクの時だけエントリーしたり、レジサポラインを使ったトレード手法など、高勝率のスキャルのEAを作れる可能性はあります。
次回の動画では今回作成したEAを更に進化させるべく、様々な手法をミックスしてリアル運用できるレベルのEAに仕上げたいと思います。

皆様も是非、Masayan EA Generator for MT4を使って高性能なEAを作ってくださいね。

他にも、こんな手法EAにしてほしいみたいなリクエストがあれば、コメントお願いします。


今回参考にしたYouTubeチャンネル
https://www.youtube.com/@cxr_toshi/featured

今回参考にしたYouTube動画URL
https://www.youtube.com/watch?v=IukfeqNIhvk

ひろぴー流トレード術(オススメ!)
https://www.youtube.com/watch?v=6Wy995eBevQ

【免責事項】
・本GPTsについて、正当性を保証するものではありません。
・本GPTsを利用して損失を被った場合でも一切の責任を負いません。
・投資の決定は、自己判断 自己責任でお願いします。

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