Desenvolvemos um gerenciador de terminais (Parte 1): Definição do problema
Introdução
Se quisermos usar apenas um EA em nossa conta de negociação, não há problema: iniciamos o terminal, executamos o EA e aguardamos o resultado. Agora, imagine uma situação em que a negociação esteja indo bem e surja a vontade de adicionar mais um ou vários EAs. Se eles puderem operar sem interferir uns nos outros, ótimo. Caso contrário, será necessário iniciar instâncias separadas do terminal para cada um deles. Isso também será necessário se quisermos negociar em contas diferentes.
A partir daí, começam a surgir dificuldades operacionais e técnicas. Como facilitar o gerenciamento de vários terminais nos quais os EAs estão operando? Além disso, esses terminais podem estar fisicamente localizados em computadores (servidores) diferentes. O que podemos fazer nesse caso?
Antigamente, era possível usar o MultiTerminal para algo semelhante. No entanto, ele permitia apenas a negociação manual em várias contas e funcionava somente com o MetaTrader 4. Depois, seu suporte foi completamente descontinuado, e uma versão equivalente para o MetaTrader 5 nunca chegou a ser lançada. As ferramentas de terceiros disponíveis atualmente oferecem apenas monitoramento, mas não um gerenciamento completo.
Por isso, queremos criar uma interface web para gerenciar a inicialização dos terminais de negociação MetaTrader 5 nos computadores disponíveis. Por meio de um site local desenvolvido para esse fim, será possível visualizar a lista de instâncias em execução, consultar informações mais detalhadas sobre o funcionamento de cada uma delas, adicionar novas instâncias ou encerrar as que já estiverem em execução. No futuro, essa lista de recursos poderá ser ampliada de forma bastante significativa. Isso não apenas facilitará o gerenciamento, como também aumentará a confiabilidade de todo o sistema de negociação.
Arquitetura
Para criar um sistema capaz de gerenciar terminais distribuídos, precisamos de uma arquitetura modular e escalável. Podemos dividi-la em três componentes principais que interagem entre si.
- Servidor web principal. É o núcleo do sistema, responsável por fornecer a interface ao usuário e coordenar o funcionamento dos demais componentes. Ele receberá os comandos do usuário, como iniciar, parar e monitorar, enviará esses comandos aos servidores web de terminal correspondentes e reunirá todas as informações disponíveis para exibi-las em um único painel.
- Servidor(es) web dos terminais. Esse componente será executado diretamente em cada computador no qual os terminais de negociação estiverem instalados. Para executar os comandos recebidos do servidor web principal, monitorar o estado dos terminais MetaTrader 5 em execução localmente e fornecer informações atualizadas, usaremos Python com a biblioteca MetaTrader 5.
- Armazenamento de dados. Na etapa inicial, usaremos SQLite para armazenar informações sobre servidores, terminais, contas e suas configurações. Trata-se de um banco de dados leve, ideal para protótipos e implantações simples. Além disso, ele pode ser acessado tanto pelo Python quanto por programas em MQL5, o que amplia as possibilidades de integração. No futuro, à medida que a carga aumentar e o sistema se tornar mais distribuído, o SQLite poderá ser substituído por um SGBD mais robusto, como o PostgreSQL. Mas, antes disso, ainda precisamos chegar lá.
Talvez ainda precisemos de um agente auxiliar nos próprios terminais, na forma de um EA ou serviço. Por enquanto, ainda não está totalmente claro.
Todo o código será disponibilizado como um projeto público, ou como vários projetos, no repositório MQL5 Algo Forge.
Traçamos o caminho
A tarefa, naturalmente, é bastante ampla e, por isso, exige algum esforço até mesmo para começarmos a resolvê-la. Mas já recorremos várias vezes a um princípio simples que ajuda bastante em situações desse tipo. Uma de suas formulações é: "Como se come um elefante? - Um pedaço de cada vez". Isso significa que qualquer tarefa complexa deve ser dividida em partes pequenas e viáveis, que serão resolvidas uma após a outra até alcançarmos o objetivo final. Essa abordagem ajuda a evitar sobrecarga, manter a motivação e chegar ao resultado com mais rapidez.
Por isso, começaremos pelo desenvolvimento de uma versão simplificada do servidor web dos terminais. Por enquanto, ele terá uma função simples: iniciar e encerrar, mediante solicitações do cliente, um terminal instalado no servidor e conectado a uma determinada conta de negociação. Entre essas solicitações, o cliente poderá enviar requisições para obter informações atualizadas sobre o estado da conta. Mas quem é o cliente nesse contexto? Neste momento, nós mesmos atuaremos principalmente como cliente, usando o navegador ou outras ferramentas capazes de enviar uma requisição HTTP ao servidor web. Mais adiante, o servidor web principal poderá assumir esse papel, caso mantenhamos a arquitetura definida inicialmente.
Vamos ver do que precisaremos para isso e como podemos usar tudo isso para chegar a um produto mínimo viável (Minimum Viable Product, MVP).
Relembrando alguns conceitos
Mencionamos que desenvolveremos um servidor web. Podemos definir um servidor web como uma aplicação que aguarda o recebimento de determinados tipos de requisição pela rede e é capaz de respondê-las, devolvendo os dados necessários. O conjunto de tipos de requisição que nosso servidor web será capaz de processar formará sua API.
Em termos mais rigorosos, uma API (Application Programming Interface - interface de programação de aplicações) é um conjunto de regras definidas que permite que uma aplicação interaja com outra. Ela determina como e quais dados podem ser solicitados ou enviados entre programas, serviços ou componentes de um sistema. De forma mais simples, uma API é um conjunto de regras que permite que um programa se comunique com outro para trocar dados.
Para que esse conjunto de regras seja fácil de usar, as requisições e respostas precisam seguir um formato específico, devidamente documentado. Ou seja, os desenvolvedores descrevem quais requisições estão disponíveis, quais parâmetros elas aceitam e o que é retornado nas respostas.Em nosso projeto, enviaremos requisições HTTP ao servidor web e receberemos como resposta dados no formato JSON ou o texto de uma página web em formato HTML.
Qualquer requisição HTTP é composta pelas seguintes partes:
- Método (por exemplo, GET, POST, PUT, DELETE) - define a ação. Ele também pode ser chamado de "verbo";
- URL - endereço do recurso no servidor (por exemplo, /start);
- Cabeçalhos (headers) - metadados da requisição (por exemplo, Content-Type, Authorization);
- Corpo da requisição (body) - dados enviados ao servidor (por exemplo, JSON ou dados de um formulário em uma página web).
Embora normalmente digamos que o método "define a ação", na prática o conjunto específico de ações executadas ao receber uma requisição é determinado pelo servidor web. Como estamos desenvolvendo esse servidor por conta própria, podemos implementar qualquer comportamento ao processar uma requisição, independentemente do método usado. Ainda assim, em geral os desenvolvedores seguem algumas convenções amplamente adotadas.
Por exemplo, requisições cujo principal resultado é obter alguma informação do servidor geralmente usam o método GET. Por isso, são chamadas de requisições GET. Se a requisição precisar enviar dados ao servidor para que sejam armazenados, normalmente é usado o verbo POST ou PUT. No entanto, vale reforçar mais uma vez que essa escolha cabe ao desenvolvedor. Poderíamos, por exemplo, usar apenas o método GET para todas as requisições. Ou apenas POST.
A parte seguinte é a URL, que especifica a requisição e permite ao servidor web entender o que está sendo solicitado. As URLs destinadas a um determinado servidor web geralmente são escritas sem o nome desse servidor, começando pelo caractere de barra "/", que representa a raiz da árvore de recursos. Uma URL pode ser composta por várias partes separadas por barras "/".
Quando definimos que uma determinada ação do servidor web será executada ao receber uma requisição com um verbo e uma URL específicos, dizemos que foi definido um "endpoint" ou uma "rota" (route).
Por exemplo, este pode ser o endpoint responsável por fazer o servidor web retornar uma página HTML com o nome do projeto e uma breve descrição:
Já para obter do servidor web, em formato JSON, a lista de todas as instâncias do MetaTrader 5 em execução nele, podemos definir o seguinte endpoint:
GET /instances
Para alguns endpoints, pode ser necessário passar parâmetros adicionais. Nesse caso, os nomes e os possíveis valores de cada parâmetro precisam estar documentados em algum lugar. Os parâmetros podem ser enviados como parte da URL, nos cabeçalhos ou no corpo da requisição.
Por exemplo, podemos definir o seguinte endpoint para obter informações resumidas sobre uma instância específica do MetaTrader 5 em execução:
GET /instance/<name>
Aqui, <name> é a parte variável da requisição, que será substituída pelo nome da instância desejada antes do envio.
O conjunto de todos os endpoints (rotas) definidos, com suas respectivas descrições detalhadas, formará justamente a API do nosso servidor web.
Existem diferentes tipos de API. O que planejamos usar neste projeto é chamado de REST API (Representational State Transfer Application Programming Interface). Trata-se de um estilo arquitetural de comunicação entre cliente e servidor pela internet usando requisições HTTP padrão. Ele apresenta justamente as características que pretendemos utilizar:
-
O estado não é armazenado no servidor. Cada requisição contém todas as informações necessárias para gerar a resposta, de modo que o servidor não precisa "lembrar" a sequência de solicitações anteriores. Isso, porém, não significa que ele não possa armazenar informações obtidas durante o processamento de requisições anteriores. Essa é a principal característica desse tipo de API.
-
Métodos HTTP como tipos de ação. Em uma REST API, normalmente são usados métodos HTTP diferentes para operações distintas sobre os dados do servidor: GET para obter dados, POST para criar novos registros, PUT / PATCH para atualizar os existentes e DELETE para removê-los. Mas isso é apenas uma recomendação.
-
Estrutura baseada em recursos. Todos os dados disponíveis no servidor são representados como recursos com URLs exclusivas. Por exemplo, como mencionamos anteriormente:
- /instances - lista de todos os terminais,
- /instance/4428341 - informações sobre um terminal específico chamado 4428341.
A lista de recursos pode ser ampliada ao longo do tempo. Ou seja, podemos iniciar o projeto implementando o fornecimento de informações sobre um pequeno conjunto de recursos e depois adicionar novos gradualmente.
Em resumo, a REST API do nosso projeto será um conjunto de endpoints (rotas HTTP) por meio dos quais será possível gerenciar, no servidor (computador) em que o servidor web estiver sendo executado, as instâncias do MetaTrader 5, iniciando-as, encerrando-as e obtendo seu estado. Portanto, para criar esse servidor web, precisamos definir um conjunto de endpoints e desenvolver um programa capaz de processar corretamente as requisições correspondentes.
Para desenvolver esse programa, também vamos recorrer a duas soluções já existentes:
- Uvicorn - servidor para Python que processa requisições HTTP de forma rápida e eficiente, com suporte a operações assíncronas. Ele é usado para executar aplicações web, inclusive aquelas criadas com FastAPI;
- FastAPI - framework que ajuda a definir a lógica da aplicação, isto é, o processamento das requisições de todos os endpoints.
Um framework é, em geral, um conjunto de código-fonte pronto que implementa operações comuns das quais os desenvolvedores podem precisar em seus próprios projetos. E qual é a diferença entre um framework e uma biblioteca? Talvez, principalmente, o fato de que um framework impõe mais restrições à forma como o projeto é desenvolvido, ou seja, ele estabelece com mais clareza as regras e a estrutura do desenvolvimento.
Uso do FastAPI
Os princípios gerais de desenvolvimento com o framework FastAPI podem ser resumidos da seguinte forma:
- Uma aplicação web é composta por um conjunto de funções, cada uma responsável por processar as requisições de um determinado endpoint. Essas funções podem ser síncronas ou assíncronas. O desenvolvedor precisa escrever apenas esses handlers.
- Acima de cada handler há um decorador que vincula a função a um endpoint específico. Nele são definidos o método HTTP correspondente, a URL e outros parâmetros necessários.
- Dentro da função, é possível obter parâmetros do corpo da requisição, dos cabeçalhos, da URL e de outras fontes usando tipos específicos do framework FastAPI, como Form, Query, Path e Body.
- A função retorna uma resposta, que o FastAPI converte automaticamente para JSON ou HTML.
Além de resolver a tarefa principal, o uso do framework FastAPI permite verificar automaticamente se os tipos estão corretos e se os parâmetros necessários estão presentes nas requisições, gerar a documentação de todos os endpoints e tratar erros, retornando um JSON com a descrição dos erros. Um pouco mais adiante, veremos isso na prática.
Criamos a primeira versão do servidor web
Para trabalhar no projeto, criamos um novo repositório vazio chamado mt5-manager no MQL5 Algo Forge. Vamos cloná-lo para uma pasta de nossa preferência e adicionar um novo arquivo da nossa aplicação web, main.py:
mt5-manager/
│
└── main.py # Arquivo principal do FastAPI
Para trabalhar com o framework e o servidor escolhidos, precisaremos instalar os módulos Python necessários usando o gerenciador pip:
pip install fastapi uvicorn jinja2 psutil
Vamos começar a montar a lista de endpoints (rotas) necessários. Por enquanto, adicionaremos apenas uma rota:
| # | Método | URL | Descrição, parâmetros | Formato da resposta |
|---|---|---|---|---|
| 1 | GET | / | Exibe a página principal com a interface web para gerenciar as instâncias do MetaTrader 5. Por enquanto, vamos exibir apenas um título com o nome do projeto. | HTML |
Vamos criar uma aplicação web mínima capaz de processar essa rota. Para isso, no início do arquivo, importaremos do framework FastAPI a classe principal da aplicação. Essa classe também se chama FastAPI:
from fastapi import FastAPI
Em seguida, criaremos um objeto dessa classe. Ele representará nossa aplicação web. Podemos chamá-lo, por exemplo, de app:
app = FastAPI()
Adicionaremos uma função chamada index(), que retornará um título de primeiro nível com o nome do projeto. Vamos torná-la assíncrona adicionando a palavra-chave async:
async def index(): return '<h1>MT5 Manager</h1>'
Agora falta apenas a última etapa, mas uma etapa muito importante: adicionar o decorador que vinculará a função à rota correspondente. Antes disso, porém, vale explicar com um pouco mais de detalhes o que ele é.
Decoradores em Python são funções que modificam o comportamento de outras funções (ou classes) sem alterar diretamente seu código. Essa modificação é feita por meio da criação de uma nova função que, internamente, utiliza de alguma forma a função original. Se depois atribuirmos essa nova função ao nome da função original, todas as chamadas seguintes passarão a executar a versão modificada. Dessa forma, podemos adicionar lógica extra, como validação, logging ou cache, antes ou depois da execução da função original.
Para aplicar um decorador a uma função, podemos chamá-lo diretamente:
# Original function def my_function(): pass # We call the decorator function, passing it the original function # We store the new function returned under the same name my_function = my_decorator(my_function)
Ou, como normalmente é feito, basta indicar o nome do decorador desejado antes da declaração da função, acrescentando o símbolo @ no início:
# Creating a new function with the my_decorator decorator applied @my_decorator def my_function(): pass
Agora vamos adicionar o decorador de que precisamos:
@app.get('/') async def index(): return '<h1>MT5 Manager</h1>'
Ou seja, @app.get('/') é um decorador que informa ao FastAPI que a função index() deve processar requisições GET no caminho /.
Vamos reunir tudo no arquivo main.py:
# Import the necessary classes from the libraries from fastapi import FastAPI # Creating an application object app = FastAPI() # GET request handler for / (the root directory) @app.get('/') async def index(): return '<h1>MT5 Manager</h1>'
A aplicação web mínima está pronta para ser executada. Agora precisaremos iniciar o servidor Uvicorn pelo console, indicando nossa aplicação web, que contém as regras de processamento das requisições:
uvicorn main:app --reload --no-use-colors
Aqui, main:app significa que nossa aplicação web está localizada no arquivo main.py da pasta atual, e que o objeto da aplicação no código-fonte se chama app. O parâmetro --reload faz com que o servidor seja reiniciado automaticamente sempre que houver alterações no código-fonte da aplicação web. Já o parâmetro --no-use-colors desativa a exibição de texto colorido no console, o que é necessário quando o servidor é iniciado em um console que não oferece esse suporte.
Depois de iniciar o servidor, veremos algo parecido com isto:

