見出し画像

【第4回】EAのインジケーター化に成功:無料のスキャル系EA「Grid Rash」に決済ロジックを追加


ChatGPT担当のナナミです。

前回の動画では、無料のスキャル系EA「Grid Rash」をMT4の最適化機能を使い、勝てるEAに仕上げるところまでやりました。


本日の内容は盛りだくさんとなっております。

まず、Grid Rashにローソク足によるプライスアクションのフィルターを追加します。

次に、決済ロジックを追加します。

最後に、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/10/16/mt4初心者向け!gptsのmasayan-ea-generatorを使った自動売買講座/

■前回記事 MT4のバックテストで勝てる設定を見つける方法を紹介
https://note.com/aimjey/n/ncf5a62842e5e

まず初めに、どのプライスアクションを採用するか考えてみます。

以前、プライスアクション手法のEAをChatGPTで作成したところ、あまり良い結果は得られませんでした。

過去記事
https://note.com/aimjey/n/n17f0c8f74027
インサイドバーにいたってはエントリーしないEAになってしまいました。

どのプライスアクションが良いのか、見当が付きません。

というわけで、ChatGPTに丸ごと投げてみます。

前回作成したGrid RashのVer3のソースコードを使い、以下の内容でプロンプトを送信してみます。

以下のEAのうち、5のADX以外をローソク足のプライスアクション手法に置き換えてください。
ロジックを入れ変える項目は1の移動平均、2のボリンジャーバンド、3のMACD、 4のRSIです。
プライスアクション手法は、ローソク足を使用したシンプルなロジックとします。

5のADX以外のロジックをシンプルなローソク足のプライスアクションに書き変えてもらいます。

作成したEAでバックテストをまわしましたがフィルターがうまく機能しません。

原因はFilterConditionの部分が間違っているみたいです。

手作業で修正をしましたが、エントリー回数の極端に少ない使えないEAになってしまいます。

仕方ないので、一個一個ChatGPTに作成してもらうようにしましょう。

ブレイクアウトからランウェイアップ・ランウェイダウンまで合計11個のロジックをChatGPTに作ってもらいます。


以前作成したEAの結果


結果のみお伝えします。

1のブレイクアウトはエントリー頻度が少ないのでNG。

2のフェイクアウトのプロフィットファクターは1.06でした。

3のスラストアップ・スラストダウンはエントリー頻度が少ないのでNG。

4のリバーサルハイ・リバーサルローはエントリー頻度が少ないのでNG。

5のピンバーのプロフィットファクターは1.06でした。

6のフェイクセットアップはプロフィットファクターは1.12ですが、トレード回数が1224回と少なすぎます。

7のフォールスブレイクアウトのプロフィットファクターは1.12ですが、トレード回数が少なすぎます。

8のスパイクはエントリーしません。

9のアウトサイドバーはエントリー頻度が少ないのでNG。

10のインサイドバーはエントリー頻度が少ないのでNG。

11のランウェイアップ・ランウェイダウンもエントリーが少ないです。

何かやり方が間違っているのでしょうか?

ここから更に時間をかけて設定をいじってみましたが、満足いく結果は得られませんでした。


今回の結果

というわけで、プライスアクション手法とは相性が悪い可能性もあることですし、気を取り直して次のテーマ「決済ロジックを追加する」に進みます。

まず、ベースとなるEAは前回の動画で作成した、Grid Rash Ver3です。

フィルター5のADXはそのまま残して、他の4つのロジックを決済のロジックに移植したいと思います。

今回は、手作業でコーディングすることにします。

/決済のテンプレート/
if(CalculateCurrentOrders() == 1){// ロングポジション保有
//ここに決済の条件を入力
CloseLongPosition();// ロングポジションを決済
} else if(CalculateCurrentOrders() == -1){// ショートポジション保有
//ここに決済の条件を入力
CloseShortPosition();// ショートポジションを決済
}

上記の決済ロジックを組み込みしました。

1の移動平均線、
2のボリンジャーバンド、
3のMACD、
4のRSIを順番に試しましたが、RSIの決済ロジックが一番成績が良かったです。

一番成績が良かった設定はRSIの期間が9でRSIUpperが70、RSILowerが30でした。

TDSによる全ティックバックテストの結果です。








5分足チャートだけではく、1分足チャートでも同じ設定で優位性が確認できました。

