【MQL4関数】OrderModify 関数の使い方!ポジションの変更注文の出し方

【中級編】MQLプログラムの読み方・書き方
朝日奈りさ
朝日奈りさ

自動でポジションの損切りや利確ラインを変更するにはどうしたらいいんだろう?

OrderModify関数とは

ポジションの損切や利確の価格を変更したいときはありませんか?

損切りや利確はエントリーするとき(OrderSend関数)に設定するのが一般的ですが、エントリー後でも変更することができます。

自動売買でポジションの変更注文を出すには、OrderModify関数を使います。

 

OrderModify関数は、ポジションの変更注文を出す関数です。

変更できるポジションの種類は以下の通りです。

  • エントリー後のポジション
  • 予約ポジション(指値・逆指値)

 

つまりどちらかのポジションであれば、変更注文を出すことができます。

しかし変更できる内容には制限があります。

もちろんロット数や取得価格を変更することはできません。

 

変更できる内容は以下の4つです。

  • 予約ポジションの注文価格
  • 損切り価格
  • 利確価格
  • 予約ポジションの有効期限

 

特に、損切り価格や利確価格を変更するときに、OrderModify関数がよく使われます。

少し上級者向けの関数ですが、OrderModify関数の使い所は多いので、覚えてしまいましょう。

 

OrderModify関数の書き方

OrderModify関数の使い方は、基本的な関数と同じです。

関数をマスターしていない方はこちらの記事が参考になります。

 

基本的な書き方

OrderModify関数の基本的な書き方は以下の通りです。

簡単のために引数を省略しています。

bool orderModify = OrderModify( ①, ②, ③, ④, ⑤, ⑥);

 

OrderModify関数の返り値は bool型で、引数は6つあります。

まずは返り値から見ていきましょう。

 

返り値(戻り値)

OrderModify関数の返り値は bool型です。

true」や「false」という値が返ってきます。

数値にすると「1」や「0」です。

true や false が返ってくる条件は以下の通りです。

  • true:変更注文が正常に完了したとき
  • false:変更注文が失敗したとき

 

もし、false が返ってきた場合、変更注文が失敗しているため、再度 OrderModify関数を実行する必要があります。

ですが、注文の99%は正常に完了するため、そこまで気にしなくても大丈夫です。

もし変更注文が失敗していた場合は、手動で変更しましょう。

 

引数

OrderModify関数の引数は6つあります。

引数が多いので、一つ一つゆっくり見ていきましょう。

 

まずは一覧で確認します。

番号データ型引数名内容
intticket変更したいポジションのチケット番号
doubleprice変更後の価格
doublestoploss変更後の損切り価格
doubletakeprofit変更後の利確価格
datetimeexpiration変更後の有効期限
colorarrow_colorチャート上の矢印の色

 

それぞれ詳しく解説しますので、一緒に OrderModify関数を作っていきましょう。

 

① ticket

ticket には変更したいポジションのチケット番号を入力します。

ポジションのチケット番号を直接入力しても良いですが、OrderTicket関数という便利な関数があるので、それを使うと良いでしょう。

OrderTicket関数を実行する前に、OrderSelect関数でポジションを選択しておく必要がありますので、注意してください。

bool orderModify = OrderModify(OrderTicket(), ②, ③, ④, ⑤, ⑥)

 

② price

price には変更後の取得価格を入力します。

こちらは予約ポジション(指値注文、逆指値注文)の場合のみ変更可能です。

エントリー後のポジションの場合は、取得価格を変更できないので注意しましょう。

エントリー後のポジションを指定する場合は、エントリーしている価格を入力します。

ここでも便利な関数として、OrderOpenPrice関数というものがありますので、利用してみてください。

bool orderModify = OrderModify(OrderTicket(), OrderOpenPrice(), ③, ④, ⑤, ⑥)

 

③ stoploss

stoploss には、変更後の損切り価格を入力します。

ここで注意していただきたいのは、stoplossの価格は、現在の価格より下に設定する事です。

もし現在の価格より、有利な価格を入力すると、注文が通りません。

エラーにはなりませんが、「変更注文を出したのに、処理されてない!」ということになる可能性もあるので、stoplossの価格設定には注意しましょう。

 

stoplossで設定できるのは以下の3通りです。

  • 0
  • 損切り価格
  • OrderStopLoss関数

 

0の場合は、損切りなしに設定します。

損切り価格を入力した場合は、その価格に損切りが入ります。

OrderStopLoss関数を利用した場合は、変更前の損切り価格が設定されます。

もし損切り価格を変更せずに、他の項目を変更したい場合は、OrderStopLoss関数を使うと良いでしょう。

