見出し画像

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


ChatGPT担当のナナミです。

前回第7回の記事では、ChatGPTを使い「MovingAverageTrail」というEAを完成させるところまでやりました。

https://note.com/aimjey/n/n3ac4a3f14097

今回第8回では、実装が難しいとされるマルチロジックの導入にチャレンジしてみました。

果たしてうまくいくのでしょうか?

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

トレーリングストップはマルチロジックには必要ありません。
パーフェクトオーダーのシングルロジックをベースに別のロジックを追加してマルチロジックEAにしたいと思います。

皆さんならどういったロジックを追加しますか?

わたしが作成したEAの中でも重量級と呼ばれるEAに「Seven Elements」というのがあります。

Seven Elements

商品ページ
https://www.gogojungle.co.jp/systemtrade/fx/49099

このEAの最大の特徴は7つのロジックを搭載し、それぞれの手法に対して重要度を指定することで、様々な相場で対応できる変幻自在なトレードが可能となる点です。

バージョン2.01では、順張り手法4つと、逆張り手法3つの計7個の手法を使用します。

100個以上のEAを開発してきたからこそ出来る、わたしの自信作になります。

さすがにこの規模のEAをChatGPTに作らせるのは無理があります。

というのは、この手のEAはプログラミングのスキルではなく、ロジックの構築とパラメーターの調整がとても重要になるからです。
ChatGPTのプログラミングのスキルは一流のプログラマー並みに高いです。
ですが、生成するデータはネットなどから拾ってきた情報を元に出力します。
ネットに出回っている手法では勝てないのはお分かりだと思います。

一流のEAクリエイターに必要なスキルは、プログラミングのスキルではなくロジックの構築とパラメーターの調整スキルになります。
EAを最適化するにあたり、過去に開発したEAのロジックの優位性を考えてEAを組み立てていきます。
そう、まるでパズルのピースを組み合わせるみたいに。

AGI 汎用人工知能の誕生により、こうしたEAクリエイターの仕事もいずれAIに置き換わるのでしょうが、現在のGPT-フォーオーにそこまでの事は望めません。

したがって、ロジックはEA開発者が考えないといけないのです。

簡単なマルチロジックのEAに「Hybrid EA Trade」というEAがあります。

Hybrid EA Trade

商品ページ
https://www.gogojungle.co.jp/systemtrade/fx/48334

朝は逆張り、日中と夜はトレンドフォロー型の2つのロジックを採用したハイブリッド型EAです。

順張りモードでは、パーフェクトオーダーのロジックを採用しているので、1個目のロジックはもう出来上がっていますね。
2個目のロジックの逆張りモードでは、テクニカル指標のボリンジャーバンドの1~2シグマとRSIとローソク足から買われ過ぎ売られ過ぎの反転ポイントを見極めて売買します。とあります。
また、逆張りでトレードする時間帯はサーバー時間の23:00~翌5:00までとなることから、これらの情報をChatGPTに伝えれば良さそうですね。

日本時間の早朝に逆張りトレードすることから、朝スキャEAに分類される手法になります。

今回作成するEAはドル円5分足チャートで動作するEAになることから、パーフェクトオーダーの移動平均線の期間も変更します。

送信するプロンプトはこんな感じです。

以下のコードはMT4のEA自動売買プログラムで、3本の移動平均線を用いた順張り型のEAです。
移動平均線の期間は以下の値に変更します。
ShortMovingPeriod = 75;
MiddleMovingPeriod = 150;
LongMovingPeriod = 240;
次に、パーフェクトオーダーを用いた順張りでトレードする時間は、サーバー時間の5時~23時までです。
23時から5時までは、逆張りのロジックで売買するようにプログラムを書き換えてください。
extern int Bands = 70;// ボリンジャーバンドの計算期間で範囲10~180で指定
extern int Deviation = 1;// ボリンジャーバンドの上方バンドと下方バンドの値で範囲1~3で指定
extern int RSI_highs = 70;// RSIの上限値を入力で範囲60~90で指定
extern int RSI_lows = 30;// RSIの下限値を入力で範囲10~40で指定
extern int RSI_Period = 14;// RSIの期間を入力で範囲6~14で指定
上記のパラメータ値で逆張りのロジックを考えてください。
サーバー時間の5時~23時は逆張りロジックとなり、ローソク足が反転したタイミングでエントリーします。
決済のタイミングは順張りロジックと逆張りロジックはドテン売買とします。

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

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

// ロットサイズの入力
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

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, "", 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, "", 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)
{
}

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

この逆張りロジックはわたしが考えた手法なので、最適化の答えをわたしは知っています。
ChatGPTにはこれらのヒントのみを教えて、ロジックを考えてもらいましょう。

果たして結果はどうなるのでしょうか?

完成したMT4のEAプログラムはこちら(無料EAソースコード)

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

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

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

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

// 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

// ボリンジャーバンドとRSIのパラメータ
extern int Bands = 70; // ボリンジャーバンドの計算期間
extern int Deviation = 1; // ボリンジャーバンドの偏差
extern int RSI_highs = 70; // RSIの上限値
extern int RSI_lows = 30; // RSIの下限値
extern int RSI_Period = 14; // RSIの期間

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

