ラリー・ウィリアムズの『市場の秘密』(第13回):隠れスマッシュデー反転パターンの自動化
はじめに
Long Term Secrets to Short Term Tradingの中で、ラリー・ウィリアムズは、隠れスマッシュデーを、従来のスマッシュデーをより控えめかつ「内部的」に表れるパターンとして紹介しています。チャート上では非常にシンプルに見えます。必ずしも直前の高値や安値を明確に更新する必要はありませんが、翌セッションがその水準を試した際に、相場がその方向へ動きを継続できないことによって、相場の勢いが失われつつあることを示唆します。重要なのは、隠れスマッシュデーのバーそのものは売買シグナルではないという点です。ウィリアムズはこの点を明確に述べています。実際のエントリーは、翌セッションで価格が隠れスマッシュデーバーの基準となる水準を突破し、その動きが確認された後に初めて成立します。
しかし、このパターンが意図的に目立ちにくく設計されていることこそが裁量トレードでは解釈の余地を生みます。「レンジの下位25%あるいは上位25%」をどこから測るのか、何をもって確認とみなすのか、ストップロスはスマッシュデーバーの構造に置くべきか、それともボラティリティに応じて広げるべきか、さらにシグナルが連続した場合にポジションを重ねるべきかなど、判断が人によって変わってしまいます。
アルゴリズムトレーダーやMQL5開発者に必要なのは、このような解釈ではなく、曖昧さのないルールです。すべての銘柄で同一条件を適用し、毎回同じ確認ロジックを使用し、ストラテジーテスター上で再現可能なリスク管理を実装する必要があります。本記事の目的は、隠れスマッシュデーを明確なルールへ落とし込み、それをMQL5で実装することです。シグナル生成、エントリー、ストップロス、テイクプロフィット、ポジションサイズのすべてを自動で計算・実行し、裁量による曖昧さを排除したEAを構築します。
隠れスマッシュデー反転パターンを理解する
隠れスマッシュデーは、バー全体のレンジに対する終値の位置と、前バーの終値との比較によって定義されます。
強気な隠れスマッシュデーセットアップでは、以下の4つの条件を満たす必要があります。
- バーの終値が前バーの終値より高いこと
- バーのレンジは、そのバー自身の高値から安値までで測定すること
- 終値がバー全体のレンジの下位25%以内に位置していること
- より厳格なフィルタとして、終値が始値を下回っていることを条件に追加することもできます

この構造は、終値自体は前バーより高いものの、バーのレンジ内では弱い位置で引けていることを示し、相場内部で勢いが失われつつある可能性を示唆します。
弱気の隠れスマッシュデーでは、条件が逆になります。
- バーの終値が前バーの終値より低いこと
- レンジはそのバー自身の高値と安値で定義すること
- 終値がバー全体のレンジの上位25%以内に位置していること
- より厳格な条件として、終値が始値を上回っていることを要求することもできます

