
新しいレールへの第一歩: MQL5内のカスタムインディケーター
はじめに
ついに新しいトレードターミナルMetaTrader 5を試す機会がやってきました。間違いなく先行タイプに比べ注目に値し、多くの新しい特徴があります。他に比べてこのプラットフォームの有利な点は以下になります。
- 基本的に修正言語はオブジェクト指向プログラミングが使えるようになった上、まだ構造化プログラミングの有利な点を使うもこと可能。
- MetaTrader 4に比べかなり速くなったコード実行スピード。
- 必要な情報を表示するための可能性が本質的に増大。
新しいターミナルと言語のすべての可能性と特徴をリストすることはしません。それらはあまりにも多くありすぎ、新規の特徴によっては別の記事で説明するに値します。またここにはオブジェクト指向プログラミングで書かれたコードがありません。開発者にとってさらに有利になる点として、ただ単に言及するにはあまりにも真剣なトピックです。
本記事ではインディケーターとその構造、描画、タイプ、そしてプログラミング の詳細についてMQL4と比較しながら考察します。
本記事には一切複雑なことはありません。さらに、ここで述べられたことはすべて添付ファイルを使って直接ターミナルで確認することができます。
初心者にも経験のある開発者にとっても本記事が有益となることを望みます。たぶん中には新しく知ったと思えるものがあると思います。
全般構造
MQL4に比べインディケーターの全般構造は変わっていません。
これまでのように、初期化・データ処理・インディケーター非初期化用の3つの関数があります。
これまでのように、多くのインディケーターパラメーターがプロパティで定義できます。 (#property キーワード) それらの多くはインディケーターのために特別に設計されています。プロパティと入力パラメーターは以前の通りグローバルで定義されます。
例としてRSI インディケーターのカスタム着色を実行を考えてみましょう。これは省略版で完全版はファイルColor_RSI.mq5にあります 。
コードの部分を見ていきましょう。
//--- group of data properties #property copyright "TheXpert" #property link "theforexpert@gmail.com" #property version "1.00" //--- description of the indicator should not exceed 511 symbols in total //--- including newline symbols #property description " " #property description "Demonstration of the indicator creation" #property description "by the example of RSI coloring"
上記で規定されているプロパティは情報パネルに表示されています ( "Common" タブのプロパティ)。それはこのように見えます。
//--- indicator properties #property indicator_separate_window // the indicator will be displayed in a separate subwindow #property indicator_buffers 2 // number of used buffers #property indicator_plots 1 // number of displayed buffers //--- plot 1 #property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // use 2 colors #property indicator_type1 DRAW_COLOR_LINE // and the special color display type
これらのプロパティはインディケータープロパティです。他のプロパティの説明はヘルプで見つけることができます。
//---- buffers double Values[]; // buffer of values double ValuesPainting[]; // buffer of color indices //--- indicator input parameters input string _1 = "RSI parameters"; input int RSIPeriod = 5; input int SmoothPeriod = 5; input ENUM_APPLIED_PRICE AppliedPrice = PRICE_CLOSE; input string _2 = "Color settings"; input color Down = clrDarkSalmon; input color Up = clrDeepSkyBlue; //--- variable for storing the indicator handle int RSIHandle;
以下がインディケーター入力パラメーター とグローバル変数です。 (クライエントターミナルのグローバル変数と混同してはいけません。) インディケーター入力パラメーターは入力識別子で規定されています。
今やパラメーターの列挙を設定することが可能になりました。時々不適切なパラメーター選択を防ぐことに役立ちます。
例えば、AppliedPrice パラメーター は、有効な値と一緒ににドロップダウンリストに表示されます。
それなのでユーザー定義を含め全ての列挙は同じドロップダウンリストに表示されます。例えば以下のパラメーターがそうです。
//... enum DayOfWeek { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }; input DayOfWeek Day; //...
が以下のように表示されます。
int OnInit() { //--- bind indicator buffers //--- Values serves as a display buffer SetIndexBuffer(0,Values,INDICATOR_DATA); //--- ValuesPainting serves as the buffer for storing colors SetIndexBuffer(1,ValuesPainting,INDICATOR_COLOR_INDEX); //--- Set the start of drawing Values buffer PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,RSIPeriod); //--- Set the indicator name IndicatorSetString(INDICATOR_SHORTNAME,"Color RSI("+string(RSIPeriod)+")"); //--- Set an empty value for plots PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE); //--- Set buffer colors PlotIndexSetInteger(0,PLOT_LINE_COLOR,0,Down); PlotIndexSetInteger(0,PLOT_LINE_COLOR,1,Up); //--- Receive indicator handles RSIHandle=iRSI(NULL,0,RSIPeriod,AppliedPrice); //--- Set the sequence of buffer indexation ArraySetAsSeries(Values,true); ArraySetAsSeries(ValuesPainting,true); //--- successful execution return(0); }
OnInitはインディケーター初期化関数です。ここでインディケーターバッファーとそのプロパティーを設定し、プロパティーで定義できないインディケーター変数または動的に設定しなければならないインディケーター変数を定義します。また、インディケーターに必要なハンドル割り当てを含め初期データ初期化があります。
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- number of bars for calculation int toCount=(int)MathMin(rates_total,rates_total-prev_calculated+1); //--- try to copy iRSI indicator data if(CopyBuffer(RSIHandle,0,0,toCount,Values)==-1) { Print("Data copy error, №",GetLastError()); //--- return command for recalculation of the indicator values return(0); } //--- coloring. Yer, now it has become that easy for(int i=toCount-2;i>=0;--i) { //--- coloring the first line if(Values[i+1]!=EMPTY_VALUE && Values[i]>Values[i+1]) ValuesPainting[i]=1; else ValuesPainting[i]=0; } //--- return value of prev_calculated for next call return(rates_total); }
OnCalculateはデータ計算のための関数です。この関数は2種類あります。これが標準書式です。詳細は以下です。
関数:
//--- this function usage in not obligatory /* void OnDeinit() { } */
OnDeinit はインディケーター非初期化関数です。例えばファイルハンドルのようにリソースを置き換えることがよく必要になります。他のケースではこの関数は不要です。
インディケーターの2つのコンセプト
一つはスタンダードで、MQL4でお馴染みだけど少し改良されたものです。関数 OnCalculate はStart関数に代わって使われます。
他のスタンダードフォームは以下のように見えます。
int OnCalculate(const int rates_total, // Arrays size const int prev_calculated, // Bars processed on the previous call const datetime& time[], // Data for the current chart and timeframe... const double& open[], const double& high[], const double& low[], const double& close[], const long& tick_volume[], const long& volume[], const int& spread[]) { return rates_total; }
データコピー目的のコード数を減らすため、チャートデータは配列として関数のパラメーターへ直接渡されます。さらに、利用可能なバー数は関数の最初のパラメーターとして渡され、 ラストコールまたは0 (ゼロ)の後に処理されたバー数は第二のパラメーターとして渡されます。
0 (ゼロ) 値は最初のインディケーターコール時また新データ・不明データのローディング時に渡されます。パラメーターはIndicatorCounted()の代替(代替もしくは同等であるかはあなた次第)ですが、多くの開発者にとって不都合です。
第二のコンセプトはMQL4関数のような i<…>OnArrayの置換と拡張です。 ターミナル例でこのようなタイプのインディケーターはカスタム移動平均です。このタイプのインディケーターはカスタムインディケーターを含め、ユーザーの選択に従ったデータ処理を目的としています。
このようなタイプのインディケーターのデータ処理関数はこのように見えます。
int OnCalculate (const int rates_total, // the size of price[] array const int prev_calculated, // bars calculated in the previous call const int begin, // where notional data start from const double& price[] // data array for calculation ) { return rates_total; }
この関数の最後のパラメーター は処理用にユーザーが選択したデータです。数多くのバッファーと一緒にインディケーターを適用したい場合、 最初のインディケーターバッファーはデータ処理のために渡されます。
First Indicator's Data とは選択ウインドウのチャートに最初に添付されたインディケーターに該当インディケーターが適用されることを意味します。
Previous Indicator's Data とは選択ウインドウのチャートに最後に添付されたインディケーター に該当インディケーターが適用されることを意味します。
これらのインディケーターはスタック全体を組み立てに使われます。例えば、カスタム移動平均 インディケーターを使うと、 最初のインディケーターを必要データに課し、第2を第1に、第3を第2に課すことで 3重スムージングできます。
この特定のコンセプトを実行したスタンダードインディケーターはたくさんあります。そのためapplied_price_or_handle関数パラメーターのプロンプトが見えた場合、
インディケーターがユーザーデータ上で計算されたように実行されたことを示します。 これらのデータの扱いはapplied_price_or_handleパラメーター として渡されなければなりません。
同じ方法で、データ処理をインディケーターコードで直接作ることができます。
{ //... RSIHandle = iRSI(NULL, 0, RSIPeriod, AppliedPrice); SmoothHandle = iMA(NULL, 0, SmoothPeriod, 0, MODE_EMA, RSIHandle); //... }
このコンセプトの別の新しい応用はユニバーサルサービスインディケーターを書けることです。このようなインディケーターの例は、ファイル Direction_Brush.mq5に添付されています。
結果は上部チャートに表されています。このケースの方向カラーリングは独立したものとして区別され他のインディケーターで実行されています。
確かに普遍性には限りがあります。なぜならゼロバッファーのインディケーターのみに適用可能だからです。それでも、このタイプのインディケーターは有益であるかもしれないと考えます。
一方、カスタムインディケーターを書く時これを考慮に入れるべきです。なぜならゼロバッファーにおける主要情報処理が一つのインディケーターでマルチ関数マシンを実行することを防げるからです。他のアクションの多くは外部サービスインディケーターで作成・実行できます。やらなければならないのは必要とされる関数と一緒にサービス・インディケーターを自身のカスタムに添付するだけです。
適用範囲は最初の印象ほど狭くありません。
- トーン視覚化を含むインディケーター特徴(トップ, 方向, レベル, セグメントなど)のカラーリング ;
- 異なった条件での異なったシグナル;
- 統計の集計と表示。 例えば、データ分布;
- 例えば移動平均ジグザグなど、一つのバッファー用に計算できるユニバーサル・インディケーターの構造。
上記の特徴はコンセプト実行例の抜粋です。後で他の多くの効果的な実行が見つかると思います。
データアクセス
データアクセスの原理がMQL5では変わりました。今や配列で直接実行されます。そのため計算スピードは著しく速くなりました。今は配列を作成して、各値をiCustom 関数と呼ぶ必要がなくなりました。そのかわり必要なデータ数は一つの関数を呼び、指定のローカル配列にコピーされた要求データを直接使うことで得られます。
システム関数 CopyBufferを使ってデータコピーが実行されます。この関数の説明はヘルプにあります。
インディケーターと静的 (事前定義されたサイズ) 配列のためにコピーする最大データ数は配列のサイズによって決まります。動的配列のサイズは コピーされたデータの数がそのサイズを超えると変わります。
その上、ヒストリカルデータにアクセスする特別関数があります。
関数 | 説明 |
---|---|
CopyBuffer | ある特定のインディケーターの指定バッファーデータを必要量入手。 |
CopyRates | 指定されたシンボル期間のMqlRate構造ヒストリーデータ指定量を rates_array配列に入れる。 |
CopyTime | 指定シンボル期間ペアーにおけるtime_array、バー・オープニング時間のヒストリーデータ指定量に働く。 |
CopyOpen | 選択されたシンボル期間ペアーにおけるopen_array、バーオープン価格のヒストリーデータ指定量に働く。 |
CopyHigh | 選択されたシンボル期間ペアーにおけるhigh_array、最高バー価格のヒストリーデータ指定量に働く。 |
CopyLow | 選択されたシンボル期間ペアーにおけるlow_array、最小バー価格のヒストリーデータ指定量に働く。 |
CopyClose | 選択されたシンボル期間ペアーにおけるclose_array、バー・クローズ価格のヒストリーデータ指定量に働く。 |
CopyTickVolume | 選択されたシンボル期間ペアーにおけるvolume_array、tickボリュームのヒストリーデータ指定量に働く。 |
CopyRealVolume | 選択されたシンボル期間ペアーにおけるvolume_array、トレードボリュームのヒストリーデータ指定量に働く。 |
CopySpread | 選択されたシンボル期間ペアーにおけるspread_array、スプレッド値のヒストリーデータ指定量に働く。 |
詳細はヘルプにあります。
このデータは インディケーターの1つの形でのみ渡され、他に関してはそれらで手に入れなければなりません。
ヒストリカルデータ配列はダブルではないこともある事実から、動的非インディケーターバッファーのみを保存のために使うことをおすすめします。
それからもう一つ説明書に記載のない詳細があります。もしコピーデータ数が0 (ゼロ)に等しい場合、関数 CopyBuffer がコード №4003エラーとなるため、 コピーするデータ数は1 つのエレメント以上でなければなりません。
インディケーター バッファー
バッファー数に限りはありません。
クラスターインディケーターの作成時、どのように情報を正確に調整すればいいか、いかに効率的に中間計算を実行するかを考える必要はもうありません。
しかしバッファーストレージにはメモリが必要なことを忘れてはいけません。そのためターミナルヒストリーの深さを1,000,000バーに指定し、「太い」クラスターインディケーターを分チャートに添付する場合、ターミナルがGb単位のメモリを消費したとしたも驚かないで下さい。
バッファーの本質も変わりました。使用されるバッファーの数はプロパティーで指定されています。
#property indicator_buffers 2 // buffers used
この値は全バッファー数に相当しなければなりません。
表示されるバッファーの数はプロパティーで定義されています。
#property indicator_plots 1 // buffers displeyed
以下がその詳細です。多くの作画スタイルは一つの INDICATOR_DATA バッファーのみ必要です。しかし、中にはいくつかのインディケーターバッファーを作画に要すスタイルがあります。
以下の作画スタイルのことです。
- DRAW_HISTOGRAM2 - 2つのインディケーター バッファー (HistogramSample.mq5)を必要とする。
- DRAW_FILLING - 2つのインディケーター バッファー (CrossMa.mq5)を必要とする。
- DRAW_CANDLES - 4つのインディケーター バッファー (CandleSample.mq5)を必要とする。
- DRAW_BARS - 4つの インディケーター バッファー (BarsSample.mq5)を必要とする。
着色できないDRAW_FILLING スタイルを除く上記タイプは全て有色類似体を持っています。
全てのインディケーター バッファーは今や3タイプに分かれます。
-
INDICATOR_DATA - データがチャートに表示されるバッファー。これらのバッファーは作画と iCustomと働くことを目的としていま す。それらは最初に登録されるべきです。任意オーダー(誤り)の場合、コードはうまくまとめられ、チャートに適用された時に作画されますが、ほとんどの場合不正確です。
-
INDICATOR_COLOR_INDEX - 色保存用のバッファー。特別カラータイプ (#property indicator_typeN)を持つ INDICATOR_DATA タイプのカラーバッファーを保存するのに必要です。これらのバッファー (カラーバッファーと呼ばれる) はそれを使うメインバッファーのすぐ後に登録されるべきです。
-
INDICATOR_CALCULATIONS - このようなタイプのバッファーは補助計算結果を保存することを目的をしています。それらはチャートに表示されません。
int OnInit() { // ... SetIndexBuffer(0, V2, INDICATOR_DATA); SetIndexBuffer(1, V2C,INDICATOR_COLOR_INDEX); SetIndexBuffer(2, V4, INDICATOR_DATA); SetIndexBuffer(3, V4C,INDICATOR_COLOR_INDEX); SetIndexBuffer(4, V1, INDICATOR_CALCULATIONS); SetIndexBuffer(5, V3, INDICATOR_CALCULATIONS); // ... return 0; }
さらにiCustom経由のインディケーターに言及する場合いくつかの特徴があります。
- 作画用バッファー (チャートに表示されるバッファー) は読み込みが可能です。バッファー数(インデックス)は一つでなければならなく、そのようにバッファーは登録されます。
- 色保存用のバッファーは読み込みが可能かもしれませんが絶対とは限りません。例えば、上記のコードでバッファーV2Cは必要値を読み込み入手しますが、バッファーV4C は不可能です。
- MQL4と同様、中間計算のためのバッファーはありません。外部データバッファーへのアクセス保証がほしければ、 INDICATOR_DATAとして宣言します。
アクセスできないバッファー要求の場合、エラー4806 ("要求されたデータが見つかりません。") が発生します。
カラーバッファーについて詳しく見ていきましょう。
MQL4では、各カラーにおいて別のバッファーを作る必要がありましたが、今はカラースタイルを使います。 一つのバッファーに対して最大63色まで指定できます。場合によっては、バッファー使用数を最適化し、メモリ使用を節約できます。また、特にトーン視覚化の使用においてインディケーターを書く新しい可能性が開けました。
さらに、こうしたイノベーションが場合によってはMQL4に比べ 色適用のロジックを大いに簡素化します。もっとも明白な例は色によるトレンドの区別です。MQL4では、(正しいケースからの) とても経済的ケースの実装には、3つのバッファーと非自明なプログラミングを必要としました。
今では、これまでになく簡単です。これはコードサンプルです。完全な実装はファイルColor_RSI.mq5にあります。
#property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // use 2 colors #property indicator_type1 DRAW_COLOR_LINE // and the special color display type //---- buffers double Values[]; // buffer of values double ValuesPainting[]; // buffer of color indices //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- bind indicator buffers //--- Values serves as a display buffer SetIndexBuffer(0,Values,INDICATOR_DATA); //--- ValuesPainting serves as the buffer for storing colors SetIndexBuffer(1,ValuesPainting,INDICATOR_COLOR_INDEX); //... return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(/*...*/) { //--- number of bars for calculation int toCount=(int)MathMin(rates_total,rates_total-prev_calculated+1); //--- try to copy iRSI indicator data if(CopyBuffer(RSIHandle,0,0,toCount,Values)==-1) { Print("Data copy error, №",GetLastError()); //--- return command for recalculation of the indicator values return(0); } //--- coloring. Yer, now it has become that easy for(int i=toCount-2;i>=0;--i) { //--- coloring the first line if(Values[i+1]!=EMPTY_VALUE && Values[i]>Values[i+1]) ValuesPainting[i]=1; else ValuesPainting[i]=0; } //--- return value of prev_calculated for next call return(rates_total); }
さらにいくつかのコードです。そして次が得られます。
作画用のカラータイプを使うとき、詳細に注意するべきです。
カラーバッファーは、動的色定義スキームを使用している場合、最大カラー数はindicator_colorNプロパティで定義したカラー数に限られます。例えば、
#property indicator_color1 clrDarkSalmon, clrDeepSkyBlue // use 2 colors
(PlotIndexSetIntegerを使って)動的に多くのカラー数を設定したとしてもバッファーのカラースキームは最大2色です。
そのため、必要なカラー数はプロパティ定義ライン一列で書かなければなりません。 そうすると動的に変えることができます。私が見つけた一番短い色は「赤」です。しかし、次が常に可能です。
以下の代わりに
#property indicator_color1 clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, clrRed, //…
以下のように書けます。
#define C clrRed #property indicator_color1 C, C, C, C, C, C, C, C, C, C, C, C, C, C, //…
バッファーの最大のカラー数は63色です。カラー数が最大数(indicator_colorNプロパティで定義)を超える場合、バッファーは表示されません。
これは最大カラー数を使ったトーン視覚化の例です。
一般的に作画の機会が著しく増え、それは素晴らしい事です。
配列
インデックスによって配列データに直接参考する時は、 データ注文タイプ AsSeries プロパティを意味する必要があります。配列タイプによっては定義ができません。
このフラグは多次元には設定できず静的配列用です。OnCalculate 関数に渡される配列タイプはこのようなフラグを設定することができます。
CopyBuffer関数を使ったデータコピーは AsSeries プロパティに左右されませんが、その実行はバッファーによって変わります。
インディケーター バッファーは利用可能なヒストリーの深さ全体をコピーする必要があります。これは覚えておかなければなりません。
動的配列と静的 (事前に定義されたサイズ) 配列においては、現在から過去へデータコピーが行われます。
CopyBuffer関数は動的 (インディケーターを除く) バッファーの必要なサイズのためにバッファーのサイズを変えます。
静的配列をデータ コピーに使う事はおすすめしません。
一般的に、コピーデータの仕方とそれらのアドレスの仕方を常に確認することをアドバイスします。もっともシンプルで安全な方法は以下です。
- ヒストリーデータ保存に使用されている全てのバッファーのAsSeries プロパティを設定。
- 異なったバッファーのバッファー構造を考慮。
- _LastError 変数値(最後のエラーコード)を常に確認。
さらに、CopyBuffer 関数とAsSeries プロパティに関連する全ての関数のヘルプを勉強することを強くアドバイスします。
インディケーター数
今ではIndicatorCounted 関数の必要性についてのディベートが忘却の中へ追いやられようとしています。 なぜならこの値は以前の関数呼び出しの戻り値として私たちが直接定義するからです。
int OnCalculate(const int rates_total, // array size const int prev_calculated, // bars processed after last call //...) { return rates_total; }
値多くの場合、現在関数呼び出しのバー数を含むrates_total 値を戻すだけで十分です。
しかし、もし価格データが最後のOnCalculate() 呼び出し後(例えばヒストリーデータのロードやヒストリーデータ空白の満杯)に変えられると、入力パラメーター prev_calculatedは
クライアントターミナルによって0(ゼロ)に設定されます。
また、 もし OnCalculate 関数がゼロに戻ると、このインディケーター値はクライアントターミナルの データウインドウ には表示されません。そのため、もしヒストリーローディングプロセス中もしくは接続不良の後にインディケーターを確認し完全な再計算を実行したい場合、0ではなく1に戻ります。
もう一つの追加された便利な特徴はインディケーターのために計算されたバー数を判定することです。大量のデータの計算を実行するExpert Advisorではさらに有益です。 この関数はBarsCalculatedで、詳細はヘルプにあります。
この関数はもう一つ便利な適用があります。もしインディケーターがロードされていない場合、インディケーターハンドル作成され、計算のための使用される時間の間、ローディングに時間がかかるかもしれません。
初期化と初期事前計算に時間が必要です。それは計算スピードとインディケーター詳細によります。
この間、CopyBuffer 関数の呼び出しエラー 4806 "要求されたデータが見つかりません "が起こります。
関数BarsCalculated はコピー用にインディケーター データの利用性 を判定します。:
//--- number of bars for calculation int toCount=rates_total-(int)MathMax(prev_calculated-1,0); //--- try to copy iWPR indicator data int copied=CopyBuffer(WPRHandle,0,0,toCount,Values); int err=GetLastError(); //--- check coying result if(copied==-1) { //--- if error number is 4806, the data simply have not been uploaded yet if(err==4806) { //--- wait till the data is uploaded for(int i=0;i<1000;++i) if(BarsCalculated(WPRHandle)>0) break; //--- try to copy iWPR indicator data again copied=CopyBuffer(WPRHandle,0,0,rates_total,Values); err=GetLastError(); } } //--- check coying result if(copied==-1) { Print("Error when trying to get WPR values, last error is ",err," bars ",rates_total); return(0); } //...
要約
結論として、本記事ではほんのいくつかの詳細だけが考察されました。基本的な側面がここで紹介できたことを望みます。
常に詳細についての情報を探し見つけることのできる場所として、本記事が参考になれば光栄です。
本記事で間違いもしくは何か重要事項を発見したら、教えて下さい。間違いを直して本記事をより良く改良していきたいです。
最近の変更をまとめる予定です。さらにコメント上で役に立つ情報が現れることを望みます。
それがじっくり読む理由です。
付属
- Color.mqh - ファイル含む。このファイルを MQL5/Includeフォルダにコピー。このファイルはToned_WPR インディケーターに必要です。
- Color.mq5 -ライブラリ。このファイルをMQL5/ライブラリフォルダにコピー。このファイルはToned_WPR インディケーターに必要です。
すべてのoverファイルはインディケーターです。
謝辞
再度、Victor Rustamov (granit77) 様には、原稿読み上げや有益な話し合い、役に立つアドバイスを頂き、お礼申し上げます。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/5





- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索