13.売買を行う関数
注文・決済を行うための関数について解説します。
リファレンスには、Trade Functionsのページにまとめられています。
Trade Functions – MQL4 Reference
注文を行う OrderSend関数
サーバーに注文を送る関数がOrderSendです。
OrderSend – Trade Functions – MQL4 Reference
関数定義は以下の通りです。
int OrderSend( string symbol, // symbol int cmd, // operation double volume, // volume double price, // price int slippage, // slippage double stoploss, // stop loss double takeprofit, // take profit string comment=NULL, // comment int magic=0, // magic number datetime expiration=0, // pending order expiration color arrow_color=clrNONE // color );
どんな注文なのかを引数で指定します。
データ型 / 名前 | 内容 |
---|---|
string / symbol | トレードを行う通貨ペア名の指定です。 EAを稼働させているチャートの通貨ペアを指定したければ、Symbol関数の戻り値を利用します。 |
int / cmd | 買いか売りか、成行か指値かを表すコマンド(列挙型)の指定です。 指定できるのは、下記の6つです。 OP_BUY:成行買い OP_SELL:成行売り OP_BUYLIMIT:買い指値 OP_SELLLIMIT:売り指値 OP_BUYSTOP:買い逆指値 OP_SELLSTOP:売り逆指値 |
double / volume | ロット数の指定です。 |
double / price | 注文レートの指定です。 成行買いの場合は、その時点での買値(Ask)を指定します。 成行売りの場合は、その時点での売値(Bid)を指定します。 指値・逆指値注文の場合は、どのレートに指値・逆指値を置くかの指定になります。 |
int / slippage | 許容スリッページの指定です(レートの最小単位=ポイントで指定します)。 |
double / stoploss | ストップロスのレートを指定します。 ストップロスを置かない場合は「0」を指定します。 |
double / takeprofit | テイクプロフィットのレートを指定します。 テイクプロフィットを置かない場合は「0」を指定します。 |
string / comment | 注文に付加するコメントを指定します。 |
int / magic | 注文に付加するマジックナンバーを指定します。 EAによる注文でも、裁量による注文でも、ポジションは口座の同じ場所に記録されますので、どれがEAによって管理されているポジションなのかをEA自身に判別させるための識別番号のようなものです。 なお複数のEAを同時に稼働させる場合や、同じEAであっても複数のチャートに適用させて同時に稼働させる場合は、すべてのマジックナンバーを異なるものにしておくべきです。 |
datetime / expiration | 指値・逆指値注文の有効期限を指定します。 成行注文の場合は無視されます。 |
color / arrow_color | EAによって注文が行われると、チャート上には矢印が表示されます。 この矢印の色を指定します。 |
OrderSend関数は、int型の値を戻り値として返します。
戻り値が「-1」の場合は、注文が失敗したことを表しています。
注文が成功した場合は、注文番号が返されます。
注文が成功したかどうかを確認する場合は、戻り値を取得して判定を行います。
成行買い注文を行う場合は、下記のようになります。
int result = OrderSend(Symbol(),OP_BUY,0.1,Ask,3,0,0,NULL,0,0,clrNONE); if(result != -1){ Print("注文成功"); }else{ Print("注文失敗"); }
※参考のため2行目~で戻り値を確認して注文が通ったかどうかを表示していますが、注文結果に関するログは自動的に吐き出されており、ツールボックスのエキスパートタブで確認できます。
成行買いでは、その時点での買値を指定する必要があり、上記では「Ask」を記述しています。
逆に、成行売りの場合は「Bid」を指定します。
このAskやBidは、MQL4であらかじめ定義済みの定数です。その他の定義済み定数は以下のURLで確認できます。
Predefined Variables – MQL4 Reference
決済を行う OrderClose関数
保有中のポジションを決済するのがOrderClose関数です。
関数の定義は以下です。
bool OrderClose( int ticket, // ticket double lots, // volume double price, // close price int slippage, // slippage color arrow_color // color );
第1引数の「ticket」は、OrderSend関数の戻り値にもなっている注文番号です。
そのEAが注文したポジションであれば、注文時に注文番号を取得することができます。
注文番号を変数に保持しておけば、決済注文に使用することができます。
ただ、注文番号をプログラムで保持せずとも決済を行うことは可能になっていて、こちらの方法が恐らく一般的です。
その方法は、口座に存在する全ての注文を1つ1つ調べて、このEAが注文したものかどうかを判別していくというものです。
口座で保有しているポジションは、注文を保持している領域(プール)に、まるで配列のように並んでいるとイメージすることができます(正確には配列ではありませんが)。
またこのプールには、保有しているポジションだけでなく、指値・逆指値の待機注文も含まれています。
前述の通り、EAによるポジションかどうかを判別するためには、一般的にそのポジションのマジックナンバーが参照されます。
マジックナンバーを調べるためのOrderMagicNumber関数があります。
OrderMagicNumber – Trade Functions – MQL4 Reference
上記リファレンスページのNoteに、
注文は、OrderSelect()関数によって事前に選択されている必要があります。
という注記があります。
プログラム内で注文を選択している状態になると、その注文のあらゆる情報を調べることが可能になります。
OrderSelect関数の定義を確認してみます。
OrderSelect – Trade Functions – MQL4 Reference
bool OrderSelect( int index, // index or order ticket int select, // flag int pool=MODE_TRADES // mode );
データ型 / 名前 | 内容 |
---|---|
int / index | 注文を特定するインデックスの指定です。 注文番号またはプール内の番号で指定することができます。 |
int / select | 注文の選択方法の指定です。 第1引数に指定したインデックスが注文番号なのかプール内の番号なのかを列挙型で指定します。 注文番号なら、SELECT_BY_TICKET プール内の番号なら、SELECT_BY_POS を指定します。 |
int / pool | プールの指定です。 保有中のポジション及び待機注文が保持されているプールとは別に、決済済みの注文が保持されているプール(履歴)も存在します。 列挙型で指定します。 未決済注文なら、MODE_TRADES 決済済み注文なら、MODE_HISTORY を指定します。 |
OrderSelect関数によって、正常に注文が選択できると、戻り値にtrueが返されます。
プール内の注文は、配列同様に0から順に番号が振られています。
プール内の注文が何個あるのかが分かれば、ループ処理することが可能です。
OrdersTotal関数を使います。
OrdersTotal – Trade Functions – MQL4 Reference
OrdersTotalは、戻り値として未決済注文数をint型で返します。
なお、決済済み注文の個数を調べる取得する場合は、OrdersHistoryTotal関数を使います。
これでやっと、注文番号が不明であってもポジションを特定することができるようになります。
処理の流れをまとめると、
- 口座の未決済注文数を取得する(OrdersTotal関数)
- 未決済注文数の分だけループ処理を行う(forループ)
- ループ処理内 – 1つずつ注文を選択する(OrderSelect関数)
- ループ処理内 – 選択したポジションのマジックナンバーを調べる(OrderMagicNumber関数)
- ループ処理内 – 買い・売りどちらの注文か調べる(OrderType関数)
- ループ処理内 – 買いポジションなら売値で、売りポジションなら買値で決済する(OrderClose関数)
となります。
なお、決済すべきポジションが複数存在するケースが考えられる場合(EAが複数ポジションを取る場合)は、注文プールを番号の大きい方からループさせることが推奨されます。
これは、注文が決済されてプール内の注文数が変化した時に、それ以降にある注文の番号が振り直されるためです。
番号が振り直されるのと、forループでの増分が同時に起こり、全ての注文を調べることが出来なくなってしまいます。
とにかくいつでも大きい方からループ、と覚えてしまって問題ないと思います。
実際に上記の流れを書くと以下のようになります。
int total = OrdersTotal(); for(int i=total-1; i>=0; i--) { if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) { if(OrderMagicNumber() == '12345') { if(OrderType() == OP_BUY) { OrderClose(OrderTicket(), OrderLots(), Bid, 3, clrNONE); } else if(OrderType() == OP_SELL) { OrderClose(OrderTicket(), OrderLots(), Ask, 3, clrNONE); } } } }
以上が売買を行うための関数です。
プール内の注文をループしての決済処理は非常に面倒くさい印象ですが、定型として覚えてしまいましょう。
次回は、ユーザー定義関数について解説します。