ラリー・ウィリアムズの『市場の秘密』(第14回):カスタムインジケータで隠れスマッシュデー反転を検出する
はじめに
隠れスマッシュデー反転パターンを理解する
隠れスマッシュデー反転パターンは、ラリー・ウィリアムズのスマッシュデー反転パターンの特別なバリエーションです。一見すると、これらのバーは現在の市場方向を支持しているように見えます。しかし、内部構造を詳しく見ると、強さではなく弱さを示していることが分かります。この見た目と実際の状態の矛盾こそが、このパターンが「隠れスマッシュデー」と呼ばれる理由です。
隠れスマッシュデーの中心的な考え方は「失敗」です。市場はセッション中に一方向へ強く動こうとしますが、その動きは引けまでに打ち消されます。価格はセッション終了時にわずかに上昇または下降して終わる場合がありますが、バー内部における終値の位置は、まったく異なる状況を示します。
隠れスマッシュデーの買いセットアップ
隠れスマッシュデーの買いセットアップは、前のバーより高く引けるバーから始まります。表面的には、これは強気の状態に見えます。しかし、重要なのは、その終値がバー自身のレンジ内のどこに位置しているかです。

有効な買いの隠れスマッシュデーバーでは、終値はバーのレンジの下側部分に位置していなければなりません。このパターンの中でも最も強い形では、終値は始値よりも下に位置します。これは、セッション終了時点で、買い手がバーの前半で得た上昇分の大部分を維持できなかったことを意味します。上昇は売り圧力に遭遇し、買いの勢いは実質的に打ち消されました。
これにより、トレーダーを欺く状況が生まれます。上昇して終わったことだけを見るトレーダーは強さとして解釈する可能性がありますが、バー内部の構造は疲弊を示しています。このパターンは、このバーだけでは完成しません。確認が必要です。
強気シグナルは、次のバーが隠れスマッシュデーバーの高値を上回って終値を形成した場合にのみ確認されます。この場合、失敗した動きが反転し、需要が再び戻り、以前の拒否を克服する十分な強さを持っていることを示します。この「失敗の後に即座に回復する」という流れが、買いの隠れスマッシュデー反転の基盤となります。
売りの隠れスマッシュデー反転のセットアップ
売りの隠れスマッシュデー反転のセットアップは、買いパターンの反対になります。この場合、バーは前のバーより低い位置で終値を形成し、最初は弱気に見えます。しかし、ここでも内部構造が本当の状況を示します。

有効な売りの隠れスマッシュバーでは、終値はバーのレンジの上側部分に位置していなければなりません。最も強いパターンでは、終値は始値よりも上に位置します。これは、セッション中に売り手が価格を下方向へ押したものの、その支配を維持できなかったことを示します。価格は終値までに高値付近へ回復し、売り圧力の弱さを露呈します。
これにより、再びトレーダーを欺く状況が発生します。トレーダーは下落して終わったことを弱さの確認として解釈する可能性がありますが、バー自体は安値の拒否を示しています。
弱気シグナルは、次のバーが隠れスマッシュデーバーの安値を下回って終値を形成した場合にのみ確認されます。この下抜けによって、回復の試みが失敗し、売り圧力が確信を伴って戻ったことが確認されます。このパターンも同様に、失敗と即時反転というテーマに従っています。
隠れスマッシュデーパターンは、反転を予測するのではなく、市場が自らの弱さを露呈するのを待ち、その後、価格アクションによって転換を確認します。そのため、このパターンはルールベースのトレードフレームワークに特に適しており、体系的な自動化の有力な候補となります。
隠れスマッシュデーインジケータの設計
実装の詳細に入る前に、このインジケータが情報を視覚的かつ論理的に伝えるために、どのように設計されているかを明確に理解することが重要です。優れたテクニカル指標は、2つのことを正しく行う必要があります。パターンを正確に識別すること、そしてそれを読みやすい形式で表示することです。このインジケータは、この両方の目的を考慮して設計されています。
このインジケータの視覚的な表現は、意図的にシンプルに設計されています。シーグリーンの上向き矢印は、買いの隠れスマッシュデーバーの直下に表示されます。この矢印は、セッション中に買い圧力が失敗し、その後の上方向への反転の可能性につながる場面を示します。一方、黒色の下向き矢印は、売りの隠れスマッシュデーバーの上に表示されます。これは、序盤では売り圧力が優勢に見えたものの、最終的には失敗し、下方向への反転に先行することが多い状況を示します。矢印をパターンが形成されたバーの位置に直接表示することで、チャートを過度に複雑にすることなく、これらのセットアップを簡単に確認できます。
視覚的な表示だけでなく、柔軟性もこの設計の中心的な要素です。隠れスマッシュデーパターンは、トレードスタイルや確認方法の好みによって、複数の解釈が可能です。そのため、このインジケータでは、入力パラメータによって選択可能な2つの識別モードをサポートしています。
最初のモードでは、隠れスマッシュデーバーが形成され、ラリー・ウィリアムズの基準を満たした時点ですぐにマークされます。このモードは、反転の可能性を早期に把握したいトレーダーや、セットアップ発生後の価格動向を注意深く監視したいトレーダーに適しています。
2つ目のモードでは、インジケータは確認が完了するまでパターンの表示を待ちます。隠れスマッシュデーバーの買いセットアップの場合、確認は次のバーが隠れスマッシュデーバーの高値を上回って終値を形成した場合のみ成立します。売りセットアップの場合は、次のバーが隠れスマッシュデーバーの安値を下回って終値を形成することが確認条件になります。この方法では、弱いセットアップを除外し、価格アクションによって失敗した動きが明確に確認されたパターンだけに集中できます。
これらのモードを設定可能な入力項目として公開することで、このインジケータは単一の解釈を強制することなく、異なるトレード手法に対応できます。トレーダーは、自身の戦略やリスク許容度に応じて、早期検出または確認済みシグナルを選択できます。
これらすべての設計要素がどのように組み合わされるかを視覚的に確認するため、実際のチャート上で起動した最終的なインジケータのスクリーンショットを以下に示します。

