MQL5入門(第19回):ウォルフ波動の自動検出
はじめに
連載「MQL5入門」の第19回へようこそ。第18回では、ウォルフ波動パターンを紹介しました。このパターンは、価格の反転を高精度で予測できるユニークな5波構造です。具体的には、価格の下落が予想される弱気セットアップと、上昇が予想される強気セットアップの2種類があります。また、ローソク足データの取得方法についても学びました。これはプライスアクション分析に不可欠です。そして、このパターンに基づいて有効な取引機会を見つけるロジックについても解説しました。
今回の記事では、理論から実装段階へと進みます。ウォルフ波動構造をプログラムで検出し、それに基づいて取引を実行する方法を探ります。これには、主要なスイングポイントの検出、パターンルールの検証、シグナルに基づくエキスパートアドバイザー(EA)の準備が含まれます。
本記事で学べる内容は以下の通りです。
- 過去の価格データを使用して、ウォルフ波動の典型的な5波構造を検出する方法
- スイングハイとスイングローをプログラムで識別し、波のポイントを決定する方法
- 特に波3と波5の形成を検証するために、フィボナッチエクスパンションレベルを適用し、修正する方法
- パターン確認と取引判断に重要な3本のトレンドライン(波1~3、波2~4、波1~4)を描画する方法
- 波1~2と波3~4の対称性や比率関係を確認し、パターンの信頼性を向上させる方法
- 波にラベルを付け、テキストや長方形、トレンドラインなどの視覚的オブジェクトをMQL5でチャート上に描画する方法
- エントリー前に確認を待つロジックを実装し、誤ったエントリーを避ける方法
- 市場が波1~4のトレンドラインに触れた際に自動で取引を終了するロジックを実装し、リスクを管理する方法
弱気ウォルフ波動パターンの特定
前回の記事では、弱気ウォルフ波動パターンの構造とルールについて詳しく解説しました。今回は、このロジックをプログラムでどのように実装するかに焦点を当てます。 前回触れた通り、弱気ウォルフ波動は5つの波で構成されており、特定の順序や構造条件を満たす必要があります。波1はスイングハイである必要があります。波2は波1の下に位置するスイングローでなければなりません。波3は、再びスイングハイを形成しますが、今回は波1より上で、波1から波2の区間における特定のフィボナッチエクスパンション内に位置する必要があります。波4は波3より下に位置するスイングローですが、波2より下にはならない位置にあります。波5はパターンを完成させ、波3より上のスイングハイを形成し、波3から波4の動きのフィボナッチエクスパンション内に収まります。
さらに、波1~2の長さと波3~4の長さが概ね同等であることも重要です。理想的には、波3~4は波1~2の70%以上の長さである必要があります。この対称性によりパターンの信頼性が高まり、パターンの有効性を確認する基準となります。このセクションでは、スイング検出関数を使用して5つの波の位置を特定し、それぞれの距離や接続がウォルフ波動の条件を満たしているかどうかをチェックします。
波1と波2の識別
弱気ウォルフ波動パターンを見つけるための最初のステップは、波1と波2を正確に認識することです。これら2つの基礎が正しく特定できなければ、残りの波を正確に捉えることは困難です。この部分では、チャート上で波1と波2の条件に合致する正当なスイングハイとスイングローを探す方法に注目します。検出が完了したら、視覚的にわかりやすいようにチャート上に明確にラベルを付けます。
また、波1と波2の間にフィボナッチエクスパンションを設定します。パターンルールに基づいて適切な価格レベルを定めることで、波3と波5の検出を誘導するのに役立ちます。この段階で基盤を作ることで、残りの波の識別プロセスをスムーズに進めることができます。
例input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; int bars_check = 500; datetime time_bar; double total_symbol_bars; double open[]; double close[]; double low[]; double high[]; datetime time[]; double wv1; datetime wv1_time; string wv1_txt; double wv2; datetime wv2_time; string wv2_txt; string fib_ext_wv1_wv2; ulong chart_id = ChartID(); //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { total_symbol_bars = Bars(_Symbol, timeframe); time_bar = iTime(_Symbol,timeframe,0); CopyOpen(_Symbol, timeframe, time_bar, bars_check, open); CopyClose(_Symbol, timeframe, time_bar, bars_check, close); CopyLow(_Symbol, timeframe, time_bar, bars_check, low); CopyHigh(_Symbol, timeframe, time_bar, bars_check, high); CopyTime(_Symbol, timeframe, time_bar, bars_check, time); if(total_symbol_bars >= bars_check) { for(int i = 7; i < bars_check - 7; i++) { if(IsSwingHigh(high, i, 7)) { wv1 = high[i]; wv1_time = time[i]; wv1_txt = StringFormat("WAVE 1 %d", i); for(int j = i; j < bars_check - 7; j++) { if(IsSwingLow(low, j, 7) && low[j] < wv1) { wv2 = low[j]; wv2_time = time[j]; wv2_txt = StringFormat("WAVE 2 %d", j); ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); } break; } } } } } } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i]) return false; } return true; }
出力

