MQL4のOrderModifyでFX自動売買の注文を変更する使い方と確認手順

MQL4のOrderModifyは、保有中の注文の損切り・利確や、待機注文の価格・有効期限を変更する関数です。SLだけ変える場合も、TPなどの引数を省略することはできません。残したい値には選択した注文の現在値を渡します。
FX自動売買へ組み込むときは、対象を選ぶ、変更する項目と保持する項目を分ける、同値なら送らない、戻り値を調べる、再選択して結果を読む、という順で考えると整理できます。この記事では一件の注文変更を扱い、価格に追従する売買ルールとは分けて説明します。
- 成行注文と待機注文で変更できる項目の違い
- SLだけ変更するときに保持する引数と0の意味
- 同値の送信を避けて戻り値とエラーを調べる流れ
- デモ口座で変更後の注文を再選択して照合する方法
FX自動売買で使うMQL4のOrderModifyの引数

変更できる項目を注文の種類で分ける
OrderModifyが扱うのは、すでに存在する注文の変更です。新しい注文を発注するOrderSendとは役割が違い、注文数量や売買方向を自由に書き換える関数でもありません。変更対象はticketという注文の識別番号で指定します。戻り値は新しいチケット番号ではなく、変更の成功・失敗を表すbool型です。
成行で約定済みの買い・売り注文では、SLとTPの変更を考えます。約定価格はすでに成立した取引の値なので、priceへ別の数字を渡して建値を動かすことはできません。一方、まだ約定していない指値・逆指値の待機注文では、注文価格と有効期限も変更対象になります。
| 注文の状態 | 変更する項目 | 保持・対象外の項目 |
|---|---|---|
| OP_BUY/OP_SELL | SL・TP | 約定価格は変更不可。有効期限の変更対象でもない |
| 指値・逆指値の待機注文 | 注文価格・SL・TP・有効期限 | 注文種別や数量はこの引数では変更しない |
| 決済済み・取消済み | 本記事の変更対象外 | 選択できたことだけで現在注文と判断しない |
この違いはMetaQuotes公式教材の注文特性と変更ルールで確認できます。目的が「保有中の買いのSLを一度変更する」なら、待機注文の価格計算や方向判定まで一つの処理に混ぜる必要はありません。最初は注文種別を限定すると、保持すべき値が見えやすくなります。
六つの引数を変更値と保持値に分ける
bool OrderModify(
int ticket,
double price,
double stoploss,
double takeprofit,
datetime expiration,
color arrow_color
);
引数の順番は、チケット、価格、SL、TP、期限、矢印の色です。SLとTPはポイント数ではなく価格水準を渡します。たとえば「150ポイント離す」という距離そのものをstoplossへ渡すと、意図する損切り価格になりません。距離から価格を求める処理は、関数を呼ぶ前に済ませます。
| 引数 | 今回変える場合 | 今回変えない場合 |
|---|---|---|
| ticket | 対象注文の番号を指定 | 別の注文の番号を流用しない |
| price | 待機注文の新しい注文価格 | 選択した注文のOrderOpenPrice() |
| stoploss | 変更後のSL価格 | 選択した注文のOrderStopLoss() |
| takeprofit | 変更後のTP価格 | 選択した注文のOrderTakeProfit() |
| expiration | 待機注文の新しい期限 | 待機注文ではOrderExpiration()を保持 |
| arrow_color | 変更表示の矢印色 | 表示不要ならclrNONE |
矢印色の指定は、変更要求の可否を決める条件ではありません。clrNONEで矢印を表示しない場合も、注文のSLやTPが変更されたかは注文情報で確認します。チャートに印が見えないという理由だけで、要求が失敗したと判断しないでください。書式と各引数はOrderModifyの公式リファレンスが根拠です。