結果的に、RSIの決済ロジックを追加した場合とそうでない場合とプロフィットファクターは同じになりました。

RSIの決済ロジックを追加した方が、ポジションの保有時間が短くなるので、好みに応じてRSIの決済フィルターをオンオフして下さい。

今回、こちらのEAのソースコードは無料で公開いたしますが、ご利用にあたり、一つだけお願いがあります。

「Grid Rash Arrow Indicator」というサインインジケーターをゴゴジャンで販売することになりました。

このインジケーターは、販売本数に応じて価格が変動します。

1本目から10本目までは550円、11本目から100本目までは5,500円、101本目以上は55,000円に値上げする予定です。

2024年10月23日現在、発売直後ということもあり、価格は550円です。

EAは無料でダウンロードできますが、インジケーター版のご購入をお願いしたいと思います。

下記ページよりご購入いただけます。 https://www.gogojungle.co.jp/tools/indicators/55251

わたくし「まさやん」への応援という形で、金銭的に余裕のある方のご協力をお待ちしております。

インジケーターが売れ始めると、わたくし「まさやん」の収益となり、今後のチャンネル運営やEA開発のモチベーションにつながります。

再生数とチャンネル登録者が足りず、YouTubeでの収益化は期待できない以上、今回このようなお願いをすることになったことをお詫び申し上げます。

この動画を作成している時点で、既に3本売れています。

値上がりして購入できない方は、YouTube動画へのいいね!をお願いします。
また、YouTubeにコメントを頂けるだけで、YouTubeのアルゴリズムが反応し、この動画が拡散されますので、コメントといいねもよろしくお願いします。

EAのソースコードは下記よりダウンロードできます。

//---START--------
#property copyright "Copyright 2024, Masayan."
#property version   "1.01"
#property strict
#property description "https://fx.reform-network.net"

extern int Magic = 202401023;  // Magic number
extern double Lots = 0.1;     // ロットサイズ
extern double StopLossRequest = 4.0;   // ストップロス幅
extern double TakeProfitRequest = 5.0; // テイクプロフィット幅
extern int MaxSpread = 50;     // 最大スプレッド
extern int MaxError = 100;     // 最大エラーカウント
extern double GridSize = 0.05;  // カギ足のサイズ(最小値0.01=1pips 推奨値0.05=5pips)
extern int FilterType = 5; // ADXフィルターなし=0 ADXフィルターあり=5
extern int ADXPeriod = 40;
extern double ADXThreshold = 15.0;
input bool RSIFilter = true;// RSIによる決済ロジックを 有効にする true 有効にしないfalse
extern int RSIPeriod = 9;
extern double RSIUpper = 70.0;
extern double RSILower = 30.0;
extern string CommentPositions = "Grid Rash Ver5";

//ここから改変可能
double lastBrickHigh = 0;  // 前回のカギ足高値
double lastBrickLow = 0;   // 前回のカギ足安値
//ここまで改変可能

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;

    double currentPrice = iClose(NULL, 0, 0);
    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){
Pips = 1000.0;
}

    if ( StopLossRequest <= 0.1 ){
    StopLossRequest = 0.1;
    }
    if ( TakeProfitRequest <= 0.1 ){
    TakeProfitRequest = 0.1;
    }
bool FilterCondition = true;  // フィルター条件の初期値をtrueに設定

switch (FilterType) {
    case 5: // ADXフィルター
    {
        double adx = iADX(NULL, 0, ADXPeriod, PRICE_CLOSE, MODE_MAIN, 1);
        if (adx > ADXThreshold) {
            FilterCondition = true; // 強いトレンド
        } else {
            FilterCondition = false; // トレンドが弱い
        }
        break;
    }
    default: // フィルターなし
        FilterCondition = true;
        break;
}

// カギ足ロジックを生成する

    // 初回は基準の高値・安値を設定
    if (lastBrickHigh == 0 && lastBrickLow == 0) {
        lastBrickHigh = currentPrice;
        lastBrickLow = currentPrice;
        return;
    }

    // カギ足の生成条件チェック
    if (currentPrice >= lastBrickHigh + (GridSize * Pips) && Seconds() < 3 && FilterCondition) { 
        ShortSign = true;
        LongSign = false;
        lastBrickHigh = currentPrice;
        lastBrickLow = currentPrice - (GridSize * Pips);
    } else if (currentPrice <= lastBrickLow - (GridSize * Pips) && Seconds() < 3 && FilterCondition) {
        LongSign = true;
        ShortSign = false;
        lastBrickLow = currentPrice;
        lastBrickHigh = currentPrice + (GridSize * Pips);
    } else {
        LongSign = false;
        ShortSign = false;
    }

