English Русский 中文 Español Deutsch Português 한국어 Français Italiano Türkçe
インディケータ間のデータ交換:簡単です

インディケータ間のデータ交換:簡単です

MetaTrader 5 | 5 10月 2015, 11:50
4 534 0
Alexey Subbotin
Alexey Subbotin

はじめに

初心者の方に感謝したいのは、彼らがかたくなに検索機能を使おうとしないため『よくある質問』、『新規利用者の方へ』、『このリストから質問する人は地獄行き』などというトピックが存在するからです。彼らが本当にするべきは「どうやって~するの?」、「~することは可能なの?」などと尋ねることですが、往々にして答えは「 まさか」とか「できるわけないよ」というものです。

永遠のフレーズ「ネバーセイ、ネバーアゲイン」は科学者、エンジニア、プログラマーが古いものを簡単に忘れ、何か新しいものを考え産み出すさまを表現しています。


1. 問題定義

MQL4のコミュニティ フォーラム(ロシア語からの翻訳)からの引用があります。

インディケータが2つあります。(それをAとBとしましょう)インディケータAは通常どおり価格チャートから直接データを使用し、BはインディケータAのデータを使用します。ここで質問です。「iCustom("インディケータA", ...)を使う代わりにBデータを動的に計算するにはどうすればよいでしょうか?」すなわち、インディケータAの設定をいくつか変更し、それがインディケータBのパラメータ変更に反映されるようにするということです。

言い換えると

チャートに移動平均を貼り付けると仮定します。そうすると、データバッファに直接アクセスするにはどうすればよいのでしょうか?

そして

... iCustomを使っていくつかのインディケータを呼ぶ場合、コールの前にロードされていたとしても再ロードされます。それを防ぐ方法はあるのでしょうか?

そういった質問は拡大する可能性があります。何度も繰り返し尋ねられます。しかも初心者からだけではありません。一般に、問題はMetaTraderがiCustom(MQL4の場合)またはiCustom+CopyBufferのバインディング(MQL5の場合)なしにカスタムインディケータデータにアクセスする手段がないことです。次の傑作を産み出すためMQLコードに関数をいくつか書くというのは心をそそられることです。それは指定のチャートからのデータを使ってデータを取得する、または何かを計算する関数です。

上述の標準関数による作業は便利とは言えません。例えばMQL4で、インディケータのコピーはiCustomが呼ばれるたびに作成されます。MQL5の問題はハンドルの使用により一部解決されました。そして計算はマスターコピーのために一度だけ行われます。しかしいまだにそれは解決策とはいえません。参照するインディケータが計算のリソースを消費しすぎると、事実上再初期化のたびに数十秒で端末はリソースを欠乏するからです。

コピー元にアクセスする方法がいくつか提供さているはずです。以下もそれに含まれるものです。

  • 物理的なディスクやメモリにファイルをマッピングする
  • データ交換にDLLを介してメモリーを共有する
  • データ交換と保存のためにクライアント端末のグローバル変数を使用する

その他のメソッドについては、上述メソッドのバリエーションです。またソケットやメイルスロットなどのような変わった方法もあります。Expert Advisorsでよく使用されるような基本的な手法もあります。インディケータ計算を直接Expert Advisorコードに変換するものですが、これは本稿では述べません。

著者の視点では、すべてのメソッドにはメリットと同時にデメリットもあります。データはまずどこかにコピーされ、それから他所に配布される、というような点です。第一にCPUのリソースを必要とします。次に送信されたデータの適切性に問題をもたらします。(ここではこの点については扱いません。)

では、問題を明らかにしていきましょう。

チャートに添付されるインディケータ データにアクセスし、次のようなプロパティを有する環境を作成したいと思います。

  • データをコピーしない(適切性に関する問題もコピーしない)
  • 使用可能なメソッドに対しては、使用する必要がある場合、最小限に手を加える
  • MQLコードの使用が好ましい(もちろんDLLを使う必要がありますが、C++コードのストリングをいくつか使うだけです)

DLL作成にあたり、著者はC++ビルダ、MetaTrader 4、MetaTrader 5を使用しました。以下に示されるソースコードはMQL5で書かれています。またMQL4コードについては本稿に添付しています。両者の主な相違点についてお話します。


2. 配列

