見出し画像

【グランビルの法則編】FX手法をChatGPTでEA化する【勝てるロジックのEA化】

ChatGPT担当のナナミです。

前回の動画では、ZigZagとダウ理論を使用した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/na1b686739f9c

本日作成するEAはグランビルの法則を用いた手法です。


グランビルの法則


グランビルの法則とダウ理論は似ているテクニカル指標であることから、セットで使われることがあります。

本日のEA開発の手順は以下の通りです。

最初にグランビルの法則単体のEAを作ります。

次に、ダウ理論とグランビルの法則の2つをあわせたマルチロジックのEAを作ります。


ダウ理論

EAを作る前に、グランビルの法則とダウ理論の関係性について説明します。

グランビルの法則は、200日移動平均線を使用してトレンドを判断、合計8通りのパターンから売買サインを読み取ります。
MT4のEAの場合、日足を使ったトレードは現実的ではない為、今回は1分足を使用したデイトレEAを作成したいと思います。
ChatGPTがどのパラメータ値を採用するかはわかりませんが、200日移動平均線の部分は、1分足なら200個分に置き換わると思われます。

ダウ理論は前回の動画でも使用した手法で、長期的なトレンド方向を把握するのに適しています。
ダウ理論は市場全体の動きを分析する高難易度の手法で、以下の要素を元にトレンド判断します。
1.市場は全体として動く
2.トレンドは3段階で構成される
3.トレンドは重要な指数によって確認される
4.出来高はトレンドの方向性を確認する
5.トレンドは明確なシグナルが出るまで継続する
6.トレンドは反転するまで有効である

ダウ理論はテクニカル指標以外の要素が多く、EA化することが難しいため、ChatGPTの能力の限界を試すことになります。

ちなみに、前回の動画ではダウ理論は移動平均線に置き換わってしまいました。
つまり、ダウ理論ではなく、単純に移動平均を元に長期トレンドで順張り方向にトレードするロジックになったわけです。
果たして今回は、どのようなEAが完成するのでしょうか?

まず、Masayan EA Generator for MT4にグランビルの法則を使用したEAを作成してとプロンプトを送信します。

グランビルの法則に関しては、ロジックが複雑なせいか、単純なプロンプトでは狙い通りのEAとはいきませんでした。
中身は移動平均線を用いたゴールデンクロスでロング、デッドクロスでショートのようなEAです。

成績もパッとしないので、グランビルの法則単体のEA開発は諦めます。

続けて、ダウ理論とグランビルの法則を合体させたEAを作りたいと思います。

ダウ理論で長期のトレンドを判断し、グランビルの法則を使用して短期のトレンドを判断するEAを作成してとプロンプトを送信し、EAを作成します。
生成されたEAで2013年1月から2024年6月まででバックテストをまわしてみます。

イマイチの成績だったので、トレードする時間帯を指定してみます。

サーバー時間の7時から18時までエントリーするように修正してとプロンプトを送信します。

1分足で始値のみのバックテスト結果は良好だったので、TDSによる全ティックバックテストをまわしてみましたが、トレード回数が異常に多いです。

始値のみだと年間200回程度と適正なトレード回数なのに、全ティックでは10倍多くトレードすることになり、年間2000回オーバーの高頻度トレードになってしまいます。

原因はここの短時間での連続トレードが影響しているようです。


短時間での連続トレード

始値のみだとこうした連続トレードは発生していません。

さらに、全ティックでのバックテストを完走させたところ、非常に興味深い結果が得られました。

収支自体は上がったり下がったりの横ばいになりましたが、わたしのEA開発の経験上、ここまでの高頻度トレードのEAは高確率で右肩下がりになります。

全ティックでのバックテスト


ですがこのEA、調整次第で勝てるEAになるかも?

わたしの第六感が何かを示唆しています。

みなさんはお気付きでしょうか?

そうです、最適化を行うことで成績は改善します。

どこまで伸びるかはやってみないと分かりません。

リアル運用できるレベルのEAになる可能性は十分あると思います。

まずは、ここまでのChatGPTとの対話を記録します。

https://chatgpt.com/share/64ef4fa5-6c14-4d90-8606-ddc5516b9a51
上記のリンクよりChatGPTとの対話の履歴をダウンロードできます。

ここからは手動でプログラミングします。

