ラリー・ウィリアムズの『市場の秘密』(第1回):MQL5でスイングストラクチャーインジケーターを構築する
はじめに
金融市場は波のように動きます。価格は日々変動し、そうした動きは重要な転換点を示すことがあります。ラリー・ウィリアムズは、これらのポイントを読み取るためのシンプルで再現性のある手法を説明しています。この理解に基づき、市場構造を明確にし、トレーダーが取引を開始するタイミングや取引を避けるタイミングを判断するのに役立つツールを開発することができます。
本記事では、MQL5でマーケットストラクチャーインジケーターを作成します。これはラリー・ウィリアムズの市場スイングに関する考え方に基づいています。このインジケーターは短期的なスイングポイントを検出し、それらを中期的なポイントと長期的なポイントに分類します。これにより、価格がどのように変動しているかが明確に把握できます。
本記事は、ラリー・ウィリアムズの市場概念に関する連載の一部です。本連載の各記事では、一度に一つの概念に焦点を当て、それを実用的なMQL5ツールに変換していきます。これにより学習が簡素化され、論理的な流れを段階的に理解しやすくなります。
ラリー・ウィリアムズとは誰なのか?
ラリー・ウィリアムズは、トレーディング業界で最も尊敬されている人物の一人です。彼は長年の実績を持つ株式・商品トレーダーです。彼はまた、多くのトレーディング関連書籍の著者でもあります。彼の最も有名な発表の1つは、Long-Term Secrets to Short-Term Tradingです。多くのトレーダーが、市場構造とスイング分析に対する実践的なアプローチを理由にこの本を研究しており、それがこの記事の基礎となっています。
ラリー・ウィリアムズは、1987年に先物取引ワールドカップ選手権で優勝したことで、大きな名声を得ました。そのコンテストで、彼は12か月以内に1万ドルを100万ドル以上に増やしました。その記録を破った者はこれまで誰もいません。10年後、彼の娘であるミシェル・ウィリアムズも同じコンテストに出場し、優勝しました。これは、彼のアイデアが他者によって学習され、うまく応用され得ることを示しました。
ウイリアムズの研究は、現代のテクニカル取引にも影響を与え続けています。そのため、彼はこの市場構造インジケーターのような構造化ツールを設計する際の優れた指針となります。
ウィリアムズの市場構造ロジックを理解する
ラリー・ウィリアムズが提唱する市場構造は、時間の経過とともに価格スイングがどのように形成されるかに基づいています。こうしたスイングは、市場が上下に動くにつれて自然に発生するものです。こうしたスイングを理解することで、より自信を持ってトレンドを把握し、市場が転換期を迎えているのか、それとも動き続けているのかを判断できるようになります。この方法は推測を排除し、シンプルな機械的枠組みに置き換えます。
ラリー・ウィリアムズは、スイングポイントを3つのグループに分類しています。これらは短期、中期、長期のスイングです。各グループは下のグループから構築されているため、構造が層ごとに形成されていく様子を見ることができます。いくつかのルールを守れば、チャートを分かりやすく整理された方法で読み取ることができます。
短期的なスイング
短期的なスイングとは、価格が小幅な動きを終え、反対方向に反転した際に形成される現象です。たとえば、短期的な安値とは、価格が新たな安値まで下落した後、それ以上下落せず、再び上昇に転じる場合を指します。簡単に言うと、最も低いバーは、より高い安値を持つ2つのバーの間に位置しています。これは、下降トレンドがひとまず終了したことを示しています。

短期的な高値は、これとは逆の形で形成されます。価格はピークに達した後、それ以上上昇できずに下落に転じます。中央の高値は、両側のバーの高値が低いため、短期的な高値となります。短期的なスイングはごく小さな転換点を示すものですが、それらは後に私たちが築き上げるすべてのものの土台となります。

中期的なスイング
短期的なスイングが十分に発生すれば、次のレベルのスイングを特定することができます。中期的な高値とは、他の2つの短期的な高値を上回る短期的な高値のことです。

同様に、短期的な安値に続いて、その上下にさらに高い短期的な安値が形成されると、中期的な安値が形成されます。

これらのスイングは、価格が通常の短期的なスイングよりも長い期間にわたって方向転換したことを確認するものであるため、より重要な転換点を示しています。
このレベルの変動であれば、わずかな雑音を取り除き、構造をより鮮明に見ることができます。チャートは追跡しやすい波を形成し始めます。市場はもはやランダムに見えず、ある一定のパターンが見られるようになります。短期的な高値と安値が組み合わさって、中期的なスイングを形成しているのです。
長期的なスイング
中期的なスイングが起こり始めたら、さらに上のレベルに進むことができます。長期的なスイングとは、中期的なスイングが周囲の他の中期的なスイングよりも上回ったり下回ったりする場合に発生する現象です。中期的な高値の後に両側により低い中期的な高値が続く場合、長期的な高値が出現します。

長期的な低値は、周囲の中期的な低値がその上方に位置するときに形成されます。