第一にいくばくかのセオリーが必要です。というのも、これからインディケータ バッファで作業するため、そのデータがメモリ内でどのように配置されるか知る必要があるからです。この情報についてはうまくまとめられたものがありません。

MQLの動的配列は変数サイズを伴うストラクチャであるため、配列サイズが増し、配列直後にメモリの空がなかった場合、MQLがデータ再割り当ての問題にいかに取り組むかというのは今興味を引くところです。その解決策は2つあります。

  1. 新規データを使用可能なメモリーの追加部分に再割り当てる(参照リストを使うなどしてこの配列ぜんぶのアドレスを保存します。)
  2. 配列全体をひとかたまりとして、割当てに十分な余裕がある新しいメモリ領域に移動させる

最初の方法は後に問題が発生します。その理由はこの場合、MQLコンパイラにより作成されるストラクチャのデータを調査しなければならないからです。次の考察が二番目のバリアントを立証します。(それはよりゆっくりとしたものです):動的配列が外部関数(DELL)に渡されるとき、あとの配列はデータの第一エレメントにポインターを取得します。他の配列要素は整理され第一配列エレメントの直後に配置されます。

参照により渡されるとき、配列データには変更が加えられ、そのため最初の方法では外部関数にそのデータを送信するための別のメモリ領域に配列全体がコピーされる際問題が生じます。そして、結果はソース配列に追加されます。それは、二番目の方法でも起こりえます。

この結論が理論的に100%正しくなくても、多少の信憑性はあるはずです。(この考えに基づいたプロダクトを正しく処理させることで証明されます。)

そこで、以下の記述が正しいと仮定します。

  • どんなときでも動的配列のデータはメモリに一つずつ順番に配置され、配列はコンピュータメモリの別領域に再配置されますが、ぜんぶをまとめてではない
  • 第一配列エレメントのアドレスは、パラメータとして動的配列が参照によって外部関数に渡されるとき、DLLライブラリに渡される

またここでその他の仮説を考えます。ちょっとした時間、すなわち対応するインディケータのOnCalculate() (for MQL4 - start ())関数が呼ばれる瞬間です。なお単純化のため、ここでのデータバッファはdouble []と同一のディメンションとタイプであるとします。


3. 必須条件

MQLはポインタ(いわゆる、一般的ポインタではないオブジェクト ポインタをのぞいて)をサポートしません。それはMetaQuotes Softwareの代表者によってくりかえし記述され確認されてきています。それでは、これが正しいか確認します。

ポインタとはそもそも何でしょうか?アスタリスクを伴なう識別子というだけではなく、コンピュータメモリ内のセルアドレスです。では、セルアドレスとは何でしょうか?ある地点から始まる連続した数字です。そうすると最後に数字とは?コンピュータメモリのあるセルに対応する整数です。ならば、なぜポインタは整数番号のように扱うことができないのでしょうか?もちろんポインタを整数番号のように扱うことは可能です。MQLプログラムは整数値と完璧に連携しているのですから!

でもポインタを整数に変換する方法は?動的リンクライブラリを使うのです。C++ typecastingの可能性を利用するのです。 C++ポインタが4バイトデータ タイプなので、この場合、intを4バイトデータ タイプとして使うのは好都合です。

サインビットは重要ではありません。また、それに頼ることもしません。(もしサインビットが1に等しければ、整数がマイナスであることを意味するだけです。)重要なことはすべてのポインタビットに変更を加えないことです。MQL5とMQL4のコードが似通っていればサインなし整数を使用することはもちろん可能ですが、MQL4コードにはサインなし整数は含まれていないからです。

そこで、

extern "C" __declspec(dllexport) int __stdcall GetPtr(double *a)
{
        return((int)a);
}

どうでしょう。配列の最初のアドレス値でロングタイプの変数を得ました。これで、i-th配列値の読み方を学習すればよいだけです。

extern "C" __declspec(dllexport) double __stdcall GetValue(int pointer,int i)
{
        return(((double*) pointer)[i]);
}

そして、値を書くのです。(ここでの場合必ずしも必要ではありませんが。。。)

extern "C" __declspec(dllexport) void __stdcall SetValue(int pointer,int i,double value)
{
        ((double*) pointer)[i]=value;
}

以上です。これでMQLのポインタを使うことができるようになりました。


4. ラッピング

