English Deutsch
preview
Candlestick Trend Constraintモデルの構築(第9回):マルチ戦略エキスパートアドバイザー(III)

Candlestick Trend Constraintモデルの構築(第9回):マルチ戦略エキスパートアドバイザー(III)

MetaTrader 5 | 11 4月 2025, 11:09
191 0
Clemence Benjamin
Clemence Benjamin

はじめに

アルゴリズム取引において、現在のトレンドの中で最適なエントリーポイントを見極めることは依然として大きな課題です。多くの戦略がタイミングの把握に苦労し、頻繁に誤ったシグナルを出してしまうことで、取引パフォーマンスが低下する原因となっています。特に日足レベルのトレンドでは、小さな値動きが精度に大きな影響を与えるため、この課題はより深刻です。

こうした問題に対して有効な手法の一つが、価格とモメンタム指標の間に生じる乖離(ダイバージェンス)を利用することです。ダイバージェンスは、反転やトレンド継続の可能性を見極めるフィルターとして機能し、エントリーの精度を大きく向上させることができます。このダイバージェンス検出をTrend Constraint EAに統合することで、トレーダーはより信頼性の高いエントリーポイントを特定できるようになります。

このアプローチは、取引の精度と一貫性を高めるだけでなく、MQL5の先進的な機能と組み合わせることで、より効率的な自動売買の実現にもつながります。この記事では、ダイバージェンスの基礎、それをMQL5エキスパートアドバイザー(EA)に統合する手順、新しい取引実行条件によるTrend Constraint EAの機能強化、そして、実践的な有効性を示すバックテストから得られた洞察について説明します。

主な内容

  1. ダイバージェンスの基礎
  2. ダイバージェンス検出を統合する手順
  3. Trend Constraint EAの機能強化:ダイバージェンスを活用するための新たな取引実行条件の導入
  4. バックテストの結果と実践
  5. 結論


ダイバージェンスの基礎

ダイバージェンスは、テクニカル分析における重要な概念であり、価格の動きとインジケーターの方向性を比較することで、反転やトレンド継続の可能性を見極める手がかりをトレーダーに提供します。 

ダイバージェンスの意義

ダイバージェンスとは、資産価格の動きがテクニカル指標の動きと逆行する現象を指します。これは、多くの場合、トレンドの勢いが弱まっていることや、反転の兆しを示唆するシグナルとして捉えられます。特にトレンドの終盤や調整局面において、その有効性が高まります。

ダイバージェンスの種類

  1. 強気ダイバージェンス:資産価格が安値を更新しているにもかかわらず、RSIなどのモメンタム系指標がより高い安値を示しているときに発生します。これは売り圧力の減少や下落モメンタムの鈍化を意味し、価格の反転上昇につながる可能性があります。
  2. 弱気ダイバージェンス:価格が高値を更新している一方で、インジケーターが低い高値を示す場合に発生します。これは上昇の勢いが弱まっていることを示し、価格の反落を予兆するシグナルとなることがあります。

背景調査

ダイバージェンスは、テクニカル分析において重要な概念であり、市場の動向やトレーダーの戦略に影響を与えます。BartとMasse (1981)の論文「Divergence of Opinion and Risk」では、市場における意見の不一致がリスクや価格の変動性を高める可能性があることが強調されており、テクニカル分析におけるダイバージェンスの役割が示されています。

TilehnoueiとShivaraj (2013)による実証研究では、MACDなどのインジケーターが、状況によってはRSIよりも優れたパフォーマンスを発揮することが示唆されています。こうしたダイバージェンスシグナルは、市場のモメンタムに関する有益な洞察を提供することがあります。これらの研究からは、RSIやMACD、プライスアクションなど、他のインジケーターとダイバージェンスを組み合わせることで、取引における有効性を高められることが分かります。これは、さまざまな業界の情報源によっても裏付けられています。

次章では、こうしたダイバージェンスの考え方を、EA開発にどう活用するか、実践的なステップを通じて解説していきます。


ダイバージェンス検出を統合する手順

