MetaTrader 5 - Exemplos MetaTrader 5 e Interação MATLAB Introdução Meu primeiro artigo A interação entre MetaTrader 4 e MATLAB Engine (máquina virtual MATLAB) foi notada pela comunidade MQL. Alguns leitores (1Q2W3E4R5T) conseguiram mover esse projeto de Borland para VS2008. Mas o tempo corre implacavelmente e (triste, mas verdadeiro) o MetaTrader 4 está desaparecendo, dando lugar ao seu sucessor MetaTrader 5 com o MQL5, que introduziu ponteiros e memória dinâmica. Graças a essas inovações, temos a oportunidade de escrever uma biblioteca universal de interação com a máquina virtual do MATLAB Engine e de vincular diretamente bibliotecas, geradas pelo MATLAB, com o MetaTrader 5. Este artigo aborda essa funcionalidade. Este artigo continua logicamente o anterior e abrange mais profundamente o problema da interação entre MetaTrader 5 e MATLAB. Para tornar o alcance deste artigo mais compreensível para leitores despreparados, dividi-lo-emos em três partes: teoria, referência e prática. A teoria irá abranger os tipos de dados utilizados no MQL5 e MATLAB, bem como a sua conversão mútua. Em Referência você aprenderá as estruturas linguísticas e a sintaxe das funções, necessárias para criar uma DLL. E na prática, analisaremos as armadilhas desta interação. Leitores experientes podem ignorar Teoria e Referência e começar com a Prática. Outros são instados a ler Teoria e Referência, e só então prosseguem para a Prática. Também vale a pena ler livros mencionados na seção Literatura. 1.1 Tipos de dados em MATLAB e MQL5 1.1.1 Tipos de dados simples Em primeiro lugar, precisamos nos familiarizar com os mundos internos do MQL5 e MATLAB. Após a inspeção superficial de tipos de variáveis, concluímos que eles são quase idênticos: Tabela 1. Tipos de dados em MATLAB e MQL5 Há uma diferença importante: as variáveis em MQL5 podem ser simples ou compostas (complexas), e em MATLAB todas as variáveis são multidimensionais (Complexo) - ou seja, matriz. Você deve sempre lembrar sobre esta diferença 1.1.2 Tipos de dados complexos No MQL5 existem 4 tipos complexos de dados: arrays, strings, estruturas e classes. O tipo de dados complexo é definido de vários tipos de dados simples, combinados em blocos de memória de certo comprimento. Ao lidar com esses dados, você sempre precisa saber o tamanho do bloco de memória em bytes ou o número de elementos (exceto as classes). Estamos interessados apenas em arrays e strings, porque a apresentação de classes e estruturas MQL5 para MATLAB não faz sentido. Ao passar arrays de qualquer tipo que você precisa saber: tipo (dimensão) e número de elementos usando a função ArraySize (). Deve ser dada especial atenção à indexação no MetaTrader 5 - geralmente é para trás (ou seja, o primeiro elemento contém dados mais recentes do que o próximo). Verifique isso usando a função ArrayIsSeries (). E MATLAB tem a seguinte indexação: o primeiro elemento contém os dados mais antigos do que o próximo - então você deve reverter suas matrizes antes de enviá-las para o MATLAB, se a bandeira ASSERIES TRUE. Com base no acima, concordamos com o seguinte: Arrays reversos invisivelmente para programas MQL5, exceto para matrizes do tipo char e arrays bidimensionais - deixe-os inalterados. Inverte invisivelmente todos os arrays do MATLAB e atribua o flag ASSERIES com TRUE, exceto para matrizes do tipo char e arrays bidimensionais - deixe-os inalterados. Em todas as matrizes no programa MQL5, criadas de acordo com a indexação para trás, o sinalizador ASSERIES deve ser VERDADEIRO, exceto para matrizes do tipo char e arrays bidimensionais - deixe-os inalterados. Mas esta não é a única limitação ao trabalhar com arrays. Quando você trabalha com matrizes multidimensionais, ou matrizes para serem mais corretas, especialmente do MATLAB, apresentamos a restrição para não mais do que arrays bidimensionais. Aqui, o sinalizador ASSERIES não pode ser VERDADEIRO e, portanto, tais matrizes não são revertidas. Não esqueça que as strings no MQL5 não são arrays dos elementos do tipo char. Então, quando passar strings vem um pequeno problema: em seqüências de caracteres MQL5 codificadas usando Unicode e MATLAB usa codificação ANSI. Então, antes de passar uma string, ele deve ser convertido em matriz de caracteres ANSI usando a função StringToCharArray (). E, vice-versa, quando você obtém uma matriz de caracteres do MATLAB, converta-a usando a função CharArrayToString () (veja a Tabela 2). Para evitar confusão, concorde: armazene todas as strings nos programas MQL5 usando Unicode, sem matrizes do tipo char. 1.2 Comparação dos tipos de dados MQL5 e MATLAB Para reduzir a quantidade de funções e simplificar o algoritmo da biblioteca, reduziremos a quantidade de tipos por meio da conversão automática, que não deve afetar a integridade dos dados. A tabela a seguir ilustra a regra da conversão do tipo de dados do MQL5 em MATLAB: com este tipo de conversão há uma perda de precisão. Não vamos usá-lo, mas você pode usar essa conversão em seus programas. Tabela 2. Comparação dos tipos de dados MQL5 e MATLAB Agora você está familiarizado com os tipos de dados usados no MQL5 e no MATLAB. Você sabe o que as armadilhas esperam na passagem de dados e como ignorá-los com competência. Você ainda precisa saber MATLAB Engine API e se familiarizar com MATLAB Compiler 4. 2. MATLAB Engine API Reference, MATLAB Compiler 4 Reference e C Input / Output Library Reference Esta seção apresenta as funções mais importantes da MATLAB Engine API, recursos de MATLAB Compiler 4 e número de funções úteis da biblioteca de entrada / saída padrão C. Então, vamos começar. 2.1 MATLAB Engine API e MCR Functions MATLAB Engine - é uma interface externa que permite que outros programas usem o desktop MATLAB. Ele fornece um trabalho totalmente funcional de todos os pacotes MATLAB sem restrições. Embora não seja dito na documentação, mas em termos de programador de sistema - é apenas uma máquina virtual, como PHP, MySQL, etc., que suporta uma maneira simples e relativamente rápida de trocar dados entre MetaTrader 4/5 e MATLAB. Esse método de conexão de programas externos com o pacote MATLAB é recomendado pelos desenvolvedores. A interface consiste em seis funções: Engine pEng engOpen (NULL) esta função chama a área de trabalho MATLAB, o parâmetro é sempre NULL, retorna um ponteiro para o descritor da área de trabalho. Int exitCode engClose (Engine pEng) esta função fecha a área de trabalho, retorna o número de usuários restantes da área de trabalho MATLAB, onde: Engine pEng apontar para o descritor da área de trabalho. MxArray mxVector mxCreateDoubleMatrix (int m, int n, int ComplexFlag) esta função cria uma variável (matriz) da área de trabalho MATLAB, retorna um ponteiro para variável (matriz), onde: mxArray mxVector ponteiro para matriz variável. Int m número de linhas. Int n número de colunas. ComplexFlag tipo de número complexo, para MetaTrader 4/5 mxREAL. Void mxDestroyArray (mxArray mxVector) esta função destrói a matriz MATLAB, é necessário para limpar a memória, onde: mxArray mxVector ponteiro para matriz variável. Int engPutVariable (Engine pEng, char Name, mxArray mxVector) esta função envia variável para área de trabalho. Você não deve apenas criar variáveis do tipo mxArray, mas também enviá-las para MATLAB, onde: Engine pEng ponteiro para o descritor da área de trabalho. Char Name nome variável do tipo char na área de trabalho MATLAB. MxArray mxVector ponteiro para matriz variável. MxArray mxVector engGetVariable (Engine pEng, char Name) esta função obtém variável da área de trabalho - o inverso da função anterior. Somente são aceitas variáveis do tipo mxArray, onde: mxArray mxVector ponteiro para matriz variável. Ponteiro pEng do mecanismo para o descritor da área de trabalho. Char Name nome variável do tipo char na área de trabalho MATLAB. Duplo p mxGetPr (mxArray mxVector), esta função obtém ponteiro para a matriz de valores, ele é usado para copiar dados junto com memcpy () (ver 2.3 C Standard Input / Output Library), onde: ponteiro duplo para matriz do tipo duplo. MxArray mxVector ponteiro para matriz variável. Int engEvalString (Engine pEng, char Command) esta função envia comandos para a área de trabalho MATLAB, onde: Engine pEng ponteiro para o descritor da área de trabalho. Comando de comando char para MATLAB, string do tipo char. Você provavelmente percebeu que o MATLAB Engine API permite que você crie a estrutura mxArray somente para o tipo duplo. Mas essa restrição não afeta suas possibilidades, mas afetará o algoritmo de sua biblioteca. MCR (instância MCR) é a biblioteca especial do pacote MATLAB, que permite executar aplicativos / bibliotecas públicas independentes, geradas pelo ambiente MATLAB em qualquer computador. Note-se que, mesmo que você tenha um pacote MATLAB completo, você ainda precisa instalar a biblioteca MCR executando o arquivo MCRInstaller. exe, localizado na pasta ltMATLABgtToolboxcompilerdeploywin32. Portanto, antes de chamar qualquer função de biblioteca pública, criada pelo ambiente MATLAB, você precisa chamar a função de inicialização MCR: bool mclInitializeApplication (const char option, int count) retorna TRUE se MCR start foi bem-sucedido, caso contrário FALSE, onde: const char option string of As opções, como no mcc - R geralmente são NULL int count size options string, normalmente 0. No final do trabalho da biblioteca pública, você deve chamar: bool mclTerminateApplication (void) retorna TRUE se MCR foi fechado com sucesso. 2.2 MATLAB Compiler 4 O MATLAB Compiler permite que você crie o seguinte a partir das funções M: Aplicativos autônomos que são executados mesmo que o MATLAB não esteja instalado. C / C compartilham bibliotecas, que podem ser usadas sem o MATLAB em sistemas de usuários finais. O compilador suporta a maioria dos comandos e pacotes do MATLAB, mas não todos. A lista completa de restrições pode ser encontrada no site MATLAB. Este método permite que você crie pacote independente de software do MetaTrader 5 e MATLAB, mas, em contraste com o mecanismo MATLAB, requer um programador bem treinado e um conhecimento profundo da compilação. O compilador MATLAB requer pelo menos um dos seguintes compiladores C / C: Lcc C (geralmente vem com MATLAB). Seu único compilador C. Borland C versões 5.3, 5.4, 5.5, 5.6. Microsoft Visual C / C versões 6.0, 7.0, 7.1. O MATLAB Compiler 4, em contraste com seus predecessores, gera apenas o código de interface (wrapper), ou seja, não traduz m-funções em código binário ou C / C, mas cria um arquivo especial com base na tecnologia de Arquivo de Tecnologia de Componentes (CTF) Que inclui integrações de vários pacotes, necessários para suportar m-funções. O compilador MATLAB também criptografa esse arquivo com uma chave única (não repetida) de 1024 bits. Agora, vamos considerar o algoritmo do MATLAB Compiler 4 funcionar, uma vez que a ignorância deste tópico levará a muitos erros estúpidos no tempo de compilação: a análise de dependências nesta etapa determina todas as funções, arquivos MEX e arquivos P, que as m-funções compiladas dependem de . Criando arquivo - O arquivo CTF é criado, é criptografado e compactado. Gerando o código do objeto do wrapper nesta etapa, todos os códigos-fonte são criados, necessários para o componente: código de interface C / C para m-funções especificadas na linha de comando (NameFilemain. c). Arquivo de componente (NameFilecomponent. dat), que contém todas as informações necessárias para executar m-code (incluindo chaves de criptografia e caminhos, armazenados no arquivo CTF). Tradução C / C. Nesta fase, os arquivos de código-fonte C / C são compilados em arquivos de objeto. Ligação. A fase final do projeto de construção. Agora, quando você está familiarizado com o comportamento do algoritmo do compilador MATLAB, você precisa aprender mais sobre chaves para ter um plano detalhado de ações, ao usar o compilador (mcc): Tabela 4. Teclas Matlab mbuild Linker (versão 4) A Tabela 4 lista As chaves principais. Para obter mais informações, use os comandos mbuild ou doc mbuild da ajuda. 2.3 C Biblioteca de entrada / saída padrão A utilização da Biblioteca de entrada / saída padrão fornece a cópia de dados correta. Seu uso irá salvá-lo de erros estúpidos que surjam durante a fase de projeto do programa (por exemplo: muitos programadores novatos copiam apenas o ponteiro para o bloco de memória em vez de copiar todo o bloco de memória). De toda a Biblioteca de Entrada / Saída, estamos interessados apenas em uma função: void pIn memcpy (void pIn, void pOut, int nSizeByte) esta função copia (clones) variável / matriz de pOut para pIn com tamanho de bytes nSizeByte, onde: void pIn Ponteiro para matriz, onde copiar. Anula pOut ponteiro para matriz, a partir do qual a cópia é feita. Int nSizeByte o tamanho dos dados copiados, não deve exceder o tamanho da matriz pIn, caso contrário, o erro de acesso à memória ocorrerá. 3. Prática Agora, terminamos com a teoria e podemos prosseguir com a realização da interação MATLAB MetaTrader 5 amp. Como você provavelmente adivinhou, isso será feito de duas maneiras: usando a máquina virtual MATLAB Engine e usando bibliotecas geradas pelo compilador MATLAB. Primeiro, considere uma maneira simples, rápida e versátil de interação via MATLAB Engine. Esta parte do artigo deve ser lida do início ao fim, uma vez que, apesar da aparente diferença entre os métodos de interação, eles têm uma filosofia e sintaxe familiar de construções de linguagem, e aprender algo novo é mais fácil com exemplos simples. 3.1 Desenvolvimento da biblioteca universal do MetaTrader 5 amp. Interação do mecanismo MATLAB Este método de interação não pode ser chamado de elegante e rápido, mas é o mais confiável e cobre todo o pacote MATLAB. Claro, devemos mencionar a velocidade do desenvolvimento do modelo final. A essência do desenvolvimento é escrever um invólucro de biblioteca universal para a interação MetaTrader 4/5 amp MATLAB Engine. Depois disso, o script / indicador / especialista do MetaTrader 4/5 pode gerenciar a área de trabalho virtual do MATLAB. E todo o algoritmo matemático pode ser armazenado no programa MQL como seqüências de caracteres, para que você possa usá-lo para proteger sua propriedade intelectual (para obter mais detalhes, consulte o artigo Proteja-se, Desenvolvedores). Também pode ser armazenado em m-functions ou arquivos separados por funções P na pasta ltMetaTrader 5gtMQL5Libraries. Possíveis áreas de aplicação dessa interação: testar ou demonstrar modelos / ideias matemáticos sem ter que escrever programas complexos (a proteção da propriedade intelectual pode ser organizada como no programa MQL e por meio do pacote MATLAB - usando funções P). Para escrever modelos matemáticos complexos usando todos os recursos do MATLAB. A todos aqueles que não vão distribuir seus scripts / indicadores / especialistas. Vamos prosseguir. Espero que você tenha lido os 1.1 tipos de dados em MATLAB e MQL5. 1.2 Comparação dos tipos de dados MQL5 e MATLAB. 2.1 MATLAB Engine API e MCR Functions e 2.3 C Standard Input / Output Library seções, pois não vamos pausar e analisar mais. Leia cuidadosamente o seguinte esquema de bloco, que ilustra o algoritmo da futura biblioteca: Figura 1. Esquema do bloco do algoritmo da biblioteca Como visto na Figura 1, a biblioteca consiste em três blocos principais. Considere os seus propósitos: bloco MQL5, preparação preliminar de dados enviados / recebidos: Arrays reversos. Conversão de tipos. Conversão de codificações de strings. Bloco C / C: converte matriz na estrutura mxArray. Passa os comandos do motor MATLAB. Sistema de cálculo de blocos de motor MATLAB. Agora, vamos lidar com algoritmos. Bem, comece com o bloco MQL5. O leitor atento já percebeu que se concentrará na implementação do que foi escrito nos Tipos de Dados na seção MATLAB e MQL5. Se você perdeu isso, dificilmente entenderá por que tudo isso é necessário. Algoritmo das funções mlInput ltvariabletypegt é quase idêntico. Vamos discutir seu trabalho usando a função mlInputDouble () que fornece entrada de variáveis do tipo duplo para a máquina virtual MATLAB. Bool mlInputDouble (double amparray, int sizeArray, string NameArray). Onde: referência de matriz para variável ou matriz do tipo duplo. SizeArray tamanho da matriz (número de elementos, não bytes). Seqüência de caracteres NameArray, contendo nome de variável exclusivo para a máquina virtual MATLAB (o nome deve corresponder aos requisitos do MATLAB). Converta String NameArray para char array usando a função StringToCharArray (). Verifique o tipo de indexação usando a função ArrayIsSeries (). Se o tipo de indexação for normal para a função mlxInputDouble (). ELSE indexação da matriz de timeseries: inverter matriz e valor de passagem para a função mlxInputDouble (). End função, passar o valor retornado para a função mlxInputDouble (). Algoritmo das funções mlGet ltvariabletypegt também é quase idêntico. Vamos discutir o seu trabalho usando a função mlGetDouble (), que retorna a variável do tipo duplo da máquina virtual MATLAB. Int mlGetDouble (double amparray, int sizeArray, string NameArray). Onde: referência de matriz para variável ou matriz do tipo duplo. SizeArray tamanho da matriz (número de elementos, não bytes). Seqüência de caracteres NameArray, contendo nome de variável exclusivo para a máquina virtual MATLAB. Converta String NameArray para char array usando a função StringToCharArray (). Encontre o tamanho da matriz usando a função mlxGetSizeOfName (). SE o tamanho é MAIS DO QUE ZERO. Aloque a matriz de destinatários do tamanho necessário usando a função ArrayResize (), obtenha dados de mlxGetDouble (). Retorne o tamanho da matriz. O tamanho do IF é ZERO. Erro de retorno, ou seja, valor nulo. Isso é o que as funções mlGetInt () e mlGetLogical () produzem a conversão de sombra dos tipos double-gt int / bool. Para este propósito, essas funções criam um buffer de memória temporário em seus corpos. Esta é uma medida forçada, porque, infelizmente, a API MATLAB não permite criar estruturas mxArray para tipos de dados diferentes do dobro. No entanto, isso não significa que o MATLAB opera exclusivamente os tipos duplos. O bloco C / C é muito mais fácil: ele deve fornecer a tradução de dados do tipo duplo para a estrutura mxArray. É feito usando o mxCreateDoubleMatrix (). Funções mxGetPr () e memcpy (). Então, usando a função engPutVariable (), ele passa dados para a máquina virtual MATLAB e, para extrair dados, ele usa a função engGetVariable (). Novamente, preste atenção às funções com prefixos Int e Logical como visto no esquema de bloco, eles não interagem diretamente com MATLAB, mas use as funções mlxInputDouble / mlxGetDouble e mlxInputChar (). Algoritmo do seu comportamento é simples: chamar os valores de entrada / saída de função mlxInputDouble / mlxGetDouble como duplo () e enviar o comando MATLAB de sombra para converter o tipo de dados através da função mlxInputChar (). MATLAB O bloco do motor é ainda mais fácil. Ele fornece apenas funções matemáticas. Seu comportamento depende dos seus comandos e das suas funções m / p. Agora, quando todos os detalhes do projeto são claros, é hora de lidar com a construção de projetos. Qualquer tipo de construção começa com a criação da biblioteca principal no nosso caso, é bloco C / C. Para esse fim, em qualquer editor de texto ANSI (Bloco de notas, Bred, etc.), crie um arquivo com a extensão DEF. É desejável que o nome deste arquivo consista em caracteres latinos sem espaços e pontuação, caso contrário você ouvirá muitas palavras lisonjeiras do seu compilador. Este arquivo fornece a permanência de suas funções. Se este arquivo estiver ausente, o compilador C / C inventará seus próprios nomes exóticos para exportar funções. Este arquivo contém: palavra de controle LIBRARY, LibMlEngine nome da biblioteca, e EXPORTS segunda palavra de controle, então venha o nome das funções. Como você provavelmente sabia, os nomes das funções de exportação não podem ter espaços e pontuação. Aqui está o texto do arquivo DllUnit. def do arquivo MATLABEngine. zip: LIBRARY LibMlEngine EXPORTS mlxClose mlxInputChar mlxInputDouble mlxInputInt mlxInputLogical mlxGetDouble mlxGetInt mlxGetLogical mlxGetSizeOfName mlxOpen Então, temos o primeiro arquivo do projeto. Agora abra o Windows Explorer e vá para a pasta ltMATLABgtExterninclude. Copie o arquivo engine. h (arquivo de cabeçalho da máquina virtual MATLAB) para a pasta, onde você foi projetado (se você não fizer isso, você precisará especificar manualmente o caminho para o arquivo no estágio da compilação). Agora é hora de criar um bloco C / C. Não incluiremos todo o código-fonte do programa no artigo, porque este arquivo pode ser encontrado em MATLABEngine. zip como DllUnit. cpp e é bem comentado. Note-se que é melhor criar funções usando a convenção stdcall, ou seja, os parâmetros são passados através da pilha e a função limpa a pilha. Esse padrão é nativo para Win32 / 64 API. Considere como declarar uma função: extern C declspec (dllexport) ltvariabletypegt stdcall Função (lttypegt ltnamegt) extern C declspec (dllexport) informa ao compilador C que essa função é externa. O tipo de variável retornado de ltvariabletypegt pode ser: void, bool, int, double. Tipos compostos (conhecidos não só da Dll, mas também do programa de chamada) e ponteiros. Declaração de stdcall sobre passar parâmetros para funcionar e voltar, é um padrão para Win32 / 64 API. Funcion o nome da sua função. Lttypegt ltnamegt tipo e nome da variável de entrada, o número máximo de variáveis é 64. C / C building building: para isso, você precisa incluir biblioteca de entrada / saída padrão e adicionar ao projeto os seguintes arquivos (no seu compilador: Project-gtAdd Project) : DllUnit. def Na pasta ltMATLABgtExternlibltwin32 / 64gtltcompilergt, onde: ltMATLABgt pasta principal do MATLAB. Ltwin32 / 64gt seja uma pasta win32 para sistema operacional de 32 bits, ou win64 para sistema operacional de 64 bits. Integre a pasta borland para Borland C / C ver. 5-6, a pasta Microsoft para Microsoft Visual C: libeng. lib libmx. lib Uma pergunta comum como essa pode surgir: eu tenho uma versão diferente do compilador ou não um tal compilador na lista (muito raramente não existem tais arquivos). Permite ver como criar manualmente uma biblioteca pública. Vamos considerar como é feito no Visual C e no Borland C: Na FAR, abra ltMATLABgtBinltwin32 / 64gt pasta, onde: ltMATLABgt MATLAB pasta principal. Ltwin32 / 64gt seja uma pasta win32 para sistema operacional de 32 bits, ou win64 para sistema operacional de 64 bits. Para Borland C digite: implib libeng. lib libeng. dll. O mesmo para libmx. dll. Para Visual C digite: lib libeng. dll. O mesmo para libmx. dll. Se outro compilador. Qualquer compilador de qualquer linguagem de programação deve ter esse utilitário - o Gerenciador de Biblioteca, geralmente este é um programa de console ltcompiler foldergtbinlib. exe. A propósito, eu esqueci de avisá-lo - não tente fazer o LIB de 64 bits para o compilador de 32 bits. Primeiro, descubra se há suporte de endereçamento de 64 bits na ajuda do compilador. Caso contrário, procure DLL MATLAB de 32 bits ou escolha outro compilador C / C. Chegando à compilação, depois do qual obtemos uma biblioteca, que deve ser colocada na pasta terminalfolderMQL5Libraries. Agora vamos começar com o bloco MQL. Execute o MetaEditor, clique em Novo e faça como nas figuras a seguir: Figura 2. Assistente MQL5: Criar Biblioteca Figura 3. Assistente MQL5: Propriedades Gerais da Biblioteca Agora, quando o Assistente MQL5 criou um modelo, proceda à sua edição: Note que o MQL 5 você Pode passar ponteiros de duas formas: void NameArray // Este método de passagem da matriz permite apenas ler dados. No entanto, se você tentar usar essa referência para editar seu conteúdo, você obterá um erro de acesso à memória (no melhor caso para você, o MetaTrader 5 solucionará silenciosamente o erro no quadro SEH, mas HAVENT WRITE a SEH-frame, então Podemos perder o motivo do erro). Voidamp NameArray // Este método de passagem permite que você leia e edite o conteúdo da matriz, mas você deve manter o tamanho da matriz. Se a função não aceita ou não passa parâmetros, especifique sempre o tipo de vazio. 2. Nós não descreveremos todas as funções do bloco MQL, porque você pode encontrar o código fonte MatlabEngine. mq5 em MATLABEngine. zip. Portanto, considere os detalhes de declaração e definição de funções externas no MQL5: conforme visto no exemplo, a declaração e a definição da função são combinadas. Nesse caso, declaramos uma função chamada mlInputChar () como externa (exportação), que retorna o valor do tipo bool e aceita a seqüência de matriz como parâmetro. Agora compile. Agora que completamos o último bloco da biblioteca e compilamos, é hora de testá-lo em condições reais. Para fazer isso, escreva um script de teste simples (ou tire-o de MATLABEngine. zip, arquivo: TestMLEngine. mq5). O código de script é simples e bem comentado: conforme visto no script, estamos entrando valores e depois obtemos valores. No entanto, em contraste com o MetaTrader 4, onde precisávamos conhecer o tamanho do buffer no estágio de design, no MetaTrader 5 não é necessário, pois usamos buffers dinâmicos. Agora que você finalmente entendeu a máquina virtual MATLAB, você pode começar a usar a DLL construída no ambiente MATLAB. 3.2 Diretrizes técnicas de construção / uso de DLL geradas pelo MATLAB Compiler 4 Na seção anterior, você aprendeu a criar uma biblioteca para interação universal com o pacote MATLAB. No entanto, esse método tem uma desvantagem: requer o pacote MATLAB do usuário final. Esta restrição cria uma série de dificuldades na distribuição de produtos de software finalizados. É por isso que o pacote matemático MATLAB possui um compilador integrado, que permite que você crie aplicativos independentes independentes do pacote MATLAB. Vamos ver isso. Por exemplo, considere um indicador simples - média móvel (SMA). Aumente a atualização adicionando um filtro de rede neural (GRNN), que permite alisar o ruído branco (rajadas aleatórias). Nomeie o novo indicador como NeoSMA e filtre como GRNNFilter. Assim, temos duas m-funções, das quais queremos criar uma DLL, que pode ser chamada do MetaTrader 5. Agora lembre-se de que o MetaTrader 5 procura em DLLs nas seguintes pastas: ltterminaldirgtMQL5Libraries ltterminaldirgt Pasta atual Diretório do sistema ltwindowsdirgtSYSTEM32 ltwindowsdirgt Diretórios listados no Variável de ambiente do sistema PATH. Portanto, coloque em um desses diretórios duas m-funções (NeoSMA. m e GRNNFilter. m), onde vamos construir DLL. Eu chamo sua atenção para este fato de colocação, pois isso não é feito por acidente. O leitor atencioso já conhece o recurso do compilador MATLAB - preserva os caminhos ao compilar (consulte 2.2 MATLAB Compiler 4). Antes de começar a compilar o projeto, você deve configurar o compilador. Para fazer isso, siga estas etapas: Na linha de comando MATLAB digite: mbuild - setup Pressione y para confirmar a descoberta de compiladores compatíveis com C / C instalados em seu sistema. Escolha o compilador padrão do Lcc-win32 C. Pressione y para confirmar o compilador selecionado. Figura 4. Compilando o projeto Agora estamos prontos para passar para o processo de compilação m-functions. Mcc - N - W lib: NeoSMA - T link: lib NeoSMA. m GRNNFilter. m Explique as chaves: - N para pular todos os caminhos desnecessários - W lib: NeoSMA diz ao compilador que o NeoSMA é o nome da biblioteca - T link: lib diz Compilador para criar biblioteca pública com links para nomes de funções de NeoSMA. m e GRNNFilter. m m-functions Agora, vamos ver o compilador criado: mccExcludedFiles. log log-file contendo ações de compiladores NeoSMA. c C versão da biblioteca (contém - code do wrapper) NeoSMA. ctf arquivo CTF (ver 2.2 MATLAB Compiler 4) seção NeoSMA. h arquivo de cabeçalho (contém declarações de bibliotecas, funções, constantes) NeoSMA. obj arquivo de objeto (arquivo de origem contendo máquina e pseudo-código) NeoSMA. exports exportou nomes de funções NeoSMA. Dll Dll para continuar a ligar o NeoSMA. lib Dll para usar em projetos C / C NeoSMAmcccomponentdata. c Versão C no componente (usado para conformidade com o arquivo CTF, contém caminhos, etc.) NeoSMAmcccomponentdata. obj versão do objeto do componente (arquivo fonte contendo máquina E pseudo-código) Então vamos lidar com wi DLL, precisamente com sua estrutura interna. Consiste em (funções básicas apenas) de: Função principal de qualquer DLL - BOOL WINAPI DllMain (). Que (de acordo com a especificação da Microsoft) lida com eventos que ocorrem em DLL: carregamento de DLL no espaço de endereço do processo, criando um novo fluxo, excluindo o fluxo e descarregando Dll da memória. Funções de serviço de inicialização / desinitiação de DLL. BOOL ltNameLibgtInitialize (void) / void ltNameLibgtTerminate (void) são necessários para iniciar / descarregar o ambiente de trabalho de matemática antes de usar as funções da biblioteca e no final da sua utilização. As m-funções exportadas void mlfltNameMfilegt (int ltnumberofreturnvaluesgt, mxArray ltreturnvaluesgt, mxArray ltinputvaluesgt.), Onde: ltnumberofreturnvaluesgt número de variáveis retornadas (não confundem com o tamanho da matriz, etc.). MxArray ltreturnvaluesgt endereço da estrutura mxArray onde os resultados do trabalho m-function serão retornados. MxArray ltinputvaluesgt ponteiro para mxArray estrutura da m-função variável de entrada. Como você pode ver, as m-funções exportadas contêm endereços e ponteiros para a estrutura mxArray e você não pode chamar diretamente essas funções do MetaTrader 5, pois não entenderá esse tipo de dados. Nós não descreveremos a estrutura do mxArray no MetaTrader 5, porque os desenvolvedores do MATLAB não garantem que não vai mudar ao longo do tempo, mesmo dentro da mesma versão do produto, então você precisa escrever um adaptador DLL simples. Seu esquema de bloco é mostrado abaixo: Figura 5. Esquema de bloco do adaptador DLL É muito semelhante ao lado direito da DLL para o MATLAB Engine, portanto, não analisamos seu algoritmo e seguimos diretamente para o código. Para fazer isso, crie dois arquivos pequenos em seu compilador C / C: nSMA. cpp (de DllMatlab. zip): nSMA. def (de DllMatlab. zip): LIBRARY nnSMA EXPORTS IsStartSMA nSMA Construa o projeto em seu compilador C / C: Para isso, você precisa incluir biblioteca de entrada / saída padrão e adicionar ao projeto os seguintes arquivos (no seu compilador: Project-gtAdd Project): nSMA. def Na pasta ltMATLABgtExternlibltwin32 / 64gtltcompilergt, onde: ltMATLABgt pasta principal do MATLAB. Ltwin32 / 64gt seja uma pasta win32 para sistema operacional de 32 bits, ou win64 para sistema operacional de 64 bits. Integre a pasta borland para Borland C / C ver. 5-6, a pasta Microsoft para o Microsoft Visual C (eu tenho arquivos para a versão 6): libmx. lib mclmcr. lib NeoSMA. lib criam manualmente (consulte 3.1 Desenvolvimento da Biblioteca Universal do MetaTrader 5 amp MATLAB Engine Interaction). O último, o que eu quero lhe dizer nesta seção, é sobre arquivos necessários ao mover projeto para outro computador, onde não há MATLAB instalado. Aqui está uma lista de arquivos e caminhos na máquina de destino: MCRInstaller. exe qualquer pasta (instalador MCR) extractCTF. exe qualquer pasta (para o instalador MCR) MCRRegCOMComponent. exe qualquer pasta (para o instalador do MCR) unzip. exe qualquer pasta (para MCR Instalador) NeoSMA. dll ltterminaldirgtMQL5Libraries NeoSMA. ctf ltterminaldirgtMQL5Libraries nnSMA. dll ltterminaldirgtMQL5Libraries Muitos programadores avançados já adivinharam que é aconselhável usar um programa de instalação (SETUP). Existem muitos deles através da Internet, incluindo produtos gratuitos. Agora, temos que testar esta DLL no MetaTrader 5. Para fazer isso, escreveremos um script simples (TestDllMatlab. mq5 do DllMatlab. zip): Conclusão Então, você sabe como criar uma biblioteca universal para a interação MATLAB MetaTrader 5 amp e Como conectar a DLL construída no ambiente MATLAB. Mas ainda há interfaces de MetaTrader 5 amp MATLAB interação para ser descrita, mas isso está além do escopo deste artigo. O tópico deste artigo é abordado em detalhes. Eu escolhi as formas mais eficazes de interação, não exigindo um tipo especial de adaptadores. Embora você possa ir de outra maneira, como a tecnologia. NET - Como exportar cotações do MetaTrader 5 para aplicativos. NET usando os Serviços WCF. Muitos leitores podem ter uma pergunta: qual método escolher. A resposta é simples - ambos, porque durante a concepção / depuração do modelo matemático a velocidade não é necessária. Mas você precisará do poder total do MATLAB sem custos especiais de produção para programação. O motor MATLAB ajudará aqui, é claro. No entanto, quando o modelo matemático é depurado e pronto para usar, você precisará de velocidade, multitarefa (trabalho de indicador e / ou sistema comercial em vários gráficos de preços) - aqui, sem dúvida, você precisará de uma DLL, construída no ambiente MATLAB. Mas tudo isso não obriga você a segui-lo. Everybody will give the answer to this question himself, relying primarily on the proportion of programming cost to the scale of the project (number of indicator and/or trade system users). It makes no sense to create Dll in the MATLAB environment for one or two users (its easier to install MATLAB on two computers). Many readers, who are unfamiliar with MATLAB, probably have a question: why all of this MQL5 has already mathematical functions The answer is that use of MATLAB enables you to effortlessly implement your mathematical ideas, here is just a partial list of possibilities: dynamic algorithm of fuzzy logic in the indicator and/or mechanical trade system dynamic genetic algorithm in mechanical trade system (dynamic strategy tester) dynamic neural network algorithm in the indicator and/or mechanical trade system three dimensional indicators simulation of nonlinear management systems So, all in your hands, and do not forget: Mathematics has always been the queen of sciences, and MATLAB package is your scientific calculator. Literature Translated from Russian by MetaQuotes Software Corp. Original article: mql5/ru/articles/44Bitfinex announced today the start of mining contracts as a trading product on their platform. In total 100 THS (terahashes per second) with an expiration in 3 months have been made available for trading under the name TH1BTC. The 100 THS are part of a larger pool of 3500 THS so more mining contracts might become available in the future. Interestingly, this marks the first time that it is possible to short a mining contract. Shorting a mining contract means to receive an amount of Bitcoin now (the price we sell it at) and subsequently paying dividends (in Bitcoin) over the following 3 month until the contract expires in the middle of December. A profit is made if the sum of all the dividends paid out (plus the interest we paid to short the contract) is less than what we received at the beginning when we sold the contract (to someone else obviously). This means the price of TH1BTC should depend on 3 variables (in decreasing order of importance): The change of the mining difficulty until 15 December The time remaining until 15 December The interest rate (swap rate) If difficulty increases dividend payments become smaller because 1 THS represents a smaller fraction of the whole network hashing power. Therefore the price of one contract should decrease if difficulty increases. The closer we get to expiration the fever Bitcoins can be mind with 1 THS in total. Therefore the price of one contract should decrease the closer we get to expiration and reach a price of 0 at expiration. The higher the interest rate the more costly it is to enter and keep the contract over the full length of 3 month. Bitfinex does not offer 90 days swaps, therefore entering a contract with the goal to hold it until the end contains quite a bit of interest rate risk because at some point a new swap has to be taken out (at a potentially unfavorable interest rate). This is less of a problem when going long (Bitcoin rates are typically low) than when going short (there is only a maximum of 100 contracts available in total, no naked shorting). To compensate for the risk prices should increase when swap rates are increasing. The big unknown is of course the change in the mining difficulty over the next 90 days. In the following figure we see how difficulty changed over the previous 6 month. The data is from Tradeblock and it shows not only a graphical representation of past changes in the difficulty (difficulty changes every 14 days depending on past hash rate. More info can be found in the wiki ) but also some basic summary statistics. On average difficulty has increased 27 over the last 30 days and 77 over the last 60 days. To estimate the fair price of one TH1BTC we will assume that difficulty will increase on average 15 per month over the next 3 month. Currently the price of buying one contract worth 1 THS is 2 BTC. The pool fee is 3 and we will ignore interest rates. Filling in all the information we get the following results: Hence if we go long one contract based on our assumptions we would make a loss of about 0.39 Bitcoin (a bit more in reality since we will start mining in the middle of September until the middle of December) because the expected dividends (monthly revenue) is not going to cover our initial costs of 2 BTC before the contract expires. On the other hand, going short at a price of 2 Bitcoin would have generated a profit of about 0.39 Bitcoin per contract. Keep in mind that we didn8217t include swap costs which are currently at around 1 per day (). There are two ways to look at the results. Either we could say prices for TH1BTC are currently overvalued and should be closer to around 1.5 BTC. If we assume difficulty will increase more than 15 per month then prices should be even lower than that. Or we could say that the market is efficient and prices are correct, which would imply that the market is expecting difficulty to decrease on average about 2 per month over the next 90 days. Either way, results will be known with certainty in 90 days. Struggling to recover from the most recent Bitcoin flash crash which originated on Bitfinex only four days go. Bitcoin prices took another dive today as margin traders got their positions liquidated on BTC-e. The event started at 1:36 PM (UTC1) when large sell orders began to show up on the third largest western Bitcoin exchange BTC-e. Downwards momentum increased steadily as the orderbook became increasingly thin, crashing prices to a low of USD 309 per Bitcoin at 1.43 PM. In the following minutes prices rebounded swiftly on thin volume back to around USD 442 as arbitrage traders started to take advantage of the discount relative to other exchanges. BTC-e is one of the few large exchanges that offer margin trading to their clients via the MetaTrader platform since November 2013, but the details of who excactly provides the funds necessary for margin trading have remained unclear. The shape and especially timing of the crash points towards margin traders being liquidated (or stop orders being executed), similar to what happened on Bitfinex a couple of days ago. However, unlike Bitfinex which is transparent about open swap positions. BTC-e does not provide important data which would be needed to provide a more thorough analysis and so this last statement can only be considered a good guess. Unlike Bitfinex, which relies on a hidden algorithm in an effort to control the order flow. BTC-e seems to have no special safeguards in place to mitigate such events. The fall below 400 was mainly due to a lack of bids in the orderbook and not because the market believed that the true value was below 400, as the rebound back to over 440 only minutes later basically proved. Hence, halting trading during extreme downwards volatility could have easily averted the bloodshed among margin traders by giving other market participants more time to thicken the orderbook. Update 4:58PM (UTC1): BrCapoeira posted on Reddit an interesting graph based on data from the Metatrader platform: This graph implies that a single large order was the cause of this event. Whether this order was created due to a margin call, a simple mistake, to manipulate the market, or to open a large short position remains unclear. Common sense would suggest that it was probably the result of a margin call of a single large trader. My previous post on this topic has been brought up during discussions in the aftermath of the most recent Bitcoin flash crash. Coindesk was one of the first to pick it up and since then various posts about transparency and the possible responsibilty of exchanges to actively manage order execution started to appear. As a result of those events Josh Rossi, Vice President of Business Development at Bitfinex, went on Reddit to openly address some of the issues brought up against the exchange. The facts we know for sure are that there were some large sell orders shortly before the crash started, for example a 500 sell order on Bitstamp at 9.49am (UTC1), about 6 minutes before a large sell order on Bitfinex triggered the crash. However, the data does not tell us whether it was insider trading, some form of market manipulation. or a simple mistake. Fact is that after the Bitcoin flash crash open swap positions decreased from around 28m to 24m which indicates about 8400 margin long positions were closed (assuming an average of 475) in one way (margin call) or another (stop order hit). The data does not tell us what the ratio is but according to Josh only about 650 Bitcoins were sold as the result of margin calls. As correctly pointed out by Jonathan Levin. fact is that starting about 24 hours before the bitcoin flash crash until the crash itself an additional 1000 Bitcoins were taken out in short positions and about 2500 shorts were subsequently closed during the crash. Whether those shorts were opened to hedge existing positions, as a malicious attempt to trigger a margin call, or a way to front-run the market using private information cannot be determined from the available data (it does look oddly suspicious though). What was unexpected Personally, the interesting point is not that Bitcoin flash crashed. Sudden price fluctuations happened in the past and will happen in the future, especially in illiquid markets such as Bitcoin. The interesting point is the involvement of Bitfinex and how they actively managed order execution without informing market participants in advance. The Bitfinex matching engine was not halted during the whole crash though it did slow down (but nowhere as bad as the infamous 70 minute order lag on the now defunct MtGox exchange during the crash in 2012 ). However, what Bitfinex did was they introduced something they now refer to as speed bumps. What it means is that they essentially flag orders they deem as invalid or potentially dangerous and slow them down intentionally . At first sight this might seem like a nice idea. Who doesn8217t want a filter to remove or slow down malicious orders However, as so often with those kind of things the devil is in the detail. The problem is that Bitfinex has not (and possibly never will ) make public how exactly they categorize an order as 8220bad8221 and 8220slow it down8221. If a market participant decides to put up a large sell order against a thin orderbook then that8217s his decision. Whether his action was intended or not is not up to the exchange to decide. It might be that this market participant was simply the first person to react to a major event and is entirely willing to bear the additional costs of the resulting slippage in anticipation of a major price move. There simply does not exist a way to accurately classify orders a priori as 8220good8221 or 8220bad8221 since that would automatically assume knowledge of all immediate future events. What can be improved Mistakes (8220fat finger8221, algorithm going havoc) happen, margins get called and people try to game the system in every possible way. Logically there have to be safeguards in place to protect markets and its participants. Bitfinex was definitely aware of potential toxic order flow and prepared counter measures. The only thing they forgot was to inform their clients about the hidden safety features. Hiding those safeguards from the public adds uncertainty to the market (especially now that we know they exist and sometimes do something) and essentially puts every traders trust into the hands of Bitfinex. At this point a trader can only hope that Bitfinex will always act in the best intentions of their clients. This hope might be futile though, since Bitfinex makes money from trading fees, independently whether a trader actually makes any money. One does not have to think for long to realize the hidden potential for abuse in such a system. The main reason brought up by Josh why Bitfinex does not intend to publicise their algorithm is to avoid giving traders the possibility to exploit it is bogus and the following shows why. Those are the official market wide circuit breakers used by NASDAQ, posted online and entirely transparent for every market participant. Those rules are certainly not perfect but they are simple, transparent, and work for one of the largest stock markets in the world. Now, I have great respect for the people working on the Bitfinex platform, but I doubt that they managed to come up with an algorithm which protects market participants better than the ones used by a major exchange trading more than 900 million shares per day on average. And if they did, now is the chance for Bitfinex to prove it to the world and possibly write history by teaching the big boys how to properly run an exchange. When it comes to public exchanges transparency is a must, not just for Bitfinex but for any exchange. Market participants have to know exactly what happens when they place an order and should under no circumstances have to rely on good faith alone. Safeguards are important because accidents happen and markets crash but it is not up to the exchange to engage in secret order discrimination. There are different ways to safeguard financial markets and none of them is perfect. Adding complexity usually increases the chance for unintended side effects and therefore a simple, transparent approach seems more appropriate than a hidden, complex one. Two days ago BitMEX reduced their trading fees to 0 and celebrated it by releasing a basic market making bot on Github. BitMEX is currently running a trading challenge until August 29, 2014 to promote their new platform. Releasing a market marking bot is probably an interesting and effective way to increase API traffic and stress test the platform a bit. Of course I couldn8217t resist and had a look. Market-maker is a forked off Liquidbot. which was original designed to run on the now obsolet MtGox exchange. There were some minor changes (new api class to connect to BitMEX, some additional prints to console, changes to adapt for futures contracts, and a huge and unnecessary print to console when starting up) but no significant changes to the trade logic. The algorithm uses REST and only checks for changes every 60 seconds. This already disqualifies the bot as it is essential too slow to react to ongoing changes in the orderbook. BitMEX limits requests to the REST API to 150 per 5 minutes so you can try reducing the 60 seconds to something like 3 but it won8217t change the fact that as soon as markets start to move you will hit the limit and be stuck with open positions. To be fair, BitMEX provides the bot more as a marketing stunt and explicitly states that switching to WebSocket will be highly beneficial as it allows real time updates. Overall, the algorithm is solidly written, technically works and is easy to setup, but it won8217t make you any money in the long run. If someone seriously considers to employ this bot I would recommend the following small changes to make the code more usable: 1. Change to Websocket 2. Exit position on close: 3. Build orders starting from the midpoint:In addition I would advise to measure volatility in some way and adapt the distance between orders dynamically as well as the size. During my testing the API was always responsive and accurate. Volume on the exchange is still low but the fundamentals of the platform look promising. This bot is a fun tool to introduce users into the world of market making and algorithmic trading but it won8217t stand a chance against established algorithms. Note: If you consider using this algorithm keep in mind that market making is a full time job. Anything less than complete dedication, fast reaction time, and 100 uptime will cause you to lose money. Edit: Follow up on the aftermath here Today Bitcoin prices took a dive as margin traders on one of the largest exchange Bitfinex got their orders liquidated. For many close market observers and more sophisticated traders this did not come as a surprise. In fact, long positions have been building up continuously over the last couple of months in anticipation of a new bubble in Bitcoin prices and reached as high as 30m in outstanding swap positions on Bitfinex. Now, this wouldn8217t be a problem all by itself as long as there is enough capital backing the loan. Unfortunately, most of those long positions were entered around 600 8211 640 USD/BTC and the collateral was mostly provided in Bitcoins itself. The following chart nicely shows the buildup of long positions, peaking around July 14 with close to 32m in swaps. Running some quick math based on the maintenance margin of Bitfinex of 13 and assuming Bitcoin as collateral we find that margin calls should start around the 520 8211 540 USD/BTC mark. Yesterday, prices came close and today they finally jumped over the cliff. The problem is that once margin calls set in you have a cascading effect which rips through the order book, causing even more orders to reach the point of no return and increasing the downward momentum further. These kind of events are not limited to Bitcoin exchanges but can also occur on major exchanges such as during the 2010 flash crash in the US. The cause of such a flash crashe can vary and goes from fat finger mistakes to programming errors to cascading margin calls. It is interesting to see how the exchanges deal with these events. In the US, Nasdaq implemented market wide circuit breakers which will cause trading to stop under such extreme circumstances. Bitcoin markets are not yet as advanced and usually continue trading. If we look at the order action on Bitfinex today we see something very peculiar: It seems (and this is just a guess as there is no official comment from the exchange) as if Bitfinex is running an algorithm to handle the margin calls. The algorithm starts selling but limits itself to a 10 drop in prices within 1 minute. If prices drop more than 10 in 1 minute it will stop selling and wait for buy orders to come in. Once there are again a certain amount of buy orders in the orderbook the algorithm starts selling again until all margin calls are met. Edit: LeMogawai was the first to point this out in this post and it matches my personal observation at the time of the event. This seems to be an interesting way to deal with cascading margin calls but can also be considered as borderline market manipulation from the exchange side. By spreading out the sell orders over time the downwards momentum is reduced, however traders end up trading against the exchange itself and not the market anymore. The exchange has an informational advantage at that point and is therefore more likely to profit than the traders. Fortunately, this only lasted for about 10 minutes after which control was given back to the market. Other exchanges which also offer margin trading such as BTC-e and OKcoin are now in a favorable position and can learn from todays events. Implementing a system more closely resembling the circuit breakers of big exchanges such as Nasdaq might be a smart first move. Recently I am working to get my new trading platform going. This new version is based on Python, uses MySQL to keep a database of all time series of different virtual currencies with automatic backfill from BitcoinCharts and integrates the 3 major exchanges MtGox, BTC-E and Bitstamp. The platform will be used as way to backtest some strategies and engage in automatic trading. During the run-up to this I decided to pull some data of BTC against USD from BitcoinCharts and based on the ideas of a paper by Hashem and Timmermann (1995) implemented a simple trading strategy. The idea is to forecast the sign of the t1 period return based on a regression, which is estimated on an automatic selection of technical indicators during the last n period up until t . Then, after t1 happened, we refresh the model and try to predict t2 using all the data available of the last n periods until t1 and so on. For my Bachelor Thesis I examined four different technical trading rules in Forex markets. It uses MCS and SPA test to search for valid models among different parameters that are not subject to data snooping. Taking into account realistic transaction costs we find no evidence of excess returns, which is consistent with market efficiency. With this code you should be able to look for Bitcoin arbitrage opportunities within BTC-e. It uses the idea of one price and applies triangular arbitrage, taking into account costs and spread. The reason I post this here is despite that it works, chances are you will be too slow to compete with other investors doing the same. Possible improvements would be to take into account the order book depth and split the trades up dynamically, trying to undercut other traders doing the same. Also setting everything up on a dedicated server close to the physical location of the BTC-e match engine should drastically reduce lag and give you a potential edge. Pós-navegação
No comments:
Post a Comment