SLだけの変更でTPに0を渡さない
0は「この項目はそのまま」という共通の指定ではありません。SL・TPへ0を渡すと、そのストップ注文を設定しない、または外す要求になります。すでにTPがある注文で、SLだけ変更したいのにtakeprofitを0にすると、利益確定の設定まで外す内容になってしまいます。
SLだけ変更するなら、新しいSLと、現在のTPを渡します。逆にTPだけ変更するなら、現在のSLと、新しいTPを渡します。すでにSLやTPが0で、その状態を残したい場合に限って、取得した0をそのまま戻すことになります。「明示的に外す0」と「取得した現在値が0」を意図として区別するわけですね。
// 対象の現在注文を正しく選択済み、という前提の呼び出し形
OrderModify(OrderTicket(), OrderOpenPrice(),
newSL, OrderTakeProfit(),
OrderExpiration(), clrNONE);
この短い形は引数の対応を示す説明用で、そのまま運用する完成コードではありません。次の章では、対象の確認、同値判定、戻り値の確認を含む例にします。ストップの維持・削除の考え方は公式教材の注文変更の説明でも扱われています。
待機注文のexpirationでも、0は「変更しない」の意味ではなく、期限を設けない指定です。既存の期限を残すならOrderExpiration()を読み、その値を渡します。保有中の成行注文では期限を変更しないため、後述の成行専用例ではexpirationに0を渡しています。同じ0でも、注文の種類と引数の意味を合わせて判断します。
同値なら送らず価格の精度をそろえる
何も変わらない値でOrderModifyを呼ぶと、公式仕様ではエラー1のERR_NO_RESULTが発生します。SLだけを扱うなら、新しいSLを銘柄の桁数へ丸めたうえで、現在のSLと比較し、実質的に同じなら呼び出しません。これにより、価格更新のたびに同じ要求を送る処理を避けられます。
比較は丸める前の計算値で行うより、送信予定の値で行う方が意図を追いやすくなります。小数点以下に差があっても、銘柄の桁数にそろえた結果が同じなら、変更要求としての違いがないためです。記事の例ではpointの半分を比較の許容幅とします。これは例の比較方法であり、業者の約定許容差を指定するものではありません。
価格の小数桁は対象銘柄のMODE_DIGITS、最小の表示単位はMODE_POINTで取得します。別銘柄の注文も扱う関数にするなら、チャートのDigitsやPointを無条件に流用しない設計が必要です。NormalizeDoubleの公式説明は、計算したSL・TPや待機注文価格を銘柄の精度へそろえることを求めています。

ただし、NormalizeDoubleを呼べばすべての銘柄で有効な注文価格になるわけではありません。小数桁の整形と、取引可能な価格刻みへの適合は確認する対象が異なります。MODE_TICKSIZEなどの仕様も確認し、表示単位と取引刻みが異なる銘柄へは、後述の最小例をそのまま適用しないでください。
注文の選択と送信前の確認を分離する
OrderTicket、OrderOpenPrice、OrderStopLossなどが返すのは、OrderSelectで選択した注文の情報です。先に注文を選び、成功を確認してから必要な値を取得します。OrderModifyそのものはticketで対象を指定しますが、保持値を安全にそろえるには、同じ対象を選択してから読む流れが欠かせません。

SELECT_BY_TICKETで見つかった注文が、まだ保有中・待機中とは限りません。チケット指定では履歴から選択されることもあるため、OrderCloseTime()が0か、注文種別が意図どおりかを確認します。銘柄とマジックナンバーも照合し、選べた注文を無条件に変更するコードにしないことが大切です。
注文を選ぶ処理の詳しい違いは、公開済みのMQL4のOrderSelectによる注文選択と絞り込みで説明しています。ここでは一覧を巡回して全注文を変える実装に広げず、確認した一件だけを変更する形を使います。
なお、選択して保持値を取得しても、注文の状態がその後ずっと固定されるわけではありません。他のEAや手動操作が同じ注文を管理すると、取得後に値が変わる可能性があります。読み取ったTPを保持値として送り返す処理が、その間の別の変更を上書きすることもあり得ます。同じチケットをどの処理が管理するかを決めておきましょう。
MQL4のOrderModifyをデモで実装して確かめる