/*ここから決済のロジック*/
    // RSIフィルター
    if(RSIFilter == true){
        double rsi = iRSI(NULL, 0, RSIPeriod, PRICE_CLOSE, 1);
        if (rsi > RSIUpper) {
            // 売りシグナル
            if(CalculateCurrentOrders() == 1){// ロングポジション保有
            CloseLongPosition();// ロングポジションを決済
            }
        } else if (rsi < RSILower) {
            // 買いシグナル
            if(CalculateCurrentOrders() == -1){// ショートポジション保有
            //ここに決済の条件を入力
            CloseShortPosition();// ショートポジションを決済
            }
        }
    }

    // スプレッドのチェック
    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) position_msg = "LongSign = true\n";
    else if (ShortSign) position_msg = "ShortSign = true\n";
    else position_msg = "No Sign\n";

    // 売買指示
    if (LongSign) {
        if (CalculateCurrentOrders() == 0) {
            CheckForOpenLong();
            e++;
        } else {
            CloseShortPosition();
        }
    }

    if (ShortSign) {
        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;
    for (int icount = 0; icount < OrdersTotal(); icount++) {
        if (!OrderSelect(icount, SELECT_BY_POS, MODE_TRADES)) break;
        if (OrderSymbol() == Symbol() && OrderMagicNumber() == Magic) {
            if (OrderType() == OP_BUY) buys++;
            if (OrderType() == OP_SELL) sells++;
        }
    }
    return (buys > 0) ? buys : -sells;
}

//+------------------------------------------------------------------+
//| ロングオーダー処理                                               |
//+------------------------------------------------------------------+
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 CloseShortPosition()
{
    for (int icount = 0; icount < OrdersTotal(); icount++) {
        if (!OrderSelect(icount, SELECT_BY_POS, MODE_TRADES)) break;
        if (OrderMagicNumber() != Magic || OrderSymbol() != Symbol()) continue;
        if (OrderType() == OP_SELL) {
            if (!OrderClose(OrderTicket(), OrderLots(), Ask, Adjusted_Slippage, clrBlue)) 
                Print("エラーコード=", GetLastError());
            break;
        }
    }
}

// ロングポジションをクローズ
void CloseLongPosition()
{
    for (int icount = 0; icount < OrdersTotal(); icount++) {
        if (!OrderSelect(icount, SELECT_BY_POS, MODE_TRADES)) break;
        if (OrderMagicNumber() != Magic || OrderSymbol() != Symbol()) continue;
        if (OrderType() == OP_BUY) {
            if (!OrderClose(OrderTicket(), OrderLots(), Bid, Adjusted_Slippage, clrRed)) 
                Print("エラーコード=", GetLastError());
            break;
        }
    }
}
//---END---

EA開発に自信のある方は、ロジックのところを変更するなどしてカスタマイズしてみてください。

好成績のEAが完成した際には、コメント頂けると幸いです。

最後に、EAのインジケーター化のやり方を解説します。

サインインジケーターを作る場合、サインとなる矢印の条件を細かく設定しないといけません。

EAの場合、売買サインに応じて発注と決済の注文を行いますが、サインインジケーターの場合はサイン点灯とアラート、場合によってはLAINE通知などを行います。

ロングとショートと決済のロジックはEAと同じコードを使用できますが、サインやアラートはインジケーター特有のものとなります。

したがって、インジケーターのテンプレートを用意して、ロジックを組み込む必要があります。

ChatGPTのGPTsを使いサインインジケーターを作ろうと思うと、テンプレートを公開することになります。

わたしの使用しているサインインジケーターのコードをそのまま流出させるわけにはいきませんので、ChatGPTに最初から作ってもらうことにします。

若干のエラーはあったものの、無事完成しました。



わたしが作ったものとサインの位置を比べてみると、矢印の位置が若干異なります。

#property copyright "Copyright 2024, Masayan."
#property version   "1.01"
#property strict
#property description "https://fx.reform-network.net"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 Blue  // ロングシグナルの色
#property indicator_color2 Red   // ショートシグナルの色

