見出し画像

【第9回】ChatGPT最新AIモデル GPT-4oを使ったEA開発方法【EA開発】

ChatGPT担当のナナミです。

前回第8回の記事では、ChatGPTを使いマルチロジックEAを完成させるところまでやりました。
https://note.com/aimjey/n/n7d849f2b5b37

今回第9回では、トレンドフォロー型の順張りナンピンピラミッティングのEA開発にチャレンジしてみました。

また、追加の機能としてMT4のEAのポジションのところにコメントを表示させたいと思います。
保有ポジションのところに表示させるコメントで、どのEAで発注されたか知るために必要な情報になります。


MT4のポジションコメント

使用するEAのソースコードは、第6回で完成したEAのものを使用します。
https://note.com/aimjey/n/n1710e9e5bfac

ロジックはドル円1分足チャートで動作する「Hyper Scalping M1」というEAを参考にしました。
https://www.gogojungle.co.jp/systemtrade/fx/44393


Hyper Scalping M1のバックテスト結果

実はこのEA、2023年6月に販売開始してから負け続け、あまりの成績の悪さからゴゴジャンのフォワード計測が停止しているEAです。

資産推移チャートも一直線で負け続け、デモ口座とはいえ100万円近くフォワードで負けたわたしの最大の失敗作です。

バックテスト結果を見ると、長期間のドローダウンはあるものの、開発した時点では直近で大きく勝っていることから、トレンド相場に強いと確信し、自身を持ってリリースしたわけですが。
結果は見事、一直線に負け続けたわけです。

今後成績が改善する可能性はあるものの、この様なドローダウンを発生させたEAなど、リアル口座で運用する気にはなれません。

というわけで、「Hyper Scalping M1」のナンピンピラミッティングのロジックをChatGPTに伝えて、このEAと同じ様な成績になるのか検証してみたいと思います。
Hyper Scalpingの成績は、10年のバックテストで49000回のトレードを行います。
年間5000回エントリーを行う高頻度トレードEAです。

まず初めに、EAのポジションのところにコメントを表示させたいと思います。

プロンプトは簡単です。

プロパティ項目にCommentPositionsを追加してください。
extern string CommentPositions = "MovingAverage";として、
CommentPositionsの内容は、OrderSend()のコメント部分に記述します

完成したコードでポジションのところに表示するコメントが表示できるようになりました。


ChatGPTが生成したEA

次回の連載から、GPTストアで公開しているわたしが作ったカスタムGPTを使用してEA開発を行います。
GPTsを使用することでEAのテンプレート化を行い、ロジックをChatGPTに伝えるだけで簡単にバグ対策の施されたEAが作成できます。

Masayan EA Generator for MT4 Ver1.01

それでは続きの作業に入ります。前回作成したEAのソースコードを使って、ナンピンピラミッティングのEAを作りたいと思います。

送信するプロンプトは以下のようになります。

以下のプログラムはパーフェクトオーダーのロジックを使用したMT4のEA自動売買プログラムです。
移動平均線の期間を以下の値に変更してください。
extern int ShortMovingPeriod = 10;
extern int MiddleMovingPeriod = 30;
extern int LongMovingPeriod = 75;
次に、ナンピン設定を行います。
ポジションは同じ方向に3ポジション持ちます。
プロパティ項目に以下の項目を追加してください。
Lots1 = 0.1;// 1ポジション目のロット数
Lots2 = 0.2;// 2ポジション目のロット数
Lots3 = 0.3;// 3ポジション目のロット数
Lots1以下の条件で、保有ポジション数が0の時にエントリーします。
ShortMovingPeriod = 10;
MiddleMovingPeriod = 30;
LongMovingPeriod = 75;
Lots2の移動平均線の期間は以下の条件で、保有ポジション数が1の時にエントリーします。
ShortMovingPeriod = 15;
MiddleMovingPeriod = 40;
LongMovingPeriod = 125;
Lots3の移動平均線の期間は以下の条件で、保有ポジション数が2の時にエントリーします。
ShortMovingPeriod = 40;
MiddleMovingPeriod = 100;
LongMovingPeriod = 300;
決済のタイミングはドテン売買とし、反対のサインが出た時にポジションをすべてクローズします。

