English Deutsch
preview
MQL5でカスタムインジケーターを作成する(第4回):デュアルオシレーター搭載Smart WaveTrend Crossover

MQL5でカスタムインジケーターを作成する(第4回):デュアルオシレーター搭載Smart WaveTrend Crossover

MetaTrader 5トレーディング |
20 0
Allan Munene Mutiiria
Allan Munene Mutiiria

はじめに

前回の記事(第3回)では、MetaQuotes Language 5 (MQL5)を用いて、セクター表示やラウンドスタイルに対応したマルチゲージインジケーターを開発しました。このインジケーターでは、複数のデータポイントを動的に可視化し、カスタマイズ可能なゲージ表示や色分けされたセクターによって直感的な分析を可能にしました。本記事(第4回)では、Smart WaveTrend Crossoverインジケーターを開発します。本インジケーターでは、2つのオシレーターを用い、1つでシグナル生成、もう1つでトレンドフィルタリングをおこないます。これにより、クロスオーバーに基づく売買シグナルを生成し、必要に応じてトレンド確認を組み合わせることができます。また、トレンド方向に応じたローソク足の色分け、クロス時の矢印シグナルの描画、期間や表示スタイルのカスタマイズにも対応しています。本記事では以下のトピックを扱います。

  1. Smart WaveTrend Crossoverフレームワークの理解
  2. MQL5での実装
  3. バックテスト
  4. 結論

この記事を最後まで読むことで、WaveTrendクロスオーバーに対応した実用的なMQL5インジケーターを作成できるようになります。それでは始めていきましょう。


Smart WaveTrend Crossoverフレームワークの理解

Smart WaveTrend Crossoverフレームワークは、WaveTrendオシレーターに基づいています。これはモメンタム系の指標であり、平滑化された価格平均を用いて買われ過ぎや売られ過ぎの状態を測定します。これにより、市場のモメンタムにおける反転や継続の可能性を把握することができます。具体的には、高値、安値、終値からソース価格を算出し、その後指数移動平均(EMA)を適用することで、2本のラインを生成します。1本は変動の速いライン、もう1本は平滑化された遅いラインです。これらのラインのクロスが、売買シグナルとして機能します。さらに、このフレームワークでは2種類のWaveTrend設定を併用します。短い期間を用いたオシレーターは高感度なシグナル生成を担当し、長い期間のオシレーターは全体的なトレンドの把握に使用します。このように、素早いエントリー判断と市場全体の文脈を組み合わせることで、レンジ相場などのノイズによる誤シグナルを効果的に抑制できます。

上昇トレンドの場合、シグナル用オシレーターにおいて、速いラインが遅いラインを上抜けるポイントに注目します。これは上昇モメンタムの強まりを示しており、特にトレンド用オシレーターでも上昇トレンドが確認できる場合に有効です。一方、下降トレンドでは、速いラインが遅いラインを下抜けることで下降モメンタムが示唆されます。この場合も、トレンド用オシレーターで下降トレンドが確認できると、逆張りを避けるうえで有効です。このアプローチにより、トレンドに逆らうことなくモメンタムの変化を捉え、効率的に取引機会を活用できます。また、ローソク足の色分けによってトレンド方向を視覚的に示し、矢印によってシグナル発生ポイントを明確に表示します。

このインジケーターでは、シグナル用とトレンド用それぞれに対して、チャネル長、平均期間、移動平均期間を個別に設定可能とし、それぞれのWaveTrend値を独立して計算します。シグナル側ではクロスオーバーを検出し、オプションとしてトレンドフィルターを適用することで、トレンド方向と一致するシグナルのみを採用できるようにします。さらに、トレンド状態に応じたローソク足の色分けや、売買シグナルを示すオフセット付き矢印の描画といった視覚要素も組み込みます。これにより、モメンタムトレードにおいて明確かつ実用的な判断材料を提供する包括的なシステムを構築します。以下に想定されるビジュアル表示の例を示します。

インジケーターフレームワーク


MQL5での実装

MQL5でインジケーターを作成するには、まずMetaEditorを開き、ナビゲータに移動して、インジケーターフォルダを見つけ、[新規]タブをクリックして、表示される手順に従ってファイルを作成します。作成後、コーディング環境においてインジケーターのプロパティと設定を定義します。たとえばバッファの数、プロット数、さらに各ラインの個別プロパティとして色、幅、ラベルです。