Por padrão, o servidor web usará a porta 8000. Portanto, para ver o servidor em funcionamento, abriremos no navegador o endereço http://127.0.0.1:8000. Aqui está o que obtemos:

Como podemos ver, a página realmente exibe o texto que retornamos pela função index(). No entanto, por algum motivo, o navegador não interpreta as tags HTML do título e simplesmente as mostra como parte do texto da página. Está tudo certo, esse comportamento é esperado. Isso acontece porque, por padrão, o servidor retorna o conteúdo como uma resposta JSON. Por isso, o navegador não interpreta o texto recebido como código HTML. Vamos corrigir isso agora.
Importaremos mais uma classe do framework FastAPI, que representa uma resposta do servidor em formato HTML. Na linha em que aplicamos o decorador, adicionaremos o parâmetro response_class e passaremos a ele o nome da classe HTMLResponse.
# Let's import the necessary classes from the libraries from fastapi import FastAPI from fastapi.responses import HTMLResponse # Creating an application object app = FastAPI() # GET request handler at / (the root directory) @app.get('/', response_class=HTMLResponse) async def index(): return '<h1>MT5 Manager</h1>'
Agora, a string retornada pela nossa função original será usada para criar um objeto da classe HTMLResponse. É justamente um objeto dessa classe que será retornado quando a função modificada pelo decorador for chamada.
Depois de salvar essas alterações, o servidor Uvicorn iniciado anteriormente será reiniciado automaticamente, e veremos no navegador o resultado esperado:

