English
preview
プライスアクション分析ツールキットの開発(第44回):MQL5でVWMAクロスオーバーシグナルEAを構築する

プライスアクション分析ツールキットの開発(第44回):MQL5でVWMAクロスオーバーシグナルEAを構築する

MetaTrader 5 |
8 4
Christian Benjamin
Christian Benjamin

はじめに

取引の成功には、市場のノイズの中から意味のある価格トレンドを見極める能力が不可欠です。数十年にわたり、単純移動平均(SMA)や指数平滑移動平均(EMA)といった移動平均線は、価格変動を平滑化し、市場の方向性を明確にするために広く活用されてきました。しかし、これらの従来型インジケーターでは、重要な要素が考慮されていません。出来高です。出来高は、トレンドの信頼性や重要性を判断する上で極めて大きな役割を果たします。

この課題を解決するために登場したのが、VWMA(出来高加重移動平均、Volume-Weighted Moving Average)です。VWMAは、出来高の多い価格により大きな重みを与えることで、市場の実質的な勢いをより正確に反映します。その結果、出来高に裏付けられた強い値動きと、参加者の少ない弱い変動とを明確に区別することが可能になります。

本記事では、アルゴリズム取引におけるVWMAクロスオーバーの実践的な活用方法を検証します。VWMAの基本概念を解説するとともに、MQL5による実装方法、テスト結果、そして裁量トレーダーおよびシステマティックトレーダー双方にとっての重要なポイントを整理します。

これらの原則を基に開発されたVWMAクロスオーバーエキスパートアドバイザー(EA)は、VWMAクロスオーバー戦略と直感的なオンチャートダッシュボードを組み合わせたツールです。あらゆるチャート背景でも視認性を保つ設計となっており、クロスオーバーイベントを自動検出し、実行可能なシグナルを強調表示するとともに、タイムリーなアラートを提供します。これにより、取引執行や市場監視の効率が向上します。

以下は、本記事で取り上げる各セクションの目次です。以降、それぞれについて詳しく解説していきます。


VWMAの概念理解

VWMAは、特定の開発者によって考案された多くのインジケーターとは異なり、実際の市場出来高を平均値に反映させたいという実務家たちのニーズから、テクニカル分析コミュニティの中で自然発生的に発展してきました。これは、1900年代初頭の初期的な平滑化手法にまで遡る移動平均の長い歴史を基盤としつつ、より明確な歴史的背景を持つVWAP(出来高加重平均価格)における機関投資家の実務的アプローチから着想を得ています。

以下は、SMAおよびEMAと比較したVWMAの主な利点です。

利点 説明
出来高の考慮 単に価格が動いた場所ではなく、実際に多くの取引がおこなわれた価格水準を反映する
優れたトレンド確認 出来高に裏付けられた本物の反転やトレンド継続を確認できる
適応的な応答性 出来高の多い市場の動きには敏感に反応し、出来高の少ないノイズを排除する
機関投資家の強さを検出 スマートマネーによる蓄積や分配の特定に有効
VWMAは、価格と出来高の両方を組み合わせることで、市場トレンドをより包括的に把握するためのテクニカル指標です。従来の移動平均が価格のみを考慮するのに対し、VWMAは、取引量の多い期間の価格をより重要視します。その計算方法は、各ローソク足の終値に対応する出来高を掛けた値を合計し、それを同期間の出来高合計で割るというものです。


VWMAの計算式

ここで

  • 𝑃(𝑖) = i番目のローソク足の価格(通常は終値)
  • 𝑉(𝑖) = i番目のローソク足の出来高
  • 𝑛 = 期間数(例:20)

以下のデータを用いて、5期間VWMAを計算する例を示します。

ローソク足 終値 出来高
1 10 100
2 11 200
3 12 150
4 13 300
5 14 250

計算手順は以下の通りです。

  • 各価格に出来高を掛けます。

(10×100) + (11×200) + (12×150) + (13×300) + (14×250) = 1000 + 2200 + 1800 + 3900 + 3500 = 12,400

  • すべての出来高を合計します。

100 + 200 + 150 + 300 + 250 = 1000

  • 価格×出来高の合計を、出来高合計で割ります。

VWMA = 12,400/1000 = 12.4

したがって、5期間VWMA = 12.4となります。

  1. 価格 > VWMAの場合、市場は出来高に裏付けられた上昇トレンドにあることが多いです。
  2. 価格 < VWMAの場合、出来高に支えられた下降トレンドを示唆します。
  3. VWMAが横ばいの場合、市場参加者が少ない、またはレンジ相場である可能性を示します。