システムのカーネルを作成してきました。これからはMQLプログラムにおいてそれを使いこなす準備です。しかしながら、外見にこだわる方々、どうか心配しないでください。まだお伝えすることがあります。

やり方は星の数ほどあります。が、ここでは以下の方法でやっていきましょう。個別のMQLプログラム間でデータ交換をするため、クライアント端末は特別な機能を備えていることを思い出しましょう。それはグローバル変数です。インディケータバッファにポインタを保管するにはそれが最もやりやすい方法です。インディケータバッファにアクセスします。そういう変数を記述テーブルと考えます。デスクリプタにはそれぞれ以下のようなストリングで表される名前がついています。

string_identifier#buffer_number#symbol#period#buffer_length#indexing_direction#random_number,

そして、その値は、コンピュータメモリのバッファポインタに応じた整数表現と等しくなっています。

デスクリプタ フィールドの詳細の一部を以下に述べます。

  • string_identifier – あらゆるストリングです。(たとえば、インディケータ名short_name変数が使用可能です、など)必要なポインタを検索するのに使用されます。インディケータの中には同じ識別子でデスクリプタを登録するものもあり、フィールドを用いてお互い同士を識別します。
  • buffer_number – バッファを区別するのに使われます。
  • buffer_length – 境界を調整するのに必要です。それがないと、クライアント端末とウィンドウズのBlue Screen of Death はクラッシュする可能性があります。
  • symbol, period – チャートウィンドウを指定するシンボルと期間です。
  • ordering_direction – 配列エレメントの整列方向を指定します。0 :通常方向、1:逆方向(AS_SERIESフラッグが true)
  • random_number – クライアント端末にアタッチされた異なるウィンドウやパラメータセットが使われていてもインディケータのコピーがあるとき使用されます。(一番目と二番目の等しい値をセットすることができます。それは両者間をいくらか区別する必要があるからです。)最もすぐれた解決法ではありませんが、使えないことはありません。

最初に、デスクリプタを登録し削除する関数が必要です。関数の最初のストリングを見てみます。グローバル変数のリストに存在する古いデスクリプタを削除するには UnregisterBuffer()を呼ぶ必要があります。

各新規バーでは、バッファサイズが1ずつ増えていきます。そこで、RegisterBuffer()を呼ぶ必要があります。バッファサイズが変わるようなら、テーブル(サイズ情報はそのままの名前で保持されます)に新規デスクリプタが作成され、古いものはテーブルに残ります。それが使われるのはそういう理由からです。

void RegisterBuffer(double &Buffer[], string name, int mode) export
{
   UnregisterBuffer(Buffer);                    //first delete the variable just in case
   
   int direction=0;
   if(ArrayGetAsSeries(Buffer)) direction=1;    //set the right ordering_direction

   name=name+"#"+mode+"#"+Symbol()+"#"+Period()+"#"+ArraySize(Buffer)+"#"+direction;
   int ptr=GetPtr(Buffer);                      // get the buffer pointer

   if(ptr==0) return;
   
   MathSrand(ptr);                              //it's convenient to use the pointer value instead
                                                //of the current time for the random numbers base 
   while(true)
   {
      int rnd=MathRand();
      if(!GlobalVariableCheck(name+"#"+rnd))    //check for unique name - we assume that 
      {                                         //nobody will use more RAND_MAX buffers :)
         name=name+"#"+rnd;                     
         GlobalVariableSet(name,ptr);           //and write it to the global variable
         break;
      }
   }   
}
void UnregisterBuffer(double &Buffer[]) export
{
   int ptr=GetPtr(Buffer);                      //we will register by the real address of the buffer
   if(ptr==0) return;
   
   int gt=GlobalVariablesTotal();               
   int i;
   for(i=gt-1;i>=0;i--)                         //just look through all global variables
   {                                            //and delete our buffer from all places
      string name=GlobalVariableName(i);        
      if(GlobalVariableGet(name)==ptr)
         GlobalVariableDel(name);
   }      
}

ここでは詳しいコメントは不必要です。最初の関数がグローバル変数に上述フォーマットの新規デスクリプタを作成し、二番目の関数がすべてのグローバル変数内を検索して変数とポインタの値に等しいデスクリプタを削除するという概要を述べるに留まります。