bool orderModify = OrderModify(OrderTicket(), OrderOpenPrice(), OrderStopLoss(), ④, ⑤, ⑥)

 

④ takeprofit

takeprofitには変更後の利確価格を入力します。

stoplossの入力と同じように、現在の価格より上でなければ注文が通りません。

 

takeprofitで設定できるのは以下の3通りです。

  • 0
  • 利確価格
  • OrderTakeProfit関数

 

0の場合は、利確なしに設定します。

利確価格を入力した場合は、その価格に利確が入ります。

OrderTakeProfit関数を利用した場合は、変更前の利確価格が設定されます。

他の項目を変更する場合は、OrderTakeProfit関数を利用すると良いでしょう。

ここでは、利確価格を変更するため、以下のように入力します。

bool orderModify = OrderModify(OrderTicket(), OrderOpenPrice(), OrderStopLoss(), 110.000, ⑤, ⑥)

 

⑤ expiration

expirationには、ポジションの有効期限を入力します。

この引数はほとんど使わないので、読み飛ばしていただいても大丈夫です。

expirationは予約ポジションのみ設定が可能です。

エントリー後のポジションには使えませんので、ご注意ください。

 

expirationはdatetime型の変数なので、datetime型の形式に合わせないとエラーが出ます。

例えば、以下のような形式です。2021年9月22日00時00分00秒まで有効という設定です。

datetime expiration = D'2021.09.22 00:00:00';

 

このように少し厄介な引数なので、基本的に OrderExpiration関数を使用すればOKです。

OrderExpiration関数は、変更前の有効期限を返す関数です。

そのため、有効期限を変更しない場合は、この関数を使用すれば問題ありません。

bool orderModify = OrderModify(OrderTicket(), OrderOpenPrice(), OrderStopLoss(), 110.000, OrderExpiration(), ⑥)

 

⑥ arrow_color

最後にarrow_colorの設定です。

こちらは、変更注文を出した位置に印がつくのですが、その色を設定します。

色をつけなくても良い場合は、「0」または、clrNONEと入力してください。

透明になりますので、チャート分析の邪魔にならないかと思います。

もし色をつけたい場合は、「clr○○」という特殊な変数がありますので、そちらを利用すると良いでしょう。

私は、clrRedが好きなので、clrRedを設定しています。

bool orderModify = OrderModify(OrderTicket(), OrderOpenPrice(), OrderStopLoss(), 110.000, OrderExpiration(), clrRed)

 

OrderModify関数の引数のまとめ

ここまでOrderModify関数の引数を、ざっと見てきました。

stoplossやtakeprofitの価格設定には気をつけるようにしてください。

また、両方同時に変更することもできるので、両方変更したい場合は、価格を入力すると変更できます。

 

変更する前の情報を取得するには、以下の関数が便利です。

  • OrderStopLoss関数
  • OrderTakeProfit関数
  • OrderExpiration関数

 

以下のプログラムはOrderModify関数の完成例です。

bool orderModify = OrderModify(OrderTicket(), OrderOpenPrice(), OrderStopLoss(), 110.000, OrderExpiration(), clrRed)

これで基本的な書き方はマスターできたと思いますので、具体的な使い方も見ていきましょう。

 

OrderModify関数の実用的なプログラム例

ここからは、実際のEAで使える実用的なプログラム例を紹介していきます。

OrderModify関数の基本的な書き方がわかったら、ぜひこれらの応用例も参考にしてみてください。

 

例1:トレーリングストップEA

最も実用的な使い方がトレーリングストップです。価格が有利な方向に動いたら、損切りラインを自動で引き上げ(引き下げ)て利益を確保する手法です。

以下のEAは、保有中の全ポジションに対してトレーリングストップを適用します。

//+------------------------------------------------------------------+
//| トレーリングストップEA                                              |
//| 価格が有利に動いたら損切りラインを自動で追従させる                      |
//+------------------------------------------------------------------+
input int TrailingStopPips = 30;  // トレーリングストップ幅(pips)
input int TrailingStartPips = 20; // トレーリング開始条件(pips利益が出てから)

