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

その後、シグナルおよびトレンド用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分だけオフセットした位置に買い矢印を配置します。同様に、ベアクロスが発生し、条件を満たす場合には高値の上に売り矢印を配置します。コンパイルすると、次の結果が得られます。

可視化から、インジケーターの計算、色付きローソク足の描画、各バッファの更新が正常におこなわれており、目的が達成されていることが確認できます。残っている作業は、このプログラムのバックテストをおこなうことです。バックテストについては次のセクションで扱います。
バックテスト
テストを実施しました。以下はコンパイル後の可視化を単一のGraphics Interchange Format (GIF)ビットマップ画像形式で示したものです。

結論
本記事では、MQL5におけるSmart WaveTrend Crossoverを実装し、2つのWaveTrendオシレーター(1つはクロスオーバーシグナル用、もう1つはトレンドフィルタ用)を組み合わせる構成を採用しました。チャネル長、平均長、移動平均長といった各種パラメータはユーザーが自由に調整可能であり、柔軟なチューニングができる設計になっています。また、トレンドに基づいたローソク足の色付け機能を備え、クロスオーバー発生時には買いや売りの矢印シグナルをチャート上に表示します。さらに、トレンド確認のオン/オフ設定や、色・オフセットといった視覚的調整オプションも組み込むことで、視認性と実用性の両立を図っています。この構成により、より大きなトレンド方向と整合したモメンタム変化を視覚的に捉えることが可能になります。
次のパートでは、このインジケーターをさらに拡張し、シグナルボックス、トレンド可視化のためのフォグオーバーレイ、カスタマイズ可能な買い/売りラベル、さらにリスク管理を強化するためのテイクプロフィットおよびストップロス表示といった高度なビジュアル要素を追加していきます。完成形としての表現力を高める改良をおこなう予定です。どうぞご期待ください。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/20811
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5入門(第34回):MQL5のAPIとWebRequest関数の習得(VIII)
MQL5入門(第33回):MQL5のAPIとWebRequest関数の習得(VII)
初心者からエキスパートへ:市場の不規則性への対処
ラリー・ウィリアムズの『市場の秘密』(第5回):MQL5におけるボラティリティブレイクアウト戦略の自動化
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索