English Русский 中文 Español Deutsch 日本語
Interação entre o MetaTrader 4 e Matlab via DDE

Interação entre o MetaTrader 4 e Matlab via DDE

MetaTrader 4Exemplos | 5 fevereiro 2016, 12:36
771 0
Dmitriy
Dmitriy

Introdução

Já publiquei um artigo sobre a troca de dados entre o MetaTrader 4 e Matlab via arquivos CSV (MT 4 Matlab) aqui. Entretanto, a abordagem descrita no artigo é, em muitos casos, impraticável e frequentemente até mesmo inaceitável.

O mecanismo de DDE (troca de dados dinâmica) suportado no MT4 nos permite transferir dados de um aplicativo para outro diretamente através da RAM do computador. O Matlab tem a funcionalidade completa para dar conta do front-end e do back-end de DDE, então gostaríamos de tirar o melhor dessa oportunidade.

A DDE do MetaTrader 4 fornece somente os dados de tick mais recentes. Entretanto, até mesmo considerando tais limitações, a DDE é mais preferível ao por exemplo, trabalhar com cotações dentro de barras.

Como no artigo "MT 4 Matlab", vou descrever a sequência de criar uma ferramenta de organização de troca.

Não esqueça de habilitar o servidor de DDE em Tools (Ferramentas)-> Options (Opções)-> Server tab (Aba servidor) no seu terminal de cliente do MetaTrader 4 e podemos começar.


Sobre DDE

Então, na organização de troca de dados usando DDE, há dois ends (front-end e back-end) entre os quais a conexão é estabelecida. O front-end é um aplicativo que requer dados (Matlab, no nosso caso), o back-end é um aplicativo que tem seus dados a sua disposição (MT4).

Os dados podem ser transferidos do servidor para o cliente via DDE de três formas:
-pela solicitação do cliente,
- pela solicitação do cliente e depois que o servidor notificou que os dados foram preparados para transferência, ou
- depois que os dados estiverem prontos para transferência.

A DDE do servidor do MetaTrader 4 trabalha somente em um (o terceiro) modo e envia os dados prontos para o cliente sem esperar por solicitações, confirmações e outras coisas o tipo. =) Então a tarefa do Matlab é notificar o MT4 que tem um cliente, informar sobre quais dados são necessários e esperar até que os dados cheguem.

Assim que os dados chegarem, iremos mostrá-los em um gráfico.

Criando uma GUI

No ambiente do Matlab, há a possibilidade de criar uma interface de usuário gráfica (GUI). Uma vez criada a GUI, iremos combinar todos os controles, gráficos de preço e informações de texto que pensamos ser necessárias exibir.

Os passos para criar uma GUI estão descritos com mais detalhes na seção 3 do artigo “MT4 Matlab”, então irei apenas mencionar aqui o comando de console nomeado "guia" que abre o GUI Creation Wizard e também darei a lista dos objetos de gráficos que precisamos.

Assim, precisamos do seguinte:
- caixa de entrada "Edit Text" ("Editar Texto") com o nome do par cambial;
- 'Axes" (“Eixos”) exibindo o gráfico
- dois campos de saída de texto 'Static Text" ("Texto Estático") exibindo o valor preciso da última cotação ou para alguma outra função.

Abaixo, mostramos como posicionei os objetos em uma planilha GUI:


Você deve configurar as propriedades do objeto de gráfico como segue:

Para Axes (Eixos):
Tag = axesChart (mostraremos o gráfico aqui);
Box = on –inclua a área do gráfico em um retângulo cheio, off – inclua área do gráfico com uma linha à esquerda e uma linha ao fundi;
FontSize = 7 (o tamanho padrão é simplesmente enorme);
Units = pixels (precisaremos disso ao montar o gráfico para configurar a escala em 1:1).

Para EditText:
Tag = editPair (iremos inserir o nome do par cambial nesse campo).