こうした長期的なスイングは、市場における大きな転換点を示すことが多いです。
3つのレベルすべてが表示されると、チャートは段階的な構造として現れます。まず、短期的なスイングによって生じる小さな変化に気づくでしょう。これらのいくつかが組み合わさって、中期的なスイングを形成します。そして、いくつかの短期的なスイングが積み重なって、長期的なスイングが形成されます。この入れ子構造により、トレンドの方向、トレンドの強さ、そして反転の可能性のあるポイントが非常に明確に示されます。これが、ラリー・ウィリアムズがスイングの論理を非常に重視する理由です。
インジケーターのデザインと視覚構造
コードを記述する前に、このインジケーターがチャートに読み込まれたときにどのように表示されるかを理解する必要があります。目的は、市場構造を検出し、それを視覚的に分かりやすく表示することです。トレーダーは、チャートを一目見ただけで、生の数値や隠された論理を解釈するのに苦労することなく、市場の転換点を即座に認識できるべきです。
このインジケーターは、市場構造の3つの異なるレベルを表示します。これらの水準は、トレーダーが混乱することなく区別できるように、それぞれ異なる方法で描画されます。
1. 短期的なスイングポイント
短期的な転換点は、単一の円で示されます。

これらは最も小さなスイングであり、最も頻繁に発生します。これらは、市場のリズムを段階的に読み取るのに役立ちます。インジケーターが短期的な安値を検出すると、ローソク足の下に円が表示されます。短期的な高値を見つけると、ローソク足の上に別の円が表示されます。
2. 中期的なスイングポイント
中期的なスイングは、短期的なスイングから生じます。これらは発生頻度が低く、市場圧力の変化を示すものであるため、より意義深いものです。これらのポイントは、二重円、つまり大きな円の中に小さな円を描いた図で示されます。

外側のリングは転換点が形成されたことを示しており、内側のリングはその重要性を裏付けています。この記号を見ることによって、トレーダーは価格が通常の短期的なスイングよりも強い勢いで反転したことを理解します。
3.長期的なスイングポイント
最後にして最も重要なカテゴリは、長期的なスイングです。これらは、より大きな潮流を形作る主要な転換点です。形成されるまでには時間がかかる場合がありますが、一度形成されると、それらは最も重要なものとなります。長期的なスイングハイとスイングローは矢印で示されます。

