勝率判定をするインジケーターを作成していて価格をNormalizeDouble するかしないかで結果か違っていて
調査をしていて本当にGet Crazyな状態になったので、その調査を纏めてみたいと思います。
今後、同じような経験をされる方がいて役に立てるかと思い記録しておきます。
そもそも価格をNormalizeDouble する必要があるかというと価格を保存するためにdoubleを使用します。
doubleは浮動小点型といわれ小数点以下にとんでもない数字が並びます。
Type |
バイトサイズ |
最小の正の値 |
最大値 |
C++ |
float |
4 |
1.175494351e-38 |
3.402823466e+38 |
float |
double |
8 |
2.2250738585072014e-308 |
1.7976931348623158e+308 |
double |
https://www.mql5.com/ja/docs/basis/types/double
MQLでは有効な数字としては小数点以下15桁が有効な数字らしいです。小数点以下15桁もあるので価格を
NormalizeDoubleであるところで四捨五入していないと 比較する際にマッチしない現象がよく起きます。なので価格を
取得する箇所にはNormalizeDouble を使うようにしていました。
ベストプラクティスにも記載されています。
トレード処理では、精度がトレーディングサーバーに要求される最低1桁を超える正規化されていない価格を使用することは不可能であることに注意が必要です。
未決注文に対するストップロス、テイクプロフィット、価格値は、値が定義済み変数 Digits に格納されている値精度で正規化します。
ところが価格をNormalizeDouble しても正常に表示されないような現象が確認できました。
こちらはAUDJPY1分足なのですが終値は、68.704になります。
MQLで価格を取得してみると、
double TestValue_NormalizeDouble = NormalizeDouble(TestValue, Digits);
Print("TestValue ",TestValue, " TestValue_NormalizeDouble ", TestValue_NormalizeDouble);
AUDJPY,M1: TestValue 68.70399999999999 TestValue_NormalizeDouble 68.70399999999999
-0.00000000000001違います。DoubleToStrを使用してストリングにして表示した場合は、
Print ("TestValueString ", DoubleToStr(TestValue, Digits), " TestValue_NormalizeDoubleString: ", DoubleToStr(TestValue_NormalizeDouble, Digits));
結果
AUDJPY,M1: TestValueString 68.704 TestValue_NormalizeDoubleString: 68.704
こちらは正しくなりました。
これらの現象はMQL5のフォーラムでかなり長い間、熱く議論されております。
英語ですが見たい方はこちら。
https://www.mql5.com/en/forum/
https://www.mql5.com/en/forum/146370/
いろいろ長い間に議論されておりはNormalizeDoubleは使用しないほうがいいのではないかとの意見もありました。
新しい記事では以下の関数を使用するのがいいとのことでした。
https://www.mql5.com/en/forum/333599
{
double ts=SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
return(NormalizeDouble(p/ts,0)*ts);
Print("NormalizePriceString ", DoubleToStr(NormalizePrice(TestValue), Digits));
AUDJPY,M1: TestValue 68.70399999999999 NormalizePrice 68.70400000000001
AUDJPY,M1: NormalizePriceString 68.704
こちらのほうも0.00000000000001違いますがこちらはPrint()の動作のようです。
正規化数は小数点以下の桁数を多く含むことがあるので、Print() 関数を使用しての操作ログへの出力時にはご注意ください。
Print(76.671)=76.67100000000001
stdlib.mqhに含まれているDoubleToStrMorePrecision関数での15桁までの値の確認してみます。そして値を比較してみます。
double TestValue_NormalizeDouble = NormalizeDouble(TestValue, Digits);
double TestValue_NormalizePrice = NormalizePrice(TestValue);
Print("DoubleToStrMorePrecision",DoubleToStrMorePrecision(TestValue,15));
Print("DoubleToStrMorePrecision",DoubleToStrMorePrecision(TestValue_NormalizeDouble,15)," ",DoubleToStrMorePrecision(TestValue_NormalizePrice,15));
Print (TestValue_NormalizeDouble," ",TestValue_NormalizePrice, " The value is same");
else
Print (TestValue_NormalizeDouble," ",TestValue_NormalizePrice, " The value is NOT same");
AUDJPY,M1: DoubleToStrMorePrecision 68.703999999999994 68.704000000000008
AUDJPY,M1: 68.70399999999999 68.70400000000001 The value is NOT same
※元の価格とNormalizeDoubleした
double TestValue1 = 68.704;
if (TestValue_NormalizeDouble == TestValue1)
Print (TestValue_NormalizeDouble," ",TestValue1, " NormalizeDouble1 The value is same");
else
Print (TestValue_NormalizeDouble," ",TestValue1, " NormalizeDouble1 The value is NOT same");
Print (TestValue_NormalizePrice," ",TestValue1, " NormalizePrice1 The value is same");
else
Print (TestValue_NormalizePrice," ",TestValue1, " NormalizePrice1 The value is NOT same");
Print (TestValue_NormalizeDouble," ",TestValue1, " NormalizeDouble2 The value is same");
else
Print (TestValue_NormalizeDouble," ",TestValue1, " NormalizeDouble2 The value is NOT same");
Print (TestValue_NormalizePrice," ",TestValue1, " NormalizePrice2 The value is same");
else
Print (TestValue_NormalizePrice," ",TestValue1, " NormalizePrice2 The value is NOT same");
AUDJPY,M1: 68.70399999999999 68.70399999999999 NormalizeDouble1 The value is same
AUDJPY,M1: 68.70400000000001 68.70399999999999 NormalizePrice1 The value is NOT same
AUDJPY,M1: 68.70399999999999 68.70399999999999 NormalizeDouble2 The value is same
AUDJPY,M1: 68.70400000000001 68.70399999999999 NormalizePrice2 The value is same
#NormalizeDouble1の価格は手入力した価格と同じになりました。
反対にNormalizePriceの価格は同じ価格との判断にはなりませんでした。手入力した価格もNormalizePriceしなければいけないようで表示もそのままでした。
今のところの結論
私が試した限りでは
もし