//+------------------------------------------------------------------+
//|                           1. Smart WaveTrend Crossover PART1.mq5 |
//|                           Copyright 2026, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Allan Munene Mutiiria."
#property link "https://t.me/Forex_Algo_Trader"
#property version "1.00"

#property indicator_chart_window
#property indicator_buffers 23
#property indicator_plots 3
#property indicator_label1 "Colored Candles"
#property indicator_type1 DRAW_COLOR_CANDLES
#property indicator_color1 clrTeal, clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
#property indicator_label2 "Buy Signals"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrForestGreen
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
#property indicator_label3 "Sell Signals"
#property indicator_type3 DRAW_ARROW
#property indicator_color3 clrOrangeRed
#property indicator_style3 STYLE_SOLID
#property indicator_width3 1

まず、インジケーターをメインチャートウィンドウに直接表示するように「#property indicator_chart_window」を設定します。これにより、価格データの上に重ねて表示され、別ウィンドウは開かれません。次に、「#property indicator_buffers 23」を指定して、計算および表示に必要なすべてのデータ配列を保持するために合計23個のバッファを確保します。また、「#property indicator_plots 3」を指定し、チャート上に表示する3つのプロットを定義します。

最初のプロットでは、「Colored Candles」というラベルを付け、タイプをDRAW_COLOR_CANDLESに設定して価格バーを動的に色分け表示します。上昇にはclrTeal、下降にはclrRedを使用し、スタイルはソリッド、太さは1に設定します。2つ目のプロットは「Buy Signals」というラベルで、タイプはDRAW_ARROWを使用します。色はclrForestGreen、スタイルはソリッド、太さは1とし、エントリー候補となる買いシグナルを視覚的に表示します。同様に3つ目のプロットは「Sell Signals」とし、DRAW_ARROWタイプを使用します。色はclrOrangeRed、スタイルはソリッド、太さは1に設定し、売りシグナルを示すために使用します。最後に、インジケーターを制御するための入力パラメータを定義していきます。

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "Colors"
input color col_up = clrTeal;                // Bull Color
input color col_dn = clrRed;                 // Bear Color

input group "WaveTrend Settings for Signals"
input int wt_channel_len = 5;                // Signal Channel Length
input int wt_average_len = 10;               // Signal Average Length
input int wt_ma_len = 4;                     // Signal MA Length

input group "WaveTrend Settings for Trend"
input int wt_trend_channel_len = 10;         // Trend Channel Length
input int wt_trend_average_len = 100;        // Trend Average Length
input int wt_trend_ma_len = 10;              // Trend MA Length

input group "Signal Settings"
input bool use_trend_filter = true;          // Use Trend Filter?
input color signal_buy_col = clrForestGreen; // Buy Signal Color
input color signal_sell_col = clrOrangeRed;  // Sell Signal Color
input int base_offset = 10;                  // Base Signal Offset from Candle

input group "Visual Settings"
input bool color_candles = true;             // Color Candles by Trend?

ここでは、ユーザー入力を論理的に整理するために「input group」を使用し、インジケーター設定ダイアログ内での可読性と直感性を高めています。まず「Colors」グループでは、上昇トレンド用カラーとしてティール(デフォルト)を選択できるようにし、下降トレンド用カラーとしてレッド(デフォルト)を設定できるようにしています。これらはローソク足の色分け表示に適用されます。次に「WaveTrend Settings for Signals」グループでは、シグナル生成用オシレーターのパラメータとして、チャネル長を5、平均長を10、移動平均長を4とする整数入力を用意しています。これにより、クロスオーバーシグナルの感度をユーザーが調整できるようにしています。

続く「WaveTrend Settings for Trend」グループでは、同様のパラメータをより長期設定として定義し、チャネル長を10、平均長を100、移動平均長を10としています。こちらは市場の大局的なトレンド検出に適した設定となっています。「Signal Settings」グループでは、トレンドフィルターを使用するかどうかを切り替えるブール値入力をデフォルト有効で用意しています。また、買いシグナルの色をフォレストグリーン(デフォルト)、売りシグナルの色をオレンジレッド(デフォルト)として選択可能にしています。さらに、シグナル矢印の表示位置を調整するための基準オフセットを10ポイントとする整数入力も追加し、チャート上での表示位置を制御できるようにしています。最後に「Visual Settings」グループでは、トレンドに基づいてローソク足を色分けする機能のオンとオフを切り替えるブール値入力を用意し、デフォルトでは有効としています。これにより、動的なローソク足表示の適用有無をユーザーが制御できるようにしています。コンパイル後、以下の入力セクションが生成されます。