void OnTick()
{
   // ポイント値を取得(5桁ブローカー対応)
   double pip = Point;
   if(Digits == 3 || Digits == 5)
      pip = Point * 10;

   // 保有中の全ポジションをループ
   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      // ポジションを選択
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      // 自分のEAの通貨ペアが一致するか確認
      if(OrderSymbol() != Symbol())
         continue;

      // 買いポジションのトレーリングストップ
      if(OrderType() == OP_BUY)
      {
         // 現在の利益がトレーリング開始条件を超えているか
         double buyProfit = Bid - OrderOpenPrice();
         if(buyProfit >= TrailingStartPips * pip)
         {
            // 新しい損切り価格を計算
            double newSL = Bid - TrailingStopPips * pip;
            // 現在のSLより有利(高い)場合のみ変更
            if(newSL > OrderStopLoss() || OrderStopLoss() == 0)
            {
               bool result = OrderModify(
                  OrderTicket(),       // チケット番号
                  OrderOpenPrice(),    // 取得価格はそのまま
                  NormalizeDouble(newSL, Digits), // 新しい損切り価格
                  OrderTakeProfit(),   // 利確はそのまま
                  OrderExpiration(),   // 有効期限はそのまま
                  clrBlue             // 矢印の色(青)
               );
               if(result)
                  Print("買いトレーリングストップ更新: SL=", newSL);
               else
                  Print("トレーリングストップ更新失敗: エラー=", GetLastError());
            }
         }
      }

      // 売りポジションのトレーリングストップ
      if(OrderType() == OP_SELL)
      {
         // 現在の利益がトレーリング開始条件を超えているか
         double sellProfit = OrderOpenPrice() - Ask;
         if(sellProfit >= TrailingStartPips * pip)
         {
            // 新しい損切り価格を計算
            double newSL = Ask + TrailingStopPips * pip;
            // 現在のSLより有利(低い)場合のみ変更
            if(newSL < OrderStopLoss() || OrderStopLoss() == 0)
            {
               bool result = OrderModify(
                  OrderTicket(),       // チケット番号
                  OrderOpenPrice(),    // 取得価格はそのまま
                  NormalizeDouble(newSL, Digits), // 新しい損切り価格
                  OrderTakeProfit(),   // 利確はそのまま
                  OrderExpiration(),   // 有効期限はそのまま
                  clrRed              // 矢印の色(赤)
               );
               if(result)
                  Print("売りトレーリングストップ更新: SL=", newSL);
               else
                  Print("トレーリングストップ更新失敗: エラー=", GetLastError());
            }
         }
      }
   }
}

 

このEAのポイントは以下の通りです。

  • TrailingStartPipsで指定したpips分の利益が出てからトレーリングを開始します
  • TrailingStopPipsで指定したpips幅で損切りラインを追従させます
  • 現在の損切りラインより有利な場合のみ変更するので、損切りが不利な方向に動くことはありません
  • NormalizeDouble関数で価格を正規化しているため、小数点エラーを防げます

 

例2:建値ストップ(ブレイクイーブン)

一定の利益が出たら、損切りラインをエントリー価格に移動させる建値ストップも非常に人気があります。

これにより、最低でも損失ゼロ(スプレッド分を除く)を保証できます。

//+------------------------------------------------------------------+
//| 建値ストップ(ブレイクイーブン)の例                                  |
//| 一定利益が出たらSLをエントリー価格に移動                              |
//+------------------------------------------------------------------+
input int BreakevenPips = 20; // 建値ストップ発動条件(pips)

void SetBreakeven()
{
   double pip = Point;
   if(Digits == 3 || Digits == 5)
      pip = Point * 10;

   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      if(OrderSymbol() != Symbol())
         continue;

      // 買いポジション
      if(OrderType() == OP_BUY)
      {
         // 条件を満たし、かつSLがまだエントリー価格以下の場合
         if(Bid - OrderOpenPrice() >= BreakevenPips * pip)
         {
            if(OrderStopLoss() < OrderOpenPrice() || OrderStopLoss() == 0)
            {
               bool result = OrderModify(
                  OrderTicket(),
                  OrderOpenPrice(),
                  OrderOpenPrice(),    // SLをエントリー価格に設定
                  OrderTakeProfit(),
                  OrderExpiration(),
                  clrGreen
               );
               if(result)
                  Print("建値ストップ設定完了: チケット=", OrderTicket());
            }
         }
      }

      // 売りポジション
      if(OrderType() == OP_SELL)
      {
         if(OrderOpenPrice() - Ask >= BreakevenPips * pip)
         {
            if(OrderStopLoss() > OrderOpenPrice() || OrderStopLoss() == 0)
            {
               bool result = OrderModify(
                  OrderTicket(),
                  OrderOpenPrice(),
                  OrderOpenPrice(),    // SLをエントリー価格に設定
                  OrderTakeProfit(),
                  OrderExpiration(),
                  clrGreen
               );
               if(result)
                  Print("建値ストップ設定完了: チケット=", OrderTicket());
            }
         }
      }
   }
}

 

例3:利確価格を段階的に変更する

利確価格を状況に応じて変更したい場合の例です。

例えば、エントリー後に相場の勢いが強い場合、利確ラインをさらに遠くに設定し直すことで、利益の最大化を狙えます。