MQL5 EAにダイバージェンス検出を組み込むには、まずiRSI()などの関数を使用して相対力指数(RSI)の値を計算し、それをプライスアクションと比較します。一定期間にわたる価格の極値は、iHigh()iLow()を使用して特定します。このプロジェクトでは、ダイバージェンスを通常のダイバージェンス(反転)と隠れたダイバージェンスの2つのタイプに分類します。

通常のダイバージェンス

通常のダイバージェンスは、トレンドの反転が起こる可能性を示すシグナルです。具体的には、価格がより低い安値を更新する一方で、インジケーターがより高い安値を示す場合は強気のセットアップとなり、価格がより高い高値をつける一方で、インジケーターがより低い高値を示す場合は弱気のセットアップとなります。

// Regular divergence conditions in code
bool CheckRegularBearishDivergence(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_H1)
{
    double priceHigh1 = iHigh(_Symbol, timeframe, 2);
    double priceHigh2 = iHigh(_Symbol, timeframe, 8);
    double rsiHigh1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 2);
    double rsiHigh2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 8);
    
    if(priceHigh1 > priceHigh2 && rsiHigh1 < rsiHigh2) return true;
    return false;
}

これらの通常のダイバージェンスを説明するために、それぞれ強気のダイバージェンスと弱気のダイバージェンスを表す2つの画像を以下に示します。

Boom 300指数H4_強気のダイバージェンス

Boom 300指数H4:強気のダイバージェンス(より低い安値Bとより高い低RSI値Dとの比較)


Boom 300指数H4_弱気_ダイバージェンス

Boom300指数H4:弱気のダイバージェンス(より高い高値Bとより低い高値RSI値Dとの比較)

隠れたダイバージェンス

一方、隠れたダイバージェンスはトレンドの継続を示唆します。隠れた強気ダイバージェンスは上昇トレンド中に発生し、価格がより高い安値をつける一方で、インジケーターがより低い安値を示すときに現れます。一方、隠れた弱気ダイバージェンスは下降トレンド中に発生し、価格がより低い高値を記録し、インジケーターがより高い高値を示すときに現れます。

//RSI and Price Levels declaration and hidden divergence condition
bool CheckHiddenBullishDivergence(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_H1)
{
    double priceLow1 = iLow(_Symbol, timeframe, 2);
    double priceLow2 = iLow(_Symbol, timeframe, 8);
    double rsiLow1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 2);
    double rsiLow2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 8);
    
    if(priceLow1 > priceLow2 && rsiLow1 < rsiLow2) return true;
    return false;
}

以下は、強気の隠れたダイバージェンスと弱気の隠れたダイバージェンスを示す画像です。これらを前述の説明と比較し、自分のチャートで同様のパターンを識別する練習をしてください。

Boom 300指数H4_Bullish_Hidden_ダイバージェンス

Boom 300指数H4:強気の隠れたダイバージェンス

Boom 300指数H4_Bearish_Hidden_Divergence

Boom 300指数H4:弱気の隠れたダイバージェンス

これらのダイバージェンスタイプをEAに統合するには、インジケーターの計算にiRSI()iMACD()などの関数を使用してインジケーターを計算し、各ティックまたはバーの終値ごとにダイバージェンスを検出する条件をコーディングする必要があります。ダイバージェンスが検出されると、それらのシグナルは日足レベルの市場センチメントに基づいて特定されたTrend Constraintと整合されます。


Trend Constraint EAの機能強化:ダイバージェンスを活用するための新たな取引実行条件の導入

上記のセクションに関して、ダイバージェンスが最終的に検出された場合、注文実行プロセスを確認して通知するための追加のインジケーターが必要になります。この目的に使用できるインジケーターは多数ありますが、本プロジェクトではMACDとRSIを実装候補として検討します。以下は、他のオプションとして使用可能な確認用インジケーターの一覧です。 

  1. ボリンジャーバンド
  2. ストキャスティックス
  3. オンバランスボリューム(OBV)とボリューム加重平均価格(VWAP)
  4. 平均動的指数(ADX)

Moving Average Convergence Divergence(MACD、移動平均収束拡散手法):

使用する理由: MACDは勢いの変化を確認できます。RSIとのダイバージェンスが検出された場合、MACDはトレンドの強さまたは弱まりの二次的な確認を提供することができます。 