どちらの場合でも、スマッシュバー自体は単なるセットアップ条件にすぎません。それだけでは取引は開始されません。次に完成するバーで確認する必要があります。
この実装では、確認バーの終値がスマッシュバーの極値を超えた場合にのみ成立します。買いシグナルの場合、確認バーはスマッシュバーの高値を上回って完全にクローズする必要があります。売りシグナルの場合、確認バーはスマッシュバーの安値を下回って完全にクローズする必要があります。
この終値による確認ルールは、本記事全体およびエキスパートアドバイザー(EA)全体を通じて一貫して使用されています。
隠れスマッシュデーを実践的なトレードルールへ変換する
自動化する前に、隠れスマッシュデーのパターンを、管理可能で再現性のある方法でどのように取引するかを決定する必要がありました。このEAの目的は、考えられるすべてのシグナルを取引することではありません。ラリー・ウィリアムズの考え方に一定の構造を与えながら、トレーダーが裁量判断を加える余地を残すというより、検証目的で柔軟に設定できるようにすることが目的です。
最初の設計ルールは、判断のタイミングに関するものです。すべてのパターン検出および取引実行ロジックは、新しいバーが開始された時点でのみ評価されます。これにより、隠れスマッシュデーのバーが完全に形成され、確認バーもそのセッションを終了していることが保証されます。この方法を採用することで、不完全な価格情報に基づいて判断することを避け、元の手法で定義されているパターンの考え方と一致した運用が可能になります。
2つ目のルールは、エントリー前に必ず確認することです。隠れスマッシュデーは、それ単独で取引されることはありません。この実装では、確認条件として、スマッシュバーの極値を超える完全なバー終値を要求します。買いセットアップの場合、次のセッションは隠れスマッシュバーの高値を上回ってクローズする必要があります。売りセットアップの場合、次のセッションはその安値を下回ってクローズする必要があります。すべての判定は新しいバーの開始時点でのみ実行されます。これにより、スマッシュバーと確認バーの両方が完全に完成した後でのみ、取引を検討できるようになります。
このEAにおける重要な設計判断の1つは、取引方向を明確に制御できるようにしたことです。このシステムには、ロングトレードのみ、ショートトレードのみ、両方を許可するの3つの異なる運用モードがあります。この機能は、ユーザー設定項目として意図的に公開されています。なぜなら、隠れスマッシュデーのパターンは、すべての市場環境において同じように機能するわけではないからです。ある市場や期間では買いセットアップが優位になる一方で、別の市場環境では売りセットアップの方が信頼性が高い場合があります。取引方向を制御できるようにすることで、このEAは固定されたブラックボックスではなく、研究や検証に利用できるツールになります。
リスク管理は後付けの要素ではなく、システムの中心的な構成要素として扱われています。ストップロスは2つの異なる方法で計算できます。1つ目の方法では、隠れスマッシュデーの構造的な水準を利用し、パターンの高値または安値にストップロスを設定します。2つ目の方法では、市場のボラティリティを利用し、ユーザーが指定した倍率のATRを適用します。この2つのアプローチを用いることで、システムは価格構造と変化するボラティリティ環境の両方に対応できます。
ポジションサイズも柔軟に設計されています。このEAは、固定されたエクスポージャーを好むトレーダー向けの手動ロット設定に対応すると同時に、口座残高の割合に基づく自動ポジションサイジングもサポートしています。自動サイジングを有効にした場合、銘柄やボラティリティに関係なく、各取引で資金の一定割合のみをリスクにさらします。これにより、長期的に安定したパフォーマンスを維持しやすくなります。
もう1つの重要なルールは、ポジションの排他管理です。このEAは、同時に複数のポジションを保有することはありません。新規注文を発注する前に、本戦略では常に既存の未決済ポジションが存在するかどうかを確認します。これにより、ポジションの重複を防ぎ、リスク管理を簡素化するとともに、バックテストにおけるパフォーマンス分析をより明確におこなうことができます。
これらのルールを組み合わせることで、隠れスマッシュデーパターンは規律ある売買システムへと変換されます。それぞれの判断は、ラリー・ウィリアムズの本来の意図を尊重しつつ、自動売買、バックテスト、および長期的なパフォーマンス評価に必要な体系性を付加しています。
隠れスマッシュデー戦略の実行フロー
If a new bar has opened Update ATR values If no open position exists Check for Hidden Smash Buy Setup at index two If the setup is valid Check if the confirming bar is closed above the smash bar high If confirmed Compute stop loss Compute the take profit Calculate position size Open buy order Check for Hidden Smash Sell Setup at index two If the setup is valid Check if the confirming bar is closed below the smash bar low If confirmed Compute stop loss Compute the take profit Calculate position size Open sell order
- セットアップとエントリーの確認が明確に分離される
- 確定したバーのみが判定に使用される
- ストップロスとテイクプロフィットが注文実行前に決定される
- 注文を送信する前にリスクが計算される
- 常に保有できるポジションは1つだけに制限される
このように実行フローを整理することで、ソースコードはこれらの手順をそのまま表現したものとなります。
隠れスマッシュデーEAの構築
ここからは、実際の実装フェーズに入ります。まず売買ロジックを実装する前に、隠れスマッシュデーEAの基本構造を段階的に組み立てていきます。この段階の目的は、すぐに売買することではなく、今後より高度な機能を追加していくための、保守性と信頼性に優れた土台を構築することです。
先に進む前に、いくつか前提条件を確認しておきます。このセクションでは、読者がMQL5プログラミング言語の基本的な構文に加え、変数、関数、列挙型、標準ライブラリについて理解していることを前提としています。また、MetaTrader 5の基本的な操作にも慣れていることを想定しており、チャートの操作、EAの適用、ストラテジーテスターの実行などを問題なくおこなえることが望まれます。さらに、MetaEditorを使用してソースファイルを作成し、コードをコンパイルし、コンパイラからのメッセージを確認できる程度の知識も必要です。
学習効果を高めるため、本記事には完成版のEAのソースファイルlwHiddenSmashDayExpert.mq5を添付しています。記事を読み進めながら、このファイルを別タブで開いておくと、新しいコンポーネントが追加されるたびに内容を比較しながら理解を深めることができます。
それでは、MetaEditorで新しい空のEAファイルを作成し、以下に示す初期テンプレートコードを貼り付けるところから始めましょう。
//+------------------------------------------------------------------+ //| lwHiddenSmashDayExpert.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" //+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> //+------------------------------------------------------------------+ //| Defines how stop loss levels are determined for smash day trades | //+------------------------------------------------------------------+ enum ENUM_STOP_LOSS_MODE { SL_ATR_BASED, SL_SMASH_BAR_STRUCTURE }; //+------------------------------------------------------------------+ //| Defines the trade direction | //+------------------------------------------------------------------+ enum ENUM_TRADE_DIRECTION { TRADE_LONG, TRADE_SHORT, TRADE_BOTH }; enum ENUM_LOT_SIZE_INPUT_MODE { MODE_MANUAL, MODE_AUTO }; //+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; input group "ATR configuration parameters" input int32_t atrPeriod = 14; input double atrMultiplier = 2.0; input group "Trade and Risk Management" input ENUM_STOP_LOSS_MODE smashStopLossMode = SL_SMASH_BAR_STRUCTURE; input double rewardRiskRatio = 3.0; input ENUM_TRADE_DIRECTION direction = TRADE_LONG; input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode = MODE_AUTO; input double riskPerTradePercent = 1.0; input double positionSize = 0.1; //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade; //--- To hep track current market prices for Buying (Ask) and Selling (Bid) double askPrice; double bidPrice; //--- To store current time datetime currentTime; //--- To help track new bar open datetime lastBarOpenTime; //--- ATR Values int atrHandle; double atrValues []; //--- Protective stops double stopLossLevel; double takeProfitLevel; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); //--- Initialize global variables lastBarOpenTime = 0; //--- Initialize the ATR indicator atrHandle = iATR(_Symbol, timeframe, atrPeriod); if(atrHandle == INVALID_HANDLE){ Print("Error while initializing the ATR indicator ", GetLastError()); return(INIT_FAILED); } //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar) ArraySetAsSeries(atrValues, true); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- Notify why the program stopped running Print("Program terminated! Reason code: ", reason); //--- Release ATR if(atrHandle != INVALID_HANDLE){ if(IndicatorRelease(atrHandle)){ Print("The computer memory tracking ATR has been freed."); } } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Retrieve current market prices for trade execution askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); currentTime = TimeCurrent(); } //+------------------------------------------------------------------+
このコードではまだ取引をおこなっていません。その代わりに、EAが後々依存する構造、構成オプション、およびコアサービスを定義します。
ファイルヘッダーとID
//+------------------------------------------------------------------+ //| lwHiddenSmashDayExpert.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"
冒頭部分では、EAのアイデンティティを定義します。ファイル名、所有者、およびバージョン情報を指定します。この部分は取引ロジックに直接影響を与えるものではありませんが、コードの構成、作者の明確化、および長期的な保守において重要な役割を果たします。これらの特性を早期に定義することで、将来の改訂作業が追跡可能かつ専門的なものとなることを保証できます。
取引ライブラリの読み込み
次のステップは、標準の取引ライブラリをインポートすることです。
//+------------------------------------------------------------------+ //| Standard Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh>
このライブラリは、後で売買注文を実行するために使用されるCTradeクラスへのアクセスを提供します。この標準インターフェースを利用することで、注文実行はより安全で明確になり、MetaTraderの内部取引処理との整合性も向上します。
現段階では、ライブラリのみをインクルードします。実際の注文実行は、シグナルロジックが確立され次第、後ほど導入されます。
行動を形作る列挙
入力やロジックを定義する前に、列挙型のセットを導入します。
//+------------------------------------------------------------------+ //| Defines how stop loss levels are determined for smash day trades | //+------------------------------------------------------------------+ enum ENUM_STOP_LOSS_MODE { SL_ATR_BASED, SL_SMASH_BAR_STRUCTURE }; //+------------------------------------------------------------------+ //| Defines the trade direction | //+------------------------------------------------------------------+ enum ENUM_TRADE_DIRECTION { TRADE_LONG, TRADE_SHORT, TRADE_BOTH }; //+------------------------------------------------------------------+ //| To track lot size input mode | //+------------------------------------------------------------------+ enum ENUM_LOT_SIZE_INPUT_MODE { MODE_MANUAL, MODE_AUTO };
これらの列挙型は、EAにおける重要な設計上の判断を明確に定義し、その動作を分かりやすく設定できるようにするためのものです。
最初の列挙型は、ストップロスの計算方法を定義します。一方のモードでは、市場のボラティリティを表すATRに基づいてストップロスを設定し、もう一方のモードでは、隠れスマッシュデーを形成したバーの高値・安値を基準としてストップロスを設定します。このように区別しているのは、リスク管理に対する異なる2つの考え方を反映しているためです。
2つ目の列挙型は、売買方向を定義します。EAは買い(ロング)のみ、売り(ショート)のみ、またはその両方を対象として動作させることができます。これは重要な設計要素であり、隠れスマッシュデーのパターンは、市場環境によって買いと売りで必ずしも同じようなパフォーマンスを示すとは限らないためです。
最後の列挙型は、ロットサイズの決定方法を制御します。ロット数を手動で指定することも、口座資金に対するリスク割合に基づいて自動計算することも可能です。この選択肢により、EAは固定ロットによる実行ツールとしても、リスクを一定に保った検証・研究用システムとしても利用できます。
このように設定項目を列挙型として定義することで、意味が分かりにくい真偽値フラグを避けることができ、EAの設定内容を読みやすく、理解しやすいものにしています。
ユーザー入力設定
列挙型が定義されたので、次はユーザー入力について説明します。設定パネルの見やすさと直感的な操作性を維持するため、入力項目は論理的にグループ化されています。
//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ulong magicNumber = 254700680002; input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; input group "ATR configuration parameters" input int32_t atrPeriod = 14; input double atrMultiplier = 2.0; input group "Trade and Risk Management" input ENUM_STOP_LOSS_MODE smashStopLossMode = SL_SMASH_BAR_STRUCTURE; input double rewardRiskRatio = 3.0; input ENUM_TRADE_DIRECTION direction = TRADE_LONG; input ENUM_LOT_SIZE_INPUT_MODE lotSizeMode = MODE_AUTO; input double riskPerTradePercent = 1.0; input double positionSize = 0.1;
まず「Information」グループには、マジックナンバーと時間足が含まれています。マジックナンバーを設定することで、このEAが保有したポジションを他のEAや裁量取引と区別して識別でき、独立して管理できるようになります。
「ATR Configuration」グループでは、ATRを用いたストップロスの計算に使用する期間と倍率を設定します。これらの値によって、市場のボラティリティをどの程度ストップロスの距離に反映させるかが決まります。
「Trade and Risk Management」グループには、EAの動作を左右する最も重要な設定項目がまとめられています。ここでは、ストップロスの計算方法、リスクリワード比、売買方向、ロットサイズの決定方法、そしてリスク管理に関する各種パラメータを設定します。これらの入力値はすべて、取引数量の決定方法、ストップロスによる保護、そしてエントリー条件の適用方法に直接影響します。
この段階では、これらの入力項目は宣言されているだけであり、まだ実際の売買には使用されません。現時点での役割は、後続のロジックで利用するための設定値を定義することにあります。
グローバル変数と市場コンテキスト
入力パラメータの定義に続いて、グローバル変数を宣言します。これらの変数により、EAの異なる部分で状態を共有できるようになります。
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ //--- Create a CTrade object to handle trading operations CTrade Trade; //--- To hep track current market prices for Buying (Ask) and Selling (Bid) double askPrice; double bidPrice; //--- To store current time datetime currentTime; //--- To help track new bar open datetime lastBarOpenTime; //--- ATR Values int atrHandle; double atrValues []; //--- Protective stops double stopLossLevel; double takeProfitLevel;
注文実行を処理するために、CTradeオブジェクトを作成します。また、Ask価格やBid価格などの市場価格は、複数の関数で一貫して利用できるよう、グローバル変数として保持します。さらに、サーバー時刻もタイミング処理のために保持します。
バーに基づく売買判断を下すために、直近のバーの始値時刻を追跡する変数も用意します。これにより、新しいバーの開始を正確に検出できるようになり、日足パターンの分析に不可欠となります。
ATRに関連する変数も宣言します。インジケータハンドルはEAをATRインジケータに接続し、配列には最新のATR値を格納します。これらの値は、後にボラティリティに基づくストップロスの計算に使用されます。
最後に、ストップロス価格とテイクプロフィット価格を格納するための変数を定義します。これらの変数には、有効な売買シグナルが発生した後にのみ値が設定されます。
EA初期化処理
初期化関数は、EAを実行するための準備をします。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Assign a unique magic number to identify trades opened by this EA Trade.SetExpertMagicNumber(magicNumber); //--- Initialize global variables lastBarOpenTime = 0; //--- Initialize the ATR indicator atrHandle = iATR(_Symbol, timeframe, atrPeriod); if(atrHandle == INVALID_HANDLE){ Print("Error while initializing the ATR indicator ", GetLastError()); return(INIT_FAILED); } //--- Treat the following arrays as timeseries (index 0 becomes the most recent bar) ArraySetAsSeries(atrValues, true); return(INIT_SUCCEEDED); }
まず、マジックナンバーが取引オブジェクトに割り当てられます。これにより、このEAが発注するすべての取引に正しくマジックナンバーが付与されます。
次に、バーを追跡する変数を初期化します。この変数を0に設定することで、最初に検出されたバーの切り替わりを正しく処理できるようになります。
続いて、設定された時間足と期間を使用してATRインジケータを初期化します。この処理に失敗した場合、EAは直ちに停止します。これは、サポートされているストップロス設定方法の1つでATR値が必要となるためです。
最後に、ATR値を格納する配列を時系列配列として設定します。これにより、最新のATR値が常にインデックス0に格納されるため、その後の計算を簡潔にできます。
初期化が完了すると、EAは実行の準備を終えますが、この時点ではまだ売買はおこないません。
初期化解除とリソースの解放
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ //--- Notify why the program stopped running Print("Program terminated! Reason code: ", reason); //--- Release ATR if(atrHandle != INVALID_HANDLE){ if(IndicatorRelease(atrHandle)){ Print("The computer memory tracking ATR has been freed."); } } }
EAがチャートから削除されたとき、またはターミナルが終了したときには、初期化解除関数が呼び出されます。
ここでは、終了メッセージをログに出力し、ATRインジケータのハンドルを解放します。インジケータハンドルを解放することは、メモリリークを防ぎ、特に繰り返しテストする際に正常に終了するために重要です。
プレースホルダーとしてのティック関数
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Retrieve current market prices for trade execution askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); currentTime = TimeCurrent(); }
この初期段階では、ティック関数は基本的な処理のみを実行します。市場価格を更新し、現在のサーバー時刻を記録します。
現時点では、まだ取引の判断はおこないません。これは意図的な設計です。関数は、パターン検出とリスク管理のロジックが定義されるまでは最小限の構成に保たれます。このようにすることで、複雑化を早めることなく、各開発段階に集中できます。
基盤の構築
この時点では、EAは取引をおこないません。それは意図的なものです。
ここまでで構築したのは、安定した基本構造です。設定項目を定義し、市場情報を準備し、インジケータへのアクセスを設定するとともに、適切な初期化と終了処理を実現しています。ここで追加した各要素は、後に実装するロジックを支えるためのものです。
次のステップでは、パターン検出、シグナルの確認、および売買ルールを順に実装していきます。基盤がすでに整っているため、これらの機能を既存の構造へ無理なく組み込むことができます。
このように段階的に進めることで、EAは複雑さが増しても、読みやすく、テストしやすく、拡張しやすい構造を維持できます。
基盤が整ったので、ここからは売買ロジックを1つずつ組み立てていきます。それぞれの要素には明確な役割があり、これまでの設計を支えるものとなっています。本セクションでは、実装の速さではなく、分かりやすさを重視します。各コンポーネントを順番に追加し、その役割とEA全体の中での位置付けを説明します。
次の取引セッションの開始を検出する
隠れスマッシュデーのパターンは、確定したバーを対象に判定します。そのため、このEAでは、すべての売買判断を新しいバーの開始時におこないます。この動作を実現するため、まず選択した時間足で新しいバーが形成されたことを判定する小さなユーティリティ関数を定義します。
//+------------------------------------------------------------------+ //| Function to check if there's a new bar on a given chart timeframe| //+------------------------------------------------------------------+ bool IsNewBar(string symbol, ENUM_TIMEFRAMES tf, datetime &lastTm){ datetime currentTm = iTime(symbol, tf, 0); if(currentTm != lastTm){ lastTm = currentTm; return true; } return false; }
この関数は、コード内のユーティリティセクションに追加します。この関数は、最新バーの始値時刻と、以前に保存されたタイムスタンプを比較します。値が変化した場合、新しいバーが形成されたことを意味します。タイムスタンプは参照渡しによって更新されるため、ティックごとに一貫してこの処理を実行できます。このロジックをサポートするために、最後に確認されたバーの始値時刻を保存するグローバル変数が必要です。
//--- To help track new bar open datetime lastBarOpenTime;
この変数は、EA起動時に初期化され、状態を正しく追跡できるようにします。
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Initialize global variables lastBarOpenTime = 0; }
これ以降、EAは、各バーにつき1回だけロジックを実行するための信頼できる方法を持つことになります。
リスク管理のためのATR値の更新
このEAでサポートされているストップロス設定方法の1つでは、ATRを使用します。ATR値を最新の状態に保つため、新しいバーが形成された際にATRバッファを更新する関数を定義します。
//+------------------------------------------------------------------+ //| To update ATR values | //+------------------------------------------------------------------+ void UpdateATRValues(){ //--- Get some ATR Values int numberOfCopiedATRValues = CopyBuffer(atrHandle, 0, 0, 5, atrValues); if(numberOfCopiedATRValues == -1){ Print("Error while copying ATR values: ", GetLastError()); } }
この関数は、他のヘルパーユーティリティと同じ場所に配置します。この関数は、最新のATR値を時系列配列に取得します。この配列では、インデックス0が常に最新の値を示します。この設計により、ストップロス計算のロジックをシンプルで読みやすく保つことができます。
OnTick関数内では、新しいバーが検出された直後にATR値を更新します。
//+------------------------------------------------------------------+ //| Returns true if the bar at index forms a Hidden Smash Day Buy Bar | //| Logic aligned with indicator implementation | //+------------------------------------------------------------------+ bool IsHiddenSmashDayBuyBar(string symbol, ENUM_TIMEFRAMES tf, int index, double closePercentThreshold = 25.0, bool allowBullishClose = true, bool allowBearishClose = true){ //--- We need at least one previous bar if(index < 1){ return false; } double high = iHigh(symbol, tf, index); double low = iLow(symbol, tf, index); double open = iOpen(symbol, tf, index); double close = iClose(symbol, tf, index); double prevClose = iClose(symbol, tf, index + 1); double range = high - low; if(range <= 0.0) return false; //--- 1. Must close higher than previous bar if(close <= prevClose) return false; //--- 2. Close must be in lower portion of its own range double closePositionPercent = ((close - low) / range) * 100.0; if(closePositionPercent > closePercentThreshold) return false; //--- 3. Optional candle direction filter if(close > open && !allowBullishClose) return false; if(close < open && !allowBearishClose) return false; return true; }
この関数は、終値がパーセントのしきい値によって定義されたバーのレンジ下部に位置しているかどうかを判定します。オプションのパラメータにより、強気または弱気の終値を許可するかどうかを柔軟に設定できます。これは、ラリー・ウィリアムズが指摘した、どちらのケースも有効になり得るという考えを反映しています。
同様に、弱気の隠れスマッシュデーのバーを検出するための反対向きの関数も定義します。
//+--------------------------------------------------------------------+ //| Returns true if the bar at index forms a Hidden Smash Day Sell Bar | //| Logic aligned with indicator implementation | //+--------------------------------------------------------------------+ bool IsHiddenSmashDaySellBar(string symbol, ENUM_TIMEFRAMES tf, int index, double closePercentThreshold = 25.0, bool allowBullishClose = true, bool allowBearishClose = true){ //--- We need at least one previous bar if(index < 1){ return false; } double high = iHigh(symbol, tf, index); double low = iLow(symbol, tf, index); double open = iOpen(symbol, tf, index); double close = iClose(symbol, tf, index); double prevClose = iClose(symbol, tf, index + 1); double range = high - low; if(range <= 0.0) return false; //--- 1. Must close lower than previous bar if(close >= prevClose) return false; //--- 2. Close must be in upper portion of its own range double closePositionPercent = ((high - close) / range) * 100.0; if(closePositionPercent > closePercentThreshold) return false; //--- 3. Optional candle direction filter if(close > open && !allowBullishClose) return false; if(close < open && !allowBearishClose) return false; return true; }
これら2つの関数は、パターン検出の基盤となります。この段階では、まだ売買シグナルは生成されません。ここでおこなっているのは、候補となるバーを特定することだけです。
有効な売買シグナルの確認
隠れスマッシュデーは、確認が完了して初めて売買対象となります。ラリー・ウィリアムズは、確認は次のセッションがスマッシュバーの極値を超えて終値を形成したときに発生すると定義していますこのルールを明確に実装するために、強気セットアップ用の確認関数を定義します。
//+------------------------------------------------------------------+ //| Confirms a Hidden Smash Day Buy signal using a confirming bar | //+------------------------------------------------------------------+ bool IsHiddenSmashDayBuySignal(int confirmingBarIndex){ int smashBarIndex = confirmingBarIndex + 1; //--- Verify smash bar exists and is valid if(!IsHiddenSmashDayBuyBar(_Symbol, timeframe, smashBarIndex)) return false; //--- Confirmation requires a close above the smash bar high if(iClose(_Symbol, timeframe, confirmingBarIndex) > iHigh (_Symbol, timeframe, smashBarIndex)){ return true; } return false; }
このロジックでは、有効な隠れスマッシュデーのバーが存在し、次のバーの終値がその高値を上回っていることを確認します。
同様の関数を使用して、弱気シグナルを確認します。
//+------------------------------------------------------------------+ //| Confirms a Hidden Smash Day Sell signal using a confirming bar | //+------------------------------------------------------------------+ bool IsHiddenSmashDaySellSignal(int confirmingBarIndex){ int smashBarIndex = confirmingBarIndex + 1; //--- Verify smash bar exists and is valid if(!IsHiddenSmashDaySellBar(_Symbol, timeframe, smashBarIndex)) return false; //--- Confirmation requires a close below the smash bar low if(iClose(_Symbol, timeframe, confirmingBarIndex) < iLow (_Symbol, timeframe, smashBarIndex)){ return true; } return false; }
この段階で、EAは有効な取引シグナルを客観的に識別することができます。
1ポジションのみ保有するルールの適用
リスクを管理し、ロジックを予測可能なものに保つため、このEAは常に1つの未決済ポジションのみを保有するように設計されています。このルールを適用するために、2つのヘルパー関数を使用します。
//+------------------------------------------------------------------+ //| To verify whether this EA currently has an active buy position. | | //+------------------------------------------------------------------+ bool IsThereAnActiveBuyPosition(ulong magic){ for(int i = PositionsTotal() - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket == 0){ Print("Error while fetching position ticket ", _LastError); continue; }else{ if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY){ return true; } } } return false; } //+------------------------------------------------------------------+ //| To verify whether this EA currently has an active sell position. | | //+------------------------------------------------------------------+ bool IsThereAnActiveSellPosition(ulong magic){ for(int i = PositionsTotal() - 1; i >= 0; i--){ ulong ticket = PositionGetTicket(i); if(ticket == 0){ Print("Error while fetching position ticket ", _LastError); continue; }else{ if(PositionGetInteger(POSITION_MAGIC) == magic && PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL){ return true; } } } return false; }
これらの関数は、すべての保有ポジションをスキャンし、EAのマジックナンバーを持つ取引がすでにアクティブな場合にはtrueを返します。ポジションを新規建てする前に、両方のチェックが実施されます。
ストップロスとテイクプロフィットの計算
有効なシグナルが検出されたら、保護用の価格水準を計算する必要があります。ストップロス関数は、2つのモードをサポートします。ATRベースと、スマッシュバーの構造を基準とする方法です。
//+------------------------------------------------------------------+ //| Computes the stop loss level based on direction and SL mode | //+------------------------------------------------------------------+ double ComputeStopLossLevel(ENUM_TRADE_DIRECTION tDirection, ENUM_STOP_LOSS_MODE slMode, int smashBarIndex) { double slLevel = 0.0; //--- ATR based stop loss if(slMode == SL_ATR_BASED) { double atrValue = atrValues[0] * atrMultiplier; if(tDirection == TRADE_LONG) slLevel = askPrice - atrValue; else slLevel = bidPrice + atrValue; } //--- Smash bar structure based stop loss else if(slMode == SL_SMASH_BAR_STRUCTURE) { if(tDirection == TRADE_LONG) slLevel = iLow(_Symbol, timeframe, smashBarIndex); else slLevel = iHigh(_Symbol, timeframe, smashBarIndex); } return NormalizeDouble(slLevel, Digits()); }
ATRモードでは、ストップロスは現在のATRに倍率を掛けた距離だけ、エントリー価格から離れた位置に設定されます。構造モードでは、ストップロスはスマッシュバー自体の高値または安値に設定されます。この柔軟性により、コードを変更することなく、異なるリスク管理の考え方をテストできます。
テイクプロフィットの水準は、固定されたリスクリワード比を使用して算出されます。
//+------------------------------------------------------------------+ //| Computes the take profit level using risk reward ratio | //+------------------------------------------------------------------+ double ComputeTakeProfitLevel(ENUM_TRADE_DIRECTION tDirection, double stopLoss) { double entryPrice; double riskDistance; double takeProfit; if(tDirection == TRADE_LONG) { entryPrice = askPrice; riskDistance = entryPrice - stopLossLevel; takeProfit = entryPrice + (riskDistance * rewardRiskRatio); } else { entryPrice = bidPrice; riskDistance = stopLossLevel - entryPrice; takeProfit = entryPrice - (riskDistance * rewardRiskRatio); } return NormalizeDouble(takeProfit, Digits()); }
テイクプロフィットをリスクに直接関連付けることで、EAは一貫したポジションサイズと期待値のロジックを維持します。
取引の実行
自動的なポジションサイズを計算するためのヘルパー関数を定義します。
//+------------------------------------------------------------------+ //| Calculates position size using OrderCalcProfit for accuracy | //+------------------------------------------------------------------+ double CalculatePositionSizeByRisk(ENUM_ORDER_TYPE orderType, double entryPrice, double stopLossPrice){ //--- Amount willing to risk double amountAtRisk = (riskPerTradePercent / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE); //--- Calculate loss for 1 lot double lossPerLot = 0.0; if(!OrderCalcProfit(orderType, _Symbol, 1.0, // 1 lot entryPrice, stopLossPrice, lossPerLot)) { Print("OrderCalcProfit failed: ", GetLastError()); return 0.0; } // Loss will be negative for losing scenario lossPerLot = MathAbs(lossPerLot); if(lossPerLot <= 0.0) return 0.0; //--- Raw volume double volume = amountAtRisk / lossPerLot; //--- Apply broker constraints double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Normalize to step volume = MathFloor(volume / lotStep) * lotStep; if(volume < minLot) volume = minLot; if(volume > maxLot) volume = maxLot; return NormalizeDouble(volume, 2); }
2つのシンプルなラッパー関数が取引の実行を処理します。
//+------------------------------------------------------------------+ //| Calculates position size using OrderCalcProfit for accuracy | //+------------------------------------------------------------------+ double CalculatePositionSizeByRisk(ENUM_ORDER_TYPE orderType, double entryPrice, double stopLossPrice){ //--- Amount willing to risk double amountAtRisk = (riskPerTradePercent / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE); //--- Calculate loss for 1 lot double lossPerLot = 0.0; if(!OrderCalcProfit(orderType, _Symbol, 1.0, // 1 lot entryPrice, stopLossPrice, lossPerLot)) { Print("OrderCalcProfit failed: ", GetLastError()); return 0.0; } // Loss will be negative for losing scenario lossPerLot = MathAbs(lossPerLot); if(lossPerLot <= 0.0) return 0.0; //--- Raw volume double volume = amountAtRisk / lossPerLot; //--- Apply broker constraints double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Normalize to step volume = MathFloor(volume / lotStep) * lotStep; if(volume < minLot) volume = minLot; if(volume > maxLot) volume = maxLot; return NormalizeDouble(volume, 2); } //+------------------------------------------------------------------+ //| Function to open a market buy position | //+------------------------------------------------------------------+ bool OpenBuy(double entryPrice, double stopLoss, double takeProfit, double lotSize){ if(lotSizeMode == MODE_AUTO){ lotSize = CalculatePositionSizeByRisk(ORDER_TYPE_BUY, entryPrice, stopLoss); } if(!Trade.Buy(lotSize, _Symbol, entryPrice, stopLoss, takeProfit)){ Print("Error while executing a market buy order: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } return true; } //+------------------------------------------------------------------+ //| Function to open a market sell position | //+------------------------------------------------------------------+ bool OpenSell(double entryPrice, double stopLoss, double takeProfit, double lotSize){ if(lotSizeMode == MODE_AUTO){ lotSize = CalculatePositionSizeByRisk(ORDER_TYPE_SELL, entryPrice, stopLoss); } if(!Trade.Sell(lotSize, _Symbol, entryPrice, stopLoss, takeProfit)){ Print("Error while executing a market sell order: ", GetLastError()); Print(Trade.ResultRetcode()); Print(Trade.ResultComment()); return false; } return true; }
自動ロット計算が有効になっている場合、ロットサイズは設定されたリスク割合に基づいて計算されます。それ以外の場合は、手動で設定したポジションサイズが使用されます。これらの関数により、売買シグナルのロジックと取引実行の処理が分離され、コードの可読性と保守性が向上します。
OnTickで全体を統合する
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Retrieve current market prices for trade execution askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); currentTime = TimeCurrent(); //--- Execute on new bar open if(IsNewBar(_Symbol, timeframe, lastBarOpenTime)){ //--- Update ATR Values UpdateATRValues(); if(IsHiddenSmashDayBuySignal(1)) { Print("New Bar!!!"); stopLossLevel = ComputeStopLossLevel(TRADE_LONG, smashStopLossMode, 2); takeProfitLevel = ComputeTakeProfitLevel(TRADE_LONG, stopLossLevel); if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ if(direction == TRADE_LONG || direction == TRADE_BOTH){ OpenBuy(askPrice, stopLossLevel, takeProfitLevel, positionSize); } } } if(IsHiddenSmashDaySellSignal(1)){ Print("New Bar!!!"); stopLossLevel = ComputeStopLossLevel(TRADE_SHORT, smashStopLossMode, 2); takeProfitLevel = ComputeTakeProfitLevel(TRADE_SHORT, stopLossLevel); if(!IsThereAnActiveBuyPosition(magicNumber) && !IsThereAnActiveSellPosition(magicNumber)){ if(direction == TRADE_SHORT || direction == TRADE_BOTH){ OpenSell(bidPrice, stopLossLevel, takeProfitLevel, positionSize); } } } } }
すべてのコンポーネントはOnTick関数内で統合されます。新しいバーごとに、EAは次の処理をおこないます。
- ATR値を更新する
- 確認済みの買いまたは売りシグナルを確認する
- 有効なポジションが存在しないことを確認する
- 売買方向のフィルタリングを適用する
- ストップロスとテイクプロフィットを計算する
- 取引を実行する
この構造化されたフローにより、すべての条件が一致した場合のみ、各判断が確定した価格情報に基づいて取引が実行されます。
明確なテストのためのチャート準備
この段階で、EAのロジックは完成しています。テスト段階へ進む前に、もう1つ実用的な作業が必要です。それは、テストで使用する視覚的な環境を、見やすく整理された状態にすることです。
戦略テストは、単にパフォーマンス数値を確認するだけではありません。観察することも重要です。価格の動き、ローソク足の構造、そして取引の配置を、視覚的なノイズなしに明確に確認できる必要があります。デフォルトのチャート設定は、情報が多すぎる場合があり、特にバーごとにエントリーや決済を確認する際に、重要な詳細が見えにくくなることがあります。
この問題に対応するため、チャート表示を設定することだけを目的とした小さなユーティリティ関数を定義します。
//+------------------------------------------------------------------+ //| 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; }
この関数は、ソースファイルの末尾付近に、他のユーティリティヘルパー関数と並べて追加します。白い背景と、明確に区別できる強気・弱気ローソク足を使用した高コントラストのテーマを適用します。グリッド線は無効化され、ノイズを減らします。また、チャートは明示的にローソク足モードに設定されます。各設定は検証され、失敗した場合は直ちに報告されます。この方法により、取引を視覚的に確認する際に、価格構造と約定ポイントを容易に解釈できるようになります。
関数を定義した後の最後のステップは、EAの初期化時にこの関数を呼び出すことです。これにより、売買ロジックが開始される前に、チャート設定が一度だけ適用されます。
OnInit関数内に、以下のブロックを追加します。
//+------------------------------------------------------------------+ //| Expert 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); }
OnInit内で呼び出すことで、テストまたは実運用が開始される前に、視覚的な環境が準備されていることを保証します。何らかの理由でチャート設定に失敗した場合、意図しない状態で動作を続けるのではなく、EAは直ちに停止します。
この最後のステップを完了することで、EAはロジック面だけでなく、分析とテストのためにも適切に準備された状態になります。意思決定ロジックと視覚的なフィードバックの両方が連携して動作するようになり、これは隠れスマッシュデーのようなパターンベースの戦略を評価する際に重要です。
この時点で、EAは完全に機能します。完全な実装は添付ファイルlwHiddenSmashDayExpert.mq5に提供されており、読者は記事内の大量のコードをコピーすることなく、ロジックを確認、テスト、拡張できます。
この構造化されたアプローチは、ラリー・ウィリアムズの手法の根底にある考え方を反映しています。明確なルール、規律ある実行、そして市場構造への尊重です。コアロジックが完成したことで、次のステップは、これらの隠れたパターンが異なる市場や状況でどのように機能するかをテストし、評価することです。
隠れスマッシュデーEAのテスト
開発段階が完了した後、次のステップは、EAが意図したとおりに動作することを確認することです。これには2つの段階があります。最初に、ライブチャート上での視覚的な確認により、システムが隠れスマッシュバーを正しく識別し、定義されたルールに従ってポジションを開くことを確認します。次に、過去のデータを使用したヒストリカルテストにより、管理された条件下で戦略を評価します。
チャート上での視覚的確認
最初のステップは、EAが隠れスマッシュデーパターンを正確に識別し、適切な確認後のみ取引を実行することを確認することです。
この段階では、2つのスクリーンショットを示します。1つは保有中のロングポジションを示します。