以下は、MQL5 EAまたはインジケーター内で、VWMAを手動計算する方法を示した簡潔なコード例です。

double VWMA(const string symbol, ENUM_TIMEFRAMES timeframe, int period, int shift)
{
   if(period <= 0) return EMPTY_VALUE;
   MqlRates rates[];
   if(CopyRates(symbol, timeframe, shift, period, rates) != period)
      return EMPTY_VALUE;
   double sumPriceVolume = 0, sumVolume = 0;
   for(int i = 0; i < period; i++)
   {
      sumPriceVolume += rates[i].close * rates[i].tick_volume;
      sumVolume += rates[i].tick_volume;
   }
   return (sumVolume == 0) ? EMPTY_VALUE : sumPriceVolume / sumVolume;
}

このEAでは、短期および長期の市場トレンドを捉えるために、高速VWMAと低速VWMAの2種類を使用しています。

高速VWMA

  • 比較的短い期間(例:10または20本)を使用する
  • 直近の価格および出来高の変化に素早く反応する
  • 短期的な市場変動に対して感度が高くなる
  • 短期トレンド、モメンタム、エントリー/エグジットポイントの検出に有効

低速VWMA

  • 比較的長い期間(例:50または60本)を使用する
  • 直近の価格および出来高の変化に対する反応は緩やか
  • 小さな変動を平滑化し、より大きなトレンドに焦点を当てる
  • 市場全体の方向性を把握したり、ノイズを除去したりするのに有効

このEAにおける売買シグナル生成のロジックは、一般的な移動平均(MA)のクロスオーバー戦略とほぼ同様であり、高速VWMAが低速VWMAを上抜けまたは下抜けした際に、買いまたは売りのシグナルが発生します。

弱気シグナル

弱気クロスオーバー

高速VWMAが低速VWMAを下抜けた場合、売りシグナルが生成されます。

具体的な売りシグナルの条件は以下の通りです。

  • 前のバーでは、高速VWMAが低速VWMAの上に位置していた
  • 現在のバーでは、高速VWMAが低速VWMAの下に移動している

この「下向きクロスオーバー」は、強気から弱気へのモメンタム転換の可能性を示しており、売りの好機であることを示唆します。

bool bear = (f_prev > s_prev) && (f_now < s_now);
if(bear /* && other filters */) {
   // Generate sell signal
}

強気シグナル

高速VWMAが低速VWMAを上抜けた場合、買いシグナルが生成されます。

具体的な買いシグナルの条件は以下の通りです。

  • 前のバーでは、高速VWMAが低速VWMAの下に位置していた
  • 現在のバーでは、高速VWMAが低速VWMAの上に移動している

この「上向きクロスオーバー」は、弱気から強気へのモメンタム転換の可能性を示しており、買いの好機であることを示唆します。

bool bull = (f_prev < s_prev) && (f_now > s_now);
if(bull /* && other filters */) {
   // Generate buy signal
}

MetaTrader 5には、VWMAは標準の組み込みインジケーターとして用意されていません。そのため、EAやインジケーターでVWMAを使用する場合は、先に共有した計算式を用いて手動で計算処理を実装する必要があります。


MQL5での実装

MQL5開発を始めるには、まずMetaTrader 5がコンピューターにインストールされていることを確認してください。MQL5の公式開発環境であるMetaEditorは、すべてのMetaTrader 5に標準で付属しているため、別途インストールする必要はありません。MetaTrader 5の準備ができたら、ターミナル内のMetaEditorアイコンをクリックするか、F4キーを押してMetaEditorを起動します。あるいは、MetaTrader 5メニューから直接起動することも可能です。MetaEditorを起動したら、[ファイル]>[新規作成]を選択し、[エキスパートアドバイザ(テンプレート)]を選んでEAの名前を入力します。これらの手順が完了すれば、これから説明する詳細な手順に従ってMQL5のEAを一から構築する準備が整います。

1. ファイルヘッダとメタ情報

//+------------------------------------------------------------------+
//|                                             VWMA CROSSOVER EA.mq5|
//|                               Copyright 2025, Christian Benjamin.|
//|                           https://www.mql5.com/ja/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "https://www.mql5.com/ja/users/lynnchris"
#property version   "1.0"
#property strict

ソースファイルの最初の数行には、MetaTrader 5ターミナルにプログラムの情報を伝えるための#propertyディレクティブが記述されます。copyrightのURLは作者情報を示し、version文字列は後にナビゲーター上で表示される正式なリリース番号を提供します。最後の「#property strict」は、最新の言語ルールをコンパイラに適用させるための指定です。strictモードを使用することで、旧来の曖昧な記述を防ぎ、型変換の問題を検出できるため、コード全体の堅牢性が向上します。

