リミット・ストップの手動設定
MQL4でのリミット・ストップの設定(または変更)の説明する前に、いつも通りMT4での操作から確認しましょう。
リミットやストップを設定する時は、設定したいポジションを選択して「注文変更または取消」をクリックします。
注文画面が開かれたら、決済逆指値の価格か、決済指値の価格(または両方)の設定をして発注します。
ちなみに決済逆指値や決済指値の価格に異常があれば発注ボタンは押せない状態になります。
決済逆指値や決済指値で設定できる価格については
H3.板情報と指値注文・逆指値注文について参照。
業者側の制限に引っかからなければ注文を受け付けてくれます。
ポジションの決済逆指値・決済指値の値が変更されました。
操作履歴のログにも、注文変更を発注して、サーバー側が受け付けた記録が残っています。
MT4で手動でリミット・ストップを設定する時はこの手順を踏む必要があります。
何度も言っていますが1から10まで指示しなければならないのがプログラムなので、
手動でやっていた事は「プログラムではやらなくて良い」なんて事はありません。
OrderSelect()でチケットNo指定選択する
前回説明しましたが、保有ポジションに対して何かしたい場合は、まず
OrderSelect()関数を使ってポジションを選択する必要があります。
前回ポジション選択するプログラムを作成したのでそれを流用しましょう。
前回
es3.ポジションの選択で作成したTest_TradeOrderSelectPosition.mq4をコピペしてTest_TradeOrderModify.mq4に名前を変更して下さい。
MQL4でリミットの設定(または変更)をするには
OrderModify()関数を使用します。
注文変更用の内部関数を作成して、その関数内にソースコードを追記します。
OrderModify()を使う時にチケットNoを指定する必要があるので、最初にポジション選択の処理を追加します。
前回登場した
OrderSelect()関数がまた登場しましたが、
今回は第2引数を
SELECT_BY_TICKETにしてチケット指定にしているので、ポジションのインデックスでは無く、
ダイレクトにチケットNo指定でポジションを選択します。
勘の良い人は気がついたかもしれませんが、
「どうしてインデックス指定でポジション選択をしているのに、今度はチケットNoで再びポジション選択をしているの?」と疑問に思いませんでした?
これは単純にこの
注文変更用の関数を独立させて、他関数との結合度を無くす為です。
プログラミング経験が少ない人にはちょっと分かり難いかもしれませんが、
結合度の高い関数は他関数との依存度が高く、他関数と一緒に使わないと正常に動作しません。
現実社会で例えるなら常に先輩に質問して一人で仕事をこなす事が出来ず、先輩と一緒に作業をさせないとまともに仕事が出来ない依存度の高い社員です。
結合度が低く独立した関数は、他関数と一緒に使わなくてもそれ単体で正常動作出来る関数です。
現実社会で例えるなら一人で仕事をこなせる他人への依存度の低い社員です。
関数の結合度を無くす事で、この関数内で何か不具合があった時にこの独立した関数内だけを
デバッグすれば良くなります。
OrderModify()でリミット設定する
先程追加した内部関数にOrderModify()関数の処理を追加してリミット設定しましょう。
簡単に説明すると、選択したポジションが買いエントリーなら、エントリー価格に200pips加算した価格をリミット価格として発注しています。
OrderModify()の第2引数と第5引数は保留中の(約定していない)エントリー指値・エントリー逆指値用ですので、
今回のような決済指値・決済逆指値の設定の時は0を設定して下さい(0を設定しなくても無効なので特に何もありませんが)。
また有効期限と色のデフォルト値が無いので、今回は省略する事が出来ません(デフォルト値追加して欲しい・・・)。
ちなみに最後の色はストップ・リミットライン自体の色では無く、注文受付した時のラインの色になります。
上位関数に呼び出し処理を追加します。
関数を呼び出す時に引数にチケットNoを渡すのを忘れないで下さい。
さっそくスクリプトを動かしてみましょう
対象のポジションのリミットが変更されました。
ちなみに逆にリミットを解除したい場合はリミット価格を0にして発注します。
変更失敗時
OrderSend()の時と同様に何かしらのエラーが発生しますので、
es2.OrderSend()関数のエラーの時と同様にエラーログ出力処理を追加します。
stdlib.mqhの
インクルードも忘れずに行って下さい。
エラーログ出力を追加したら、もう一度スクリプトを動かしてみましょう
結果:
EURUSD,H1: [112]オーダー変更エラー。 エラーコード=1 エラー内容=no error, trade conditions not changed
3桁以内の
トレードサーバーが返すエラーコードが出力されました。
内容は「エラーでは無いけど、変化がありません」。
理由は簡単で今現在設定されているリミット価格に対して同じリミット価格で変更するように要求した為、
エラーというか「意味の無い事をしていますよ」という警告がログ出力されました。
これを回避する為に、価格に変化が無い場合は発注しないようにしましょう。
OrderModify()関数の直前にこの処理を追加します。
設定済みのリミット価格と、設定するリミット価格の差の絶対値が0.1pips未満の場合は処理を終了します。
これで警告回避出来ます。
売り注文に対するリミット設定
買い注文に対するリミット設定の方法は分かったと思いますので、売り注文のリミット設定は自作してみて下さい。
と言っても問い合わせが来ると思うのでソースコードだけ記載しておきます。
ソースコード:
if ( get_order_type == OP_BUY ) {
set_limit_rate = entry_rate + set_offset;
} else if ( get_order_type == OP_SELL ) {
set_limit_rate = entry_rate - set_offset;
} else {
return ret;
}
動作確認する前に上位関数でOP_BUYでフィルタリングしているので、そこを削除して下さい。
売り注文に対してもリミット設定されました。
ストップを設定する
リミット設定でしなければならない事が分かった所で、最後にストップの設定をします。
リミットとストップは同時に変更注文を出せるので、単純にストップ価格設定の処理を追加します。
ソースコード:
double set_stop_rate = 0 ;
if ( get_order_type == OP_BUY ) {
set_limit_rate = entry_rate + set_offset;
set_stop_rate = entry_rate - set_offset;
} else if ( get_order_type == OP_SELL ) {
set_limit_rate = entry_rate - set_offset;
set_stop_rate = entry_rate + set_offset;
} else {
return ret;
}
double limit_diff;
double stop_diff;
limit_diff = MathAbs( set_limit_rate - OrderTakeProfit() );
stop_diff = MathAbs( set_stop_rate - OrderStopLoss() );
if ( limit_diff < Point() && stop_diff < Point() ) {
return ret;
}
modify_bool = OrderModify(
in_ticket,
0,
set_stop_rate,
set_limit_rate,
0,
clrYellow
);
注意しなければならないのは、前に説明した通りリミットやストップを取り消したい場合は0を設定する必要があるので、
変更しないからと言って0を設定するとリミット・ストップ注文が取り消されます。
その為リミットが設定済みの状態で、ストップ設定の発注を行う場合は、リミットは現在設定済みの価格と同じ設定をする必要があります。
同価格設定の警告はリミットとストップの両方の設定価格が同一だった場合に発生するので、
リミットとストップの設定価格差の両方をチェックする必要があります。
なのでリミット価格差とストップ価格差の両方をチェックして、両方とも同一価格なら処理終了させています。
ソースコード変更したら動作チェックしましょう。
ストップロスも変更されました。
完成したサンプルソースコード
ソースコード:
#property copyright "yuki"
#property link "https://yukifx.web.fc2.com/"
#property version "1.00"
#property strict
#include <stdlib.mqh>
void OnStart()
{
int position_total = OrdersTotal();
printf( "[%d]ポジション数:%d" , __LINE__ , position_total );
for ( int icount = 0 ; icount < position_total ; icount++ ) {
bool ret = false;
ret = OrderSelect( icount,
SELECT_BY_POS
);
if ( ret == true ) {
if ( OrderMagicNumber() != 20200518 ) {
continue;
}
if ( OrderSymbol() != Symbol() ) {
continue;
}
Modify_Order( OrderTicket() );
printf( "[%d]インデックス=%d, チケットNo=%d, マジックナンバー=%d, ロット=%f"
, __LINE__
, icount
, OrderTicket()
, OrderMagicNumber()
, OrderLots()
);
}
}
}
bool Modify_Order( int in_ticket ){
bool ret = false;
bool select_bool;
select_bool = OrderSelect(
in_ticket ,
SELECT_BY_TICKET
);
if ( select_bool == false ) {
return ret;
}
bool modify_bool;
int get_order_type;
double set_limit_rate = 0 ;
double set_stop_rate = 0 ;
double entry_rate;
double set_offset;
entry_rate = OrderOpenPrice();
get_order_type = OrderType();
set_offset = Point() * 10 * 200;
if ( get_order_type == OP_BUY ) {
set_limit_rate = entry_rate + set_offset;
set_stop_rate = entry_rate - set_offset;
} else if ( get_order_type == OP_SELL ) {
set_limit_rate = entry_rate - set_offset;
set_stop_rate = entry_rate + set_offset;
} else {
return ret;
}
double limit_diff;
double stop_diff;
limit_diff = MathAbs( set_limit_rate - OrderTakeProfit() );
stop_diff = MathAbs( set_stop_rate - OrderStopLoss() );
if ( limit_diff < Point() && stop_diff < Point() ) {
return ret;
}
modify_bool = OrderModify(
in_ticket,
0,
set_stop_rate,
set_limit_rate,
0,
clrYellow
);
if ( modify_bool == false) {
int get_error_code = GetLastError();
string error_detail_str = ErrorDescription(get_error_code);
printf( "[%d]オーダー変更エラー。 エラーコード=%d エラー内容=%s"
, __LINE__ , get_error_code , error_detail_str
);
} else {
ret = true;
}
return ret;
}