インジケーター入力

ここまでで入力定義が完了したため、次にインジケーターバッファ用のグローバル変数およびグローバルに使用するヘルパー関数を定義していきます。

//+------------------------------------------------------------------+
//| Buffers                                                          |
//+------------------------------------------------------------------+
double esa_signal[];          //--- Signal ESA buffer
double d_signal[];            //--- Signal D buffer
double ci_signal[];           //--- Signal CI buffer
double wt1_signal[];          //--- Signal WT1 buffer
double wt2_signal[];          //--- Signal WT2 buffer
double signal_hist[];         //--- Signal histogram buffer
double signal_bull_cross[];   //--- Signal bull cross buffer
double signal_bear_cross[];   //--- Signal bear cross buffer

double esa_trend[];           //--- Trend ESA buffer
double d_trend[];             //--- Trend D buffer
double ci_trend[];            //--- Trend CI buffer
double wt1_trend[];           //--- Trend WT1 buffer
double wt2_trend[];           //--- Trend WT2 buffer
double trend_hist[];          //--- Trend histogram buffer
double trend_is_bull[];       //--- Trend bull indicator buffer
double trend_is_bear[];       //--- Trend bear indicator buffer

double openBuf[];             //--- Open price buffer
double highBuf[];             //--- High price buffer
double lowBuf[];              //--- Low price buffer
double closeBuf[];            //--- Close price buffer
double candleColorBuf[];      //--- Candle color buffer
double buyArrowBuf[];         //--- Buy arrow buffer
double sellArrowBuf[];        //--- Sell arrow buffer

//+------------------------------------------------------------------+
//| Calculate EMA manually                                           |
//+------------------------------------------------------------------+
double CalcEMA(double prev, double val, int period) {
   if (period < 1) return val;              //--- Handle invalid period
   double alpha = 2.0 / (period + 1);       //--- Compute alpha factor
   return alpha * val + (1 - alpha) * prev; //--- Return EMA value
}

ここでは、インジケーター計算中に中間データおよび最終データを保持するための一連のdouble配列バッファを宣言します。これには、シグナル用WaveTrendコンポーネントのバッファが含まれます。具体的には、指数平滑平均(EMA)、差分値、チャネルインデックス、そしてクロスオーバー検出に使用する2本のメインライン用バッファを用意します。さらに、強気および弱気シグナルを示すヒストグラムおよびクロスフラグ用のバッファも定義します。同様に、トレンド判定用WaveTrendに対しても並列のバッファを構成し、専用の平滑化データ、差分、インデックス、ライン、ヒストグラム、およびトレンド状態(上昇/下降)を識別するブールフラグを保持します。加えて、ローソク足を再描画して色分け表示するための価格データ用バッファとして、始値、高値、安値、終値、、およびカラーインデックス用バッファを割り当てます。さらに、買い・売りシグナル発生位置に矢印を描画するための専用バッファも別途用意します。

WaveTrend計算を補助するために、CalcEMA関数を定義します。この関数では、まず期間が不正な値でないかをチェックし、無効な場合はそのまま値を返します。次に、平滑化係数alphaを「2.0 / (期間 + 1)」として算出し、それを用いて現在値と前回EMA値をブレンドすることで指数平滑移動平均を計算します。これにより、滑らかな平均値を得ることができます。この関数はティックごとの計算処理で使用する予定ですが、現時点ではまずインジケーターの初期化処理を以下の方法で実装していきます。

