MQL5の高度な変数とデータ型
はじめに
MQL5は、最も人気高いとされる取引プラットフォームMetaTrader 5のプログラミング言語です。シンプルなものから複雑なものまで、あらゆる取引システムを作成するために使用できるツールや概念が非常に豊富です。開発者としての私たちの目的は、開発目的を達成するためにこれらのツールや概念をどのように使用するかを理解することです。
MQL5における単純な変数とデータ型の基本については、「アルゴリズム取引システムを設計する理由と方法を学ぶという」稿ですでに触れ、取引ソフトウェアをコーディングするためにMQL5をどのように使用できるか、変数の定義、型、そしてそれらをどのように使用できるかについて詳しく学びました。また、integer、float、double、string、boolといった単純なデータ型についても学びました。さらに、入力変数についても触れ、ユーザーがソフトウェアで自分の好みを設定する際に、入力変数をどのように使用すれば有利になるかを説明しました。
別の「MQL5で日付と時刻を扱う方法を学ぶ」稿では、Datetimeデータ型について詳しく学びました。ソフトウェア(特に取引ソフトウェア)でこのデータ型を使用しないものはないというほど、このデータ型は重要だからです。
この記事では、MQL5における変数とデータ型について、そしてそれらがMQL5取引ソフトウェアを作成または構築する際にどのように役立つのかについて、言及し、さらに深く学んでいきます。変数とデータ型に関するいくつかの高度な概念について、以下のトピックを通して学びます。
- 定数: 変わらない値を持つ識別子
- 配列: 複数の値を持つあらゆる型の変数
- 列挙体:整数値を持つ定数の整数リスト
- 構造体:異なる型を持つ関連変数の集合
- 型キャスト:ある型の値を別の型に変換すること
- ローカル変数:関数の内部でローカルに宣言された変数
- グローバル変数:関数の外でグローバルに宣言された変数
- 静的変数:メモリー内に値を保持する、宣言されたローカル変数
- 定義済み変数:プログラミング言語の起草者によって定義された変数
それぞれのトピックについて詳しく説明し、どのように使用できるかを理解し、できるだけ多くの例を見て理解を深め、言及されたすべての概念を効果的に使用できるようにします。プログラミングは実践が非常に重要な科学であるため、学んだことを応用し、効果的な取引システムを作成するために、コード全体の一部として各概念をどのように使用できるかを理解することが非常に重要です。
定数
この部分では、プログラミングとMQL5における定数の概念を深く掘り下げ、なぜこのタイプのもの使用する必要があるのかを理解します。定数は、読み取り専用または名前付き定数とも呼ばれ、最初の初期化以降は変更できないし、新しい値を代入することもできません。コードがそうしようとすると、エラーが発生します。定数の概念はほとんどのプログラミング言語でサポートされており、MQL5もこの概念をサポートしています。プログラムで定数を定義するには 2 つの方法があります。
- グローバル定数
- const指定子
グローバル定数
グローバル定数は、プログラムの冒頭で#defineプリプロセッサー指令を使用してグローバルに定義することができます。#defineの後に識別子を指定し、その後に代入する定数値が続きます。定数値は、文字列型などどのようなデータ型でもよく、私たちのソフトウェアでは以下のようにコーディングします。
#define Identifier_Name constant_value
先のコードでわかるように、#defineプリプロセッサ指令は定数宣言があることを指定します。この方法についてより深く理解するために、次のような例を見ることができます。
以下の例では、グローバルスコープでまず#defineプリプロセッサー指令を使用して定数があること、識別子がPLATFORM_NAMEであること、識別子の定数値が文字列データ型の「MetaTrader 5」であることを宣言します。
#define PLATFORM_NAME "MetaTrader 5"
定数を必要とする関数では次のように識別子を出力します(OnStart()の例)。
void OnStart() { Print("The trading platform name is: ", PLATFORM_NAME); }
コンパイルして実行すると、[エキスパート]タブに以下のスクリーンショットと同じようなメッセージが表示されます。
前述のように、MetaTrader 5の定数値は変更できません。
const指定子
この方法では、変数とそのデータ型の前にconst指定子を使用して定数を宣言することができます。この指定子で、変更できない定数を宣言します。以下はこれをおこなう方法です。
const in varName = assignedVal
以下は、この方法で定数を宣言する簡単な例です。
const int val = 1;
この変数の値を表示するには、
Print("constant val is: ", val);
とします。結果は次のようになります。
前述のように、変数(val)を更新しようとすると、定数で更新できないため、次のようなエラーが発生します。
正規変数と定数の違いを説明するために、2つの値があり、一方が定数、もう一方が正規変数である場合を例に挙げます。
const int val = 1; int val2 = 2; Print("constant val is: ", val); Print("val 2 is: ", val2);実行すると、以下のスクリーンショットと同じ結果になります。
では、次のコードでval2を別の値4で更新してみましょう。
val2 = 4;
この2つの値をもう一度出力すると、結果は次の例と同じになります。
見ての通り、val2の値は2ではなく4で更新されています。
配列
この部分では、あらゆるプログラミング言語の基本概念である配列を理解します。配列は、任意の単一のデータ型の値を多数格納できる変数です。配列は、インデックスと対応する値を持つリストと考えることができます。特定の値にアクセスする必要がある場合は、インデックスを作成することでアクセスできます。
配列のインデックスはゼロから始まるので、最大インデックスは、配列のサイズを1つ小さくした結果と同じになります。5つの値を持つ配列があるとすると、そのインデックスは(0, 1, 2, 3, 4)であり、5-1である4が最大値であることがわかります。
特定のインデックスの値にアクセスするには、角括弧[]で指定されたインデックスを参照します。これが、前述したインデックスによるアクセスの意味です。
また、配列には静的配列と動的配列があります。配列のサイズに関する両者の違いは、静的配列はサイズが固定された配列であってサイズを変更することはできないが、動的配列にはサイズがないということです。配列のサイズを変更する必要がある場合は、静的配列の代わりに動的配列を使用できます。
静的配列宣言
新しい静的配列を宣言するには、以下の例のようにできます。
int newArray[5]; newArray[0] = 1; newArray[1] = 2; newArray[2] = 3; newArray[3] = 4; newArray[4] = 5; Print("newArray - Index 0 - Value: ", newArray[0]); Print("newArray - Index 1 - Value: ", newArray[1]); Print("newArray - Index 2 - Value: ", newArray[2]); Print("newArray - Index 3 - Value: ", newArray[3]); Print("newArray - Index 4 - Value: ", newArray[4]);
実行すると、以下のスクリーンショットと同じ結果が得られます。
効率化向上のために、配列は次のように省略したコードで宣言できます。配列に値を代入するときは、{}括弧で囲み、カンマ「,」で区切ります。
int newArray[5] = {1, 2, 3, 4, 5}; Print("newArray - Index 0 - Value: ", newArray[0]); Print("newArray - Index 1 - Value: ", newArray[1]); Print("newArray - Index 2 - Value: ", newArray[2]); Print("newArray - Index 3 - Value: ", newArray[3]); Print("newArray - Index 4 - Value: ", newArray[4]);
前のコードを実行すると、前述のようにメッセージが出力されたのと同じ結果が得られます。
動的配列宣言
前述したように、動的配列は固定サイズのない配列であり、サイズを変更することができます。ここで知っておく必要があるのは、このタイプの配列は、MQL5で価格や指標の値などのデータを格納するために使用されるということです。これらのデータは動的であるため、このタイプが非常に適しているからです。
新しい動的配列を宣言するには、次のコードのようにできます。
double myDynArray[]; ArrayResize(myDynArray,3); myDynArray[0] = 1.5; myDynArray[1] = 2.5; myDynArray[2] = 3.5; Print("Dynamic Array 0: ",myDynArray[0]); Print("Dynamic Array 1: ",myDynArray[1]); Print("Dynamic Array 2: ",myDynArray[2]);
前のコードでわかるように、空の角括弧で配列を宣言し、ArrayResize関数を使用して配列のサイズを変更しました。実行すると、次のスクリーンショットに示すような結果が表示されます。
前のスクリーンショットでわかるように、各インデックスとそれに対応する値について3つのメッセージが出力されています。ここまでは1次元配列について説明してきましたが、2次元、3次元、4次元といった多次元配列も使用することができます。
多次元配列
多次元配列はネストされた配列、つまり配列の中の配列と考えることができます。例として、2つの配列があり、それぞれに2つの要素があるとします。以下で、このタイプの配列をコーディングする方法を確認できます。
2つの要素を持つ2次元配列を宣言します。
double newMultiArray[2][2];
インデックス0を持つ最初の配列に値(要素)を代入します。
newMultiArray[0][0] = 1.5; newMultiArray[0][1] = 2.5;
インデックス1を持つ第2の配列の値(要素)を代入します。
newMultiArray[1][0] = 3.5; newMultiArray[1][1] = 4.5;
2つの配列の値とその値(要素)を出力します。
Print("Array1 - Index 0 - Value: ", newMultiArray[0][0]); Print("Array1 - Index 1 - Value: ", newMultiArray[0][1]); Print("Array2 - Index 0 - Value: ", newMultiArray[1][0]); Print("Array2 - Index 1 - Value: ", newMultiArray[1][1]);
このコード実行すると、以下のスクリーンショットと同じ出力が表示されます。
前のスクリーンショットでわかるように、配列1には1.5と2.5の2つの値があり、配列2には3.5と4.5の2つの値があります。
ここで、次の例のように、多次元配列を宣言する場合は最初の次元を空のままにすることしかできないことにも言及しておきます。
double newMultiArray[][2];
その後、次のような変数を使用して渡すことができます。
int array1 = 2; ArrayResize(newMultiArray, array1);
コンパイルして実行すれば、以前と同じ結果が得られるでしょう。
列挙体
この部分では、列挙体について説明します。列挙体とは、関連する概念を記述するために使用できるデータの集合、または定数や項目のリストと考えることができます。組み込みの列挙体とカスタムの列挙体があります。組み込みの列挙体はMQL5であらかじめ定義されているものでプログラムで呼び出して使用することができますが、カスタムの列挙体は私たちのニーズに従ったカスタムのものです。
組み込み列挙体は、MQL5のドキュメントやリファレンスにENUM_DAY_OF_WEEKのように記載されていますが、カスタム列挙型を見てみると、作成したソフトウェアで後で使用するために必要なものを設定できることがわかります。
コードでこれらの列挙型を使用する方法は以下に示されています。まず、enumキーワードを使用して列挙型を定義し、列挙型の名前と、カンマで区切られた関連データ型の値のリストを定義します。
enum name of enumerable type { value 1, value 2, value 3 };
workingDaysという名前のenumを作る例を見てみましょう。workingDaysは、後で変数を宣言する型になります。
enum workingDays
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
};
次に、workingDays型の新しい関連変数を宣言し、リストに定義されている今日の値を代入します。そしてこの変数を出力してみます。
workingDays toDay;
toDay = Wednesday;
Print(toDay);
次のスクリーンショットのように、月曜日を表す0から金曜日を表す4までのリストから、水曜日を営業日とする「2」メッセージを見つけることができます。
また、0の代わりに開始番号を割り当てることもできます。例えば、月曜日=1と指定すれば、1から開始する番号を割り当てることができます。
構造体
関連する変数について、いくつもの異なるデータ型を宣言する必要があるかもしれません。その場合、このセクションで説明するように、構造体を使用して効果的にこれをおこなうことができます。同じ型しか持つことができない列挙型とは異なり、この構造体のメンバーはどのようなデータ型であっても構いません。列挙と同じく、MQL5には例えばMqlTickのように定義済みの構造体がありますが、構造体を独自に作成することもできます。
例えば、tradeInfoの構造体を作成する必要があるとします。メンバーとして、銘柄、価格、ストップロス、テイクプロフィット、取引時間があり、銘柄は文字列、価格、ストップロス、テイクプロフィットはdouble値なので、それぞれ異なるデータ型を持っていることがわかります。この場合、以下のように構造体を使用します。
structキーワードを使用して独自の構造体を宣言します。
struct tradeInfo { string symbol; double price; double stopLoss; double takeProfit; };
次に、tradeInfo型の新しいtradeオブジェクトを宣言し、オブジェクト名の後に「.」を使用してこの構造体のメンバーにアクセスすることで、以下のように値を割り当てることができます。
tradeInfo trade; trade.symbol = "EURUSD"; trade.price = 1.07550; trade.stopLoss = 1.07500; trade.takeProfit = 1.07700;
以下のように、メンバーの値を出力することで、動作したことを以下のように確認することができます。
Print("Symbol Trade Info: ", trade.symbol); Print("Price Trade Info: ", trade.price); Print("SL Trade Info: ", trade.stopLoss); Print("TP Trade Info: ", trade.takeProfit);
出力されたメッセージは、以下のスクリーンショットのようになります。
先ほどのスクリーンショットでわかるように、割り当てられた値は必要なものと同じなので、同じコンテキストで目的を達成するためにいろいろすることができます。
型キャスト
このセクションでは、型キャストの概念とその意味を理解します。変数の値をあるデータ型から別のデータ型に変換する必要がある場合、この処理を型キャストと呼ぶことができます。ただし、この処理は予期せぬ結果を生むことがあります。
何故でしょうか。ある変数から別の変数に数値をキャストしたりコピーしたりする場合、この処理が型間でおこなわれるとデータの損失に直面する可能性があります。
以下は、データが失われなかった例、または一部のデータが失われた例です。
integer値をlong値にコピーした場合、小さい変数から大きい変数へのキャストがおこなわれるため、このようなデータ損失は発生しません。integerをdoubleにコピーする場合もinteger値がコピーされ、小数点以下の値は0になるため、失われるデータはありません。double値をinteger変数にコピーすると、その逆のケースとなり、小数点以下の値は失われるか切り捨てられます。
理解しやすいように、コードの例を見てみましょう。まず、問題のないケースを見ます。
int price = 3 - 1; double newPrice = price + 1; Print ("The price is ", newPrice);
見てわかるように、変数priceの型はintであり、それをdouble変数にキャストして表示します。小さい型から大きい型へのキャストなので、問題やデータの損失はないものと思われます。以下は出力されたメッセージの結果です。
しかし、その逆をおこなうと、次のように違うものが見えてきます。
double price = 3.50 - 1; int newPrice = price + 1; Print ("The price is ", newPrice);
見てわかるように、price変数の型はdoubleで値は3.50です。これを新しいint変数newPriceにキャストして、表示します。小数点以下のデータ(.50)の値は失われ、結果は3になります。出力メッセージは次のようになります。
ここで言及することが非常に重要なのは、大きい型の値を小さい型の変数にキャストすると、コンパイラは警告を発するので、目的や失われたデータの機密性に応じて、このメッセージを無視するかどうかを決定できるということです。以下はこの警告の例です。
有害でなければ警告を無視することもできるし、コンパイラからの警告を表示するためにdouble変数の前にintを加えて四捨五入することもできます。
ローカル変数
この部分では、ローカル変数という新しい概念を取り上げます。私たちのソフトウェアには、グローバルスコープとローカルスコープがあるとします。グローバルスコープとは、プログラムのどこにいてもアクセスできるもので、グローバルという概念については、次のトピックで変数の観点から見ていくことにします。ローカルのものは、それが宣言されているのと同じスコープ内でのみアクセスできます。
さらに明確にしておくと、関数があり、その関数の内部で関数のレベルでのみ宣言された変数がある場合、それらの変数はローカル変数とみなされます。つまり、これらの変数は、関数の実行中はアクセス可能で、関数を終了するとアクセスできなくなります。
明確にするために、例を挙げてみましょう。
void myFunc() { int var = 5; Print(var); }
前のコードでわかるように、myFuncという関数があり、この関数はどこからでも呼び出すことができます。この関数を呼び出すと、varというローカル変数が見つかり、この変数は関数内でのみ実行可能ですが、関数から抜けるとアクセスできなくなります。
つまり、この関数を呼び出すと、出力は5となり、次のようになります。
関数の外からローカル変数であるvarにアクセスしようとすると、コンパイラは次のようなundeclared identifierエラーを出します。
関数内にネストしたレベルやスコープがある場合も同様で、宣言された変数はそのスコープ内でのみアクセス可能となります。
このケースについても例を挙げてみます。
void myFunc() { int var = 5; if(var == 5) { int newVar = 6 ; Print(newVar); } }
前の例でわかるように、varの値を5と比較するためにifを入れ子にしており、もしそれがtrueであれば、newVarの値を6と宣言し、それを表示します。次のように条件がtrueであるため、出力は6となります。
このnewVarは、if演算子のスコープ内にある新しいローカル変数であり、他のローカルスコープと同じように、その外にアクセスすることはできません。試してみると、コンパイラでundeclared variableエラーが発生します。重要なのは、ローカル変数と同じ名前を持つ変数に対する宣言は、以前の宣言を上書きし、より高いスコープで型付けされるということです。
つまり概念は非常にシンプルで、ローカル変数は関数内のそのレベルまたはスコープでアクセスできます。しかし、プログラム内の任意の場所の変数にアクセスする必要がある場合はどうすればよいでしょうか。ここではグローバル変数が機能します。次のグローバル変数のトピックで説明します。
グローバル変数
グローバル変数とは、グローバルに宣言された変数のことで、ローカル変数とは異なり、プログラムのどこからでもアクセスすることができます。そのため、グローバル変数を宣言する必要がある場合は、グローバルに宣言するか、関数の外にあるプログラムのグローバルスコープで宣言するように注意します。このタイプの変数は、多くの関数で何度も使用される変数を宣言する必要がある場合に使用できます。
グローバル変数は、入力変数の後に、コードの最初または一番上に定義されます。ローカル変数と違って、グローバル変数をブロック内で宣言しようとすると、「Declaration of variable hides global declaration」というコンパイルエラーが発生します。
以下の例と同じように、ソフトウェアのどの時点でもグローバル変数を更新することができます。グローバル変数を宣言および更新する方法が示されています。
int stopLoss = 100; void OnStart() { Print("1st Value: ", stopLoss); addToSL(); } void addToSL() { stopLoss = stopLoss + 50; Print("Updated Value: ", stopLoss); }
前のコードのブロックからわかるように、プログラムの先頭でstopLoss変数をグローバルに宣言し、代入された値を表示し、最初に代入された値に50を追加するaddToSL()関数を呼び出し、更新された値を表示しています。コンパイルして実行すると、以下のようなメッセージが出力されます。
静的変数
この部分では、変数のもう1つのタイプである静的変数について説明します。静的変数はローカル変数ですが、ソフトウェアが静的変数のスコープから出た場合でも値はメモリ内に保持されます。静的変数は関数またはローカル変数のブロックで宣言できます。
変数名の前にstaticというキーワードを使い、その値を代入することでそれが可能になります。詳しくは、次の例を見てください。
void staticFunc() { static int statVar = 5; statVar ++; Print(statVar); }
次に、staticFunc()関数を呼び出します。
staticFunc();
ここで、変数はメモリに値5を保持し、関数を呼び出すたびに出力は6 (5+1)になります。以下は出力のスクリーンショットです。
[エクスパート]タブで、メッセージに6の値が出力されることがわかります。
定義済み変数
プログラミングの分野では、一般的に使用される処理を実行するために、何度も何度も多くの行を記述する必要があることに気づくかもしれません。多くのプログラミング言語には、事前定義された変数や関数があります。つまり、それらはコーディングされており、すべてのコードを再度書き直す必要がなく、簡単に使用できます。ここで、定義済み変数の役割が登場します。必要なのは、何かをするためのキーワードを覚えておくか知っていて、それを使用することだけです。
MQL5には多くの定義済み変数があり、以下の値にアクセスすることができます。
- _Symbol:チャート上の現在の銘柄
- _Point:現在の銘柄のポイント値(現在の銘柄の5桁では0.00001、現在の銘柄の3桁では0.001)
- _Period:銘柄の現在の期間または時間枠
- _Digits:現在の銘柄の小数点以下の桁数(5桁の銘柄では、小数点以下の数字が5、3桁の銘柄では小数点以下の数字が3)
- _LastError:最後のエラーの値
- _RandomSeed:擬似乱数生成器の現在の状態
- _AppliedTo:指標の計算に使用されたデータの種類を確認できる
これらの変数やその他の定義済み変数は、ドキュメントの「定義済み変数」セクションにあります。
結論
この記事では、データ型や変数など、高度なソフトウェアを開発するために不可欠なプログラミングの概念に焦点を当てながら、MQL5の複雑な機能について解説しました。MQL5を使いこなすには、基本的な側面と複雑な側面の両方を理解する必要があり、習熟するためには練習が基本となります。このガイドは、読者の取引プラットフォーム開発のためのMQL5の学習の旅を支援することを目的としています。移動平均やRSIなど、人気のあるテクニカル指標を使用した取引システムのプログラミングや作成に関する詳しい洞察については、私の出版物の詳細記事や戦略開発のヒントをご覧ください。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/14186
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索