説明
弱気ウォルフ波動のようなパターンを効率的に特定して記録するために、最初の2つの波に関する重要な情報を保持する変数が宣言されます。波1には3つの変数を用意します。価格レベルを保持するwv1、その価格が発生した正確な時間を記録するwv1_time、波をチャート上で視覚的に表すためのテキストラベル「wv1_txt」です。波2についても同様に、wv2、wv2_time、wv2_txtの3つの変数が用意され、同じ役割を果たします。
これらのデータは、EAが重要なスイングハイやスイングローを検出した際の正確な価格と時刻を保存・参照できるため、非常に重要です。また、文字列ラベルを使うことで、トレーダーはどの波がチャート上で強調されているかどうかを簡単に把握でき、視覚的な明瞭さが向上します。
さらに、fib_ext_wv1_wv2という文字列変数が宣言され、波データに加えてフィボナッチエクスパンションオブジェクトの名前を保存します。これにより、波1から波2への拡張線オブジェクトを必要に応じて適切に生成、変更、削除できるようになります。チャート上の各オブジェクトは一意の識別子を持つ必要があるため、通常は波のインデックスを名前に含めて、EAが過去のバーをスキャンして複数のパターンを作成する場合でも重複を避けます。
コードではさらに、ChartID()関数を使って現在のチャートの一意のIDを取得し、chart_id変数に保存します。MetaTrader 5では複数のチャートを同時に開くことができるため、これは非常に重要です。関連するチャートIDを明示的に指定することで、EAが意図したチャート上でグラフィック要素を作成して操作でき、誤った配置や競合を防ぎます。
OnDeinit()関数内のObjectsDeleteAll(chart_id);は、EAの終了時、またはチャートを閉じた際に、EAが描画したすべてのオブジェクトを削除するクリーンアップ関数です。これにより、古い波ラベルやフィボナッチ線などが残らず、チャートが散らかることを防げます。
EAは、パターン検出ロジックを開始する前に十分なバー数が存在するかどうかを確認します。具体的には、スキャンするバーの数(bars_check)と、チャート上の総バー数(total_symbol_bars)を比較します。表示に十分な履歴バーが読み込まれている場合(if(total_symbol_bars >= bars_check))、EAは弱気ウォルフ波動の初期サインを探すために価格データのスキャンを開始します。
検出の最初のステップは、以下のforループです。
for(int i = 7; i < bars_check - 7; i++)
スイングハイやスイングローを安全に確認するために、ループはインデックス0ではなく7から開始します。スイング検出関数は、対象バーの前後7本のバーを比較して、局所的な高値や安値かどうかを判断します。ループが0から始まった場合、比較用の前方バーが存在せず、配列範囲外エラーが発生する可能性があります。同様に、ループを「bars_check - 7」で止めることで、後方にも十分なバーが確保されます。
この外側ループでは、ウォルフ波動の波1(スイングハイ)となり得るバーを探します。インデックスiの高値が局所的な最大値(周囲のローソク足よりも高い値)であるかどうかを判定します。各インデックスiは過去のローソク足を表します。
if(IsSwingHigh(high, i, 7))
この関数がtrueを返した場合、EAはこのバーを波1の候補として認識します。チャート上で使用するラベル文字列(wv1_txt)を作成し、関連する高値と時間をwv1とwv1_timeに保存します。
有効な波1(スイングハイ)が見つかった後、EAは次に続く有効なスイングロー(波2)を探すために内側ループを開始します。
for(int j = i; j < bars_check - 7; j++)
このループは外側ループと同じインデックスiから開始し、前方に向かってスキャンします。以下の2つの条件を満たすバーを探します。
- スイングロー(局所的な安値)であること
- 価格が波1の価格より低いこと
if(IsSwingLow(low, j, 7) && low[j] < wv1)
両方の条件を満たす場合、EAは波2の価格、時間、ラベルをそれぞれwv2、wv2_time、wv2_txtに保存します。その後、break文を使って内側ループを終了し、波1と波2の間にフィボナッチエクスパンションオブジェクトを描画し、両波のラベルをチャートに追加します。これにより、EAは現在の波1に対して適切な波2候補を1つだけ検出し、さらに複数の波2候補を探すことを防ぎます。
波3と波4の識別
ウォルフ波動パターンを構築するには、まず波1と波2を正確に特定したうえで、波3と波4を特定するのが合理的です。ただし、波1と波2の位置や構造は、その後の波に大きな影響を与えます。特に波3は、次の2つの重要な条件を満たす必要があります。波1の高値を上回る位置で形成されること、かつ、波1から波2の動きに基づいて計算された、事前に定められたフィボナッチエクスパンションの範囲内に収まることです。
波1から波2までの価格変動を利用してフィボナッチエクスパンションを描き、この範囲を特定します。このツールは、最初の波の大きさや勢いに基づいて、将来の価格が到達する可能性のある水準を予測するのに役立ちます。価格が反転する前に到達すると予想される範囲は、通常127.2%から161.8%のエクスパンションレベルです。この範囲内でスイングハイを形成する価格が、波3の候補として適しています。
また、波3は価格レベルだけでなく、パターンの対称性も尊重する必要があります。波1と波2の構造や規模は、波3と波4によって適切に反映されるべきです。この幾何学的なバランスこそが、正しいウォルフ波動パターンの特徴です。本記事では、この対称性を簡単かつ定量的に定義します。具体的には、波1から波2までの全体の動きが、波3と波4の大きさの少なくとも70%であることが望ましいとします。EAを開発する過程では、この割合を基準としてパターンの検証をおこない、ウォルフ波動の構造的・視覚的な整合性を維持することができます。
波1と波2の間の距離の70%を決定する
まず、波1と波2の間の距離の70%を決定しましょう。
例
double wv1_wv2_size; double wv1_wv2_70p;
if(total_symbol_bars >= bars_check) { for(int i = 7; i < bars_check - 7; i++) { if(IsSwingHigh(high, i, 7)) { wv1 = high[i]; wv1_time = time[i]; wv1_txt = StringFormat("WAVE 1 %d", i); for(int j = i; j < bars_check - 7; j++) { if(IsSwingLow(low, j, 7) && low[j] < wv1) { wv2 = low[j]; wv2_time = time[j]; wv2_txt = StringFormat("WAVE 2 %d", j); ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); } wv1_wv2_size = MathAbs(wv1 - wv2); wv1_wv2_70p = (wv1_wv2_size / 100) * 70; string luh = StringFormat("bb 2 %d", j); ObjectCreate(chart_id, luh, OBJ_TREND, 0, wv1_time, wv1 - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p); break; } } } } }
出力
説明
また、波3は価格レベルだけでなく、パターンの対称性も尊重する必要があります。波1と波2の構造や規模は、波3と波4によって適切に反映されるべきです。この幾何学的なバランスこそが、正しいウォルフ波動パターンの特徴です。本記事では、この対称性を簡単かつ定量的に定義します。具体的には、波1から波2までの全体の動きが、波3と波4の大きさの少なくとも70%であることが望ましいとします。EAを開発する過程では、この割合を基準としてパターンの検証をおこない、ウォルフ波動の構造的・視覚的な整合性を維持することができます。
この基準を視覚的に確認するために、チャート上に水平トレンドラインを引きます。波1の高値から波2の価格までの間に、波1の高値より70%下の価格レベルにラインを描画します。このラインは、後続の波が最小サイズの要件を満たしていることを確認するための参照点として機能します。
波1と波2の順序にかかわらず、ここではMathAbs関数を使用して、計算されたサイズが常に正の値になるようにしています。今回の弱気パターンでは波1は波2より上に位置することが予想されるため、厳密には必須ではありません。しかし、MathAbsを使用することで、波の位置が誤って評価されたり、入れ替わった場合でも計算ミスを防ぐ予防策となります。
波1と波2のフィボナッチエクスパンション
IsSwingHigh関数を使用して、アルゴリズムは波2の後に発生し、波1と波2のフィボナッチエクスパンション127.2%から161.8%の範囲内に収まるスイングハイを探すことで波3を特定します。条件に合致する場合、その価格を波3として保存します。正しいパターンを扱っていることを確認するため、チャート上にはフィボナッチツール、70%ライン、そして波の名前だけを描画します。その後、アルゴリズムは波3の後に発生するスイングローを探し、波4を特定します。
例
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; input double max_fib_ext_wv12 = 161.8; input double min_fib_ext_wv12 = 127.2; int bars_check = 500; datetime time_bar; double total_symbol_bars; double open[]; double close[]; double low[]; double high[]; datetime time[]; double wv1; datetime wv1_time; string wv1_txt; double wv2; datetime wv2_time; string wv2_txt; string fib_ext_wv1_wv2; ulong chart_id = ChartID(); double wv1_wv2_size; double wv1_wv2_70p; string perc_70; double fib_ext_1_2_161_8; double fib_ext_1_2_127_2; string fib_ext_range; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { total_symbol_bars = Bars(_Symbol, timeframe); time_bar = iTime(_Symbol, timeframe, 0); CopyOpen(_Symbol, timeframe, time_bar, bars_check, open); CopyClose(_Symbol, timeframe, time_bar, bars_check, close); CopyLow(_Symbol, timeframe, time_bar, bars_check, low); CopyHigh(_Symbol, timeframe, time_bar, bars_check, high); CopyTime(_Symbol, timeframe, time_bar, bars_check, time); if(total_symbol_bars >= bars_check) { for(int i = 7; i < bars_check - 7; i++) { if(IsSwingHigh(high, i, 7)) { wv1 = high[i]; wv1_time = time[i]; wv1_txt = StringFormat("WAVE 1 %d", i); for(int j = i; j < bars_check - 7; j++) { if(IsSwingLow(low, j, 7) && low[j] < wv1) { wv2 = low[j]; wv2_time = time[j]; wv2_txt = StringFormat("WAVE 2 %d", j); ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); } wv1_wv2_size = MathAbs(wv1 - wv2); wv1_wv2_70p = (wv1_wv2_size / 100) * 70; perc_70 = StringFormat("70 PERCENT %d", j); ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_time, wv1 - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p); fib_ext_1_2_127_2 = MathAbs((((wv1 - wv2) / 100) * (min_fib_ext_wv12 - 100)) + wv1); fib_ext_1_2_161_8 = MathAbs((((wv1 - wv2) / 100) * (max_fib_ext_wv12 - 100)) + wv1); fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j); ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_time, fib_ext_1_2_161_8, wv2_time, fib_ext_1_2_127_2); break; } } } } } } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOW | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGH | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high_price[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i]) return false; } return true; }
出力