//+------------------------------------------------------------------+
//| Initialize indicator                                             |
//+------------------------------------------------------------------+
int OnInit() {
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);   //--- Set indicator digits
   IndicatorSetString(INDICATOR_SHORTNAME, "Smart WaveTrend Crossover PART1"); //--- Set short name

   SetIndexBuffer(0, openBuf, INDICATOR_DATA);       //--- Bind open buffer
   SetIndexBuffer(1, highBuf, INDICATOR_DATA);       //--- Bind high buffer
   SetIndexBuffer(2, lowBuf, INDICATOR_DATA);        //--- Bind low buffer
   SetIndexBuffer(3, closeBuf, INDICATOR_DATA);      //--- Bind close buffer
   SetIndexBuffer(4, candleColorBuf, INDICATOR_COLOR_INDEX); //--- Bind color buffer
   PlotIndexSetInteger(0, PLOT_SHOW_DATA, color_candles); //--- Set candle visibility

   SetIndexBuffer(5, buyArrowBuf, INDICATOR_DATA);   //--- Bind buy arrow buffer
   SetIndexBuffer(6, sellArrowBuf, INDICATOR_DATA);  //--- Bind sell arrow buffer
   PlotIndexSetInteger(1, PLOT_ARROW, 233);          //--- Set buy arrow symbol
   PlotIndexSetInteger(1, PLOT_SHOW_DATA, true);     //--- Enable buy arrow display
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 0);       //--- Set buy draw begin
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, 0, signal_buy_col); //--- Set buy color

   PlotIndexSetInteger(2, PLOT_ARROW, 234);          //--- Set sell arrow symbol
   PlotIndexSetInteger(2, PLOT_SHOW_DATA, true);     //--- Enable sell arrow display
   PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, 0);       //--- Set sell draw begin
   PlotIndexSetInteger(2, PLOT_LINE_COLOR, 0, signal_sell_col); //--- Set sell color

   SetIndexBuffer(7, esa_signal, INDICATOR_CALCULATIONS);  //--- Bind signal ESA
   SetIndexBuffer(8, d_signal, INDICATOR_CALCULATIONS);    //--- Bind signal D
   SetIndexBuffer(9, ci_signal, INDICATOR_CALCULATIONS);   //--- Bind signal CI
   SetIndexBuffer(10, wt1_signal, INDICATOR_CALCULATIONS); //--- Bind signal WT1
   SetIndexBuffer(11, wt2_signal, INDICATOR_CALCULATIONS); //--- Bind signal WT2
   SetIndexBuffer(12, signal_hist, INDICATOR_CALCULATIONS); //--- Bind signal hist
   SetIndexBuffer(13, signal_bull_cross, INDICATOR_CALCULATIONS); //--- Bind bull cross
   SetIndexBuffer(14, signal_bear_cross, INDICATOR_CALCULATIONS); //--- Bind bear cross

   SetIndexBuffer(15, esa_trend, INDICATOR_CALCULATIONS); //--- Bind trend ESA
   SetIndexBuffer(16, d_trend, INDICATOR_CALCULATIONS);   //--- Bind trend D
   SetIndexBuffer(17, ci_trend, INDICATOR_CALCULATIONS);  //--- Bind trend CI
   SetIndexBuffer(18, wt1_trend, INDICATOR_CALCULATIONS); //--- Bind trend WT1
   SetIndexBuffer(19, wt2_trend, INDICATOR_CALCULATIONS); //--- Bind trend WT2
   SetIndexBuffer(20, trend_hist, INDICATOR_CALCULATIONS); //--- Bind trend hist
   SetIndexBuffer(21, trend_is_bull, INDICATOR_CALCULATIONS); //--- Bind trend bull
   SetIndexBuffer(22, trend_is_bear, INDICATOR_CALCULATIONS); //--- Bind trend bear

   return(INIT_SUCCEEDED);                                 //--- Return success
}

OnInitイベントハンドラ内では、まずインジケーターの基本プロパティを設定します。IndicatorSetIntegerを使用して表示桁数を銘柄の精度に合わせ、IndicatorSetStringでインジケーター名を設定し、プラットフォーム上で識別しやすい短い名前を付けます。次に、価格関連バッファ(始値、高値、安値、終値)を最初のプロットインデックスに対してデータバッファとしてバインドします。また、色付きローソク足の描画をサポートするためにカラーインデックスバッファも関連付け、ユーザー設定に応じてPlotIndexSetIntegerを用いてプロットの表示/非表示を制御します。矢印プロットについては、買いシグナルおよび売りシグナル用のバッファをそれぞれ対応するプロットインデックスにリンクします。さらに、Wingdingsフォントのコード(例:買いシグナルに233、売りシグナルに234など)を指定して矢印シンボルを定義し、チャート開始時から表示されるよう設定します。また、PlotIndexSetIntegerを使用してカスタムカラーも適用します。使用するコードはMQL5 Wingdingsコードから任意に選択可能です。