もう一方のグラフは、EAが建てたショートポジションを示します。

ロング取引の例では、隠れスマッシュバーは自身のレンジ下部で終値を形成しながら、前のバーの終値より上で終了しています。これは、強気の隠れスマッシュデーに必要な構造条件を満たしています。その後のバーがスマッシュバーの高値を上回って終了することで、反転が確認されます。この確認バーが完成すると、EAは新しいバーの開始時にロングポジションを開きます。
ショート取引の例では、同じロジックが反対方向に適用されます。隠れスマッシュバーはレンジ上部で終了し、前のバーの終値より下で終了しています。これにより、弱気の隠れスマッシュセットアップが形成されます。続くセッションがスマッシュバーの安値を下回って終了することで、シグナルが確認されます。次のバーの開始時に、EAはショートポジションを許可します。
これらの例により、システムが記事の前半で定義したルールを正しく適用していることが確認できます。セットアップバーは正しく検出され、確認には確定したバーが必要であり、シグナル条件が満たされた後にのみ取引が実行されます。
過去パフォーマンスのテスト
チャート上で売買ロジックが正しく動作することを確認した後、次のステップは過去データを使用してシステムを評価することです。
バックテストは、日足のゴールド(XAUUSD)を使用して実施しました。テスト期間は2025年1月1日に開始し、2026年2月28日まで実施され、1年以上の市場動向を対象としています。
このテストでは、EAは買いと売りの両方の隠れスマッシュデーセットアップを許可するよう設定しました。自動ポジションサイズ計算を有効にし、各取引のリスクは口座残高の1%に設定しました。
結果を完全に再現可能にするため、2つの補助ファイルを記事に添付しています。最初のファイルconfigurations.iniには、ストラテジーテスター環境の設定が含まれています。2番目のファイルparameters.setには、テストで使用した正確な入力パラメータが含まれています。
バックテスト結果

