Por onde começar ao criar um robô de negociação para operar na Bolsa de Valores de Moscou MOEX
Na Bolsa de Valores de Moscou, muitos traders gostariam de automatizar seus algoritmos de negociação, mas não sabem por onde começar. A linguagem MQL5 não só oferece uma enorme gama de funções de negociação, mas também classes prontas que facilitam os primeiros passos no mundo do trading algorítmico. Neste artigo, mostraremos quais recursos prontos oferece a linguagem de estratégias de negociação MQL5 aos algo-traders na Bolsa de Valores de Moscou.
Existem dois tipos ordens de negociação na Bolsa de Valores de Moscou MOEX
A Bolsa de Valores de Moscou suporta dois tipos de ordens de negociação, isto é, ordens de mercado e limite. - As ordens de mercado são entregues imediatamente à bolsa de valores e executadas ao melhor preço disponível. Para envias estas ordens para a MQL5, usam-se ordens de mercado do tipo ORDER_TYPE_BUY e ORDER_TYPE_SELL.
- As ordens limite são armazenadas no servidor da bolsa, elas são executadas assim que aparece uma ordem oposta apropriada. Na linguagem MQL5, estas ordens são representadas por dois tipos de ordens, isto é,ORDER_TYPE_BUY_LIMIT e ORDER_TYPE_SELL_LIMIT.
A ordem de mercado garante a transação (mas nem sempre), porém não garante o preço. Isto significa que o preço da transação pode diferir significativamente da oferta atual. Uma ordem limite garante o preço, mas não garante a execução da operação segundo esse preço. Como resultado, sua ordem pode não ser executada.
Todos os outros tipos de ordens - que são oferecidos aos traders da Bolsa de Valores de Moscou - são parte do software através do qual esses traders podem interagir com a bolsa. Em outras palavras, as restantes ordens são algorítmicas. Elas são armazenadas e processadas fora da Bolsa de Valores de Moscou, bem como enviadas para essa bolsa na forma de ordens de mercado ou limite como resultado de um processamento interno.
A plataforma MetaTrader 5 oferece aos traders os seguintes tipos de ordens de negociação, eles podem ser utilizados para negociação na Bolsa de Valores de Moscou:
Identificador | Descrição | Armazenamento e execução |
ORDER_TYPE_BUY | Ordem de mercado para compra | A ordem se envia à bolsa de valores como uma ordem de mercado para comprar ao melhor preço de venda disponível |
ORDER_TYPE_SELL | Ordem de mercado para venda | A ordem se envia à bolsa de valores como uma ordem de mercado para vender ao melhor preço de compra disponível |
ORDER_TYPE_BUY_LIMIT | Ordem pendente Buy Limit | A ordem se envia à bolsa de valores como uma ordem limite para comprar e se executa assim que aparecer uma oferta de venda quer ao melhor preço de venda disponível quer ao preço estabelecido |
ORDER_TYPE_SELL_LIMIT | Ordem pendente Sell Limit | A ordem se envia à bolsa de valores como uma ordem limite para vender e se executa assim que aparecer uma oferta de compra quer ao melhor preço de compra disponível quer ao preço estabelecido |
ORDER_TYPE_BUY_STOP | Ordem pendente Buy Stop | A ordem se armazena no servidor da MetaTrader 5, e assim que se executa, envia-se à bolsa:
|
ORDER_TYPE_SELL_STOP | Отложенный ордер Sell Stop | A ordem se armazena no servidor da MetaTrader 5, e assim que se executa, envia-se à bolsa:
|
ORDER_TYPE_BUY_STOP_LIMIT | Ordem pendente BUY STOP LIMIT | A ordem se armazena no servidor da MetaTrader 5, e assim que se executa, envia-se como uma ordem limite para compra |
ORDER_TYPE_SELL_STOP_LIMIT | Ordem pendente SELL STOP LIMIT | A ordem se armazena no servidor da MetaTrader 5, e assim que se executa, envia-se como uma ordem limite para venda |
A plataforma MetaTrader 5 permite definir os níveis TakeProfit e StopLoss para posições abertas. Os níveis são armazenados no servidor de negociação da MetaTrader 5 e são executados automaticamente, mesmo sem conexão à conta de negociação:
- o nível TakeProfit define o preço para fechar a posição em uma direção favorável, assim que ele se executa, a ordem limite se envia para a bolsa ao preço TakeProfit;
- o nível StopLoss serve para implementar um stop de proteção em uma direção desfavorável, assim que ele se executa, a ordem limite se envia para a bolsa ao preço StopLoss para mercados de moedas e de valores mobiliários, enquanto para FORTS, se envia uma ordem limite ao pior preço de fronteira do canal.
Além disso, a plataforma MetaTrader 5 permite definir e modificar os níveis StopLoss/TakeProfit para posições pendentes, bem como modificar os níveis de execução de todas as ordens pendentes.
Operações de negociação na MetaTrader 5
A MetaTrader 5 proporciona alguns tipos básicos de operações de negociação que podem ser necessários em seu robô de negociação:
- compra/venda ao preço atual;
- colocação de uma ordem pendente para compra/venda sob certa condição;
- modificação/exclusão de uma ordem pendente;
- fechamento/aumento/redução/inversão da posição.
Na MQL5, todas as operações de negociação são implementadas com ajuda da função OrderSend(), ela retorna o controle do programa, após enviar com sucesso a ordem de negociação para a Bolsa de Valores de Moscou. Nesse momento, o estado da ordem muda para o valor ORDER_STATE_PLACED, porém isso não significa que sua ordem será executada com sucesso (o estado dessa ordem mudará para ORDER_STATE_FILLED ou ORDER_STATE_PARTIAL). O resultado final da execução de seu pedido de negociação depende do mercado atual, e a ordem pode ser rejeitada pela bolsa (estado ORDER_STATE_REJECTED) por razões diferentes.
Aliás, a OrderSendAsync(), que é uma versão assíncrona desta função, trabalha mais rápido do que a OrderSend(), uma vez que não espera o envio da ordem para o sistema de a bolsa. A resposta a esta função se envia assim que o pedido é enviado pelo terminal MetaTrader 5 para o lado de fora. Isso significa que seu pedido de negociação passou a verificação básica no terminal, e agora foi enviado para processamento no servidor de negociação da MetaTrader 5. O tempo necessário para adicionar seu pedido à fila de espera da bolsa e o momento em que a ordem será executada ou rejeitada dependem da carga de trabalho da bosa de valores e de quão rápida é sua conexão de Internet.
A grande variedade de operações de negociação é descrita pela estrutura MqlTradeRequest, ela contém a descrição do pedido de negociação. Por isso, a única dificuldade aqui é preencher corretamente a estrutura MqlTradeRequest e processar o resultado da execução do pedido.
De acordo com as regras de seu sistema de negociação, você pode fazer uma compra ou uma venda ao preço de mercado (BUY ou SELL), ou pode colocar uma ordem pendente para executar a compra/venda a uma certa distância do preço atual de mercado:
- BUY STOP, SELL STOP — compra o venda uma vez o preço rompe o nível especificado (pior do que o preço atual). As ordens deste tipo são armazenadas no servidor da MetaTrader 5 e são enviadas para a Bolsa de Valores de Moscou assim que a ordem é executada, ela é enviada como ordem de mercado (mercados de moedas e valores mobiliários) ou ordem limite (FORTS).
- BUY LIMIT, SELL LIMIT — compra ou venda uma vez o nível especificado é atingido (melhor preço atual). As ordens deste tipo são enviadas para a Bolsa de Valores de Moscou na forma de ordem limite. Deve-se notar que, na Bolsa de Valores de Moscou, você pode especificar, na ordem limite, o nível dentro do spread, ou mesmo no verso do spread. Isso ajuda a limitar a derrapagem (slippage) durante a execução da transação.
- BUY STOP LIMIT, SELL STOP LIMIT — colocação da ordem BUY LIMIT ou SELL LIMIT uma vez o preço especificado é atingido. As ordens deste tipo são armazenadas no servidor da MetaTrader 5, e, uma vez a ordem é executada, para a Bolsa de Valores de Moscou é enviada uma ordem limite habitual. O nível de abertura dessa ordem limite pode ser tanto superior quanto inferior ao preço de execução da ordem.
A seguinte figura apresenta o princípio de uso das ordens BUY STOP, SELL STOP e BUY LIMIT, SELL LIMIT, bem como as maneiras de colocá-las a partir do livro de ofertas.
Além disso, você pode precisar modificar ou mesmo eliminar uma ordem pendente. Isto também é feito com a ajuda das funções OrderSend()/OrderSendAsync(). Trabalhar com a posição aberta também não é difícil, pois ocorre como resultado das mesmas operações de negociação.
Neste artigo, mostraremos quão simples e fácil é programar a compra e venda na MQL5, demonstraremos como trabalhar com uma conta de negociação e propriedades dos símbolos. Para fazer isso, serão úteis as classes de negociação da Biblioteca Padrão.
Negociar na bolsa de valores com a ajuda de robôs é fácil
A linguagem MQL5 suporta nativamente todas os recursos de negociação da plataforma MetaTrader 5, nela há muitas funções de negociação para trabalhar com ordens, posições e pedidos de negociação. Além disso, as operações são sempre as mesmas, não importa o que você esteja negociando: títulos, futuros, títulos, opções, etc.
Com ajuda dos recursos da MQL5, você pode criar uma ordem de negociação e enviá-la para o servidor usando a função OrderSend() ou OrderSendAsync(), obter o resultado de sua execução, observar o histórico de negociação, conferir as especificações do contrato para o instrumento, manipular um evento de negociação e obter outras informações úteis.
Para os desenvolvedores de robôs de negociação, é importante compreender: cada transação de negociação quer a abertura de uma posição quer a colocação de StopLoss ou TakeProfit quer o fechamento de uma posição pela transação oposta sempre abarca um conjunto de transações realizadas no servidor da MetaTrader 5 e na Bolsa de Valores de Moscou. Para ver como isso acontece, você pode executar, em sua conta, o conselheiro (EA)TradeTransactionListener.mql5, ele apenas escuta os eventos TradeTransaction e exibe um breve resumo sobre eles:
//+------------------------------------------------------------------+ //| TradeTransactionListener.mq5 | //| Copyright 2016, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- PrintFormat("LAST PING=%.f ms", TerminalInfoInteger(TERMINAL_PING_LAST)/1000.); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { //--- static int counter=0; // contador de chamadas OnTradeTransaction() static uint lasttime=0; // hora da última chamada da OnTradeTransaction() //--- uint time=GetTickCount(); //--- se a última operação tiver sido realizada há mais de 1 segundo, if(time-lasttime>1000) { counter=0; // irá significar que isto é uma nova operação de negociação e é possível redefinir o contador if(IS_DEBUG_MODE) Print(" Nova operação de negociação"); } lasttime=time; counter++; Print(counter,". ",__FUNCTION__); //--- resultado da execução do pedido de negociação ulong lastOrderID =trans.order; ENUM_ORDER_TYPE lastOrderType =trans.order_type; ENUM_ORDER_STATE lastOrderState=trans.order_state; //--- nome do símbolo segundo o qual foi realizada a transação string trans_symbol=trans.symbol; //--- tipo de transação ENUM_TRADE_TRANSACTION_TYPE trans_type=trans.type; switch(trans.type) { case TRADE_TRANSACTION_POSITION: // alteração da posição { ulong pos_ID=trans.position; PrintFormat("MqlTradeTransaction: Position #%I64u %s modified: SL=%.5f TP=%.5f", pos_ID,trans_symbol,trans.price_sl,trans.price_tp); } break; case TRADE_TRANSACTION_REQUEST: // envia do pedido de negociação PrintFormat("MqlTradeTransaction: TRADE_TRANSACTION_REQUEST"); break; case TRADE_TRANSACTION_DEAL_ADD: // adição de transação { ulong lastDealID =trans.deal; ENUM_DEAL_TYPE lastDealType =trans.deal_type; double lastDealVolume=trans.volume; //--- identificador da transação no sistema externo: bilhete atribuído pela Bolsa de Valores de Moscou string Exchange_ticket=""; if(HistoryDealSelect(lastDealID)) Exchange_ticket=HistoryDealGetString(lastDealID,DEAL_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("(MOEX deal=%s)",Exchange_ticket); PrintFormat("MqlTradeTransaction: %s deal #%I64u %s %s %.2f lot %s",EnumToString(trans_type), lastDealID,EnumToString(lastDealType),trans_symbol,lastDealVolume,Exchange_ticket); } break; case TRADE_TRANSACTION_HISTORY_ADD: // adição da ordem no histórico { //--- identificador da ordem no sistema externo: bilhete atribuído pela Bolsa de Valores de Moscou string Exchange_ticket=""; if(lastOrderState==ORDER_STATE_FILLED) { if(HistoryOrderSelect(lastOrderID)) Exchange_ticket=HistoryOrderGetString(lastOrderID,ORDER_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("(MOEX ticket=%s)",Exchange_ticket); } PrintFormat("MqlTradeTransaction: %s order #%I64u %s %s %s %s",EnumToString(trans_type), lastOrderID,EnumToString(lastOrderType),trans_symbol,EnumToString(lastOrderState),Exchange_ticket); } break; default: // outras transações { //--- identificador da ordem no sistema externo: bilhete atribuído pela Bolsa de Valores de Moscou string Exchange_ticket=""; if(lastOrderState==ORDER_STATE_PLACED) { if(OrderSelect(lastOrderID)) Exchange_ticket=OrderGetString(ORDER_EXTERNAL_ID); if(Exchange_ticket!="") Exchange_ticket=StringFormat("MOEX ticket=%s",Exchange_ticket); } PrintFormat("MqlTradeTransaction: %s order #%I64u %s %s %s",EnumToString(trans_type), lastOrderID,EnumToString(lastOrderType),EnumToString(lastOrderState),Exchange_ticket); } break; } //--- bilhete da ordem ulong orderID_result=result.order; string retcode_result=GetRetcodeID(result.retcode); if(orderID_result!=0) PrintFormat("MqlTradeResult: order #%d retcode=%s ",orderID_result,retcode_result); //--- } //+------------------------------------------------------------------+ //| converte códigos numéricos de respostas para uma cadeia de caracteres Mnemonic | //+------------------------------------------------------------------+ string GetRetcodeID(int retcode) { switch(retcode) { case 10004: return("TRADE_RETCODE_REQUOTE"); break; case 10006: return("TRADE_RETCODE_REJECT"); break; case 10007: return("TRADE_RETCODE_CANCEL"); break; case 10008: return("TRADE_RETCODE_PLACED"); break; case 10009: return("TRADE_RETCODE_DONE"); break; case 10010: return("TRADE_RETCODE_DONE_PARTIAL"); break; case 10011: return("TRADE_RETCODE_ERROR"); break; case 10012: return("TRADE_RETCODE_TIMEOUT"); break; case 10013: return("TRADE_RETCODE_INVALID"); break; case 10014: return("TRADE_RETCODE_INVALID_VOLUME"); break; case 10015: return("TRADE_RETCODE_INVALID_PRICE"); break; case 10016: return("TRADE_RETCODE_INVALID_STOPS"); break; case 10017: return("TRADE_RETCODE_TRADE_DISABLED"); break; case 10018: return("TRADE_RETCODE_MARKET_CLOSED"); break; case 10019: return("TRADE_RETCODE_NO_MONEY"); break; case 10020: return("TRADE_RETCODE_PRICE_CHANGED"); break; case 10021: return("TRADE_RETCODE_PRICE_OFF"); break; case 10022: return("TRADE_RETCODE_INVALID_EXPIRATION"); break; case 10023: return("TRADE_RETCODE_ORDER_CHANGED"); break; case 10024: return("TRADE_RETCODE_TOO_MANY_REQUESTS"); break; case 10025: return("TRADE_RETCODE_NO_CHANGES"); break; case 10026: return("TRADE_RETCODE_SERVER_DISABLES_AT"); break; case 10027: return("TRADE_RETCODE_CLIENT_DISABLES_AT"); break; case 10028: return("TRADE_RETCODE_LOCKED"); break; case 10029: return("TRADE_RETCODE_FROZEN"); break; case 10030: return("TRADE_RETCODE_INVALID_FILL"); break; case 10031: return("TRADE_RETCODE_CONNECTION"); break; case 10032: return("TRADE_RETCODE_ONLY_REAL"); break; case 10033: return("TRADE_RETCODE_LIMIT_ORDERS"); break; case 10034: return("TRADE_RETCODE_LIMIT_VOLUME"); break; case 10035: return("TRADE_RETCODE_INVALID_ORDER"); break; case 10036: return("TRADE_RETCODE_POSITION_CLOSED"); break; default: return("TRADE_RETCODE_UNKNOWN="+IntegerToString(retcode)); break; } //--- } //+------------------------------------------------------------------+
Exemplo para a operação "ouvinte":
2016.06.09 14:51:19.763 TradeTransactionListener (Si-6.16,M15) LAST PING=14 ms Compra 2016.06.09 14:51:24.856 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:51:24.856 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_ADD order #49118594 ORDER_TYPE_BUY ORDER_STATE_STARTED 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeResult: order #49118594 retcode=TRADE_RETCODE_PLACED 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) 3. OnTradeTransaction 2016.06.09 14:51:24.859 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118594 ORDER_TYPE_BUY ORDER_STATE_REQUEST_ADD 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) 4. OnTradeTransaction 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118594 ORDER_TYPE_BUY ORDER_STATE_PLACED 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) 5. OnTradeTransaction 2016.06.09 14:51:24.881 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_DELETE order #49118594 ORDER_TYPE_BUY ORDER_STATE_PLACED 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) 6. OnTradeTransaction 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_HISTORY_ADD order #49118594 ORDER_TYPE_BUY Si-6.16 ORDER_STATE_FILLED (MOEX ticket=3377179723) 2016.06.09 14:51:24.884 TradeTransactionListener (Si-6.16,M15) 7. OnTradeTransaction 2016.06.09 14:51:24.885 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_DEAL_ADD deal #6945344 DEAL_TYPE_BUY Si-6.16 1.00 lot (MOEX deal=185290434) Colocação de SL/TP 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:51:50.872 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: Position #0 Si-6.16 modified: SL=62000.00000 TP=67000.00000 Fechamento da posição (venda) 2016.06.09 14:52:24.063 TradeTransactionListener (Si-6.16,M15) 1. OnTradeTransaction 2016.06.09 14:52:24.063 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_ADD order #49118750 ORDER_TYPE_SELL ORDER_STATE_STARTED 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) 2. OnTradeTransaction 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_REQUEST 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeResult: order #49118750 retcode=TRADE_RETCODE_PLACED 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) 3. OnTradeTransaction 2016.06.09 14:52:24.067 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118750 ORDER_TYPE_SELL ORDER_STATE_REQUEST_ADD 2016.06.09 14:52:24.071 TradeTransactionListener (Si-6.16,M15) 4. OnTradeTransaction 2016.06.09 14:52:24.071 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_UPDATE order #49118750 ORDER_TYPE_SELL ORDER_STATE_PLACED 2016.06.09 14:52:24.073 TradeTransactionListener (Si-6.16,M15) 5. OnTradeTransaction 2016.06.09 14:52:24.073 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_DEAL_ADD deal #6945378 DEAL_TYPE_SELL Si-6.16 1.00 lot (MOEX deal=185290646) 2016.06.09 14:52:24.075 TradeTransactionListener (Si-6.16,M15) 6. OnTradeTransaction 2016.06.09 14:52:24.075 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_ORDER_DELETE order #49118750 ORDER_TYPE_SELL ORDER_STATE_PLACED 2016.06.09 14:52:24.077 TradeTransactionListener (Si-6.16,M15) 7. OnTradeTransaction 2016.06.09 14:52:24.077 TradeTransactionListener (Si-6.16,M15) MqlTradeTransaction: TRADE_TRANSACTION_HISTORY_ADD order #49118750 ORDER_TYPE_SELL Si-6.16 ORDER_STATE_FILLED (MOEX ticket=3377182821)
Agora é hora de revisar os exemplos do código-fonte.
Trabalhando com sua conta de negociação
Para começar, ao executar o robô de negociação, é necessário obter informações sobre a conta de negociação na qual ele vai operar.
Para trabalhar com a conta, existe a classe CAccountInfo, ela foi especialmente concebida para isto. Adicionamos ao nosso código de conexão o arquivo AccountInfo.mqh e e declaramos a variável dessa classe account:
#include <Trade\AccountInfo.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- objeto para trabalhar com a com a conta CAccountInfo account; //--- obtemos o número da conta em que o conselheiro (EA) está em execução long login=account.Login(); Print("Login=",login); //--- exibimos a moeda da conta Print("Валюта счета: ",account.Currency()); //--- exibimos o saldo e o lucro atual na conta Print("Balance=",account.Balance()," Profit=",account.Profit()," Equity=",account.Equity()); //--- exibimos o tipo de conta Print("Tipo de conta: ",account.TradeModeDescription()); //--- aclaramos se é possível negociar nessa conta if(account.TradeAllowed()) Print("Negociação permitida nesta conta"); else Print("Negociação restringida na conta: possivelmente o login foi feito usando a senha de investidor"); //--- mod de cálculo de margem Print("modo de cálculo de margem: ",account.MarginModeDescription()); //--- aclaramos se é permitido negociar na conta usando o conselheiro (EA) if(account.TradeExpert()) Print("Negociação automática permitida na conta de negociação"); else Print("Negociação automática restrita usando conselheiros e scripts"); //--- número máximo permitido de ordens especificadas ou não int orders_limit=account.LimitOrders(); if(orders_limit!=0)Print("Número máximo permitido de ordens pendentes atuais: ",orders_limit); //--- exibimos o nome da empresa e o servidor Print(account.Company(),": server ",account.Server()); Print(__FUNCTION__," completed"); //--- }
Como pode ser visto a partir do código acima, usando a variável account na função OnInit(), é possível obter muita informação útil. Você pode adicionar esse código ao seu conselheiro (Expert Advisor), desse modo será muito mais fácil analisar os logs, ao analisar as operações do EA.
A figura abaixo mostra o resultado da execução do script.
Obtenção das propriedades de um instrumento financeiro
Obtivemos informações sobre a conta, no entanto, a fim de poder executar operações de negociação, também precisamos saber as propriedades do ativo financeiro que vamos a operar. Para fazer isso, vamos usar mais uma classe CSymbolInfo com um número grande de métodos. Apresentamos abaixo apenas uma pequena parte deles.
#include<Trade\SymbolInfo.mqh> //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- objeto para obter as propriedades do símbolo CSymbolInfo symbol_info; //--- definimos o nome do símbolo para o qual vamos receber informações symbol_info.Name(_Symbol); //--- obtermos as cotações atuais e exibimos symbol_info.RefreshRates(); Print(symbol_info.Name()," (",symbol_info.Description(),")", " Bid=",symbol_info.Bid()," Ask=",symbol_info.Ask()); //--- obtemos o número de símbolos após o ponto e o tamanho do ponto Print("Digits=",symbol_info.Digits(), ", Point=",DoubleToString(symbol_info.Point(),symbol_info.Digits())); //--- solicitamos o tipo de execução de ordem, verificamos as restrições Print("Restrições para as operações de negociação: ",EnumToString(symbol_info.TradeMode()), " (",symbol_info.TradeModeDescription(),")"); //--- aclaramos o modo de realização das negociações Print("Modo de execução das transações: ",EnumToString(symbol_info.TradeExecution()), " (",symbol_info.TradeExecutionDescription(),")"); //--- aclaramos o modo de cálculo do custo do contrato Print("Cálculo do custo do contrato: ",EnumToString(symbol_info.TradeCalcMode()), " (",symbol_info.TradeCalcModeDescription(),")"); //--- tamanho do contrato Print("Tamanho do contrato padrão: ",symbol_info.ContractSize()); //--- tamanho da margem inicial para 1 contrato Print("Margem inicial para o contrato padrão: ",symbol_info.MarginInitial()," ",symbol_info.CurrencyBase()); //--- tamanhos mínimo e máximo de volume nãs operações de negociação Print("Volume info: LotsMin=",symbol_info.LotsMin()," LotsMax=",symbol_info.LotsMax(), " LotsStep=",symbol_info.LotsStep()); //--- Print(__FUNCTION__," completed"); }
A figura abaixo mostra as propriedades do símbolo Si-6.16 a partir das seções do mercado FORTS da Bolsa de Valores de Moscou. Agora estamos prontos para ir diretamente para a negociação.
Programação de operações de negociação
Para enviar ordens de negociação, na linguagem MQL5 existem duas funções, isto é, OrderSend() e OrderSendAsync(). Na verdade, estas são duas implementações da mesma função. A função OrderSend() envia o pedido de negociação e espera o resultado de sua execução, enquanto a função assíncrona OrderSendAsync() simplesmente envia o pedido e permite continuar trabalhando com o programa, sem esperar pela resposta do servidor de negociação. Assim, o trabalho na MQL5 se torna realmente simples, pois basta uma função para todas as operações de negociação
Ambas as funções obtém, como primeiro parâmetro, a estrutura MqlTradeRequest, ela contém mais de uma dúzia de campos. O conjunto de campos necessários depende do tipo de operação de negociação, por isso nem todos os campos precisam ser preenchidos Se um valor estiver incorreto ou faltar um campo obrigatório, o pedido falhará ao passar a verificação no terminal, e simplesmente não será enviado para o servidor. 5 campos requerem que você especifique o valor correto a partir das enumerações predefinidas.
O pedido de negociação tem um número tão grande de campos, devido à necessidade de descrever o conjunto de propriedades da ordem, elas podem variar dependendo da política de execução, o tempo de expiração e outros parâmetros. Você não precisa memorizar todos esses detalhes, basta usar a classe pronta CTrade. O uso desta classe em seu robô de negociação pode ter a seguinte aparência:
#include<Trade\Trade.mqh> //--- objeto para negociação CTrade trade; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- especificamos o MagicNumber para identificação de nossas ordens int MagicNumber=123456; trade.SetExpertMagicNumber(MagicNumber); //--- definimos a derrapagem (slippage) permitida em pontos ao efetuar uma compra/venda int deviation=10; trade.SetDeviationInPoints(deviation); //--- modo de execução da ordem, é preciso usar esse modo, porque ele é permitido pelo servidor trade.SetTypeFilling(ORDER_FILLING_RETURN); //--- modo log: é melhor não chamar esse método, a classe e si definirá o melhor modo trade.LogLevel(1); //--- que função usar para negociar: true - OrderSendAsync(), false - OrderSend() trade.SetAsyncMode(true); //--- return(0); }
Para negociar na bolsa de valores, habitualmente usa-se o modo de execução ORDER_FILLING_RETURN. Descrição:
Esse modo se usa apenas nos modos "Execução no mercado" e "Execução na bolsa": para mercados (ORDER_TYPE_BUY e ORDER_TYPE_SELL), ordens limite e stop limite(ORDER_TYPE_BUY_LIMIT, ORDER_TYPE_SELL_LIMIT, ORDER_TYPE_BUY_STOP_LIMIT e ORDER_TYPE_SELL_STOP_LIMIT). No caso de uma execução parcial, a ordem de mercado ou limite com um volume residual não é removida e permanece em vigor.
Durante a ativação das ordens ORDER_TYPE_BUY_STOP_LIMIT e ORDER_TYPE_SELL_STOP_LIMIT, será criado a ordem limite correspondente ORDER_TYPE_BUY_LIMIT/ORDER_TYPE_SELL_LIMIT com execução ORDER_FILLING_RETURN.
É hora de ver como CTrade ajuda nas operações de negociação.
Compra/venda aos preços disponíveis
Muitas vezes, em estratégias de negociação, é necessária comprar ou vender aos preços que estejam disponíveis nesse momento. O CTrade conhece esta situação e pede apenas o volume necessário de negociação. Todos os outros parâmetros, isto é, o preço de abertura, o nome do símbolo, os níveis Stop Loss e Take Profit e os comentários da ordem podem ser omitidos.
//--- 1. exemplo de compra no símbolo atual if(!trade.Buy(1)) { //--- notificamos sobre o erro Print("O método Buy() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método Buy() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Por padrão, se o nome do instrumento não for especificado, o CTrade usará o nome do símbolo, no gráfico em que ele está em execução. Isso facilita o trabalho para estratégias simples. Para o robô, que negocia em vários instrumentos, você precisa a cada vez especificar explicitamente o símbolo no qual a negociação será realizada.
//--- 2. exemplo de compra em um símbolo especificado if(!trade.Buy(1,"Si-6.16")) { //--- notificamos sobre o erro Print("O método Buy() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método Buy() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Você pode especificar todos os parâmetros da ordem: os níveis Stop Loss/Take Profit, o preço de abertura e o comentário.
//--- 3. exemplo de compra no símbolo especificado com SL e TP especificados double volume=1; // indicamos o volume da operação de negociação string symbol="Si-6.16"; // indicamos o símbolo no qual é realizada a operação int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // número de caráteres após a vírgula double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ponto double bid=SymbolInfoDouble(symbol,SYMBOL_BID); // preço atual para fechamento LONG double SL=bid-100*point; // valor não-normalizado SL SL=NormalizeDouble(SL,digits); // normalizamos o Stop Loss double TP=bid+100*point; // valor não-normalizado TP TP=NormalizeDouble(TP,digits); // normalizamos o Take Profit //--- obtemos o preço atual de abertura para posições LONG double open_price=SymbolInfoDouble(symbol,SYMBOL_ASK); string comment=StringFormat("Buy %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(open_price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); if(!trade.Buy(volume,symbol,open_price,SL,TP,comment)) { //--- notificamos sobre o erro Print("O método Buy() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método Buy() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Lembramos que o MagicNumber e a derrapagem permissível são definidas ao inicializar a instância CTrade, por isso não precisamos defini-los agora. Embora eles também podem ser definidos diretamente antes de cada transação, se necessário.
Colocação de uma ordem limite
Para enviar uma ordem limite, é usado o método apropriado da classe BuyLimit() ou SellLimit(). Uma versão breve, onde você especifica somente o preço e o volume, será suficiente na maioria dos casos. O preço de abertura para BuyLimit deve ser inferior ao preço atual, enquanto para SellLimit, deve ser superior. Ou seja, essas ordens são usados para entrar no mercado ao melhor preço, por exemplo, nas estratégias à expectativa de uma recuperação do nível de suporte. Neste caso, usa-se o símbolo em que o conselheiro (EA) está em execução:
//--- 1. exemplo de colocação de uma ordem pendente BuyLimit string symbol="Si-6.16"; // definimos o símbolo no qual é colocada a ordem int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // número de caráteres após a vírgula double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ponto double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // preço atual de compra double price=ask-100*point; // preço de abertura não-normalizado price=NormalizeDouble(price,digits); // normalizamos o preço de abertura //--- tudo está pronto, enviamos para o servidor a ordem pendente Buy Limit if(!trade.BuyLimit(1,price)) { //--- notificamos sobre o erro Print("O método BuyLimit() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método BuyLimit() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Você também pode usar uma versão mais detalhada mostrando todos os parâmetros: níveis SL/TP, tempo de expiração, nome do instrumento e um comentário para a ordem.
//--- 2. exemplo de como colocar uma ordem pendente BuyLimit com todos os parâmetros double volume=1; string symbol="Si-6.16"; // definimos o símbolo no qual é colocada a ordem int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // número de caráteres após a vírgula double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ponto double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // preço atual de compra double price=ask-100*point; // preço de abertura não-normalizado price=NormalizeDouble(price,digits); // normalizamos o preço de abertura int SL_pips=100; // Stop Loss em pontos int TP_pips=100; // Take Profit em pontos double SL=price-SL_pips*point; // valor não-normalizado SL SL=NormalizeDouble(SL,digits); // normalizamos o Stop Loss double TP=price+TP_pips*point; // valor não-normalizado TP TP=NormalizeDouble(TP,digits); // normalizamos o Take Profit datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); string comment=StringFormat("Buy Limit %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); //--- tudo está pronto, enviamos para o servidor a ordem pendente Buy Limit if(!trade.BuyLimit(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment)) { //--- notificamos sobre o erro Print("O método BuyLimit() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método BuyLimit() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Na segunda versão, é necessário especificar corretamente os níveis SL e TP. Não se esqueça que, para a compra, o nível Take Profit deve ser superior ao preço de abertura, enquanto o nível Stop Loss, inferior ao preço de abertura. Para ordens SellLimit é tudo ao contrário. Você pode facilmente descobrir seu erro ao testar o conselheiro (EA) de acordo como os dados históricos, a classe CTrade exibe automaticamente mensagens em tais casos (se você não chamar funções LogLevel).
Colocação de uma ordem stop
Para colocar ordens stop, usam-se os métodos BuyStop() e SellStop(). O preço de abertura para Buy Stop deve ser superior ao preço atual, enquanto para SellStop, inferior. As ordens stop se usam nas estratégias que entram no mercado quando existe uma fuga de um nível de resistência, bem como para limitar as perdas. Versão simples:
//--- 1. exemplo de colocação de uma ordem pendente Buy Stop string symbol="RTS-6.16"; // definimos o símbolo no qual é colocada a ordem int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // número de caráteres após a vírgula double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ponto double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // preço atual de compra double price=ask+100*point; // preço de abertura não-normalizado price=NormalizeDouble(price,digits); // normalizamos o preço de abertura //--- tudo está pronto, enviamos para o servidor a ordem pendente Buy Stop if(!trade.BuyStop(1,price)) { //--- notificamos sobre o erro Print("O método BuyStop() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método BuyStop() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Esta é uma versão mais detalhada de como especificar os parâmetros máximos para a ordem pendente BuyStop:
//--- 2. exemplo de colocação de uma ordem pendente Buy Stop com todos os parâmetros double volume=1; string symbol="RTS-6.16"; // definimos o símbolo no qual é colocada a ordem int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // número de caráteres após a vírgula double point=SymbolInfoDouble(symbol,SYMBOL_POINT); // ponto double ask=SymbolInfoDouble(symbol,SYMBOL_ASK); // preço atual de compra double price=ask+100*point; // preço de abertura não-normalizado price=NormalizeDouble(price,digits); // normalizamos o preço de abertura int SL_pips=100; // Stop Loss em pontos int TP_pips=100; // Take Profit em pontos double SL=price-SL_pips*point; // valor não-normalizado SL SL=NormalizeDouble(SL,digits); // normalizamos o Stop Loss double TP=price+TP_pips*point; // valor não-normalizado TP TP=NormalizeDouble(TP,digits); // normalizamos o Take Profit datetime expiration=TimeTradeServer()+PeriodSeconds(PERIOD_D1); string comment=StringFormat("Buy Stop %s %G lots at %s, SL=%s TP=%s", symbol,volume, DoubleToString(price,digits), DoubleToString(SL,digits), DoubleToString(TP,digits)); //--- tudo está pronto, enviamos para o servidor a ordem pendente Buy Stop if(!trade.BuyStop(volume,price,symbol,SL,TP,ORDER_TIME_DAY,expiration,comment)) { //--- notificamos sobre o erro Print("O método BuyStop() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método BuyStop() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Para enviar uma ordem SellStop, é implementado o método apropriado da classe CTrade, o mais importante aqui é especificar o preço correto.
Trabalhando com a posição
Em vez do usar os métodos Buy() e Sell(), você pode implementar os métodos para abertura da posição. No entanto, neste caso, é necessário especificar mais detalhes:
//--- número de símbolos após a vírgula int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //--- valor do punto double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- obtemos o preço de compra double price=SymbolInfoDouble(_Symbol,SYMBOL_ASK); //--- calculamos e normalizamos os níveis SL e TP double SL=NormalizeDouble(price-100*point,digits); double TP=NormalizeDouble(price+100*point,digits); //--- preenchemos os comentários string comment="Buy "+_Symbol+" 1 at "+DoubleToString(price,digits); //--- tudo está pronto, tentamos abrir a posição para compra if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,1,price,SL,TP,comment)) { //--- notificamos sobre o erro Print("O método PositionOpen() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método PositionOpen() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Para fechar a posição, basta indicar o nome do instrumento, o trabalho restante é feito pela a classe CTrade.
//--- fechamos a posição de acordo como o símbolo atual if(!trade.PositionClose(_Symbol)) { //--- notificamos sobre o erro Print("O método PositionClose() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método PositionClose() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Na posição aberta, você pode alterar os níveis StopLoss e TakeProfit. Isto é feito usando o método ModifyPosition().
//--- número de símbolos após a vírgula int digits=(int)SymbolInfoInteger(_Symbol,SYMBOL_DIGITS); //--- valor do punto double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); //--- obtemos o preço atual Bid double price=SymbolInfoDouble(_Symbol,SYMBOL_BID); //--- calculamos e normalizamos os níveis SL e TP double SL=NormalizeDouble(price-100*point,digits); double TP=NormalizeDouble(price+100*point,digits); //--- tudo está pronto, tentamos modificar a posição para compra if(!trade.PositionModify(_Symbol,SL,TP)) { //--- notificamos sobre o erro Print("O método PositionModify() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método PositionModify() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Modificação e exclusão da ordem
Para alterar os parâmetros da ordem pendente, na classe CTrade existe o método OrderModify(), ao qual devem ser passados os parâmetros necessários.
//--- verificamos a presença da ordem if(!OrderSelect(ticket)) { Print("Ordem #",ticket," não encontrada"); return; } //--- símbolo string symbol=OrderGetString(ORDER_SYMBOL); //--- número de símbolos após a vírgula int digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); //--- valor do punto double point=SymbolInfoDouble(symbol,SYMBOL_POINT); //--- obtemos o preço de abertura double price=OrderGetDouble(ORDER_PRICE_OPEN); //--- calculamos e normalizamos os níveis SL e TP double SL=NormalizeDouble(price-200*point,digits); double TP=NormalizeDouble(price+200*point,digits); //--- tudo está pronto, tentamos modificar a ordem if(!trade.OrderModify(ticket,price,SL,TP,ORDER_TIME_DAY,0)) { //--- notificamos sobre o erro Print("O método OrderModify() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método OrderModify() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Você precisa obter o bilhete da ordem que você deseja alterar e, dependendo do seu tipo, especificar os níveis corretos StopLoss e TakeProfit. Além disso, o novo preço de abertura também deve ser correto em relação ao preço atual.
Para excluir uma ordem pendente, basta saber o seu bilhete:
//--- verificamos a presença da ordem if(!OrderSelect(ticket)) { Print("Ordem #",ticket," não encontrada"); return; } //--- tudo está pronto, tentamos excluir a ordem if(!trade.OrderDelete(ticket)) { //--- notificamos sobre o erro Print("O método OrderDelete() falhou. Código de retorno=",trade.ResultRetcode(), ". Descrição do código: ",trade.ResultRetcodeDescription()); } else { Print("O método OrderDelete() foi bem-sucedido. Código de retorno=",trade.ResultRetcode(), " (",trade.ResultRetcodeDescription(),")"); }
Na classe també existe o método universal OrderOpen(), ele pode colocar ordens pendentes de quaisquer tipos. Em contraste como os métodos especializados BuyLimit, BuyStop, SellLimit e SellStop, ele exige definir os parâmetros mais necessários. Talvez alguns achem que ele é mais simples.
Que outra coisa ver nas classes de negociação
Neste artigo, mostramos técnicas simples para programar operações de negociação de compra e venda, bem como trabalhar com ordens pendentes. Mas, na seção Classes de negociação, existem vários assistentes amigáveis para desenvolvimento de robôs de negociação na MQL5:
- COrderInfo — para trabalhar com ordens;
- CHistoryOrderInfo — para trabalhar com ordens do histórico de negociação;
- CPositionInfo — para trabalhar com posições;
- CDealInfo — para trabalhar com transações;
- CTerminalInfo — para obter informações sobre o terminal.
Usando essas classes, você pode se concentrar apenas em sua estratégia, trazendo todas as questões técnicas a um mínimo. Além disso, pode usar a classe CTrade para estudar os pedidos de negociação, por exemplo, usando o depurador. Depois de algum tempo, você poderá criar suas próprias classes, nelas implementará a lógica necessária para processar resultados de execução de ordens de negociação.
Comece sua viagem à negociação algorítmica com scripts simples
Embora muitos desenvolvedores experientes também possam encontrar algo novo e útil, os métodos propostos neste artigo estão destinados principalmente para desenvolvedores de robôs de negociação iniciantes. Tente executar os scripts simples descritos neste artigo para entender que a criação de um robô de negociação é mais fácil do que pensava.
Para aqueles que decidiram ir mais longe, oferecemos mais dois artigos sobre este tópico:
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2513
- 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