第3波の識別
コードは波2の後方に進み、波1と波2の127.2%から161.8%のフィボナッチエクスパンション内に収まるスイングハイを探すことで、弱気ウォルフ波動の波3を特定します。その範囲内で有効なスイングハイが見つかった場合、その価格を波3として指定します。
例double wv3; datetime wv3_time; string wv3_txt;
if(total_symbol_bars >= bars_check) { for(int i = 7; i < bars_check - 7; i++) { if(IsSwingHigh(high, i, 7)) { wv1 = high[i]; wv1_time = time[i]; wv1_txt = StringFormat("WAVE 1 %d", i); for(int j = i; j < bars_check - 7; j++) { if(IsSwingLow(low, j, 7) && low[j] < wv1) { wv2 = low[j]; wv2_time = time[j]; wv2_txt = StringFormat("WAVE 2 %d", j); wv1_wv2_size = MathAbs(wv1 - wv2); wv1_wv2_70p = (wv1_wv2_size / 100) * 70; fib_ext_1_2_127_2 = MathAbs((((wv1 - wv2) / 100) * (min_fib_ext_wv12 - 100)) + wv1); fib_ext_1_2_161_8 = MathAbs((((wv1 - wv2) / 100) * (max_fib_ext_wv12 - 100)) + wv1); for(int k = j; k < bars_check - 7; k++) { if(IsSwingHigh(high, k, 7) && time[k] > wv2_time) { wv3 = high[k]; wv3_time = time[k]; wv3_txt = StringFormat("WAVE 3 %d", k); if(wv3 >= fib_ext_1_2_127_2 && wv3 <= fib_ext_1_2_161_8) { ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); } perc_70 = StringFormat("70 PERCENT %d", j); ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_time, wv1 - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p); fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j); ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_time, fib_ext_1_2_161_8, wv2_time, fib_ext_1_2_127_2); ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_time, wv3); ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3"); ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue); } break; } } break; } } } } }
出力

説明
波3を定義するために、コードでは3つの変数が使用されます。価格レベルを保持するwv3、時刻を記録するwv3_time、チャート上でラベル表示するwv3_txtです。IsSwingHigh関数を用いて、波2の後に発生する正当なスイングハイを検出することで、波2の位置から前方をスキャンし波3を特定します。以前は、すべてのオブジェクトは波2を検出した後に描画されていましたが、現在は波3が波1と波2の指定されたフィボナッチ範囲内に収まった場合にのみ、チャート上にオブジェクトが表示されます。
第4波の識別
次のステップは、波3の後に形成されるスイングローである波4を特定することです。波3から波4への価格変動が、波1から波2の動きと適切な対称性を保っているかを確認し、パターンが有効であることを検証する必要があります。本プロジェクトでは、シンプルな対称性の基準を使用しています。具体的には、波3から波4の長さは、波1から波2の長さの少なくとも70%である必要があります。この条件により、極端に小さい戻りや不釣り合いな動きを防ぎ、ウォルフ波動パターンの構造的バランスを維持することができます。次の部分では、この基準を実際に適用し、波3の後に正当なスイングローを探し、最小サイズの条件を満たしているかを確認します。
例double wv4; datetime wv4_time; string wv4_txt; double wv3_wv4_size;
if(total_symbol_bars >= bars_check) { for(int i = 7; i < bars_check - 7; i++) { if(IsSwingHigh(high, i, 7)) { wv1 = high[i]; wv1_time = time[i]; wv1_txt = StringFormat("WAVE 1 %d", i); for(int j = i; j < bars_check - 7; j++) { if(IsSwingLow(low, j, 7) && low[j] < wv1) { wv2 = low[j]; wv2_time = time[j]; wv2_txt = StringFormat("WAVE 2 %d", j); wv1_wv2_size = MathAbs(wv1 - wv2); wv1_wv2_70p = (wv1_wv2_size / 100) * 70; fib_ext_1_2_127_2 = MathAbs((((wv1 - wv2) / 100) * (min_fib_ext_wv12 - 100)) + wv1); fib_ext_1_2_161_8 = MathAbs((((wv1 - wv2) / 100) * (max_fib_ext_wv12 - 100)) + wv1); for(int k = j; k < bars_check - 7; k++) { if(IsSwingHigh(high, k, 7) && time[k] > wv2_time) { wv3 = high[k]; wv3_time = time[k]; wv3_txt = StringFormat("WAVE 3 %d", k); if(wv3 >= fib_ext_1_2_127_2 && wv3 <= fib_ext_1_2_161_8) { for(int l = k; l < bars_check - 7; l++) { if(IsSwingLow(low, l, 7) && time[l] > wv3_time) { wv4 = low[l]; wv4_time = time[l]; wv4_txt = StringFormat("WAVE 4 %d", l); wv3_wv4_size = MathAbs(wv3 - wv4); if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2 && wv4 < wv3) { ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); } perc_70 = StringFormat("70 PERCENT %d", j); ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_time, wv1 - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p); fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j); ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_time, fib_ext_1_2_161_8, wv2_time, fib_ext_1_2_127_2); ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_time, wv3); ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3"); ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4); ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4"); ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue); } break; } } } break; } } break; } } } } }
出力

説明
波3を特定した後、次のステップは波4を識別することです。プログラムは波3と波4の距離を計算し、wv3_wv4_sizeに保存します。スイングローを検出すると、バーの時刻と価格をそれぞれwv4_timeとwv4に保存します。プログラムは、波3と波4の大きさが波1と波2と類似していることを確認するとともに、波4が波2より上、波3より下に位置するようにして、正しいパターン構造を維持します。また、波3が指定されたフィボナッチエクスパンション範囲内に収まっているかも確認します。これにより、スイング検出ロジック、フィボナッチフィルタ、対称性基準を組み合わせて波4を特定し、すべての条件を満たした後でチャート上にラベルを表示します。これでウォルフ波動パターンのセットアップが完成します。


最初の画像(図11)を見ると、波3は波3から波4への移行の間で最高値になっていません。波3は波4への下落前の最も重要なピークであるべきなので、これは問題です。波3として指定した後にそれよりも高い高値が出現している場合、検出機構は正しい構造を捉えられていません。また、2番目の画像(図12)では、波1が波1と波2の間で最高値になっていないこともわかります。
さらに、これはパターンルールの違反にもなります。波1はそのレッグにおける支配的なスイング高値であるべきだからです。アルゴリズムが根本的に間違った波のポイントを選び続けると、ウォルフ波動パターンの信頼できる結果は得られません。この問題を解決することで、検出ロジックの信頼性を向上させる必要があります。
そこで、先ほどの例で見られた問題に対応するため、すべての波に対して追加の検証ステップを導入します。波3は、下落前のピークを正確に表すために、波3から波4までの間での最高値である必要があります。波2は、次の上昇トレンド前の底を正しく反映するために、波2から波3までの間での最安値である必要があります。波1は、そのパターン区間における重要なスイング高値として簡単に認識できるように、波1から波2までの間での最高値である必要があります。
例
int wv3_wv4_bars; int wv3_highest_index; double wv3_hh; datetime wv3_hh_t; int wv2_wv3_bars; int wv2_lowest_index; double wv2_ll; datetime wv2_ll_t; int wv1_wv2_bars; int wv1_highest_index; double wv1_hh; datetime wv1_hh_t;
for(int l = k; l < bars_check - 7; l++) { if(IsSwingLow(low, l, 7) && time[l] > wv3_time) { wv4 = low[l]; wv4_time = time[l]; wv4_txt = StringFormat("WAVE 4 %d", l); wv3_wv4_bars = Bars(_Symbol, timeframe, wv3_time, wv4_time); wv3_highest_index = ArrayMaximum(high, k, wv3_wv4_bars); wv3_hh = high[wv3_highest_index]; wv3_hh_t = time[wv3_highest_index]; wv2_wv3_bars = Bars(_Symbol, timeframe, wv2_time, wv3_time); wv2_lowest_index = ArrayMinimum(low, j, wv2_wv3_bars); wv2_ll = low[wv2_lowest_index]; wv2_ll_t = time[wv2_lowest_index]; wv1_wv2_bars = Bars(_Symbol, timeframe, wv1_time, wv2_time); wv1_highest_index = ArrayMaximum(high, i, wv1_wv2_bars); wv1_hh = high[wv1_highest_index]; wv1_hh_t = time[wv1_highest_index]; wv1_wv2_size = MathAbs(wv1_hh - wv2_ll); wv1_wv2_70p = (wv1_wv2_size / 100) * 70; fib_ext_1_2_127_2 = MathAbs((((wv1_hh - wv2_ll) / 100) * (min_fib_ext_wv12 - 100)) + wv1_hh); fib_ext_1_2_161_8 = MathAbs((((wv1_hh - wv2_ll) / 100) * (max_fib_ext_wv12 - 100)) + wv1_hh); wv3_wv4_size = MathAbs(wv3_hh - wv4); if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2_ll && wv4 < wv3_hh && wv3_hh >= fib_ext_1_2_127_2 && wv3_hh <= fib_ext_1_2_161_8) { ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_hh_t, wv1_hh); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_ll_t, wv2_ll); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_ll_t, wv2_ll, wv1_hh_t, wv1_hh, wv2_ll_t, wv2_ll); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); perc_70 = StringFormat("70 PERCENT %d", j); ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_hh_t, wv1_hh - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p); fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j); ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_hh_t, fib_ext_1_2_161_8, wv2_ll_t, fib_ext_1_2_127_2); ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_hh_t, wv3_hh); ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3"); ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4); ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4"); ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue); } break; } }
出力