価格の上に矢印が表示されている場合は、強い高値と、その後の下落の可能性を示しています。価格の下にある矢印は、強い安値と今後の上昇の可能性を示しています。これらの矢印が表示されると、それらははっきりと目立ち、すぐに注目を集めます。
MQL5における市場構造ロジックの実装
このセクションでは、市場構造のロジックを実際のMQL5インジケーターに変換する作業を開始します。MQL5プログラムには、スクリプト、インジケーター、エキスパートアドバイザー(EA)、およびサービスの4つの種類があります。私たちの目標は、チャート上にスイングの構造を検出して描画することなので、これをカスタムインジケーターとして実装します。
まず、MetaEditor 5を開き、新しいインジケーターファイルを作成して、次の名前を付けます。
larryWilliamsMarketStructureIndicator.mq5
ファイルが作成されたら、その中のすべての内容を削除し、以下のスターターコードを貼り付けてください。
//+------------------------------------------------------------------+ //| larryWilliamsMarketStructureIndicator.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/ja/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/ja/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { //--- Return total bars processed return(rates_total); } //--- UTILITY FUNCTIONS //+------------------------------------------------------------------+
コードの詳細
以下に、ファイル内の各部分の意味と、それがなぜ重要なのかを説明します。
1. ファイルヘッダ
//+------------------------------------------------------------------+ //| larryWilliamsMarketStructureIndicator.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/ja/users/chachaian | //+------------------------------------------------------------------+
一番上の最初のコメントブロックは、読みやすさのためだけのものです。ファイル名、作成者情報、著作権表示が含まれています。これはインジケーターの動作には影響しません。単に所有権を記録するだけです。
2. プロパティの宣言
以下の#property行は、インジケーターのメタデータを設定します。
#property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/ja/users/chachaian" #property version "1.00"
ここでは、著作権、リンク、およびバージョン番号を定義します。今後、チャートの描画を制御しチャートをバッファするためのプロパティがさらに追加される予定です。
3.OnInit関数
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ return(INIT_SUCCEEDED); }
この関数は、インジケーターが開始される瞬間に一度だけ実行されます。後ほど、このセクションを使用してバッファの割り当て、チャートの設定、および描画スタイルの設定をおこないます。今のところ、成功を返しています。
4. OnCalculate関数
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { //--- Return total bars processed return(rates_total); }
この関数は、チャートが更新されるたび、または新しいローソク足が形成されるたびに実行されます。価格配列(始値、高値、安値、終値)を受け取り、処理されたバーの数を返します。近いうちに、短期、中期、長期のスイング検出ロジックをすべてここに追加する予定です。
5.ユーティリティ関数セクション
一番下には、ヘルパー関数のプレースホルダーがあります。ここにユーティリティ関数を配置します。ユーティリティ関数を分離することで、メインコードがより簡潔になり、読みやすくなります。
プロジェクトファイルが配置されたので、次のステップは、MetaTrader 5にインジケーターを表示する場所を指示することです。すべてのインジケーターは、メインの価格チャートに表示されるか、別のサブウィンドウに表示されるかを明示する必要があります。サブウィンドウインジケーターにはRSIやMACDなどのツールが含まれる一方、移動平均線などの価格ベースのインジケーターは通常、チャート上に直接チャートされます。
ここで構築しているスイング構造は、価格に基づいたものです。転換点はローソク足と一致する必要があるため、メインチャートウィンドウに描画する必要があります。MQL5では、簡単なディレクティブを使ってこれを指定できます。
ファイル上部のプロパティラインのすぐ下に、以下のセクションを追加してください。
//+------------------------------------------------------------------+ //| Custom Indicator specific directives | //+------------------------------------------------------------------+ #property indicator_chart_window
この行は、MetaTrader 5に対し、インジケーターの出力結果を別のサブウィンドウではなく、メインのローソク足チャートに表示するように指示します。この設定が完了すると、描画するすべてのグラフィック要素が価格バーに直接連動するようになり、これはまさにスイングハイとスイングローを識別するために望ましい動作です。
表示位置を設定したら、次にインジケーターが使用するチャートとバッファの数を定義します。MQL5では、チャート上に描画されるすべての視覚出力は、インジケーターバッファにリンクされています。バッファとは、価格値やシグナルを格納する配列であり、それらは後で図形や線として表示されます。宣言するチャートの数は、MetaTrader 5に描画する独立した視覚要素の数を指示します。
この市場構造インジケーターには、それぞれ独自のバッファを持つ6つのスイングポイントが存在します。検出する6種類のスイングタイプは以下のとおりです。
- 短期的な安値
- 短期的な高値
- 中期的な安値
- 中期的な高値
- 長期的な安値
- 長期的な高値
これらの各点は、図表上に個別に描画されます。1つのチャートには1つのバッファが必要なので、6つのバッファと6つのチャートが必要になります。既存のプロパティディレクティブの下に、以下のコードを追加します。
//+------------------------------------------------------------------+ //| Custom Indicator specific directives | //+------------------------------------------------------------------+ ... #property indicator_plots 6 #property indicator_buffers 6
これら2つのプロパティは、MetaTrader 5に対し、インジケーターが6つのグラフィカル出力を生成すること、そしてそれらの値を格納するための6つのバッファにメモリを割り当てる必要があることを伝えます。
インジケーターが使用するバッファの総数を指定しましたので、次のステップは実際のストレージ配列を宣言することです。これらの配列には、スイングポイントの最初のカテゴリ、つまり短期的な高値と短期的な安値の値が格納されます。それらをファイルのグローバル領域に配置することで、インジケーター全体からアクセスできるようにします。プロパティ指示のすぐ下に、以下の行を追加します。
//+------------------------------------------------------------------+ //| Indicator buffers | //+------------------------------------------------------------------+ double shortTermLows []; double shortTermHighs[];
この段階では、配列はまだ何も描画しません。それらは単にスイングポイント値を格納するための容器として機能するだけです。
グローバルスコープでストレージ配列を宣言した後、次に論理的におこなうべき作業は、それらをインジケータバッファとして登録することです。この手順により、MetaTrader 5は配列を描画システムの一部として認識できるようになります。登録が完了すると、それらに保存された値はすべてリアルタイムでチャート上にプロットできます。
この登録は、OnInit関数内でSetIndexBufferを使用して行います。この関数は、特定の配列をインジケータ内のバッファインデックスにリンクします。プラットフォームは後ほどこのリンクを使用して、スイングポイントを描画します。
OnInit関数内に以下のコードを追加します。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Bind arrays to indicator buffers SetIndexBuffer(0, shortTermLows, INDICATOR_DATA); SetIndexBuffer(1, shortTermHighs, INDICATOR_DATA); return(INIT_SUCCEEDED); }
次のステップは、短期的なスイングポイントを検出するアルゴリズムを構築することです。このロジックはOnCalculate関数内に配置されます。ここでインジケーターの主要な処理がおこなわれます。MetaTrader 5は、チャートが更新されるたびにこの関数を呼び出します。これには、インジケーターを初めて適用したとき、新しいローソク足が形成されたとき、および価格ティックが到着したときが含まれます。
最も重要な状況は2つあります。まずは最初の実行です。prev_calculatedがゼロに等しい場合にそれを検出します。このケースを使用してインジケーターを準備します。たとえば、読み込み時に完全な履歴を表示したい場合は、バッファを初期化し、以前の値をすべてクリアし、過去のスイング値を埋める必要があります。
2つ目は、新しいバーイベントです。これはprev_calculatedがrates_totalより小さい場合です。その場合は、ローソク足が確定するごとに一度実行されるロジックを実装できるはずです。こうすることで、ティックごとに同じ処理を繰り返す必要がなくなり、インジケーターの効率性を維持できます。
次に、OnCalculate関数を、以下の構造に合わせて更新します。
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { //--- Start with clean buffers at first calculation if(prev_calculated == 0){ } //--- Recalculate structures only when a new bar is added if(prev_calculated < rates_total){ } //--- Return total bars processed return(rates_total); }
インジケーターを初めて起動する際には、すべてのバッファがクリーンな値で開始されるようにする必要があります。これにより、配列に不要なデータが保持されるのを防ぎ、実際に存在するスイングポイントのみが表示されるようになります。スイングシグナルはすべてのバーに現れるわけではないため、空のバッファから始めて、計算プロセス中に新しい値でバッファを埋めていくことが不可欠です。
これを実現するために、prev_calculated == 0かどうかをチェックする初期化ロジックをブロック内に追加します。このブロックは、インジケーターがチャートに初めて適用されたときに一度だけ実行されます。ここでは、ArrayInitialize関数を使用して、両方の配列をEMPTY_VALUEで埋めます。EMPTY_VALUEは、MetaTrader 5に対してそのバーの描画をおこなわないように指示する特別な値です。
以下の構造に合わせてコードを更新します。
//--- Start with clean buffers at first calculation if(prev_calculated == 0){ ArrayInitialize(shortTermLows, EMPTY_VALUE); ArrayInitialize(shortTermHighs, EMPTY_VALUE); }
この簡単な手順で、インジケーターの次の段階に向けた準備が整います。次の段階では、短期的なスイングポイントを検出してチャートし始めます。
OnCalculateにおける短期的なスイングポイントの検出
//--- Recalculate structures only when a new bar is added if(prev_calculated < rates_total){ ArrayInitialize(shortTermLows, EMPTY_VALUE); ArrayInitialize(shortTermHighs, EMPTY_VALUE); for(int32_t i = 1; i < rates_total - 2; i++){ //--- Identify a short-term low if(low[i] < low[i - 1] && low[i] < low[i + 1]){ shortTermLows[i] = low[i]; } //--- Identify a short-term high if(high[i] > high[i - 1] && high[i] > high[i + 1]){ shortTermHighs[i] = high[i]; } } }
このブロックは、インジケーターが最初に起動されたときと、新しいバーが表示されるたびに実行されます。短期的なバッファをクリアした後、価格履歴をスキャンして短期的な高値と安値を特定します。このコードは、シンプルな3本のローソク足を使ったルールを採用しています。短期的な安値とは、その安値が左隣のバーの安値よりも低く、かつ右隣のバーの安値よりも低いバーのことです。短期的な高値はその逆です。その高さは左右のバーの高さよりも高くなります。
以下に各部品の役割と必要性を示します。
ArrayInitialize(shortTermLows, EMPTY_VALUE); ArrayInitialize(shortTermHighs, EMPTY_VALUE);
これらの呼び出しは、両方のバッファを空の値にリセットします。EMPTY_VALUE定数は、MetaTrader 5に対して、そのバーに何も表示しないように指示します。配列をクリアすることで、古いマークが残らないようにし、現在の処理をまっさらな状態から開始できるようにします。
for(int32_t i = 1; i < rates_total - 2; i++){ }
ループは、使用可能な各バーインデックスを順に処理します。インデックスi-1に左隣の要素が必要なので、1から始めます。最後のバーの手前で終了するため、i+1の右隣のバーが常に存在します。これにより、範囲外の読み取りを防ぎ、エラーを回避できます。
ループ内で、3バーテストを適用します。
if(low[i] < low[i - 1] && low[i] < low[i + 1]){ shortTermLows[i] = low[i]; }
これは短期的な安値をチェックするものです。条件がtrueの場合、インデックスiのshortTermLowsバッファに最安値を書き込みます。バッファに実際の価格を書き込むと、チャートシステムはそのバーに対応する記号を描画します。
//--- Identify a short-term high if(high[i] > high[i - 1] && high[i] > high[i + 1]){ shortTermHighs[i] = high[i]; }
これは短期的な高値をチェックするものです。条件を満たした場合、インデックスiのshortTermHighsバッファに高値を書き込みます。値を入力すると、チャートでそのバーの最高値を示すマーカーが表示されます。
新しいローソク足が形成される際に再初期化と再計算をおこなうことで、インジケーターは現在の構造を反映するようになります。新しい価格データが届くと、価格スイングは出現したり消滅したりする可能性があります。バッファをリセットすると、古いマークや誤ったマークがすべて削除されます。これにより、目に見える構造が正確かつ一貫性を保つことができます。
短期的なスイングポイントのチャート設定
この段階では、当社のインジケーターは短期的な高値と安値を検出することができ、その値はすでにデータウィンドウに表示されています。しかし、インジケーターを価格チャートに添付しても、チャート上にはまだ何も表示されません。これは正常です。MQL5では、インジケーターは1つ以上のチャートを設定した後にのみ表示されます。チャートとは、MetaTrader 5が特定のインジケーターバッファに格納されているデータをどのように描画するかを指示するものです。この手順がないと、プラットフォームはどのような図形を描画すればよいのか、また、それらがどのような見た目であるべきなのかを認識できません。
2つの区画を追加します。一方のチャートは短期的な安値を示し、もう一方のチャートは短期的な高値を示します。どちらの図も、円形のWingdingsフォントの記号を使用します。ラリー・ウィリアムズはこれらのポイントを「輪状の」スイングと表現することが多いので、円形の記号は最適です。
以下のコードは、インジケーターバッファを登録した直後のOnInit関数内に配置できます。短期的なスイングポイントを示すチャートを作成します。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Configure Graphic Plots PlotIndexSetInteger(0, PLOT_ARROW, 161); PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (0, PLOT_LABEL, "ShortTermLows"); PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(1, PLOT_ARROW, 161); PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (1, PLOT_LABEL, "ShortTermHighs"); return(INIT_SUCCEEDED); }
各行がどのような役割を果たしているのか、詳しく見ていきましょう。
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_ARROW); ... PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW);
これは描画スタイルを設定します。円形のWingdings記号は矢印のファミリーに属しているため、DRAW_ARROWを選択しました。これにより、各バーに揺れが発生する箇所に小さな円を描くことができます。
PlotIndexSetInteger(0, PLOT_ARROW, 161); ... PlotIndexSetInteger(1, PLOT_ARROW, 161);
ここでは、Wingdingsの文字コードを選択します。コード161は、チャート上できれいなリングのように見える円形の記号です。これは、ラリー・ウィリアムズがスイングポイントを表現する際に用いる視覚的なスタイルと一致します。
PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, EMPTY_VALUE); ... PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
これは、MetaTrader 5に対して「何も描画しない」という意味を伝えるものです。未使用のバーをEMPTY_VALUEで初期化するため、インジケーターはバッファに実際の価格を書き込んだバーにのみ記号を描画します。
PlotIndexSetString (0, PLOT_LABEL, "ShortTermLows"); ... PlotIndexSetString (1, PLOT_LABEL, "ShortTermHighs");
これにより、各チャートにラベルが付けられます。これらのラベルはデータウィンドウに表示されるため、どの系列がどのタイプのスイングポイントに対応しているかを簡単に識別できます。
設定を完了するには、プロパティディレクティブを使用してチャートの外観も制御する必要があります。既存の#propertyディレクティブのすぐ下に、以下の行を追加してください。
#property indicator_color1 clrGreen #property indicator_color2 clrBlack #property indicator_width1 1 #property indicator_width2 1
これらの指令が果たす役割は以下のとおりです。
indicator_color1とindicator_color2
これらは2つのチャートの色を設定します。最初の色は短期的な安値を示し、2番目の色は短期的な高値を示します。後で別の配色にしたい場合は、これらの設定を変更できます。
indicator_width1とindicator_width2
これらはチャート記号の太さを設定します。円形のWingdingsフォントを使用しているため、幅を1にすることで文字がすっきりとして読みやすくなります。
現時点では、このインジケーターには基本的な検出ロジック、バッファ登録、およびチャート設定が備わっています。最初のテストの準備が整いました。ファイルを保存し、MetaEditor 5でコンパイルボタンを押して、エラーがないことを確認してください。次に、そのインジケーターを任意のチャートに添付します。日足やH1など、十分な数のバーがある時間軸を使用します。読み込みが完了すると、チャート上に小さな円が表示されるのがわかります。これらの円は、短期的な値動きにおける安値と高値を示しています。履歴をスクロールするか、新しいバーが形成されるまで待機します。スイングポイントが自動的に更新されるはずです。これらはチャート記号の太さを設定します。円形のWingdingsフォントを使用しているため、幅を1にすることで文字がすっきりとして読みやすくなります。