では、二番目のタスクについて考察しましょう。インディケータからのデータ取得です。データへの直接アクセスを実装する前に、まず対応するデスクリプタを見つける必要があります。以下の関数を用いて行うことができます。そのアルゴリズムは下記のとおりです。すべてのグローバル変数にあたり、デスクリプタで指定されたフィールド値がそこにあるか確認します。

フィールド値を見つけたら、配列にその名前を追加し、最後のパラメータとして渡します。その結果、関数は検索条件に合うメモリアドレスをすべて返します。返される値はみつかったデスクリプタの数です。

int FindBuffers(string name, int mode, string symbol, int period, string &buffers[]) export
{
   int count=0;
   int i;
   bool found;
   string name_i;
   string descriptor[];
   int gt=GlobalVariablesTotal();

   StringTrimLeft(name);                                    //trim string from unnecessary spaces
   StringTrimRight(name);
   
   ArrayResize(buffers,count);                              //reset size to 0

   for(i=gt-1;i>=0;i--)
   {
      found=true;
      name_i=GlobalVariableName(i);
      
      StringExplode(name_i,"#",descriptor);                 //split string to fields
      
      if(StringFind(descriptor[0],name)<0&&name!=NULL) found=false; //check each field for the match
                                                                    //condition
      if(descriptor[1]!=mode&&mode>=0) found=false;
      if(descriptor[2]!=symbol&&symbol!=NULL) found=false;
      if(descriptor[3]!=period&&period>0) found=false;
      
      if(found)
      {
         count++;                                           //conditions met, add it to the list
         ArrayResize(buffers,count);
         buffers[count-1]=name_i;
      }
   }
   
   return(count);
}

関数コードにあるように、検索条件の中にはオミットされているものもあります。たとえば、名前としてNULLを渡した場合string_identifierのチェックはとばされます。その他のフィールドについても同じです。mode<0、symbol:=NULL または period<=0。それによりデスクリプタのテーブルにある検索オプションを拡げることができます。

たとえば、全チャートウィンドウですべての移動平均を見つけることが可能です。あるいは、M15期間におけるEURUSDチャートのみを検索する、などです。備考:string_identifierの確認は厳密な同等性確認の代わりにStringFind()関数によって行われます。デスクリプタの一部によって検索の可能性が得られます。(インディケータのいくつかが“MA(xxx)”タイプのストリングを設定する場合、サブストリングの“MA”によって検索を行うことが可能です。その結果、登録された全移動平均を見つけることができます。)

またStringExplode関数(string s、string separator、string &result[])を使いました。それは、セパレータを用いて指定されたstring sを下位ストリングに分離し、結果配列に結果を書き込む関数です。

void StringExplode(string s, string separator, string &result[])
{
   int i,pos;
   ArrayResize(result,1);
   
   pos=StringFind(s,separator); 
   if(pos<0) {result[0]=s;return;}
   
   for(i=0;;i++)
   {
      pos=StringFind(s,separator); 
      if(pos>=0)
      {
         result[i]=StringSubstr(s,0,pos);
         s=StringSubstr(s,pos+StringLen(separator));
      }
      else break;
      ArrayResize(result,ArraySize(result)+1);
   }
}

さて、必要なデスクリプタリストを得たら、インディケータからデータを取得することが可能です。

double GetIndicatorValue(string descriptor, int shift) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check that the descriptor is valid
   {                
      ptr = GlobalVariableGet(descriptor);             //get the pointer value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split its name to fields
         size = fields[4];                             //we need the current array size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //if the ordering_direction is reverse
         if(shift>=0&&shift<size)                      //check for its validity - to prevent crashes
            return(GetValue(MathAbs(ptr),shift));      //ok, return the value
      }   
   } 
   return(EMPTY_VALUE);                                //overwise return empty value 
}

ご覧のようにそれは、DLLからのGetValue()関数のラッピングです。配列の境界について、デスクリプタの有効期限を確認し、インディケータバッファ順序に配慮することが必要です。失敗したら、関数はEMPTY_VALUEを返します。

インディケータバッファの修正も同様です。

bool SetIndicatorValue(string descriptor, int shift, double value) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check for its validity
   {                
      ptr = GlobalVariableGet(descriptor);             //get descriptor value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split it to fields
         size = fields[4];                             //we need its size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //the case of the inverse ordering
         if(shift>=0&&shift<size)                      //check index to prevent the crash of the client terminal
         {
            SetValue(MathAbs(ptr),shift,value);
            return(true);
         }   
      }   
   }
   return(false);
}