説明
ウォルフ波動の構造を正しく確認し、各波がその区間における最も重要な高値や安値を正確に表すようにするため、新たな計算方法を提案します。各波区間での極値(最高値や最安値)を特定するために、まずいくつかの変数を宣言します。波3と波4の間のバー数はwv3_wv4_barsに格納されます。この範囲内での最高値は、ArrayMaximum関数を使って求めます。開始インデックスは波3の位置であるkとし、結果はwv3_highest_indexに保存されます。このインデックスから正しい高値(wv3_hh)とその時刻(wv3_hh_t)を取得します。
波2と波3の間の範囲はwv2_wv3_barsに格納されます。ここではArrayMinimum関数を使用して最安値を求め、得られたインデックスをwv2_lowest_indexに保存します。その後、対応する安値(wv2_ll)と時刻(wv2_ll_t)を取得します。 波1と波2の間のバー数はwv1_wv2_barsに記録されます。この区間では波1が支配的なピークであるべきなので、再度ArrayMaximum関数を使って最高値を求めます。得られたインデックスをwv1_highest_indexに保存し、そこから高値(wv1_hh)と対応する時刻(wv1_hh_t)を取得します。
wv3_wv4_barsには、波3と波4の間のバーの数が記録されます。この範囲内の最高値を見つけるために、ArrayMaximumを使ってwv3_wv4_barsの数だけバーをスキャンし、開始インデックスは波3の位置であるkです。得られた結果はwv3_highest_indexに保持され、このインデックスを使用して正しい高値(wv3_hh)とその時刻(wv3_hh_t)を取得します。 これらの制約をプログラムで適用することにより、ウォルフ波動の認識精度を向上させ、弱い波や誤った波を誤認する可能性を減らすことができます。この追加検証ステップにより、パターンが技術的概念に適合していること、そして実際の取引分析でも信頼できることが保証されます。
こうして取得した検証済みの高値や安値は、チャート上のオブジェクト作成に使用されます。このとき使用するのは、初期に選ばれた波のポイントではなく、各区間の実際の極値です。波1については、wv1_timeとwv1をそれぞれwv1_hh_tとwv1_hhに置き換えます。波2についてはwv2_timeとwv2をwv2_ll_tとwv2_llに置き換え、波3についてはwv3_timeとwv3をwv3_hh_tとwv3_hhに置き換えます。 この調整により、チャート上で描画されるオブジェクトが各波区間の実際の構造的高値・安値を反映し、ウォルフ波動の可視化精度と信頼性が大幅に向上します。これらの検証済みのエリアにチャートオブジェクトを描画することで、ウォルフ波動の可視化精度と信頼性を大幅に向上させます。これにより、トレーダーはより正確なパターン構造に基づいて、より適切な判断をおこなうことができます。
フィボナッチエクスパンションや波1~4のチャートアイテム、その他のグラフィック要素は、次の条件がすべて満たされた場合にのみ生成されます。
if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2_ll && wv4 < wv3_hh && wv3_hh >= fib_ext_1_2_127_2 && wv3_hh <= fib_ext_1_2_161_8)この条件により、弱気ウォルフ波動としての価格および構造上の要件がすべて満たされていることが確認されます。具体的には、波3と波4の縦方向の距離が波1と波2以上であることにより対称性が保たれ、波4の位置が波2と波3の間に正しく収まっていることも確認されます。さらに、波3は波1と波2に対する127.2%~161.8%のフィボナッチエクスパンション範囲内にある必要があります。
これらすべての条件が満たされるまで、ソフトウェアは波に名前を付けたり、フィボナッチ投影を作成したり、必要なトレンドラインをチャートに描画したりしません。この厳密な検証により、ウォルフ波動の認識精度が高まり、誤ったパターンや不完全なパターンが描画されるのを防ぐことができます。
波5の識別
ウォルフ波動の検出手順において、次のステップは波5の識別です。この波はパターンの潜在的な反転ポイントを示すため、非常に重要です。波5はテクニカル分析において有用な指標であり、トレーダーは市場の方向性の変化を予測するためによくこの波を探します。しかし、波5を正しく識別する前に、まず確認の指針となる3つの重要なトレンドラインを作成する必要があります。
最初のトレンドラインは波1と波3を結びます。このラインは通常、弱気ウォルフ波動におけるパターンの上限を示します。パターンの構造や方向を定義するのに役立ち、波4の形成や波5の展開中に価格がその範囲内に留まることから、抵抗線としての役割も果たします。2つ目のトレンドラインは波2と波4を結びます。このラインはウォルフ波動のチャネル構造や対称性を明確にするために重要です。価格が反転ポイントに近づくにつれて、波5の予想される軌道のおおよその目安を示してくれます。
3つ目のトレンドラインは波1と波4を結びます。これは最初の2つとは異なる役割を持ちます。通常、このラインは取引終了の判断に使用されます。弱気ウォルフ波動では、波5が識別されて価格が予想通りの方向に動き始めたとき、波1から波4のラインが利確目標として機能します。市場がこのトレンドラインを越えた時点で取引は終了されます。これは予想された動きが完了し、パターンが完成したことを示すためです。
例string tline_1_3; string tline_2_4; string tline_1_4;
if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2_ll && wv4 < wv3_hh && wv3_hh >= fib_ext_1_2_127_2 && wv3_hh <= fib_ext_1_2_161_8) { ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_hh_t, wv1_hh); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_ll_t, wv2_ll); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_ll_t, wv2_ll, wv1_hh_t, wv1_hh, wv2_ll_t, wv2_ll); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); } perc_70 = StringFormat("70 PERCENT %d", j); ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_hh_t, wv1_hh - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p); fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j); ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_hh_t, fib_ext_1_2_161_8, wv2_ll_t, fib_ext_1_2_127_2); ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_hh_t, wv3_hh); ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3"); ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4); ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4"); ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue); tline_1_3 = StringFormat("TREND LINE WAVE 1 AND 3 %d", i); ObjectCreate(chart_id, tline_1_3, OBJ_TREND, 0, wv1_hh_t, wv1_hh, wv3_hh_t, wv3_hh); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_COLOR, clrBlue); tline_2_4 = StringFormat("TREND LINE WAVE 2 AND 4 %d", i); ObjectCreate(chart_id, tline_2_4, OBJ_TREND, 0, wv2_ll_t, wv2_ll, wv4_time, wv4); ObjectSetInteger(chart_id, tline_2_4, OBJPROP_COLOR, clrBlue); tline_1_4 = StringFormat("TREND LINE WAVE 1 AND 4 %d", i); ObjectCreate(chart_id, tline_1_4, OBJ_TREND, 0, wv1_hh_t, wv1_hh, wv4_time, wv4); ObjectSetInteger(chart_id, tline_1_4, OBJPROP_COLOR, clrBlue); }
出力

説明
文字列変数tline_1_3、tline_2_4、tline_1_4を最初に宣言する目的は、チャート上のトレンドラインを一意に識別するためです。tline_1_3には、波1と波3を結ぶトレンドラインが格納され、弱気ウォルフ波動パターンの上限を示します。ObjectCreate()を用いて、波1と波3の確認済み最高値を基にトレンドラインを作成し、視認性を高めるために青色で描画します。2つ目のトレンドラインは波2と波4を結び、tline_2_4に保持されます。波2の最安値(wv2_ll_t、wv2_ll)から波4のポイント(wv4_time、wv4)まで描画され、こちらも青色でスタイリングされます。3つ目のトレンドラインは波1と波4を結ぶもので、tline_1_4に保持されます。
しかし、この方法ではトレンドラインがあらかじめ決められた2つのアンカーポイント間のみに生成されるという問題があります。本来のウォルフ波動パターンでは、各ラインは2つ目のアンカーを越えて延長され、アンカーと価格の将来の相互作用を示す必要があります。たとえば、波1と波3を結ぶトレンドラインは単に2点間の短い線分ではなく、市場がそのラインを上抜けて再び下落するまでの間、波3を越えて将来まで続くことが想定されます。この延長部分によって、パターン上限と価格の関係が時間を通してより明確に示されます。
ウォルフ波動の対称性を維持し、波5の可能性のある位置を予測するためには、波2と波4を結ぶトレンドラインも、波1と波3のラインと同じ長さと方向に延長する必要があります。価格がこのラインに近づくと、取引終了目標として機能し、取引を完了するタイミングを示します。そのため、波4を越えてもラインを延長する必要があります。言い換えれば、3本のトレンドラインすべてを適切に将来まで延長することで、正確なパターン確認と取引準備が可能になります。
例
tline_1_3 = StringFormat("TREND LINE WAVE 1 AND 3 %d", i); ObjectCreate(chart_id,tline_1_3,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv3_hh_t,wv3_hh); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true); tline_2_4 = StringFormat("TREND LINE WAVE 2 AND 4 %d", i); ObjectCreate(chart_id,tline_2_4,OBJ_TREND,0,wv2_ll_t,wv2_ll,wv4_time,wv4); ObjectSetInteger(chart_id, tline_2_4, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true); tline_1_4 = StringFormat("TREND LINE WAVE 1 AND 4 %d", i); ObjectCreate(chart_id,tline_1_4,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv4_time,wv4); ObjectSetInteger(chart_id, tline_1_4, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true);