成行注文のSLだけを変える最小例
次はデモ口座で、一件の成行注文のSLだけを変更するMQL4スクリプトの学習例です。新規注文は発注せず、入力したチケット、チャート銘柄、マジックナンバーが一致する現在注文だけを対象にします。SLを外す用途は除き、NewSLが0以下なら中止します。実口座ではIsDemoの判定で中止する形です。
MetaEditorでのコンパイルとMT4での実行は未確認です。注文距離、取引刻み、変更禁止領域、取引許可などを網羅する完成EAではありません。一般的なFX銘柄を想定し、使用するデモ環境の仕様を確認してから読み進めてください。入力値は説明のための初期値で、推奨する損切り価格を意味しません。
#property strict
#property script_show_inputs
input int TargetTicket = 0;
input int ExpectedMagic = 0;
input double NewSL = 0.0;
// -1: 中止/失敗、0: 変更不要、1: 照合済み、2: 送信成功後の照合未完了
int ModifyMarketSLDemo(int ticket, int magic, double requestedSL)
{
if(!IsDemo() || ticket <= 0 || requestedSL <= 0) return -1;
ResetLastError();
if(!OrderSelect(ticket, SELECT_BY_TICKET))
{
int selectError = GetLastError();
Print("select failed: ", selectError);
return -1;
}
int type = OrderType();
if(OrderCloseTime() != 0 || (type != OP_BUY && type != OP_SELL)) return -1;
if(OrderSymbol() != Symbol() || OrderMagicNumber() != magic) return -1;
string sym = OrderSymbol();
int digits = (int)MarketInfo(sym, MODE_DIGITS);
double point = MarketInfo(sym, MODE_POINT);
if(point <= 0) return -1;
double sl = NormalizeDouble(requestedSL, digits);
if(sl <= 0) return -1;
double oldSL = OrderStopLoss();
double keepTP = OrderTakeProfit();
double keepPrice = OrderOpenPrice();
if(MathAbs(sl - oldSL) < point / 2.0) return 0;
ResetLastError();
bool ok = OrderModify(ticket, keepPrice, sl, keepTP, 0, clrNONE);
if(!ok)
{
int modifyError = GetLastError();
Print("modify failed: ticket=", ticket, " error=", modifyError);
return -1;
}
ResetLastError();
if(!OrderSelect(ticket, SELECT_BY_TICKET))
{
int verifyError = GetLastError();
Print("verify select failed: ", verifyError);
return 2;
}
if(OrderCloseTime() != 0 || OrderType() != type) return 2;
if(MathAbs(OrderStopLoss() - sl) >= point / 2.0) return 2;
if(MathAbs(OrderTakeProfit() - keepTP) >= point / 2.0) return 2;
if(MathAbs(OrderOpenPrice() - keepPrice) >= point / 2.0) return 2;
return 1;
}
void OnStart()
{
int result = ModifyMarketSLDemo(TargetTicket, ExpectedMagic, NewSL);
Print("result=", result);
}
ExpectedMagicが0なら、識別番号0の注文に一致するという意味です。「すべてのEAを対象にする」という指定ではありません。デモで対象注文の番号と識別番号を確認し、その注文の銘柄のチャートで実行する前提です。コードを読む際も、対象外で戻る分岐を先に確認すると、変更が行われる範囲を追えます。
この例のresultは、OrderModifyの公式の戻り値ではなく、記事内で定義した確認結果です。0は送信せずに済んだ状態、1は送信後の再読込で照合できた状態です。2は送信自体は成功したものの、その後の確認を完了できなかった状態なので、同じ値で即座に再送せず、現在の注文を調べます。
待機注文は価格と期限の保持を先に決める
待機注文の価格だけ変える場合も考え方は同じです。変えるのはpriceで、SL・TP・有効期限を絶対価格と日時のまま残すなら、それぞれの現在値を渡します。注文価格を動かしたからといって、SLとTPが同じ距離だけ自動で動く前提にはしません。
次は呼び出し部分だけの説明用断片です。デモ口座で対象を選択し、OrderCloseTimeが0、銘柄・識別番号が一致し、OrderTypeがOP_BUYLIMIT・OP_SELLLIMIT・OP_BUYSTOP・OP_SELLSTOPのいずれかと確認済み、という前提です。newPriceとdigits、pointも対象銘柄の値として準備します。
double keepSL = OrderStopLoss();
double keepTP = OrderTakeProfit();
datetime keepExpiry = OrderExpiration();
double price = NormalizeDouble(newPrice, digits);
if(MathAbs(price - OrderOpenPrice()) >= point / 2.0)
{
ResetLastError();
bool ok = OrderModify(OrderTicket(), price,
keepSL, keepTP, keepExpiry, clrNONE);
if(!ok)
{
int err = GetLastError();
Print("pending modify failed: ", err);
}
// 成功後はチケットで再選択し、種別・価格・SL・TP・期限を照合する
}

たとえば買い指値の価格を上へ移し、SLとTPの絶対価格は保持すると、注文価格からSLまでの距離と、TPまでの距離が変わります。希望する管理が「価格だけ変更」なのか「距離を維持して三つを再計算」なのかを先に決めてください。後者はSL・TPも変える別の要求です。保持と再計算を混同しないことが、意図しない変更を防ぎます。
期限だけを変えるなら、price・SL・TPを保持し、新しいexpirationを渡します。期限を扱えない取引サーバーでは、0以外を指定するとエラー147になる場合があります。失敗したからといって自動で0へ置き換えると、「期限付きで待ちたい」という元の条件を変えるため、対応方針を決めずに代替送信しないでください。
falseの直後にエラーを一度だけ取得する
失敗を調べるときは、OrderModifyの直前にResetLastErrorを呼び、戻り値がfalseなら直後にGetLastErrorで理由を変数へ保存します。前の処理のエラーと、今回の変更要求のエラーを混同しにくい順序です。成功の判定はboolの戻り値を基準にし、エラー値だけを眺めて置き換えないようにします。
GetLastErrorの公式仕様では、呼び出すと_LastErrorの内容がリセットされます。そのため、同じ失敗について何度もGetLastErrorを呼ぶより、一度取得したerrを、画面表示や条件分岐に使う方が確実です。ResetLastErrorの公式仕様も、事前に値を0へ戻す処理と合わせて確認できます。
| エラー | 切り分ける内容 | 最初に取る行動 |
|---|---|---|
| 1 | 変更内容が実質的に同じ | 丸め後の値と現在値、同値判定を確認 |
| 130 | SL・TPなどの価格条件が不正 | 方向、距離、対象銘柄の仕様を確認 |
| 145 | 市場に近すぎる等で変更拒否 | 変更禁止領域とその時点の注文状態を確認 |
| 147 | 待機注文の期限指定が拒否された | 期限の利用可否と要求条件を確認 |
| 128/通信上の問題 | タイムアウトなどで確認が必要 | 再接続後に注文を再選択して照合 |
コード番号の名称はMetaQuotesの実行エラー一覧で確認できます。すべてを同じ待ち時間で繰り返す設計にはせず、要求値を直す問題、状態の再確認が必要な問題、一時的な通信問題を分けます。同じ要求を毎ティック送り続けると、原因が埋もれるうえ、不要な要求が増えます。