仕組み

EAは、ダイバージェンスの検出結果と一致するMACDラインのクロスオーバーまたはヒストグラムの変化を探します。たとえば、MACDラインがシグナルラインを下回ったり、ヒストグラムが減少し始めたりした場合、弱気のダイバージェンスが確認される可能性があります。

MACDインジケーターのハイライト

MetaEditor 5ソフトウェアの「Indicators」フォルダの下にある「Examples」フォルダ内でアクセスできるMACDなどの組み込みインジケーターのプレビューを表示するには、以下の説明画像を確認してください。

MetaEditor 5でMACDソースにアクセスする

MetaEditor 5MACDソースファイルへのアクセス

コードにアクセスする理由は、バッファがどのように設計されているかを簡単に把握し、EAで簡単に適応できるようにすることです。以下は、私たちが関心を持っているインジケーター内のバッファを宣言するコードスニペットです。

//--- indicator buffers
double ExtMacdBuffer[];
double ExtSignalBuffer[];
double ExtFastMaBuffer[];
double ExtSlowMaBuffer[];

int    ExtFastMaHandle;
int    ExtSlowMaHandle;

さて、バッファを念頭に置いて、以下に概説する開発プロセスを段階的に実行してみましょう。

ダイバージェンス戦略の開発 

手順1:宣言と入力パラメータ

 まず、ダイバージェンス戦略の入力パラメータ、MACDバッファを宣言し、CTradeクラスを初期化します。

#include <Trade\Trade.mqh>
CTrade trade;

input bool UseDivergenceStrategy = true;        // Enable/Disable Divergence Strategy
input int DivergenceMACDPeriod = 12;            // MACD Fast EMA period
input int DivergenceSignalPeriod = 9;           // MACD Signal period
input double DivergenceLots = 1.0;              // Lot size for Divergence trades
input double DivergenceStopLoss = 300;          // Stop Loss in points for Divergence
input double DivergenceTakeProfit = 500;        // Take Profit in points for Divergence
input int DivergenceMagicNumber = 87654321;     // Magic number for Divergence Strategy
input int DivergenceLookBack = 8;               // Number of periods to look back for divergence

double ExtMacdBuffer[]; // MACD values
double ExtSignalBuffer[]; // Signal line values
int macd_handle; // MACD indicator handle

手順2:MACDインジケーターを初期化する

OnInit()中にMACDハンドルを初期化し、バッファメモリを割り当てます。

int OnInit()
{
    macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE);
    if (macd_handle == INVALID_HANDLE)
    {
        Print("Failed to initialize MACD. Error: ", GetLastError());
        return INIT_FAILED;
    }
    ArrayResize(ExtMacdBuffer, DivergenceLookBack);
    ArrayResize(ExtSignalBuffer, DivergenceLookBack);
    return INIT_SUCCEEDED;
}

手順3:  ダイバージェンス検出

ダイバージェンスは、資産の価格変動が指標(この場合はMACD)と一致しないときに発生します。この戦略は、強気の通常ダイバージェンス、強気の隠れたダイバージェンス、弱気の通常ダイバージェンス、弱気の隠れたダイバージェンスの4種類のダイバージェンスを検出します。各タイプには特定の条件があり、価格の高値または安値を対応するMACDの高値または安値と比較して、ダイバージェンスの存在を判断します。

bool CheckBullishRegularDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];
    return (priceLow1 < priceLow2 && macdLow1 > macdLow2);
}

bool CheckBearishHiddenDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2];
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];
    return (priceHigh1 < priceHigh2 && macdHigh1 > macdHigh2);
}

手順4:取引ロジック

まず、この戦略では、ダイバージェンス取引が有効になっており、ダイバージェンスベースのポジションが3つ以下しか開かれていないことを保証します。MACDバッファデータを取得し、失敗した場合は再試行し、完全なバーでのみ取引を実行します。さらに、毎日のローソク足のトレンドに合わせて取引を調整し、強気の日にのみ買い、弱気の日にのみ売りをおこなうことを保証します。

