【検証#10】OrderModifyエラー1の正体¶
この記事の3行まとめ
- 🚨
OrderModify error 1は「同じ値で変更しようとした」エラー - 💻 原因は浮動小数点誤差で「同じ」と判定されてしまうこと
- ✅ 対策:0.5*_Pointのシフトまたは事前の一致チェック
はじめに¶
この記事は、「とあるMetaTraderの備忘秘録」様のブログ記事を検証・紹介するシリーズの第10弾です。
トレーリングストップを実装したEAで頻発する「error 1」の正体を解明します。
元ネタ
TrailingStop と OrderModify error 1 の微妙な関係。
https://fai-fx.hatenadiary.org/entry/20091122/1258893252
(2009年11月22日 公開)
時代背景に関する注意
この記事の元となるブログ記事は2009年頃に公開されたものです。
MQL5では OrderModify の代わりに PositionModify などの関数を使用しますが、浮動小数点誤差による判定の問題は共通です。
問題提起¶
よくあるエラーログ¶
2024.01.15 10:30:45 OrderModify error 1
2024.01.15 10:30:47 OrderModify error 1
2024.01.15 10:30:49 OrderModify error 1
error 1 は「不明なエラー」という意味ですが、実際には明確な原因があります。
エラー1の正体¶
公式ドキュメント¶
ERR_NO_RESULT (1): No error returned, but the result is unknown
実際の原因¶
「変更前と変更後の値が同じ」 場合にこのエラーが発生します。
// 現在のSLが 150.000
// 新しいSLも 150.000(計算結果)
OrderModify(ticket, price, 150.000, tp, 0); // → error 1
なぜ「同じ値」になるのか¶
トレーリングストップのロジック¶
// 典型的なトレーリングストップ
double newSL = Bid - TrailingStop * Point;
if(newSL > OrderStopLoss())
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0);
}
問題が発生するケース¶
Bid = 150.500
TrailingStop = 50 pips
新しいSL = 150.500 - 0.500 = 150.000
しかし現在のSLも 150.000...
計算上は newSL > OrderStopLoss() が true になるべきだが、
浮動小数点誤差で微妙に異なる値になり、
比較は true でも OrderModify は「同じ値」と判定する
浮動小数点の罠(再び)
150.000000000001 と 149.999999999999 のような
極めて近い値が「異なる」と判定され、変更を試みます。
しかしサーバー側では「同じ」と判定されるため、error 1。
MQL4での対策¶
対策1: 0.5*Point シフト¶
// 変更前にわずかなマージンを追加
double newSL = NormalizeDouble(Bid - TrailingStop * Point, Digits);
double currentSL = NormalizeDouble(OrderStopLoss(), Digits);
// 0.5*Pointより大きい差がある場合のみ変更
if(newSL - currentSL > 0.5 * Point)
{
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0);
}
対策2: 事前に一致チェック¶
double newSL = NormalizeDouble(Bid - TrailingStop * Point, Digits);
double currentSL = NormalizeDouble(OrderStopLoss(), Digits);
// 完全に同じ場合はスキップ
if(MathAbs(newSL - currentSL) < Point)
{
return; // 変更不要
}
OrderModify(OrderTicket(), OrderOpenPrice(), newSL, OrderTakeProfit(), 0);
MQL5での対応¶
MQL5では PositionModify を使用しますが、同様の問題が発生する可能性があります。
MQL5検証コード¶
//+------------------------------------------------------------------+
//| OrderModify_Error.mq5 |
//| 「とあるMetaTraderの備忘秘録」様の記事を検証 |
//| https://fai-fx.hatenadiary.org/entry/20091122/1258893252 |
//+------------------------------------------------------------------+
#property copyright "FXおもしろラボ"
#property link "https://fx-omoshiro-lab.com/"
#property version "1.00"
#include <Trade/Trade.mqh>
void OnStart()
{
Print("=== OrderModify error 1 の解説 ===");
Print("");
Print("このスクリプトは概念説明用です。");
Print("実際のポジションは開きません。");
Print("");
// 問題のシミュレーション
Print("--- 問題のシミュレーション ---");
double currentSL = 150.000000000000;
double newSL_raw = 150.000000000001; // 計算結果(微妙に異なる)
PrintFormat("現在のSL: %.15f", currentSL);
PrintFormat("新しいSL: %.15f", newSL_raw);
PrintFormat("差分: %.15f", newSL_raw - currentSL);
PrintFormat("newSL > currentSL: %s", (newSL_raw > currentSL) ? "true" : "false");
Print("");
Print("→ 比較はtrueだが、サーバーでは「同じ値」と判定されerror 1");
Print("");
// 対策1の解説
Print("--- 対策1: 0.5*_Point シフト ---");
double point = 0.001; // 3桁通貨の場合
double margin = 0.5 * point;
PrintFormat("マージン: %.4f", margin);
PrintFormat("差分(%.15f) > マージン(%.4f): %s",
newSL_raw - currentSL, margin,
(newSL_raw - currentSL > margin) ? "true → 変更実行" : "false → スキップ");
Print("");
// 対策2の解説
Print("--- 対策2: 事前一致チェック ---");
double newSL_normalized = NormalizeDouble(newSL_raw, 3);
double currentSL_normalized = NormalizeDouble(currentSL, 3);
PrintFormat("正規化後 新SL: %.3f", newSL_normalized);
PrintFormat("正規化後 現SL: %.3f", currentSL_normalized);
PrintFormat("一致: %s", (newSL_normalized == currentSL_normalized) ? "true → スキップ" : "false → 変更実行");
Print("");
// 安全なトレーリングストップ関数
Print("--- 安全なトレーリングストップの実装例 ---");
Print("void SafeTrailingStop(ulong ticket, double trailPips)");
Print("{");
Print(" double newSL = NormalizeDouble(bid - trailPips * _Point, _Digits);");
Print(" double currentSL = PositionGetDouble(POSITION_SL);");
Print(" ");
Print(" // 0.5*_Point以上の差がある場合のみ変更");
Print(" if(newSL - currentSL > 0.5 * _Point)");
Print(" {");
Print(" trade.PositionModify(ticket, newSL, tp);");
Print(" }");
Print("}");
Print("");
Print("=== 解説完了 ===");
}
安全なトレーリングストップ関数¶
#include <Trade/Trade.mqh>
CTrade trade;
//+------------------------------------------------------------------+
//| 安全なトレーリングストップ |
//+------------------------------------------------------------------+
void SafeTrailingStop(ulong ticket, double trailPoints)
{
if(!PositionSelectByTicket(ticket))
return;
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
long posType = PositionGetInteger(POSITION_TYPE);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double newSL = 0;
if(posType == POSITION_TYPE_BUY)
{
newSL = NormalizeDouble(bid - trailPoints * _Point, _Digits);
// 利益が出ている & 新SLが現SLより高い & 差が十分ある
if(bid > openPrice + trailPoints * _Point &&
newSL > currentSL &&
newSL - currentSL > 0.5 * _Point) // ← ここが重要
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
else if(posType == POSITION_TYPE_SELL)
{
newSL = NormalizeDouble(ask + trailPoints * _Point, _Digits);
// 利益が出ている & 新SLが現SLより低い(または0) & 差が十分ある
if(ask < openPrice - trailPoints * _Point &&
(currentSL == 0 || newSL < currentSL) &&
(currentSL == 0 || currentSL - newSL > 0.5 * _Point))
{
trade.PositionModify(ticket, newSL, currentTP);
}
}
}
まとめ¶
| ポイント | 内容 |
|---|---|
| エラー | OrderModify error 1 |
| 意味 | 「変更前後の値が同じ」 |
| 原因 | 浮動小数点誤差 |
| 対策 | 0.5*_Point マージン or 事前チェック |
オリジナル記事への謝辞
この記事は「とあるMetaTraderの備忘秘録」様の貴重な知見をもとに、
MQL5での検証と解説を加えたものです。
オリジナル記事に心より感謝いたします。
ソースコードのダウンロード¶
この記事で紹介したコードをダウンロードできます。
ファイルの種類:スクリプト
保存先: MQL5/Scripts/ フォルダ
使い方: MT5のナビゲーターから「スクリプト」を展開し、チャートにドラッグ&ドロップで実行
10_OrderModify_Error.mq5 をダウンロード
関連記事・用語¶
- #01 double型の比較ミスを防ぐ — 同じ根本原因
- #08 MathModの罠 — 同じ根本原因
- 用語集 - NormalizeDouble