すべての値が正しければ、DLLからSetValue()関数を呼びます。返された値は修正結果に対応しています。成功であればtrue、エラーの場合はfalseです。


5. 動作確認

それでは確認しましょう。目標は、標準パッケージからAverage True Range (ATR)を使用し、値を他のインディケータ ウィンドウにコピーするにあたり、修正箇所を示すことです。また、バッファデータの修正テストも行います。

まず行うのは、OnCalculate()関数にコード行をいくつか追加することです。

//+------------------------------------------------------------------+

//| Average True Range                                               |
//+------------------------------------------------------------------+
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 &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   if(prev_calculated!=rates_total)
      RegisterBuffer(ExtATRBuffer,"ATR",0);
…

示されるように、新規バーができると、毎回新規バッファ登録が行われます。ここでは時間の経過とともに(通常)起こります。または、追加の履歴データがダウンロードされる場合に起こります。

その他、インディケータの操作が終了すると、テーブルからデスクリプタを削除する必要があります。deinitイベント ハンドラにコードをいくらか追加する必要があるのはこのためです。(覚えておくべきは、それはボイドを返しconst int reason入力パラメータを持っていることです。)

void OnDeinit(const int reason)
{
   UnregisterBuffer(ExtATRBuffer);
}

クライアント端末のチャートウィンドウに修正されたインディケータ(図1)と、グローバル変数にそのデスクリプタ(図2)があります。

図1 平均的真の範囲

図1 平均的真の範囲

図2 クライアント端末のグローバル変数リストに作成されたデスクリプタ

図2 クライアント端末のグローバル変数リストに作成されたデスクリプタ

次のステップはATRデータへのアクセスです。以下のコードを用いて新規インディケータ(便宜的にテストと名付けます)を作成します。

//+------------------------------------------------------------------+
//|                                                         test.mq5 |
//|                                             Copyright 2009, alsu |
//|                                                 alsufx@gmail.com |
//+------------------------------------------------------------------+
#property copyright "2009, alsu"
#property link      "alsufx@gmail.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1

#include <exchng.mqh>

//---- plot ATRCopy
#property indicator_label1  "ATRCopy"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- indicator buffers
double ATRCopyBuffer[];