//+------------------------------------------------------------------+
//| Calculate current server time                                    |
//| 現在のサーバー時間を取得する関数                                 |
//+------------------------------------------------------------------+
bool IsTrendTime()
{
    int hour = TimeHour(TimeCurrent());
    return (hour >= 5 && hour < 23);
}

//+------------------------------------------------------------------+
//| Calculate current orders                                         |
//| 現在のオープンポジションを計算する関数                           |
//+------------------------------------------------------------------+
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 (IsTrendTime())
    {
        // 買いの条件(移動平均線のパーフェクトオーダー)
        if (shortMA > middleMA && middleMA > longMA && OrderCount < MaxOrder)
        {
            // 買いオーダーを送信
            res = OrderSend(Symbol(), OP_BUY, LotsOptimized(), Ask, 3, Ask - slPoints, Ask + tpPoints, "", 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, "", MAGICMA, 0, Red);
            if (res > 0)
            {
                OrderCount++;
                LastOrderTime = TimeCurrent();
            }
            else
            {
                Print("OrderSend error ", GetLastError()); // オーダー送信エラーを表示
            }
            return;
        }
    }
    else
    {
        // 逆張りのロジック(23時から5時)
        double rsi = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE, 0);
        double upperBand = iBands(NULL, 0, Bands, Deviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
        double lowerBand = iBands(NULL, 0, Bands, Deviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
        double close = iClose(NULL, 0, 0);

        // RSIとボリンジャーバンドによる逆張りの買い条件
        if (rsi < RSI_lows && close < lowerBand && OrderCount < MaxOrder)
        {
            res = OrderSend(Symbol(), OP_BUY, LotsOptimized(), Ask, 3, Ask - slPoints, Ask + tpPoints, "", MAGICMA, 0, Blue);
            if (res > 0)
            {
                OrderCount++;
                LastOrderTime = TimeCurrent();
            }
            else
            {
                Print("OrderSend error ", GetLastError());
            }
            return;
        }

        // RSIとボリンジャーバンドによる逆張りの売り条件
        if (rsi > RSI_highs && close > upperBand && OrderCount < MaxOrder)
        {
            res = OrderSend(Symbol(), OP_SELL, LotsOptimized(), Bid, 3, Bid + slPoints, Bid - tpPoints, "", 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, rsi,close; // rsiと移動平均線の値を保存する変数

    // 各移動平均線の値を取得
    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 (IsTrendTime())
            {
                // パーフェクトオーダーが崩れた場合
                if (shortMA < middleMA && middleMA < longMA && OrderCount < MaxOrder)
                {
                    if (!OrderClose(OrderTicket(), OrderLots(), Bid, 3, White))
                        Print("OrderClose error ", GetLastError()); // オーダークローズエラーを表示
                    OrderCount++;
                }
            }
            // 逆張り時間帯のクローズ条件
            else
            {
                rsi = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE, 0);
                double upperBand = iBands(NULL, 0, Bands, Deviation, 0, PRICE_CLOSE, MODE_UPPER, 0);
                close = iClose(NULL, 0, 0);

                // RSIとボリンジャーバンドによる逆張りのクローズ条件
                if (rsi > RSI_highs || close > upperBand)
                {
                    if (!OrderClose(OrderTicket(), OrderLots(), Bid, 3, White))
                        Print("OrderClose error ", GetLastError());
                    OrderCount++;
                }
            }
        }

        // 売りオーダーの場合のクローズ条件
        if (OrderType() == OP_SELL)
        {
            // 順張り時間帯のクローズ条件
            if (IsTrendTime())
            {
                // パーフェクトオーダーが崩れた場合
                if (shortMA > middleMA && middleMA > longMA && OrderCount < MaxOrder)
                {
                    if (!OrderClose(OrderTicket(), OrderLots(), Ask, 3, White))
                        Print("OrderClose error ", GetLastError()); // オーダークローズエラーを表示
                    OrderCount++;
                }
            }
            // 逆張り時間帯のクローズ条件
            else
            {
                rsi = iRSI(NULL, 0, RSI_Period, PRICE_CLOSE, 0);
                double lowerBand = iBands(NULL, 0, Bands, Deviation, 0, PRICE_CLOSE, MODE_LOWER, 0);
                close = iClose(NULL, 0, 0);

                // RSIとボリンジャーバンドによる逆張りのクローズ条件
                if (rsi < RSI_lows || close < lowerBand)
                {
                    if (!OrderClose(OrderTicket(), OrderLots(), Ask, 3, White))
                        Print("OrderClose error ", GetLastError());
                    OrderCount++;
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| 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は、ドル円5分足チャートでの設定になります。

このEAには、証拠金不足等で発生するエラー131のバグがあります。
リアルマネーでの運用には細心の注意を払ってください。

ボリンジャーバンドのバンド幅は1σか2σで指定してください。
Deviation = 1~2(1σの方がトレード回数が増える)

ポンド円5分足チャートで動かす場合、移動平均の期間を短期移動平均線の期間を30に、中期移動平均線の期間を225に、長期移動平均の期間を300に変えてください。

設定と計測期間を「Hybrid EA Trade」と同じにして、TDSでバックテストをまわしてみます。

ドル円5分足チャートのバックテスト結果

次回行う作業はナンピンEA制作です。

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

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