English Русский Deutsch 日本語
preview
Balanceando riscos ao negociar múltiplos instrumentos simultaneamente

Balanceando riscos ao negociar múltiplos instrumentos simultaneamente

MetaTrader 5Exemplos | 15 julho 2024, 15:50
24 0
Aleksandr Seredin
Aleksandr Seredin

Este artigo abordará o tema de balancear riscos ao negociar múltiplos instrumentos intraday ao mesmo tempo. O propósito deste artigo é permitir que o usuário escreva um código para balancear instrumentos do zero e apresentar aos usuários experientes outras implementações, talvez não usadas anteriormente, de ideias antigas. Para fazer isso, consideraremos a definição do conceito de risco, selecionaremos critérios para otimização, focaremos nos aspectos técnicos da implementação de nossa solução, analisaremos o conjunto de capacidades padrão do terminal para tal implementação, e também abordaremos outras formas possíveis de integrar este algoritmo em sua infraestrutura de software.


Critérios para balancear instrumentos de negociação por riscos

Ao negociar vários instrumentos financeiros simultaneamente, levaremos em conta dois fatores principais como critérios de balanceamento de risco.

  • Preço do tick do símbolo
  • Volatilidade média diária do símbolo

O preço do tick é um valor em moeda da mínima variação de preço em um símbolo com um lote padrão do símbolo. Levamos esse critério em consideração porque o valor do tick pode variar significativamente em diferentes instrumentos. Por exemplo, de 1.27042 para EURGBP a 0.61374 para AUDNZD.

A volatilidade média diária é a mudança característica no preço do símbolo ao longo de um dia. Esse valor é menos constante em diferentes símbolos do que o critério selecionado anteriormente e pode mudar ao longo do tempo dependendo do estágio do mercado. Por exemplo, o EURGBP geralmente se move em média cerca de 336 pontos, enquanto o CHFJPY pode se mover 1271 pontos no mesmo dia, o que é quase quatro vezes mais. Os dados apresentados aqui caracterizam os valores "usuais" e "mais prováveis" de volatilidade de preço sem levar em conta a volatilidade anormalmente alta do símbolo em certos momentos quando o preço começa a se mover muito fortemente em uma direção sem retrocesso. Abaixo está um exemplo de tal movimento no USDJPY.

Figura 1. Aumento da volatilidade do símbolo no gráfico D1

Figura 1. Aumento da volatilidade do símbolo no gráfico D1

Esse comportamento pode causar riscos muito sérios para o depósito, que são descritos em detalhes suficientes no artigo "Como reduzir os riscos do trader". Neste artigo, proporemos a tese de que é impossível se proteger contra tal risco balanceando instrumentos. Esta é uma categoria completamente diferente de risco. Ao balancear, podemos proteger o depósito contra o risco de que o mercado vá contra nossa posição dentro da estrutura da volatilidade média. Se você quiser proteger seus fundos contra movimentos anormais, ou "cisnes negros", então use os seguintes princípios. 

  • não assuma um grande percentual de risco em um único símbolo, 
  • não busque estar em uma posição aberta constantemente, 
  • não coloque todos os fundos sob gestão em uma única conta com um único corretor, 
  • não negocie as mesmas entradas em diferentes contas e corretores simultaneamente. 

Seguindo esses princípios, você pode minimizar as perdas se acontecer que o preço do símbolo vá contra você quando estiver em uma posição aberta. Agora vamos voltar a considerar os riscos associados à volatilidade padrão.

A consideração simultânea desses dois fatores permitirá balancear os riscos e normalizar os lucros esperados para cada par de moedas ao negociar simultaneamente, sem sobrecarregar os riscos em qualquer instrumento único. Essa abordagem fornecerá posteriormente estatísticas de negociação mais homogêneas para análise posterior na história de transações e reduzirá o erro quando o otimizador de estratégia trabalhar com esses dados e, consequentemente, reduzirá o desvio padrão da amostra dos dados médios. Para entender mais detalhadamente como o valor do desvio padrão afeta a qualidade da análise de um conjunto, você pode ler o artigo "Matemática na negociação: Como estimar resultados comerciais". Agora vamos passar para a escolha de um contêiner para armazenar dados.


