2018年10月18日木曜日

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

MQL4 の関数の中には、失敗した場合、GetLastError() でエラーコードを取得できるものがあります。

しかし、このエラーコードは Trade Server Return Codes に載ってないものが多く、そのままでは役に立ちません。

そのため、ErrorDescription() という標準関数が用意されています1

2018/10/19 追記

「載ってないものが多く」と書いたのですが、Runtime Errors の方には 4000番台のエラーも載っていました2Trade Server Return CodesRuntime 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度呼んではいけない

  1. 公式のサンプルの中でもよく使われています 
  2. どこかで見た気はしていた 
?

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

MQL4 では直前の関数が失敗した場合、GetLastError() を使ってエラーコードを取得することが出来ます。

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

2度目は値が0

具体的には以下のように、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() を使う場合など、うっかり書いてしまうことはありそうです。

GetLastError() は必ず変数で受けてから使う

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

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

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

参考

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

2018/10/19 追記

「一部のコードしか載っていません」と書いたのですが、Runtime Errors の方には 4000番台のエラーも載っていました3Trade Server Return CodesRuntime Errors から若い番号のものを抜粋した表のようです。
OrderModify() 等は 4000番台のエラーも返すのに、リンクされているドキュメントが不十分というのは不親切だなぁ。

  1. なお、4108 は "Invalid Ticket" 
  2. 4108 も載っていない 
  3. どこかで見た気はしていた 
?
2018年10月16日火曜日

[MT4] 「チャートの最大バー数」は何を意味するのか

@MetaTrader 4.00 Build 1090

MetaTrader 4 の設定には「チャートの最大バー数」という項目があります。

この設定値が何を意味するのか調べてみました。 と言っても、正式なドキュメントを見つけることができなかったので動作からの推察です。

なお、この値は** MT4 を再起動しないと反映されません**1

2018年10月15日月曜日

[MQL4] CArrayObj と CList の違い

include/Arrays 以下にはオブジェクトを格納できる CArrayObjCList が定義されています。 どちらも大体同じような機能を提供しているのですが、何が違うのか見てみました。

結論から言うと、CArrayObj の方は内部で配列を持っているのに対し、CListCObject の持つリンクリスト機能を使っています。

CArrayObj

CArrayObj は一般的な配列同様に使用できます。 ランダムアクセスする場合などはこちらを使うと良いでしょう。

参考

MQL4 で可変長配列を使用する方法 | Strategy of C

CList

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());
    }
}

なお、CListCArrayObj と同様、Sort 済みでないと検索が出来ません。 また、Compare() の実装によっては検索結果が想定外になることもあるので気をつけてください。

参考

[MQL4] CArrayObj はソート済みじゃないと Search() の結果が -1 になってしまう

2018年10月14日日曜日

[MQL4] CArrayObj::Insert() に最大値以上の index を指定すると Add と同じ動作になる

CArrayObjInsert()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 で可変長配列を使用する方法 | Strategy of C

  1. そして、ソースコードも読めるので 
?
2018年10月13日土曜日

[MQL4] CArrayObj::Search() が想定外のインデックスを返してくることがある

MQL4 で可変長配列を使用する方法 に書いたのですが、Sort() 済みの CArrayObj から Search() でインデックスを取得しても正しいインデックスを返さない場合があるので注意が必要です。

具体的な例を以下に挙げます。

2018年10月12日金曜日

[MQL4] CArrayObj はソート済みじゃないと Search() の結果が -1 になってしまう

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
2018年10月11日木曜日

MQL4 で可変長配列を使用する方法

2019/4/12 追記

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 について簡単な使い方を書こうと思いますが、 CArrayIntCArrayChar 等他の class も基本の思想は同じです。

公式のリファレンスを探したのですが、 MQL5 用のものしか見つけられませんでした。 ただ、確認した限り、MQL4 でも同様に使用できるようです2

参考

Standard Library / Data Collections - Reference on algorithmic/automated trading language for MetaTrader 5

なお、完全に動作するソースコードは最後に載せておきます。