A primeira versão está pronta. Agora podemos pensar em ampliar a lista de rotas processadas.
Adicionamos a inicialização e o encerramento do terminal
Não vamos avançar rápido demais e, primeiro, vamos implementar a inicialização e o encerramento de uma única instância predefinida do terminal. Suponha que, no computador em que planejamos executar nosso servidor web, exista um terminal instalado no seguinte caminho:
C:/Program Files/MetaTrader 5/terminal64.exe
Vamos tentar fazer com que ele possa ser iniciado e encerrado ao enviarmos as requisições correspondentes ao servidor web. Para isso, adicionaremos mais duas rotas à tabela:
| # | Método | URL | Descrição, parâmetros | Formato da resposta |
|---|---|---|---|---|
| 1 | GET | / | Exibe a página principal com a interface web para gerenciar as instâncias do MetaTrader 5. Por enquanto, vamos exibir apenas um título com o nome do projeto. | HTML |
| 2 | POST | /start | Inicia uma instância do terminal MetaTrader 5 no caminho configurado. | JSON |
| 3 | POST | /stop | Encerra o processo do MetaTrader 5 em execução. | JSON |
Definimos o formato de resposta dessas rotas como JSON porque não queremos que essas rotas retornem uma página HTML para ser exibida pelo navegador. Em princípio, essa poderia ser uma página com uma mensagem informando o resultado da inicialização, mas futuramente poderemos exibir essas informações na página principal retornada pela primeira rota. Para deixar o código mais claro, também podemos indicar explicitamente que o resultado será um objeto de resposta em formato JSON.
Importaremos mais uma classe do framework:
from fastapi.responses import HTMLResponse, JSONResponse
Agora podemos usá-la de forma semelhante à classe HTMLResponse ou criar explicitamente um objeto da classe desejada ao retornar o valor pelo handler da rota. A seguir, mostraremos como funciona a segunda opção.
Uma boa prática é manter o código interno dos handlers das rotas o mais enxuto possível. Para isso, quase todas as ações são delegadas a funções auxiliares separadas. Levando isso em conta, o código dos dois novos handlers poderia ser escrito da seguinte forma:
# POST /start handler: starting the terminal @app.post('/start') async def start_instance(): result = start_mt5() return JSONResponse(result) # POST /stop handler: stopping the terminal @app.post('/stop', response_class=JSONResponse) async def stop_instance(): result = stop_mt5() return JSONResponse(result)
As funções start_mt5() e stop_mt5() serão implementadas um pouco mais adiante. Pelos próprios nomes, fica claro que essas funções conterão o código responsável por iniciar ou encerrar diretamente o terminal MetaTrader 5.
Antes disso, porém, criaremos uma constante com o caminho completo para o arquivo executável do terminal e um dicionário para armazenar informações sobre o processo iniciado:
# Path to the terminal executable file MT5_PATH = 'C:/Program Files/MetaTrader 5/terminal64.exe' # A dictionary for storing information about a running terminal instances = {}
Para implementar a inicialização do terminal, precisaremos da biblioteca subprocess. Primeiro, vamos importá-la e depois implementar a própria função start_mt5(). Por enquanto, faremos a inicialização por meio da função Popen(), à qual deve ser passada uma lista com os argumentos da linha de comando. No nosso caso, essa lista terá apenas um elemento: o caminho completo para o arquivo do terminal MetaTrader 5.
import subprocess # Starting the terminal def start_mt5(): # Start a new process process = subprocess.Popen([MT5_PATH]) # ID of the running process pid = process.pid # Save the process ID and status instances['default'] = {'pid': pid, 'status': 'running'} # Return the result: successful launch return {'instance': 'default', 'pid': pid}
Esse método de inicialização também facilita a implementação da função de encerramento do terminal. Como, ao iniciar o terminal, armazenamos o identificador do novo processo (pid), podemos usar a biblioteca psutil para encerrá-lo. Essa biblioteca possui uma classe que representa processos em execução. Na função stop_mt5(), criamos um objeto dessa classe com o identificador do processo desejado e, em seguida, o encerramos chamando o método terminate().
Se tudo ocorrer normalmente, retornamos informações indicando que o processo foi encerrado com sucesso. Caso contrário, informamos que o processo não foi encontrado.
import psutil # Stopping the terminal def stop_mt5(): # If the terminal was previously started if 'default' in instances: # Get its process ID pid = instances['default']['pid'] try: # Get the terminal process object p = psutil.Process(pid) # Stop the process p.terminate() # Remove information about the previously started process del instances['default'] # Return the result: successful stop return {'status': 'stopped', 'instance': 'default'} except psutil.NoSuchProcess: # If the process is not found, then # remove information about the previously started process del instances['default'] # Return the result: process not found return {'status': 'not found', 'instance': 'default'}
Vamos salvar as alterações feitas no arquivo main.py e ver como podemos testar a funcionalidade adicionada.
Documentação e testes
Se a primeira rota pode ser testada simplesmente informando a URL necessária na barra de endereços, com a segunda e a terceira isso não funcionará. Isso acontece porque o navegador sempre envia uma requisição GET quando usamos a barra de endereços. Agora precisamos verificar como funcionam endpoints que só podem ser acessados por meio de requisições POST. Felizmente, o framework também nos ajuda nesse caso.
O FastAPI gera automaticamente uma documentação interativa da API com base nos decoradores e nas anotações de tipo presentes no código. Além disso, o FastAPI inclui suporte integrado ao Swagger UI. Swagger ou OpenAPI é um padrão de descrição de REST APIs que permite visualizar e testar todos os endpoints diretamente no navegador. Isso é muito conveniente, principalmente se futuramente formos usar essa API em outras aplicações web.
Para acessar essa ferramenta, basta iniciar a aplicação web e abrir a página http://127.0.0.1:8000/docs. Embora não tenhamos adicionado esse endpoint por conta própria, ele está disponível em qualquer aplicação web criada com o framework FastAPI.
Ao acessar essa página, veremos a lista de todos os endpoints (rotas) do nosso servidor web que foram adicionados à aplicação:

