見出し画像

【ジョルノ式トレード編】FX手法をChatGPTでEA化する【勝てるロジックのEA化】


ChatGPT担当のナナミです。

前回の動画では、エリオット波動を使用した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/n35720e344514

本日は、ジョルノ式トレードを使用したEAを作成します。
ジョルノ式トレードとは、カリスマ投資家のジョルノ氏が開発した手法で、2020年に16億円稼いだとされている手法です。

開発者のジョルノ氏がSNSでその手法を公開したことで瞬く間に手法が拡散され、現在多くのトレーダーにより検証されています。

ジョルノさんが2020年に16億円稼いだ時は、GOLDをメインでトレードされていたようです。

今回、検証する通貨ペアは、ドル円だけでなくGOLDも検証してみたいと思います。

果たしてジョルノ式トレードは本当に稼げる手法なのでしょうか?

また、現在の相場で通用する手法なのでしょうか?

まずはじめに、ジョルノ式トレードの条件を細かく指定しMasayan EA Generator for MT4にプロンプトを送信します。

送信するプロンプトは以下の通りです。

まず、ジョルノ式トレードの概要を説明します。

時間軸は1時間足や4時間足を使用します。
買われ過ぎや売られすぎの反発を狙う逆張り手法です。
ポジションは数日保有するスイングトレードのようです。

使用するインジケーターは以下の3つです。
1、ボリンジャーバンド6本
2、移動平均線2本
3、CCI2本

ChatGPTが考えたエントリールールです。
1、CCI (14,12) が±200を超える。
2、CCIが確定後にもう一度伸びた後にエントリー。
3、300ボリンジャーバンドまたは200SMA/200EMAにタッチしたときにエントリー。

少し長いですが、この様な条件でEAを作成してもらいました。

ジョルノ式トレードは設定するパラメータ値が多く、利用者により設定条件が異なります。
ジョルノ式トレードのルールを正確に伝えることで、理想のEAが作れると思います。

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

ですが、ここで致命的な欠陥に気付きました。

まず、売りと買いが反対になっています。
また、CCIが200とか-200は、RSIで言うところの+90とか10みたいな感じで、逆張りで使用するには条件が厳しすぎます。

試しにCCIが200以上でショート、-200以下でロングのロジックでバックテストをまわしてみたところ、エントリー条件が厳しすぎてほとんどエントリーしないEAになってしまいます。

ゴールドもドル円でも同じ結果です。

CCIについて少し説明をします。

ジョルノ式トレードで使用するテクニカル指標のCCI


CCIとは、コモディティ・チャネル・インデックス(Commodity Channel Index)の略で、「売られ過ぎ」や「買われ過ぎ」を判断するオシレーター系のテクニカル指標です。

CCIはその日の高値・安値・終値の平均値の移動平均から、現在価格がどれくらい乖離しているかを知ることができます。

またRSIと違い上限と下限がありません。

RSIは終値ベースで計算するのに対し、CCIはローソク足のヒゲを組み込むことから瞬間的な値動きを把握するのに適します。

ゴールドや原油などのコモディティ相場で使用するテクニカル指標ということですね。

エントリー条件を緩くしないといけないので、試しにCCIの値を100以上でショート、-100以下でロングの条件に変更してみました。

すると、トレード回数は劇的に増えました。

しかしながら、資産チャートは右肩下がりになってしまいます。

わたしはこれまで、100個くらいEAを作ってきましたが、CCIは今回初めて使用しました。

RSIと同じ様な使い方ができるのであれば、朝スキャEAに使える可能性があります。

試しにサーバー時間の23時から6時にエントリーするように修正してバックテストをまわしてみましたが、成績はイマイチです。

これはひょっとして、そのままの設定では使えないのでは?と思いました。

ジョルノさんは裁量トレーダーで、ゴールドの瞬間的な値動きをとらえるプライスアクションの手法を得意としているのではないでしょうか?
つまり、売られ過ぎ買われ過ぎを判断するのにジョルノ式トレードを開発したわけで、そのままEA化しても勝てないということです。