//+------------------------------------------------------------------+
//| 利確価格を段階的に変更する例                                        |
//+------------------------------------------------------------------+
input int InitialTPPips = 50;   // 初期利確幅(pips)
input int ExtendedTPPips = 100; // 拡張利確幅(pips)
input int ExtendTriggerPips = 30; // 拡張トリガー(pips利益)

void AdjustTakeProfit()
{
   double pip = Point;
   if(Digits == 3 || Digits == 5)
      pip = Point * 10;

   for(int i = OrdersTotal() - 1; i >= 0; i--)
   {
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
         continue;

      if(OrderSymbol() != Symbol())
         continue;

      // 買いポジションの利確拡張
      if(OrderType() == OP_BUY)
      {
         double currentProfit = Bid - OrderOpenPrice();
         double initialTP = OrderOpenPrice() + InitialTPPips * pip;
         double extendedTP = OrderOpenPrice() + ExtendedTPPips * pip;

         // トリガー条件を満たし、TPがまだ初期値の場合
         if(currentProfit >= ExtendTriggerPips * pip)
         {
            if(NormalizeDouble(OrderTakeProfit(), Digits) == NormalizeDouble(initialTP, Digits))
            {
               bool result = OrderModify(
                  OrderTicket(),
                  OrderOpenPrice(),
                  OrderStopLoss(),
                  NormalizeDouble(extendedTP, Digits), // 利確を拡張
                  OrderExpiration(),
                  clrYellow
               );
               if(result)
                  Print("利確拡張完了: 新TP=", extendedTP);
            }
         }
      }

      // 売りポジションの利確拡張
      if(OrderType() == OP_SELL)
      {
         double currentProfit = OrderOpenPrice() - Ask;
         double initialTP = OrderOpenPrice() - InitialTPPips * pip;
         double extendedTP = OrderOpenPrice() - ExtendedTPPips * pip;

         if(currentProfit >= ExtendTriggerPips * pip)
         {
            if(NormalizeDouble(OrderTakeProfit(), Digits) == NormalizeDouble(initialTP, Digits))
            {
               bool result = OrderModify(
                  OrderTicket(),
                  OrderOpenPrice(),
                  OrderStopLoss(),
                  NormalizeDouble(extendedTP, Digits), // 利確を拡張
                  OrderExpiration(),
                  clrYellow
               );
               if(result)
                  Print("利確拡張完了: 新TP=", extendedTP);
            }
         }
      }
   }
}

 

OrderModify関数を使うときの注意点

最後に、OrderModify関数を使うときの注意点をまとめます。

 

注意点1:OrderSelect関数を事前に実行する

OrderModify関数を使う前に、必ずOrderSelect関数でポジションを選択しておきましょう。

OrderTicket関数やOrderOpenPrice関数などは、OrderSelect関数で選択されたポジションの情報を返すため、選択を忘れるとエラーの原因になります。

 

注意点2:NormalizeDouble関数で価格を正規化する

価格を計算で求める場合は、NormalizeDouble関数で小数点以下の桁数を揃えましょう。

桁数が合わないと注文が通らないことがあります。

// 正しい例
double newSL = NormalizeDouble(Bid - 30 * Point, Digits);

// 間違いやすい例(正規化なし)
double newSL = Bid - 30 * Point; // 桁数がずれる可能性あり

 

注意点3:変更内容がない場合はエラーになる

OrderModify関数は、何も変更がない状態で実行するとエラー(エラーコード1)が発生します。

損切り価格が同じなのに変更注文を出すと失敗するため、変更前と変更後の値が異なることを確認してから実行しましょう。

 

注意点4:ストップレベルに注意する

ブローカーによっては、現在価格から一定距離以内にSL/TPを設定できないストップレベルが設けられています。

ストップレベルはMarketInfo関数で取得できます。

// ストップレベルの取得
double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * Point;
Print("ストップレベル: ", stopLevel);

 

まとめ

今回は、OrderModify関数の使い方を解説しました。

 

OrderModify関数を使えば、エントリー後のポジションの損切りや利確を自動で変更できます。

特にトレーリングストップ建値ストップは実際のEAでも非常によく使われるテクニックです。

 

大切なポイントをおさらいしましょう。

  • OrderModify関数の返り値はbool型(true:成功、false:失敗)
  • 引数は6つ(チケット番号、価格、損切り、利確、有効期限、矢印の色)
  • 変更しない項目には、OrderStopLoss関数やOrderTakeProfit関数で現在の値を設定する
  • 事前にOrderSelect関数でポジションを選択しておくことを忘れずに
  • 価格はNormalizeDouble関数で正規化する

 

OrderModify関数をマスターすれば、より柔軟で高度な自動売買プログラムを作成できるようになります。

ぜひ今回の記事を参考に、OrderModify関数を使ったEA開発に挑戦してみてください。