//+------------------------------------------------------------------+
//|                                               Moving Average.mq4 |
//|                                         https://note.com/aimjey/ |
//+------------------------------------------------------------------+

// EA識別用マジックナンバー
extern int MAGICMA = 20240611;

// ロットサイズの入力
extern double Lots = 0.1;

// 移動平均線の期間を設定
extern int ShortMovingPeriod = 20;  // 短期移動平均線の期間
extern int MiddleMovingPeriod = 75; // 中期移動平均線の期間
extern int LongMovingPeriod = 100;  // 長期移動平均線の期間

// 1時間あたりの注文回数の上限(プロパティ項目で設定可能)
input int MaxOrder = 20;  // 最小=10 最大=50 デフォルト=20

// スプレッドの最大値(ピップス単位)
input double MaxSpread = 2.0; // スプレッドフィルターを追加

// ストップロスとテイクプロフィット(ピップス単位)
extern double StopLoss = 400; // 最小=10 最大=1000
extern double TakeProfit = 500; // 最小=10 最大=1000

// 注文コメント
extern string CommentPositions = "MovingAverage";

int OrderCount = 0; // 現在の注文数をカウント
datetime LastOrderTime = 0; // 最後に注文を行った時間

//+------------------------------------------------------------------+
//| Calculate open positions                                         |
//| 現在のオープンポジションを計算する関数                             |
//+------------------------------------------------------------------+
int CalculateCurrentOrders(string symbol)
{
    int buys = 0, sells = 0; // 買いポジションと売りポジションのカウンタを初期化

    // 全てのオープンオーダーをチェック
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) break; // オーダーが選択できない場合、ループを抜ける
        if (OrderSymbol() == Symbol() && OrderMagicNumber() == MAGICMA)
        {
            // 買いオーダーの場合、カウンタを増やす
            if (OrderType() == OP_BUY) buys++;
            // 売りオーダーの場合、カウンタを増やす
            if (OrderType() == OP_SELL) sells++;
        }
    }

    // 買いオーダーの数を返す。売りオーダーの場合は負の値を返す
    if (buys > 0) return (buys);
    else return (-sells);
}

//+------------------------------------------------------------------+
//| Calculate optimal lot size                                       |
//| 最適なロットサイズを計算する関数                                  |
//+------------------------------------------------------------------+
double LotsOptimized()
{
    // 入力パラメータのロット数を小数点第2位で正規化
    double lot = NormalizeDouble(Lots, 2);

    // 最小ロットサイズを取得
    double minLot = 0.01;
    // 最大ロットサイズを設定
    double maxLot = 10.0;
    
    // 最小ロットサイズ以上に調整
    if (lot < minLot) lot = minLot;
    // 最大ロットサイズ以下に調整
    if (lot > maxLot) lot = maxLot;
    // ロットステップに従って丸める
    lot = NormalizeDouble(lot, 2);
    return (lot);
}

