MetaTrader 4 は勝手に再起動してアップデートすることがある

本日、MetaTrader 4 を起動したら突然終了してしまいました。その後、数秒待つと自動で再起動し、以後問題なく動作しています。
実被害は無かったのですが、何が起こったのかログを確認してみたところ、アップデートが走ったようです。
本日、MetaTrader 4 を起動したら突然終了してしまいました。その後、数秒待つと自動で再起動し、以後問題なく動作しています。
実被害は無かったのですが、何が起こったのかログを確認してみたところ、アップデートが走ったようです。
Android 版 MetaTrader 4 を使用していると、時々オーダーの左端に赤や緑の帯が表示されます。
気になって調べてみたのですが1、MetaTrader 5 のヘルプしか見つけられませんでした。 ただ、動作を確認してみたところ、MetaTrader 4 も 5 と同じように動作するようです。
OnTick() と OnTimer() が同一スレッドで動いている ことは 確認したのですが、ストラテジーが異なる場合はどうなのか調べてみました。
結論から言うと、各ストラテジーは別スレッドで動いているようです1。
1つのストラテジーが OnTick()
内で重たい処理をしていても、他のストラテジーの OnTick()
は呼ばれます。
とはいえ、プロセスは1つなので完全に無関係というわけにはいかないでしょう。 同時実行のストラテジーはどれくらいまで増やせるのだろうか?
@MetaTrader 4.00 Build 1090
Expert Adviser の OnTick()
で何か重たい処理をしているとき1、OnTimer()
はどのように処理されるのか、調べてみました。
結論から言うと、以下のような動作をするようです。 (同一スレッド上で動いているか、少なくとも排他されている。)
OnTick()
の処理中に OnTimer()
のタイミングが来た場合、OnTick()
終了直後にOnTimer()
が実行される(OnTimer()
はペンディングされる)OnTick()
終了直後に実行される OnTimer()
は1回のみOnTimer()
が呼ばれるタイミングには影響しない(EventSetTimer()
が呼ばれた時点から指定間隔で呼ばれ続ける2)この動作、逆の場合も当てはまるようです。
つまり、OnTimer()
で重たい処理をしていると、その間に呼ばれるはずだった OnTick()
が無視されることになります3。
また、OnTick()
で重たい処理をしていても、同じように OnTick()
が無視されます。
OnTick()
や OnTimer()
ではなるべく重たい処理をしないようにすべきですね。
かつ OnTimer()
の呼び出し頻度は少なめに。
[MT4] PeriodConverter を使うとヒストリーデータが消えることがある を書いている時に 以下の投稿を見つけました。
案外知られていない、MT4添付スクリプト「PeriodConverter」のバグ(正しくは制約)| nekonoteのFXで自動売買
オフラインチャートに「PeriodConverter」を適用するのは本来の使い方ではないらしい。
「PeriodConverter」の本来の目的は、オンラインのチャートに常駐させて、パラメータで指定した倍率の上位時間足をリアルタイムで生成していくものです。 ですので、オフラインチャートの5分足~月足を生成して終わり、という使い方は、スクリプトの本来の目的から外れています。
知らなかった。 確かに「生成が終わってもスクリプトが終了せず常駐している」とは思っていたけど、そういう意味があったとは。
そのため、以下のような問題があるとのこと。
最新時間足が生成されない問題は、リアルタイム用ということで理解できますが、週足・月足の問題はなぜでしょう? 今度調べてみたいと思います1。
MetaTrader には PeriodConverter
というスクリプトが標準で付属しています。
この PeriodConverter
は M1 のデータ等から他の時間足のデータを再生成する際に使用します。
便利に使っていたのですが、ある日、再生成したヒストリーデータが直近数時間分になってしまう現象がおこりました。 M1 のデータは 1971年から1あるのにも関わらずです。
先に原因を書いておくと、「チャートの最大バー数」が小さい場合、このような現象が起こります。
ツール → オプション → チャート
@ MetaTrader 4.00 Build 1090 (on Windows 10 Pro Ver. 1803)
MetaTrader でライブラリを作成する際、#property strict
を書いてしまうと、実行時に以下のエラーが出てしまいます1。
libsumtest USDJPY,H1: unresolved import function call
#property strict
を消すと正しく動作します。
なお、ライブラリの修正後はライブラリをコンパイルし直すのをお忘れなく。
参考
MQL4 の関数の中には、失敗した場合、GetLastError() でエラーコードを取得できるものがあります。
しかし、このエラーコードは Trade Server Return Codes に載ってないものが多く、そのままでは役に立ちません。
そのため、ErrorDescription()
という標準関数が用意されています1。
「載ってないものが多く」と書いたのですが、Runtime Errors の方には 4000番台のエラーも載っていました2。
Trade Server Return Codes は Runtime Errors から若い番号のものを抜粋した表のようです。
OrderModify() 等は 4000番台のエラーも返すのに、リンクされているドキュメントが不十分というのは不親切だなぁ。
ErrorDescription()
の使用方法まず stdlib.mqh
を include します。
1 | #include <stdlib.mqh> |
あとは以下のように GetLastError()
で取得したエラーコードを与えると、対応したエラーメッセージが返されます。
1 2 3 4 5 6 7 | 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度目の呼び出しでは値が 0 になってしまいます1。
1 2 3 4 5 6 7 8 | if (! OrderModify (ticket, price, stoploss, takeprofit, expiration)) { printf ( "GetLastError1 %d" , GetLastError ()); printf ( "GetLastError2 %d" , GetLastError ()); } // 結果 // GetLastError1 4108 // GetLastError2 0 |
こんなあからさまに2度呼ぶことは無いと思うのですが、後述の ErrorDescription()
を使う場合など、うっかり書いてしまうことはありそうです。
対策としては、エラー発生直後に必ず変数で受けるよう習慣づけるのが一番かと思います。
1 2 3 4 5 6 7 8 9 | 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 に一覧があると思いきや、この表には一部のコードしか載っていません2。
これではエラーの原因がわからないので、これを文字列に変換する標準ライブラリが用意されています。
参考
「一部のコードしか載っていません」と書いたのですが、Runtime Errors の方には 4000番台のエラーも載っていました3。
Trade Server Return Codes は Runtime Errors から若い番号のものを抜粋した表のようです。
OrderModify() 等は 4000番台のエラーも返すのに、リンクされているドキュメントが不十分というのは不親切だなぁ。
@MetaTrader 4.00 Build 1090
MetaTrader 4 の設定には「チャートの最大バー数」という項目があります。
この設定値が何を意味するのか調べてみました。 と言っても、正式なドキュメントを見つけることができなかったので動作からの推察です。
なお、この値は** MT4 を再起動しないと反映されません**1。
include/Arrays
以下にはオブジェクトを格納できる CArrayObj と CList が定義されています。
どちらも大体同じような機能を提供しているのですが、何が違うのか見てみました。
結論から言うと、CArrayObj
の方は内部で配列を持っているのに対し、CList
は CObject の持つリンクリスト機能を使っています。
CArrayObj
は一般的な配列同様に使用できます。
ランダムアクセスする場合などはこちらを使うと良いでしょう。
参考
CList
は内部にポジションを持っており、GetNextNode()
のようにイテレーティブな処理をする場合、高速に処理できます。
全要素に対して一気に処理をかけるような処理に向いています。
以下に簡単な例を挙げます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | #property strict #include <Object.mqh> #include <Arrays/List.mqh> class Item : public CObject { private : const int m_value; public : Item( int value) : m_value(value) { } virtual ~Item() { } int GetValue() const { return m_value; } virtual int Compare( const CObject *node, const int mode=0) const { int diff = this .GetValue() - dynamic_cast < const item*= "" >(node).GetValue(); return (diff == 0) ? 0 : (diff > 0) ? 1 : -1; } }; CList m_item_list; void OnStart() { for ( int i = 0; i < 10; i++) { int value = (i % 2 == 0) ? i : i + 10; m_item_list.Add( new Item(value)); } // 内部ポインタを最初にもどす m_item_list.MoveToIndex(0); Item* item; // 「最後まで一気に処理する」みたいなのに向いている while ((item = m_item_list.GetNextNode()) != NULL ) { printf ( "%d" , item.GetValue()); } } |
なお、CList
も CArrayObj
と同様、Sort 済みでないと検索が出来ません。
また、Compare()
の実装によっては検索結果が想定外になることもあるので気をつけてください。
参考
CArrayObj の Insert()
で Total
以上の値を指定したらどのようになるかやってみました。
結果は、「どんなに大きな値を指定しても最後に挿入される」でした。 エラーが返ってこない仕様なので1、そうだろうとは思ってたけど、念の為。
1 2 3 4 5 6 7 8 9 10 11 | // index 0〜9 の要素を追加 for ( int i = 0; i < 10; i++) { int value = (i % 2 == 0) ? i : i + 10; m_item_array.Add( new Item(value)); } Item* item = new Item(99); // index 99?に挿入 // 実際は index 10 (最後)に挿入される m_item_array.Insert(item, 99); |
Item
等の実装は以下のリンク先に掲載したものと同じです。
参考
MQL4 で可変長配列を使用する方法 に書いたのですが、Sort()
済みの CArrayObj から Search()
でインデックスを取得しても正しいインデックスを返さない場合があるので注意が必要です。
具体的な例を以下に挙げます。
MQL4 で可変長配列を使用する方法 で既に書いたのですが、以下のように CArrayObj::Search()
を呼んでも正しくインデックスが返ってこない時があります。
Item
等の定義は MQL4 で可変長配列を使用する方法 を参照してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | for ( int i = 0; i < 10; i++) { int value = (i % 2 == 0) ? i : i + 10; m_item_array.Add( new Item(value)); } // 途中に挿入 Item* item_100 = new Item(100); m_item_array.Insert(item_100, 5); // 今挿入したばかりのオブジェクトを検索する int index = m_item_array.Search(item_100); printf ( "index: %d" , index); // 結果 // index: -1 |
CArray 系クラスの中で使用されているメモリの動的確保について書きました。
MQL4 で配列用メモリを動的確保する方法 | Strategy of C
場合によっては、こちらを直接使った方が良いかもしれません。
@MetaTrader 4 (Version 4.00 Build 1090)
MQL4 では C++ の std::vector のような動的配列が標準で使用できます1。
Include/Arrays/
以下に定義されており、#include <Arrays/ArrayObj.mqh>
のように include するだけで使用可能です。
今回はその中の CArrayObj について簡単な使い方を書こうと思いますが、 CArrayInt、CArrayChar 等他の class も基本の思想は同じです。
公式のリファレンスを探したのですが、 MQL5 用のものしか見つけられませんでした。 ただ、確認した限り、MQL4 でも同様に使用できるようです2。
参考
なお、完全に動作するソースコードは最後に載せておきます。
MQL4 のヘッダファイル (.mqh) は Include
ディレクトリに置くのが普通ですが、実は Experts
, Script
, Libraries
等に置くこともできます。
Android の設定画面から変更する方法を見つけました。
MT4 Android 版で Notification の音を変更する方法
MetaTrader の EA から Android, iOS に通知を送る で「通知音を変更しておくと便利」と書いたのですが、私の Android 端末では最新のアップデートで通知音が変更出来なくなってしまいました。
正確には以下のように設定変更は出来るのですが、それが反映されずデフォルトの通知音が鳴ってしまいます。
EA を使った自動売買派でも、MetaTrader のモバイルアプリは便利に使えます。
特に通知機能。 注文が通った時など、EA の中から任意のタイミングでモバイル端末に通知を送る事が出来ます。 EA からメールを送信することも出来ますが、通知の方が速く届くことが多いです1。
設定の仕方は以下の通り。
FXで重要なのは資産管理。 1トレードで許容されるリスクは、総資産の N% 以内に収めるべし。という教えはよくききます。
これを実現するには、最大ストップロスが総資産の N% になるよう、1トレードのロット数を求めれば良いわけです。 Metatrader ではこれも自動化することが出来ます。
ここで紹介している SYMBOL_TRADE_TICK_VALUE
は Strategy Tester の中では使用できないのでご注意ください。
[MQL4] SYMBOL_TRADE_TICK_VALUE は過去の値がとれない | Strategy of C
「ストップロスに引っかかったらどれくらいの損失になるのか」等、値幅を基準通貨の金額に換算したいケースがあります。
そういった場合、SystemInfoDouble() の SYMBOL_TRADE_TICK_VALUE
を使うと、値幅を基準通貨の金額に換算することが出来ます。
なお、SystemInfoDouble() の代わりに MarketInfo() を使っても同じ結果が出るとは思いますが、 環境によっては、どちらか一方しか動かない可能性もあるので、事前に確認してください。
参考
ある値幅を基準通貨の金額に換算するには、以下のようにします。 この時の金額は 1ロット分です。
1 2 3 4 5 6 | // (high - low) = 値幅が与えられているとして (high > low) // 値幅が 何 ticks 相当か計算する double ticks = (high - low) / SymbolInfoDouble ( Symbol (), SYMBOL_TRADE_TICK_SIZE); // SYMBOL_TRADE_TICK_VALUE は 1 tick 当たりの基準通貨の金額になるので、ticks とかけると金額が求まる double price_per_lot = ticks * SymbolInfoDouble ( Symbol (), SYMBOL_TRADE_TICK_VALUE); |
上記の値は 1ロット当たりの金額なので、実際に取引するロット数をかければ基準通貨での金額になります。
例えば、以下は最小ロット1で取引した場合の金額を求める方法です。
1 2 | // 最小ロットでの金額を求める。 double price_per_min_lot = price_per_lot * MarketInfo ( Symbol (), MODE_MINLOT); |
なお、ロットに関する情報は SystemInfoDouble() で取れないようなので、MarketInfo() を使用しています。
この方法、便利なのですが、基準通貨を含まない通貨ペアの場合、正確な値は求められないので注意してください。
例えば、基準通貨が日本円で EUR/GBP の取引をする場合、値幅を円に変換するには GBP/JPY の値を(内部で)使用します。しかし、GBP/JPY も変化していますし、将来どのように動くかはわかりません。 つまり、あくまで現在の GBP/JPY を使った換算値しか求められないということになります。
余談ですが、決済済みポジションの損益は、OrderProfit() で求められます。
こんな投稿をみつけた。
大分昔のもので、かつ MQL5 の話だけど…
MarketInfo() を使うのではなく SymbolInfoDouble() 等を使ったほうが良いらしい。
MQL5 から移ってきた機能は動作が不安定なものもあるので、一概に良いとは言えないですが、将来的な移行も見据えるのなら動作確認をしっかりした上で従っておくのが良いかもしれません。
MarketInfo() で指定できるモードには MODE_POINT
と MODE_TICKSIZE
という項目があります。
何が違うのか気になりつつ、今まで放置してきたのですが、今回ちょっと時間が出来たので調べてみました。
結論としては…やはりよくわかりません。
私の環境では MODE_POINT
と MODE_TICKSIZE
は常に同じ値を返すので、検証しようにも出来ないのです。
ただ、ドキュメント等を読んで推測した結果、以下のような状況があり得るのではないかと思われます。
MODE_POINT | 0.001 |
MODE_TICKSIZE | 0.025 |
つまり、MODE_POINT
は最小の桁を表すので必ず 10 の N乗になる1。
それに対し、MODE_TICKSIZE
は最小変動幅を表すので、様々な値を取りうる、ということです2。
しつこいですが、あくまで推測です。 誰か詳しい方がいたら教えてください。
なお、MODE_TICKSIZE
が最後の変動幅を返すという記述を見かけたのですが、動作を確認してみたところ、少なくとも私の環境ではそのような動きはしませんでした。
MarketInfo は使わない方が良いらしい ので、SymbolInfoDouble の
SYMBOL_POINT
と SYMBOL_TRADE_TICK_SIZE
でもやってみましたが、結果変わらず。
@MetaTrader 4 (Version 4.00 Build 1090)
MT4 のカスタムインジケーターでは計算用のバッファーを用意する機能があり、計算の途中結果等を保存しておくことが出来ます。 当然、それは非表示になる…はずなのですが、普通に作ると何故か表示されてしまうのです。
これを想定通りに動作させるには少し工夫が必要なので書いておきます。
Period() は現在のチャートの時間軸を分で返すので、この値に60をかければ、秒に変換できます。
また、PeriodSeconds() は入力タイムフレームの時間軸を秒で返します。
私はPeriodSeconds()
の存在を知らずに、ずっと Period()
を60倍していました。
どちらを使っても大した差はありませんが。
MetaTrader のフォーラムに .hst
ファイルフォーマットについて書かれていたので、Python で読み込むクラスを作ってみました。
参考
MQL のカスタムインジケーターは便利ですが、 作り方を間違えると不思議な動作をするので注意が必要です。
input
の終わりにコメントを書くとそちらが表示されます。
1 2 3 4 5 | #property strict //--- input parameters input int MiddleMa; // 中期トレンドを見るためのEMA期間 [本数] input int ShortMa; // 仕掛けのためのEMA期間 [本数] input double StopPoint; // 許容される最大ストップ [Point] |
参考
Input Variables - Variables - Language Basics - MQL4 Reference
特に単位などを明示するのに便利。
使用に際して以下のような制約があるので気をつけましょう。
ところで、例として日本語を使っておいて何ですが、日本語は使わない方がよいと思います。
参考
Metatrader の point(s) と pip(s) の使い分け方について私見ですが書いてみようと思います。
まずは簡単に違いから。
詳しい違いについては以下を参考にしてください
MT4のpoint(ポイント)とpips(ピップス)の違い | MT4ラボ|メタトレーダーの使い方/EA・インジケーター検証
OrdersHistoryTotal() のドキュメントに以下のような記述があります。
Consecutive selection of orders using the SELECT_BY_POS parameter returns information in the sequence in which it was received from the trading server. Sorting of the resulting list of orders cannot be guaranteed.
最後にさらりと、「ソート順は保証されていません」と書いてあるではないですか。
つまり、直近の履歴だけを見たい場合でも、最低1回はループを回さないといけないわけですね。
そりゃ、「口座履歴」タブで取得期間を絞っておく必要があるわけだ。
参考
OrdersHistoryTotal() のドキュメントに以下のような記述があります。
The number of closed orders in the account history loaded into the terminal. The history list size depends on the current settings of the "Account history" tab of the terminal.
つまり、"Account history"(口座履歴)タブの設定によって取得できるリストの期間が異なるとのこと。
オーダー約定時刻からの時間は OrderOpenTime() と TimeCurrent() の差分で求められます。 なお、OrderOpenTime() を実行する前には OrderSelect() で対象の Order を指定しておく必要があります。
1 2 3 | // OrderSelect() は実行済みとする long diff = TimeCurrent () - OrderOpenTime (); printf ( "%lld" , diff); // 約定時刻からの秒 |
この値は秒で求められます。
参考
[MQL4] datetime を long にキャストすると 1970/1/1 からの秒になる | Strategy of C
この時、土日に入っている場合は TimeCurrent() が正確ではない可能性があるので注意が必要です。
参考
TimeCurrent() のドキュメントに以下のような記述があります。
Returns the last known server time,
last known
ということは、実際のサーバータイムとは異なっている可能性もあるということです。
string と datetime の加算演算は strict モードかどうかで動作が変わる に書いたように datetime の string への変換は明示的に行ったほうが良いと思います。
基本的には、MQL4 の固定フォーマット "yyyy.mm.dd hh:mi" を使うのが簡単ですが、独自のフォーマットで出力することも可能です1。
MQL4 のコンパイラは strict モードか否かで微妙に動作を変えます。
strict モードは、変数のスコープ等が厳格になっており、特別な理由が無い場合は使用すべきかと思います。 ただ、string と datetime の加算は混乱を招く仕様となっているので、注意が必要です。
参考
エラーの理由は単純で、64個の mail queue がいっぱいになったということです。
参考
Queue というのは窓口の列みたいなものです。 (マ○ドナルドのレジが1つしかない状況を想像してください)
MT4 のメール用 Queue は 64通しか保持することができないので、それ以上のメール送信リクエストがくると、このエラーが起こります。
MetaEditor がサジェストしてきた関数の中に PrintFormat() というのがあって、リファレンスを読んでみたら、printf と同等との記述が。
printf のリファレンスがないことから、PrintFormat() の方が本家で printf はエイリアスのようです。
知らなかった。 けど、今まで printf をずっと使ってきてしまったから、もう変えないけど。
参考
Datetime Type - Integer Types - Data Types - Language Basics - MQL4 Reference
公式ドキュメントによると、datetime
は 1970/1/1 からの秒を格納する 8 bytes の型だそうです。
これは long
と同じサイズで、相互変換が可能です。
どちらを使っていても動作上問題はないのですが、以下のような特徴があるので使い分けると良いでしょう。
datetime
long
MQL4 のコンパイラ自体は UTF-8 や UTF-16 のマルチバイト文字を問題なく扱えているように見えます。
printf
内の文字列も出力できています。
コメントも日本語で大丈夫のようです。
1 2 | // コメントも OK printf ( "テスト" ); |
動作確認環境は
アプリケーション開発から組み込みまで手を出しているフリーランスのエンジニア
投資経験は10年以上
現在、完全自動売買を目指して研究中
当ブログ内の情報は環境等により動作が異なる場合があります。 情報は正確になるよう努めていますが、もし間違いを見つけた方はご一報いただけると幸いです。 なお、当ブログの情報によって損害が生じたとしても管理人は一切の責任を負いません。 全て自己責任でお願い致します。