2018年11月11日日曜日

[MQL4] ストラテジー毎にスレッドは別

[OnTick() と OnTimer() が同一スレッドで動いている](https://strategyofc.blogspot.com/2018/11/mql4-ontick-ontimer.html) ことは
確認したのですが、ストラテジーが異なる場合はどうなのか調べてみました。

結論から言うと、各ストラテジーは別スレッドで動いているようです当たり前というか、そうじゃないと使い物にならない気がするけど。

1つのストラテジーが `OnTick()` 内で重たい処理をしていても、他のストラテジーの `OnTick()` は呼ばれます。

とはいえ、プロセスは1つなので完全に無関係というわけにはいかないでしょう。
同時実行のストラテジーはどれくらいまで増やせるのだろうか?

2018年11月10日土曜日

[MQL4] OnTick() と OnTimer() は同一スレッドで動いている

@MetaTrader 4.00 Build 1090

Expert Adviser の `OnTick()` で何か重たい処理をしているとき本来は、なるべく重い処理はしない方が良いけれど、`OnTimer()` はどのように処理されるのか、調べてみました。

結論から言うと、以下のような動作をするようです。
(同一スレッド上で動いているか、少なくとも排他されている。)

- `OnTick()` の処理中に `OnTimer()` のタイミングが来た場合、`OnTick()` 終了直後に`OnTimer()`が実行される(`OnTimer()` はペンディングされる)
- ただし、2回以上ペンディングされたとしても、`OnTick()` 終了直後に実行される `OnTimer()` は1回のみ
- ペンディングされたとしても、`OnTimer()` が呼ばれるタイミングには影響しない(`EventSetTimer()` が呼ばれた時点から指定間隔で呼ばれ続けるただし、多少の誤差はある)

この動作、逆の場合も当てはまるようです。
つまり、`OnTimer()` で重たい処理をしていると、その間に呼ばれるはずだった `OnTick()` が無視されることになります最後の一回を除いて。

また、`OnTick()` で重たい処理をしていても、同じように `OnTick()` が無視されます。

`OnTick()` や `OnTimer()` ではなるべく重たい処理をしないようにすべきですね。
かつ `OnTimer()` の呼び出し頻度は少なめに。

2018年11月6日火曜日

[MT4] PeriodConverter はオフラインチャートで上位時間足を再生成するためのものではない

[[MT4] PeriodConverter を使うとヒストリーデータが消えることがある](https://strategyofc.blogspot.com/2018/11/mt4-periodconverter.html) を書いている時に
以下の投稿を見つけました。

[案外知られていない、MT4添付スクリプト「PeriodConverter」のバグ(正しくは制約)| nekonoteのFXで自動売買](http://nya0nekonote.blog.fc2.com/blog-entry-307.html)

オフラインチャートに「PeriodConverter」を適用するのは本来の使い方ではないらしい。

> 「PeriodConverter」の本来の目的は、オンラインのチャートに常駐させて、パラメータで指定した倍率の上位時間足をリアルタイムで生成していくものです。
> ですので、オフラインチャートの5分足~月足を生成して終わり、という使い方は、スクリプトの本来の目的から外れています。

知らなかった。
確かに「生成が終わってもスクリプトが終了せず常駐している」とは思っていたけど、そういう意味があったとは。

そのため、以下のような問題があるとのこと。

- 最新時間足が生成されない
- 週足・月足のオープン時間が異なる

最新時間足が生成されない問題は、リアルタイム用ということで理解できますが、週足・月足の問題はなぜでしょう?
今度調べてみたいと思いますと TODO に積んでおいて放置されるパターン…?

[MT4] PeriodConverter を使うとヒストリーデータが消えることがある

MetaTrader には `PeriodConverter` というスクリプトが標準で付属しています。
この `PeriodConverter` は M1 のデータ等から他の時間足のデータを再生成する際に使用します。

便利に使っていたのですが、ある日、再生成したヒストリーデータが直近数時間分になってしまう現象がおこりました。
M1 のデータは 1971年からMetaQuotes 提供のもの。1900年代のデータは飛び飛びであまり使いものにならない。あるのにも関わらずです。

先に原因を書いておくと、「チャートの最大バー数」が小さい場合、このような現象が起こります。

ツール → オプション → チャート
2018年11月5日月曜日

[MQL4] Library に strict を付けてはいけない

@ MetaTrader 4.00 Build 1090 (on Windows 10 Pro Ver. 1803)

MetaTrader でライブラリを作成する際、`#property strict` を書いてしまうと、実行時に以下のエラーが出てしまいますコンパイルは通る。

> libsumtest USDJPY,H1: unresolved import function call

`#property strict` を消すと正しく動作します。 なお、ライブラリの修正後はライブラリをコンパイルし直すのをお忘れなく。 > 参考 > > [Problem with #import library (Win XP, build 646) - Trading Strategies That Work - MQL4 and MetaTrader 4 - MQL4 programming forum](https://www.mql5.com/en/forum/152047)
2018年10月18日木曜日

[MQL4] GetLastError() で取得したエラーコードをエラーメッセージに変換する方法

MQL4 の関数の中には、失敗した場合、[GetLastError()](https://docs.mql4.com/check/getlasterror) でエラーコードを取得できるものがあります。

しかし、このエラーコードは [Trade Server Return Codes](https://docs.mql4.com/constants/errorswarnings/enum_trade_return_codes) に載ってないものが多く、そのままでは役に立ちません。

そのため、`ErrorDescription()` という標準関数が用意されています公式のサンプルの中でもよく使われています
2018/10/19 追記
「載ってないものが多く」と書いたのですが、[Runtime Errors](https://docs.mql4.com/constants/errorswarnings/errorcodes) の方には 4000番台のエラーも載っていましたどこかで見た気はしていた。 [Trade Server Return Codes](https://docs.mql4.com/constants/errorswarnings/enum_trade_return_codes) は [Runtime Errors](https://docs.mql4.com/constants/errorswarnings/errorcodes) から若い番号のものを抜粋した表のようです。
[OrderModify()](https://docs.mql4.com/trading/ordermodify) 等は 4000番台のエラーも返すのに、リンクされているドキュメントが不十分というのは不親切だなぁ。
### `ErrorDescription()` の使用方法 まず `stdlib.mqh` を include します。 ```mq4 #include <stdlib.mqh> ``` あとは以下のように `GetLastError()` で取得したエラーコードを与えると、対応したエラーメッセージが返されます。 ```mq4 if (!OrderModify(ticket, price, stoploss, takeprofit, expiration)) { int error = GetLastError(); printf("OrderModify error \"%s\"(%d)", ErrorDescription(error), error); } // 結果 // OrderModify error "invalid ticket"(4108) ``` なお、`GetLastError()` は2度呼ぶと値が変わってしまうので、上記のように変数で一度受けて使用しましょう。 > 参考 > > [[MQL4] GetLastError() は2度呼んではいけない](https://strategyofc.blogspot.com/2018/10/mql4-getlasterror-2.html)

[MQL4] GetLastError() は2度呼んではいけない

MQL4 では直前の関数が失敗した場合、[GetLastError()](https://docs.mql4.com/check/getlasterror) を使ってエラーコードを取得することが出来ます。

しかし、この関数を連続して呼び出すと値が変わってしまうので要注意です。


### 2度目は値が0
具体的には以下のように、2度目の呼び出しでは値が 0 になってしまいますなお、4108 は "Invalid Ticket"。

```mq4
if (!OrderModify(ticket, price, stoploss, takeprofit, expiration)) {
    printf("GetLastError1 %d", GetLastError());
    printf("GetLastError2 %d", GetLastError());
}

// 結果
// GetLastError1 4108
// GetLastError2 0
```

こんなあからさまに2度呼ぶことは無いと思うのですが、後述の `ErrorDescription()` を使う場合など、うっかり書いてしまうことはありそうです。


### GetLastError() は必ず変数で受けてから使う
対策としては、エラー発生直後に必ず変数で受けるよう習慣づけるのが一番かと思います。

```mq4
if (!OrderModify(ticket, price, stoploss, takeprofit, expiration)) {
    int error = GetLastError();
    printf("GetLastError1 %d", error);
    printf("GetLastError2 %d", error);
}    

// 結果
// GetLastError1 4108
// GetLastError2 4108
```


### エラーコードをエラーメッセージに変換する方法
このエラーコード、[Trade Server Return Codes](https://docs.mql4.com/constants/errorswarnings/enum_trade_return_codes) に一覧があると思いきや、この表には一部のコードしか載っていません4108 も載っていない。

これではエラーの原因がわからないので、これを文字列に変換する標準ライブラリが用意されています。

> 参考
>
> [[MQL4] GetLastError() で取得したエラーコードをエラーメッセージに変換する方法](https://strategyofc.blogspot.com/2018/10/mql4-getlasterror.html)

2018/10/19 追記
「一部のコードしか載っていません」と書いたのですが、[Runtime Errors](https://docs.mql4.com/constants/errorswarnings/errorcodes) の方には 4000番台のエラーも載っていましたどこかで見た気はしていた。 [Trade Server Return Codes](https://docs.mql4.com/constants/errorswarnings/enum_trade_return_codes) は [Runtime Errors](https://docs.mql4.com/constants/errorswarnings/errorcodes) から若い番号のものを抜粋した表のようです。
[OrderModify()](https://docs.mql4.com/trading/ordermodify) 等は 4000番台のエラーも返すのに、リンクされているドキュメントが不十分というのは不親切だなぁ。