//+------------------------------------------------------------------+
//| Check for open order conditions                                  |
//| 新しいオーダーを開く条件をチェックする関数                         |
//+------------------------------------------------------------------+
void CheckForOpen()
{
    double shortMA, middleMA, longMA; // 移動平均線の値を保存する変数
    int res;

    // スプレッドを取得
    double spread = (Ask - Bid) / (Point() * 10);

    // スプレッドが最大値を超える場合、何もしない
    if (spread > MaxSpread) return;

    // 各移動平均線の値を取得
    shortMA = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
    middleMA = iMA(NULL, 0, MiddleMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
    longMA = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);

    // ストップロスとテイクプロフィットをpipsに変換
    double slPoints = StopLoss * (Point() * 10);
    double tpPoints = TakeProfit * (Point() * 10);

    // 買いの条件(移動平均線のパーフェクトオーダー)
    if (shortMA > middleMA && middleMA > longMA && OrderCount < MaxOrder)
    {
        // 買いオーダーを送信
        res = OrderSend(Symbol(), OP_BUY, LotsOptimized(), Ask, 3, Ask - slPoints, Ask + tpPoints, CommentPositions, MAGICMA, 0, Blue);
        if (res > 0)
        {
            OrderCount++;
            LastOrderTime = TimeCurrent();
        }
        else
        {
            Print("OrderSend error ", GetLastError()); // オーダー送信エラーを表示
        }
        return;
    }

    // 売りの条件(移動平均線のパーフェクトオーダー)
    if (shortMA < middleMA && middleMA < longMA && OrderCount < MaxOrder)
    {
        // 売りオーダーを送信
        res = OrderSend(Symbol(), OP_SELL, LotsOptimized(), Bid, 3, Bid + slPoints, Bid - tpPoints, CommentPositions, MAGICMA, 0, Red);
        if (res > 0)
        {
            OrderCount++;
            LastOrderTime = TimeCurrent();
        }
        else
        {
            Print("OrderSend error ", GetLastError()); // オーダー送信エラーを表示
        }
        return;
    }
}

//+------------------------------------------------------------------+
//| Check for close order conditions                                 |
//| オーダーを閉じる条件をチェックする関数                             |
//+------------------------------------------------------------------+
void CheckForClose()
{
    double shortMA, middleMA, longMA; // 移動平均線の値を保存する変数

    // 各移動平均線の値を取得
    shortMA = iMA(NULL, 0, ShortMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
    middleMA = iMA(NULL, 0, MiddleMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
    longMA = iMA(NULL, 0, LongMovingPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);

    // 全てのオープンオーダーをチェック
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES) == false) break; // オーダーが選択できない場合、ループを抜ける

        if (OrderMagicNumber() != MAGICMA || OrderSymbol() != Symbol()) continue; // EAが管理するオーダーか確認

        // 買いオーダーの場合のクローズ条件
        if (OrderType() == OP_BUY)
        {
            // パーフェクトオーダーが崩れた場合
            if (shortMA < middleMA && middleMA < longMA && OrderCount < MaxOrder)
            {
                if (!OrderClose(OrderTicket(), OrderLots(), Bid, 3, White))
                    Print("OrderClose error ", GetLastError()); // オーダークローズエラーを表示
                OrderCount++;
            }
            break;
        }

        // 売りオーダーの場合のクローズ条件
        if (OrderType() == OP_SELL)
        {
            // パーフェクトオーダーが崩れた場合
            if (shortMA > middleMA && middleMA > longMA && OrderCount < MaxOrder)
            {
                if (!OrderClose(OrderTicket(), OrderLots(), Ask, 3, White))
                    Print("OrderClose error ", GetLastError()); // オーダークローズエラーを表示
                OrderCount++;
            }
            break;
        }
    }
}

