MQL5で用意されている基本のデータ型は、整数型、論理型、リテラル型、文字列型、浮動小数点型、カラー型、日付時刻型、列挙型が用意されています。
EAを作成するにせよ、カスタム指標(インジケーター)を作成するにせよ、値を扱う際のデータ型はプログラムを作成する上で基本中の基本なのでしっかり覚えておきましょう。
整数/小数を扱うデータ型
整数型
整数を扱う場合には、整数型のデータ型を利用します。
扱う値の範囲や、負の数(マイナス)を扱うかによって、8つのデータ型の中から適切なデータ型を選択しましょう。
正の数/負の数を扱う
扱う値の範囲により、char、short、int、longの中から選択します。(ほとんどの場合、扱える数字の範囲からintを使うことが多い)
型 | 長さ | 値の範囲 |
---|---|---|
char | 1バイト | -128〜127 |
short | 2バイト | -32,768〜32,767 |
int | 4バイト | -2,147,483,648〜2,147,483,647 |
long | 8バイト | -9,223,372,036,854,775,808〜9,223,372,036,854,775,807 |
正の数のみを扱う
正の値のみ扱う場合には、符号なし(unsigned)を示すu付きのデータ型を使用できます。
型 | 長さ | 値の範囲 |
---|---|---|
uchar | 1バイト | 0〜255 |
ushort | 2バイト | 0〜65,535 |
uint | 4バイト | 0〜4,294,967,295 |
ulong | 8バイト | 0〜18,446,744,073,709,551,615 |
符号を表現する1ビット分多く値を格納できるようになるので、uなしのデータ型よりも扱える値の上限が広くなります。
整数値の表現方法
次の整数型の値を用意して、実行結果を確認してみます。
char var1=-128;
char var2=127;
uchar var3=255;
PrintFormat("var1: %d", var1);
PrintFormat("var2: %d", var2);
PrintFormat("var3: %d", var3);
データ型が対応している範囲の値を使用しているので、当然ですが格納している値が正しく表示されます。
この実行結果は次のようになります。
var1: -128
var2: 127
var3: 255
一方で扱える範囲外の値を使用するとどうなるでしょうか。
char var1=-129; // 扱える範囲の下限以下の値を使用
char var2=128; // 扱える範囲の上限以上の値を使用
uchar var3=-100; // 符号なし整数型に負の値を使用
uchar var4=257; // 符号なし整数型に上限以上の値を使用
PrintFormat("var1: %d", var1);
PrintFormat("var2: %d", var2);
PrintFormat("var3: %d", var3);
PrintFormat("var4: %d", var4);
実行してみます。
var1: 127
var2: -128
var3: 156
var4: 1
結果はどれも変数に代入した値とまったく異なるものになってしまいます。
想定外の値として扱われることは致命的なバグになってしまうので十分な注意が必要です。
浮動小数点型
小数部を持つ値を扱うには、浮動小数点型のデータ型を利用します。
型 | 長さ | 最小の正の値 | 最大値 |
---|---|---|---|
float | 4バイト | 1.175494351e-38 | 3.402823466e+38 |
double | 8バイト | 2.2250738585072014e-308 | 1.7976931348623158e+308 |
2つのデータ型が用意されていますが、基本的にはdouble型を利用します。
浮動小数点型では、確保されるメモリ量が多いほど、表現できる小数点以下の値が長くなります。
つまりは精度が高くなるため、無限小数(10÷3のように小数点以下が無限に連なる小数値)のような計算による誤差が発生しにくくなります。
実際の取引においては、スリッページや、EAを稼働するコンピュータの処理やネットワークの遅延など、狙ったところで約定できない、誤差が発生するといった箇所はデータ型によるもの以外にもあります。小数部が丸められることによる誤差が一度発生した程度であれば、その誤差は限りなく小さいものですし、損失に直結するとは考えにくいです。
ただし、計算を繰り返すと、限りなく小さかった誤差が許容できなくなるほど大きくなる場合もあります。
誤差が発生しうる箇所各々で、誤差をできる限り小さくしておくことが望ましいです。
浮動小数点値を使用していて値を出力する時には注意が必要です。
float var1=123.456;
double var2=123.456;
float var3=0.00000000012;
double var4=0.00000000012;
PrintFormat("var1: %e", var1);
PrintFormat("var2: %e", var2);
PrintFormat("var3: %20.12f", var3);
PrintFormat("var4: %20.12f", var4);
文字列のフォーマットに気をつけないと、指数表記で出力されたり、桁が丸められてしまいます。
var1: 1.234560e+02
var2: 1.234560e+02
var3: 0.000000000120
var4: 0.000000000120
真偽値を扱うデータ型
論理型
真(true)または偽(false)を表すデータ型。
ある条件を満たしているのか否か、その条件によってプログラムの振る舞いを制御するためによく利用します。
型 | 長さ | 値の範囲 |
---|---|---|
bool | 1バイト | false(0)もしくはtrue(0以外) |
整数値でも扱えますが、真か偽か、プログラムを人間が見てわかりやすく表現できます。
trueって0だっけ? 1だっけ?
プログラマあるある。
真偽値を表したい場合にはboolを使って可読性を高めるようにしましょう。
bool var1=true;
bool var2=false;
Print("var1:", var1, ", ", "var2:", var2);
if(var1) {
Print("var1 is true.");
}
if(!var2){
Print("var2 is false.");
}
print関数でそのまま表示してもtrue/falseで表示されるため使い勝手がよいです。
var1 is true.
var2 is false.
var1:true, var2:false
文字/文字列を扱うデータ型
リテラル型
MQL5内部ではUnicodeで文字データを扱っています。
Unicodeは2バイトで一文字を表現しているため、内部的には単一の文字をushort型の整数として保持されています。
型 | 長さ | 値の範囲 |
---|---|---|
ushort | 2バイト | 0〜65,535 |
リテラル型とMQL5リファレンスでは呼ばれていますが、文字型(≠文字列型)と考えても良いでしょう。
ここではリテラルを単一の文字として書いていますが、リテラルとは本来「直値」という意味です。
数字であれ、1文字であれ、文字列であれ、直接プログラム上に書いた値のことを指します。
一重引用符で括られた部分をリテラルとして扱い、例えばAという文字リテラルを指定する場合は ‘A’ と書きます。
ushort型のため、文字コードを数値リテラル(10進数、16進数など)で指定することもできます。
ushort var1='A';
string var2="";
StringSetCharacter(var2, 0, var1);
PrintFormat("var1 and var2: %d, %#x, %s", var1, var1, var2);
ushort var3=66;
string var4="";
StringSetCharacter(var4, 0, var3);
PrintFormat("var3 and var4: %d, %#x, %s", var3, var3, var4);
整数値なので、四則演算することももちろんできます。
var1 and var2: 65, 0x41, A
var3 and var4: 66, 0x42, B
文字列型
単一の文字でなく、単語や文章など複数の文字の集まりを文字列型と呼びます。
FXだと、USDJPYのような銘柄の表記も文字列。
型 | 長さ | 値の範囲 |
---|---|---|
string | - | - |
文字列型はほかのデータ型のように長さの制約がありません。
格納する文字列の文字数に応じて、内部では動的に必要なメモリを確保したりしているため、MQL5に限らず、一般的に文字列操作はコストが高くなります。
ただ、EAを作る場合は各種情報をログに出力したくなるので、かなり多用することになります。
文字列は二重引用符で括って表現します。
string var1="文字列は二重引用符で括ります。";
string var2="特殊文字も\n使えます。";
PrintFormat("%s", var1);
PrintFormat("%s", var2);
文字列中に特殊文字を含めることもできます。
文字列は二重引用符で括ります。
特殊文字も
使えます。
カラー型
カラー型はその名のとおり、色の情報を保持するための型です。
カスタムインジケーターを作成する場合には、色を工夫することで注目すべき部分を強調表示することできるので有用です。
一方でEAを作成する場合には覚えてなくても使わないので問題なし。
内部では4バイトのメモリが使用されますが、実際には下位3バイトで赤・緑・青の割合を保持していて色を表現します。
カラー型は次の3つの形式のいずれかで値を設定することができます。
- リテラル
- 整数
- 名称
- リテラル
-
赤・緑・青それぞれの色の割合を0〜255の整数値で指定します。
C’128,128,128′
整数値は16進数でも可。
赤・緑・青と覚えるよりもRGB(Red, Green, Blue)と覚えておきましょう
- 整数
-
色は3バイトで表現できる整数値でも指定できます。
0xFFFFFF
- 名称
-
一番わかりやすくて使い勝手がいいのが、色の名称を指定する方法。
ウェブカラーとして定義されている色を示す定数を使用します。
一般的に使用頻度の高い色はウェブカラーとして定義されており、定数名に色の名前が含まれているので、後からプログラムを見た時にわかりやすくなっています。
clrRed
次のような形で指定します。
color var1=C'128, 128, 128';
color var2=0xFFFFFF;
color var3=clrRed;
日付時刻型
日付時刻型は日時を示すデータのための型。
1970年から3000年までの間の日時を格納することができます。
型 | 長さ | 値の範囲 |
---|---|---|
datetime | 8バイト | 1970年1月1日〜3000年12月31日 |
西暦3000年になったら、そもそも為替市場は存在しているんでしょうか
日時の値は次のいずれかのリテラルで指定します。(D”で表します)
- D’年.月.日 時:分:秒‘
- D’日.月.年 時:分:秒‘
- D’年.月.日‘
時:分:秒だけ指定するのはコンパイル時に警告が出ますし、まったく別の値になってしまうのでやめておきましょう。
datetime var1=D'2022.10.01 12:30:00'; // 年.月.日 時:分:秒
datetime var2=D'01.10.2022 12:30:00'; // 日.月.年 時:分:秒
datetime var3=D'2022.10.02'; // 年.月.日のみ指定
datetime var4=D'14.50.30'; // 時:分:秒のみ指定すると警告が出ます
PrintFormat("var1: %s %s", TimeToString(var1, TIME_DATE), TimeToString(var1, TIME_SECONDS));
PrintFormat("var2: %s %s", TimeToString(var2, TIME_DATE), TimeToString(var2, TIME_SECONDS));
PrintFormat("var3: %s %s", TimeToString(var3, TIME_DATE), TimeToString(var3, TIME_SECONDS));
PrintFormat("var4: %s %s", TimeToString(var4, TIME_DATE), TimeToString(var4, TIME_SECONDS));
日付時刻型を出力する場合は、TimeToString関数で整形して出力します。
var1: 2022.10.01 12:30:00
var2: 2022.10.01 12:30:00
var3: 2022.10.02 00:00:00
var4: 2034.02.14 00:00:00
日付時刻型として時刻だけ指定した場合、次のような警告が出力されます。
invalid date
警告なのでそのまま実行はできますが、不正な値となるため必ず修正しましょう。
列挙型
列挙型は名前付きの定数のリストです。
リストを構成する各定数をメンバと呼びます。
メンバは値を指定することもできますが、定数名だけ宣言して値は省略することができ、値は0から始まります。
値が指定されているされていないに関わらず、1ずつカウントアップした値がセットされます。
enum test01 // test01という名前の列挙型を宣言(とりうる値は4つ)
{
Spring,
Summer,
Autumn=2,
Winter
};
test01 var1 = Summer; // test01列挙型の変数を宣言し、値Summerをセット
switch (var1) {
case Spring:
Print("季節は春");
break;
case Summer:
Print("季節は夏");
break;
case Autumn:
Print("季節は秋");
break;
case Winter:
Print("季節は冬");
break;
default:
Print("Unknown");
}
列挙型は上記のように、取りうる値が決められた何種類かの中で変動する場合の条件分岐などで活用することができます。
季節は夏
なんでこんなに型がある?
基本のデータ型にはさまざまな種類がありますが、そもそもなんでこんなに種類があるんでしょう?
メモリの節約
整数型と文字列型、日付時刻型のように、まったく異なるデータを扱う型ならまだしも、整数型に至ってはunsigned含めると8つもデータ型があります。
ひとつにまとめてくれたっていいじゃないか
これはプログラムが実行されるコンピュータの資源を無駄に使わないよう節約するためです。
例えば0〜100までの値しか取り得ないデータを保持するのにlong型で宣言した途端に、必要なくても8バイトが確保されてしまいます。charよりも7バイト、余計なメモリ領域が確保されてしまうことに。
今時のパソコンは8GBのメモリを積んでるなんて当たり前ですが、EAを運用するためにVPSを使用している場合、搭載しているメモリ量が1GBや1.5GBの環境なんていうのはザラにあります。
MT5を1GBで稼働すること自体かなりギリギリなので、実際にはもう一段階メモリの多いVPSを使うことが望ましいのですが、できる部分は節約しておくに越したことはありません。
ただし、格納できる範囲以上の値を扱おうとすると、想定外の動作を引き起こす要因となります。
十分に設計した上で利用するデータ型を選択しましょう。
処理性能への影響の回避
データを格納するためにメモリを確保するという作業は、メモリの中から空いている領域を探して、空いてる領域を見つけたらそこを予約して…と、コンピュータにとって少しコストの高い処理となります。
確保する領域が小さい方が当然負荷としては低くなります。
また、特に浮動小数点のデータ型は、精度の低い(小数点以下の桁数が少ない)ほうが演算処理に掛かるコストを低減できます。
実際目に見えるほど性能が劣化するのか?
それぞれ単体で考えた時には、EAなどの動作に影響を与えるほどの遅延は発生しません。
しかしプログラミングはFX同様小さなことでもコツコツと。
一見大丈夫だと思っても、負担のかかるような処理が繰り返していて、実行してみたらなんだかラグが起きてしまう…なんてこともないとは言い切れません。
細かいところでも、どんなデータを使用するのか理解して、しっかり意味あるプログラムを書くようにしましょう。
まとめ
MQL5で利用できるさまざまなデータ型について紹介しました。
理想は取りうる値を正確に把握して、必要最小限のデータ型として利用していくことですが、難しく考えず、最低限次のデータ型は抑えておきましょう。
- int
- double
- bool
- string
- datetime