高度なICT取引システムの開発:オーダーブロックインジケーターでのシグナルの実装
- 入力パラメータ(Inputs)の更新
- インジケーターシグナル生成のロジック
- 取引戦略の実装
- テイクプロフィット(TP)とストップロス(SL)レベルの設定
- チャート上でTPとSLレベルを可視化する
- TPレベルとSLレベルのバッファの追加(4)
- メインコードの最終処理とクリーンアップ
はじめに
MQL5に関する記事へようこそ。本記事では、インジケーターにバッファとエントリーシグナルを追加し、自動売買戦略で活用するための主要な機能を完成させることに焦点を当てます。
本連載を初めてご覧になる方は、まず前回の記事をご確認ください。前回は、インジケーターの基本構造を一から作成し、基礎的な概念について解説しました。
板情報に基づく注文ブロック検出
本記事で紹介する、板情報を利用したオーダーブロック検出ロジックは以下の手順で構成されています。
- 配列の作成:各ローソク足における出来高を保存するために、2つの配列を作成します。これにより、出来高データを効率的に整理、分析できるようになります。
- 板情報データの収集:イベント内。
void OnBookEvent( )
OnBookEvent()イベント内で市場の板情報の変化を検出し、新しい出来高データをリアルタイムで記録します。
3. オーダーブロックの判定ルール:出来高データを配列に保存した後、プライスアクションのルールを併用してオーダーブロックを検証します。
板情報を用いたオーダーブロックの識別ルール
これまでのインジケーターでは、一定のローソク足範囲(x本分)内でオーダーブロックを探索していました。しかし、本記事で扱う板情報を利用した手法では、より限定的に「3本目のローソク足」(0が現在足とした場合)に注目します。
| ルール | 上昇オーダーブロック | 下降オーダーブロック |
|---|---|---|
| ローソク足3での出来高ピーク | 足3の買いボリュームが、足2および足4の合計買い・売りボリュームを一定比率以上上回ること | 足3の売りボリュームが、足2および足4の合計買い・売りボリュームを一定比率以上上回ること |
| 3本連続のローソク足 | 連続する3本が陽線であること (足1・2・3) | 連続する3本が陰線であること (足1・2・3) |
| ローソク足3の実体条件 | 足2の安値が、足3の実体の半分より上であること | 足2の高値が、足3の実体の半分より下であること |
| ローソク足3の高値/安値 | 足3の高値が、足2の終値より下であること | 足3の安値が、足2の終値より上であること |
これらのルールにより、次のことが保証されます。
- 売買の不均衡:特定のローソク足において、買いまたは売りの出来高が前後のローソク足を一定比率で上回る「明確な出来高の偏り」を確認します。
- 不均衡時の実体コントロール:需給バランスの崩れによる未約定注文(オーダーブロック)が、次の足で吸収されていないことを確認し、ブロックの有効性を検証します。
- 強い上昇または下降の動き:これらのパターンが、明確なトレンド方向への勢いを示すものかを確認し、プライスアクション上の強い不均衡を特定します。
これらの条件を満たすことで、板情報をもとにしたオーダーブロック検出ロジックを、MQL5のコードに変換して実装することが可能になります。
「注文書」イベントの初期化と完了、および配列の作成
配列の作成
オーダーブックを使用する前に、出来高データを格納する動的配列を作成する必要があります。これらの配列は次の型になります。
long これらの配列は、それぞれ買いと売りの出来高を格納するために使用されます。
- プログラムのグローバルセクションに移動し、次のように動的配列を宣言します。
long buy_volume[]; long sell_volume[];2. 次に、OnInitイベント内で配列のサイズを1に変更し、各配列のインデックス0に値0を代入します。
ArrayResize(buy_volume,1); ArrayResize(sell_volume,1); buy_volume[0] = 0.0; sell_volume[0] = 0.0;
板情報イベントの初期化と終了
板情報を開始する前に、この機能が利用可能かどうかを示すグローバル変数を作成します。 これにより、次のようなエラーを回避することができます。
INIT_FAILEDすべてのブローカーがすべての銘柄に対してマーケットデプスで取引出来高を提供しているわけではありません。この方法により、インジケーターはブローカーの提供状況に完全には依存しないようになります。 - 取引したい銘柄が板情報に対応しているかどうかを確認するには、以下の手順を実行してください。
1. チャート左上の該当ボックスをクリックします。 ![]()
2. 銘柄が板情報の出来高を利用可能かどうかを確認します。対応している場合は、以下のような確認が表示されます
板情報対応銘柄の例
非対応銘柄の例:
このように、板情報の出来高はすべての銘柄で利用できるとは限らず、ブローカーに依存します。
板情報の初期化と完了に移りましょう。
1. グローバル制御変数
まず、板情報の利用可否を示すグローバルブール変数を定義します。
bool use_market_book = true; //true by default
この変数はデフォルトでtrueですが、初期化に失敗した場合はfalseに変更されます。
2. 板情報の初期化
板情報を初期化するには、次の関数を使用します。
MarketBookAdd() この関数は、指定した銘柄の板情報を開きます。引数には現在の銘柄を指定します。
_Symbol OnInitイベント内で、初期化が成功したかどうかを確認します。
if(!MarketBookAdd(_Symbol)) //Verify initialization of the order book for the current symbol { Print("Error Open Market Book: ", _Symbol, " LastError: ", _LastError); //Print error in case of failure use_market_book = false; //Mark use_market_book as false if initialization fails }
3. 板情報の終了
OnDeinitイベント内で、次の関数を使用して板情報を解放します。
MarketBookRelease() その後、クローズを確認し、結果に応じてメッセージを出力します。
//--- if(MarketBookRelease(_Symbol)) //Verify if closure was successful Print("Order book successfully closed for: " , _Symbol); //Print success message if so else Print("Order book closed with errors for: " , _Symbol , " Last error: " , GetLastError()); //Print error message with code if not
配列での取引量板情報データの収集
板情報の初期化が完了したら、関連データの収集を開始できます。このため、板情報に変更が発生するたびに呼び出されるOnBookEventイベントを作成します。
- OnBookEventの作成
void OnBookEvent(const string& symbol)2. 銘柄と板情報の可用性の確認
if(symbol !=_Symbol || use_market_book == false) return; // Exit the event if conditions are not met
この条件チェックにより、完全なOnBookEvent関数は以下のように構成できます。
void OnBookEvent(const string& symbol) { if(symbol !=_Symbol || use_market_book == false) return; // Define array to store Market Book data MqlBookInfo book_info[]; // Retrieve Market Book data bool book_count = MarketBookGet(_Symbol,book_info); // Verify if data was successfully obtained if(book_count == true) { // Iterate through Market Book data for(int i = 0; i < ArraySize(book_info); i++) { // Check if the record is a buy order (BID) if(book_info[i].type == BOOK_TYPE_BUY || book_info[i].type == BOOK_TYPE_BUY_MARKET) { buy_volume[0] += book_info[i].volume; } // Check if the record is a sell order (ASK) if(book_info[i].type == BOOK_TYPE_SELL || book_info[i].type == BOOK_TYPE_SELL_MARKET) { sell_volume[0] += book_info[i].volume; } } } else { Print("No Market Book data retrieved."); } }
コードの説明
- 出来高検索:板情報に変化が生じるたびに、OnBookEventは最新の注文データを取得し、出来高を集計します。
- 配列の更新:買い注文の出来高はbuy_volume配列のインデックス0に、売り注文の出来高はsell_volume配列のインデックス0に加算されます。
新しいローソク足ごとに板情報の出来高を配列に蓄積し、さらに30要素などのローリング履歴を保持できるようにするためには、以下の調整が必要です。
1. 新しいローソク足の検証とカウンタの確認(1より大きいこと)
プログラム起動直後の誤検出を避け、配列が新しいローソク足の開始時にのみ更新される(少なくとも1回の開始後)ようにするため、カウンタ変数とnew_velaを組み合わせたチェックを実装します。これにより、配列の更新が実際に新しい情報が利用可能な場合にのみおこなわれるようになります。
静的変数の宣言counterはOnCalculate関数の呼び出し間で値を保持できるよう、static変数として宣言します。new_vela変数は、新しいローソク足が開始したかどうかを示します。
static int counter = 0;
新しいローソク足およびカウンタ検証条件
counterが1より大きく、new_velaがtrueであり、かつuse_market_bookがtrueの場合にのみ、配列のサイズ変更と要素のシフトを実行します。これにより、誤った早期のサイズ変更を防止し、配列が有効なデータを持ち、マーケットブックが現在の銘柄に対して取引出来高を提供している場合にのみ更新されるようになります。
if(counter > 1 && new_vela == true && use_market_book == true)
カウンタ更新
新しいローソク足が検出されるたびに、カウンタを1増加させます。
counter++;
2. 配列サイズの制御
配列が最大サイズ(30要素)を超えないように確認します。もし超えた場合は、配列を30にリサイズし、最も古い要素を削除します。
if(ArraySize(buy_volume) >= 30) { ArrayResize(buy_volume, 30); // Keep buy_volume size at 30 ArrayResize(sell_volume, 30); // Keep sell_volume size at 30 }
3. 新しい値に合わせたサイズ変更
新しい出来高データを格納するために、配列のサイズを1つ拡張します。
ArrayResize(buy_volume, ArraySize(buy_volume) + 1); ArrayResize(sell_volume, ArraySize(sell_volume) + 1);
4. 要素のシフト
すべての配列要素を1つずつ前方に移動します。これにより、最新のデータが常にインデックス0に保存され、古い値は高いインデックスにシフトされます。
for(int i = ArraySize(buy_volume) - 1; i > 0; i--) { buy_volume[i] = buy_volume[i - 1]; sell_volume[i] = sell_volume[i - 1]; }
5. 出来高の確認
配列のインデックス1の位置にある買いと売りの出来高を出力して、前回のローソク足に記録された出来高を確認します。
Print("Buy volume of the last candle: ", buy_volume[1]); Print("Sell volume of the last candle: ", sell_volume[1]);
6. 出来高のリセット
両方の配列のインデックス0を0にリセットし、新しいローソク足の出来高を蓄積し始めるようにします。
buy_volume[0] = 0; sell_volume[0] = 0;
7. 不整合なマーケットブックデータによるエラー防止の条件
追加の安全策として、buy_volumeとsell_volumeの最近の位置(インデックス3、2、1)の値がすべて0である場合、use_market_bookを自動的に無効化します。この調整は、銘柄がライブ取引でマーケットブックデータを持っているように見えても、ストラテジーテスターでは更新がおこなわれず、配列が正しく埋まらない場合があるため必要です。これにより、ゼロが記録され、インジケーターが誤った情報を扱う可能性があります。
この検証により、インジケーターが無効なデータを処理しないようにし、マーケットブックに有効な値が含まれている場合にのみuse_market_bookが適用されるようにします。
if(ArraySize(buy_volume) > 4 && ArraySize(sell_volume) > 4) { if(buy_volume[3] == 0 && sell_volume[3] == 0 && buy_volume[2] == 0 && sell_volume[2] == 0 && buy_volume[1] == 0 && sell_volume[1] == 0) use_market_book = false; }
統合コードスニペット
if(counter > 1 && new_vela == true && use_market_book == true) { if(ArraySize(buy_volume) > 4 && ArraySize(sell_volume) > 4) { if(buy_volume[3] == 0 && sell_volume[3] == 0 && buy_volume[2] == 0 && sell_volume[2] == 0 && buy_volume[1] == 0 && sell_volume[1] == 0) use_market_book = false; } // If array size is greater than or equal to 30, resize to maintain a fixed length if(ArraySize(buy_volume) >= 30) { ArrayResize(buy_volume, 30); // Ensure buy_volume does not exceed 30 elements ArrayResize(sell_volume, 30); // Ensure sell_volume does not exceed 30 elements } ArrayResize(buy_volume,ArraySize(buy_volume)+1); ArrayResize(sell_volume,ArraySize(sell_volume)+1); for(int i = ArraySize(buy_volume) - 1; i > 0; i--) { buy_volume[i] = buy_volume[i - 1]; sell_volume[i] = sell_volume[i - 1]; } // Reset volumes at index 0 to begin accumulating for the new candlestick buy_volume[0] = 0; sell_volume[0] = 0; }
板情報を利用したオーダーブロック検出の戦略
この戦略は、これまで使用してきたロジックと同じ考え方に従いますが、1つの重要な違いがあります。それは、ループを使用して繰り返し処理をおこなう代わりに、ローソク足3に対して直接チェックをおこなう点です。基本的なロジックは同じであり、特定の条件を確認し、オーダーブロックの種類に応じて最も関連性の高いローソク足を特定し、対応する値を構造体に代入して、オーダーブロックを配列に追加します。ここでは同じプロセスをより簡略化した形で適用します。
まず、オーダーブロック情報を格納するための構造体を作成します。
OrderBlocks newVela_Order_block_Book_bajista; OrderBlocks newVela_Order_block_Book;
1. 初期条件
最初に、buy_volumeおよびsell_volume配列が少なくとも5つの要素を持っているかどうかを確認します。これにより、分析に十分な履歴データが存在することを保証します。また、板情報を処理するためにuse_market_bookが有効になっていることを確認します。
if(ArraySize(buy_volume) >= 5 && ArraySize(sell_volume) >= 5 && use_market_book == true)
2. 制御変数の定義
特定の出来高条件が満たされているかどうかを示す変数case_bookを定義します。比率「ratio」は1.4に設定されており、買い出来高の顕著な増加を検出するための比較係数として機能します。
bool case_book = false; double ratio = 1.4;
3. 買い出来高の条件(Case Book)
ここでは、インデックス3の買い出来高が、インデックス2および4の買い・売り出来高よりもratio倍以上大きいかどうかを確認します。この条件が満たされると、case_bookが有効化されます。
強気の場合
if(buy_volume[3] > buy_volume[4] * ratio && buy_volume[3] > buy_volume[2] * ratio && buy_volume[3] > sell_volume[4] * ratio && buy_volume[3] > sell_volume[2] * ratio) { case_book = true; }弱気の場合
if(sell_volume[3] > buy_volume[4]*ratio && sell_volume[3] > buy_volume[2]*ratio && sell_volume[3] > sell_volume[4]*ratio && sell_volume[3] > sell_volume[2]*ratio) { case_book = true; }
4. ローソク足実体の計算
インデックス3のローソク足の実体(body_tree)を、始値から終値を引くことで計算します。
double body_tree = closeArray[3] - openArray[3];
double body_tree = openArray[3] - closeArray[3]; 5. 強気セットアップの価格条件の確認
前述の条件(上の表参照)を評価します。
強気の場合
if(lowArray[2] > ((body_tree * 0.5) + openArray[3]) && highArray[3] < closeArray[2] && closeArray[3] > openArray[3] && closeArray[2] > openArray[2] && closeArray[1] > openArray[1])
弱気の場合
if(highArray[2] < (openArray[3]-(body_tree * 0.5)) && lowArray[3] > closeArray[2] && closeArray[3] < openArray[3] && closeArray[2] < openArray[2] && closeArray[1] < openArray[1])
6. 直近の強気ローソク足の特定
関数「FindFurthestAlcista」を呼び出し、インデックス3から20本分の範囲内で最も遠い強気のローソク足を検索します。これにより、強い上昇セットアップの基準となる参照ローソク足を特定します。強気ローソク足が見つかった場合、そのインデックスは0より大きくなり、処理を続行できます。
強気の場合
int furthestAlcista = FindFurthestAlcista(Time[3], 20); if(furthestAlcista > 0)
7. オーダーブロックへの値の代入
すべての条件が満たされた場合、識別されたローソク足の値を使用して、オーダーブロック(newVela_Order_block_BookまたはnewVela_Order_block_Book_bearish)を定義します。
強気の場合
Print("Case Book Found"); datetime time1 = Time[furthestAlcista]; double price2 = openArray[furthestAlcista]; double price1 = lowArray[furthestAlcista]; //Assign the above variables to the structure newVela_Order_block_Book.price1 = price1; newVela_Order_block_Book.time1 = time1; newVela_Order_block_Book.price2 = price2; newVela_Order_block_Book.mitigated = false; newVela_Order_block_Book.name = "Bullish Order Block Book " + TimeToString(newVela_Order_block_Book.time1); AddIndexToArray_alcistas(newVela_Order_block_Book);
弱気の場合
Print("Case Book Found"); datetime time1 = Time[furthestBajista]; double price1 = closeArray[furthestBajista]; double price2 = lowArray[furthestBajista]; //Assign the above variables to the structure newVela_Order_block_Book_bajista.price1 = price1; newVela_Order_block_Book_bajista.time1 = time1; newVela_Order_block_Book_bajista.price2 = price2; newVela_Order_block_Book_bajista.mitigated = false; newVela_Order_block_Book_bajista.name = "Order Block Bajista Book " + TimeToString(newVela_Order_block_Book_bajista.time1); AddIndexToArray_bajistas(newVela_Order_block_Book_bajista);
完全なコードは、次のとおりです。
if(ArraySize(buy_volume) >= 5 && ArraySize(sell_volume) >= 5 && use_market_book == true) { bool case_book = false; double ratio = 1.4; if(sell_volume[3] > buy_volume[4]*ratio && sell_volume[3] > buy_volume[2]*ratio && sell_volume[3] > sell_volume[4]*ratio && sell_volume[3] > sell_volume[2]*ratio) { case_book = true; } double body_tree = openArray[3] - closeArray[3]; if(highArray[2] < (openArray[3]-(body_tree * 0.5)) && lowArray[3] > closeArray[2] && closeArray[3] < openArray[3] && closeArray[2] < openArray[2] && closeArray[1] < openArray[1]) { int furthestBajista = FindFurthestBajista(Time[3],20); //We call the "FindFurthestAlcista" function to find out if there are bullish candlesticks before "one candle" if(furthestBajista > 0) // Whether or not there is a furthest Bullish candle, it will be greater than 0 since if there is none, the previous candlestick returns to "one candle". { Print("Case Book Found"); datetime time1 = Time[furthestBajista]; double price1 = closeArray[furthestBajista]; double price2 = lowArray[furthestBajista]; //Assign the above variables to the structure newVela_Order_block_Book_bajista.price1 = price1; newVela_Order_block_Book_bajista.time1 = time1; newVela_Order_block_Book_bajista.price2 = price2; newVela_Order_block_Book_bajista.mitigated = false; newVela_Order_block_Book_bajista.name = "Order Block Bajista Book " + TimeToString(newVela_Order_block_Book_bajista.time1); AddIndexToArray_bajistas(newVela_Order_block_Book_bajista); } } } //-------------------- Bullish -------------------- if(ArraySize(buy_volume) >= 5 && ArraySize(sell_volume) >= 5 && use_market_book == true) { bool case_book = false; double ratio = 1.4; if(buy_volume[3] > buy_volume[4]*ratio && buy_volume[3] > buy_volume[2]*ratio && buy_volume[3] > sell_volume[4]*ratio && buy_volume[3] > sell_volume[2]*ratio) { case_book = true; } double body_tree = closeArray[3] - openArray[3]; if(lowArray[2] > ((body_tree * 0.5)+openArray[3]) && highArray[3] < closeArray[2] && closeArray[3] > openArray[3] && closeArray[2] > openArray[2] && closeArray[1] > openArray[1]) { int furthestAlcista = FindFurthestAlcista(Time[3],20); //We call the "FindFurthestAlcista" function to find out if there are bullish candlessticks before "one candle" if(furthestAlcista > 0) // Whether or not there is a furthest Bullish candle, it will be greater than 0 since if there is none, the previous candlestick returns to "one candle". { Print("Case Book Found"); datetime time1 = Time[furthestAlcista]; //let's assign the index time of furthestAlcista to the variable time1 double price2 = openArray[furthestAlcista]; //let's assign the open of furthestAlcista as price 2 (remember that we draw it on a bearish candlestick most of the time) double price1 = lowArray[furthestAlcista]; //let's assign the low of furthestAlcista as price 1 //Assign the above variables to the structure newVela_Order_block_Book.price1 = price1; newVela_Order_block_Book.time1 = time1; newVela_Order_block_Book.price2 = price2; newVela_Order_block_Book.mitigated = false; newVela_Order_block_Book.name = "Bullish Order Block Book " + TimeToString(newVela_Order_block_Book.time1); AddIndexToArray_alcistas(newVela_Order_block_Book); } } }
インジケーターバッファの作成
MQL5でオーダーブロックインジケーターのバッファを作成し、設定するために、まず強気および弱気オーダーブロックの価格レベルを格納し表示するための2つのバッファと2つのプロットをグローバルに定義します。
1. バッファおよびプロットの宣言
プログラムのグローバルセクションで、オーダーブロックの価格データを格納するための2つのバッファを宣言します。さらに、チャート上にオーダーブロックを可視化するための2つのプロットを定義します。
#property indicator_buffers 2 #property indicator_plots 2 #property indicator_label1 "Bullish Order Block" #property indicator_label2 "Bearish Order Block"
2. バッファ用の動的配列を作成する
強気および弱気オーダーブロックに対応する価格を格納するために、buyOrderBlockBufferとsellOrderBlockBufferという2つの動的配列を宣言します。これらの配列はインジケーターバッファにリンクされ、チャート上でオーダーブロックデータを視覚的に表示できるようにします。
//--- Define the buffers double buyOrderBlockBuffer[]; // Buffer for bullish order blocks double sellOrderBlockBuffer[]; // Buffer for bearish order blocks
説明
- buyOrderBlockBuffer:強気オーダーブロックの価格レベルを格納し、価格がサポートを見つける可能性のあるポイントを表します。
- sellOrderBlockBuffer:弱気オーダーブロックの価格レベルを格納し、価格がレジスタンスに遭遇する可能性のあるポイントを表します。
バッファを設定するためのOnInit関数の変更
このセクションでは、インジケーターバッファを設定するためにOnInit関数を調整し、強気および弱気オーダーブロックの配列をインジケーターバッファに割り当てます。これにより、インジケーターがチャート上にデータを正しく格納し表示できるようになります。
手順
1. SetIndexBufferを使用してデータバッファを割り当てる
OnInit内で、buyOrderBlockBufferおよびsellOrderBlockBuffer配列をSetIndexBuffer関数を使用してインジケーターバッファに割り当てます。これにより、配列がチャート上でデータを格納し表示できるようになります。
//--- Assign data buffers to the indicator SetIndexBuffer(0, buyOrderBlockBuffer, INDICATOR_DATA); SetIndexBuffer(1, sellOrderBlockBuffer, INDICATOR_DATA)
2. バッファを時系列として設定し、空の値で初期化する
データを時間の逆順(時系列のように)で表示するため、配列を時系列として設定します。また、実際の値が計算されるまで誤ったデータが表示されないよう、両方のバッファをEMPTY_VALUEで初期化します。
ArraySetAsSeries(buyOrderBlockBuffer, true); ArraySetAsSeries(sellOrderBlockBuffer, true); ArrayFill(buyOrderBlockBuffer, 0,0, EMPTY_VALUE); // Initialize to EMPTY_VALUE ArrayFill(sellOrderBlockBuffer, 0,0, EMPTY_VALUE); // Initialize to EMPTY_VALUE
インジケーターへのバッファの実装(2)
このセクションでは、強気および弱気オーダーブロックの価格をインジケーターバッファに割り当てます。これらのバッファにより、各オーダーブロックの時間(time1)に対応するインデックスでデータを利用できるようになります。1. 強気オーダーブロックの価格を割り当てる
ob_alcistas内で各強気ブロックを評価するループ内で、price2をbuyOrderBlockBufferに割り当てます。iBarShiftを使用して、time1がオーダーブロックの時間に一致するチャート上の正確なインデックスを取得します。
buyOrderBlockBuffer[iBarShift(_Symbol, _Period, ob_alcistas[i].time1)] = ob_alcistas[i].price2;
ここで、強気ブロックのprice2が対応するインデックスのbuyOrderBlockBufferに割り当てられ、バッファがチャート上でそのブロックの価格レベルを反映するようになります。
2. 弱気オーダーブロックの価格を割り当てる
同様に、ob_bajistas配列を走査し、各弱気ブロックのprice2を対応するインデックスにsellOrderBlockBufferへ設定します。
sellOrderBlockBuffer[iBarShift(_Symbol, _Period, ob_bajistas[i].time1)] = ob_bajistas[i].price2;要約
- iBarShiftは、ブロックのtimeがチャート上の位置と一致する正確なインデックスを特定します。
- buyOrderBlockBufferおよびsellOrderBlockBufferは、それぞれprice2の値を受け取り、チャートおよびインジケーターの計算で正しいタイミングに価格を記録できるようにします。
入力パラメータ(Inputs)の更新
このセクションでは、ユーザーがテイクプロフィット(TP)およびストップロス(SL)の計算方法をカスタマイズできるように、入力パラメータを設定します。2つのオプション、ATR (Average True Range)またはPOINT(固定ポイント)を選択できる列挙型を作成します。
ENUM_TP_SL_STYLE
この列挙型により、ユーザーは2つのTPおよびSL計算モードのいずれかを選択できます。
- ATR:平均的な価格変動幅(ボラティリティ)に基づいてTPおよびSLを設定し、現在の市場の変動に自動的に適応します。
- POINT:ユーザーが定義した固定ポイント数でTPおよびSLを設定します。
enum ENUM_TP_SL_STYLE
{
ATR,
POINT
};
説明
-
ATR:ユーザーはATRに対する倍率を設定し、市場のボラティリティに応じてTPおよびSLの距離を自動的に拡大または縮小できます。倍率が大きいほど、TPとSLの距離が広くなります。
-
POINT:ユーザーが固定ポイント数を手動で定義し、ボラティリティに関係なく固定的なレベルを維持します。
続いて、インジケーターパラメータを整理して入力構造を定義します。sinputを使用して設定をセクションごとにグループ化し、より視覚的で整理されたパラメータ表示を実現します。これにより、ユーザーが簡単に設定をおこなえるようになります。
1. Strategyセクション
TPおよびSLの計算スタイルを含むストラテジーグループを作成します。
sinput group "-- Strategy --" input ENUM_TP_SL_STYLE tp_sl_style = POINT; // TP and SL style: ATR or fixed points
ここで、tp_sl_styleによりユーザーはTPおよびSLをATRに基づいて計算するか、固定ポイントで設定するかを選択できます。
2. 選択された方式ごとのTPおよびSL設定
各方式の特定の設定を考慮するために、ATR方式用と固定ポイント方式用の2つの追加グループを追加します。
ATRグループ:ATR倍率を指定する2つのdouble型入力変数を含め、ボラティリティに基づいてTPおよびSLの範囲を調整します。
sinput group " ATR " input double Atr_Multiplier_1 = 1.5; // Multiplier for TP input double Atr_Multiplier_2 = 2.0; // Multiplier for SL
POINTグループ:固定ポイントでTPおよびSLを定義するために、2つのint型入力変数を追加し、距離を手動かつ正確に制御できるようにします。
sinput group " POINT " input int TP_POINT = 500; // Fixed points for TP input int SL_POINT = 275; // Fixed points for SL
この構造により、パラメータが整理され分類され、使用しやすく明確になります。ユーザーはATRに基づく自動設定と固定ポイントによる手動設定のいずれかを直感的に選択できます。
パラメータの完全なコード
sinput group "--- Order Block Indicator settings ---" sinput group "-- Order Block --" input int Rango_universal_busqueda = 500; input int Witdth_order_block = 1; input bool Back_order_block = true; input bool Fill_order_block = true; input color Color_Order_Block_Bajista = clrRed; input color Color_Order_Block_Alcista = clrGreen; sinput group "-- Strategy --" input ENUM_TP_SL_STYLE tp_sl_style = POINT; sinput group " ATR " input double Atr_Multiplier_1 = 1.5; input double Atr_Multiplier_2 = 2.0; sinput group " POINT " input int TP_POINT = 500; input int SL_POINT = 275;
インジケーターシグナル生成のロジック
買いまたは売りのシグナルを生成するには、次の2つの静的変数が使用されます。
| 変数 | 詳細 |
|---|---|
| time_およびtime_b | オーダーブロックがミティゲートされた時刻を記録し、期限切れのために5ローソク足分のマージン(秒単位)を加えます。 |
| buscar_obaおよびbuscar_obb | 新たにミティゲートされたオーダーブロックの検索を制御します。条件に応じて有効または無効になります。 |
シグナル生成プロセス
ミティゲートされたオーダーブロックの検出- オーダーブロックがミティゲートされた場合、time_は現在時刻に5ローソク足分のマージンを加えた値に設定されます。
- シグナル条件を検証している間は、searcher変数をfalseに設定して、新たな検索を一時停止します。
- シグナルは30期間の指数移動平均(EMA)とミティゲーション時間(time_)に基づいて評価されます。
| シグナルの種類 | EMA条件 | 時間条件 |
|---|---|---|
| 買 | 30期間EMAがローソク足1の終値より下 | time_が現在時刻より大きい |
| 売 | 30期間EMAがローソク足1の終値より上 | time_bが現在時刻より大きい |
注意:これらの条件により、オーダーブロックがミティゲートされた後、5ローソク足分のマージン内でシグナルが生成されることが保証されます。
条件が満たされた場合と満たされない場合のアクション
| 状態 | アクション |
|---|---|
| 達成 | TPおよびSLバッファに値を設定し、対応する取引をおこないます。 |
| 未達成 | searcherをtrueにリセットし、time_とtime_bを0に設定。最大時間経過後に新しいオーダーブロックの検索が再開されます。 |
ブロック図
買

売

取引戦略の実装
まず、指数移動平均(EMA)のハンドルを作成します。
グローバル変数(配列とハンドル)を作成します。
int hanlde_ma; double ma[];
OnInit内でハンドルを初期化し、有効な値が割り当てられているか確認します。
hanlde_ma = iMA(_Symbol,_Period,30,0,MODE_EMA,PRICE_CLOSE); if(hanlde_ma == INVALID_HANDLE) { Print("The EMA indicator is not available. Failure: ", _LastError); return INIT_FAILED; }次に、検索状態とオーダーブロック(OB)の有効化時刻を管理する静的変数を宣言し、買いと売りのシナリオを区別します。
//Variables for buy static bool buscar_oba = true; static datetime time_ = 0; //Variables for sell static bool buscar_obb = true; static datetime time_b = 0;
続いて、ソフトオーダーブロックをループ処理し、条件を追加します。
まず条件を追加します。
//Bullish case if(buscar_oba == true) //Bearish case if(buscar_obb == true)
オーダーブロックがミティゲートされたかどうかを判定します。価格がOBと接触した場合、その時刻を記録し、検索を一時停止します。これは強気・弱気の両方のケースでおこないます。
// Bearish case for(int i = 0; i < ArraySize(ob_bajistas); i++) { if(ob_bajistas[i].mitigated == true && !Es_Eliminado_PriceTwo(ob_bajistas[i].name, pricetwo_eliminados_obb) && ObjectFind(ChartID(), ob_bajistas[i].name) >= 0) { Alert("The bearishorder block is being mitigated: ", TimeToString(ob_bajistas[i].time1)); buscar_obb = false; // Pause search time_b = iTime(_Symbol,_Period,1); // Record the mitigation time Agregar_Index_Array_1(pricetwo_eliminados_obb, ob_bajistas[i].name); break; } } // Bullish case for(int i = 0; i < ArraySize(ob_alcistas); i++) { if(ob_alcistas[i].mitigated == true && !Es_Eliminado_PriceTwo(ob_alcistas[i].name, pricetwo_eliminados_oba) && ObjectFind(ChartID(), ob_alcistas[i].name) >= 0) { Alert("The bullish order block is mitigated: ", TimeToString(ob_alcistas[i].time1)); time_ = iTime(_Symbol,_Period,0); Agregar_Index_Array_1(pricetwo_eliminados_oba, ob_alcistas[i].name); buscar_oba = false; // Pause search break; } }
このセクションでは、緩和策が検出されるとシステムが検索を停止し、重複したシグナルを回避します。
取引実行の初期条件
戦略では、OBがミティゲートされ、最大待機時間が超過していない場合に買いまたは売りシグナルの検索を開始します。
// Buy if(buscar_oba == false && time_ > 0 && new_vela) { /* Code for Buy */ } // Sell if(buscar_obb == false && time_b > 0 && new_vela) { /* Code for Sell */ }
これらの条件では
- buscar_obaまたはbuscar_obbがfalseであること(以前のミティゲーションを確認)
- time_またはtime_bが0より大きいこと(時刻が記録されていること)
- new_velaがtrueであること(新しいローソク足に対してのみロジックを適用し、重複動作を防ぐ)
買いまたは売り条件の検証
必要な条件を設定するため、まず最大待機時間を格納する変数を用意します。さらに、ローソク足1の終値とそのEMA値を取得します。終値はiCloseで取得し、EMAは配列に保存して履歴全体を保持します。
// Buy double close_ = NormalizeDouble(iClose(_Symbol,_Period,1),_Digits); datetime max_time_espera = time_ + (PeriodSeconds() * 5); if(close_ > ma[1] && iTime(_Symbol,_Period,0) <= max_time_espera) { // Code for Buy... } // Sell close_ = NormalizeDouble(iClose(_Symbol,_Period,1),_Digits); max_time_espera = time_b + (PeriodSeconds() * 5); if(close_ < ma[1] && iTime(_Symbol,_Period,0) <= max_time_espera) { // Code for Sell... }
オーダーブロック検索のリセット
最大待機時間が経過しても条件が満たされない場合、検索をリセットして新しいOB検出を可能にします。
// Reset for Buy if(iTime(_Symbol,_Period,0) > max_time_espera) { time_ = 0; buscar_oba = true; } // Reset for Sell if(iTime(_Symbol,_Period,0) > max_time_espera) { time_b = 0; buscar_obb = true; }
最後に、TPおよびSLを描画し、バッファに追加する関数が必要です。これは以下のコードで実現できます。
新しいセクションに進みましょう。
テイクプロフィット(TP)とストップロス(SL)レベルの設定
このセクションでは、TPおよびSLを計算する関数「GetTP_SL」を作成します。入力構成で前述したように、計算には、ATRまたは固定ポイントのいずれかを使用します。
1:関数の定義
GetTP_SL関数は、ポジションの始値、ポジションタイプ(ENUM_POSITION_TYPE)、および計算結果を格納する参照変数(tp1、tp2、sl1、sl2)をパラメータとして受け取ります。
void GetTP_SL(double price_open_position, ENUM_POSITION_TYPE type, double &tp1, double &tp2, double &sl1, double &sl2)
2:ATRの取得
ATRベースのレベルを計算するため、最新ローソク足のATR値を格納する配列が必要です。CopyBufferを使ってatr配列に値を取得します。
double atr[]; ArraySetAsSeries(atr, true); CopyBuffer(atr_i, 0, 0, 1, atr);
3:ATRに基づくTPおよびSLの計算
tp_sl_styleがATRに設定されている場合、ATR値に定義された倍率(Atr_Multiplier_1とAtr_Multiplier_2)を掛け、ポジションタイプに応じて始値に加減してTPおよびSLを計算します。
if (type == POSITION_TYPE_BUY) { sl1 = price_open_position - (atr[0] * Atr_Multiplier_1); sl2 = price_open_position - (atr[0] * Atr_Multiplier_2); tp1 = price_open_position + (atr[0] * Atr_Multiplier_1); tp2 = price_open_position + (atr[0] * Atr_Multiplier_2); } if (type == POSITION_TYPE_SELL) { sl1 = price_open_position + (atr[0] * Atr_Multiplier_1); sl2 = price_open_position + (atr[0] * Atr_Multiplier_2); tp1 = price_open_position - (atr[0] * Atr_Multiplier_1); tp2 = price_open_position - (atr[0] * Atr_Multiplier_2); }
4:固定ポイントに基づくTPおよびSLの計算
tp_sl_styleがPOINTに設定されている場合、指定されたポイント数(TP_POINTとSL_POINT)に銘柄のポイント値(_Point)を掛けて始値に加減します。ATRベースよりも簡単な計算方法です。
if (type == POSITION_TYPE_BUY) { sl1 = price_open_position - (SL_POINT * _Point); sl2 = price_open_position - (SL_POINT * _Point * 2); tp1 = price_open_position + (TP_POINT * _Point); tp2 = price_open_position + (TP_POINT * _Point * 2); } if (type == POSITION_TYPE_SELL) { sl1 = price_open_position + (SL_POINT * _Point); sl2 = price_open_position + (SL_POINT * _Point * 2); tp1 = price_open_position - (TP_POINT * _Point); tp2 = price_open_position - (TP_POINT * _Point * 2); }
チャート上でTPとSLレベルを可視化する
このセクションでは、ラインとテキストオブジェクトを使ってチャート上にTPおよびSLレベルを描画する関数を作成します。
ラインの作成
bool TrendCreate(long chart_ID, // Chart ID string name, // Line name int sub_window, // Subwindow index datetime time1, // Time of the first point double price1, // Price of the first point datetime time2, // Time of the second point double price2, // Price of the second point color clr, // Line color ENUM_LINE_STYLE style, // Line style int width, // Line width bool back, // in the background bool selection // Selectable form moving ) { ResetLastError(); if(!ObjectCreate(chart_ID,name,OBJ_TREND,sub_window,time1,price1,time2,price2)) { Print(__FUNCTION__, ": ¡Failed to create trend line! Error code = ",GetLastError()); return(false); } ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style); ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,width); ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); ChartRedraw(chart_ID); return(true); }
テキストの場合
bool TextCreate(long chart_ID, // Chart ID string name, // Object name int sub_window, // Subwindow index datetime time, // Anchor time double price, // Anchor price string text, // the text string font, // Font int font_size, // Font size color clr, // color double angle, // Text angle ENUM_ANCHOR_POINT anchor, // Anchor point bool back=false, // font bool selection=false) // Selectable for moving { //--- reset error value ResetLastError(); //--- create "Text" object if(!ObjectCreate(chart_ID,name,OBJ_TEXT,sub_window,time,price)) { Print(__FUNCTION__, ": ¡Failed to create object \"Text\"! Error code = ",GetLastError()); return(false); } ObjectSetString(chart_ID,name,OBJPROP_TEXT,text); ObjectSetString(chart_ID,name,OBJPROP_FONT,font); ObjectSetInteger(chart_ID,name,OBJPROP_FONTSIZE,font_size); ObjectSetDouble(chart_ID,name,OBJPROP_ANGLE,angle); ObjectSetInteger(chart_ID,name,OBJPROP_ANCHOR,anchor); ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); ChartRedraw(chart_ID); return(true); }
それでは関数の作成に移りましょう。
手順1:入力詳細
この関数は次のパラメータを受け取ります。
- tp1、tp2:2つのテイクプロフィットレベルの値
- sl1、sl2:2つのストップロスレベルの値
void DrawTP_SL( double tp1, double tp2, double sl1, double sl2)
手順2:時間の準備
まず、文字列「curr_time」を作成し、チャート上のローソク足の現在の日付と時刻を格納します。次に、extension_timeを計算し、現在時刻から15期間先まで延長してTPおよびSLのラインを右方向に投影します。text_timeはテキストラベルの位置をextension_timeより少し右に調整するために使用します。
string curr_time = TimeToString(iTime(_Symbol, _Period, 0)); datetime extension_time = iTime(_Symbol, _Period, 0) + (PeriodSeconds(PERIOD_CURRENT) * 15); datetime text_time = extension_time + (PeriodSeconds(PERIOD_CURRENT) * 2);
手順3:TPおよびSLラインとラベルの描画
- テイクプロフィット1 (tp1)
- TrendCreateを使用して、tp1の位置に緑色の点線(STYLE_DOT)を描画します。
- TextCreateを使用して、tp1の位置に「TP1」というテキストラベルを追加します。
TrendCreate(ChartID(), curr_time + " TP1", 0, iTime(_Symbol, _Period, 0), tp1, extension_time, tp1, clrGreen, STYLE_DOT, 1, true, false); TextCreate(ChartID(), curr_time + " TP1 - Text", 0, text_time, tp1, "TP1", "Arial", 8, clrGreen, 0.0, ANCHOR_CENTER);2. テイクプロフィット2 (tp2)
- tp2に別の緑の点線を描き、テキストラベル「TP2」を追加します。
TrendCreate(ChartID(), curr_time + " TP2", 0, iTime(_Symbol, _Period, 0), tp2, extension_time, tp2, clrGreen, STYLE_DOT, 1, true, false); TextCreate(ChartID(), curr_time + " TP2 - Text", 0, text_time, tp2, "TP2", "Arial", 8, clrGreen, 0.0, ANCHOR_CENTER);3. ストップロス1 (sl1)
- sl1に赤い点線を描き、テキストラベル「SL1」を追加します。
TrendCreate(ChartID(), curr_time + " SL1", 0, iTime(_Symbol, _Period, 0), sl1, extension_time, sl1, clrRed, STYLE_DOT, 1, true, false); TextCreate(ChartID(), curr_time + " SL1 - Text", 0, text_time, sl1, "SL1", "Arial", 8, clrRed, 0.0, ANCHOR_CENTER);4. ストップロス2 (sl2)
- 同様に、sl2に赤い線を描き、テキストラベル「SL2」を追加します。
TrendCreate(ChartID(), curr_time + " SL2", 0, iTime(_Symbol, _Period, 0), sl2, extension_time, sl2, clrRed, STYLE_DOT, 1, true, false); TextCreate(ChartID(), curr_time + " SL2 - Text", 0, text_time, sl2, "SL2", "Arial", 8, clrRed, 0.0, ANCHOR_CENTER);
完全なコードは、次のとおりです。
void DrawTP_SL(double tp1, double tp2, double sl1, double sl2) { string curr_time = TimeToString(iTime(_Symbol,_Period,0)); datetime extension_time = iTime(_Symbol,_Period,0) + (PeriodSeconds(PERIOD_CURRENT) * 15); datetime text_time = extension_time + (PeriodSeconds(PERIOD_CURRENT) * 2); TrendCreate(ChartID(),curr_time+" TP1",0,iTime(_Symbol,_Period,0),tp1,extension_time,tp1,clrGreen,STYLE_DOT,1,true,false); TextCreate(ChartID(),curr_time+" TP1 - Text",0,text_time,tp1,"TP1","Arial",8,clrGreen,0.0,ANCHOR_CENTER); TrendCreate(ChartID(),curr_time+" TP2",0,iTime(_Symbol,_Period,0),tp2,extension_time,tp2,clrGreen,STYLE_DOT,1,true,false); TextCreate(ChartID(),curr_time+" TP2 - Text",0,text_time,tp2,"TP2","Arial",8,clrGreen,0.0,ANCHOR_CENTER); TrendCreate(ChartID(),curr_time+" SL1",0,iTime(_Symbol,_Period,0),sl1,extension_time,sl1,clrRed,STYLE_DOT,1,true,false); TextCreate(ChartID(),curr_time+" SL1 - Text",0,text_time,sl1,"SL1","Arial",8,clrRed,0.0,ANCHOR_CENTER); TrendCreate(ChartID(),curr_time+" SL2",0,iTime(_Symbol,_Period,0),sl2,extension_time,sl2,clrRed,STYLE_DOT,1,true,false); TextCreate(ChartID(),curr_time+" SL2 - Text",0,text_time,sl2,"SL2","Arial",8,clrRed,0.0,ANCHOR_CENTER); }
TPレベルとSLレベルのバッファの追加(4)
price2を格納する2つのバッファと同様に、TPおよびSL用の追加バッファを作成します。
#property indicator_label3 "Take Profit 1" #property indicator_label4 "Take Profit 2" #property indicator_label5 "Stop Loss 1" #property indicator_label6 "Stop Loss 2"
プロットとバッファの数を2から6に増やします。
#property indicator_buffers 6 #property indicator_plots 6
バッファの配列を作成します。
double tp1_buffer[]; double tp2_buffer[]; double sl1_buffer[]; double sl2_buffer[];
配列を初期化し、系列として設定します。
SetIndexBuffer(2, tp1_buffer, INDICATOR_DATA); SetIndexBuffer(3, tp2_buffer, INDICATOR_DATA); SetIndexBuffer(4, sl1_buffer, INDICATOR_DATA); SetIndexBuffer(5, sl2_buffer, INDICATOR_DATA); ArraySetAsSeries(buyOrderBlockBuffer, true); ArraySetAsSeries(sellOrderBlockBuffer, true); ArrayFill(buyOrderBlockBuffer, 0,0, EMPTY_VALUE); // Initialize to EMPTY_VALUE ArrayFill(sellOrderBlockBuffer, 0,0, EMPTY_VALUE); // Initialize to EMPTY_VALUE ArraySetAsSeries(tp1_buffer, true); ArraySetAsSeries(tp2_buffer, true); ArrayFill(tp1_buffer, 0, 0, EMPTY_VALUE); // Initialize to EMPTY_VALUE ArrayFill(tp2_buffer, 0, 0, EMPTY_VALUE); // Initialize to EMPTY_VALUE ArraySetAsSeries(sl1_buffer, true); ArraySetAsSeries(sl2_buffer, true); ArrayFill(sl1_buffer, 0, 0, EMPTY_VALUE); // Initialize to EMPTY_VALUE ArrayFill(sl2_buffer, 0, 0, EMPTY_VALUE); // Initialize to EMPTY_VALUE
これにより、TP値とSL値が正しく保存され、チャートに表示されます。
メインコードの最終処理とクリーンアップ
インジケーターを完成させるために、クリーンアップと最適化のコードを実装します。これにより、バックテスト性能が向上し、不要になった配列(OrderBlocksを格納する配列など)のメモリリソースが解放されます。
1. 配列のクリア
OnCalculate内で新しい日足ローソク足を監視します。最後のローソク足時刻をグローバル変数に保存します。
datetime tiempo_ultima_vela_1;新しい日次ローソク足が開くたびに、配列からメモリを解放して古いデータの蓄積を防ぎ、パフォーマンスを最適化します。 if(tiempo_ultima_vela_1 != iTime(_Symbol,PERIOD_D1, 0)) { Eliminar_Objetos(); ArrayFree(ob_bajistas); ArrayFree(ob_alcistas); ArrayFree(pricetwo_eliminados_oba); ArrayFree(pricetwo_eliminados_obb); tiempo_ultima_vela_1 = iTime(_Symbol,PERIOD_D1, 0); }
2. OnDeinitの変更
OnDeinitで、EMAインジケーターハンドルを解放し、すべての配列をクリアします。これにより、インジケーターが削除されたときにメモリリソースが残らないことが保証されます。
void OnDeinit(const int reason) { Eliminar_Objetos(); ArrayFree(ob_bajistas); ArrayFree(ob_alcistas); ArrayFree(pricetwo_eliminados_oba); ArrayFree(pricetwo_eliminados_obb); if(atr_i != INVALID_HANDLE) IndicatorRelease(atr_i); if(hanlde_ma != INVALID_HANDLE) //EMA IndicatorRelease(hanlde_ma); ResetLastError(); if(MarketBookRelease(_Symbol)) //Verify if closure was successful Print("Order book successfully closed for: " , _Symbol); //Print success message if so else Print("Order book closed with errors for: " , _Symbol , " Last error: " , GetLastError()); //Print error message with code if not }
3. オブジェクト削除関数
Eliminar_Objetos関数を最適化し、TPおよびSLラインおよびオーダーブロック矩形を削除して、チャートを清潔に保ちます。void Eliminar_Objetos() { for(int i = 0 ; i < ArraySize(ob_alcistas) ; i++) // iterate through the array of bullish order blocks { ObjectDelete(ChartID(),ob_alcistas[i].name); // delete the object using the order block's name } for(int n = 0 ; n < ArraySize(ob_bajistas) ; n++) // iterate through the array of bearish order blocks { ObjectDelete(ChartID(),ob_bajistas[n].name); // delete the object using the order block's name } //Delete all TP and SL lines ObjectsDeleteAll(0," TP",-1,-1); ObjectsDeleteAll(0," SL",-1,-1); }
4. OnInitでの初期設定
インジケーターのショートネームとチャートプロットラベルを設定し、データウィンドウ上で正しく表示されるようにします。
string short_name = "Order Block Indicator"; IndicatorSetString(INDICATOR_SHORTNAME,short_name); // Set data precision for digits // Assign labels for each plot PlotIndexSetString(0, PLOT_LABEL, "Bullish Order Block"); PlotIndexSetString(1, PLOT_LABEL, "Bearish Order Block"); PlotIndexSetString(2, PLOT_LABEL, "Take Profit 1"); PlotIndexSetString(3, PLOT_LABEL, "Take Profit 2"); PlotIndexSetString(4, PLOT_LABEL, "Stop Loss 1"); PlotIndexSetString(5, PLOT_LABEL, "Stop Loss 2");
5. 取引開始時のTPとSLレベルの設定
最後に、買いおよび売りのテイクプロフィット(TP)とストップロス(SL)レベルを設定します。買い場合はAsk価格を使用し、売りの場合はBid価格を使用します。計算後、DrawTP_SL関数を用いて、チャート上にTPおよびSLラインを描画し、モニタリングできるようにします。
//Buy double ask= NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); double tp1; double tp2; double sl1; double sl2; GetTP_SL(ask,POSITION_TYPE_BUY,tp1,tp2,sl1,sl2); DrawTP_SL(tp1,tp2,sl1,sl2); tp1_buffer[iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,_Period,0))] = tp1; tp2_buffer[iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,_Period,0))] = tp2; sl1_buffer[iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,_Period,0))] = sl1; sl2_buffer[iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,_Period,0))] = sl2; time_ = 0; buscar_oba = true; //Sell double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); double tp1; double tp2; double sl1; double sl2; GetTP_SL(bid,POSITION_TYPE_SELL,tp1,tp2,sl1,sl2); DrawTP_SL(tp1,tp2,sl1,sl2); tp1_buffer[iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,_Period,0))] = tp1; tp2_buffer[iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,_Period,0))] = tp2; sl1_buffer[iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,_Period,0))] = sl1; sl2_buffer[iBarShift(_Symbol,PERIOD_CURRENT,iTime(_Symbol,_Period,0))] = sl2; time_b = 0; buscar_obb = true;
| ステップ | 買 | 売 |
|---|---|---|
| 価格 | Askを取得して正規化 | Bidを取得して正規化 |
| 変数 | TPとSLを格納する変数を初期化 (tp1、tp2、sl1、sl2) | 同じ変数を使用 (tp1、tp2、sl1、sl2) |
| 計算方法 | GetTP_SLは買い取引のAsk価格に基づいてTPレベルとSLレベルを計算 | GetTP_SLは、売り取引のBid価格に基づいてTPレベルとSLレベルを計算 |
| 描画 | DrawTP_SLは、買い取引のTP/SLレベルをチャート上に視覚的に表示 | DrawTP_SLは売り取引のTP/SLレベルをチャート上に視覚的に表示 |
| バッファ | iBarShiftを使用して現在のバーインデックスを見つけ、TP/SLをバッファに保存 (tp1_buffer、tp2_buffer、sl1_buffer、sl2_buffer) | 現在のバーのインデックスを見つけて、TP/SLを同じバッファに保存 (tp1_buffer、tp2_buffer、sl1_buffer、sl2_buffer) |
| 静的変数 | 静的変数をリセットして、次の反復で強気注文の新しいブロックを検索 (静的変数:time_およびbuscar_oba) | 静的変数をリセットして、次の反復で弱気注文の新しいブロックを検索 (静的変数:time_bおよびsearch_obb) |
結論
この記事では、板情報の出来高に基づくオーダーブロックインジケーターの作成方法を解説し、元のインジケーターに追加バッファを加えることで機能を最適化する手法を紹介しました。
最終結果

これで、オーダーブロックインジケーターの開発は完了です。次回の記事では、リスク管理クラスをゼロから作成し、このリスク管理を統合した取引ボットを開発する方法を解説します。ボットは、今回作成したインジケーターのシグナルバッファを活用して、より正確で自動化された取引をおこないます。
MetaQuotes Ltdによりスペイン語から翻訳されました。
元の記事: https://www.mql5.com/es/articles/16268
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
取引におけるニューラルネットワーク:Attentionメカニズムを備えたエージェントのアンサンブル(最終回)
リプレイシステムの開発(第78回):新しいChart Trade(V)
取引におけるニューラルネットワーク:予測符号化を備えたハイブリッド取引フレームワーク(StockFormer)
取引におけるニューラルネットワーク:Attentionメカニズムを備えたエージェントのアンサンブル(MASAAT)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
https://www.mql5.com/ja/articles/16268
5.取引開始時のTPとSLレベルの設定
最後に、買いと売りの取引にテイクプロフィットとストップロスのレベルを設定します。買い 取引には アスク価格を使い、売り 取引には ビッド価格を使います。そして、チャート上にTPラインとSLラインを引いて監視します。
これは 少し単純化できそうです。