OBJPROP_RAY_RIGHT属性は、トレンドラインをアンカーポイントの先まで延長する機能を持ちますが、これも問題となる場合があります。この機能はラインの視認性を維持するうえで便利ですが、ウォルフ波動分析で求められる正確な挙動とは一致しません。今回の状況では、トレンドラインを無限に延長したいわけではありません。むしろ、特定の条件が満たされたときに、ラインが指定された時点で停止することが必要です。
たとえば、波1と波3を結ぶトレンドラインは、市場がこのラインを上抜けた後に再び下回るまでの範囲でのみ延長されるべきです。こうすることで、精度が維持され、チャート上に不要である可能性のある余計なトレンドラインが表示されることを防げます。各ラインの終了時刻を実際の価格の動きに基づいて指定することで、ウォルフ波動パターンの視覚的構造はより正確で、より整理された状態になります。
例tline_1_3 = StringFormat("TREND LINE WAVE 1 AND 3 %d", i); if(ObjectCreate(chart_id,tline_1_3,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv3_hh_t,wv3_hh)) { ObjectSetInteger(chart_id, tline_1_3, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); } tline_2_4 = StringFormat("TREND LINE WAVE 2 AND 4 %d", i); if(ObjectCreate(chart_id,tline_2_4,OBJ_TREND,0,wv2_ll_t,wv2_ll,wv4_time,wv4)) { ObjectSetInteger(chart_id, tline_2_4, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_2_4, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(chart_id, tline_2_4, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); } tline_1_4 = StringFormat("TREND LINE WAVE 1 AND 4 %d", i); if(ObjectCreate(chart_id,tline_1_4,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv4_time,wv4)) { ObjectSetInteger(chart_id, tline_1_4, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_1_4, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(chart_id, tline_1_4, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); }

説明
このコードは、すべての時間軸で非表示にする属性を設定することで、チャート上には表示されない内部利用用の見えないトレンドラインを生成します。そして、その後この波動1~3のトレンドラインを監視し、ローソク足がこのラインの上で終値を付けた後に下で終値を付けるといった重要な価格変動を検出します。
例double t_1_3_values; double t_2_4_values; string tline_1_3_visible; string tline_2_4_visible;
for(int m = l; m < bars_check - 2; m++) { t_1_3_values = ObjectGetValueByTime(chart_id, tline_1_3, time[m], 0); t_2_4_values = ObjectGetValueByTime(chart_id, tline_2_4, time[m], 0); if(close[m] > open[m] && open[m] < t_1_3_values && close[m] > t_1_3_values && time[m] > time[l+4]) { tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i); ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[m],t_1_3_values); ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue); tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i); ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[m],t_2_4_values); ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue); break; } }
出力

説明
このコードのセクションは、波1と波3を結ぶトレンドラインを価格が上抜けするタイミングを検出するために、波4以降のバーをチェックします。各バーの時点でのトレンドラインの価格レベルを取得するために変数を使用し、始値がトレンドラインの下で、終値がその上にある強気のローソク足を探します。これは波4の少なくとも4バー後に発生する必要があるブレイクアウトを示します。ブレイクアウトが検出されると、2本の追加の可視トレンドラインが生成され、StringFormatを使用して動的に名前が付けられます。チャート上には、波1からブレイクアウトポイントを結ぶラインと、波2から同じポイントを結ぶラインが表示されます。これにより、価格が波1~3のトレンドラインを上抜けた正確なバーを明確に視覚化し、ウォルフ波動パターンの進行状況をよりわかりやすく監視し、評価することができます。
しかし、これは最終的な目的ではありません。市場が波1~3のトレンドラインを上抜けて終値を付けた際に生成されるこれらの可視トレンドラインは、ブレイクアウトを強調するのに役立つものの、まだウォルフ波動の構造を完成させるものではありません。本来の目的は、市場が波1~3のトレンドラインを下抜けて終値を付けるまで、これらのラインを延長し続けることにあります。
例double t_1_3_values_2;for(int m = l; m < bars_check - 2; m++) { t_1_3_values = ObjectGetValueByTime(chart_id, tline_1_3, time[m], 0); if(close[m] > open[m] && open[m] < t_1_3_values && close[m] > t_1_3_values && time[m] > time[l+4]) { for(int n = m; n < bars_check - 1; n++) { t_1_3_values_2 = ObjectGetValueByTime(chart_id, tline_1_3, time[n], 0); t_2_4_values = ObjectGetValueByTime(chart_id, tline_2_4, time[n], 0); if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2) { tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i); ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n],t_1_3_values_2); ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue); tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i); ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[n],t_2_4_values); ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue); break; } } break; } }
出力