ネットで公開されている検証結果を見ると、トレード回数が少ないが、ゴールドだけでなく、FX通貨でも優位性はあるとの結果が多いようです。

これは詳細に調べる必要がありますね。

参考までに、他のサイトで検証したルールを元に作成したEAのコードをシェアします。
https://chatgpt.com/share/c2b0f5e9-b5f7-4098-95fc-fdbc01ed71c3
1時間足でドル円でもプラスにはなるのですが、トレード回数は非常に少ないです。

それでは本日のまとめです。

・CCIの期間14と12で、±200を超えることは稀なので、1時間足や4時間足ではそもそもトレード条件に合わない。

・5分足や30分足などの短期足を併用することで、エントリー回数を増やすことが出来る。

・EAトレーダーよりも裁量トレーダー向きの手法であり、マルチタイムフレーム分析や他のテクニカル指標と併用する必要がある。

わたしが考えたゴールドで5分足チャートに適正な値は以下の設定です。

CCIPeriod1 = 14;
CCIPeriod2 = 12;
CCILong = -200;
CCIShort = 200;
Deviation = 1;//ボリンジャーバンドのバンドの値(範囲1~3で指定)
BollingerBandsPeriod = 300;
SMAPeriod = 200;
EMAPeriod = 200;


プロパティ設定の画面

プロパティ設定で、ボリンジャーバンドのシグマやCCIの設定が出来るようにプログラムを書き換えました。
下記に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 = 1.0; // 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 CCIPeriod1 = 14;
extern int CCIPeriod2 = 12;
extern int CCILong = -200;
extern int CCIShort = 200;
extern int Deviation = 1;//ボリンジャーバンドのバンドの値(範囲1~3で指定)
extern int BollingerBandsPeriod = 300;
extern int SMAPeriod = 200;
extern int EMAPeriod = 200;
//ここまで改変可能

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 CCI1 = iCCI(NULL, 0, CCIPeriod1, PRICE_CLOSE, 0);
    double CCI2 = iCCI(NULL, 0, CCIPeriod2, PRICE_CLOSE, 0);
    double BollingerUpper = iBands(NULL, 0, BollingerBandsPeriod, Deviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
    double BollingerLower = iBands(NULL, 0, BollingerBandsPeriod, Deviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
    double SMA200 = iMA(NULL, 0, SMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 0);
    double EMA200 = iMA(NULL, 0, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 0);

    //ボリンジャーバンドと移動平均線SMAとEMAの値から逆張りでトレード
    if (CCI1 > CCIShort && CCI2 > CCIShort && High[0] > BollingerUpper && High[0] > SMA200 && High[0] > EMA200)
    {
        LongSign = false;
        ShortSign = true;
    }
    else if (CCI1 < CCILong && CCI2 < CCILong && Low[0] < BollingerLower && Low[0] < SMA200 && Low[0] < EMA200)
    {
        LongSign = true;
        ShortSign = false;
    }
//ここまで改変可能

    if (MaxSpread < Info_Spread)
    {
        LongSign = false;
        ShortSign = false;
        spread_msg = "Max spread exceeded\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でTDSによる全ティックバックテストを回してみたところ、ゴールドでは勝てないことが分かりました。

TDSによる全ティックバックテスト

同じ設定でも、始値のみでスプレッドを狭くするとプラスの収支になります。

始値のみでスプレッドを狭くするとプラスの収支

計測期間は2013年1月から2024年6月です。

ジョルノ式トレードのような複雑なロジックはEA化が難しく、裁量トレードに向いている手法だと思います。

わたしが唯一開発することが出来ないEAに、秒スキャ分スキャのEAがあります。

秒スキャは検証が難しいということから開発は後回しにしていますが、分スキャは1分足チャートを使用してバックテストをまわすことで検証出来ます。

今回使用したジョルノ式トレードは、CCIというRSIの様な逆張りのテクニカル指標を使用しています。

ひょっとしたら、CCIを使用した分スキャEAが作れるのでは?との期待を込めて、次回は「ジョルノ式トレードをドル円1分足チャートに最適化してみた」という内容で動画を撮っていこうと思います。

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

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


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