この例では、矢印がどれほど明確に表示されるか、またインジケータがクリーンな価格チャートに自然に統合され、チャートを過度に複雑化しないことが確認できます。
MQL5で隠れスマッシュデーインジケータを実装する
隠れスマッシュデー反転パターンの基本概念が明確に定義されたので、次のステップはその概念を動作するカスタムインジケータへ変換することです。このセクションではMQL5を使用した実際の実装を開始します。ここでの目的は、シグナルへ急ぐことではなく、隠れスマッシュデーパターンを正確に検出し、視覚的に表示するための、整理された信頼性の高い基盤を構築することです。
ロジックを書く前に、開発環境を適切に準備することが重要です。これには、MQL5構文の基本的な理解、MetaTrader 5ターミナルへの理解、そしてMetaEditor内で作業するための知識が含まれます。これらの準備が整ったら、段階的にインジケータの構築を進めることができます。
このインジケータの完全な実装コードは、記事にlwHiddenSmashDayIndicator.mq5として添付されています。チュートリアルを読み進めながら実際にコードを書いていくことを強くお勧めします。コードを手動で作成し、添付されたバージョンと比較することで、理解が深まり、問題が発生した場合にも原因を特定しやすくなります。
インジケータの基本構造の作成
まず、MetaEditor内で新しい空のカスタムインジケータファイルを作成します。ファイル名は自由に設定できますが、添付されているソースコードと同じ名前を使用すると、一貫性を維持できます。ファイルを作成したら、最初の基本コード(ボイラープレートコード)を配置します。
//+------------------------------------------------------------------+ //| lwHiddenSmashDayIndicator.mq5 | //| Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/ja/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/ja/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Custom Indicator specific directives | //+------------------------------------------------------------------+ #property indicator_chart_window #property indicator_plots 2 #property indicator_buffers 2 //+------------------------------------------------------------------+ //| Defines how Hidden Smash Day patterns are validated | //+------------------------------------------------------------------+ enum ENUM_HIDDEN_SMASH_VALIDATION_MODE { HIDDEN_SMASH_BAR_ONLY, HIDDEN_SMASH_CONFIRMED }; //+------------------------------------------------------------------+ //| Hidden Smash Detection Settings | //+------------------------------------------------------------------+ input ENUM_HIDDEN_SMASH_VALIDATION_MODE hiddenSmashValidationMode = HIDDEN_SMASH_CONFIRMED; //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; //+------------------------------------------------------------------+ //| Indicator buffers | //+------------------------------------------------------------------+ double buySmashArrowBuffer []; double sellSmashArrowBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- To configure the chart's appearance if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance ", GetLastError()); return INIT_FAILED; } 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[]) { ArraySetAsSeries(open, false); ArraySetAsSeries(high, false); ArraySetAsSeries(low, false); ArraySetAsSeries(close, false); //--- Temporary buffers used to store price data for rendering custom colored candles double lwOpen []; double lwHigh []; double lwLow []; double lwClose[]; //--- Copy price data into local working buffers to safely manipulate candle values without altering the original price arrays ArrayCopy(lwOpen, open); ArrayCopy(lwHigh, high); ArrayCopy(lwLow, low); ArrayCopy(lwClose, close); //--- This block will be executed whenever the indicator is initially attached to a chart if(prev_calculated == 0){ //--- Start with clean arrays } //--- This block is executed on new bar open if(prev_calculated != rates_total && prev_calculated != 0){ //--- Update buffers on new bar open } //--- This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ //--- Update buffers on new tick } return(rates_total); } //+------------------------------------------------------------------+ //| 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 bullish 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; } //+------------------------------------------------------------------+
この初期コードでは、インジケータの構造を定義し、後で拡張されるすべての基本コンポーネントを準備します。この段階では、トレードロジックやパターン検出処理は実行されません。目的は、構成、明確性、そして将来的な拡張性を確保することです。
インジケータのプロパティとメタデータ
//+------------------------------------------------------------------+ //| lwHiddenSmashDayIndicator.mq5 | //| Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/ja/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/ja/users/chachaian" #property version "1.00"
ソースファイルの最初のセクションでは、著作権情報、バージョン番号、参照リンクなどの一般的なメタデータを定義します。これらの宣言は単なる形式的なものではありません。作成者に関する情報を明確にし、インジケータが進化していく過程でのバージョン管理にも役立ちます。
その直後に、インジケータ固有のディレクティブが宣言されます。これらのディレクティブは、インジケータがチャートウィンドウ上に直接描画されること、そして2つのプロットと2つのバッファを使用することをターミナルに通知します。この設計は、強気および弱気の両方の隠れスマッシュデーバーを矢印で視覚的に表示するという目的に基づいています。
検証モードの設定
隠れスマッシュデーパターンは、複数の方法で解釈することができます。一部のトレーダーは、バーが形成された時点ですぐにパターンを確認することを好みます。一方で、他のトレーダーは次のバーによる確認を必要とします。両方のアプローチに対応するため、カスタム列挙型が定義されています。
//+------------------------------------------------------------------+ //| Defines how Hidden Smash Day patterns are validated | //+------------------------------------------------------------------+ enum ENUM_HIDDEN_SMASH_VALIDATION_MODE { HIDDEN_SMASH_BAR_ONLY, HIDDEN_SMASH_CONFIRMED };
検証モードの列挙型では、2つの明確な選択肢を導入しています。1つ目の選択肢では、隠れスマッシュデーバー自体の内部構造に基づいて、形成された時点ですぐにマークします。もう1つの選択肢では、次のセッションで確認が行われた後にのみ、そのバーをマークします。この選択はユーザー入力パラメータとして公開されており、コードを変更することなく、インジケータを異なる分析スタイルに適応させることができます。
//+------------------------------------------------------------------+ //| Hidden Smash Detection Settings | //+------------------------------------------------------------------+ input ENUM_HIDDEN_SMASH_VALIDATION_MODE hiddenSmashValidationMode = HIDDEN_SMASH_CONFIRMED;
この設定を早い段階で導入することで、インジケータのロジックは柔軟性を維持し、ハードコードされた前提条件を避けることができます。
グローバルスコープと時間足の処理
グローバルな時間足変数が宣言され、現在のチャート時間足に設定されます。
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;
これにより、インジケータは適用されたチャートに自動的に適応します。チャートが1時間足、4時間足、または日足であっても、同じロジックが変更なしで適用されます。
このアプローチにより、インジケータはラリー・ウィリアムズの原則との一貫性を維持しながら、複数の時間足で動作できます。
インジケータバッファと視覚的出力
2つのインジケータバッファが宣言されます。
//+------------------------------------------------------------------+ //| Indicator buffers | //+------------------------------------------------------------------+ double buySmashArrowBuffer []; double sellSmashArrowBuffer[];
1つのバッファは強気の隠れスマッシュデーバー用に使用され、もう1つは弱気の隠れスマッシュデーバー用に使用されます。これらのバッファには、後ほど矢印を描画する位置となる価格値が格納されます。
この段階では、バッファは意図的に空の状態になっています。有効な条件が検出されるまで、インジケータは何も表示しません。これにより、チャートを整理された状態に保ち、誤解を招くシグナルを避けることができます。
視覚的な目的はシンプルかつ明確です。強気の隠れスマッシュデーバーの下にはシーグリーンの矢印が描画され、弱気の隠れスマッシュデーバーの上には黒色の矢印が描画されます。この色と配置の選択により、明確な視覚的コントラストが生まれ、トレーダーは反転の可能性があるゾーンを素早く識別できます。
初期化ロジック
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- To configure the chart's appearance if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance", GetLastError()); return INIT_FAILED; } 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[]) { ArraySetAsSeries(open, false); ArraySetAsSeries(high, false); ArraySetAsSeries(low, false); ArraySetAsSeries(close, false); //--- Temporary buffers used to store price data for rendering custom colored candles double lwOpen []; double lwHigh []; double lwLow []; double lwClose[]; //--- Copy price data into local working buffers to safely manipulate candle values without altering the original price arrays ArrayCopy(lwOpen, open); ArrayCopy(lwHigh, high); ArrayCopy(lwLow, low); ArrayCopy(lwClose, close); //--- This block will be executed whenever the indicator is initially attached on a chart if(prev_calculated == 0){ //--- Start with clean arrays } //--- This block is executed on new bar open if(prev_calculated != rates_total && prev_calculated != 0){ //--- Update buffers on new bar open } //--- This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ //--- Update buffers on new tick } return(rates_total); }
計算関数は、最終的にすべてのインジケータロジックが配置される場所になります。現時点では、異なる実行シナリオを分離するための構造化されたワークフローを確立します。
価格配列は、ローカルの作業用バッファへコピーすることで慎重に処理されます。これにより、ターミナルが提供する元の価格系列を変更することなく、安全にデータを操作できます。
3つの実行パスが明確に分離されています。1つ目のパスは、インジケータが初めてチャートに適用された場合を処理します。2つ目のパスは、新しいバーが形成された場合を処理します。最後のパスは、現在のバーが形成されている途中で受信するティックデータを処理します。
このように処理を分離しているのは意図的です。隠れスマッシュデーパターンは、完成したバーと確認ロジックに依存します。早い段階で実行フローを整理することで、パターン検出が適切なタイミングで行われ、リペイントや不安定なシグナル生成を防ぐことができます。
この段階では、これらのブロックはプレースホルダーです。それらが存在することで、後から追加されるロジックの配置場所が明確になり、インジケータを制御された予測可能な方法で成長させることができます。
この基盤が整ったことで、次のステップでは隠れスマッシュデーの買いおよび売りパターンの実際の検出ロジックを導入し、それをインジケータバッファへ接続します。ここから先は、それぞれの追加要素が、すでに定義された内容を自然に基礎として構築されます。
まず、チャート環境をクリーンで読みやすい状態にします。次に、ラリー・ウィリアムズが説明した通りの隠れスマッシュデーパターンを識別する再利用可能な関数を定義します。3番目に、それらの関数をインジケータのライフサイクルへ接続し、過去データ、新しいバー、そしてリアルタイムでパターンを検出できるようにします。各ステップは直前のステップを直接基盤として構築され、インジケータを整理された、予測可能で、保守しやすいものに保ちます。
視覚的な明確性のためのチャート準備
検出ロジックを適用する前に、テスト時および実際の使用時の両方で視覚的なシグナルを容易に解釈できるよう、チャート表示をリセットします。
//+------------------------------------------------------------------+ //| 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 bullish 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; }
ConfigureChartAppearance関数は、すべてのチャート表示設定ルールを1か所に集約します。この関数では、明るい背景を設定し、不要なグリッド線を削除し、強気および弱気のローソク足にコントラストのある色を適用します。これにより、インジケータの矢印が明確に表示される、ニュートラルな視覚環境が作成されます。
OnInit内部からこの関数を呼び出すことで、インジケータがチャートへ適用された直後に一度だけチャート設定が行われ、その後の価格計算やバッファ更新処理には影響しません。
インジケータバッファの登録と準備
チャートの準備が完了したら、次のステップはインジケータバッファの初期化と登録です。このインジケータでは、2つのバッファを使用します。
- 1つ目のバッファは、強気の隠れスマッシュデーバーの値を格納する
- 2つ目のバッファは、弱気の隠れスマッシュデーバーの値を格納する
両方のバッファは、明示的に非時系列形式のインデックス設定にされます。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Set arrays as series ArraySetAsSeries(buySmashArrowBuffer, false); ArraySetAsSeries(sellSmashArrowBuffer, false); return(INIT_SUCCEEDED); }
つまり、インデックス0は最新のバーではなく、履歴上で最も古いバーを表します。この決定は意図的なものであり、重要です。なぜなら、すべての検出ロジックは、履歴データをループ処理する際に、前方インデックス形式の配列を前提としているためです。
バッファのインデックス方向を設定した後、SetIndexBufferを使用してバッファを登録します。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Bind arrays to indicator buffers SetIndexBuffer(0, buySmashArrowBuffer, INDICATOR_DATA); SetIndexBuffer(1, sellSmashArrowBuffer, INDICATOR_DATA); return(INIT_SUCCEEDED); }
この時点以降、MetaTraderはチャート上に矢印を描画する際に、どこからデータを読み取るべきかを正確に認識します。
隠れスマッシュデーバー検出ロジックの定義
バッファ構造の準備が完了したため、次に隠れスマッシュデーパターンを識別する関数を定義できます。生のパターンを検出するために、2つの基本関数を作成します。
1つは強気の隠れスマッシュデーバーを検出します。
//+------------------------------------------------------------------+ //| Identifies a Bullish Hidden Smash Bar at a specific index | //| Larry Williams definition applied to any timeframe | //+------------------------------------------------------------------+ bool IsHiddenSmashBuyBarAtIndex( const int i, const double &open[], const double &high[], const double &low[], const double &close[], const bool requireCloseBelowOpen = false ) { // We need a previous bar to confirm an up close if(i < 1) return false; // 1. Must close higher than the previous bar if(close[i] <= close[i - 1]) return false; // 2. Use the bar's own range double range = high[i] - low[i]; if(range <= 0.0) return false; // 3. Close must be in the lower 25 percent of the bar range double lowerQuarter = low[i] + (range * 0.25); if(close[i] > lowerQuarter) return false; // 4. Best patterns only if requested if(requireCloseBelowOpen && close[i] >= open[i]) return false; return true; }
1つは弱気の隠れスマッシュデーバーを検出します。
//+------------------------------------------------------------------+ //| Identifies a Bearish Hidden Smash Bar at a specific index | //| Larry Williams definition applied to any timeframe | //+------------------------------------------------------------------+ bool IsHiddenSmashSellBarAtIndex( const int i, const double &open[], const double &high[], const double &low[], const double &close[], const bool requireCloseAboveOpen = false ) { // We need a previous bar to confirm a down close if(i < 1) return false; // 1. Must close lower than the previous bar if(close[i] >= close[i - 1]) return false; // 2. Use the bar's own range double range = high[i] - low[i]; if(range <= 0.0) return false; // 3. Close must be in the upper 25 percent of the bar range double upperQuarter = high[i] - (range * 0.25); if(close[i] < upperQuarter) return false; // 4. Best patterns only if requested if(requireCloseAboveOpen && close[i] <= open[i]) return false; return true; }
各関数は、指定されたインデックスの単一バーを評価し、ラリー・ウィリアムズのルールを正確に適用します。強気セットアップの場合、バーは前のバーより高く終値を形成し、さらに自身のレンジの下側25%以内で終値を形成する必要があります。弱気セットアップの場合は、このロジックを反転させます。つまり、安値で終値を形成し、バーのレンジ上側25%以内で終値を形成することが条件になります。
さらに、オプション条件によって、最も厳格なパターン条件を適用することもできます。たとえば、より高品質なセットアップの場合、終値が始値より下(強気の場合)または上(弱気の場合)で終了することを要求できます。このロジックを専用の関数へ分離することで、インジケータはモジュール化され、後から拡張しやすい構造になります。
隠れスマッシュデーパターンの確認
ラリー・ウィリアムズは、次のバーの動きが、最も強力な隠れスマッシュデーバーパターンを確認する要素になることを強調しました。これをサポートするために、2つの確認関数を導入します。強気パターンは、次のバーが隠れスマッシュデーバーの高値を上回って終値を形成した場合のみ確認されます。
//+------------------------------------------------------------------+ //| Returns true if a Hidden Smash Day Buy pattern is confirmed | //| Confirmation requires next bar to close above smash bar high | //+------------------------------------------------------------------+ bool IsConfirmedHiddenSmashBuyAtIndex(const double &open[], const double &high[], const double &low[], const double &close[], int index ) { int confirmationIndex = index + 1; // Ensure confirmation bar exists if(confirmationIndex >= ArraySize(close)) return false; // Smash bar must be valid if(!IsHiddenSmashBuyBarAtIndex(index, open, high, low, close)) return false; // Confirmation close above smash bar high if(close[confirmationIndex] > high[index]) return true; return false; }
弱気パターンは、次のバーが隠れスマッシュデーバーの安値を下回って終値を形成した場合のみ確認されます。
//+------------------------------------------------------------------+ //| Returns true if a Hidden Smash Day Sell pattern is confirmed | //| Confirmation requires next bar to close below smash bar low | //+------------------------------------------------------------------+ bool IsConfirmedHiddenSmashSellAtIndex(const double &open[], const double &high[], const double &low[], const double &close[], int index ) { int confirmationIndex = index + 1; // Ensure confirmation bar exists if(confirmationIndex >= ArraySize(close)) return false; // Smash bar must be valid if(!IsHiddenSmashSellBarAtIndex(index, open, high, low, close)) return false; // Confirmation close below smash bar low if(close[confirmationIndex] < low[index]) return true; return false; }
これらの確認関数は、基本となる検出ロジックを再利用し、確認ステップを追加します。このアプローチにより、重複した処理を避け、インジケータ全体で一貫したパターン評価を確保できます。
インジケータ初期化時のパターンマッピング
インジケータが初めてチャートへ適用された時点では、まだ過去のパターンは処理されていません。これに対応するため、専用の初期マッピング関数が導入されます。
//+------------------------------------------------------------------+ //| Scans historical bars and maps Hidden Smash patterns on load | //+------------------------------------------------------------------+ void MapHiddenSmashPatternsOnInitialization( const int rates_total, const double &open[], const double &high[], const double &low [], const double &close[], int hidden_SmashValidationMode, double closePercentThreshold = 25.0 ) { //--- Safety check if(rates_total <= 1) return; //--- Loop through all historical bars for(int i = 1; i <= rates_total - 1; i++) { //--- Default to empty buySmashArrowBuffer[i] = EMPTY_VALUE; sellSmashArrowBuffer[i] = EMPTY_VALUE; //--- Mode 1: Bar only identification if(hiddenSmashValidationMode == HIDDEN_SMASH_BAR_ONLY) { if(IsHiddenSmashBuyBarAtIndex(i, open, high, low, close)) { buySmashArrowBuffer[i] = low[i]; } if(IsHiddenSmashSellBarAtIndex(i, open, high, low, close)) { sellSmashArrowBuffer[i] = high[i]; } } //--- Mode 2: Confirmed pattern identification else if(hiddenSmashValidationMode == HIDDEN_SMASH_CONFIRMED) { if(IsConfirmedHiddenSmashBuyAtIndex(open, high, low, close, i)) { buySmashArrowBuffer[i] = low[i]; } if(IsConfirmedHiddenSmashSellAtIndex(open, high, low, close, i)) { sellSmashArrowBuffer[i] = high[i]; } } } }
この関数は、利用可能なすべての過去バーをループ処理し、選択された検証モードに従って各バーを評価します。基本検出モードでは、各バーを個別に評価します。確認モードが選択されている場合は、確認用の次のバーと組み合わせて評価します。
有効なパターンが検出されると、対応するバッファへ矢印の値が書き込まれます。それ以外のバーは空の状態のまま維持され、実際の隠れスマッシュデーバーパターンのみが表示されるようになります。この処理は、不要な再計算を避けるため、prev_calculated == 0ブロック内で1回だけ実行されます。
//+------------------------------------------------------------------+ //| 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[]) { ... //--- This block will be executed whenever the indicator is initially attached on a chart if(prev_calculated == 0){ //--- Start with clean arrays ArrayInitialize(buySmashArrowBuffer, EMPTY_VALUE); ArrayInitialize(sellSmashArrowBuffer, EMPTY_VALUE); //--- Map all hidden smash bar patterns MapHiddenSmashPatternsOnInitialization(rates_total, lwOpen, lwHigh, lwLow, lwClose, hiddenSmashValidationMode); } ... return(rates_total); }
新しいバー形成時のパターン更新
市場は常に変化しているため、インジケータは新しいバーの形成に対応する必要があります。新しいバーが出現すると、インジケータは直近で完成したバーを評価し、それに応じてバッファの値を更新します。
//+------------------------------------------------------------------+ //| Evaluates hidden smash patterns on new bar formation | //| Updates indicator buffers for the recently completed bar | //+------------------------------------------------------------------+ void ProcessHiddenSmashOnNewBar( const double &open[], const double &high[], const double &low[], const double &close[], int rates_total ) { int completedBarIndex = rates_total - 2; int currentBarIndex = rates_total - 1; //--- Safety check if(completedBarIndex < 0) return; //--- Always clear buffers for involved bars buySmashArrowBuffer [completedBarIndex] = EMPTY_VALUE; sellSmashArrowBuffer[completedBarIndex] = EMPTY_VALUE; buySmashArrowBuffer [currentBarIndex] = EMPTY_VALUE; sellSmashArrowBuffer[currentBarIndex] = EMPTY_VALUE; //--- Mode 1: Detect bar only if(hiddenSmashValidationMode == HIDDEN_SMASH_BAR_ONLY) { if(IsHiddenSmashBuyBarAtIndex(completedBarIndex, open, high, low, close)) buySmashArrowBuffer[completedBarIndex] = low[completedBarIndex]; if(IsHiddenSmashSellBarAtIndex(completedBarIndex, open, high, low, close)) sellSmashArrowBuffer[completedBarIndex] = high[completedBarIndex]; return; } //--- Mode 2: Confirmed smash if(hiddenSmashValidationMode == HIDDEN_SMASH_CONFIRMED) { int smashBarIndex = completedBarIndex - 1; if(smashBarIndex < 0) return; if(IsHiddenSmashBuyBarAtIndex(smashBarIndex, open, high, low, close)) { if(close[completedBarIndex] > high[smashBarIndex]) buySmashArrowBuffer[smashBarIndex] = low[smashBarIndex]; } if(IsHiddenSmashSellBarAtIndex(smashBarIndex, open, high, low, close)) { if(close[completedBarIndex] < low[smashBarIndex]) sellSmashArrowBuffer[smashBarIndex] = high[smashBarIndex]; } } }
これにより、過去データ全体を再処理することなく、パターンを迅速に検出できます。別々のロジックによって基本検出と確認済み検出を処理し、選択されたモードに応じて、正しいバーインデックスに矢印が表示されるようにします。
//+------------------------------------------------------------------+ //| 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[]) { ... //--- This block is executed on new bar open if(prev_calculated != rates_total && prev_calculated != 0){ //--- Update buffers on new bar open ProcessHiddenSmashOnNewBar(lwOpen, lwHigh, lwLow, lwClose, rates_total); } ... return(rates_total); }
このアプローチにより、パフォーマンスと精度のバランスが取られ、低時間足でもインジケータが高い応答性を維持できます。
リアルタイムティック更新のサポート
インジケータを実際の市場環境で使用できるようにするため、リアルタイムでのティック処理も実装されています。
//+------------------------------------------------------------------+ //| Evaluates hidden smash patterns on tick updates | //| Supports real-time detection and confirmation logic | //+------------------------------------------------------------------+ void ProcessHiddenSmashOnTick( const double &open[], const double &high[], const double &low[], const double &close[], int rates_total ) { int currentBarIndex = rates_total - 1; int completedBarIndex = rates_total - 2; if(currentBarIndex < 0) return; //--- Always reset current bar buffer values buySmashArrowBuffer [currentBarIndex] = EMPTY_VALUE; sellSmashArrowBuffer[currentBarIndex] = EMPTY_VALUE; //--- Mode 1: Bar-only detection in real time if(hiddenSmashValidationMode == HIDDEN_SMASH_BAR_ONLY) { if(IsHiddenSmashBuyBarAtIndex(currentBarIndex, open, high, low, close)) buySmashArrowBuffer[currentBarIndex] = low[currentBarIndex]; if(IsHiddenSmashSellBarAtIndex(currentBarIndex, open, high, low, close)) sellSmashArrowBuffer[currentBarIndex] = high[currentBarIndex]; return; } //--- Mode 2: Confirmed smash evaluated continuously if(hiddenSmashValidationMode == HIDDEN_SMASH_CONFIRMED) { int smashBarIndex = completedBarIndex; if(smashBarIndex < 0) return; if(IsHiddenSmashBuyBarAtIndex(smashBarIndex, open, high, low, close)) { if(close[currentBarIndex] > high[smashBarIndex]) buySmashArrowBuffer[smashBarIndex] = low[smashBarIndex]; } if(IsHiddenSmashSellBarAtIndex(smashBarIndex, open, high, low, close)) { if(close[currentBarIndex] < low[smashBarIndex]) sellSmashArrowBuffer[smashBarIndex] = high[smashBarIndex]; } } }
各ティックを受信するたびに、インジケータは現在のバーを評価し、バッファの値を動的に調整します。
//+------------------------------------------------------------------+ //| 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[]) { ... //--- This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ //--- Update buffers on new tick ProcessHiddenSmashOnTick(lwOpen, lwHigh, lwLow, lwClose, rates_total); } return(rates_total); }
これにより、基本的な隠れスマッシュデーバーは形成されるとすぐに表示され、確認済みパターンは確認条件が満たされた瞬間に更新されます。このリアルタイムな応答性により、このインジケータは裁量分析やアクティブな監視において実用的なものになります。
矢印プロットによる視覚出力の設定
正しいバッファ値が設定されていても、インジケータにデータの描画方法が指定されていなければ、チャート上には何も表示されません。矢印ベースのプロットは、OnInit内のPlotIndexSet関数を使用して設定されます。
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Configure Graphic Plots PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(0, PLOT_ARROW, 233); PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrSeaGreen); PlotIndexSetInteger(0, PLOT_ARROW_SHIFT, +20); PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2); PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (0, PLOT_LABEL, "Hidden Smash Buy Bar"); PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW); PlotIndexSetInteger(1, PLOT_ARROW, 234); PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrBlack); PlotIndexSetInteger(1, PLOT_ARROW_SHIFT, -20); PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 2); PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetString (1, PLOT_LABEL, "Hidden Smash Sell bar"); //--- General indicator configurations IndicatorSetString(INDICATOR_SHORTNAME, "Larry Williams Market Structure Indicator"); return(INIT_SUCCEEDED); }
強気の隠れスマッシュデーバーは、価格の下側に配置された上向き矢印で表示されます。弱気の隠れスマッシュデーバーは、価格の上側に配置された下向き矢印で表示されます。色、サイズ、シフト、ラベルは明示的に定義されており、異なる銘柄や時間足でも一貫して描画されるようになっています。これらのプロット設定が完了すると、インジケータは視覚的にも完成します。
この段階で、インジケータは完全に機能する状態になります。ラリー・ウィリアムズのルールを使用して隠れスマッシュデーパターンを検出し、複数の検証モードをサポートし、市場の変化に応じて動的に更新され、チャート上に明確な視覚シグナルを表示します。
完全な実装コードは、添付されたlwHiddenSmashDayIndicator.mq5という名前のソースファイルに提供されています。実際の使用においては、記事内にソースコード全体を掲載するよりも、添付ファイルを参照する方法が推奨されます。
このセクションでは、隠れスマッシュデーインジケータの完全な開発を終了し、テスト、解釈、そしてさらなる検証への土台を整えます。
テストと視覚的確認
まず、MetaEditor内でインジケータのソースコードをコンパイルします。すべての手順が正しく実行されていれば、コードはエラーや警告なしでコンパイルされるはずです。正常なコンパイルは、すべての関数が正しく定義され、バッファが適切に登録され、インジケータのロジック構造が正しく構築されていることを確認します。
コンパイルエラーが発生した場合、最も安全な方法は、現在作業しているファイルと添付されたlwHiddenSmashDayIndicator.mq5ソースコードを比較することです。関数シグネチャやバッファ処理の小さな違いでも問題につながる可能性があり、参照実装と照合することで迅速に解決できます。
コンパイルが成功すると、インジケータを価格チャートへ適用できます。読み込み後、選択された検証モードに基づき、有効な隠れスマッシュデーパターンが検出された過去バー上に、矢印がすぐに表示されます。強気パターンは価格の下にシーグリーン色の矢印で表示され、弱気パターンは価格の上に黒の矢印で表示されます。
完成したインジケータが実際のチャート上でどのように表示されるかを示すスクリーンショットを以下に示します。

この視覚的な確認により、検出ロジックと描画設定の両方を検証できます。
ここからは、異なる銘柄や時間足でこのインジケータを試してみる価値があります。パターンロジックは各バー自身のレンジに基づいて適用されるため、イントラデイチャートから高時間足まで自然に適応します。異なる市場環境において隠れスマッシュデーパターンがどのように機能するかを観察することで、新しい研究アイデアや実用的な応用方法を見つけることができます。結論
- 価格チャート上で隠れスマッシュデーの買いおよび売りセットアップを客観的に検出する
- カスタムインジケータを使用して、ラリー・ウィリアムズの反転パターンを直接可視化する
- 複数の銘柄や時間足において、パターンを一貫した方法で検証する
- 検出ロジックを拡張し、研究用途や自動売買システムへの統合に利用する
- 潜在的なトレード機会を分析する際に、追加のコンテキストフィルタを適用する
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/21392
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5取引ツール(第23回):カメラ制御対応DirectX 3Dグラフによる二項分布分析
PDアレイを使いこなす:PDアレイ内のインバランスを活用したトレードの最適化
MQL5における切断ニュートン共役勾配(TNC)アルゴリズムの実装
KnitPkgを用いたMQL5向けパッケージベース開発手法
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
ありがとうございます