パラメータの調整をChatGPTで行うことは出来ますが、時間がかかります。

今回は、手動で修正することにします。

MQL4の知識が必要になりますが、プログラムソースコードを細かく見てみましょう。

プログラムソースコード

ダウ理論に基づく長期トレンドの判断は、長期移動平均線より現在値が上でロング、下でショートになっているようです。
次に、グランビルの法則によるエントリー条件を確認します。

ダウ理論がロングの時に、現在の値が短期移動平均線を下から上に抜けたらロングエントリーとなります。
反対に、ダウ理論がショートの時に、現在の値が短期移動平均線を上から下に抜けたらショートエントリーとなります。

トレードが頻発する原因を調べるにはエントリー条件を細かく分析しないと分かりません。

今回行った修正は以下の通りです。

まず、現在値を参照するプログラムを1本前の終値を参照するプログラムに書き換え、始値のみできちんとバックテストできるように修正しました。

修正前のプログラム


この修正により、始値のみと全ティックとでバックテストした場合に、トレード成績に大きな違いが無くなります。

バックテスト結果はプロフィットファクターは1.04、トレード回数は20,000回になります。

次に、パラメータの最適化を行います。

短期移動平均の期間と長期移動平均の期間を総当たりでバックテストし、パラメータ値の最適化を行いました。

LongTermPeriod = 950;// 長期トレンドを判断する移動平均線の期間 200⇒950
ShortTermPeriod = 30;// 短期トレンドを判断する移動平均線の期間 50⇒30

ドル円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 LongTermPeriod = 950;        // 長期トレンドを判断する移動平均線の期間
extern int ShortTermPeriod = 30;        // 短期トレンドを判断する移動平均線の期間
// ここまで改変可能

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;
     }

// ここから改変可能
   double longTermMA = iMA(NULL, 0, LongTermPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
   double shortTermMA = iMA(NULL, 0, ShortTermPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
   double prevShortTermMA = iMA(NULL, 0, ShortTermPeriod, 0, MODE_SMA, PRICE_CLOSE, 2);

   // ダウ理論に基づく長期トレンドの判断(tyou)
   bool isLongTermUptrend = (Close[1] > longTermMA);
   bool isLongTermDowntrend = (Close[1] < longTermMA);

   // グランビルの法則による短期トレンドの判定
   if(isLongTermUptrend && Close[1] > shortTermMA && Close[2] < prevShortTermMA)
     {
      LongSign = true;
      ShortSign = false;
     }
   else if(isLongTermDowntrend && Close[1] < shortTermMA && Close[2] > prevShortTermMA)
     {
      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 && Hour() >= 7 && Hour() <= 18)
     {
      if(CalculateCurrentOrders() == 0)
        {
         // ポジション保有していない場合
         CheckForOpenLong();  // 新規ロングオーダー処理を行う
         e++;
        }
      else
        {
         // ポジション保有している場合
         CheckForCloseLong();  // ショートポジションがある場合のみ決済ロング処理
        }
     }
   if(ShortSign == true && Hour() >= 7 && Hour() <= 18)
     {
      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;
  }

2013年からのバックテストではこのような結果になりました。

2013年からのバックテスト(完成版)

トレード回数は9843回と半分以下になりましたが、成績は安定しました。
残念ながらリアル運用できるレベルの完成度とはいきませんが、それなりに優位性のあるEAに仕上がったのではないでしょうか。

今回の結果について、EA開発者の目線で言わせてもらえば、グランビルの法則がロジックにきちんと組み込まれていない点がマイナスです。

複雑なロジックはEA化が難しく、きちんとプロンプトを送信しないとまったく違ったEAに仕上がってしまいます。

グランビルの法則に精通しているトレーダー様は、ぜひ、グランビルの法則の詳細をプロンプトに打ち込み、EAを完成して頂けると、このGPTsを作った甲斐があります。

素晴らしいEAが開発できたなら、Xのコメントでシェアして頂けると幸いです。

次回の動画では「エリオット波動」を使用したEAの開発を行いたいと思います。

エリオット波動

エリオット波動はグランビルの法則とセットで使用できるとネットの記事で見たことがあります。
この様な超高難易度のマルチロジックEAも、ChatGPTを使えば一瞬で作成することができます。
他にも、こんな手法EAにしてほしいみたいなリクエストがあれば、Xにコメントお願いします。


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