中期的なスイングポイントの構築
短期的なスイングポイントを検出できるようになったので、次の目標はそれらから中期的なスイングポイントを構築することです。ラリー・ウィリアムズは、市場構造は階層的に形成されると教えています。短期的なスイングが基礎を形成し、そこからより大きなスイングを導き出すことで、トレンドの強さをより明確に把握することができます。次の段階をサポートするために、まず2つのグローバル配列を宣言します。一方は中期的なスイング安値を、もう一方は中期的なスイング高値をそれぞれ保存します。
double intermediateTermLows []; double intermediateTermHighs[];
OnInit関数内で、SetIndexBufferを呼び出すことにより、これらの配列を新しいインジケータバッファのインデックスにリンクします。これにより、MetaTrader 5はこれらのチャートの値がどこから取得されるのかを確実に認識できるようになります。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... SetIndexBuffer(2, intermediateTermLows, INDICATOR_DATA); SetIndexBuffer(3, intermediateTermHighs, INDICATOR_DATA); ... return(INIT_SUCCEEDED); }
それが整えば、2つの小さなユーティリティ関数を導入できます。これらの関数の目的は単純で、私たちが既に計算した短期的なスイングポイントをスキャンし、中期的なスイングとして適格なポイントのみを抽出します。
//+------------------------------------------------------------------+ //| Build intermediate highs from short-term highs | //+------------------------------------------------------------------+ void BuildIntermediateHighs(const double &shortHighs[], const int32_t rates_total, double &intermediateHighs[]) { // ensure target array sized if(ArraySize(intermediateHighs) != rates_total) ArrayResize(intermediateHighs, rates_total); // clear intermediate buffer for(int32_t i = 0; i < rates_total; ++i) intermediateHighs[i] = EMPTY_VALUE; // collect indices of short-term highs int SH_idx[]; ArrayResize(SH_idx, 0); for(int32_t i = 0; i < rates_total; ++i) { if(shortHighs[i] != EMPTY_VALUE) { int newlen = ArraySize(SH_idx) + 1; ArrayResize(SH_idx, newlen); SH_idx[newlen - 1] = i; } } // compress: each short high with lower short highs on both sides becomes an intermediate high int count = ArraySize(SH_idx); if(count < 3) return; // need at least three short-highs for a middle one to qualify for(int k = 1; k < count - 1; ++k) { int prev_i = SH_idx[k - 1]; int cur_i = SH_idx[k]; int next_i = SH_idx[k + 1]; // strict comparison per Larry: current must be higher than neighbors if(shortHighs[cur_i] > shortHighs[prev_i] && shortHighs[cur_i] > shortHighs[next_i]) intermediateHighs[cur_i] = shortHighs[cur_i]; } } //+------------------------------------------------------------------+ //| Build intermediate lows from short-term lows | //+------------------------------------------------------------------+ void BuildIntermediateLows(const double &shortLows[], const int32_t rates_total, double &intermediateLows[]) { if(ArraySize(intermediateLows) != rates_total) ArrayResize(intermediateLows, rates_total); for(int32_t i = 0; i < rates_total; ++i) intermediateLows[i] = EMPTY_VALUE; int SL_idx[]; ArrayResize(SL_idx, 0); for(int32_t i = 0; i < rates_total; ++i) { if(shortLows[i] != EMPTY_VALUE) { int newlen = ArraySize(SL_idx) + 1; ArrayResize(SL_idx, newlen); SL_idx[newlen - 1] = i; } } int count = ArraySize(SL_idx); if(count < 3) return; for(int k = 1; k < count - 1; ++k) { int prev_i = SL_idx[k - 1]; int cur_i = SL_idx[k]; int next_i = SL_idx[k + 1]; // strict comparison: current low must be lower than neighbors if(shortLows[cur_i] < shortLows[prev_i] && shortLows[cur_i] < shortLows[next_i]) intermediateLows[cur_i] = shortLows[cur_i]; } }
これらのユーティリティがどのように動作するのかを説明するために、BuildIntermediateHighs関数を使用してみましょう。この関数は、まず対象配列に十分な空き容量があることを確認し、次に空の値で埋めることで配列をクリアします。次に、短期的な高値をループ処理し、真のスイングハイが発生したバーインデックスを収集します。これらのインデックスは、小さな一時リストに格納されます。このリストができたら、関数は各短期高値をその近傍の高値の中央値でチェックします。あるバーが中期的なスイングハイとみなされるのは、その値が直前および直後の短期的な高値よりも高い場合に限られます。これは典型的な3点構造です。左肩は中央のピークが高く、右肩は低くなります。この条件が満たされると、値は中期的高値バッファに書き戻されます。
中期的安値を構築する関数も、同じ手順に従います。唯一の違いは、より高い値ではなく、より低い値をチェックする点です。つまり、一方の関数は中央のピークを探すのに対し、もう一方の関数は中央の谷を探すということです。この逆比較を除けば、全体的なロジックは同じです。
中期的なスイングポイントを構築するユーティリティ関数を作成したら、次のステップはそれらをインジケーターのワークフローに統合することです。短期的バッファと同様に、中期的スイングバッファもクリーンな状態から開始する必要があります。インジケーターがチャート上で最初に起動されたときに、それらをEMPTY_VALUEに初期化することで、これを実現します。これは、Prev_calculatedがゼロに等しいときに実行されるセクションのOnCalculate関数で発生します。
//--- Start with clean buffers at first calculation if(prev_calculated == 0){ ... ArrayInitialize(intermediateTermLows, EMPTY_VALUE); ArrayInitialize(intermediateTermHighs, EMPTY_VALUE); }
初期化が完了したら、中期的なスイングポイントを計算できます。これは、新しいバーが形成されたとき、またはインジケーターが初めて読み込まれたときに実行されるブロック内で実行されます。つまり、これはprev_calculatedがrates_totalより小さい場合に発生します。このブロックは既に短期的なスイングポイントを計算するために使用しているので、そのセクションの下に関数呼び出しを追加します。
//--- Recalculate structures only when a new bar is added if(prev_calculated < rates_total){ ... BuildIntermediateLows(shortTermLows, rates_total, intermediateTermLows); BuildIntermediateHighs(shortTermHighs, rates_total, intermediateTermHighs); }
これら2つの呼び出しにより、インジケーターは更新された短期構造から直接中期的なスイングを導き出すことができます。新しいバーが追加されるたびに、インジケーターはまず基礎層を再計算し、次に第2層を適用します。これにより、市場の動きに合わせてスムーズに更新される、明確で一貫性のあるスイングポイントの階層構造が構築されます。
中期的なスイングロジックが完全に実装されたので、次のステップはこれらの値をチャート上に表示することです。計算だけでは不十分です。短期的な構造と同様に、中期的なスイングポイントについても、OnInit関数内で独自のグラフィックチャート構成が必要です。既存の短期チャート設定の直下に、以下の2つのチャート定義を追加します。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(2, PLOT_ARROW, 161); PlotIndexSetDouble (2, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (2, PLOT_LABEL, "intermediateTermLows"); PlotIndexSetInteger(3, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(3, PLOT_ARROW, 161); PlotIndexSetDouble (3, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (3, PLOT_LABEL, "IntermediateTermHighs"); return(INIT_SUCCEEDED); }
これらの行は以前の構成と同じ構造に従っているため、その動作は既にお馴染みのはずです。これらは、中期的スイングバッファに格納されている値を、短期ポイントと同じ円形のWingdings文字を使用して描画する方法をターミナルに指示します。表示プロパティを導入すると、その違いが明確になります。
トレーダーが中期的なスイングと短期的なスイングを視覚的に区別できるように、追加のプロパティディレクティブを定義します。
#property indicator_color3 clrGreen #property indicator_color4 clrBlack #property indicator_width3 4 #property indicator_width4 4
ここでは以前と同じ色を使用しますが、幅を大幅に広げます。その結果、中期的なスイングポイントはより大きな円形のマークとして現れます。中期的なスイングが短期的なスイングと同じバーで発生すると、自然と大きな円が小さな円の上に重なり、二重のリング効果が生じます。これは意図的なものであり、ラリー・ウィリアムズが提唱する、チャート上で上位のスイングポイントを視覚的に際立たせるという手法に倣ったものです。
この段階で、インジケーターを再コンパイルして、1時間足や日足などの任意のチャートに適用することができます。すべて正しく入力されていれば、短期および中期のスイングポイントが画面上に明確に表示されるはずです。この機会に、ロジックが期待どおりに機能しているか、またインジケーターが市場構造のさまざまなレベルを正しく識別しているかを確認しましょう。

長期的なスイングポイントの構築
短期および中期的な構造が既に確立されている中で、市場構造階層の最終層は長期的なスイングポイントです。これらの長期的なスイングは、短期的なスイングから中期的なスイングが導き出されたのと同様の方法で、中期的なスイングから導き出されます。読者はこのワークフローに精通しているため、ここではこのレベルで新たに導入される部分のみに焦点を当てます。
前回と同様に、まずグローバルスコープで2つの配列を宣言することから始めます。これらの配列には長期的な安値と高値が格納され、後でそれぞれのインジケーターバッファにマッピングされます。
double longTermLows []; double longTermHighs[];
OnInit関数内で、これらの配列をインジケーターバッファとして登録することで、ターミナルがそれらがチャートデータを保持することを認識できるようにします。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... SetIndexBuffer(4, longTermLows, INDICATOR_DATA); SetIndexBuffer(5, longTermHighs, INDICATOR_DATA); ... return(INIT_SUCCEEDED); }
インジケーターが初めて実行されるとき、prev_calculated == 0ブロック内でEMPTY_VALUEをこれらのバッファに埋めることで、これらのバッファを準備します。これにより、実際の長期的なスイングポイントのみがチャートされるようになります。
//--- Start with clean buffers at first calculation if(prev_calculated == 0){ ... ArrayInitialize(longTermLows, EMPTY_VALUE); ArrayInitialize(longTermHighs, EMPTY_VALUE); }
長期的なスイングポイントを導き出すためのロジックは、既に実装済みのものとほぼ同じです。私たちは依然として同じビルダー関数であるBuildIntermediateLowsとBuildIntermediateHighsを使用しています。唯一の違いは、この段階では、短期的なスイングを入力として渡す代わりに、中期的なスイングを入力として渡すという点です。これにより、同じアルゴリズムを用いて、次のレベルのデータを使用して長期的な転換点を特定することが可能になります。再計算を処理するブロック内では、呼び出しは次のようになります。
//--- Recalculate structures only when a new bar is added if(prev_calculated < rates_total){ ... BuildIntermediateLows(intermediateTermLows, rates_total, longTermLows); BuildIntermediateHighs(intermediateTermHighs, rates_total, longTermHighs); }
長期的なスイングポイントが算出されたら、次のステップはそれらのチャート表示を設定することです。これらのポイントはチャート上で明確に目立つようにする必要があるため、(短期および中期のスイングで使用したような)円形のWingdingsフォントを使用する代わりに、矢印記号を使用して描画します。また、矢印がローソク足や過去のスイングポイントと重ならないように、価格の上下にわずかにずらします。この視覚的な区分けにより、読者は長期的な構造とそれ以外の部分を瞬時に区別することができます。
OnInit関数内で、チャートを次のように設定します。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... PlotIndexSetInteger(4, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(4, PLOT_ARROW, 233); PlotIndexSetInteger(4, PLOT_ARROW_SHIFT, +30); PlotIndexSetDouble (4, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (4, PLOT_LABEL, "LongTermHighs"); PlotIndexSetInteger(5, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(5, PLOT_ARROW, 234); PlotIndexSetInteger(5, PLOT_ARROW_SHIFT, -30); PlotIndexSetDouble (5, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (5, PLOT_LABEL, "LongTermHighs"); return(INIT_SUCCEEDED); }
視覚的な設定を完了するために、長期的なスイングに適した色と線の太さを割り当てる2つの#propertyディレクティブを追加します。
#property indicator_color5 clrGreen #property indicator_color6 clrBlack #property indicator_width5 2 #property indicator_width6 2
実装を完了する前に、もう一つ追加しておきたい改善点があります。このインジケーターはスイングポイントの視覚的な解釈に大きく依存しているため、チャート自体が見やすく、一貫性があり、読みやすいものであると非常に役立ちます。読み込まれるチャートテンプレートはユーザーごとに異なる場合があり、それらのテンプレートの中には、ダークテーマ、太いグリッド線、特殊なローソク足の色など、チャートされたスイング構造が見えにくくなるものがあります。
インジケーターが常に明確に表示されるようにするために、ConfigureChartAppearanceという小さなユーティリティ関数を定義します。
//+------------------------------------------------------------------+ //| This function configures the chart's appearance. | //+------------------------------------------------------------------+ bool ConfigureChartAppearance() { if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){ Print("Error while setting chart background, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){ Print("Error while setting chart grid, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_MODE, CHART_CANDLES)){ Print("Error while setting chart mode, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){ Print("Error while setting chart foreground, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BULL, clrSeaGreen)){ Print("Error while setting bullish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CANDLE_BEAR, clrBlack)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_UP, clrSeaGreen)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_CHART_DOWN, clrBlack)){ Print("Error while setting bearish candles color, ", GetLastError()); return false; } return true; }
この関数は、白い背景を設定し、グリッドを非表示にし、前景とローソク足の色を統一することで、チャートをシンプルでコントラストの高いレイアウトに調整します。ここでは取引ロジックを一切変更していません。この関数は、インジケーターが読み込まれた際にスイングチャートがすぐに目立つようにチャートを標準化するだけです。
関数が定義されたら、OnInit関数の冒頭でその関数を呼び出します。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- To configure the chart's appearance if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance", GetLastError()); return INIT_FAILED; } ... }
設定が正常に適用されると、インジケーターは通常どおり初期化処理に進みます。そうでない場合は、エラーが報告されます。この呼び出しを先頭に配置することで、バッファやチャートが設定される前にチャートが完全に準備されるようになります。
この最後の追加機能により、このインジケーターは構造を確実に計算するだけでなく、それをすっきりとしたプロフェッショナルなビジュアルレイアウトで表示するため、基となるスイングレベルを一目で簡単に識別できるようになります。
インジケーターを再コンパイルしてから、チャート上で起動します。3つのレイヤーすべてが実装されたことで、短期、中期、長期のスイング構造全体が視覚的に分かりやすくなり、一目で区別できるようになるはずです。

この最後の仕上げで、インジケーターは完成です。短期、中期、長期のスイングポイントを特定することから、明確な視覚化のためのチャート設定まで、すべてのコンポーネントが完全に実装されました。作業を容易にするため、完全なソースコードを「larryWilliamsMarketStructureIndicator.mq5」として添付しました。もし何か不足している点や期待どおりに動作しない点があれば、ご自身の実装を添付の参照ファイルと比較してください。
取引でインジケーターを使用する方法
インジケーターが完全に機能し、チャート上に短期および中期のスイングポイントを表示するようになった今、トレーダーがこの情報を実際にどのように活用できるかを説明することが不可欠です。市場構造は価格分析において最も価値のあるツールの1つであり、これらのスイングポイントはトレーダーが市場の方向性や重要な変化がどこで起こっているかを理解するのに役立ちます。このインジケーターは、それ自体で買い注文や売り注文を生成するものではありません。むしろ、市場構造が変化するレベルを明確に示し、トレーダーはその情報を自身のより広範な取引戦略に組み込むことができます。
トレーダーは、意思決定の基礎として、中期的なスイングポイントまたは長期的なスイングポイントのいずれかを選択することができます。アイデアはシンプルです。中期的なスイングローが形成されると、買い手が介入して価格の一時的な底値を作ったことを示唆します。トレンドに従うことを好むトレーダーは、この時点で買いポジションを取り、中期的な高値によって構造が無効になるまでポジションを保持することを選択するかもしれません。同じ論理が逆の場合にも当てはまります。中期的な高値圏が形成された場合、これは売り手が天井を作り出したことを示唆する可能性があります。トレーダーはその後、売りポジションを取り、新たな中期的なスイングローが現れるまでそのポジションを維持することを検討できます。
長期的なスイングポイントも同様に利用できますが、出現頻度は低く、市場構造のより広範な変化を反映するシグナルとなります。そのため、これらの商品は、よりゆっくりとした取引スタイルを好むトレーダーや、より長い時間軸で取引をおこなうトレーダーに適しています。長期的なスイングローは、より長期的な強気相場の可能性を示す強いシグナルとなる可能性があり、一方、長期的なスイングハイは、長期的な弱気相場の兆候となる可能性があります。これらの長期的な構造は、特にトレンドフィルターやより長い時間軸の分析と組み合わせることで、トレーダーがより自信を持ってポジションを保有するのに役立ちます。
このインジケーターは、特に決済やストップロス注文の設定に関する取引管理上の意思決定を支援することもできます。例えば、トレーダーがポジションを積み上げている状態で、価格が以前の中期的安値を下回った場合、これは構造が崩れたことを示している可能性があります。このような場合、取引から撤退することが論理的な選択肢となります。同様に、価格が前回の中期的高値を上回った場合、ショートポジションを決済することができます。進行中の取引を管理するためにスイングポイントを利用することで、トレーダーは利益を守り、市場が明らかに不利な方向に動いたときにポジションを保持することを避けることができます。
このインジケーターは構造に基づいた貴重な情報を提供するものの、常に包括的な取引計画を補完するものとして使用すべきです。スイングポイントだけでは、一貫した意思決定を行うには不十分です。トレーダーは、これらをトレンドの方向性、流動性ゾーン、移動平均線、出来高分析など、他の種類のコンフルエンスと組み合わせて使用することが推奨されます。目標は、スイングトレードの構造がトレーダーが市場で既に認識していることを確認するのに役立つ、堅牢なプロセスを構築することです。
要約すると、このインジケーターは市場構造を解釈するための明確かつ体系的な方法を提供します。これは勢いの変化を明確にし、トレンドの継続点と反転点を特定するのに役立ち、エントリーとエグジットへの分かりやすいアプローチを提供します。正しく使用し、他の信頼できるツールと組み合わせることで、トレーダーの市場分析能力と情報に基づいた意思決定能力を大幅に向上させることができます。
結論
本記事では、ラリー・ウィリアムズが著書『Long-Term Secrets to Short-Term Trading』で紹介した市場構造の概念を、完全に機能するMQL5インジケーターに変換する方法を紹介します。段階的に、チャートを設計し、バッファを準備し、価格データから短期および中期のスイングポイントを直接抽出するアルゴリズムを構築しました。最終的に出来上がったのは、トレーダーがチャート上に配置することで市場構造を明確に視覚化できる実用的なツールです。
本記事では、インジケーターそのものだけでなく、バッファ管理、チャート構成、構造化インジケーターロジックなど、MQL5のコアコンセプトに関する実践的な経験を積むことができます。これらのスキルは、長期的なスイング検出、アラート、自動化戦略、あるいはその他追加したい機能強化を構築するための出発点として役立ちます。これは実際の応用例を持つ実際のプロジェクトであり、学習リソースとしてだけでなく、将来の発展のための基盤としても役立つはずです。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/20511
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5でカスタムインジケーターを作成する(第1回):Canvasグラデーションを使用したピボットベースのトレンドインジケーターの構築
MQL5入門(第31回):MQL5のAPIとWebRequest関数の習得(V)
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
MQL5における取引戦略の自動化(第46回):Liquidity Sweep on Break of Structure (BoS)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
CRTとICT ALGOのコードも書いてくれる?
CRTとICT ALGOのコードも書いてくれる?