MQL5 WINGDINGSコード

その後、シグナルおよびトレンド用WaveTrendを構成するすべての計算バッファ(平滑化平均、差分、インデックス、ライン、ヒストグラム、クロスオーバー、トレンド判定フラグなど)を、非表示の計算専用バッファとして高いインデックス領域に割り当てます。最後に、初期化が正常に完了したことを示すためにIINIT_SUCCEEDED を返します。これで初期化処理は完了です。以降は、ティックごとに必要な計算処理をおこなうことになります。まず最初に、インジケーターを初めてチャートに適用した際にはすべてのバー分のバッファを初期化し、その後に継続的なティック更新ごとの計算処理へ移行する構成とします。

//+------------------------------------------------------------------+
//| Calculate indicator values                                       |
//+------------------------------------------------------------------+
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[]) {
   int start = prev_calculated - 1;               //--- Set start index
   if (start < 0) start = 0;                      //--- Adjust invalid start

   if (prev_calculated == 0) {                    //--- Handle initial calculation
      ArrayInitialize(esa_signal, EMPTY_VALUE);   //--- Init signal ESA
      ArrayInitialize(d_signal, EMPTY_VALUE);     //--- Init signal D
      ArrayInitialize(ci_signal, EMPTY_VALUE);    //--- Init signal CI
      ArrayInitialize(wt1_signal, EMPTY_VALUE);   //--- Init signal WT1
      ArrayInitialize(wt2_signal, EMPTY_VALUE);   //--- Init signal WT2
      ArrayInitialize(signal_hist, EMPTY_VALUE);  //--- Init signal hist
      ArrayInitialize(signal_bull_cross, 0);      //--- Init bull cross
      ArrayInitialize(signal_bear_cross, 0);      //--- Init bear cross

      ArrayInitialize(esa_trend, EMPTY_VALUE);    //--- Init trend ESA
      ArrayInitialize(d_trend, EMPTY_VALUE);      //--- Init trend D
      ArrayInitialize(ci_trend, EMPTY_VALUE);     //--- Init trend CI
      ArrayInitialize(wt1_trend, EMPTY_VALUE);    //--- Init trend WT1
      ArrayInitialize(wt2_trend, EMPTY_VALUE);    //--- Init trend WT2
      ArrayInitialize(trend_hist, EMPTY_VALUE);   //--- Init trend hist
      ArrayInitialize(trend_is_bull, 0);          //--- Init trend bull
      ArrayInitialize(trend_is_bear, 0);          //--- Init trend bear

      ArrayInitialize(buyArrowBuf, EMPTY_VALUE);  //--- Init buy arrows
      ArrayInitialize(sellArrowBuf, EMPTY_VALUE); //--- Init sell arrows
   }

   return(rates_total);                           //--- Return total rates
}

OnCalculateイベントハンドラでは、引数としてバーの総数、前回計算済みバー数(prev_calculated)、および時間、価格、出来高、スプレッドの各配列を受け取り、新規データのみを効率的に処理します。まず、計算開始インデックスを決定します。これは prev_calculatedの値から1を引いた位置を基準とし、そこから再計算をおこなう形にします。ただし、この値が0未満になる場合は0に補正し、配列の不正アクセスを防止します。

インジケーターが初回読み込まれた場合、または全体再計算が発生した場合(prev_calculatedが0の場合)には、すべてのバッファを初期化します。このときArrayInitializeを使用し、シグナルおよびトレンド計算用バッファについてはEMA、差分、インデックス、ライン、ヒストグラムなどをEMPTY_VALUEに設定し、クロスオーバーおよびトレンド判定フラグについては0に初期化します。また、買いと売りの矢印バッファもEMPTY_VALUEに設定することで、描画状態をクリーンな初期状態へリセットします。最後に、計算サイクルの終了を示すために、レートの総数を返します。これで、バーを使ってループ処理をおこない、計算を実行できます。それを達成するために用いた手法は以下のとおりです。