説明
しかし、これは最終的な目的ではありません。市場が波1~3のトレンドラインを上抜けて終値を付けた際に生成されるこれらの可視トレンドラインは、ブレイクアウトを強調するのに役立つものの、まだウォルフ波動の構造を完成させるものではありません。本来の目的は、市場が波1~3のトレンドラインを下抜けて終値を付けるまで、これらのラインを延長し続けることにあります。特に、内部のforループでは、変数t_1_3_values_2を使用して、後の時点における波1~3トレンドラインの価格レベルを保持します。このループは、トレンドラインを上抜けたバー(m)以降から先をスキャンし始めます。そして、各バーnにおいてObjectGetValueByTimeを用いてその時点のトレンドラインの値を取得し、t_1_3_values_2に格納します。
その後、「if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2)」という条件式によって、トレンドラインの上で始まり下で終わる陰線を検出します。これは、価格が再びトレンドラインを下抜けたことを示しており、このポイントはしばしば波5の終点、すなわち予想される価格反転の始まりを示す重要な位置となります。この2度目のクロスが検出されたらすぐに、2本の新しい可視トレンドラインが生成されます。1本は波1からこの新しい位置を結び、もう1本は波2から同じ位置を結びます。これにより、ウォルフ波動の形成初期からブレイクアウト、そして反転に至るまでの流れをより正確かつ包括的に視覚化することができます。これらの新しいトレンドラインは、以前のラインに代わって、価格動向がパターン全体を確定させた正確なポイントまで延長されます。
次のステップは、波5の価格レベルを特定することです。これを行うには、市場が最初に波1~3トレンドラインを上抜けて終値を付けた時点と、再びそのラインを下抜けて終値を付けた時点の2つの重要なポイントの間で、最高値を求める必要があります。通常、この範囲はウォルフ波動パターンの最終段階を示し、予想される反転が始まる前に価格が最後の上昇を見せる領域となります。この期間内で最高値を特定することで、波5の頂点を正確に把握することが可能になります。
例int cross_bars; int cross_bars_highest; string wv5_txt;
if(close[m] > open[m] && open[m] < t_1_3_values && close[m] > t_1_3_values && time[m] > time[l+4]) { for(int n = m; n < bars_check - 1; n++) { t_1_3_values_2 = ObjectGetValueByTime(chart_id, tline_1_3, time[n], 0); t_2_4_values = ObjectGetValueByTime(chart_id, tline_2_4, time[n], 0); if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2) { tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i); ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n],t_1_3_values_2); ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue); tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i); ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[n],t_2_4_values); ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue); cross_bars = Bars(_Symbol,timeframe,time[n], time[m]); cross_bars_highest = ArrayMaximum(high,m,cross_bars); wv5_txt = StringFormat("WAVE 5 %d", i); ObjectCreate(chart_id, wv5_txt, OBJ_TEXT, 0, time[cross_bars_highest], high[cross_bars_highest]); ObjectSetString(chart_id, wv5_txt, OBJPROP_TEXT, "WV5"); ObjectSetInteger(chart_id, wv5_txt, OBJPROP_COLOR, clrBlue); break; } }
説明
このコードセクションでは、ウォルフ波動パターンの波5を特定し、ラベル付けします。アルゴリズムは、価格が波1~3のトレンドラインを上抜けた後に再び下抜けるまでのブレイクアウト点と反転点の間のバー数を算出し、その間で波動5が出現するおおよそのタイミングを判断します。コードはインデックスmから開始し、変数cross_barsに格納されたバー数分を走査します。このとき、ArrayMaximum()関数を使用して配列「high[]」の中から最も高い高値を検索します。この関数によって返されるインデックスが、波5の頂点を示す位置となります。このインデックスは変数cross_bars_highestに保持されます。
次に、この特定された高値を「WV5」としてラベル付けするため、チャート上にテキストオブジェクトを生成します。ラベルはcross_bars_highestのインデックスに対応する日時と価格をもとに配置され、色はこれまでの波動ラベルと同様に青色に設定されます。これにより、ブレイクアウトから反転までの間で最も高い地点に波5が正しくマークされ、ウォルフ波動の構造がチャート上で視覚的に完成します。
フィボナッチエクスパンション
ご存じのとおり、すでに説明したように、波5は、波3と波4によって形成される価格変動の拡張範囲の中に位置しなければなりません。一般的に、この範囲は波3と波4の価格変動幅の127.2%から161.8%の間に収まることが多いとされています。
例input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // TIMEFRAME input double max_fib_ext_wv12 = 161.8; // WAVE 1 AND 2 FIBO EXTENSION MAX LEVEL input double min_fib_ext_wv12 = 127.2; // WAVE 1 AND 2 FIBO EXTENSION MIN LEVEL input double max_fib_ext_wv34 = 120.0; // WAVE 3 AND 4 FIBO EXTENSION MAX LEVEL input double min_fib_ext_wv34 = 200.0; // WAVE 3 AND 4 FIBO EXTENSION MIN LEVEL string fib_ext_3_4; double fib_ext_3_4_161_8; double fib_ext_3_4_127_2; string fib_ext_3_4_168_127; string fib_ext_range_3_4; int no_bars;
for(int l = k; l < bars_check - 7; l++) { if(IsSwingLow(low, l, 7) && time[l] > wv3_time) { wv4 = low[l]; wv4_time = time[l]; wv4_txt = StringFormat("WAVE 4 %d", l); wv3_wv4_bars = Bars(_Symbol, timeframe, wv3_time, wv4_time); wv3_highest_index = ArrayMaximum(high, k, wv3_wv4_bars); wv3_hh = high[wv3_highest_index]; wv3_hh_t = time[wv3_highest_index]; wv2_wv3_bars = Bars(_Symbol, timeframe, wv2_time, wv3_time); wv2_lowest_index = ArrayMinimum(low, j, wv2_wv3_bars); wv2_ll = low[wv2_lowest_index]; wv2_ll_t = time[wv2_lowest_index]; wv1_wv2_bars = Bars(_Symbol, timeframe, wv1_time, wv2_time); wv1_highest_index = ArrayMaximum(high, i, wv1_wv2_bars); wv1_hh = high[wv1_highest_index]; wv1_hh_t = time[wv1_highest_index]; wv1_wv2_size = MathAbs(wv1_hh - wv2_ll); wv1_wv2_70p = (wv1_wv2_size / 100) * 70; fib_ext_1_2_127_2 = MathAbs((((wv1_hh - wv2_ll) / 100) * (min_fib_ext_wv12 - 100)) + wv1_hh); fib_ext_1_2_161_8 = MathAbs((((wv1_hh - wv2_ll) / 100) * (max_fib_ext_wv12 - 100)) + wv1_hh); wv3_wv4_size = MathAbs(wv3_hh - wv4); if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2_ll && wv4 < wv3_hh && wv3_hh >= fib_ext_1_2_127_2 && wv3_hh <= fib_ext_1_2_161_8) { tline_1_3 = StringFormat("TREND LINE WAVE 1 AND 3 %d", i); if(ObjectCreate(chart_id,tline_1_3,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv3_hh_t,wv3_hh)) { ObjectSetInteger(chart_id, tline_1_3, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(chart_id, tline_1_3, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); } tline_2_4 = StringFormat("TREND LINE WAVE 2 AND 4 %d", i); if(ObjectCreate(chart_id,tline_2_4,OBJ_TREND,0,wv2_ll_t,wv2_ll,wv4_time,wv4)) { ObjectSetInteger(chart_id, tline_2_4, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_2_4, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(chart_id, tline_2_4, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); } tline_1_4 = StringFormat("TREND LINE WAVE 1 AND 4 %d", i); if(ObjectCreate(chart_id,tline_1_4,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv4_time,wv4)) { ObjectSetInteger(chart_id, tline_1_4, OBJPROP_COLOR, clrBlue); ObjectSetInteger(chart_id, tline_1_4, OBJPROP_RAY_RIGHT, true); ObjectSetInteger(chart_id, tline_1_4, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); } fib_ext_3_4 = StringFormat("FIB EXTENSION WAVE 3 AND 4 %d", i); fib_ext_3_4_127_2 = MathAbs((((wv3_hh - wv4) / 100) * (min_fib_ext_wv34 - 100)) + wv3_hh); fib_ext_3_4_161_8 = MathAbs((((wv3_hh - wv4) / 100) * (max_fib_ext_wv34 - 100)) + wv3_hh); fib_ext_3_4_168_127 = StringFormat("FIB EXTENSION wv3 wv4 %d", i); for(int m = l; m < bars_check - 2; m++) { t_1_3_values = ObjectGetValueByTime(chart_id, tline_1_3, time[m], 0); if(close[m] > open[m] && open[m] < t_1_3_values && close[m] > t_1_3_values && time[m] > time[l+4]) { for(int n = m; n < bars_check - 1; n++) { t_1_3_values_2 = ObjectGetValueByTime(chart_id, tline_1_3, time[n], 0); t_2_4_values = ObjectGetValueByTime(chart_id, tline_2_4, time[n], 0); no_bars = Bars(_Symbol, timeframe, wv3_hh_t, time[n]); cross_bars = Bars(_Symbol,timeframe,time[n], time[m]); cross_bars_highest = ArrayMaximum(high,m,cross_bars); if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2 && no_bars < 100 && time[n] > time[m] && high[cross_bars_highest] >= fib_ext_3_4_127_2 && high[cross_bars_highest] <= fib_ext_3_4_161_8) { ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_hh_t, wv1_hh); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_ll_t, wv2_ll); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_ll_t, wv2_ll, wv1_hh_t, wv1_hh, wv2_ll_t, wv2_ll); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); } perc_70 = StringFormat("70 PERCENT %d", j); ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_hh_t, wv1_hh - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p); fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j); ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_hh_t, fib_ext_1_2_161_8, wv2_ll_t, fib_ext_1_2_127_2); ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_hh_t, wv3_hh); ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3"); ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4); ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4"); ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue); tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i); ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n],t_1_3_values_2); ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue); tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i); ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[n],t_2_4_values); ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue); wv5_txt = StringFormat("WAVE 5 %d", i); ObjectCreate(chart_id, wv5_txt, OBJ_TEXT, 0, time[cross_bars_highest], high[cross_bars_highest]); ObjectSetString(chart_id, wv5_txt, OBJPROP_TEXT, "WV5"); ObjectSetInteger(chart_id, wv5_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, fib_ext_3_4,OBJ_EXPANSION, 0,wv4_time, wv4,wv3_hh_t,wv3_hh,wv4_time,wv4); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id,fib_ext_3_4,OBJPROP_LEVELCOLOR,i,clrBlue); ObjectSetInteger(chart_id,fib_ext_3_4,OBJPROP_COLOR,clrBlue); } fib_ext_range_3_4 = StringFormat("Fibo EXPENSION RANGE WV3 WV4 %d", i); ObjectCreate(chart_id,fib_ext_range_3_4,OBJ_RECTANGLE,0,wv3_hh_t,fib_ext_3_4_127_2,time[cross_bars_highest],fib_ext_3_4_161_8); break; } } break; } } } break; } }
出力