string atr_buffer;
string buffers[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ATRCopyBuffer,INDICATOR_DATA);
//---
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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[])
  {
//---
   int found=FindBuffers("ATR",0,NULL,0,buffers);   //search for descriptors in global variables
   if(found>0) atr_buffer=buffers[0];               //if we found it, save it to the atr_buffer
   else atr_buffer=NULL;
   int i;
   for(i=prev_calculated;i<rates_total;i++)
   {
      if(atr_buffer==NULL) break;
      ATRCopyBuffer[i]=GetIndicatorValue(atr_buffer,i);  //now it easy to get data
      SetIndicatorValue(atr_buffer,i,i);                 //and easy to record them
   }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

難しいことはありませんね。サブストリングによりデスクリプタを取得し、われわれの関数を使用してインディケータバッファを読みます。最後に、そこでの不要物をいくつか書き出します。(われわれの例では配列エレメント インデックスに対応する値を書きます。)

そしてテストインディケータを実行します。(ここで目標としているATRはまだチャートにアタッチする必要があります。)図3にあるように、ATR値はさらに下位のサブウィンドウ(ここでのテストインディケータでは)にあり、その代りに直線が描写されているのがわかります。実際には配列インデックスに対応している値が書き込まれています。

図3 Test.mq5動作の結果

図3 Test.mq5動作の結果

簡単ですね。以上でお終いです。


6. 下位互換性

著者はMQ4の標準になるべく近い形でMQL5にライブラリを作成しようとしました。MQL4で使うには数点の修正が必要です。

特にMQL5だけに存在するエクスポートキーワードを除去する必要があります。そして、間接のtypecastingを使ったコードを修正する必要があります。(MQL4はあまり柔軟性がないので)実際には、インディケータで関数を使用するのはまったく同じです。異なる点は新規バーと履歴追加コントロールの手法だけです。


おわりに

役に立つことを数点だけお話します。

本稿で述べたメソッドの利用は本来の目的だけに限られたものではないでしょう。高速カスケードインディケータの構築に加え、Expert Advisorsには、履歴テストも含めライブラリもうまく適用できるはずです。その他のアプリケーションについては著者がイメージするには難しいものですが、私が思うに、ユーザーのみなさんはこの方向で楽しんで作業されることでしょう。

以下の技術的節目が必要になるのではないかと考えます。

  • デスクリプタ テーブルの改善(特にウィンドウのインディケーション)
  • デスクリプタ作成順序に対する追加テーブルの開発。それはトレーディングシステムの安定性にとって重要でしょう。

ライブラリの改善に関するいかなるご提案もいただけるとありがたく思います。

必要なファイルはすべて本稿に添付しています。MQL5およびMQL4のコード、 exchng.dllライブラリコードも添付にあります。ファイルはクライアント端末のフォルダと同じ場所にあります。


免責条項

本稿で掲示されている資料はプログラムテクニックを述べるもので、不注意な使用はお使いのコンピュータソフトウェアに障害を与える可能性があります。添付ファイルをご使用の際は、これまで報告されていない副次的影響の可能性を考慮ください。


謝辞

著者はMQL4コミュニティフォーラムhttp://forum.mql4.com にて提示された問題点やユーザーによる案 igor.senych、satop、bank、StSpirit、TheXpert、artmailru、ForexTools、 marketeer、IlyaAを使用しました。リストが不完全な場合はご容赦ください。

MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/19

添付されたファイル |
cpp.zip (0.36 KB)
mql4.zip (39.29 KB)
mql521x.zip (46.87 KB)

この著者による他の記事

MQL5におけるインディケータemissionの描写 MQL5におけるインディケータemissionの描写
本稿では、マーケットリサーチの新手法であるインディケータ エミッションについて考察していきたいと思います。エミッションの計算は異なるインディケータの交点が基本になります。ティックのあとに異なる色や形のポイントが多数表示されます。それらは星雲、雲、軌道、線、アーチなど様々なクラスターを形成します。こういった形は市場価格を左右する見えないバネや力を検出するのに役立ちます。
データの交換方法:10分で書くMQL5のためのDLL データの交換方法:10分で書くMQL5のためのDLL
シンプルなDLLを書く方法や異なるシステム バインディングの特殊機能は何かを覚えている開発者はあまりいないと思います。いくつか例を挙げながら、10分でDLLを書く手順を網羅するとともに、バインディング実装の技術的詳細もいくらか説明していきたいと思います。異なるタイプの変数(数値、配列、ストリングなど)に置き換えた例を取り上げ、ビジュアル スタジオでDLLを作成する手順を段階的にお見せしていきます。それに加え、カスタムDLLのクラッシュからクライアント端末を保護する方法についてもご説明します。
МetaTrader 5からWCFサービスを利用し.NETアプリケーションにクオートをエクスポートする方法 МetaTrader 5からWCFサービスを利用し.NETアプリケーションにクオートをエクスポートする方法
MetaTrader 5からご自身のアプリケーションにクオートをエクスポートしたくありませんか?MQL5-DLLジャンクションを使用するとそのソリューションの作成が可能です!本稿でMetaTrader 5から.NETに書かれたアプリケーションにクオートをエクスポートする方法の一つをお伝えします。私はまさにこのプラットフォームの使用により、クオートのエクスポート実装がおもしろく、根本的にやさしく行えました。残念ながらバージョン5はまだ.NETをサポートしていないので、かつてのように.NETサポート機能のあるwin32 dllを interlayerとして利用します。
プライスヒストグラム(マーケット特性)とMQL5への導入 プライスヒストグラム(マーケット特性)とMQL5への導入
「マーケットプロファイル」は非常に優秀な思索家、 ピーター・スタイドルマイヤー氏によって展開されました。氏は全く異なるモデルセットに導く『水平的な』また『垂直的な』マーケットの動きに関する情報の別の解釈の仕方を提案しました。潜在するマーケットの拍動、または均衡と不均衡の周期と呼ばれる基本パターンがある、と仮定したのです。本記事では、プライスヒストグラムについて考察していきたいと思います。それは、マーケットプロファイルの最も単純なモデルです。また、その MQL5への導入について述べていきたいと思います。