価格制約を見る際はMODE_STOPLEVELとMODE_FREEZELEVELを区別します。STOPLEVELが0でも、動的な制約がないと断定はできません。これらの意味は公式の銘柄プロパティ一覧が根拠です。距離違反の具体的な確認はMT4のエラー130を直す注文条件の確認手順へ切り分け、この記事では引数と処理順を中心に扱います。
再選択で変更した値と保持した値を照合する
OrderSelectは、注文情報をプログラム側へコピーする関数です。選択後に注文が変わっても、保持している属性が自動で常に最新になるとは扱えません。OrderSelectの公式説明に沿って、変更後も対象チケットを再選択してから、SL・TPなどを読み直します。
SL変更の確認なら、新しいSLだけでなく、保持したTPが残っているか、注文が未決済か、注文種別が同じかも見ます。待機注文なら、注文価格・SL・TP・有効期限を確認します。期限を残す予定だったのに0になっていた場合は、価格の変更だけを見て成功としないようにします。
送信後に待機注文が約定したり、成行注文が決済されたりする場合もあります。その場合は、変更前と同じ種類の注文として比較を続ける前に、現在の状態を分類します。再選択が失敗したことだけで「変更は実行されていない」と決めつけず、取引画面と接続状態を確かめてください。
また、戻り値がtrueで照合できても、それは照合した時点の結果です。その後に別のEAや手動操作が変更することまで防ぐ仕組みではありません。取得・送信・確認の間を完全に一つの操作として固定する例でもないので、複数の処理が同じ注文を書き換える環境では、管理担当の整理が別途必要です。
デモで変更不要と保持を順に確認する
最初の確認では、動作が分かっている一件のデモ注文を用意し、チケット、銘柄、注文種別、マジックナンバー、SL・TP・有効期限を確認します。初回から口座全体を一括変更しないことが、どの値が変わったかを追うための条件になります。既存のSL・TPを持つ注文を使うと、保持項目も確認できます。
| 確認する場面 | 期待する処理 | 照合する項目 |
|---|---|---|
| 現在SLと同じ価格を入力 | 変更不要として送信しない | result=0、既存SL・TPの維持 |
| 条件に合う新SLを入力 | 一回の変更要求後に再選択 | SLだけが新値、TPと約定価格は維持 |
| 異なる銘柄・識別番号を指定 | 対象外として中止 | 注文の値が変わっていない |
| 決済済みチケットを指定 | 変更対象外として中止 | 現在注文として処理していない |
| 待機注文の価格だけ変更 | 期限・SL・TPは指定方針で保持 | 注文価格に加えて保持項目も一致 |

これは確認手順の例であり、実行して得た検証結果ではありません。通信失敗や業者側の制約をわざと本番口座で起こす必要もありません。デモで要求値と戻り値、再選択した値を対応づけ、分からない失敗が残る場合は、条件を広げずにその一件を調べます。
OnTickへ移す際には、同値判定だけでなく、いつSLを変更するのかという売買条件も必要になります。この例は含み益に応じたトレーリングを実装していません。端末の標準機能を使う目的ならMT4標準のトレーリングストップ設定と動作条件を参照し、関数の呼び出しと追従ルールを分けて検討してください。
MQL4のOrderModifyは保持値まで確認して使う
OrderModifyを使うときは、対象チケットを確認し、成行注文か待機注文かを分けたうえで、今回変える項目だけを新しい値へ差し替えます。残したいSL・TP・価格・期限には、対象注文から取得した値を渡します。0を保持の省略記号として使わないことが、引数を読むときの基本です。
そのうえで、送信予定の値を適切な精度へそろえ、同値なら送信しない、falseなら直後にエラーを保存する、送信後は再選択して変更値と保持値を照合する、という流れにします。まずはデモで一件の注文に絞り、変更不要・正常変更・対象外を区別できることから確認しましょう。