for (int i = start; i < rates_total; i++) {   //--- Loop through bars
   double src_wt = (high[i] + low[i] + close[i]) / 3.0; //--- Compute source

   if (i == 0) {                               //--- Handle first bar signal
      esa_signal[i] = src_wt;                  //--- Set initial ESA
      d_signal[i] = MathAbs(src_wt - esa_signal[i]); //--- Set initial D
      double denom_signal = 0.015 * d_signal[i]; //--- Compute denominator
      ci_signal[i] = (denom_signal != 0.0) ? (src_wt - esa_signal[i]) / denom_signal : 0.0; //--- Set CI
      wt1_signal[i] = ci_signal[i];            //--- Set initial WT1
      wt2_signal[i] = wt1_signal[i];           //--- Set initial WT2
   } else {                                    //--- Handle subsequent bars
      esa_signal[i] = CalcEMA(esa_signal[i-1], src_wt, wt_channel_len); //--- Update ESA
      d_signal[i] = CalcEMA(d_signal[i-1], MathAbs(src_wt - esa_signal[i]), wt_channel_len); //--- Update D
      double denom_signal = 0.015 * d_signal[i]; //--- Compute denominator
      ci_signal[i] = (denom_signal != 0.0) ? (src_wt - esa_signal[i]) / denom_signal : 0.0; //--- Update CI
      wt1_signal[i] = CalcEMA(wt1_signal[i-1], ci_signal[i], wt_average_len); //--- Update WT1
      wt2_signal[i] = 0;                       //--- Reset WT2
      int cnt_ma = 0;                          //--- Init MA count
      for (int k = 0; k < wt_ma_len; k++) {    //--- Loop for MA
         if (i - k < 0) break;                 //--- Skip invalid index
         wt2_signal[i] += wt1_signal[i - k];   //--- Accumulate WT1
         cnt_ma++;                             //--- Increment count
      }
      if (cnt_ma > 0) wt2_signal[i] /= cnt_ma; //--- Average WT2
   }

   signal_hist[i] = wt1_signal[i] - wt2_signal[i]; //--- Compute signal hist
   signal_bull_cross[i] = (wt1_signal[i] > wt2_signal[i] && (i == 0 || wt1_signal[i-1] <= wt2_signal[i-1])) ? 1 : 0; //--- Detect bull cross
   signal_bear_cross[i] = (wt1_signal[i] < wt2_signal[i] && (i == 0 || wt1_signal[i-1] >= wt2_signal[i-1])) ? 1 : 0; //--- Detect bear cross

   double src_wt_trend = src_wt;              //--- Set trend source
   if (i == 0) {                              //--- Handle first bar trend
      esa_trend[i] = src_wt_trend;            //--- Set initial ESA
      d_trend[i] = MathAbs(src_wt_trend - esa_trend[i]); //--- Set initial D
      double denom_trend = 0.015 * d_trend[i]; //--- Compute denominator
      ci_trend[i] = (denom_trend != 0.0) ? (src_wt_trend - esa_trend[i]) / denom_trend : 0.0; //--- Set CI
      wt1_trend[i] = ci_trend[i];             //--- Set initial WT1
      wt2_trend[i] = wt1_trend[i];            //--- Set initial WT2
   } else {                                   //--- Handle subsequent bars
      esa_trend[i] = CalcEMA(esa_trend[i-1], src_wt_trend, wt_trend_channel_len); //--- Update ESA
      d_trend[i] = CalcEMA(d_trend[i-1], MathAbs(src_wt_trend - esa_trend[i]), wt_trend_channel_len); //--- Update D
      double denom_trend = 0.015 * d_trend[i]; //--- Compute denominator
      ci_trend[i] = (denom_trend != 0.0) ? (src_wt_trend - esa_trend[i]) / denom_trend : 0.0; //--- Update CI
      wt1_trend[i] = CalcEMA(wt1_trend[i-1], ci_trend[i], wt_trend_average_len); //--- Update WT1
      wt2_trend[i] = 0;                       //--- Reset WT2
      int cnt_ma_trend = 0;                   //--- Init MA count
      for (int k = 0; k < wt_trend_ma_len; k++) { //--- Loop for MA
         if (i - k < 0) break;                //--- Skip invalid index
         wt2_trend[i] += wt1_trend[i - k];    //--- Accumulate WT1
         cnt_ma_trend++;                      //--- Increment count
      }
      if (cnt_ma_trend > 0) wt2_trend[i] /= cnt_ma_trend; //--- Average WT2
   }

   trend_hist[i] = wt1_trend[i] - wt2_trend[i]; //--- Compute trend hist
   trend_is_bull[i] = (wt1_trend[i] > wt2_trend[i]) ? 1 : 0; //--- Set bull trend
   trend_is_bear[i] = (wt1_trend[i] < wt2_trend[i]) ? 1 : 0; //--- Set bear trend

   if (color_candles) {                       //--- Check candle coloring
      openBuf[i] = open[i];                   //--- Set open
      highBuf[i] = high[i];                   //--- Set high
      lowBuf[i] = low[i];                     //--- Set low
      closeBuf[i] = close[i];                 //--- Set close
      candleColorBuf[i] = trend_is_bull[i] == 1 ? 0 : 1; //--- Set color index
   }

   buyArrowBuf[i] = EMPTY_VALUE;              //--- Reset buy arrow
   sellArrowBuf[i] = EMPTY_VALUE;             //--- Reset sell arrow
   if (signal_bull_cross[i] == 1 && (!use_trend_filter || trend_is_bull[i] == 1)) { //--- Check buy condition
      buyArrowBuf[i] = low[i] - _Point * base_offset; //--- Place buy arrow
   }
   if (signal_bear_cross[i] == 1 && (!use_trend_filter || trend_is_bear[i] == 1)) { //--- Check sell condition
      sellArrowBuf[i] = high[i] + _Point * base_offset; //--- Place sell arrow
   }
}