Selecionando contêineres para armazenamento de dados 

Ao selecionar contêineres para armazenamento de dados em nosso projeto, levaremos em conta os seguintes fatores:

  • desempenho do contêineres
  • requisito de memória para sua inicialização
  • disponibilidade de funcionalidade embutida para análise de dados
  • facilidade de inicialização através da interface do usuários

Os critérios mais comuns ao escolher contêineres para armazenamento de dados são o desempenho dos contêineres e a necessidade de memória do computador para armazená-los. Diferentes tipos de armazenamento podem geralmente fornecer melhor desempenho ao processar dados, ou um ganho na quantidade de memória ocupada.

Para realizar uma verificação, vamos declarar um array simples e um contêiner especial do tipo vector, enquanto os inicializamos previamente com o tipo de dados double e valores idênticos.

   double arr[] = {1.5, 2.3};
   vector<double> vect = {1.5, 2.3};
Use a operação sizeof e determine o tamanho da memória que corresponde aos tipos acima no estágio de compilação.
Print(sizeof(arr));
Print(sizeof(vect));

Como resultado, obtemos 16 e 128 bytes. Essa diferença nos requisitos de memória para o tipo de dado vector é determinada pela presença de funcionalidade embutida, incluindo alocação de memória redundante adicional para garantir melhor desempenho.

Com base nisso, usaremos um tipo de armazenamento simples como um array, considerando nossas tarefas para contêineres que requerem apenas armazenar dados previamente selecionados. Para tipos de dados homogêneos que lidaremos posteriormente durante nosso algoritmo, seria aconselhável usar o tipo de dado especial vector. Usar esse tipo também economizará tempo de desenvolvimento em termos de escrever funções personalizadas para operações padrão, que já estão implementadas em vector de fábrica.

Como resultado, o armazenamento de dados necessário para o cálculo será assim.
   string symbols[];       // symbols used for balancing

   double tick_val[],      // symbol tick price
          atr[],           // symbol volatility
          volume[],        // calculated position volume for symbols taking into account balancing 
          point[];         // value of one price change point 

   vector<double> risk_contract; // risk amount for a standard contract

Agora vamos passar a considerar opções para implementar soluções para entrada de dados para símbolos balanceados.


Selecionando um método de entrada de símbolo

Existem muitas soluções ao escolher métodos de entrada de dados no MetaTrader 5. Globalmente, elas são divididas em soluções voltadas para interagir diretamente com o usuário através de uma caixa de diálogo padrão do terminal, ou para interagir com outras aplicações através de arquivos salvos no disco, ou interfaces de interação remota.

Dada a necessidade de inserir muitos símbolos armazenados no terminal no formato string, podemos destacar as seguintes opções possíveis para implementar a entrada de dados em nosso script:

  1. ler dados do arquivo de tabela *.csv
  2. ler dados do arquivo binário *.bin
  3. ler dados do arquivo de banco de dados .sqlite
  4. usar métodos de terceiros para interação do terminal com bancos de dados remotos
  5. usar soluções de API web
  6. uso padrão do terminal de uma variável(s) do tipo apropriado com um modificador de classe de memória do tipo input

Antes de selecionar um método de entrada de dados em nosso projeto, consideraremos brevemente os prós e contras de cada opção para implementar essa tarefa. Ao escolher o método descrito no ponto 1, a leitura de dados de um arquivo de tabela do tipo *.csv pode ser implementada através das funções padrão do terminal para manipulação de arquivos. Em geral, o código pode se parecer com isso:

   string file_name = "Inputs.csv";                         // file name

   int handle=FileOpen(file_name,FILE_CSV|FILE_READ,";");   // attempt to find and open the file

   if(handle!=INVALID_HANDLE)                               // if the file is found, then
     {
      while(FileIsEnding(handle)==false)                    // start reading the file
        {
         string str_follow = FileReadString(handle);        // reading
        
        // here we implement filling the container depending on its type
        }
     }

Outras opções para implementar a funcionalidade de trabalho com arquivos são descritas em detalhes suficientes na documentação do terminal. Nesta opção, o usuário só precisa preparar um arquivo de parâmetro de entrada usando um aplicativo de planilha de terceiros, como MS Excel ou OpenOffice. Até mesmo o Bloco de Notas padrão do Windows serve.