説明
このコードでは、波3と波4のフィボナッチエクスパンションを使用しています。波3から価格が波1~3のトレンドラインを下抜けて終値を付けるまでの間のバー数を計算し、さらにフィボナッチレベルを求めることで、パターンの構造的な整合性とコンパクトさを確認します。EAは、条件ブロック内で特定の陰線を探します。この陰線は、一度波1~3のトレンドラインを上抜けた後、再びその下で終値を付けるものでなければなりません。反転は100本未満のバー内で発生する必要があり、さらにブレイクアウトと反転の間で記録された最高値が、フィボナッチエクスパンションの許容範囲内に収まっていることが条件となります。これらすべての要件が満たされた場合、波5は有効と見なされます。
次に、波3と波4を基準脚としてフィボナッチエクスパンションオブジェクトを描画します。視認性を高めるため、0%、127.2%、161.8%の3つの拡張レベルがすべて青色で表示されます。さらに、フィボナッチの目標ゾーンを視覚的に強調するために、長方形も描画されます。重要なのは、この条件ブロックが、拡張ゾーン、ラベル、トレンドラインなど、関連するすべてのチャートオブジェクトを生成する唯一の部分であるという点です。これにより、誤検出を減らし、ウォルフ波動検出ロジックの信頼性を高めることができます。すべてのルールが満たされた場合のみ、完全なパターンがタグ付けされるよう設計されています。
波1から波4までのトレンドラインを描く
EAに取引を実行させる前に、波1と波4を結ぶトレンドラインを描く必要があります。このトレンドラインは、ウォルフ波動パターンにおいて、特に、エグジット(決済)ポイントの計画において中心的な要素となります。int no_wv1_n_bars; int no_n_c_bars; string tline_1_4_visible; double t_1_4_values; string tline_1_4_visible_2; if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2 && no_bars < 100 && time[n] > time[m] && high[cross_bars_highest] >= fib_ext_3_4_127_2 && high[cross_bars_highest] <= fib_ext_3_4_161_8) { ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_hh_t, wv1_hh); ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1"); ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_ll_t, wv2_ll); ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2"); ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue); fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i); ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_ll_t, wv2_ll, wv1_hh_t, wv1_hh, wv2_ll_t, wv2_ll); ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue); } perc_70 = StringFormat("70 PERCENT %d", j); ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_hh_t, wv1_hh - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p); fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j); ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_hh_t, fib_ext_1_2_161_8, wv2_ll_t, fib_ext_1_2_127_2); ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_hh_t, wv3_hh); ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3"); ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4); ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4"); ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue); tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i); ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n],t_1_3_values_2); ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue); tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i); ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[n],t_2_4_values); ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue); wv5_txt = StringFormat("WAVE 5 %d", i); ObjectCreate(chart_id, wv5_txt, OBJ_TEXT, 0, time[cross_bars_highest], high[cross_bars_highest]); ObjectSetString(chart_id, wv5_txt, OBJPROP_TEXT, "WV5"); ObjectSetInteger(chart_id, wv5_txt, OBJPROP_COLOR, clrBlue); ObjectCreate(chart_id, fib_ext_3_4,OBJ_EXPANSION, 0,wv4_time, wv4,wv3_hh_t,wv3_hh,wv4_time,wv4); for(int i = 0; i <= 2; i++) { ObjectSetInteger(chart_id,fib_ext_3_4,OBJPROP_LEVELCOLOR,i,clrBlue); ObjectSetInteger(chart_id,fib_ext_3_4,OBJPROP_COLOR,clrBlue); } fib_ext_range_3_4 = StringFormat("Fibo EXPENSION RANGE WV3 WV4 %d", i); ObjectCreate(chart_id,fib_ext_range_3_4,OBJ_RECTANGLE,0,wv3_hh_t,fib_ext_3_4_127_2,time[cross_bars_highest],fib_ext_3_4_161_8); no_wv1_n_bars = Bars(_Symbol, timeframe, wv1_hh_t, time[n]); no_n_c_bars = Bars(_Symbol, timeframe, time[n], TimeCurrent()); if(no_n_c_bars <= no_wv1_n_bars) { t_1_4_values = ObjectGetValueByTime(chart_id, tline_1_4, TimeCurrent(), 0); tline_1_4_visible = "TL WAVE 1 AND 4 Visible"; ObjectCreate(chart_id,tline_1_4_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,TimeCurrent(),t_1_4_values); ObjectSetInteger(chart_id,tline_1_4_visible,OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,tline_1_4_visible,OBJPROP_COLOR,clrBlue); } if(no_n_c_bars > no_wv1_n_bars) { ObjectDelete(chart_id,tline_1_4_visible); t_1_4_values = ObjectGetValueByTime(chart_id, tline_1_4, time[n + no_wv1_n_bars], 0); tline_1_4_visible_2 = StringFormat("TL WAVE 1 AND 4 DISPLAY %d", i); ObjectCreate(chart_id,tline_1_4_visible_2,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n+no_wv1_n_bars],t_1_4_values); ObjectSetInteger(chart_id,tline_1_4_visible_2,OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,tline_1_4_visible_2,OBJPROP_COLOR,clrBlue); } break; }
説明
2つの重要なポイント間のバー数は、変数「no_wv1_n_bars」と「no_n_c_bars」を使用して算出されます。具体的には、no_wv1_n_barsは「波1のタイムスタンプ」と、市場が再びトレンドラインを下抜けて反転した時点の「タイムスタンプ」との間に存在するバー数を求めます。一方で、no_n_c_barsは「現在のバー」と「反転ポイント(波5)」との間にあるバー数をカウントします。この比較により、波1から波4を結ぶトレンドラインが、将来の特定バーまで延長すべきか、それとも現在の市場位置までに留めるべきかを判断します。
現在のバーが、波1と反転ポイントが定義する範囲内にある(no_n_c_bars <= no_wv1_n_bars)場合は、波1から現在の市場時点までを結ぶトレンドラインが描画されます。このトレンドラインは「TL WAVE 1 AND 4 Visible」というラベルを持ち、ObjectGetValueByTimeによって現在時点のトレンドライン値が取得されます。視認性を高めるため、ラインの色は青で、スタイルは破線として表示されます。
一方、反転ポイント以降のバー数が波動1と反転の間隔よりも多い場合(no_n_c_bars > no_wv1_n_bars)には、以前に描かれたラインはObjectDeleteを使って削除されます。その後、初期の予測と同じ長さを維持できるよう、新しいトレンドラインが再作成されます。このラインは波1から未来の特定時点(time[n + no_wv1_n_bars])までを結び、動的なラベルを持ち、同様に青色の破線で表示されます。このアルゴリズムにより、リアルタイムのチャート更新に追随しながらも、波1から波4までのトレンドラインが常に一貫性を保つようになっています。
取引執行
ここまでで、ウォルフ波動パターンにおける波1~波5のすべてを正確に認識し、さらに、波1~3、波2~4、波1~4を結ぶ3本の主要トレンドラインも特定することができました。これらのトレンドラインは、パターンを視覚的に確認できるだけでなく、実際の取引判断の基準としても利用されます。
次のステップは、定義された構造に基づいて取引を実行することです。弱気ウォルフ波動パターンにおけるエントリー条件は、価格がどのように波動1~3のトレンドライン付近で動くかに密接に関係しています。具体的には、まずローソク足が波1~3のトレンドラインを一度上抜ける必要があります。これは上昇トレンドの勢いが弱まりつつあることを示唆します。その後、再びトレンドラインを下抜けて終値を付ける動きが確認された時点で、反転(下降)局面に入る可能性が高まります。この2段階の確認(ブレイクアウト → 再クロス)が完了するまでは、EAは取引を開始してはいけません。
例
#include <Trade/Trade.mqh> CTrade trade; input int MagicNumber = 6160; input double lot_size = 0.01; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // TIMEFRAME input double max_fib_ext_wv12 = 161.8; // WAVE 1 AND 2 FIBO EXTENSION MAX LEVEL input double min_fib_ext_wv12 = 127.2; // WAVE 1 AND 2 FIBO EXTENSION MIN LEVEL input double max_fib_ext_wv34 = 200.0; // WAVE 3 AND 4 FIBO EXTENSION MAX LEVEL input double min_fib_ext_wv34 = 120.0; // WAVE 3 AND 4 FIBO EXTENSION MIN LEVEL
datetime time_exe[]; datetime lastTradeBarTime = 0; double ask_price; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- trade.SetExpertMagicNumber(MagicNumber); ArraySetAsSeries(time_exe,true); //--- return(INIT_SUCCEEDED); }
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- total_symbol_bars = Bars(_Symbol, timeframe); time_bar = iTime(_Symbol,timeframe,0); CopyOpen(_Symbol, timeframe, time_bar, bars_check, open); CopyClose(_Symbol, timeframe, time_bar, bars_check, close); CopyLow(_Symbol, timeframe, time_bar, bars_check, low); CopyHigh(_Symbol, timeframe, time_bar, bars_check, high); CopyTime(_Symbol, timeframe, time_bar, bars_check, time); CopyTime(_Symbol, timeframe, 0, 2, time_exe); datetime currentBarTime = iTime(_Symbol, timeframe, 0); ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
if(time[n] == time_exe[1] && currentBarTime != lastTradeBarTime) { trade.Sell(lot_size,_Symbol,ask_price, high[cross_bars_highest],wv2_ll); lastTradeBarTime = currentBarTime; }
出力