Para StaticText abaixo do campo EditText (Editar Texto):
Tag = textBid (iremos inserir o valor preciso da última cotação aqui);
HorizontalAlignment = left (esquerda) (não é muito importante, você pode deixar como 'center' ("centro").

Para StaticText no fundo da planilha:
Tag = textInfo;
HorizontalAlignment = left (esquerda).

Agora podemos pressionar RUN.
O nome do projeto é "DDEs", então se desejar que sua versão não tenha nenhuma discrepância com a minha, por favor, coloque o mesmo nome no seu projeto.
Se a aparência da sua GUI parece apropriada para você e o m-file estiver pronto para editar, vamos começar a criar um cliente de DDE.

Inicializando a conexão

Antes de tudo, você deve organizar o canal para conectar com o servidor ao abrir a GUI e tomar cuidado com quedas de conexão ao fechar a interface.
No Matlab, a conexão de DDE é inicializada pela função: channel = ddeinit('service','topic'),
onde:
‘service’ – nome do servidor de DDE (‘MT4’)
'topic’ – nome de uma seção de dados. No nosso caso, pode tomar os valores de 'BID', ‘ASK’, ‘QUOTE’, etc.
A função retorno para o descritor do canal inicializado. Esse descritor será uado para conversas adicionais com o servidor de DDE.

Você deve também especificar o método de troca. No Matlab, o método de troca suportado pelo MT4 é chamado "Advisory link" e inicializado pela função: rc = ddeadv(channel,'item','callback','upmtx',format);,
onde:
channel– descritor do canal inicializado,
‘item’ – dados pelos quais estamos interessados, ou seja, o nome do símbolo de um par cambial,
'callback' – uma linha para ser executada depois da chegada dos dados do servidor,
'upmtx' – nome do símbolo da variável para inserir os dados recebidos do servidor,
format – arranjo de duas flags que definem o formato dos dados enviados.
A função ddeadv retorna a "1" se for bem sucedida; senão, retorna a "0".

Por favor, note que uma expressão de símbolo é dada como parâmetro 'callback', não como um descrito de função. na verdade, iremos executar a função "eval" que executa a linha como se tivesse sido digitada em um console. Essa característica produz a seguinte dificuldade: Quando uma nova cotação chega, temos que executar uma função grande para receber a nova cotação. Ao mesmo tempo, gostaríamos de passar a essa função a estrutura dos descritores "handles" que serão usadas para obter acesso aos objetos de gráfico da GUI. Entretanto, não encontrei nem os métodos para passar a estrutura dos descritores handles para uma linha executável nem a forma de solicitar a função localizada no m-file que descreve a GUI
Tudo isso resultou em ter que posicionar a função de recebimento da nova cotação em um m-file separado e chamá-lo de uma função normal do Matlab. Entretanto, a inconveniência acabou se tornando uma vantagem depois que descobri que poderia editar a função de processamento sem interromper as operações de clientes de DDE.

Assim, antes de tudo, vamos criar uma função de processamento separada que irá exibir apenas os dados recebidos no console.

function newTick(simbols)
% new tick processing
disp(simbols); % display the argument in console
song = wavread('C:\WINDOWS\Media\Windows XP - launch.wav'); % read the sound
wavplay(song,40000); % play the sound with the sampling rate of 40 kHz

A função exemplar acima também irá tocar o arquivo 'C:\WINDOWS\Media\Windows XP - launch.wav' assim que a nova cotação chegar. Salvar o texto da função como newTick.m no diretório de trabalho do MATLAB.

Agora vamos editar o m-file descrevendo o comportamento da nossa GUI. Adicione a inicialização da conexão à função DDEs_OpeningFcn , e a (Par será adicionada à função figure1_CloseRequestFcn.
(Para adicionar a função CloseRequestFcn ao arquivo m-file, você deve executar o seguinte editor de GUI: View(Visualizar) -> View Callbacks(Visuzalizar Callbacks) -> CloseRequestFcn).

% --- Executes just before DDEs is made visible.
function DDEs_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to DDEs (see VARARGIN)

channel = ddeinit('MT4','QUOTE'); % initialization
pair = get(handles.editPair,'UserData'); % read the symbol name
rc = ddeadv(channel, pair,'newTick(x)','x',[1 1]); % establish connection
if (rc==1) % if the connection has been established,
disp('Connected'); % inform the console
end
handles.chann = channel; % save the channel ID in handles

% Choose default command line output for DDEs
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes DDEs wait for user response (see UIRESUME)
% uiwait(handles.figure1);

% --- Executes when user attempts to close figure1.
function figure1_CloseRequestFcn(hObject, eventdata, handles)
% hObject handle to figure1 (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)

channel = handles.chann; % get the channel ID from handles
pair = get(handles.editPair,'UserData'); % read the symbol name
ddeunadv(channel,pair); % disconnect
rc = ddeterm(channel); % deinitialization
if (rc==1) % if everything is OK
disp('Disconnected'); % inform the console
end


% Hint: delete(hObject) closes the figure
delete(hObject);

% --- Executes during object creation, after setting all properties.
function editPair_CreateFcn(hObject, eventdata, handles)
% hObject handle to editPair (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles empty - handles not created until after all CreateFcns called

set(hObject, 'String', 'EURUSD'); % Enter the symbol name in the input field
set(hObject, 'UserData', 'EURUSD'); % In the UserData of the input field - save


% Hint: edit controls usually have a white background on Windows.
% See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor','white');
end


Forneci acima os textos completos sobre modificar funções juntamente com o texto preparado para funções de esqueleto vazio por desenvolvedores do Matlab.

O último bloco insere o nome do símbolo no campo correspondente antes que a GUI seja aberta. A entrada será copiada para a propriedade do 'UserData'. Sempre usaremos a cópia no 'UserData', enquanto que somente usaremos os nomes exibidos no campo ('String') se o usuário tentar modificar a segurança. Se o usuário estiver enganado ao digitar e um nome errado foi escrito no 'String', iremos retornar para o nome armazenado no 'UserData'.

O código abaixo realiza a função da mudança do nome do símbolo do usuário.

function editPair_Callback(hObject, eventdata, handles)
% hObject handle to editPair (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)

oldPair = get(hObject,'UserData'); % previous symbol name
newPair = get(hObject,'String'); % new symbol name
channel = handles.chann; % get the channel ID

disconn = ddeunadv(channel,oldPair); % disconnect
if (disconn==0) % if you fail to
disconnect
set(hObject,'String',oldPair); % restore the old symbol name in the input field
else % if diconnected
conn = ddeadv(channel, newPair,'newTick(x)','x',[1 1]); % establish a new connection
if (conn==1) % if the connection is established
set(hObject,'UserData',newPair); % memorize what symbol is used
else % if you fail to
establish a new connection
ddeadv(channel, oldPair,'newTick(x)','x',[1 1]); % restore the old one
set(hObject,'String',oldPair); %
restore the old symbol name in the input field
end
end

% Hints: get(hObject,'String') returns contents of editPair as text
% str2double(get(hObject,'String')) returns contents of editPair as a double



Ticks recebidos

Considere que a conexão é estabelecida e, na chegada de um novo tick, a função "newTick(x)" solicitada e "sela" o argumento recebido do MT 4 em um console. Primeiro, vamos exibir a última cotação recebida na linha correspondente da nossa GUI.

Para isso, temos que ter um estrutura de descritores de objetos gráficos de GUI - handles estão à disposição da função "newTick". Vamos usar a função setappdata(h,name,value) que salva os dados para o domínio do aplicativo. Especifique "0" como a ID do aplicativo. É o descritor da 'raiz' do objeto do Matlab, é invariante, então sempre podemos saber sobre ela.

Adicione a linda de "setappdata(0,'hndls',handles);" imediatamente depois do título da função "DDEs_OpeningFcn":

function DDEs_OpeningFcn(hObject, eventdata, handles, varargin)
setappdata(0,'hndls',handles); %

Agora, na função "newTick", podemos extrair os handles pela função value = getappdata(h,name), tendo especificado "0" como o argumento de "h". Então, poderemos gerenciar os objetos da GUI a partir da função "newTick".

Então, transformamos o argumento de string passado para a função a partir do servidor de DDE e exibimos o valor de Bid na GUI. Além disso, detectamos o tempo local de recebimento da cotação e o exibimos também, mas na na barra de status da GUI. O tempo local é necessário, uma vez que o servidor DDE passa o tempo com a precisão de minutos, o que é inaceitável para trabalhar com ticks. A função 'now' retorna o tempo local com a precisão de até frações de um milissegundo, então não ficaremos preocupados se os ticks diferentes tiverem o mesmo tempo fixo. Iremos também extrair o tempo do servidor da linha recebida do servidor de DDE e transformá-la no formato de tempo do Matlab.

Abaixo está mais um exemplo da função "newTick":

function newTick(simbols)
% NEW TICK PROCESSING

timeLocal = now; % Detect the exact local time
handles = getappdata(0,'hndls'); % Receive handles from root

% disp(simbols); % put the argument into console (commented)
song = wavread('C:\WINDOWS\Media\Windows XP - launch.wav'); %read the sound
wavplay(song,40000); %
play the sound with the sampling rate of 40 kHz

set(handles.textInfo,'String', datestr(timeLocal)); % show the local time in GUI

% --- transforming the line received from MT 4 ---
parts = sscanf(simbols, '%i/%i/%i %i:%i %f %f' ); % studying the line according
%to the format: int/int/int int:int float float
timeServerVect = parts(1:5); % extract the time
timeServerVect = timeServerVect'; % transpose (column into line)
timeServerVect = [timeServerVect 00]; % add seconds
timeServer = datenum(timeServerVect); % transform into the Matlab time format
Bid = parts(6); % extract Bid
Ask = parts(7); % extract Ask
% --- end of transforming ---

set(handles.textBid,'String',['Bid: ' num2str(Bid)]); % Show Bid in GUI


Elaborando um gráfico de tick

Aqui está a continuação da função "newTick" que foi iniciada acima. O código é fornecido com os comentários detalhados, então, suponho que não será nenhum trabalho para você encontrar o começo e o final dele.
Irei somente explicar que o arranjo de cotações de Bid, assim como handles, é armazenado na área de objeto 'root'
, mas salvo como "dados". Os dados armazenados representam um estrutura que consistem em dois campos:
data.name - nome do símbolo de um par cambial;
data.array - o próprio arranjo de cotações.

Na função "newTick" function, esses dados se encontram com o nome de "ticks" e os campos da estrutura tem os nomes de ticks.name e ticks.array, respectivamente.

ticks.array representa um arranjo que consiste de três colunas:
- tempo local no formato do Matlab (com precisão suportada pelo Matlab [microssegundos]);
- tempo do servidor
no formato de tempo do Matlab (com a precisão em minutos);
- Bid.

A função "newTick" esvazia o arranjo de citações, se o nome do símbolo no campo "editPair" mudou e as cotações para outro símbolo começaram a chegar. Se isso NÃO mudou, as linhas são adicionadas ao arranjo existente.

O bloco de operações com o gráfico define os parâmetros (tamanho e posição) da janela do gráfico de eixos e extrai a largura da janela em pixels a partir delas. Isso é necessário para o programa configurar a escala horizontal da exibição - uma cotação por um pixel.
Se houverem menos cotações do que pixels na largura da janela, o gráfico será elaborado como um todo. Se houverem mais cotações pixels, então somente os últimos dados que se encaixam no gráfico serão exibidos.

% --- working with quotes array ---
GUIpairName = get(handles.editPair, 'UserData'); % symbol name
if (~isappdata(0,'data')) % if no data
ticks.name = GUIpairName; % form the name field
ticks.array = []; % form a field - an empty array
setappdata(0,'data',ticks); % write the data into root
end
ticks = getappdata(0,'data'); % extract data
if ~strcmp(ticks.name,GUIpairName) % if the name has changed
ticks.name = GUIpairName; % form the name field
ticks.array = []; %
form a field - an empty array
setappdata(0,'data',ticks); % write the data into root
end
ticks.array = [ticks.array; timeLocal timeServer Bid]; % add a line
% containing the new data to the existing data array
setappdata(0,'data',ticks); %
write the data into root
% --- end of working with the array ---

% --- working with chart ---
chartSize = get(handles.axesChart,'Position');% get the chart window size
chartSize = chartSize(3); % extract the chart window width
lenArray = size(ticks.array); % get the size of the data array
lenArray = lenArray(1); % extract the amount of lines in the data array

set(handles.axesChart, 'NextPlot','replace'); % drawing mode - replace
% the old chart with a new one

if (chartSize >= lenArray)
stairs(handles.axesChart,ticks.array(:,3)); % draw the whole chart
else
stairs(handles.axesChart,ticks.array(lenArray-chartSize+1:lenArray,3));
% display the latest data fitting into the chart
end
set(handles.axesChart,'XLim',[1 chartSize]); % set the scale - one count
% in one width pixel
set(handles.axesChart, 'NextPlot','add'); % drawing mode - adding
plot(handles.axesChart,[1 chartSize], [Bid Bid],'m');% draw the Bid horizontal



Salvando os dados no arquivo

A última função a ser descrita é salvar os dados de tick em um arquivo pelo pedido do usuário.
Salvaremos dados ao pressionar um botão, então adicione o objeto "Push Button" ao formulário da GUI usando o editor.

Configurar as seguintes propriedades do objeto: Tag = pushSave, String = Save.

Após pressionar o botão "M-file Editor", o template da função pushSave_Callback será adicionado ao final do "DDEs.m" automaticamente.

Abaixo está o texto completo da função que salva os dados:

% --- Executes on button press in pushSave.
function pushSave_Callback(hObject, eventdata, handles)
% hObject handle to pushSave (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
date = datestr(now,'yyyy-mm-dd'); % get to know the date (string)
time = datestr(now,'HH-MM-SS') % get to know the time (string)
name = get(handles.editPair,'UserData');% get to know the symbol name (string)
template = [name '@' date '@' time]; % form the file name
[userName, userPath] = uiputfile([template '.txt']); % get the name and the path from the user
if userName~=0 % if "Cancel" is not pressed
ticks = getappdata(0,'data'); % get the data from root

timesStr = datestr(ticks.array(:,1)); % form a string array
% of time and date
bidStr = num2str(ticks.array(:,3)); % form string array named BID
delimStr(1:length(bidStr)) =' ' ; % form a "column" separator
% more exactly, form a line that will be transposed into a column
matrix=[timesStr delimStr' bidStr]; % gather write all Str into one matrix
dlmwrite([userPath userName], matrix, '');% save the matrix in a file
end


A função prepara o nome do arquivo que consiste na data, tempo e nome do símbolo da segurança.
Ao salvar, as matrizes de símbolo são preliminarmente preparadas:
- timesStr -- tempo e data locais correspondentes com as cotações;
- delimStr -- delimitadores;
- bidStr -- coluna BID.
Então, são todos reunidos em uma matriz.

delimStr representa uma linha consistindo em espaços; o comprimento da linha é igual ao comprimento da coluna BID. Ao fundir, a linha delimStr é transposta em um coluna e separa a coluna de cotações do tempo.


Conclusão

Espero que o método descrito acima permita a você usar todas as funções matemáticas no Matlab para desenvolvimento e teste das suas estratégias de trading automatizadas.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/1528

Arquivos anexados |
work.rar (7.61 KB)
work.zip (7.72 KB)
Modificação de dois estágios de posições abertas Modificação de dois estágios de posições abertas
A abordagem de dois estágios permite que você evite o fechamento e reabertura desnecessários de posições em situações próximas à tendência e em casos de possível ocorrência de divergência.
Trailing Stop padrão e saída de mercado Trailing Stop padrão e saída de mercado
Desenvolvedores de algoritmos de modificação/fechamento de ordem sofrem de uma aflição contínua - como comparar resultados obtidos por métodos diferentes? O mecanismo de verificação é bem conhecido - é o Testador de Estratégia. Mas como fazer um EA funcionar igualmente para ordens de abertura/fechamento? O artigo descreve uma ferramenta que fornece uma forte repetição de aberturas de ordem que nos permite manter uma plataforma matematicamente correta para comparar os resultados de algoritmos diferentes para trailing stops e para sair do mercado.
Falácias, Parte 2. A estatística é uma pseudociência ou uma crônica sobre a queda de uma fatia de pão com manteiga Falácias, Parte 2. A estatística é uma pseudociência ou uma crônica sobre a queda de uma fatia de pão com manteiga
Inúmeras tentativas de aplicar métodos estatísticos à realidade objetiva, ou seja, séries financeiras, falham quando encontramos processos não estacionários, "mentiras" sobre acompanhar a distribuição de probabilidade e volume insuficiente de dados financeiros. Nesta publicação, tentarei me referir não às séries financeiras como tal, mas sim as suas apresentações subjetivas - nesse caso, à forma que um trader tenta prender as séries, ou seja, ao sistema de trading. O ensino das regularidades estatísticas do processo de resultados de trading é uma tarefa atraente. Em alguns casos, conclusões bastante verdadeiras sobre o modelo desse processo podem ser feitas e elas podem ser aplicadas ao sistema de trading.
Falácias, Parte 1: O gerenciamento de dinheiro é secundário e não é muito importante Falácias, Parte 1: O gerenciamento de dinheiro é secundário e não é muito importante
A primeira demonstração dos resultados de teste de um estratégia baseada em lote 0,1 está se tornando um fator padrão no fórum. Após receber um “nada mau” de profissionais, um iniciante vê que o teste “0,1” traz resultados modestos e decide introduzir um gerenciamento de dinheiro agressivo pensando que a expectativa matemática positiva automaticamente fornece resultados positivos. Vamos ver quais resultados podem ser alcançados. Juntamente com isso, irei tentar construir gráficos de equilíbrio artificiais que são muito instrutivos.