
MQL5 クックブック:ОСО オーダー
はじめに
本稿は OCO のような注文ペアタイプの処理に焦点を当てています。このメカニズムは MetaTrader 5 と競合するトレーディングターミナルいくつかに実装されているものです。OCO 注文を処理するためのパネルを持つ EA の作成例から2つの目的を達成します。一方で、標準ライブラリの特徴を説明したいと思います。その一方で、トレーダーのツールセットを拡張したいと思います。
1. OCO 注文の基礎
OCO 注文(one-cancels-the-other order:1件が別の注文を取り消す)は2件の未決注文のペアです。
それらはお互いのキャンセル関数で連結しています。第1の注文が実行されると、第2の注文は処理されないまま、またはその逆です。
図1 OCO 注文ペア
図1 はシンプルな注文相互依存スキームです。基本的な定義を反映しています。両方の注文が存在する限りペアは存在する、というものです。論理面では、ペアのどちらか(ひとつ)の注文が基本ですが、ペアの存在には十分な条件ではありません。
ペアの1つは未決注文でもう1つは逆指し値注文、さらに注文の方くは1方向(売りか買いのどちらか)でなければならない、とするソースもあります。私の意見では、その制限は柔軟なトレーディング戦略作成には役立たないと思います。多様な注文がペアで分析することを提案します。またもっとも重要なことはこのペアのプログラムを試みることです。
2. 注文ペアのプログラミング
私の意見では、OCO 注文の管理に関したタスクをプログラムするのに ООP ツールセットが最高の形で適しています。
次のセクションではわれわれの目的に役立つデータタイプについてお話します。まずはCiOcoObject クラスです。
2.1. CiOcoObject クラス
相互関連の注文を管理するソフトウェアオブジェクトをいくつか考え出す必要があります。
従来どおり、抽象クラスCObject を基に新しいオブジェクトを作成します。
以下がこの新しいクラスの記述です。
//+------------------------------------------------------------------+ //| Class CiOcoObject | //| Purpose: a class for OCO orders | //+------------------------------------------------------------------+ class CiOcoObject : public CObject { //--- === Data members === --- private: //--- tickets of pair ulong m_order_tickets[2]; //--- initialization flag bool m_is_init; //--- id uint m_id; //--- === Methods === --- public: //--- constructor/destructor void CiOcoObject(void){m_is_init=false;}; void ~CiOcoObject(void){}; //--- copy constructor void CiOcoObject(const CiOcoObject &_src_oco); //--- assignment operator void operator=(const CiOcoObject &_src_oco); //--- initialization/deinitialization bool Init(const SOrderProperties &_orders[],const uint _bunch_cnt=1); bool Deinit(void); //--- get id uint Id(void) const {return m_id;}; private: //--- types of orders ENUM_ORDER_TYPE BaseOrderType(const ENUM_ORDER_TYPE _ord_type); ENUM_BASE_PENDING_TYPE PendingType(const ENUM_PENDING_ORDER_TYPE _pend_type); //--- set id void Id(const uint _id){m_id=_id;}; };
OCO 注文の各ペアはそれ自身の識別子を持ちます。その値は乱数ジェネレータ(CRandom クラスのオブジェクト)という方法で設定されます。
ペアの初期化と再初期化のメソッドはインターフェースのコンテキストの関心事です。最初のものはペアを作成(初期化)し、次のものはペアを削除(再初期化)します。
CiOcoObject::Init() メソッドは SOrderPropertiesタイプのストラクチャの配列を引数として受け付けます。このストラクチャタイプはペア、すなわち OCO 注文での注文プロパティを表します。
2.2 SOrderProperties ストラクチャ
上記のストラクチャのフィールドを考察します。
//+------------------------------------------------------------------+ //| Order properties structure | //+------------------------------------------------------------------+ struct SOrderProperties { double volume; // order volume string symbol; // symbol ENUM_PENDING_ORDER_TYPE order_type; // order type uint price_offset; // offset for execution price, points uint limit_offset; // offset for limit price, points uint sl; // stop loss, points uint tp; // take profit, points ENUM_ORDER_TYPE_TIME type_time; // expiration type datetime expiration; // expiration string comment; // comment }
初期化メソッドが動作するようにするには、前もって2点の要素を考慮しつつストラクチャの配列を書き込む必要があります。簡単に言うと、どの注文を出すのかプログラムに説明する必要があるのです。
このストラクチャでは ENUM_PENDING_ORDER_TYPE タイプの列挙が使用されます。
//+------------------------------------------------------------------+ //| Pending order type | //+------------------------------------------------------------------+ enum ENUM_PENDING_ORDER_TYPE { PENDING_ORDER_TYPE_BUY_LIMIT=2, // Buy Limit PENDING_ORDER_TYPE_SELL_LIMIT=3, // Sell Limit PENDING_ORDER_TYPE_BUY_STOP=4, // Buy Stop PENDING_ORDER_TYPE_SELL_STOP=5, // Sell Stop PENDING_ORDER_TYPE_BUY_STOP_LIMIT=6, // Buy Stop Limit PENDING_ORDER_TYPE_SELL_STOP_LIMIT=7, // Sell Stop Limit };
一般的に言うと、それは標準列挙ENUM _ORDER_TYPE と似て見えますが、それは未決注文だけ選択できるようにします。またはより正確にはその類の注文タイプです。
それは入力パラメータ内で対応する注文タイプを選択するとき、エラーから保護します(図2)。
図2 使用可能な注文タイプのドロップダウンリストを持つ『タイプ』フィールド
それでも標準列挙 ENUM _ORDER_TYPE を使用するなら、マーケット注文タイプ(ORDER_TYPE_BUY または ORDER_TYPE_SELL)を設定することができますが、ここでは未決注文しか処理しないため、それは不要です。
2.3. ペア の初期化
上述のように、CiOcoObject::Init() メソッドは注文ペアの初期化を行います。
事実、それは注文ペア自体を出し、新規ペアの生成が正常に行われたか失敗したかを記録します。それ自体がトレーディング処理を行うため、これは能動的なメソッドであると言えます。受動的なメソッドを作成することも可能です。それは個別に出されすでにアクティブとなっている未決注文に接続します。
メソッド全体のコードは提しません。ただトレードクラスメソッド CTrade::OrderOpen() がトレード注文を処理する価格すべて(開始、ストップ、収益、限界)を計算することが重要であることをご注意申し上げたいと思います。そのために配慮することが2つあります。注文の方向(売りか買いか)と現在価格に関連する注文実行価格のポジション(上か下か)です。
このメソッドはプライベートなメソッドを呼びだします。BaseOrderType() および PendingType()です。前者は注文の方向を決め、後者は未決注文タイプを判断します。
注文が出されると、そのチケットは m_order_tickets[] 配列に記録されます。
私はこのメソッド検証のため簡単な Init_OCO.mq5 スクリプトを使用しました。
#property script_show_inputs //--- #include "CiOcoObject.mqh" //+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ sinput string Info_order1="+===--Order 1--====+"; // +===--Order 1--====+ input ENUM_PENDING_ORDER_TYPE InpOrder1Type=PENDING_ORDER_TYPE_SELL_LIMIT; // Type input double InpOrder1Volume=0.02; // Volume input uint InpOrder1PriceOffset=125; // Offset for execution price, points input uint InpOrder1LimitOffset=50; // Offset for limit price, points input uint InpOrder1SL=250; // Stop loss, points input uint InpOrder1TP=455; // Profit, points input string InpOrder1Comment="OCO Order 1"; // Comment //--- sinput string Info_order2="+===--Order 2--====+"; // +===--Order 2--====+ input ENUM_PENDING_ORDER_TYPE InpOrder2Type=PENDING_ORDER_TYPE_SELL_STOP; // Type input double InpOrder2Volume=0.04; // Volume input uint InpOrder2PriceOffset=125; // Offset for execution price, points input uint InpOrder2LimitOffset=50; // Offset for limit price, points input uint InpOrder2SL=275; // Stop loss, points input uint InpOrder2TP=300; // Profit, points input string InpOrder2Comment="OCO Order 2"; // Comment //--- globals CiOcoObject myOco; SOrderProperties gOrdersProps[2]; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- property of the 1st order gOrdersProps[0].order_type=InpOrder1Type; gOrdersProps[0].volume=InpOrder1Volume; gOrdersProps[0].price_offset=InpOrder1PriceOffset; gOrdersProps[0].limit_offset=InpOrder1LimitOffset; gOrdersProps[0].sl=InpOrder1SL; gOrdersProps[0].tp=InpOrder1TP; gOrdersProps[0].comment=InpOrder1Comment; //--- property of the 2nd order gOrdersProps[1].order_type=InpOrder2Type; gOrdersProps[1].volume=InpOrder2Volume; gOrdersProps[1].price_offset=InpOrder2PriceOffset; gOrdersProps[1].limit_offset=InpOrder2LimitOffset; gOrdersProps[1].sl=InpOrder2SL; gOrdersProps[1].tp=InpOrder2TP; gOrdersProps[1].comment=InpOrder2Comment; //--- initialization of pair if(myOco.Init(gOrdersProps)) PrintFormat("Id of new OCO pair: %I32u",myOco.Id()); else Print("Error when placing OCO pair!"); }
ここでペアの将来の注文の多様なプロパティを設定することが可能です。MetaTrader 5 には異なる5種類の未決注文タイプがあります。
このコンテキストで、ペアのバリアント(combinations)は15です(ペアに異なる注文があると仮定して)。
C(k,N) = C(2,6) = 15
バリアントはすべてスクリプトを用いて検証されています。ペア 逆指値注文 - 指し値つきストップ注文 を例とします。
注文タイプはスクリプトパラメータで設定します(図3)。
図3 『逆指値注文』と『指し値つきストップ注文』のペア
以下の情報が登録『エキスパート』に表示されます。
QO 0 17:17:41.020 Init_OCO (GBPUSD.e,M15) Code of request result: 10009 JD 0 17:17:41.036 Init_OCO (GBPUSD.e,M15) New order ticket: 24190813 QL 0 17:17:41.286 Init_OCO (GBPUSD.e,M15) Code of request result: 10009 JH 0 17:17:41.286 Init_OCO (GBPUSD.e,M15) New order ticket: 24190814 MM 0 17:17:41.379 Init_OCO (GBPUSD.e,M15) Id of new OCO pair: 3782950319
ただループに頼ることなくスクリプトを用いて最大まで OCO 注文を処理することはできません。
2.4. ペアの再初期化
このメソッドは注文ペアを制御します。ペアはアクティブな注文リストから任意の注文が去ると『死んで』しまいます。
このメソッドを EA のコードのハンドラ OnTrade() または OnTradeTransaction() に入れると仮定します。その方法で EA はディレイなくいかなる注文のアクティブ化処理も行うことができるのです。
//+------------------------------------------------------------------+ //| Deinitialization of pair | //+------------------------------------------------------------------+ bool CiOcoObject::Deinit(void) { //--- if pair is initialized if(this.m_is_init) { //--- check your orders for(int ord_idx=0;ord_idx<ArraySize(this.m_order_tickets);ord_idx++) { //--- current pair order ulong curr_ord_ticket=this.m_order_tickets[ord_idx]; //--- another pair order int other_ord_idx=!ord_idx; ulong other_ord_ticket=this.m_order_tickets[other_ord_idx]; //--- COrderInfo order_obj; //--- if there is no current order if(!order_obj.Select(curr_ord_ticket)) { PrintFormat("Order #%d is not found in active orders list.",curr_ord_ticket); //--- attempt to delete another order if(order_obj.Select(other_ord_ticket)) { CTrade trade_obj; //--- if(trade_obj.OrderDelete(other_ord_ticket)) return true; } } } } //--- return false; }
ある詳細についてお話したいと思います。ペア初期化フラグはクラスメソッドの本文でチェックされます。注文確認の試行はフラグがクリアされていると行われません。この方法により別の注文がまだ出されていないときあるアクティブな注文が削除されることはありません。
注文が数件出されるスクリプトに機能性を追加します。このため、Control_OCO_EA.mq5 検証 EA を作成します。
一般的に言えば、この EA はコードの Trade() イベント処理ブロックが異なるだけです。
//+------------------------------------------------------------------+ //| Trade function | //+------------------------------------------------------------------+ void OnTrade() { //--- OCO pair deinitialization if(myOco.Deinit()) { Print("No more order pair!"); //--- clear pair CiOcoObject new_oco; myOco=new_oco; } }
ビデオに MetaTrader 5 ターミナルでの両プログラムの動作が示されています。
ただしどちらの検証プログラムにも弱点があります。
前者のプログラム(スクリプト)はペアは能動的に作成しますが、その後そのペアに対しての制御は失います。
後者のプログラム(Expert Advisor)はペアの制御は行いますが、最初のペア作成後他のペアを繰り返し作成することはできません。OCO 注文プログラム(スクリプト)をフル装備とするには、そのツールセットに発注機能を加えて拡張する必要があります。それは次のセクションで行います。
3. EA の制御
ペア注文のパラメータを入れて設定するためのチャート上に OCO 注文管理パネルを作成します。
それは EA 制御の一部です(図4)。ソースコードは Panel_OCO_EA.mq5 にあります。
図4 OCO 注文作成用パネル:初期状態
将来の注文タイプを選択し、OCO 注文のペアを入れるフィールドに書き込みます。
その後パネル上の唯一のボタンは変更されます(テキストプロパティ、図5)。
図5 OCO 注文作成用パネル:新規ペア
「パネル」構築には 標準ライブラリ の以下のクラスが使用されました。
- CAppDialog -メインのアプリケーションダイアログ
- CPanel -長方形のラベル
- CLabel -テキストラベル
- CComboBox -ドロップダウン付のフィールド
- CEdit -入力フィールド
- CButton -ボタン
もちろん親クラスメソッドは自動で呼び出されました。
それではコードに行きます。インディケーションパネルおよびダイアログ作成専用の標準ライブラリ部分はひじょうに大きいということを言う必要があります。
たとえば、ドロップダウンリストのクローズイベントが必要であれば、呼び出しのス宅を詳しく調べる必要があります(図6)。
図6 呼び出しのスタック
開発者は特定のイベントに対し %MQL5\Include\Controls\Defines.mqh ファイルにマクロと表記法を設定します。
私は OCO ペアを作成するために ON_OCO カスタムイベントを作成しました。
#define ON_OCO (101) // OCO pair creation event
今後の注文のパラメータが書き込まれ、OnChartEvent() ハンドラの本文にペアが生成されます。
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- handling all chart events by main dialog myDialog.ChartEvent(id,lparam,dparam,sparam); //--- drop-down list handling if(id==CHARTEVENT_CUSTOM+ON_CHANGE) { //--- if it is Panel list if(!StringCompare(StringSubstr(sparam,0,7),"myCombo")) { static ENUM_PENDING_ORDER_TYPE prev_vals[2]; //--- list index int combo_idx=(int)StringToInteger(StringSubstr(sparam,7,1))-1; ENUM_PENDING_ORDER_TYPE curr_val=(ENUM_PENDING_ORDER_TYPE)(myCombos[combo_idx].Value()+2); //--- remember order type change if(prev_vals[combo_idx]!=curr_val) { prev_vals[combo_idx]=curr_val; gOrdersProps[combo_idx].order_type=curr_val; } } } //--- handling input fields else if(id==CHARTEVENT_OBJECT_ENDEDIT) { //--- if it is Panel's input field if(!StringCompare(StringSubstr(sparam,0,6),"myEdit")) { //--- find object for(int idx=0;idx<ArraySize(myEdits);idx++) { string curr_edit_obj_name=myEdits[idx].Name(); long curr_edit_obj_id=myEdits[idx].Id(); //--- if names coincide if(!StringCompare(sparam,curr_edit_obj_name)) { //--- get current value of field double value=StringToDouble(myEdits[idx].Text()); //--- define gOrdersProps[] array index int order_num=(idx<gEditsHalfLen)?0:1; //--- define gOrdersProps structure field number int jdx=idx; if(order_num) jdx=idx-gEditsHalfLen; //--- fill up gOrdersProps structure field switch(jdx) { case 0: // volume { gOrdersProps[order_num].volume=value; break; } case 1: // execution { gOrdersProps[order_num].price_offset=(uint)value; break; } case 2: // limit { gOrdersProps[order_num].limit_offset=(uint)value; break; } case 3: // stop { gOrdersProps[order_num].sl=(uint)value; break; } case 4: // profit { gOrdersProps[order_num].tp=(uint)value; break; } } } } //--- OCO pair creation flag bool is_to_fire_oco=true; //--- check structure filling for(int idx=0;idx<ArraySize(gOrdersProps);idx++) { //--- if order type is set if(gOrdersProps[idx].order_type!=WRONG_VALUE) //--- if volume is set if(gOrdersProps[idx].volume!=WRONG_VALUE) //--- if offset for execution price is set if(gOrdersProps[idx].price_offset!=(uint)WRONG_VALUE) //--- if offset for limit price is set if(gOrdersProps[idx].limit_offset!=(uint)WRONG_VALUE) //--- if stop loss is set if(gOrdersProps[idx].sl!=(uint)WRONG_VALUE) //--- if take profit is set if(gOrdersProps[idx].tp!=(uint)WRONG_VALUE) continue; //--- clear OCO pair creation flag is_to_fire_oco=false; break; } //--- create OCO pair? if(is_to_fire_oco) { //--- complete comment fields for(int ord_idx=0;ord_idx<ArraySize(gOrdersProps);ord_idx++) gOrdersProps[ord_idx].comment=StringFormat("OCO Order %d",ord_idx+1); //--- change button properties myButton.Text("New pair"); myButton.Color(clrDarkBlue); myButton.ColorBackground(clrLightBlue); //--- respond to user actions myButton.Enable(); } } } //--- handling click on button else if(id==CHARTEVENT_OBJECT_CLICK) { //--- if it is OCO pair creation button if(!StringCompare(StringSubstr(sparam,0,6),"myFire")) //--- if to respond to user actions if(myButton.IsEnabled()) { //--- generate OCO pair creation event EventChartCustom(0,ON_OCO,0,0.0,"OCO_fire"); Print("Command to create new bunch has been received."); } } //--- handling new pair initialization command else if(id==CHARTEVENT_CUSTOM+ON_OCO) { //--- OCO pair initialization if(gOco.Init(gOrdersProps,gOcoList.Total()+1)) { PrintFormat("Id of new OCO pair: %I32u",gOco.Id()); //--- copy pair CiOcoObject *ptr_new_oco=new CiOcoObject(gOco); if(CheckPointer(ptr_new_oco)==POINTER_DYNAMIC) { //--- add to list int node_idx=gOcoList.Add(ptr_new_oco); if(node_idx>-1) PrintFormat("Total number of bunch: %d",gOcoList.Total()); else PrintFormat("Error when adding OCO pair %I32u to list!",gOco.Id()); } } else Print("OCO-orders placing error!"); //--- clear properties Reset(); } }
ハンドラコードは長くなります。数ブロックについて強調したいと思います。
全チャートイベントの最初の処理はメインダイアログに入ります。
次はさまざまなイベント処理のブロックです。
- 注文タイプを定義するドロップダウンリスト変更
- 注文のプロパティを記入する入力フィールドの編集
- ON_OCO イベント生成用ボタンのクリック
- ON_OCO イベント応答:注文ペア作成
EA はパネルフィールド記入の正確性は検証しません。そのため自分で値をチェックする必要があります。そうでないと EA は OCO 注文がエラーを出すのを表示します。
OnTrade() ハンドラの本文ではペアの削除、または残っている注文をクローズする必要があるか確認します。
おわりに
私は特定タスクのいくつかを遂行する標準ライブラリクラスの豊富さを実証しようとしました。
特に、OCO 注文処理の課題に対処しました。ここで紹介したOCO 注文処理用パネルを伴う EA のコードが、より複雑な注文ペア作成のスタート地点となるとよいと思います。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1582





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索