2. 外部入力パラメータ

input ENUM_TIMEFRAMES InpTimeframe = PERIOD_CURRENT;
input uint   FastPeriod  = 20;
input uint   SlowPeriod  = 60;
input uint   SR_LookBack = 50;
input double SR_OffsetPips = 3.0;

input bool   Draw_Arrows = true;
input bool   Send_Alerts = true;
input bool   Show_VWMA_Lines = true;
input int    PlotDepth = 500;

ヘッダの直後には、EAの設定画面でユーザーが変更できる入力変数が定義されます。InpTimeframeは動作する時間足を指定します。PERIOD_CURRENTのままにすると、EAはチャートの時間足をそのまま使用します。FastPeriodとSlowPeriodは、後に比較される2本のVWMAの計算期間を定義します。SR_LookBackとSR_OffsetPipsは、簡易的なサポートとレジスタンスのフィルタを制御するためのパラメータで、直近N本のバーから高値と安値を取得し、価格が一定のpips幅を超えた場合のみシグナルを許可します。また、3つのブール値フラグにより、矢印の描画、アラート送信、VWMAラインの表示有無をユーザーが選択できます。PlotDepthはチャート上に描画する過去バーの本数を制限し、描画負荷を抑える役割を果たします。

3. グローバル実行時変数

datetime g_last_bar_time = 0;
int      g_last_signal = 0; // 1 = BUY, -1 = SELL, 0 = none
datetime g_last_signal_time = 0;
string   DASH_BASE = "VWMA_DASH_";

これらのグローバル変数は、ティック間でEAの状態を保持するために使用されます。g_last_bar_timeは直前に処理したバーの開始時刻を記憶し、1本のバーにつき1回だけロジックが実行されるようにします。g_last_signalとg_last_signal_timeは、直近のシグナルの方向と発生時刻を保持し、ダッシュボード表示に利用されます。DASH_BASEは、チャート上に作成されるパネル関連オブジェクトの名前に付与される共通の接頭辞です。

4. VWMA計算ルーチン

double VWMA(const string sym, ENUM_TIMEFRAMES tf, uint per, int shift)
  {
   if(per == 0)
      return EMPTY_VALUE;
   MqlRates r[];
   if(CopyRates(sym, tf, shift, per, r) != (int)per)
      return EMPTY_VALUE;
   double num = 0, den = 0;
   for(int i = 0; i < (int)per; i++)
     {
      double vol = (double)r[i].tick_volume;
      num += r[i].close * vol;
      den += vol;
     }
   return (den == 0) ? EMPTY_VALUE : num / den;
  }

double VWMA(const string sym, ENUM_TIMEFRAMES tf, uint per, int shift)は、このプログラムの数学的中核を担う関数です。この関数は、まずCopyRatesを使用して過去のローソク足データをper本分コピーします。この取得は現在のバーからshift本前のバーを起点としておこなわれます。取得したデータに対してシンプルなループ処理をおこない、2つの累積和を計算します。1つ目のnumは、各バーの終値に出来高を掛けた値を合計したものです。2つ目のdenは、出来高の合計です。最終的にnumをdenで割ることで、標準的なVWMAが算出されます。

なお、価格データが取得できない場合や、出来高の合計が0の場合には、MetaTraderの定数であるEMPTY_VALUEを返します。EMPTY_VALUEは後続の処理でNaNのように扱われ、計算や描画におけるエラーを防ぐ役割を果たします。

5. サポートとレジスタンスのヘルパー

double HighestHigh(const string s, ENUM_TIMEFRAMES tf, uint lb)
  {
   if(lb == 0)
      return EMPTY_VALUE;
   double a[];
   if(CopyHigh(s, tf, 0, lb, a) != (int)lb)
      return EMPTY_VALUE;
   double m = a[0];
   for(uint i = 1; i < lb; i++)
      if(a[i] > m)
         m = a[i];
   return m;
  }

double LowestLow(const string s, ENUM_TIMEFRAMES tf, uint lb)
  {
   if(lb == 0)
      return EMPTY_VALUE;
   double a[];
   if(CopyLow(s, tf, 0, lb, a) != (int)lb)
      return EMPTY_VALUE;
   double m = a[0];
   for(uint i = 1; i < lb; i++)
      if(a[i] < m)
         m = a[i];
   return m;
  }