void CheckDivergenceTrading()
{
    if (!UseDivergenceStrategy) return;

    int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
    if (openDivergencePositions == 0 || openDivergencePositions < 3)
    {
        if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
            CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
        {
            double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
            double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
            bool isDailyBullish = dailyClose > dailyOpen;
            bool isDailyBearish = dailyClose < dailyOpen;

            if (isDailyBullish && 
                (CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) || 
                CheckBullishHiddenDivergence())
            {
                ExecuteDivergenceOrder(true);
            }

            if (isDailyBearish && 
                (CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) || 
                CheckBearishHiddenDivergence())
            {
                ExecuteDivergenceOrder(false);
            }
        }
    }
}

手順5:注文の実行

ダイバージェンスが検出されると、この戦略はロットサイズ、ストップロス、テイクプロフィットなどの事前定義されたパラメータを使用して取引を実行します。ExecuteDivergenceOrder関数は、取引方向に基づいて適切なレベルを計算し、取引オブジェクトを使用して買い注文または売り注文を配置します。

void ExecuteDivergenceOrder(bool isBuy)
{
    trade.SetExpertMagicNumber(DivergenceMagicNumber);

    double currentPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double stopLossPrice = isBuy ? currentPrice - DivergenceStopLoss * _Point : currentPrice + DivergenceStopLoss * _Point;
    double takeProfitPrice = isBuy ? currentPrice + DivergenceTakeProfit * _Point : currentPrice - DivergenceTakeProfit * _Point;

    if (isBuy)
    {
        if (trade.Buy(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Buy"))
            Print("Divergence Buy order placed.");
    }
    else
    {
        if (trade.Sell(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Sell"))
            Print("Divergence Sell order placed.");
    }
}

手順6:注文管理

過剰取引を防ぐために、この戦略ではCountOrdersByMagicユーティリティ関数を使用して、指定されたマジックナンバーを持つすべてのポジションをカウントします。これにより、ダイバージェンスベースの取引に設定された最大ポジション制限が確実に遵守されます。

int CountOrdersByMagic(int magic)
{
    int count = 0;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            if (PositionGetInteger(POSITION_MAGIC) == magic)
            {
                count++;
            }
        }
    }
    return count;
}

マジックナンバー 

もう一つの重要な側面は、ポジションにアイデンティティを与えることです。この場合、ダイバージェンス戦略によって管理される取引に固有のマジックナンバーを割り当てました。

    // Ensure the magic number is set for the trade
    trade.SetExpertMagicNumber(DivergenceMagicNumber);

手順7:利益保護

Trend Constraint EAには、オープン中のポジションから利益を動的に確保するための利益保護関数を導入しました。この関数は、利益ロックロジックを組み込み、利益を確定させるタイミングを自動的に判断します。LockProfits関数はすべてのアクティブポジションをスキャンし、利益が100ポイントを超えるポジションを対象として抽出します。該当する各ポジションに対しては、profitLockerPointsパラメータ(例:エントリー価格から20ポイント)に基づいて新たなストップロス水準を計算します。

この調整により、ストップロスが現在価格により近づくため、利益を確実に確保することが可能になります。買いポジションの場合は、ストップロスがエントリー価格より上に移動し、売りポジションの場合は、エントリー価格より下に移動します。この関数は、新しいストップロスレベルが現行よりも有利な場合にのみ更新をおこない、最適なリスク管理を実現します。変更が成功した場合には、追跡用メッセージとしてログに記録されます。この機能により、利益を確保しつつ、ポジションがさらなる利益を伸ばす余地も残すことができます。

以下はLockProfits関数です。

//+------------------------------------------------------------------+
//| Profit Locking Logic                                             |
//+------------------------------------------------------------------+
void LockProfits()
{
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentProfit = PositionGetDouble(POSITION_PROFIT);
            double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
            
            // Convert profit to points
            double profitPoints = MathAbs(currentProfit / _Point);

            // Check if profit has exceeded 100 points
            if (profitPoints >= 100)
            {
                double newStopLoss;
                
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    newStopLoss = entryPrice + profitLockerPoints * _Point; // 20 points above entry for buys
                }
                else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                {
                    newStopLoss = entryPrice - profitLockerPoints * _Point; // 20 points below entry for sells
                }
                else
                {
                    continue; // Skip if not a buy or sell position
                }

                // Modify stop loss only if the new stop loss is more protective
                double currentStopLoss = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    if (currentStopLoss < newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for buy position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
                else // POSITION_TYPE_SELL
                {
                    if (currentStopLoss > newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for sell position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
            }
        }
    }
}

手順8:OnTick()に統合する

メインEAループ内でダイバージェンス取引ロジックを呼び出します。

void OnTick()
{
    CheckDivergenceTrading();
}

手順9:シャットダウン

void OnDeinit(const int reason)
{
    IndicatorRelease(macd_handle);
}

メインのTrend Constrant EAに戦略を統合します。

本連載の前回の記事では、ドンチャンチャネルを基にしたEAを開発し、2つの戦略を1つのEAに組み込む方法を紹介しました。今回はその続きとして、3つ目の戦略を新たに追加し、各戦略のオン・オフを切り替えるためにブール型のデータ型を活用します。

// Input parameters for controlling strategies
input bool UseTrendFollowingStrategy = false;   // Enable/Disable Trend Constraint Strategy
input bool UseBreakoutStrategy = false;         // Enable/Disable Breakout Strategy
input bool UseDivergenceStrategy = true;        // Enable/Disable Divergence Strategy

ストラテジーテスターの実行中に混乱しないように、ダイバージェンス戦略をtrueに設定します。

最後に、戦略をメインコードに慎重に統合します。

//+------------------------------------------------------------------+
//|                                      Trend Constraint Expert.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/ja/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/ja/users/billionaire2024/seller"
#property version   "1.02"

#include <Trade\Trade.mqh>
CTrade trade;

// Input parameters for controlling strategies
input bool UseTrendFollowingStrategy = false;   // Enable/Disable Trend Following Strategy
input bool UseBreakoutStrategy = false;         // Enable/Disable Breakout Strategy
input bool UseDivergenceStrategy = true;        // Enable/Disable Divergence Strategy

// Input parameters for Trend Constraint Strategy
input int    RSI_Period = 14;           // RSI period
input double RSI_Overbought = 70.0;     // RSI overbought level
input double RSI_Oversold = 30.0;       // RSI oversold level
input double Lots = 0.1;                // Lot size
input double StopLoss = 100;            // Stop Loss in points
input double TakeProfit = 200;          // Take Profit in points
input double TrailingStop = 50;         // Trailing Stop in points
input int    MagicNumber = 12345678;    // Magic number for the Trend Constraint EA
input int    OrderLifetime = 43200;     // Order lifetime in seconds (12 hours)

// Input parameters for Breakout Strategy
input int InpDonchianPeriod = 20;       // Period for Donchian Channel
input double RiskRewardRatio = 1.5;     // Risk-to-reward ratio
input double LotSize = 0.1;             // Default lot size for trading
input double pipsToStopLoss = 15;       // Stop loss in pips for Breakout
input double pipsToTakeProfit = 30;     // Take profit in pips for Breakout

// Input parameters for Divergence Strategy
input int DivergenceMACDPeriod = 12;    // MACD Fast EMA period
input int DivergenceSignalPeriod = 9;   // MACD Signal period
input double DivergenceLots = 1.0;      // Lot size for Divergence trades
input double DivergenceStopLoss = 300;   // Stop Loss in points for Divergence
input double DivergenceTakeProfit = 500; // Take Profit in points for Divergence
input int DivergenceMagicNumber = 87654321;     // Magic number for Divergence Strategy
input int DivergenceLookBack = 8;       // Number of periods to look back for divergence
input double profitLockerPoints  = 20;  // Number of profit points to lock 

// Indicator handle storage
int rsi_handle;                         
int handle;                             // Handle for Donchian Channel
int macd_handle;


double ExtUpBuffer[];                   // Upper Donchian buffer
double ExtDnBuffer[];                   // Lower Donchian buffer
double ExtMacdBuffer[];                 // MACD buffer
double ExtSignalBuffer[];               // Signal buffer
int globalMagicNumber;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // Initialize RSI handle
    rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
    if (rsi_handle == INVALID_HANDLE)
    {
        Print("Failed to create RSI indicator handle. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Create a handle for the Donchian Channel
    handle = iCustom(_Symbol, PERIOD_CURRENT, "Free Indicators\\Donchian Channel", InpDonchianPeriod);
    if (handle == INVALID_HANDLE)
    {
        Print("Failed to load the Donchian Channel indicator. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Initialize MACD handle for divergence
    globalMagicNumber = DivergenceMagicNumber;
    macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE);
    if (macd_handle == INVALID_HANDLE)
    {
        Print("Failed to create MACD indicator handle for divergence strategy. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Resize arrays for MACD buffers
    ArrayResize(ExtMacdBuffer, DivergenceLookBack);
    ArrayResize(ExtSignalBuffer, DivergenceLookBack);

    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(rsi_handle);
    IndicatorRelease(handle);
    IndicatorRelease(macd_handle);
}

//+------------------------------------------------------------------+
//| Check and execute Trend Following EA trading logic               |
//+------------------------------------------------------------------+
void CheckTrendFollowing()
{
    if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    double rsi_value;
    double rsi_values[];
    if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0)
    {
        Print("Failed to get RSI value. Error: ", GetLastError());
        return;
    }
    rsi_value = rsi_values[0];

    double ma_short = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
    double ma_long = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE);

    bool is_uptrend = ma_short > ma_long;
    bool is_downtrend = ma_short < ma_long;

    if (is_uptrend && rsi_value < RSI_Oversold)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double stopLossPrice = currentPrice - StopLoss * _Point;
        double takeProfitPrice = currentPrice + TakeProfit * _Point;

        // Corrected Buy method call with 6 parameters
        if (trade.Buy(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Buy"))
        {
            Print("Trend Following Buy order placed.");
        }
    }
    else if (is_downtrend && rsi_value > RSI_Overbought)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double stopLossPrice = currentPrice + StopLoss * _Point;
        double takeProfitPrice = currentPrice - TakeProfit * _Point;

        // Corrected Sell method call with 6 parameters
        if (trade.Sell(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Sell"))
        {
            Print("Trend Following Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| Check and execute Breakout EA trading logic                      |
//+------------------------------------------------------------------+
void CheckBreakoutTrading()
{
    if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    ArrayResize(ExtUpBuffer, 2);
    ArrayResize(ExtDnBuffer, 2);

    if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0)
    {
        Print("Error reading Donchian Channel buffer. Error: ", GetLastError());
        return;
    }

    double closePrice = iClose(_Symbol, PERIOD_CURRENT, 0);
    double lastOpen = iOpen(_Symbol, PERIOD_D1, 1);
    double lastClose = iClose(_Symbol, PERIOD_D1, 1);

    bool isBullishDay = lastClose > lastOpen;
    bool isBearishDay = lastClose < lastOpen;

    if (isBullishDay && closePrice > ExtUpBuffer[1])
    {
        double stopLoss = closePrice - pipsToStopLoss * _Point;
        double takeProfit = closePrice + pipsToTakeProfit * _Point;
        if (trade.Buy(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Buy") > 0)
        {
            Print("Breakout Buy order placed.");
        }
    }
    else if (isBearishDay && closePrice < ExtDnBuffer[1])
    {
        double stopLoss = closePrice + pipsToStopLoss * _Point;
        double takeProfit = closePrice - pipsToTakeProfit * _Point;
        if (trade.Sell(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Sell") > 0)
        {
            Print("Breakout Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| DIVERGENCE TRADING STRATEGY                                      |
//+------------------------------------------------------------------+

bool CheckBullishRegularDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 < priceLow2 && macdLow1 > macdLow2);
}

bool CheckBullishHiddenDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 > priceLow2 && macdLow1 < macdLow2);
}

bool CheckBearishRegularDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2];
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 > priceHigh2 && macdHigh1 < macdHigh2);
}

bool CheckBearishHiddenDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2]; 
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 < priceHigh2 && macdHigh1 > macdHigh2);
}

void CheckDivergenceTrading()
{
    if (!UseDivergenceStrategy) return;

    // Check if no position is open or if less than 3 positions are open
    int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
    if (openDivergencePositions == 0 || openDivergencePositions < 3)
    {
        int barsAvailable = Bars(_Symbol, PERIOD_CURRENT);
        if (barsAvailable < DivergenceLookBack * 2)
        {
            Print("Not enough data bars for MACD calculation.");
            return;
        }

        int attempt = 0;
        while(attempt < 6)
        {
            if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
                CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
                break; 
            
            Print("Failed to copy MACD buffer, retrying...");
            Sleep(1000);
            attempt++;
        }
        if(attempt == 6)
        {
            Print("Failed to copy MACD buffers after ", attempt, " attempts.");
            return;
        }

        if(TimeCurrent() == iTime(_Symbol, PERIOD_CURRENT, 0))
        {
            Print("Skipping trade due to incomplete bar data.");
            return;
        }

        double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);
        double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
        double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
        bool isDailyBullish = dailyClose > dailyOpen;
        bool isDailyBearish = dailyClose < dailyOpen;

        // Only proceed with buy orders if D1 is bullish
        if (isDailyBullish)
        {
            if ((CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) ||
                CheckBullishHiddenDivergence())
            {
                ExecuteDivergenceOrder(true);
            }
        }

        // Only proceed with sell orders if D1 is bearish
        if (isDailyBearish)
        {
            if ((CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) ||
                CheckBearishHiddenDivergence())
            {
                ExecuteDivergenceOrder(false);
            }
        }
    }
    else
    {
        Print("Divergence strategy: Maximum number of positions reached.");
    }
}

void ExecuteDivergenceOrder(bool isBuy)
{
    // Ensure the magic number is set for the trade
    trade.SetExpertMagicNumber(DivergenceMagicNumber);
    
    double currentPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double stopLossPrice = isBuy ? currentPrice - DivergenceStopLoss * _Point : currentPrice + DivergenceStopLoss * _Point;
    double takeProfitPrice = isBuy ? currentPrice + DivergenceTakeProfit * _Point : currentPrice - DivergenceTakeProfit * _Point;

    if (isBuy)
    {
        if (trade.Buy(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Buy"))
        {
            Print("Divergence Buy order placed.");
        }
    }
    else
    {
        if (trade.Sell(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Sell"))
        {
            Print("Divergence Sell order placed.");
        }
    }
}

int CountOrdersByMagic(int magic)
{
    int count = 0;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            if (PositionGetInteger(POSITION_MAGIC) == magic)
            {
                count++;
            }
        }
    }
    return count;
}
//+------------------------------------------------------------------+
//| Profit Locking Logic                                             |
//+------------------------------------------------------------------+
void LockProfits()
{
    for (int i = PositionsTotal() - 1; i >= 0; i--)

    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentProfit = PositionGetDouble(POSITION_PROFIT);
            double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
            
            // Convert profit to points
            double profitPoints = MathAbs(currentProfit / _Point);

            // Check if profit has exceeded 100 points
            if (profitPoints >= 100)
            {
                double newStopLoss;
                
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    newStopLoss = entryPrice + profitLockerPoints * _Point; // 20 points above entry for buys
                }
                else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                {
                    newStopLoss = entryPrice - profitLockerPoints * _Point; // 20 points below entry for sells
                }
                else
                {
                    continue; // Skip if not a buy or sell position
                }

                // Modify stop loss only if the new stop loss is more protective
                double currentStopLoss = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    if (currentStopLoss < newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for buy position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
                else // POSITION_TYPE_SELL
                {
                    if (currentStopLoss > newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for sell position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    if (UseTrendFollowingStrategy) CheckTrendFollowing();
    if (UseBreakoutStrategy) CheckBreakoutTrading();
    if (UseDivergenceStrategy) CheckDivergenceTrading();
    LockProfits(); // Call this function to check and lock profits
}


バックテストの結果と実践

テストを実行するには、端末の[エキスパート]の下にあるTrend Constraint EAを見つける必要があります。デモ口座で実行していることを確認してください。ストラテジーテスターウィンドウが開き、さまざまな最適化に合わせて入力設定を調整できます。デフォルト設定の以下の画像を参照してください。

入力設定

Trend Constraint EA:デモ設定 

ストラテジーテスターを実行したところ、取引は正常におこなわれました。このシステムは、セッションあたりのポジションの最大数を3つに制限し、複数の制御されていない注文の実行を効果的に防止しました。以下は、バックテストプロセスの実行中の一部を示す画像です。

テスターのEA

Trend Constraint EA:EURUSD M15のテスト

下の画像は、ポジション管理関数が期待どおりに動作していることを示しています。実装されたロジックに従って、最大3つの注文がアクティブになります。各注文には「Divergence Sell」というコメントが付けられており、戦略に沿った形で識別しやすくなっています。


Trend Constraint EA:1回あたり最大3ポジション


結論

本記事では、さまざまな種類のダイバージェンスを取り上げ、RSIやMACDといったインジケーターを組み合わせたコンフルエンス戦略として、それらをMQL5コードに実装しました。生成された取引シグナルは、日足ベースのTrend Constraintsによってさらに洗練され、より広範な市場の流れと一致するように調整されており、信頼性の高いエントリーポイントを実現しています。現在のTrend Constraint EAは、3つの異なる構成可能な戦略を搭載しており、ユーザーは自身の取引スタイルや市場環境に合わせて、柔軟にカスタマイズできるようになっています。

また、各ポジションに一意のマジックナンバーを付与し、戦略ごとのポジション数制限を可能にすることで、取引管理の精度を大幅に向上させました。加えて、市場がテイクプロフィットに届く前に反転した場合でも利益を動的に保護する「利益ロック機能」を実装しました。この機能により、リスク管理と成長の両立が実現され、EAの堅牢性と適応力が強化されています。以下にEAプログラムのソースファイルを添付していますので、ぜひご自身で試してみてください。さまざまな構成をテストし、フィードバックがあればコメント欄で共有していただければと思います。注意事項:本記事で紹介しているコードは教育目的のサンプルです。すべてのテストは必ずデモ口座でおこなってください。

トレーダーの皆さん、開発をお楽しみください。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16549

添付されたファイル |
プライスアクション分析ツールキットの開発(第4回):Analytics Forecaster EA プライスアクション分析ツールキットの開発(第4回):Analytics Forecaster EA
チャート上に表示された分析済みのメトリックを見るだけにとどまらず、Telegramとの統合によってブロードキャストを拡張するという、より広い視点へと移行しています。この機能強化により、Telegramアプリを通じて、重要な結果がモバイルデバイスに直接配信されるようになります。この記事では、この新たな取り組みを一緒に探っていきましょう。
MQL5取引ツールキット(第4回):履歴管理EX5ライブラリの開発 MQL5取引ツールキット(第4回):履歴管理EX5ライブラリの開発
詳細なステップバイステップのアプローチで拡張履歴管理EX5ライブラリを作成し、MQL5を使用してクローズされたポジション、注文、取引履歴を取得、処理、分類、並べ替え、分析、管理する方法を学びます。
出来高による取引の洞察:トレンドの確認 出来高による取引の洞察:トレンドの確認
強化型トレンド確認手法は、プライスアクション、出来高分析、そして機械学習を組み合わせることで、真の市場動向を見極めることを目的としています。この手法では、取引を検証するために、価格のブレイクアウトと平均比50%以上の出来高急増という2つの条件を満たす必要があります。さらに、追加の確認手段としてLSTMニューラルネットワークを活用します。システムはATR (Average True Range)に基づいたポジションサイズ設定と動的リスク管理を採用しており、誤ったシグナルを排除しつつ、多様な市場環境に柔軟に対応できる設計となっています。
MQL5入門(第10回):MQL5の組み込みインジケーターの操作に関する初心者向けガイド MQL5入門(第10回):MQL5の組み込みインジケーターの操作に関する初心者向けガイド
この記事では、プロジェクトベースのアプローチを使用してRSIベースのエキスパートアドバイザー(EA)を作成する方法に焦点を当て、MQL5の組み込みインジケーターの活用方法を紹介します。RSI値を取得して活用し、流動性スイープに対応し、チャートオブジェクトを使用して取引の視覚化を強化する方法を学びます。さらに、パーセンテージベースのリスク設定、リスク報酬比率の実装、利益確保のためのリスク修正など、効果的なリスク管理についても解説します。