ここでは、開始インデックスから利用可能な総バー数までループを回し、各バーのソース価格をそのバーの高値、安値、終値の平均として計算し、両方のWaveTrendオシレーターの入力として使用します。

シグナル用WaveTrendでは、まず最初のバーにおいて指数平滑平均をソース値から直接初期化し、MathAbs関数を用いて偏差バッファ用の絶対差を計算します。その後、偏差に0.015を掛けて分母を算出し、ソースと平滑平均の差を分母で割ることでチャネルインデックスを求めますが、ゼロ除算を避けるため分母が0でない場合のみ計算し、そうでなければ0を設定します。そして両方のメインラインにチャネルインデックス値をそのまま代入します。2本目以降のバーでは、CalcEMAを用いて前回値、ソース、チャネル長から指数平滑平均を更新し、同様にMathAbsによる差分のEMAを計算して偏差を更新します。その後分母を再計算し、チャネルインデックスを同様のロジックで更新します。第1メインラインは平均長を用いたCalcEMAで平滑化し、第2メインラインは移動平均長に基づき、ネストしたループで直近の第1ライン値を累積し、インデックスの有効性を確認しながら平均化して算出します。

その後、シグナルヒストグラムは2本のメインラインの差として算出し、第1ラインが第2ラインを上抜けし、かつ前バーでその状態でなかった場合(または初回バー)に強気クロスオーバーフラグを立てます。同様に、第1ラインが第2ラインを下抜けし、かつ前バーでその状態でなかった場合に弱気クロスオーバーフラグを設定します。トレンド用WaveTrendも同様の流れで処理します。ソースは同じものを使用し、初回バーでは指数平滑平均、MathAbsによる偏差、分母、チャネルインデックス、2本のメインラインを同様に初期化しますが、以降のバーではトレンド専用のチャネル長、平均長を用いてCalcEMAで更新し、第2メインラインはトレンド用移動平均長に基づきネストループで平均化します。トレンドヒストグラムは両ラインの差として計算し、第1ラインが第2ラインより上であればブルトレンドフラグを1、下であればベアトレンドフラグを1に設定します。

ローソク足の色付けが有効な場合は、各バーの始値、高値、安値、終値を対応するバッファにコピーし、上昇トレンドの場合はカラーインデックスを0、下降トレンドの場合は1に設定します。最後に、買いと売りの矢印バッファを一度EMPTY_VALUEにリセットし、シグナルクロスが発生し、かつトレンドフィルターが無効またはトレンドがブル条件を満たす場合には、安値の下にbase offset ×_Point分だけオフセットした位置に買い矢印を配置します。同様に、ベアクロスが発生し、条件を満たす場合には高値の上に売り矢印を配置します。コンパイルすると、次の結果が得られます。