bool ValidateSR(bool buy, double bid, double ask, double& sup, double& res)
  {
   res = HighestHigh(_Symbol, InpTimeframe, SR_LookBack);
   sup = LowestLow(_Symbol, InpTimeframe, SR_LookBack);
   if(res == EMPTY_VALUE || sup == EMPTY_VALUE)
      return false;
   double off = SR_OffsetPips * _Point;
   return buy ? (bid >= sup + off) : (ask <= res - off);
  }

2つの簡単なラッパー関数、HighestHighとLowestLowは、CopyHighおよびCopyLowから取得した生データ配列を読み取り、直近N本のバーの中での最高値または最安値を検出します。これらの上に位置するValidateSRは、ブール値を返す関数で、単純ながらも有効な売買フィルタの役割を果たします。まず、先の2つのラッパーを呼び出して直近の高値(res)と安値(sup)を取得します。次に、ユーザーが指定したpipsオフセットを加算または減算し、現在のBidまたはAsk価格がこれらのレベルを突破しているかを確認します。実際には、この処理によりEAが抵抗線付近で買い注文を出したり、支持線付近で売り注文を出したりすることを防ぎます。

6. オブジェクト管理ユーティリティ

//+------------------------------------------------------------------+
//| Chart Utilities                                                  |
//+------------------------------------------------------------------+
void SafeDeleteObject(const string name)
  {
   if(ObjectFind(0, name) != -1)
      ObjectDelete(0, name);
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void PlotChartArrow(const string n, datetime t, double price, color clr, int code)
  {
   SafeDeleteObject(n);
   ObjectCreate(0, n, OBJ_ARROW, 0, t, price);
   ObjectSetInteger(0, n, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, n, OBJPROP_ARROWCODE, code);
   ObjectSetInteger(0, n, OBJPROP_WIDTH, 2);
  }

SafeDeleteObjectは一行の関数で、まず指定されたグラフィカルオブジェクトが存在するかを確認し、存在する場合にのみ削除します。このメソッドにより、すでに削除されたオブジェクトを削除しようとしてターミナルのログが不要に増えることを防ぎます。PlotChartArrowはヘルパー関数で、指定された時刻と価格に対して、一貫したスタイル(色、矢印コード、幅)でOBJ_ARROWオブジェクトを作成します。この2つのヘルパー関数で削除と作成を集中管理することで、他のコード部分はチャートの管理ではなく、売買ロジックに専念できるようになります。

7. VWMAラインの描画

void DrawLineVWMA(const string name, int period, color clr, int depth)
  {
   if(!Show_VWMA_Lines)
     {
      SafeDeleteObject(name);
      int total = ObjectsTotal(0);
      for(int i = total - 1; i >= 0; i--)
        {
         string nm = ObjectName(0, i);
         if(StringFind(nm, name + "_seg_") == 0)
            ObjectDelete(0, nm);
        }
      return;
     }
   const int extra = period + 2;
   MqlRates r[];
   if(CopyRates(_Symbol, InpTimeframe, 0, depth + extra, r) < depth + extra)
      return;
   int total = ObjectsTotal(0);
   for(int i = total - 1; i >= 0; i--)
     {
      string nm = ObjectName(0, i);
      if(StringFind(nm, name + "_seg_") == 0)
         ObjectDelete(0, nm);
     }
   for(int i = 0; i < depth - 1; i++)
     {
      double vw1 = VWMA(_Symbol, InpTimeframe, period, i);
      double vw2 = VWMA(_Symbol, InpTimeframe, period, i + 1);
      if(vw1 == EMPTY_VALUE || vw2 == EMPTY_VALUE)
         continue;
      datetime t1 = r[i].time;
      datetime t2 = r[i + 1].time;
      string seg = name + "_seg_" + IntegerToString(i);
      if(ObjectCreate(0, seg, OBJ_TREND, 0, t1, vw1, t2, vw2))
        {
         ObjectSetInteger(0, seg, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, seg, OBJPROP_WIDTH, 1);
         ObjectSetInteger(0, seg, OBJPROP_RAY_RIGHT, false);
        }
     }
  }

このEAは、バッファを用いた別のインジケーターを作成する代わりに、DrawLineVWMA関数を使用してVWMAを直接チャート上に描画します。これにより、EAのロジックとシームレスに統合され、移動平均線が常にチャートとパラメータに同期した状態で表示されます。関数はまずShow_VWMA_Linesフラグを確認します。ユーザーがVWMAラインの描画を無効にしている場合は、VWMA表示に使用されたオブジェクトを名前の接頭辞で特定してすべて削除し、チャートを整理した状態で処理を終了します。

描画が有効な場合、関数はCopyRatesを使用して過去のローソク足データを取得します。取得するバー数は、ユーザーが指定した処理対象のバー数に加え、計算精度を確保するための余分な本数を含めて設定されており、これにより表示範囲全体にわたってVWMAを算出できるようになっています。新しいラインを描画する前に、チャート上に存在するすべてのオブジェクトを再度走査し、以前の実行で残されたVWMAセグメントをすべて削除します。その後、メインの描画ルーチンでは、指定された期間内にある連続する各バーのペアを順に処理し、各ペアについてセグメントの両端におけるVWMAを計算します。いずれかのVWMA値が無効な場合(たとえば、データが不足している場合など)は、そのペアをスキップします。両方のVWMA値が有効であれば、それぞれの時刻と価格に対応する2点を結ぶ短いトレンドライン(OBJ_TREND)を作成します。各セグメントには、後で簡単にクリーンアップできるよう一意の名前を付け、選択された色と線幅を適用し、ラインが端点を超えて延長されないように設定します。

この処理を多くのバーのペアで繰り返すことで、視覚的に連続したVWMAラインを構築します。このセグメント方式は効率的で効果的であり、メモリ使用量を最小限に抑えつつ、新しいバーが追加されたりパラメータが変更された場合でも迅速に更新されます。関数は堅牢な自動管理を念頭に設計されており、呼び出されるたびに古いセグメントを削除し、最新のVWMA計算に基づいて新しいセグメントを描画するため、常に正確で最新の視覚表現を維持できます。要約すると、DrawLineVWMAは、EAの視覚的な出力の一部としてVWMAをチャート上に重ねて描画するための、実用的かつ堅牢な仕組みを提供します。この機能は視認性、処理効率、ユーザーによる制御性のバランスに優れており、別途インジケーターファイルやバッファを用意することなく実装可能です。さらに、ユーザーは必要に応じてVWMAラインの表示と非表示を切り替えることができるため、チャートを情報豊かでありながら整理された状態に保つことができます。

8. ダッシュボードの構築とメンテナンス

void UpdateDashboard(double fast_vwma, double slow_vwma)
  {
   int corner = 0, x = 12, y = 12;
   int panel_w = 320, panel_h = 130, pill_w = 120, pill_h = 36, gap_y = 12;
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   string prefix = DASH_BASE;
   // ... (creation and property setting for each rectangle and label)
   // See full code for all label/rectangle setup
   string sia = prefix + "SIGN_ARROW";
   if(ObjectFind(0, sia) == -1)
      ObjectCreate(0, sia, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, sia, OBJPROP_CORNER, corner);
   ObjectSetInteger(0, sia, OBJPROP_XDISTANCE, x + pill_w + 130);
   ObjectSetInteger(0, sia, OBJPROP_YDISTANCE, y + (pill_h + gap_y) * 2 + 10);
   ObjectSetInteger(0, sia, OBJPROP_FONTSIZE, 28);
   string txt = "";
   color arrclr = clrGray;
   if(g_last_signal == 1)
     {
      txt = "▲";
      arrclr = clrLime;
     }
   if(g_last_signal == -1)
     {
      txt = "▼";
      arrclr = clrRed;
     }
   ObjectSetInteger(0, sia, OBJPROP_COLOR, arrclr);
   ObjectSetString(0, sia, OBJPROP_TEXT, txt);
  }

UpdateDashboardは、チャート左上にコンパクトな三段構成のコントロールパネルを生成します。黒色のOBJ_RECTANGLEを背景として、その内部に色分けされた2つの情報ボックスを配置し、最新の高速VWMAおよび低速VWMAの値を表示します。さらに、その下には幅の広い情報ボックスを配置し、直近の売買シグナルを表示します。すべての矩形およびラベルは共通の「DASH_BASE」接頭辞を用いて命名されており、EAがアンロードされる際にはDeleteDashboardObjects関数によって一括削除が可能です。この関数は、矢印グリフの種類や色、ならびにVWMA値の小数点以下の表示精度を動的に制御します。これにより、トレーダーはツールチップやエキスパートログを確認することなく、現在の市場状況を一目で把握できます。

9. 売買シグナルの実行

void ExecuteSignal(bool buy, const string cmt)
  {
   g_last_signal = buy ? 1 : -1;
   g_last_signal_time = TimeCurrent();

   // Plot arrow
   if(Draw_Arrows)
     {
      string name = (buy ? "SIGNAL_BUY_" : "SIGNAL_SELL_") + (string)g_last_signal_time;
      color clr = buy ? clrLime : clrRed;
      int code = buy ? 233 : 234;
      double price = buy ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      PlotChartArrow(name, g_last_signal_time, price, clr, code);
     }
   // Alert
   if(Send_Alerts)
     {
      string msg = (buy ? "VWMA SIGNAL BUY " : "VWMA SIGNAL SELL ") + _Symbol + " | " + cmt;
      Alert(msg);
      PlaySound("alert.wav");
     }
   // Update dashboard
   double f = VWMA(_Symbol, InpTimeframe, FastPeriod, 0);
   double s = VWMA(_Symbol, InpTimeframe, SlowPeriod, 0);
   if(f != EMPTY_VALUE && s != EMPTY_VALUE)
      UpdateDashboard(f, s);
  }

戦略ロジックが新しい買いまたは売りのシグナルを検出した場合、ExecuteSignal関数が呼び出されます。この関数は、実際の取引をおこなうこと以外のすべての関連処理を管理します。まず、最新のシグナルの種類と時刻をグローバル変数に更新し、ダッシュボードなどの他のコンポーネントと同期させます。矢印描画が有効になっている場合、この関数はシグナル発生位置に色付きの矢印を描画します。買いシグナルの場合はライム色、売りシグナルの場合は赤色で表示され、視覚的にシグナルを簡単に確認できます。アラートが有効になっている場合は、ポップアップメッセージを表示し、通知音を再生することで、ユーザーにシグナルと対象銘柄の情報を即座に提供します。最後に、最新のVWMA値を用いてダッシュボードを更新し、視覚的な要約が最新の市場状況を反映するようにします。なお、この関数はシグナルの生成と表示のみをおこない、実際の取引はおこないません。そのため、このEAは、将来的に自動売買機能を追加可能なシグナル生成器としても利用できます。

10. ライフサイクルイベントハンドラ

int OnInit()
  {
   Print("VWMA Crossover Dashboard (signal-only) loaded.");
   g_last_bar_time = 0;
   g_last_signal = 0;
   g_last_signal_time = 0;
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   SafeDeleteObject("VWMA_FAST");
   SafeDeleteObject("VWMA_SLOW");
   int total = ObjectsTotal(0);
   for(int i = total - 1; i >= 0; i--)
     {
      string nm = ObjectName(0, i);
      if(StringFind(nm, "SIGNAL_BUY_") == 0 || StringFind(nm, "SIGNAL_SELL_") == 0)
         ObjectDelete(0, nm);
      if(StringFind(nm, "VWMA_FAST_seg_") == 0 || StringFind(nm, "VWMA_SLOW_seg_") == 0)
         ObjectDelete(0, nm);
     }
   DeleteDashboardObjects();
  }

OnInitは、EAがチャートに追加されたときまたは再コンパイルされたときに一度だけ実行されます。すべての状態変数を初期化し、コンソールに挨拶メッセージを表示することで、ユーザーが初期化が成功したことを確認できるようにします。一方、OnDeinitは逆の処理をおこないます。まず個別のマスターオブジェクト(VWMA_FAST、VWMA_SLOW)を削除し、その後チャート上の残りすべてのオブジェクトをループで確認して削除します。削除対象は、名前が「SIGNAL_BUY_」、「SIGNAL_SELL_」、またはポリライン描画で使用されるセグメント接頭辞で始まるオブジェクトです。さらにダッシュボードも取り除かれ、EAを読み込む前のチャートの状態に完全に戻ります。

11. メイン処理(OnTick)

void OnTick()
  {
   // Update dashboard every tick
   double f_now_dash = VWMA(_Symbol, InpTimeframe, FastPeriod, 0);
   double s_now_dash = VWMA(_Symbol, InpTimeframe, SlowPeriod, 0);
   if(f_now_dash != EMPTY_VALUE && s_now_dash != EMPTY_VALUE)
      UpdateDashboard(f_now_dash, s_now_dash);

   datetime cur = iTime(_Symbol, InpTimeframe, 0);
   if(cur == g_last_bar_time)
     {
      DrawLineVWMA("VWMA_FAST", FastPeriod, clrDodgerBlue, PlotDepth);
      DrawLineVWMA("VWMA_SLOW", SlowPeriod, clrOrangeRed, PlotDepth);
      return;
     }
   g_last_bar_time = cur;

   double f_prev = VWMA(_Symbol, InpTimeframe, FastPeriod, 1);
   double s_prev = VWMA(_Symbol, InpTimeframe, SlowPeriod, 1);
   double f_now  = VWMA(_Symbol, InpTimeframe, FastPeriod, 0);
   double s_now  = VWMA(_Symbol, InpTimeframe, SlowPeriod, 0);
   if(f_prev == EMPTY_VALUE || s_prev == EMPTY_VALUE || f_now == EMPTY_VALUE || s_now == EMPTY_VALUE)
      return;

   bool bull = (f_prev < s_prev && f_now > s_now);
   bool bear = (f_prev > s_prev && f_now < s_now);

   double sup, res;
   if(bull && ValidateSR(true, SymbolInfoDouble(_Symbol, SYMBOL_BID), SymbolInfoDouble(_Symbol, SYMBOL_ASK), sup, res))
      ExecuteSignal(true, "VWMA BUY");
   if(bear && ValidateSR(false, SymbolInfoDouble(_Symbol, SYMBOL_BID), SymbolInfoDouble(_Symbol, SYMBOL_ASK), sup, res))
      ExecuteSignal(false, "VWMA SELL");

   DrawLineVWMA("VWMA_FAST", FastPeriod, clrDodgerBlue, PlotDepth);
   DrawLineVWMA("VWMA_SLOW", SlowPeriod, clrOrangeRed, PlotDepth);
  }

すべての市場ティックが到着するたびにOnTickが呼び出されますが、計算負荷の高い部分の処理は1本のバーごとに一度だけ実行されます。この関数はまず、ダッシュボードの値を毎ティック更新し、数字を常に最新の状態に保ちます。その後、現在のローソク足の時刻iTime(...,0)がg_last_bar_timeと異なるかを確認します。異ならない場合は、MAラインのみを再描画して関数は素早く終了します。  新しいバーが検出された場合、EAは前のローソク足と現在のローソク足の両方で高速VWMAと低速VWMAを取得します。高速VWMAが低速VWMAを下から上へ抜けた場合は上昇クロスと判定され、逆の場合は下降クロスと判定されます。各売買候補はValidateSRで確認され、条件を満たした場合にExecuteSignalが呼び出されます。シグナルが発生したかどうかに関わらず、処理の最後に両方のVWMAポリラインを再描画し、新しく形成されたローソク足と視覚要素が同期するようにします。

MQL5 EAのコード作成が完了したら、MetaEditorで[コンパイル]ボタンをクリックしてください。エラーや警告がなければ、EAは展開およびテスト可能な状態です。パフォーマンスを評価するには、MetaTrader 5を開き、ナビゲータウィンドウの「Experts」を展開してEAをチャートにドラッグし、リアルまたはデモ環境で動作を確認します。あるいは、ストラテジーテスターを使用して過去データでEAの挙動をバックテストすることも可能です。

手順

  • MetaEditorで[コンパイル]ボタンを押してEAをコンパイルします。
  • エラーや警告がないことを確認します。問題なければEAは準備完了です。
  • MetaTrader 5を開き、ナビゲータ(通常は左側)に移動して、「Experts」の下にあるEAを見つけます。
  • EAをチャートにドラッグ&ドロップして、リアルまたはデモモードで実行します。
  • 過去のパフォーマンスを確認する場合は、ストラテジーテスターを開き(Ctrl+Rまたは表示メニュー)、EAを選択し設定をおこない、バックテストを開始します。


結果

以下は、このツールのバックテスト結果を示した一例です。専用パネルが表示され、新しいティックが到着するたびに高速VWMAおよび低速VWMAの現在値がリアルタイムで更新されます。チャート上では、緑色の矢印が買いシグナルを示し、赤色の矢印が売りシグナルを示すため、各取引イベントを視覚的に明確に把握できます。全体として、有効と判断できるシグナルが連続して確認され、その大部分は有利な取引機会を示しています。誤ったシグナルも一部見られますが、その数は比較的少なく、このツールが市場ノイズを抑制し、実質的なトレンドを識別する上で一定の有効性を持つことが分かります。

バックテスト


結論

結論として、単純移動平均(SMA)や指数移動平均(EMA)といった従来の移動平均は、現在でも有用なテクニカル分析ツールである一方、主に価格データのみに焦点を当てているため、市場動向における出来高の重要性を見落とす場合があります。VWMAクロスオーバーEAは、計算に出来高を組み込むことで、これらの手法を補完し、より多角的な価格分析を可能にします。価格と出来高の双方を重視することで、このツールは、とくに市場参加が活発な局面において、強気および弱気の反転ポイントをより効果的に識別する助けとなります。このような追加的な洞察は、取引判断の精度向上に寄与し、売買シグナルに対する理解を深めます。その結果、VWMAクロスオーバーEAは、あらゆるトレーダーの分析ツールキットに加える価値のある実用的な手法と言えるでしょう。

他の記事もぜひご覧ください。

注意:本記事およびVWMAクロスオーバーEAは教育目的で提供されており、テクニカル分析やシグナルベース取引の理解を深めることを目的としています。EAはシグナルジェネレーターとして機能し、取引を自動で実行するものではないため、取引の判断は常にユーザー自身がおこないます。ツールを試し、十分にバックテストをおこない、入力パラメータを自身の戦略や市場環境に合わせて調整することを推奨します。シグナルは分析の一部として活用し、必ず自身の判断で検証してください。慎重なテストと適切なカスタマイズにより、このツールは取引ツールキットにおける価値ある補助となります。

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

添付されたファイル |
最後のコメント | ディスカッションに移動 (4)
Wilson Malinda
Wilson Malinda | 9 10月 2025 において 15:50
XAUSDのシグナルが5日間表示されないのはなぜですか?
Christian Benjamin
Christian Benjamin | 9 10月 2025 において 16:53
Wilson Malinda #:
XAUSDのシグナルが5日間表示されません。
バックテストか何かですか?このツールは今日投稿されたばかりなのに、どうしてもう5日も経っているんだ?
smartchartfx
smartchartfx | 17 10月 2025 において 12:15
興味深いセットアップだ。VWMAの クロスオーバーはきれいに見える。値動きだけに頼るのではなく、トレンド確認と出来高を組み合わせているのがいい。より低いタイムフレーム、特にボラティリティの高いセッションでどのように機能するか見てみたい。
beetle2008
beetle2008 | 27 10月 2025 において 07:41
出来高加重移動平均線と プライス・アクションをミックスして反転を捉える方法が素晴らしい。チャート上のパネルがとてもきれいで、フォローしやすい。このアイデアで遊ぶ余地はたくさんある。みんなと共有してくれてありがとう。
知っておくべきMQL5ウィザードのテクニック(第83回): ストキャスティクスとFrAMAのパターンの使用 - 行動アーキタイプ 知っておくべきMQL5ウィザードのテクニック(第83回): ストキャスティクスとFrAMAのパターンの使用 - 行動アーキタイプ
ストキャスティクスとフラクタル適応型移動平均(FrAMA: Fractal Adaptive Moving Average)は、互いに補完し合う特性を持っており、MQL5のエキスパートアドバイザー(EA)で使える指標ペアの1つです。ストキャスティクスはモメンタムの変化を捉えるために使用し、FrAMAは現在のトレンドを確認するために利用します。本記事では、これら2つのインジケーターの組み合わせについて、MQL5ウィザードを活用して構築およびテストをおこない、その有効性を検証します。
取引システムの構築(第5回):構造化された取引決済による利益管理 取引システムの構築(第5回):構造化された取引決済による利益管理
利益目標まであとわずかというところで価格が反転し、ストップロスにかかってしまう。トレーリングストップによって建値で決済された直後に、市場が元の方向へ大きく動き、当初の目標を超えていく。多くのトレーダーにとって、これはおなじみの悩みでしょう。本記事では、異なるリスクリワードレシオ(RRR)で複数のエントリーを配置する手法に焦点を当て、利益を体系的に確保しながら、全体のリスク曝露を抑えるアプローチを解説します。
無効化されたオーダーブロックをミティゲーションブロックとして再利用する(SMC) 無効化されたオーダーブロックをミティゲーションブロックとして再利用する(SMC)
本記事では、以前に無効化されたオーダーブロックをスマートマネーコンセプト(SMC)におけるミティゲーションブロックとして再利用する方法を解説します。これらのゾーンは、オーダーブロックが失敗した後に機関投資家が再び市場に参入するポイントを示しており、支配的なトレンドに沿った取引継続の確率が高いエリアを提供します。
MQL5でのAI搭載取引システムの構築(第3回):複数行入力の克服、チャットの持続性の確保、シグナル生成 MQL5でのAI搭載取引システムの構築(第3回):複数行入力の克服、チャットの持続性の確保、シグナル生成
本記事では、ChatGPTを統合したMQL5プログラムを拡張し、改良されたテキストレンダリングにより複数行入力の制限を克服します。さらに、AES256暗号化およびZIP圧縮で保存された永続的なチャット履歴をナビゲートするサイドバーを導入し、チャートデータの統合による初期売買シグナルの生成もおこないます。