
再起動なしでの MQL4 プログラムの外部パラメータ変更
はじめに
EA やインディケータを実行するために起動したことがある人ならだれでも、プログラムの正常な動作の基本となる外部パラメータを事前設定するというようなことに出くわしたことがあるものです。プログラム操作中にパラメータを変更する機能がありますが、初期化せずそれを行う方法とは?ある場合には、これは前にオープンしているオーダー管理に影響する可能性があります。本稿では、できるだけ柔軟にこの問題を解決してみます。
問題提示
われわれはこの問題をインディケータというよりは Expert Advisor に対して考察していきます。インディケータに対しては、この問題はたいして切実ではありませんが、方法は共通しており、どんなプログラムにも適用することができます。
従来の EA プログラミングでは、コンテキストはティックごとに作成する必要があります。コンテキストには2種類あります。状態のコンテキストと取引のコンテキストです。前者には、インディケータやオーダーのオープン/クローズ/変更に関する意思決定の元となる他のエレメントの現在の状態が含まれます。後者には、オープンしているオーダーやその状態、量に関する情報が含まれます。取引コンテキストが分析したら、トレーダーは自身のオーダーについでどうするか判断します。コンテキストは条件つきでタイプによって分けられるのです。この分類は便宜のために導入されます。
コンテキストがティックごとに復元されるなら、問題は起こりません。自由に外部パラメータを変更するパラメータを変更することができ、EA はロジックに従って動作を続けます。まず、初期化、そして EA はインディケータ値を計算し、オープンしているオーダーを『取り』、収集された情報に従って数多くの処理を行います。すべて簡単で分かりきったことです。
プログラムの完全再起動の必要がない、もっと精密なパラメータ変更技術が必要な状況を考察します。
例 1
オープンしているオーダーの中で、特別な方法で処理をすべきものがあります。すなわち、EA によってオープンされているオーダーの中からそれらを探し出す必要があります。このために、いくつかパラメータを使用します。チケット、マジックナンバー、通貨、コメントです。マジックナンバーは他の EA やマニュアルによるシステム内でオープンしているオーダーについてわれわれのオーダーを分離するのに使われます。「マジックナンバー」に加え、「通貨」によって同じ EA を異なる通貨ペアに対して使用することができます。残りは互いに独特なパラメータ2つです。「コメント」は最適なものです。それはあらゆるテキスト情報を持ちえますが、欠点があります。たとえば部分的クローズなど、変更できるのは、サーバー側です。「チケット」は各オーダーにとってユニークですが、チケットナンバーだけではクラスによってオーダーを分けるのには不十分です。そのため、オーダーのチケットは別の情報と突き合わせるる必要があります。見てわかるように、この状況では、「チケット」パラメータを使用し、オーダーのクラスに応じてチケットを格納するのが好都合です。したがって、再起動時にチケットを持つ配列は失われてしまいます。よって、オーダーの仮想クラスに関する情報を復元することは不可能となります。
例 2
オーダー状態の変化を分析できるようになるユニバーサルなメカニズムが作成されます。すなわち、オーダー状態のコンテキストが2つあります。1番目のリストは現行ティックにあり、2番目は前回ティックにあります。情報を分析するにあたり、どのオーダーがオープン/変更/クローズされているか、またその操作の理由に関して結論を導くことができます。この方法は、EA のデバッグ、トレードロジック作成の両方にとってひじょうに快適なものです。ただ、欠点は、先行ティックにオーダー状態を保存する必要があることです。それに応じてこのデータは EA の再起動時に失われてしまいます。
例 3
EA が外部データを受信しています。どのような方法で、またどこからデータがやってくるかは問題ではありません。処理のためにこのデータを蓄積する必要があり、その履歴を依頼することができない場合、EA の動作中ずっとコンテキストを作成する必要がある、ということが重要です。外部パラメータを変更することはシステムの再起動につながり、外部ソースからのデータを再び蓄積することとなります。
例 4
MQL4 プログラムはコンパイル済みファイルとして配布されています。そのため、ユーザーはコードを変更したり自分のアルゴリズムを見ることができません。それは知的財産保護には優れています。ですが、プログラムが外部変数を持つ可能性があり、一度作成して新しい値でコンパイルする代わりに毎回それを変更するのはあまり快適ではありません。この例はコンテキスト格納に直接関連していませんがここで解決すべき問題という観点からはひじょうに典型的なものです。
EA 作成時、操作中コンテキストの格納は避けるというロジックを構築すべきです。そのような開発はより複雑ですが、この場合は、システムは最大限安定したものとなります。コンテキスト格納を避けることができない場合には、検討中の方法はパラメータ変更時のデータ損失なく EA 設定を柔軟に管理することに役立ちます。
アプリケーション
上記例の問題を解決するために、MQL4 プログラムの外部パラメータ変更を管理する複合体を開発します。そのシステムによって何ができるのでしょうか?
定義によって、全タイプの外部パラメータを変更することが可能です。
• Integer
• Bool
• Double
• String
• Datetime
ただし、すでにできたことで満足するべきではありません。この方法の可能性をすべて実現するには、ひじょうにシンプルな例でシステムの処理原理を考察する必要があります。
複合体は以下のように動作します。初期化段階においては、MQL4 プログラムは、ユーザーが対応するファイルに変更する全値を保存します。外部プログラムはこのファイルを開き、データを読み、画面にそれを配置します。そしてユーザーが作業を開始するのです。ユーザーが外部プログラムのパラメータの1つを変更するとすぐに、ファイルは修正されます。すなわち、古い値の代わりにユーザーによって設定される新しい値が書き込まれるのです。その後、関数 'start' の呼び出しで、MQL4 プログラムは変更された値に対して何か変更が見つかるかどうかデータファイルをチェックし、対応する関数を呼びます。外部値の変更を処理する関数はプリセットのアルゴリズムの一部を実行します。ほとんどの場合、変数に新しい値を割り当てるだけで十分です。これは複合体を変更する外部パラメータの1サイクルのおおよその動作の仕方です。
上記の例からは、MQL4 プログラムと外部プログラム間の安定したダイアログの作成は複合体が正常に動作するためにもっとも重要であることがわかります。ここでダイアログをまとめる手段と方法を選択する必要があります。ただ、DLL を使用しないひじょうに簡単な方法に対してでも、単なる外部パラメータの変更以上のことが可能です。
この方法の追加機能を表す例をいくつか考察します。対話ダイアログがあるので、MQL4 プログラムは外部プログラムからの依頼に答えることができます。
たとえば、ユーザーが変数値を変更します。外部プログラムはファイルに変数の新しい値を書き込み、この変数の状態を「MQL4 プログラムからのアクション待ち」に変えます。ファイルを処理する際、MQL4 プログラムはこの状態の変数のみ考慮に入れ、その他は変更されません。見つけられた変数を処理するとき、状態を「変更受け入れ済み」に変えます。よって、外部プログラムでは、MQL4 プログラム内で変更が行われたときを直接確認することができます。それはモニターするのに便利な関数です。
関数 "action" を実行してみます。外部プログラムで変数が実行されても MQL4 プログラムでの対応する変数が変更されず、特定のアクションは一度だけ実行されるようにする必要があります。これは以下のように実行されます。変数は外部プログラムで変更されます。その変数は 0~1 の値を取ります。値が 1 であれば、MQL4 プログラムは特定のアクションを実行し、変数に値 0 を割り当てます。このアクションは外部プログラムから再び行われる可能性があります。これは単なる変数変更以上のものです。このアクションによって、多くの便利な関数を実行することができます。アクションは MQL4 のスクリプトとほとんど同じですが、大きな違いが一つあります。スクリプトは MetaTrader4 クライアントターミナルの操作中いつでも実行可能です。一方、アクションは関数が呼び出された直後、すなわち現行価格変更時、に一度だけ処理されます。
最後に、外部ソースから MQL4 プログラムにデータを転送するメソッドの使用例を考察します。外部変数は変更する必要がなく、MQL4 プログラムというより EA は外部ソースから管理する必要がある、と仮定します。外部ソースがデータを処理し、計算を行い、取得した結果を基にオーダーのオープン/クローズを判断するのです。オーダーをオープンするには、主になるパラメータとオープンするコマンドをプログラムに渡す必要があります。変更がプログラムにアクションの実行を強要することはないため、オーダーパラメータは「待機中」状態です。この例では、オーダーオープンのパラメータを読み出すイベントはアクション変数です。
複合体を使用するにあたり、3つの側面を考察しました。第1は、外部プログラムから MQL4 プログラムへ一方向に状態のパラメータを渡すことです。この場合、ダイアログは状態の値を変えることで実行されます。第2は、特定のアクションを行う依頼です。ダイアログの中で、変数値と状態の値両方が変更されます。第3でもっともシンプルな側面はファイルに格納されたイベント情報の抽出です。ダイアログは使用されません。
複合体の作成
複合体を作成するには、数々の問題を解決しなければなりません。まず最初に、プログラム間で情報を転送するためのドキュメントストラクチャを作成する必要があります。それから、このドキュメントを転送するプロトコルを選択します。最後に、MQL4 または特定の外部プログラムに適したその他任意のプログラム言語と連携するライブラリを書きます。
ドキュメント ストラクチャの作成
ドキュメント ストラクチャの作成は複合体にとってはもっとも重要なタスクの一つです。ドキュメントは上述の側面すべてを反映するものでなければなりません。
情報格納のためには、XML 技術を使用するのがもっともユニバーサルな方法でしょう。ただし、そこには数多くの問題があります。まず、MQL4 には XML と連携するための組み込まれた手段がありません。次に、DLL によって XML と連携するなら、システム全体が数倍複雑なものとなってしまいます。よって、標準的なテキストファイルとデータ表現の独自のストラクチャを使用します。
ドキュメントの主な対象は変数または値です。値は以下のエレメントで構成される明確に定義されたストラクチャです。
• タイトル
• タイプ
• 状態
• 値
一つずつ考察します。
タイトル -変数名オブジェクトを特定するためにプログラムによって使用されます。
タイプ -変数タイプ。変数タイプに従って処理ロジックが変化します。タイプは数値で指定されます。
タイプ | 説明 | MQL での類似体 | 値 |
---|---|---|---|
0 | ブール型またはロジカル | bool | 0 -偽、1 -真 |
1 | String | string | 文字セット |
2 | 整数文字 | int | 境界値は MQL4 ルールに従います。 |
3 | 実際の特性 | double | 境界値は MQL4 ルールに従います。区切り文字はポイントです。 |
4 | 日付 | datetime | レコードの形式はルールに従います。 |
5 | アクション | 対応なし | 0 -実行は不要、1 -要実行 |
状態 -状態変数数値によって指定されます。可能なバリエーションのテーブルを考えます。
値 | 説明 |
---|---|
0 | 待機中処理は必要ありません。 |
1 | 変更依頼MQL4 プログラムはこの状態値に応答します。 |
2 | 処理は必要ありません。変数を直接変更する以外のイベントに対し処理される必要がある変数の値です。 |
値 -変数値。値をレコードするルールは変数タイプで決まります。
パラメータの相互作用
外部プログラム値変更
状態=0 の場合、「値」の値変更には、値は、状態=1。
状態= 1 の場合、「値」の値変更には、「状態」の値は変化しません。
状態= 2 の場合、「値」の値変更には、「状態」の値は変化しません。
外部プログラム値変更取り消し
値がまちがって変更されたり、誤った値で置き換えられる、または変更確認後、値変更に関してユーザーの気が変わった、という状況があります。この手順をキャンセルする機能を考察します。ただし、MQL4 プログラムがすでに変更情報を持つファイルを処理してしまっていたら、キャンセルはできないことを考慮する必要があります。
状態=1 であれば、「値」は前の値に変わり、状態=0 です。
状態=2 であれば、「値」は前の値に変わりますが、「状態」の値は変わりません。
MQL4 プログラム処理は変わります。
MQL4 プログラムは変数「状態」が 1 の場合のみ変更を考慮します。定義では、どの変数タイプが変更されていても問題ではありません。というのも、各変更に処理関数を呼んでいるためです。違いは、「値」が値をもっているかいないか、すなわち変数変更が変更なのかあるいはアクションの実行依頼なのか、にあるだけです。これを詳しくみていきます。
状態=1 かつ「タイプ」の範囲が 0~4 であれば、「値」を使い、「状態」を 0 に変えます。
状態=1 かつタイプ=5 であれば、アクションを実行し、「状態」を 0 に、「値」を 0 に変えます。
アクションの実行中に、状態=2 の追加の値を使用する場合、使用後「状態」の値は変わりません。
例をいくつか考察します。
例 1 変数の変更
整数タイプで値 10 の変数 Var1 があります。この変数は「待機中」状態です。
タイトル=Var1
タイプ=2
状態=0
値=10
外部プログラムでは、Var1 の値を 30 に変えます。取得するのは以下です。
タイトル=Var1
タイプ=2
状態=1
値=30
「値」の値は変わりました。また「状態」の値も「処理の変更依頼」に変わりました。
MQL4プログラムは変更を検出し、必要な関数を呼び出し、ドキュメントを修正します。
タイトル=Var1
タイプ=2
状態=0
値=30
「値」の値は受け取られ、「状態」の値は「待機中」に変わりました。
例 2 アクションの実行
アクションタイプの変数 Var2 があります。この変数は「待機中」状態です。
タイトル=Var2
タイプ=5
状態=0
値=0
外部プログラムでは、Var2 の値を 1 に変えます。少し先を見ると、外部プログラムのアクション変数に誤った値を割り当てるエラーを避けるために、フィールドではなくボタンを使うということが言えます。取得するのは以下です。
タイトル=Var2
タイプ=5
状態=1
値=1
「値」の値は変わり、「状態」の値も「処理の変更依頼」に変わっています。
MQL4プログラムは変更を検出し、必要な関数を呼び出し、ドキュメントを修正します。
タイトル=Var2
タイプ=5
状態=0
値=0
「値」の値は 0 に変わり、また「状態」の値は「待機中」に変わっています。
アクションの実行時、「状態」の値と「値」の値が両方変わるのがわかります。変数が変わる場合、「状態」の値だけが変わり、変数値は変わりません。
転送プロトコルの選択
MQL4 プログラムと外部プログラム間でのデータ転送の数多くの手段の中で、ファイルを選択します。ファイルを使用すると MT 4 に追加のライブラリを作成する必要がありません。メソッドはかなりユニバーサルで、フットプリントが小さく、実装が簡単です。デメリットは多くありますが簡単に排除でき、重大な制限はなにもありません。
MQL4 プログラムは特定のイベント、すなわち、現行通貨ペアにおける現行価格の変更、についてのみ変数ファイル内の変更に応答します。
いつでも外部プログラムから変数ファイルをチェックすることで開始できます。チェックには最適な時間間隔を選択し、PC のリソースに負荷がかかりすぎないように、またファイルを長期間占有しないようにします。それは同時に MQL4 プログラムによっても使用される可能性があるためです。外部プログラムでの変更は主に人によって行われます、すなわち、コンピュータのように速く行われないのです。よって、状況を追跡するには半秒に1度のファイルチェックで十分でしょう。もちろん、このパラメータは調整可能で、時間間隔はご自身で経験的に選択することです。
MQL4 プログラムでファイルを開こうとしているとき、書き込み用に外部プログラムによって開くことができます。そのような状況を見越して、1処理の間にファイル呼び出しを複数回実装し、新規の価格変更を待って時間を無駄にすることがないようにします。同じことが外部プログラムにも関連しています。ファイルが MQL4 プログラムによって使用されている場合、一定の時間間隔でのファイル呼び出し試行を取り入れます。
MQL4 への適応
変数
int ExVH_VarCnt; string ExVH_FileName; string ExVH_Project; string ExVH_Title[]; string ExVH_Type[]; string ExVH_Status[]; string ExVH_Value[];
ユーザー向け関数
bool ExVH_Open(string FileName) { bool Result=true; ExVH_FileName=FileName; if (!ExVH_i_Load()) Result=false; if (Result) if (!ExVH_i_GetVarCnt()) Result=false; if (Result) ExVH_i_Disassemble(); return(Result); } int ExVH_Close() { ExVH_i_Assemble(); ExVH_i_Save(); } int ExVH_GetStatus(int Id) { if ((Id<1)||(Id>ExVH_VarCnt)) return(-1); else return(StrToInteger(ExVH_Status[Id-1])); } int ExVH_SetStatus(int Id, int Status) { int Result; if ((Id<1)||(Id>ExVH_VarCnt)) Result=-1; else { Result=1; ExVH_Status[Id-1]=Status; } return(Result); } string ExVH_GetValue(int Id) { if ((Id<1)||(Id>ExVH_VarCnt)) return("N/A"); else return(ExVH_Value[Id-1]); }
内部使用関数
bool ExVH_i_Load() { bool Result=true; ExVH_Project=""; int i=0; int FS=0; int handle; int Buf[]; handle=FileOpen(ExVH_FileName,FILE_BIN|FILE_READ); if(handle>0) { FS=FileSize(handle); ArrayResize(Buf,FS); while(!FileIsEnding(handle)) { Buf[i] = FileReadInteger(handle, CHAR_VALUE); i++; } FileClose(handle); string Str=""; for (i=0;i<FS;i++) Str=Str+CharToStr(Buf[i]); ExVH_Project=Str; } else Result=false; return(Result); } bool ExVH_i_Save() { bool Result=true; int handle=FileOpen(ExVH_FileName,FILE_BIN|FILE_WRITE); if(handle>0) { FileWriteString(handle,ExVH_Project,StringLen(ExVH_Project)); FileClose(handle); } else Result=false; return(Result); } bool ExVH_i_GetVarCnt() { bool Result=true; string Value=ExVH_i_GetVarValue("Var_Cnt"); if (Value=="N/A") Result=false; else ExVH_VarCnt=StrToInteger(Value); return(Result); } void ExVH_i_Disassemble() { int i; ArrayResize(ExVH_Title, ExVH_VarCnt); ArrayResize(ExVH_Type, ExVH_VarCnt); ArrayResize(ExVH_Status, ExVH_VarCnt); ArrayResize(ExVH_Value, ExVH_VarCnt); for (i=0;i<ExVH_VarCnt;i++) { ExVH_Title[i]=ExVH_i_GetVarValue("Var"+(i+1)+"_Title"); ExVH_Type[i]=ExVH_i_GetVarValue("Var"+(i+1)+"_Type"); ExVH_Status[i]=ExVH_i_GetVarValue("Var"+(i+1)+"_Status"); ExVH_Value[i]=ExVH_i_GetVarValue("Var"+(i+1)+"_Value"); } } void ExVH_i_Assemble() { ExVH_Project="[ExVH 1.0]\r\n\r\n"; ExVH_Project=ExVH_Project+"Var_Cnt="+ExVH_VarCnt+"\r\n\r\n"; int i; for (i=0;i<ExVH_VarCnt;i++) { ExVH_Project=ExVH_Project+"Var"+(i+1)+"_Title="+ExVH_Title[i]+"\r\n"; ExVH_Project=ExVH_Project+"Var"+(i+1)+"_Type="+ExVH_Type[i]+"\r\n"; ExVH_Project=ExVH_Project+"Var"+(i+1)+"_Status="+ExVH_Status[i]+"\r\n"; ExVH_Project=ExVH_Project+"Var"+(i+1)+"_Value="+ExVH_Value[i]+"\r\n\r\n"; } } string ExVH_i_GetVarValue(string VarName) { string Result="N/A"; int Start,Stop; VarName=VarName+"="; Start=StringFind(ExVH_Project,VarName,0); if (Start!=-1) { Start=Start+StringLen(VarName); Stop=StringFind(ExVH_Project,CharToStr('\n'),Start); if (Stop!=-1) { Stop=Stop-1; Result=StringSubstr(ExVH_Project,Start,Stop-Start); } } return(Result); }
C++ 言語への適応
プロジェクトは C++ Builder 2007 で書かれています。ExVH_CPP.zip という名前のソースコードのアーカイブは本稿に添付があります。
検証
ほとんどの機能を示す小さな実証例を実装します。
そこで、検証ドキュメントを作成します。
[ExVH 1.0]
Var_Cnt=5
Var1_Title=Boolean test
Var1_Type=0
Var1_Status=2
Var1_Value=0
Var2_Title=String test
Var2_Type=1
Var2_Status=2
Var2_Value=Hello world!
Var3_Title=Integer test
Var3_Type=2
Var3_Status=0
Var3_Value=12345
Var4_Title=Double test
Var4_Type=3
Var4_Status=0
Var4_Value=123.45
Var5_Title=Action test
Var5_Type=5
Var5_Status=0
Var5_Value=0
フォルダ[MetaTrader]/experts/files/に ExVH.evh としてドキュメントを保存します。
署名 [ExVH 1.0] により、ファイルを開く際ドキュメントを認識でき、それが使用されているドキュメントのストラクチャバージョンを伝えてくれます。ドキュメントストラクチャが変われば、署名も変更になるのです。これにより、ドキュメントファイルの拡張子が変わらないことを考えると、混同が避けられます。
Var_Cnt=5. このレコードはドキュメントに変数が5個入っていることを伝えています。
そして、変数それぞれの情報を持つ同一タイプのレコードが続きます。レコードは上記の仕様に従って作成されます。
VarX_Title=
VarX_Type=
VarX_Status=
VarX_Value=
このため、アクション(Var5)が出現したらすぐに読みだされるべき変数は2つ(Var1 と Var2)あり、その変更が個別に考慮されるべき変数が2つ(Var3 と Var4)あります。
MQL4 プログラムは、変数内で変更が作成されるとすぐに画面に対応するメッセージを表示する必要があります。
検証 MQL4 コード
int init() { return(0); } int deinit() { return(0); } int start() { if (!ExVH_Open("ExVH.evh")) return(0); // Checking for action status if (ExVH_GetStatus(5)==1) { Alert("Actioned!"); string VarValue; if (ExVH_GetValue(1)=="1") VarValue="true"; else VarValue="false"; // Boolean variable value Alert("Boolean test variable="+VarValue); // String variable value Alert("String test variable="+ExVH_GetValue(2)); ExVH_SetStatus(5,0); } // Integer variable value if (ExVH_GetStatus(3)==1) { Alert("Integer test variable has been changed. New value="+ExVH_GetValue(3)); ExVH_SetStatus(3,0); } // Double variable value if (ExVH_GetStatus(4)==1) { Alert("Double test variable has been changed. New value="+ExVH_GetValue(4)); ExVH_SetStatus(4,0); } ExVH_Close(); return(0); }
起動と検証
複合体のどの部分を最初に起動するかは問題ではありません。この場合は MetaTrader 4 とします。事前にファイルExVH_Demo.mq4 をフォルダ [MetaTrader]/experts/ にコピーし、それからターミナルを起動します。ドキュメント ExVH.evh はすでにプログラムコード内に書き込まれています。この EA を起動します。 EA はファイル内の変更を待っているので、何も起こりません。
事前に PC にインストールされたExVH.exe を起動します。
プログラムの概観
「プロジェクト」を開き、項目...を「開く」を選択します。
ドキュメントを開きます。
プログラムは変数をロードし、これで変数の変更が可能です。
'Status' 列に値が2個反映されています。アイドル(待機中)と ExVH により変更済み(変更は外部プログラムによって行われました)、です。以下は「アクション」をアクティブにした後の画面表示です。
「アクション」をアクティブにしたときのプログラム概観
異なるタイプの変数に対する変更は別の方法で行われます。
ブール変数に対しては、プログラムは2つのメッセージの内の1つを出します。
「アクション」のアクティブ化には以下です。
その他タイプの変数すべてに対しては、統一された別の形式です。
変数値を変更する形式
数多くの変更を行うと、ターミナルの 'Alert' ウィンドウが以下のように表示されます。
プログラム処理中のメッセージ例
デメリット
どんなに良い考えにもメリットとデメリットがあるものです。
• このメソッドはバックテストに対しては積極的に使用できません。
• パラメータ変更がみなさんの EA ロジックと矛盾する可能性があります。変数内の変更はひじょうに注意して処理する必要があります。これはデメリットというよりは特徴です。
• コマンドの実行は新規ティック到着時のみ可能です。
その他行うこと
システムの機能性はさまざまな方向に強化することが可能です。アクションのフィールドは特に限りがありません。
• 異なるシナリオに従ったパラメータ変更たとえば、パスワードについて、入力情報を非表示にする機能を持つことができます。
• パスワード非表示を有効にするための暗号化されたファイルのストレージ
• プロトコルをより信頼性高く安全にしてデータ転送を変更することができます。
おわりに
MQL プログラムの内部変数値を変更する方法について考察しました。これでプログラムを再起動することなく、外部変数やその他の変数を管理する機会を得ました。
本稿添付ファイル
ファイル | 説明 |
---|---|
ExVH_Demo.mq4 | 検証 EA |
ExVH_Project.zip | 準備済みプロジェクトファイル |
ExVH_CPP.zip | プログラムのソースコード |
ExVH_Install.zip | 外部プログラムのインストーラー |
注意: ExVH_Install は高度なインストーラープログラムのデモバージョンを使って作成されています。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/1538





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