説明
最初の行の#include文では、MQL5の組み込み取引ライブラリがインポートされます。これにより、CTradeクラスが取引管理機能へアクセスできるようになります。ライブラリのインクルード後に、tradeという名前のCTradeクラスのインスタンスが生成されます。このオブジェクトは、売買注文の実行、テイクプロフィットやストップロスの設定、注文の管理など、すべてのトレード操作をおこないます。次に、入力変数としてMagicNumberとlot_sizeが定義されます。MagicNumberはこのEAの取引を一意に識別するための番号であり、同じ口座内で他のEAが発注した取引と区別するのに役立ちます。lot_sizeは取引数量を表し、この場合は0.01ロットとして設定されています。
次に、time_exe[]、lastTradeBarTime、ask_priceなどの変数が宣言されます。time_exe配列は、最近のバーの時間データを保持します。lastTradeBarTimeは初期値を0に設定し、同一バー内で複数回取引が実行されるのを防止するために使用されます。ask_priceは、現在のAsk価格を格納します。これは、売り注文を出す際の基準価格となります。trade.SetExpertMagicNumber(MagicNumber);の行では、取引オブジェクトにMagicNumberが割り当てられ、EAが発注した取引を後から識別できるようにします。さらに、「ArraySetAsSeries(time_exe, true);」によって、time_exe配列が時系列配列(最新バーがインデックス0)として扱われるように設定されます。
次に、「CopyTime(_Symbol, timeframe, 0, 2, time_exe);」によって、直近2本のバーの時刻データがtime_exe配列にコピーされます。続いて、「datetime currentBarTime = iTime(_Symbol, timeframe, 0);」が実行され、現在形成中のバーの開始時間を取得します。また、「ask_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);」によって、現在の取引銘柄のAsk価格(売りエントリー可能な価格)を取得します。これは売り注文を出すために不可欠な値です。
次の条件文「if (time[n] == time_exe[1] && currentBarTime != lastTradeBarTime)」では、time[n]に格納された時刻が1本前のバーの開始時間(time_exe[1])と一致しているか、 またcurrentBarTimeがlastTradeBarTimeと異なっているかどうかどうかを確認します。条件を満たす場合、EAはtrade.Sell()を使って売り注文を出します。この注文は現在のask_priceで出され、ストップロスはhigh[cross_bars_highest](おそらく波5の高値)に設定、テイクプロフィットはwv2_ll(波2の安値、すなわち目標値の可能性)に設定されます。最後に、lastTradeBarTimeに現在のバーの時刻を代入し、次のバーまで同じロウソク足内での再エントリーを防止します。
最後にある重要な項目は、取引のエグジット処理です。ウォルフ波動パターンに基づいて取引を実行した後は、適切な決済ロジック(エグジット戦略)を組み込む必要があります。価格が波1と波4を結ぶトレンドラインに到達した際、自動的にポジションを決済(クローズ)するように設定するのが理想です。このトレンドラインは理論上、価格反転が予想される領域であり、最も合理的なテイクプロフィットポイントとなります。 さらに、過度なポジション保有を避けるための保護処理も重要です。もし価格が一定のバー数(ローソク足数)以内にトレンドラインへ到達しない場合、EAはその時点でポジションをクローズすべきです。この仕組みによって、パターンが失敗した場合や市場状況が変化した場合のリスクを最小化し、不要な損失や長期的な含み損を回避することができます。
例double low_m[]; double high_m[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- trade.SetExpertMagicNumber(MagicNumber); ArraySetAsSeries(time_exe,true); ArraySetAsSeries(low_m,true); ArraySetAsSeries(high_m,true); //--- return(INIT_SUCCEEDED); }
CopyLow(_Symbol, timeframe, 0, 3, low_m); CopyHigh(_Symbol, timeframe, 0, 3, high_m);
if(no_n_c_bars <= no_wv1_n_bars) { t_1_4_values = ObjectGetValueByTime(chart_id, tline_1_4, TimeCurrent(), 0); tline_1_4_visible = "TL WAVE 1 AND 4 Visible"; ObjectCreate(chart_id,tline_1_4_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,TimeCurrent(),t_1_4_values); ObjectSetInteger(chart_id,tline_1_4_visible,OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,tline_1_4_visible,OBJPROP_COLOR,clrBlue); if(time[n] == time_exe[1] && currentBarTime != lastTradeBarTime) { trade.Sell(lot_size,_Symbol,ask_price, high[cross_bars_highest],wv2_ll); lastTradeBarTime = currentBarTime; } for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); datetime positionTime = 0; if(PositionSelectByTicket(ticket)) { if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { positionTime = (datetime)PositionGetInteger(POSITION_TIME); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { double pos_bars = Bars(_Symbol, timeframe, positionTime, TimeCurrent()); if((ask_price < t_1_4_values || low_m[1] < t_1_4_values || no_n_c_bars == no_wv1_n_bars) && no_n_c_bars == pos_bars+1) { trade.PositionClose(ticket); } } } } } } if(no_n_c_bars > no_wv1_n_bars) { ObjectDelete(chart_id,tline_1_4_visible); t_1_4_values = ObjectGetValueByTime(chart_id, tline_1_4, time[n + no_wv1_n_bars], 0); tline_1_4_visible_2 = StringFormat("TL WAVE 1 AND 4 DISPLAY %d", i); ObjectCreate(chart_id,tline_1_4_visible_2,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n+no_wv1_n_bars],t_1_4_values); ObjectSetInteger(chart_id,tline_1_4_visible_2,OBJPROP_STYLE,STYLE_DASH); ObjectSetInteger(chart_id,tline_1_4_visible_2,OBJPROP_COLOR,clrBlue); }
出力

説明
コードの最初のセクションでは、「double low_m[];」と「double high_m[];」の2つの配列が宣言されています。これらの配列には、チャート上の直近の安値と高値が保持されます。MetaTrader 5の標準的なチャート動作と同様に、「ArraySetAsSeries(low_m, true);」と「ArraySetAsSeries(high_m, true);」の行によって、配列のインデックスが反転され、インデックス0が常に最新バーに対応するようになります。これは、直近または現在の価格を重要なトレンドラインの値と比較する際に必要です。
次に、「CopyLow(_Symbol, timeframe, 0, 3, low_m);」と「CopyHigh(_Symbol, timeframe, 0, 3, high_m);」によって、直近3本の安値と高値を、それぞれlow_mとhigh_mに格納します。この時点で、価格が特定のトレンドライン(例:波動1~4のトレンドライン)に接触したか、下抜けたかを判断します。
ポジションが売り取引であれば、これは弱気ウォルフ波動パターンに従う場合です。コードは、「Bars(_Symbol, timeframe, positionTime, TimeCurrent())」を使用して、取引が開始されてから何本のバーが経過したかどうかを計算します。その後、市場がエグジットポイントに到達したかどうかを判断する条件が評価されます。具体的には、次のいずれかの条件を満たす場合です。
- 現在のAsk価格が波動1–4のトレンドライン(t_1_4_values)より下にある
- 直前バーの安値(low_m[1])がトレンドラインを下抜けた
- エントリー後のバー数が、波動1–4のトレンドラインに到達するのに必要なバー数と一致する(no_n_c_bars == no_wv1_n_bars)
- ポジションがちょうどそのバー数だけ開かれている(no_n_c_bars == pos_bars + 1)
これらすべての条件が満たされた場合、EAはPositionCloseを使ってポジションをクローズします。この条件により、取引が予想されるエグジットポイントに到達した、もしくは保持期間が長すぎる場合に対応できます。 EAが最新のウォルフ波動パターンに正しく取引を紐付けるために、「no_n_c_bars == pos_bars + 1」という条件が使用されます。このEAでは、一定期間のバーを対象に複数のウォルフ波動パターンを検出しますが、取引は「time[n] == time_exe[1]」のタイミングでのみ実行されます。これにより、最新パターンの確認と同じ瞬間に取引が開始されることが保証されます。
取引が開始されてから経過したバーの本数は、pos_barsという変数で表されます。この値は、シグナル発生と取引実行のタイミングを一致させるために計算されます。一方で、no_n_c_barsは、現在の時間とウォルフ波動パターン確認ポイント(time[n])の間で経過したバー数を示します。「no_n_c_bars == pos_bars + 1」であれば、EAはその取引が最新のウォルフ波パターンによって発生したものであり、以前のパターンによるものではないことを認識します。
これは非常に重要です。なぜなら、EAはチャート上で複数のウォルフ波動パターンを検出する可能性がありますが、最新パターンに対応する取引のみを正確に扱い、閉じるべきだからです。この条件がなければ、EAは誤って以前のシグナルに基づく取引をクローズしてしまったり、処理を混乱させたりする可能性があります。EAはこの整合性を確認することで、取引処理の正確性を保証し、シグナルの衝突を防ぎ、実行やリスク管理の一貫性を維持することができます。
強気ウォルフ波動パターンの特定
強気ウォルフ波動は、先に説明した弱気ウォルフ波動の逆のパターンです。構造やロジックは非常に似ているため、すべての詳細を再度説明するのではなく、ここでは、逆になっている部分や異なる部分に焦点を当てます。つまり、売りではなく、買いの機会を示唆する5波構造を探すことになります。
強気ウォルフ波動における波1は、最初の基点としてスイングローでなければなりません。波2はスイングハイを形成し、波3は波1を下回らず、波2の後に再びスイングローを作ります。波4はスイングハイで、通常は波2より低くなります。そして波5が最後のスイングローを形成して、強気ウォルフ波動のセットアップを完成させます。
波1から波3は、隣接する波動の間で最も低いポイントまたは最も高いポイントである必要があります。波5は、波3と波4の127.2%~161.8%のフィボナッチエクスパンションの範囲内に位置しなければなりません。この範囲を変えることで、パターン検出の感度をユーザーが調整することも可能です。エントリーの信頼性を高めるため、価格はまず波1~3のトレンドラインを下抜け、その後上抜ける必要があります。波動1~4のトレンドラインは、利確目標としてもよく利用されます。また、パターン認識や取引のエグジット計画をサポートするために、波1~3、波2~4、波1~4を結ぶ重要なトレンドラインが自動的に描画されます。

結論
本記事では、MQL5で作成したEAが、強気ウォルフ波動と弱気ウォルフ波動の両方を認識できる仕組みについて説明しました。スイングハイやスイングローを用いて5波構造を特定する方法、フィボナッチエクスパンションを適用する方法、重要なトレンドラインを作成する方法、そしてすべての条件が揃った場合にのみ取引を実行する方法を解説しました。フィボナッチ範囲は一般的に127.2%~161.8%ですが、個々の戦略に合わせて変更することも可能です。このプロジェクトは、複雑なチャートパターンの自動化に加え、パターンベースの高度な取引システムを構築するための堅固な基盤を提供します。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/18884
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5取引ツール(第6回):パルスアニメーションとコントロールを備えたダイナミックホログラフィックダッシュボード
MQL5での取引戦略の自動化(第24回):リスク管理とトレーリングストップを備えたロンドンセッションブレイクアウトシステム
プライスアクション分析ツールキットの開発(第33回):Candle Range Theory Tool
知っておくべきMQL5ウィザードのテクニック(第76回): Awesome Oscillatorのパターンとエンベロープチャネルを教師あり学習で利用する
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索