最終テストGIF

可視化から、インジケーターの計算、色付きローソク足の描画、各バッファの更新が正常におこなわれており、目的が達成されていることが確認できます。残っている作業は、このプログラムのバックテストをおこなうことです。バックテストについては次のセクションで扱います。


バックテスト

テストを実施しました。以下はコンパイル後の可視化を単一のGraphics Interchange Format (GIF)ビットマップ画像形式で示したものです。

バックテストGIF


結論

本記事では、MQL5におけるSmart WaveTrend Crossoverを実装し、2つのWaveTrendオシレーター(1つはクロスオーバーシグナル用、もう1つはトレンドフィルタ用)を組み合わせる構成を採用しました。チャネル長、平均長、移動平均長といった各種パラメータはユーザーが自由に調整可能であり、柔軟なチューニングができる設計になっています。また、トレンドに基づいたローソク足の色付け機能を備え、クロスオーバー発生時には買いや売りの矢印シグナルをチャート上に表示します。さらに、トレンド確認のオン/オフ設定や、色・オフセットといった視覚的調整オプションも組み込むことで、視認性と実用性の両立を図っています。この構成により、より大きなトレンド方向と整合したモメンタム変化を視覚的に捉えることが可能になります。

次のパートでは、このインジケーターをさらに拡張し、シグナルボックス、トレンド可視化のためのフォグオーバーレイ、カスタマイズ可能な買い/売りラベル、さらにリスク管理を強化するためのテイクプロフィットおよびストップロス表示といった高度なビジュアル要素を追加していきます。完成形としての表現力を高める改良をおこなう予定です。どうぞご期待ください。

機能比較

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

MQL5入門(第34回):MQL5のAPIとWebRequest関数の習得(VIII) MQL5入門(第34回):MQL5のAPIとWebRequest関数の習得(VIII)
MetaTrader 5でインタラクティブなコントロールパネルを作成する方法を学びます。入力フィールド、アクションボタン、テキストを表示するためのラベルを追加する基本について説明します。プロジェクトベースのアプローチを用いて、ユーザーがメッセージを入力し、最終的にAPIからのサーバー応答を表示するパネルを設定する方法を学びます。
MQL5入門(第33回):MQL5のAPIとWebRequest関数の習得(VII) MQL5入門(第33回):MQL5のAPIとWebRequest関数の習得(VII)
本記事では、MQL5を使用してGoogle Generative AI APIをMetaTrader 5に統合する方法を解説します。APIリクエストの構築、サーバー応答の処理、AI生成コンテンツの抽出、レート制限の管理、そして結果をテキストファイルに保存して簡単に参照できるようにする方法を学びます。
初心者からエキスパートへ:市場の不規則性への対処 初心者からエキスパートへ:市場の不規則性への対処
市場のルールは常に変化しており、かつて有効だった原則も、時間の経過とともにその効力を徐々に失っていきます。過去に機能していたものが、現在では一貫して機能しなくなることがあります。本記事では、このような市場の不確実性に対応するために、「確率レンジ(ゾーン)」という考え方に焦点を当てます。さらに、MQL5を用いて、特に値動きが不安定な相場環境でも機能するアルゴリズムの構築方法を解説していきます。ディスカッションにぜひご参加ください。
ラリー・ウィリアムズの『市場の秘密』(第5回):MQL5におけるボラティリティブレイクアウト戦略の自動化 ラリー・ウィリアムズの『市場の秘密』(第5回):MQL5におけるボラティリティブレイクアウト戦略の自動化
ラリー・ウィリアムズのボラティリティブレイクアウト戦略をMQL5で自動化する方法を、実践的なステップで解説します。日次のレンジ拡張の計算方法、買いと売りレベルの導出、値幅に基づくストップロスとリスクリワードに基づく利益目標によるリスク管理、そしてMetaTrader 5で動作するプロフェッショナルなエキスパートアドバイザー(EA)の構造まで学ぶことができます。これは、ラリー・ウィリアムズの市場概念を完全にテスト可能かつ実運用できる自動売買システムへと変換したいトレーダーや開発者向けに設計されています。