extern double GridSize = 0.05;  // カギ足のサイズ
extern int FilterType = 5;      // ADXフィルターなし=0 ADXフィルターあり=5
extern int ADXPeriod = 40;
extern double ADXThreshold = 15.0;
input bool RSIFilter = true;    // RSIによる決済ロジックを有効にする true 有効にしない false
extern int RSIPeriod = 9;
extern double RSIUpper = 70.0;
extern double RSILower = 30.0;

// シグナルを保持するバッファ
double LongBuffer[];
double ShortBuffer[];

double lastBrickHigh = 0;  // 前回のカギ足高値
double lastBrickLow = 0;   // 前回のカギ足安値
double Pips = 0.01;

int OnInit()
{
    SetIndexBuffer(0, LongBuffer);
    SetIndexBuffer(1, ShortBuffer);
    SetIndexStyle(0, DRAW_ARROW);
    SetIndexArrow(0, 233); // ロングシグナル矢印
    SetIndexStyle(1, DRAW_ARROW);
    SetIndexArrow(1, 234); // ショートシグナル矢印
    return(INIT_SUCCEEDED);
}

int OnCalculate(const int rates_total,
                 const int prev_calculated,
                 const datetime &time[],
                 const double &open[],
                 const double &high[],
                 const double &low[],
                 const double &close[],
                 const long &tick_volume[],
                 const long &volume[],
                 const int &spread[])
{
    if (rates_total < 10) return 0;

    int start = prev_calculated > 0 ? prev_calculated - 1 : 0;

    for (int i = start; i < rates_total; i++)
    {
        double currentPrice = close[i];
        bool FilterCondition = true;

        // ADXフィルターの適用
        if (FilterType == 5) {
            double adx = iADX(NULL, 0, ADXPeriod, PRICE_CLOSE, MODE_MAIN, i);
            if (adx <= ADXThreshold) {
                FilterCondition = false;
            }
        }

        // カギ足ロジック
        if (lastBrickHigh == 0 && lastBrickLow == 0) {
            lastBrickHigh = currentPrice;
            lastBrickLow = currentPrice;
        }

        if (currentPrice >= lastBrickHigh + (GridSize * Pips) && FilterCondition) {
            // ショートシグナル
            ShortBuffer[i] = high[i];  // 矢印を高値に表示
            LongBuffer[i] = EMPTY_VALUE;
            lastBrickHigh = currentPrice;
            lastBrickLow = currentPrice - (GridSize * Pips);
        } else if (currentPrice <= lastBrickLow - (GridSize * Pips) && FilterCondition) {
            // ロングシグナル
            LongBuffer[i] = low[i];   // 矢印を安値に表示
            ShortBuffer[i] = EMPTY_VALUE;
            lastBrickLow = currentPrice;
            lastBrickHigh = currentPrice + (GridSize * Pips);
        } else {
            LongBuffer[i] = EMPTY_VALUE;
            ShortBuffer[i] = EMPTY_VALUE;
        }

        // RSIによるフィルター
        if (RSIFilter) {
            double rsi = iRSI(NULL, 0, RSIPeriod, PRICE_CLOSE, i);
            if (rsi > RSIUpper) {
                // RSIが上限を超えた場合、ショートシグナルをキャンセル
                ShortBuffer[i] = EMPTY_VALUE;
            } else if (rsi < RSILower) {
                // RSIが下限を下回った場合、ロングシグナルをキャンセル
                LongBuffer[i] = EMPTY_VALUE;
            }
        }
    }

    return rates_total;
}

上記のインジケーターはChatGPTが作成したもので、バグがあるかもしれません。
またサインのタイミングがEAと同じタイミングではないので、負けるインジケーターの可能性が高いです。

リアル運用をされる方は、きちんと検証を行ってからにしてください。

それにしても、恐るべしはChatGPT、EAのインジケーター化までをやってのけるとは、もはやプログラマー廃業時代の到来ですね。

まさかChatGPTがEAのインジケーター化までも、サクッとやってしまうとか、もう驚きですよね。

要望があれば、ChatGPTを使用してインジケーターを作ってみた!みたいな感じの動画もやっていこうと思います。

サインインジケーターの場合、EAの売買タイミングと完全に一致させることができないのがネックです。

なぜなら、EAは毎ティックごとに売買判定できますが、インジケーターの場合でリペイントさせないとすると、バーが確定したタイミングでしかサインを出せないからです。

最後までご覧いただきありがとうございました。

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

いいなと思ったら応援しよう!