テストは初期口座残高10,000ドルで開始しました。テスト期間中、システムは1,371.64ドルの純利益を生み出し、13%を少し上回るリターンとなりました。テスト期間中の勝率は100%でした。この特定のサンプル期間では、ロングセットアップのみが発生しました。
以下のエクイティカーブは、大きな下落や急激なドローダウンがなく、滑らかな上昇推移を示しています。

事前に定義されたストップロスルールが損失を制御し、1ポジションのみを保有するルールが重複したエクスポージャーを防ぎました。
これらの結果は、1つの設定と1つの市場に基づくものですが、テスト条件下において、EAが隠れスマッシュデーのロジックを一貫して実行し、安定したエクイティ推移を示すことを確認できます。
戦略の検証
このプロジェクトの主な目的の1つは、固定された売買システムではなく、構造化された研究ツールを提供することです。EAには、実験を可能にする複数のパラメータが用意されています。売買方向、ストップロス方法、リスクリワード比、ATR設定、リスク割合はすべて、ソースコードを変更することなく調整できます。
他の銘柄、時間足、またはパラメータの組み合わせで追加のテストをすることで、隠れスマッシュデーパターンが異なる条件でどのように動作するか、またはどの条件でより効果的に機能するかを発見できる可能性があります。これらの違いを検証することを強く推奨します。テスト中に発見された興味深い結果や改善点は、記事のコメント欄で共有することで、本連載を追っている他の読者にも役立てることができます。
本記事では以下ができるようになりました。
- 明確な構造ルールを使用した、隠れスマッシュデーのセットアップの識別
- スマッシュバーの極値を超えた確定バーの終値による、シグナルの確認
- 裁量的な判断なしでの、新しいバーでの自動的な取引実行
- 構造ベースのストップまたはATRベースのストップを使用したリスク管理
- 再現可能な設定を使用した、MetaTraderストラテジーテスターでの一貫したテスト実行
システムの制限
この実装は、隠れスマッシュデーのコアパターンと、その確認ロジックのみに焦点を当てています。取引パフォーマンスに影響を与える可能性があるいくつかの要素は、意図的に含めていません。
このEAでは、トレンドフィルターや市場環境フィルターは適用していません。また、スプレッドの変動、取引セッション、ニュースイベントも考慮していません。ギャップへの対応や高度なポジション管理手法も、この実装の範囲外です。
これらの除外は意図的なものです。本記事の目的は、元のパターン構造を反映した、明確でテスト可能な基盤を構築することです。追加のフィルターや拡張機能については、今後の実験や本連載の後続記事で検討できます。
結論
本記事では、隠れスマッシュデーの反転パターンを、完全かつテスト可能なMQL5 EAとして体系化しました。
セットアップルールは、バー自身のレンジと前回の終値との関係に基づく、測定可能な条件を使用して定義しました。確認条件は、スマッシュバーの極値を超えた完全なバーの終値として厳密に定義しました。部分的なバーによる曖昧さを排除するため、売買判断は新しいバーの開始時のみおこなわれます。
リスク管理は、システムに直接組み込まれています。ストップロスはスマッシュバーの水準に構造的に配置することも、ATRを使用して計算することもできます。利益目標は固定のリスクリワード比に基づいて算出されます。自動ポジションサイズ計算では、OrderCalcProfitを使用して、銘柄が異なる場合でも一貫した金額ベースのリスクを確保します。
本記事を通じて、以下が可能になりました。
- 隠れスマッシュデーのセットアップを客観的に検出する。
- 一貫した終値による確認ルールを適用する。
- 確定したバーでのみ取引を実行する。
- 構造ベースまたはボラティリティベースのストップを選択する。
- 正確な割合ベースのリスク管理によってエクスポージャーを制御する。
- 提供された設定ファイルを使用してテスト結果を再現する。
最終的な成果物は、裁量的な解釈を必要とせずに隠れスマッシュデーのパターンを実装した、コンパイル可能なEAです。このフレームワークは、元のルール構造を維持しながら、テスト、変更、拡張をおこなうことができます。
添付ファイルの概要
再現とさらなる研究を容易にするため、本記事で使用および参照したすべてのファイルを添付しています。以下の表では、各ファイルの概要と目的を簡単に説明します。これらの添付ファイルにより、戦略を曖昧さなく確認、テスト、拡張できます。
| ファイル名 | 説明 |
|---|---|
| lwHiddenSmashDayExpert.mq5 | 本記事で解説および構築したEAの完全なソースコード |
| configurations.ini | バックテストに使用したストラテジーテスターの環境設定 |
| parameters.set | 表示したバックテスト結果を正確に再現するために使用した入力パラメータ設定ファイル |
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/21391
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5取引ツールのアクセシビリティ課題を克服する(第2回):Pythonのテキスト読み上げエンジンでEAを音声対応にする
共和分株式による統計的裁定取引(最終回):特化型データベースを用いたデータ分析
取引規律をコードに組み込む(第2回):MQL5で口座全体の全取引に対応する日次取引制限エンフォーサーを構築する
プライスアクション分析ツールキットの開発(第62回):MQL5による適応型平行チャネル検出とブレイクアウトシステムの構築
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
アイデアとコードの共有、ありがとうございます。
まったく同じ設定でEAを実行したところ、4回の買い取引と2回のSL/2回のTPが発生し、利益は508 USDでした // これはブローカーの違いが原因だと思いますが、どうでしょうか?!?
EAの時間足としてD1、H1、M5を試しましたが、同じ日付で全く同じ取引が行われ、SL/TPの結果も全く同じでした