No segundo ponto, a função padrão do terminal FileLoad() é adequada para usar arquivos *.bin. Para usá-lo, você precisará saber de antemão qual estrutura de dados a aplicação usou ao salvar este arquivo para ler dados de um arquivo com esta extensão. A implementação de tal ideia pode ser assim.

   struct InputsData                   // take the structure, according to which the binary file was created 
     {
      int                  symbol_id;  // id of a balanced symbol
      ENUM_POSITION_TYPE   type;       // position type
     };

   InputsData inputsData[];            // storage of inputs

   string  filename="Inputs.bin";      // file name

   ArrayFree(inputsData);              // array released

   long count=FileLoad(filename,inputsData,FILE_COMMON); // load file

A principal desvantagem dessa abordagem é que a função FileLoad() não funciona com estruturas de dados que contêm tipos de dados de objeto. Consequentemente, não funcionará com uma estrutura se ela contiver o tipo de dados string. Neste caso, você terá que usar adicionalmente dicionários de contêineres personalizados para converter o id dos caracteres no valor inteiro int apropriado para o tipo de dados string ou fazer uma solicitação adicional ao banco de dados correspondente. Em geral, esse método não será o mais bem-sucedido para nossa implementação devido à complexidade excessiva ao realizar operações bastante simples.

O terceiro ponto especificamente sugere usar a funcionalidade embutida no terminal para trabalhar com um banco de dados de arquivos .sqlite. Essa é uma opção embutida no terminal para trabalhar com um banco de dados relacional construído na interação com um arquivo salvo no disco rígido.

   string filename="Inputs.sqlite"; // file name with inputs prepared in advance

   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE |
                       DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON); // open the database

   if(db!=INVALID_HANDLE)                                            // if opened
     {
      // implement queries to the database using the DatabaseExecute() function
      // the query structure will depend on the structure of the database tables
     }

Na implementação dessa abordagem, será importante inicialmente construir a estrutura das tabelas do banco de dados. A estrutura das consultas para obter os dados necessários dependerá disso. A principal vantagem dessa abordagem será a possibilidade de armazenamento relacional de dados, na qual os ids de ferramentas serão armazenados em tabelas em formato inteiro em vez de formato string, o que pode fornecer um ganho muito significativo na otimização do espaço em disco do computador. Também vale a pena notar que, sob certas condições, essa versão do banco de dados pode ser muito produtiva. Veja mais detalhes no artigo "SQLite: Manipulação nativa de bancos de dados SQL no MQL5".

O quarto parágrafo descreve a opção de aplicar bancos de dados remotos, o que exigirá o uso de bibliotecas de terceiros adicionais. Essa opção será mais trabalhosa do que o método descrito no parágrafo anterior, pois não pode ser totalmente implementada através da funcionalidade padrão do terminal. Há muitas publicações sobre o assunto, incluindo uma boa opção para implementar a interação do terminal com o banco de dados MySQL, descrita no artigo "Como acessar o banco de dados MySQL a partir do MQL5 (MQL4)".

O uso de solicitações de API web para obter entradas, descrito no quinto parágrafo, provavelmente pode ser considerado a solução mais universal e multiplataforma para nossa tarefa. A funcionalidade é integrada ao terminal através da função WebRequest() predefinida e será perfeita se você já tiver uma infraestrutura para aplicativos back-end e front-end devido à sua versatilidade. Caso contrário, pode levar bastante tempo e recursos para desenvolver esses aplicativos do zero, embora essas soluções possam ser escritas em muitas linguagens de programação modernas e interpretadores.

Na implementação atual, usaremos variáveis com o modificador de classe de memória do tipo input do tipo de dados string</a> do ponto seis, simplesmente porque essa opção é capaz de fornecer toda a funcionalidade necessária sem a necessidade de desenvolvimento adicional de programas personalizados. Naturalmente, não declararemos muitas variáveis para cada símbolo, pois não podemos saber de antemão quantos símbolos balancearemos, e há uma solução mais elegante e flexível para isso. Trabalharemos com uma linha contendo todos os valores com posterior separação dos dados nela. Para fazer isso, declaramos uma variável no nível global na seguinte forma:

input string input_symbols = "EURCHFz USDJPYz";

Certifique-se de inicializá-la com o valor padrão para que um usuário que não seja desenvolvedor entenda exatamente como inserir símbolos para que o aplicativo funcione corretamente. Nesse caso, usaremos um espaço regular como separador de linha para conveniência do usuário.

Organizaremos a obtenção de dados dessa variável e o preenchimento de nosso array symbols[] usando uma função predefinida do terminal para manipulação de strings StringSplit() na seguinte forma:

   string symbols[];                         // storage of user-entered symbols
   
   StringSplit(input_symbols,' ',symbols);   // split the string into the symbols we need

   int size = ArraySize(symbols);            // remember the size of the resulting array right away

Ao executar a função StringSplit(), o array symbols[] passado para ela por referência é preenchido com dados extraídos da string usando o delimitador (' ') em forma de espaço.

Agora que temos um array preenchido com os valores dos nomes dos símbolos para balanceamento de risco, vamos passar a solicitar os dados necessários sobre os símbolos do terminal selecionado para cálculos posteriores.


Obtendo os dados necessários do símbolo através de funções predefinidas do terminal

Para cálculos, precisaremos saber o valor da mínima variação no preço de cada instrumento e quanto essa variação nos custará na moeda do depósito. Podemos implementar isso através da função predefinida do terminal SymbolInfoDouble() com a enumeração necessária ENUM_SYMBOL_INFO_DOUBLE como um dos parâmetros da função. A implementação da enumeração de dados será organizada através do popular loop 'for' da seguinte forma:

for(int i=0; i<size; i++)  // loop through previously entered symbols
     {
      point[i] = SymbolInfoDouble(symbols[i],SYMBOL_POINT);                	// requested the minimum price change size (tick)
      tick_val[i] = SymbolInfoDouble(symbols[i],SYMBOL_TRADE_TICK_VALUE_LOSS);  // request tick price in currency
     }

Note que a enumeração ENUM_SYMBOL_INFO_DOUBLE contém não apenas o valor SYMBOL_TRADE_TICK_VALUE_LOSS para solicitar o preço do tick em moeda, mas também o valor SYMBOL_TRADE_TICK_VALUE e o valor SYMBOL_TRADE_TICK_VALUE_PROFIT igual a ele. Na verdade, uma solicitação para qualquer um dos valores especificados pode ser usada em nosso cálculo, pois a diferença nesses valores não é muito significativa. Por exemplo, os valores dos argumentos especificados para o cruzamento AUDNZD são apresentados na tabela a seguir:

Parâmetro da função Valor do preço do tick retornado pela função
SYMBOL_TRADE_TICK_VALUE 0.6062700000000001
SYMBOL_TRADE_TICK_VALUE_LOSS 0.6066200000000002
SYMBOL_TRADE_TICK_VALUE_PROFIT 0.6062700000000001

Tabela 1. Diferença nos valores do preço do tick retornados para diferentes parâmetros da função SymbolInfoDouble() para AUDNZD

Apesar de ser mais correto usar o parâmetro SYMBOL_TRADE_TICK_VALUE_LOSS em nosso caso, acredito que podemos usar qualquer uma das opções propostas aqui. Como Richard Hamming observou em 1962: 

"O propósito da computação é a percepção, não os números"

Vamos passar para a solicitação dos dados necessários sobre a volatilidade.


Obtendo os dados de volatilidade necessários através da classe customizada padrão CiATR

Nos capítulos anteriores, já mencionamos o conceito de volatilidade como um indicador que caracteriza uma mudança típica de preço para um símbolo durante um certo período de tempo, no nosso caso durante um dia de negociação. Apesar de toda a obviedade desse indicador, os métodos para calculá-lo podem diferir bastante, principalmente devido aos seguintes aspectos:

  • levar em conta gaps não fechados em gráficos diários
  • usar médias e o período em que é aplicado
  • excluir barras com volatilidade "anormal" (excepcionalmente rara) do cálculo
  • calcular com base no máximo/mínimo da barra diária ou apenas nas aberturas/fechamentos
  • ou nós geralmente negociamos barras Renko e o tempo de média não tem importância para nós

Levar em conta gaps ao calcular a volatilidade é muito frequentemente usado no trabalho em bolsas de valores, em contraste com o mercado de câmbio, simplesmente porque gaps de mercado que não são fechados durante o dia são muito raros no mercado de câmbio e o cálculo final da volatilidade média diária não muda. O cálculo aqui diferirá apenas em que tomamos o valor máximo dos dois valores. O primeiro é a diferença entre o máximo e o mínimo de cada barra, e o segundo é a diferença entre o máximo e o mínimo da barra atual e o fechamento da barra anterior. Podemos usar a função embutida MathMax() para conseguir isso.

Ao aplicar a média dos valores obtidos, é necessário levar em conta que quanto maior o período de média, mais lento esse indicador se torna para mudanças na volatilidade do mercado. Como regra, um período de 3 a 5 dias é usado para média da volatilidade diária no mercado de câmbio. Também é aconselhável excluir movimentos anormais no mercado ao calcular o indicador de volatilidade. Isso pode ser feito automaticamente usando o valor mediano da amostra. Para fazer isso, podemos usar o método embutido Median() chamado para uma instância da classe do tipo vector.

Muitos traders preferem fazer a média levando em conta a volatilidade apenas através dos preços de abertura e fechamento sem levar em conta os pavios das velas. Esse método não é recomendado, pois pode produzir um valor muito inferior à volatilidade real do mercado. Ao usar barras Renko, a lógica muda muito e aqui a agregação da volatilidade não vem do período de negociação, mas o período de negociação é determinado pela volatilidade. Portanto, essa abordagem não nos servirá em nosso artigo.

Na nossa implementação, usaremos a solicitação de volatilidade através do indicador ATR do terminal usando a classe customizada padrão CiATR armazenada na biblioteca do terminal em formato open source. Usaremos o valor rápido 3 para a média do indicador. Em geral, o código da solicitação de volatilidade ficará assim. No nível global, declare o nome de uma variável de classe com uma chamada do construtor padrão.

CiATR indAtr[];

Aqui usamos o array de indicadores para armazenar dados simultaneamente e não sobrecarregar um indicador existente, principalmente por conveniência e pela possibilidade de expandir a funcionalidade do código. Em seguida, adicionamos o seguinte código ao nosso loop de iteração de símbolos, onde já estamos solicitando dados sobre símbolos para solicitar valores de indicadores.

indAtr[i].Create(symbols[i],PERIOD_D1, atr_period);   // create symbol and period
   
indAtr[i].Refresh();          // be sure to update data

atr[i] = indAtr[i].Main(1);   // request data on closed bars on D1

Agora que todos os dados iniciais necessários para o cálculo foram obtidos, prossiga diretamente para os cálculos.


Duas opções para a lógica de cálculo de risco para diferentes métodos de saída de uma posição

Ao escrever a lógica para balancear riscos ao negociar vários instrumentos simultaneamente, resta levar em conta os seguintes pontos importantes. Como as saídas de posições balanceadas são fornecidas em nosso sistema de negociação e como levamos em conta a correlação dos símbolos que vamos balancear. Mesmo à primeira vista, um critério claro para a correlação de cruzamentos nos mercados de câmbio nem sempre fornece um nível garantido de correlação no período de tempo em consideração e muitas vezes pode ser considerado como símbolos separados. Por exemplo, se no início do dia de negociação decidimos o conjunto de símbolos que negociaremos hoje e a direção das entradas, devemos saber o seguinte com antecedência. Fecharemos todas as posições no final do dia de negociação ou não? Consideraremos sair das posições durante o dia separadamente para cada posição ou simplesmente fecharemos todas as posições ao longo do tempo? Não pode haver uma espécie de receita universal para todos, simplesmente porque cada trader toma decisões sobre entrada e saída a partir de um conjunto bastante grande de critérios com base em seu próprio conhecimento e experiência.

Nesta implementação, faremos um cálculo universal que nos permitirá determinar de forma flexível o volume de entrada em uma posição com base no fato de que você está negociando simplesmente alterando os parâmetros de risco de entrada para o instrumento e incluindo/excluindo instrumentos que, na opinião do trader, estão correlacionados no momento. Para isso, declaramos o parâmetro de risco de entrada para um instrumento da seguinte forma.

input double risk_per_instr = 50;

Além disso, para a universalidade do uso, forneceremos a saída simultânea dos resultados do balanceamento ao regredir o risco inserido pelo usuário no símbolo de negociação considerado separadamente e levando em conta a correlação desses símbolos. Isso permitirá que o trader obtenha uma faixa de volumes de posição variados e, o mais importante, as proporções desses instrumentos para negociação simultânea. Para isso, primeiro precisamos adicionar a seguinte entrada ao nosso ciclo de cálculo principal.

risk_contract[i] = tick_val[i]*atr[i]/point[i]; // calculate the risk for a standard contract taking into account volatility and point price

Em seguida, fora do loop especificado, encontramos o instrumento em nosso conjunto com o valor máximo de risco para construir uma proporção a partir dele para balancear os volumes de negociação em todo o conjunto. A funcionalidade embutida de nosso contêiner é destinada exatamente a isso.

double max_risk = risk_contract.Max();          // call the built-in container method to find the maximum value

Agora que sabemos o risco máximo em nosso conjunto, organizamos outro loop para calcular o volume balanceado para cada instrumento em nossa amostra, desde que não haja correlação entre eles, e imediatamente exiba os resultados no diário.

for(int i=0; i<size; i++)	// loop through the size of our symbol array
     {
      volume[i] = NormalizeDouble((max_risk / risk_contract[i]) * (risk_per_instr / max_risk),calc_digits); // calculate the balanced volume
     }

Print("Separate");		// display the header in the journal preliminarily

for(int i=0; i<size; i++)	// loop through the array again
     {
      Print(symbols[i]+"\t"+DoubleToString(volume[i],calc_digits));	// display the resulting volume values
     }

Este se tornou o volume máximo para negociação intradiária em nossas posições, balanceado pela volatilidade média diária mais provável e esperada, levando em conta o risco que atribuímos ao símbolo.

Em seguida, calcule o volume de nossas posições com base na premissa de que os símbolos podem começar a se correlacionar no dia de negociação, o que pode de fato aumentar significativamente o risco esperado no símbolo em relação ao que inserimos nos parâmetros de entrada. Para isso, adicione o seguinte código onde simplesmente dividimos o valor resultante pelo número de instrumentos negociados.

Print("Complex");		// display the header in the journal preliminarily
   
for(int i=0; i<size; i++)	// loop through the array
     {
      Print(symbols[i]+"\t"+DoubleToString(volume[i]/size,calc_digits));	// calculate the minimum volume for entry
     }

Agora obtivemos os limites máximo e mínimo para os volumes de posições balanceadas por risco. O trader é capaz de determinar de forma independente o volume de entradas dentro de uma faixa dada com base nelas. O principal é aderir às proporções indicadas no cálculo. Também vale a pena notar que registrar resultados é o método mais simples, mas não o único. Você também pode usar outras funções padrão do terminal para exibir informações para o usuário. Neste caso, o terminal oferece uma funcionalidade muito ampla, incluindo o uso de funções como MessageBox(), Alert(), SendNotification(), SendMail() e muitas outras. Passamos para o código completo do EA no arquivo compilado.


Implementação final da solução no scripts 

Como resultado, o código da nossa implementação ficará assim.

#property strict		

#include <Indicators\Oscilators.mqh>

//---
input string input_symbols = "EURCHFz USDJPYz";
input double risk_per_instr = 50;
input int atr_period = 3;
input int calc_digits = 3;
CiATR indAtr[];


//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
   string symbols[];
   StringSplit(input_symbols,' ',symbols);   

   int size = ArraySize(symbols);
   double tick_val[], atr[], volume[], point[];
   vector<double> risk_contract;

   ArrayResize(tick_val,size);
   ArrayResize(atr,size);
   ArrayResize(volume,size);
   ArrayResize(point,size);
   ArrayResize(indAtr,size);
   risk_contract.Resize(size);

   for(int i=0; i<size; i++)
     {
      indAtr[i].Create(symbols[i],PERIOD_D1, atr_period);
      indAtr[i].Refresh();

      point[i] = SymbolInfoDouble(symbols[i],SYMBOL_POINT);
      tick_val[i] = SymbolInfoDouble(symbols[i],SYMBOL_TRADE_TICK_VALUE);

      atr[i] = indAtr[i].Main(1);
      risk_contract[i] = tick_val[i]*atr[i]/point[i];
     }

   double max_risk = risk_contract.Max();
   Print("Max risk in set\t"+symbols[risk_contract.ArgMax()]+"\t"+DoubleToString(max_risk));

   for(int i=0; i<size; i++)
     {
      volume[i] = NormalizeDouble((max_risk / risk_contract[i]) * (risk_per_instr / max_risk),calc_digits);
     }

   Print("Separate");
   for(int i=0; i<size; i++)
     {
      Print(symbols[i]+"\t"+DoubleToString(volume[i],calc_digits));
     }

   Print("Complex");
   for(int i=0; i<size; i++)
     {
      Print(symbols[i]+"\t"+DoubleToString(volume[i]/size,calc_digits));
     }
  }
//+------------------------------------------------------------------+

Após a compilação, a janela de entradas aparece. Por exemplo, queremos balancear três símbolos e o risco máximo é de USD 500.


Figura 2. Entradas do trader

Figura 2. Entradas do trader

Como resultado da execução do script, os seguintes dados serão obtidos para o volume de cada símbolo, levando em conta o balanceamento de risco.

Figura 3. Dados de saída

Figura 3. Dados de saída

Aqui está um código completo para calcular volumes de negociação simultâneos de vários símbolos balanceados por risco usando a funcionalidade mais simples, mas minimamente necessária, fornecida pelo terminal. Se desejar, podemos escalar este algoritmo usando os recursos adicionais indicados no artigo, bem como nossos próprios desenvolvimentos.


Conclusão

Recebemos um script totalmente funcional que permitirá aos traders que não negociam através de negociação algorítmica ajustar rapidamente e com precisão os volumes de posição ao negociar intraday. Espero que mesmo para aqueles que negociam algoritmicamente, haja novas ideias aqui para melhorar sua infraestrutura de software e possivelmente melhorar seus resultados de negociação atuais. Obrigado por ler e pelos comentários!

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

Arquivos anexados |
RiskBallance.mq5 (2.27 KB)
Construindo e testando sistemas de negociação com o Canal Keltner Construindo e testando sistemas de negociação com o Canal Keltner
Neste artigo, tentaremos fornecer sistemas de negociação usando um conceito muito importante no mercado financeiro, que é a volatilidade. Forneceremos um sistema de negociação baseado no indicador Canal Keltner após compreendê-lo e como podemos codificá-lo e criar um sistema de negociação baseado em uma estratégia simples de negociação e testá-lo em diferentes ativos.
Desenvolvendo um sistema de Replay (Parte 56): Adequando os Módulos Desenvolvendo um sistema de Replay (Parte 56): Adequando os Módulos
Apesar dos módulos estarem se comunicando de maneira adequada, existe uma falha quando é tentado usar o indicador de mouse no serviço de replay. Precisamos corrigir isto agora, antes de dar o próximo passo. Além disto, havia uma falha que finalmente foi devidamente corrigida no código do indicador de mouse. Então esta versão finalmente se tornou estável, e devidamente finalizada.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Introdução ao MQL5 (Parte 4): Estruturas, classes e funções de tempo Introdução ao MQL5 (Parte 4): Estruturas, classes e funções de tempo
Nesta série, continuamos a desvendar os segredos da programação. No novo artigo, vamos estudar as bases das estruturas, classes e funções de tempo e adquirir novas habilidades para programação eficiente. Este guia pode ser útil não apenas para iniciantes, mas também para desenvolvedores experientes, pois simplifica conceitos complexos, fornecendo informações valiosas para dominar o MQL5. Continue aprendendo coisas novas, aperfeiçoe suas habilidades de programação e domine o mundo da negociação algorítmica.