
MetaTrader 4とMatlabのDDEによる連携
はじめに
MatlabからMetaTrader 4へCSVファイルを用いてのデータの移送方法 (MT 4 <-CSV->Matla)についてすでに記事にしてい公開しています。しかし、その記事で紹介したアプローチは多くの場合、実行不可能で、受け入れ難いものです。
MT4でのDDE(Dynamic data exchange)のメカニズムは、一つのアプリから他のアプリでのRAMを通したデータの移送を可能にします。Matlabは、DDEのフロントとバックエンドの両方を実現しており、これを最大限活用したいと思います。
MetaTrader 4 DDEサーバーは、最新ティックデータのみを提供しています。しかし、そのような制限を考慮しても、DDEは例えばバーで取引価格を扱う際には好まれます。
"MT 4 <-CSV->Matlab"という記事のように交換の管理ツールの作成を紹介します。
MetaTrader 4 Client Terminalの Tools-> Options -> ServerタブからDDEサーバを使用できるようにすることを忘れず始めてください。
DDEについて
DDEを用いたデータの交換において、接続が構築される二つのポイントがあります。(フロントエンドとバックエンド)フロントエンドは、データを必要とするアプリケーション(この場合、Matlab)、バックエンドはこのデータを持つアプリケーション(MT4)です。
このデータは、サーバからDDEを経由して三つの方法でクライアントに送ることができます
- クライアントのリクエスト、
- クライアントのリクエストとサーバがデータの移送準備が整った通知をした後、
- データ送信の準備が整い次第、の三つです。
MetaTrader 4 DDEサーバは、一つの方法でのみ稼働し、準備されたデータをリクエストや確認などなしでクライアントに送ります。=) そのため、Matlabのタスクは、MT4にクライアントが存在し、どのデータが必要とされることを通知し、データが到着するまで待つことです。
データが到着すれば、グラフにて表示します。
GUIの作成
Matlabの環境では、グラフィカルユーザーインターフェスを作成できます。GUIを作成すれば、コントロールや価格チャート、表示する必要があると思うテキスト情報を組み込みます。
GUIの作成は、“MT4 <-CSV->Matlab”のセクション3にて詳細に記していますので、GUI Creation Wizardを立ち上げる"guide"というコンソールコマンドのみ述べ、必要なグラフィカルオブジェクトのリストを提示しておきます。
必要なものは以下です:
- 通貨ペア名を入力するため、入力ボックス “Edit Text”;
- チャートを表示するための“Axes”;
- 最後の取引価格の正確な値を表示するためのテキスト表示フィールド"Static Text"二つ
GUIシートのそのオブジェクトをどのように配置したかが以下に示されています:
以下の通りにグラフィカルオブジェクトのプロパティを設定しなければなりません:
Axes:
Tag = axesChart (チャートをここに表示します);
Box = on – 長方形でチャートの区域を囲みます、off - チャートの左と下を線で囲みます;
FontSize = 7 (標準のサイズは大きいです);
Units = pixels (1対1のスケールを設定するために必要です).
For EditText:
Tag = editPair (このフィールドに通貨ペア名を入力します).
EditTextフィールド下StaticText:
Tag = textBid (最後の取引価格の正確な値を入力します);
HorizontalAlignment = left (これはそこまで重要ではなく、'center'にしておいてください).
シートの下部StaticText:
Tag = textInfo;
HorizontalAlignment = left.
やっとRUNを押すことができます。
"DDEs"というプロジェクト名にしましたので、もし私のバージョンと一致していれば、同様の名前にしておいてください。
もしGUIが使いやすそうで、m-fileの編集準備ができていれば、DDEクライアントの作成を始めましょう。
接続の初期化
まず、GUIの立ち上げ時にサーバとの接続を行うためにチャネルを作成し、インターフェースを閉じた際に接続の切断を行うようにします。
Matlabでは、DDEの接続は、以下の関数によって初期化されます channel = ddeinit('service','topic'),
where:
‘service’ – DDE サーバー名 (‘MT4’)
'topic’ – データ選択名この場合、'BID', ‘ASK’, ‘QUOTE’などの値を取得できます。
その関数は、初期化されたチャネルの記述子を返します。その記述子は、DDEとの通信に使用されます。
データ通信メソッドも明記する必要があります。Matlabでは、MT4によってサポートされている通信メソッドは、"Advisory link"と呼ばれ、以下の関数によって初期化されます: rc = ddeadv(channel,'item','callback','upmtx',format);,
where:
channel – 初期化されたチャネルの記述子
‘item’ – 通貨ペアのシンボル名の必要なデータ,
'callback' – サーバからのデータの到着後に実行されるコード
'upmtx' – サーバから受け取られるデータの配置のためのシンボル名の変数
format – 送られるデータのフォーマットを定義する二つのフラッグの配列
関数 ddeadv は、 “1”を成功すれば返し、そうでなければ “0”を返します。
シンボルの表記は関数記述子ではなく'callback'パラメータとして提供されます。まるでコンソールに入力されるかのようにその行を実行する"eval"関数を実行します。この特徴は以下の問題を生み出します:新しい取引価格が到着すると、新しい取引価格を受け取る関数を実行する必要があります。同時に、この関数にGUIグラフィカルオブジェクトにアクセスするために使用される"handles"記述子ストラクチャーを渡したいと思います。しかし、実行可能なコードにハンドルストラクチャー記述子を渡すメソッドや、GUIを描画するm-fileに位置する関数を呼ぶ方法も見つかりませんでした。
この結果、個別のm-fileに新しい取引価格受け取り関数を配置し、標準のMatlab関数としてよばなければなりませんでした。しかし、その不便さはDDEクライアントの処理を妨害せずにその関数を編集できるということを知った後、アドバンテージに変わりました。
従って、まず受け取られたデータをコンソールに表示する個別の加工関数を作成しましょう。
関数newTick(simbols) % 新しいディックの加工 disp(simbols); % コンソールに表示します。 song = wavread('C:\WINDOWS\Media\Windows XP - launch.wav'); % 音を読み込みます。 wavplay(song,40000); % p40 kHzのサンプリングレートで音を再生します
上の関数は、'C:\WINDOWS\Media\Windows XP - launch.wav' ファイルを新しい取引価格が到着次第、再生します。MATLABディレクトリにその関数テキストをnewTick.m として保存してください。
GUIの動作を記述しているm-fileを編集しましょう。DDEs_OpeningFcn関数にその接続の初期化を追加してください。ディイニシャライぜーションは、 figure1_CloseRequestFcn関数に追加されます。
(CloseRequestFcn関数を m-fileに追加するためには、GUIエディターの以下を実行する必要があります: View -> View Callbacks -> CloseRequestFcn).
% --- DDEが見えるようになる前に実行します。 function DDEs_OpeningFcn(hObject, eventdata, handles, varargin) % この関数はアウトプットのアーギュメントとはありません、OutputFcnをご覧ください。 % hObject ハンドル % eventdata reserved - MATLABの将来のバージョンに定義されます。 % ハンドルのあるストラクチャーとユーザーデータを扱います(GUIDATAをご覧ください) % DDEへのvarargin コマンドラインアーギュメント(VARARGINをご覧ください) channel = ddeinit('MT4','QUOTE'); % 初期化 pair = get(handles.editPair,'UserData'); % シンボル名を読み込んでください rc = ddeadv(channel, pair,'newTick(x)','x',[1 1]); % 接続を構築します。 if (rc==1) % 接続が構築された場合, disp('Connected'); コンソールに通知します。e end handles.chann = channel; % チャンネルIDをハンドルに保存します。 % DDEの標準コマンドラインアウトプットを選択します handles.output = hObject; % ハンドルストラクチャーを更新します guidata(hObject, handles); % UIWAIT はDDEにユーザーの反応まで待機させます(UIRESUMEを見てください) % uiwait(handles.figure1);
% --- 図1を選択しようとユーザーが試みる際に実行します。 関数 figure1_CloseRequestFcn(hObject, eventdata, handles) % 図1へのハンドル hObject (GCBOをご覧ください) % eventdata reserved - MATLABの将来のバージョンで定義されます。 % ユーザーでたとハンドルのあるストラクチャーを扱います (GUIDATAをご覧ください) channel = handles.chann; % ハンドルからチャネルIDを取得します pair = get(handles.editPair,'UserData'); % シンボル名を読み込みます ddeunadv(channel,pair); % 接続切断 rc = ddeterm(channel); % ディイニシャライゼーション if (rc==1) % すべてOKの場合 disp('Disconnected'); % コンソールを通知します end % Hint: delete(hObject) はその図を閉じます delete(hObject);
% --- すべてのパラメーターを設定した後、オブジェクト作成中に実行します 関数 editPair_CreateFcn(hObject, eventdata, handles) % editPairに対するhObjectハンドル (GCBOをご覧ください) % eventdata reserved - MATLABの将来のバージョンで定義されます。 % handles empty - すべてのCreateFcnsが呼ばれ終わるまでにハンドルは作成されません set(hObject, 'String', 'EURUSD'); % 入力フィールドにシンボル名を入力します set(hObject, 'UserData', 'EURUSD'); % UserData内に保存します。 % Hint: 編集コントロールは、ウィンドウズにて普通白の背景を表示しています。. % ISPCとCOMPUTERをご覧ください。 if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor')) set(hObject,'BackgroundColor','white'); end
Matlab開発者によるからのスケルトン関数のために準備されたテキストとともに、修正関数のすべてのテキストを上で提供しました。
最後のブロックはGUIが立ち上げられる前に一致するフィールドにシンボル名を入力します。そのエントリーは、'UserData'プロパティにコピーされます。常に'UserData'のコピーを用います。一方、もしユーザーがセキュリティを変更しようとする場合、フィールド('String')に表示sれた名前のみを用います。もしそのユーザーが入力時に間違っており、異なった名前が'String'に記述されていれば、'UserData'に保存されている名前に戻ります。
以下のコードは、ユーザーのシンボル名の変更の関数を実現しています:
関数 editPair_Callback(hObject, eventdata, handles) % hObjectに対するhObjectハンドル (GCBOをご覧ください) % eventdata reserved - MATLABの将来のバージョンにて定義されます % ハンドルとユーザデータのあるストラクチャーを扱います (GUIDATAをご覧ください) oldPair = get(hObject,'UserData'); % 以前のシンボル名 newPair = get(hObject,'String'); % 新しいシンボル名 channel = handles.chann; % チャネルIDを取得します disconn = ddeunadv(channel,oldPair); % 接続切断する if (disconn==0) % もし 接続の切断に失敗した場合 set(hObject,'String',oldPair); % 入力フィールドに古いシンボル名を復元します else % もし接続が切断された場合 conn = ddeadv(channel, newPair,'newTick(x)','x',[1 1]); % 新しい接続を構築します if (conn==1) % もしその接続が構築された場合 set(hObject,'UserData',newPair); % どのシンボルが使用されるか記憶します else % 新しい接続を構築失敗した場合 ddeadv(channel, oldPair,'newTick(x)','x',[1 1]); % 古いものを復元します set(hObject,'String',oldPair); % 入力フィールドに古いシンボル名を復元します end end % Hints: get(hObject,'String')は、テキストとしてeditPairsのコンテンツを返します。 % str2double(get(hObject,'String'))は、editPairをdouble型でコンテンツを返します
ティックの受け取り
その接続が構築され、新しいティックの到着時に、その"newTick(x)"関数がMT4からコンソールに受け取られる引数を隠すと考えます。まず、GUIの一致する行に最後に受け取られた取引価格を表示しましょう。
このため、GUI グラフィカルオブジェクト記述子のストラクチャーを持つ必要があります。ハンドルはnewTick"関数を自由に使用できます。そのアプリケーションドメインにデータを保存するsetappdata(h,name,value) 関数を使用しましょう。"0"をアプリケーションIDに明記してください。それは、Matlabオブジェクトの記述子'root'で、固定的です。
"DDEs_OpeningFcn"関数のヘディングの後に:"setappdata(0,'hndls',handles);"を追加します :
関数 DDEs_OpeningFcn(hObject, eventdata, handles, varargin) setappdata(0,'hndls',handles); %
"newTick"関数にて、 value = getappdata(h,name)関数によりハンドルを抜き出し、"h"の引数として"0"を明記します。 "newTick" 関数からGUIオブジェクトを管理することができるようになります。
そして、DDEサーバーからその関数に渡されるString型引数を変形させ、GUiのBid値を表示させます。さらに、取引価格の受け取りの時刻を発見し、GUIステータスバーにそれを表示します。DDEサーバは、ティックを扱う際には受け入れることができない、分刻みの正確な時間を渡すので、ローカル時刻が必要です。'now' 関数は、ミリ秒単位でローカル時刻を返すので、異なるティックが同じ固定された時間を持つことについては心配する必要はありません。DDEサーバから受け取られた行からサーバー時刻を抽出し、Matlabの時間形式に変換します。.
以下は、 "newTick"関数のもう一つの例です:
関数newTick(simbols) % NEW TICK PROCESSING timeLocal = now; % 正確なローカル時間を発見します handles = getappdata(0,'hndls'); % ルートからハンドルを受け取ります % disp(simbols); % コンソールに引数を挿入します song = wavread('C:\WINDOWS\Media\Windows XP - launch.wav'); % サウンドを読み取ります wavplay(song,40000); % 40kHzのサンプリング率でサウンドを再生します set(handles.textInfo,'String', datestr(timeLocal)); % GUIでローカル時間を表示します % --- MT 4 から受け取った行を変換します--- parts = sscanf(simbols, '%i/%i/%i %i:%i %f %f' ); % 以下の形式に沿ってその行を読み取ります %フォーマット: int/int/int int:int float float timeServerVect = parts(1:5); % 時刻を抽出します timeServerVect = timeServerVect'; % (カラムを一行に変換) timeServerVect = [timeServerVect 00]; % 秒の追加 timeServer = datenum(timeServerVect); % Matlabの時刻フォーマットに変換します Bid = parts(6); % Bidを抽出します Ask = parts(7); % Askを抽出します % --- 変換の終了 --- set(handles.textBid,'String',['Bid: ' num2str(Bid)]); % BidをGUIに表示します
ティックチャートの描画
上記からの"newTick"関数の続きです・そのコードは、詳細なコメントが付いているので、理解するのに困らないと思います。
BIdの取引価格配列が7rootオブジェクトエリアに保存されていますが、"data"オブジェクトとして保存されているということだけ説明します。保存されているそのデータは、二つのフィールドからなりたちます。:
data.name - 通貨ペアシンボル名;
data.array - 取引価格の配列.
"newTick"関数において、このデータは"ticks"という名前の下に記載され、そのストラクチャーのフィールドは、ticks.nameやticks.arraytとそれぞれ名前を持ちます。
ticks.array は三つのカラムからなる配列を示します。:
- Matlab時間フォーマットのローカル時間 ( Matlab [microseconds]により正確さを保ちます);
- サーバー時間 Matlab時間フォーマットのローカル時間 ( 分単位の正確sさです);
- Bid.
"newTick" 関数は、"editPair"フィールドのシンボル名が変更され、その他のシンボルの都r匹価格が到着し始めた場合、その取引価格の配列を空にします。もしそれが何も変更されていなければ、既存の配列に追加されます。
そのチャートのオペレーションブロックは、axesChart ウィンドウのパラメーター(sizeとposition)を定義し、それらをピクセル単位でそのウィンドウから抽出します。これは1ピクセルごとに1取引価格を表示する横縮尺を設定するために必要です。
もしウィンドウの幅のピクセルよりも少ない取引価格があれば、そのチャートは全体で表示されます。もしそのピクセル数よりも多ければ、チャートに合うよう最新のデータのみ表示されます。
% --- working with quotes array --- GUIpairName = get(handles.editPair, 'UserData'); % シンボル名 if (~isappdata(0,'data')) % データがなにもない場合 ticks.name = GUIpairName; % ticks.array = []; % form a field - 空の配列 setappdata(0,'data',ticks); % ルートにそのデータを書き込みます end ticks = getappdata(0,'data'); % データを抽出します if ~strcmp(ticks.name,GUIpairName) % もし名前が変更されれば ticks.name = GUIpairName; % 名前フィールドを形成します ticks.array = []; % 空の配列のフィールドを形成します setappdata(0,'data',ticks); % データをルートに書き込みます end ticks.array = [ticks.array; timeLocal timeServer Bid]; % 行を追加します % 既存のデータ配列に新しいデータを含めます setappdata(0,'data',ticks); % そのデータをルートに書き込みます % --- その配列の取り扱い終了 --- % --- チャートを扱う --- chartSize = get(handles.axesChart,'Position');% チャートウィンドウサイズを取得します chartSize = chartSize(3); % チャートのウィンドウ幅を抽出します lenArray = size(ticks.array); % そのデータ配列のサイズを取得します lenArray = lenArray(1); % そのデータ配列のデータを抽出します set(handles.axesChart, 'NextPlot','replace'); % 描画モード - % 新しいものと古いチャートを取り替える if (chartSize >= lenArray) stairs(handles.axesChart,ticks.array(:,3)); % チャート全体を描きます else stairs(handles.axesChart,ticks.array(lenArray-chartSize+1:lenArray,3)); % チャートにフィットされている最新のデータを表示させます end set(handles.axesChart,'XLim',[1 chartSize]); % 1ピクセル幅に1秒でスケールを設定します % set(handles.axesChart, 'NextPlot','add'); % 描画モード - plot(handles.axesChart,[1 chartSize], [Bid Bid],'m');% Bid水平線を描きます
ファイルにデータを保存する
最後に紹介する関数は、ユーザーのリクエストによりファイルにティックデータを保存します。
ボタンを押すとデータを保存します。エディターを用いてGUIに"Push Button"オブジェクトを追加します。
以下のオブジェクトプロパティを設定します: Tag = pushSave, String = Save.
"M-file Editor"ボタンを押すと、pushSave_Callbak関数のテンプレートが"DDEs.m"の文末に自動的に追加されます。
以下は、データを保存する関数の全テキストです。
% --- Executes on button press in pushSave. function pushSave_Callback(hObject, eventdata, handles) % pushSave へのhObject ハンドル (GCBOをご覧ください) % eventdata reserved - MATLABの将来のバージョンで定義されます。 % ユーザーデータやハンドルを扱うハンドルストラクチャー (GUIDATAをご覧ください) date = datestr(now,'yyyy-mm-dd'); % 日付を取得します (string) time = datestr(now,'HH-MM-SS') % 時間を取得します (string) name = get(handles.editPair,'UserData');% シンボル名を取得します (string) template = [name '@' date '@' time]; % ファイル名を作成します [userName, userPath] = uiputfile([template '.txt']); % ユーザーから名前とパスを取得します if userName~=0 % もし "Cancel" が押されなければ、 ticks = getappdata(0,'data'); % ルートからデータを取得します timesStr = datestr(ticks.array(:,1)); % 時間と日付のString配列を作成します % bidStr = num2str(ticks.array(:,3)); % BIDと名付けられたString配列を作成します delimStr(1:length(bidStr)) =' ' ; % "column" セパレーターを作成します % カラムに置き換えられる行を作成します matrix=[timesStr delimStr' bidStr]; % すべてのStrを一つの行列に集約します dlmwrite([userPath userName], matrix, '');% 一つのファイルにその行列を保存します end
その関数は、日付、時間、シンボル名からなるファイル名を準備します。
保存する際は、そのシンボルの行列が準備されます:
- timesStr -- 取引価格に一致するローカル時刻と日付;
- delimStr -- デリミター;
- bidStr -- BID カラム.
それらすべてが一つの行列に統合されます。
delimStr は、スペースからなる行を示します:その行の長さは、BIDカラムの長さに等しいです。統合時に、delimStr行はカラムに置き換えられ、その時刻から取引価格のカラムを分けます。
まとめ
上記のメソッドが自動トレーディング戦略のテストや開発のためのMatlabの数学的関数を使用の役に立つことを願っています。
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1528




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