//+------------------------------------------------------------------+
//| OnTick function                                                  |
//| 毎ティック実行される関数                                          |
//+------------------------------------------------------------------+
void OnTick()
{
    // 必要なバー数がない場合、または取引が許可されていない場合は何もしない
    if (Bars < 100 || !TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
        return;

    // 1時間ごとにOrderCountをリセット
    if (TimeCurrent() - LastOrderTime >= 3600)
    {
        OrderCount = 0;
    }

    // 現在のシンボルのオープンオーダーを計算
    int currentOrders = CalculateCurrentOrders(Symbol());
    if (currentOrders == 0) 
        CheckForOpen(); // 新しいオーダーを開く条件をチェック
    else 
        CheckForClose(); // 既存のオーダーを閉じる条件をチェック

    // スプレッドを取得
    double spread = (Ask - Bid) / (Point() * 10);

    // 売買サインを取得
    string signal;
    if (currentOrders > 0)
        signal = "LONG";
    else if (currentOrders < 0)
        signal = "SHORT";
    else
        signal = "NEUTRAL";

    // チャート左上にスプレッドと売買サインを表示
    Comment("Spread: ", DoubleToString(spread, 1), "\nSignal: ", signal);
}

//+------------------------------------------------------------------+
//| Check input parameters                                           |
//| 入力パラメータのチェック関数                                      |
//+------------------------------------------------------------------+
void CheckInputParameters()
{
    if (StopLoss < 10) StopLoss = 10;
    if (StopLoss > 1000) StopLoss = 1000;
    if (TakeProfit < 10) TakeProfit = 10;
    if (TakeProfit > 1000) TakeProfit = 1000;
}

//+------------------------------------------------------------------+
//| OnInit function                                                  |
//| 初期化時に実行される関数                                          |
//+------------------------------------------------------------------+
int OnInit()
{
    // 入力パラメータをチェック
    CheckInputParameters();
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//| 非初期化時に実行される関数                                        |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
}

//+------------------------------------------------------------------+

何度か試したものの、どうしてもワンポジEAになってしまいます。

仕方ないので、MQL4でナンピンEAを作成してください。とだけ伝えます。

すると、うまくいかなかったポジション数をカウントする関数が作成され、狙い通りのEAに仕上がりました。

完成したEAで、return value of 'OrderClose' should be checkedという警告が出ていたので修正してもらいました。

完成したコードがこちらです。

//+------------------------------------------------------------------+
//|                                                      MartingaleEA.mq4 |
//|                        Copyright 2024, MetaQuotes Software Corp.       |
//|                                       https://www.metaquotes.net/      |
//+------------------------------------------------------------------+
#property strict

extern int MAGICMA = 20240611;

// ポジション毎のロットサイズ
extern double Lots1 = 0.1;
extern double Lots2 = 0.2;
extern double Lots3 = 0.3;

// 1ポジション目の移動平均線の期間
extern int ShortMovingPeriod1 = 10;
extern int MiddleMovingPeriod1 = 30;
extern int LongMovingPeriod1 = 75;

// 2ポジション目の移動平均線の期間
extern int ShortMovingPeriod2 = 15;
extern int MiddleMovingPeriod2 = 40;
extern int LongMovingPeriod2 = 125;

// 3ポジション目の移動平均線の期間
extern int ShortMovingPeriod3 = 40;
extern int MiddleMovingPeriod3 = 100;
extern int LongMovingPeriod3 = 300;

int ticket;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Initialization code here
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // Cleanup code here
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   int totalPositions = CountMagicNumberPositions(MAGICMA);

   double maShort1 = iMA(NULL, 0, ShortMovingPeriod1, 0, MODE_SMA, PRICE_CLOSE, 0);
   double maMiddle1 = iMA(NULL, 0, MiddleMovingPeriod1, 0, MODE_SMA, PRICE_CLOSE, 0);
   double maLong1 = iMA(NULL, 0, LongMovingPeriod1, 0, MODE_SMA, PRICE_CLOSE, 0);

   double maShort2 = iMA(NULL, 0, ShortMovingPeriod2, 0, MODE_SMA, PRICE_CLOSE, 0);
   double maMiddle2 = iMA(NULL, 0, MiddleMovingPeriod2, 0, MODE_SMA, PRICE_CLOSE, 0);
   double maLong2 = iMA(NULL, 0, LongMovingPeriod2, 0, MODE_SMA, PRICE_CLOSE, 0);

   double maShort3 = iMA(NULL, 0, ShortMovingPeriod3, 0, MODE_SMA, PRICE_CLOSE, 0);
   double maMiddle3 = iMA(NULL, 0, MiddleMovingPeriod3, 0, MODE_SMA, PRICE_CLOSE, 0);
   double maLong3 = iMA(NULL, 0, LongMovingPeriod3, 0, MODE_SMA, PRICE_CLOSE, 0);

   // ポジション数に応じた移動平均線の条件を確認
   if (totalPositions == 0 && CheckPerfectOrder(maShort1, maMiddle1, maLong1))
     {
      ticket = OrderSend(Symbol(), OP_BUY, Lots1, Ask, 3, 0, 0, "Buy Order 1", MAGICMA, 0, Blue);
      if (ticket < 0) Print("OrderSend error: ", GetLastError());
     }
   else if (totalPositions == 1 && CheckPerfectOrder(maShort2, maMiddle2, maLong2))
     {
      ticket = OrderSend(Symbol(), OP_BUY, Lots2, Ask, 3, 0, 0, "Buy Order 2", MAGICMA, 0, Blue);
      if (ticket < 0) Print("OrderSend error: ", GetLastError());
     }
   else if (totalPositions == 2 && CheckPerfectOrder(maShort3, maMiddle3, maLong3))
     {
      ticket = OrderSend(Symbol(), OP_BUY, Lots3, Ask, 3, 0, 0, "Buy Order 3", MAGICMA, 0, Blue);
      if (ticket < 0) Print("OrderSend error: ", GetLastError());
     }

   // 決済条件を確認
   if (totalPositions > 0)
     {
      if (CheckReverseOrder())
        {
         for (int i = totalPositions - 1; i >= 0; i--)
           {
            if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
              {
               if (OrderMagicNumber() == MAGICMA)
                 {
                  bool result = OrderClose(OrderTicket(), OrderLots(), Bid, 3, Red);
                  if (!result) Print("OrderClose error: ", GetLastError());
                 }
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| マジックナンバーに対応するポジション数をカウントする関数           |
//+------------------------------------------------------------------+
int CountMagicNumberPositions(int magicNumber)
  {
   int count = 0;
   for (int i = 0; i < OrdersTotal(); i++)
     {
      if (OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
        {
         if (OrderMagicNumber() == magicNumber)
           {
            count++;
           }
        }
     }
   return(count);
  }

//+------------------------------------------------------------------+
//| パーフェクトオーダーを確認する関数                                |
//+------------------------------------------------------------------+
bool CheckPerfectOrder(double shortMA, double middleMA, double longMA)
  {
   return (shortMA > middleMA && middleMA > longMA);
  }

//+------------------------------------------------------------------+
//| 反対のサインを確認する関数                                        |
//+------------------------------------------------------------------+
bool CheckReverseOrder()
  {
   double maShortRev = iMA(NULL, 0, ShortMovingPeriod1, 0, MODE_SMA, PRICE_CLOSE, 0);
   double maMiddleRev = iMA(NULL, 0, MiddleMovingPeriod1, 0, MODE_SMA, PRICE_CLOSE, 0);
   double maLongRev = iMA(NULL, 0, LongMovingPeriod1, 0, MODE_SMA, PRICE_CLOSE, 0);

   return (maShortRev < maMiddleRev && maMiddleRev < maLongRev);
  }
//+------------------------------------------------------------------+

成績を比べてみましょう。

元になったEAのバックテスト結果


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

完全に同じとはいきませんが、ドローダウン発生時期など同じなので、同じようなタイミングでエントリーしているようです。

どうやら複雑なEAはうまく処理できないみたいです。

次回の動画から「あなたの考えたFXトレード手法を、ChatGPTでEA化する」というタイトルで連載動画を作成したいと思います。

あなたの考えたFXトレード手法を、ChatGPTでEA化する

条件を指定するだけで高性能なEAを生成できるGPTsのまさやんオリジナルのジェネレーター。

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

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

どなたでも無料でご利用でき、高性能なワンポジション型EAが作成できます。

今後、こちらのGPTsを使用してEA開発を行いたいと思います。

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

#GPTsつくってみた

1,446件

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