Cada rota pode ser expandida para exibir a lista de parâmetros com suas descrições e o formato da resposta. Como nossas rotas ainda não têm parâmetros, por enquanto vemos apenas "No parameters":

Mas o mais interessante, naturalmente, é a possibilidade de enviar manualmente uma requisição para cada endpoint e conferir o resultado. Para isso, na rota expandida, clicamos no botão "Try it out" e, em seguida, no botão "Execute" que aparecerá.

Como resultado, veremos a resposta recebida do servidor web com o código 200 (OK) e informações sobre a instância do terminal MetaTrader 5 em execução, com o identificador de processo pid=16480. Ao abrir o Gerenciador de Tarefas do Windows em nosso computador, podemos confirmar que esse processo realmente existe e que corresponde ao terminal MetaTrader 5:

Se agora fizermos o mesmo para a rota POST /stop, o terminal em execução será encerrado, e o servidor web retornará uma confirmação:

Assim, com essa ferramenta, verificamos que nosso servidor web processa corretamente as requisições enviadas a todos os endpoints e executa as ações necessárias.
Conclusão
Por enquanto, vamos parar por aqui e ver até onde avançamos em direção ao objetivo definido. Criamos com sucesso um protótipo funcional do sistema de gerenciamento de terminais MetaTrader 5. Nosso servidor web mínimo baseado em FastAPI já consegue iniciar e encerrar o terminal por meio de uma simples requisição HTTP. Não apenas implementamos a lógica básica, como também confirmamos que ela funciona corretamente.
Este MVP é um primeiro passo extremamente importante, pois confirma que o caminho escolhido está correto. Já "demos a primeira mordida no elefante" e obtivemos uma base sobre a qual poderemos desenvolver as próximas funcionalidades. Os próximos passos mais naturais serão:
- Adicionar a possibilidade de registrar vários computadores com terminais.
- Criar uma interface para vincular contas de negociação a servidores e terminais específicos.
- Ampliar a API para obter informações detalhadas sobre o status das contas, as posições abertas e o funcionamento dos EAs.
Ainda temos um longo caminho pela frente, mas o primeiro passo já foi dado.
Obrigado pela atenção e até a próxima!
Conteúdo do arquivo compactado
| # | Nome | Versão | Descrição | Últimas alterações |
|---|---|---|---|---|
| mt5-manager | Pasta de trabalho do projeto do servidor web dos terminais | |||
| 1 | main.py | 1.00 | Aplicação web para o servidor web dos terminais | Parte 1 |
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/19804
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Redes neurais em trading: Treinamento de modelos spiking profundos (Conclusão)
Redes neurais em trading: Treinamento de modelos spiking profundos (Integração de spikes)
Desenvolvemos um gerenciador de terminais (Parte 2): Execução de várias instâncias
Algoritmo dos Macacos-Azuis — Blue Monkey (BM) Algorithm
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso