English Русский 中文 Español Deutsch 日本語
preview
Indicadores baseados na classe CCanvas: Preenchendo canais com transparência

Indicadores baseados na classe CCanvas: Preenchendo canais com transparência

MetaTrader 5Exemplos | 30 junho 2023, 09:37
563 0
Samuel Manoel De Souza
Samuel Manoel De Souza

Introdução

Neste artigo, exploraremos os métodos de criação de indicadores personalizados que são desenhados usando a classe CCanvas da Biblioteca Padrão. Daremos especial atenção aos indicadores que preenchem a área entre duas linhas com uma cor sólida. Primeiro, examinaremos as razões pelas quais o uso da classe CCanvas pode ser a melhor opção em comparação com outras alternativas para indicadores desse tipo. Em seguida, analisaremos algumas propriedades do gráfico necessárias para o cálculo de coordenadas e o processo principal envolvido no trabalho com CCanvas.

O objetivo final é criar um indicador que aplique transparência. Todo o trabalho será realizado levando em consideração apenas a janela principal do gráfico. Assim que alcançarmos nosso objetivo, poderemos começar a trabalhar com indicadores em subjanelas.

Os tópicos do artigo estão listados abaixo:

Motivos para usar a classe CCanvas

Alguém poderia perguntar por que usar CCanvas, já que existe o DRAW_FILLING para indicadores personalizados. Existem pelo menos duas razões:

  1. As cores do indicador se mesclam com as cores de outros indicadores, velas e objetos do gráfico.
  2. DRAW_FILLING não permite transparência.

Gráfico com dois indicadores e um objeto

Propriedades da janela do gráfico

Antes de criar um gráfico personalizado, é necessário pensar em suas propriedades. Você pode encontrar todas as propriedades na documentação. Para obter os valores das propriedades, é necessário usar as funções correspondentes ChartGetInteger e ChartGetDouble. Também há a função ChartGetString, mas não a utilizaremos aqui.

As propriedades que pretendemos usar são listadas abaixo, juntamente com uma breve descrição. Se precisarmos de algo mais, mencionarei separadamente.

  • CHART_WIDTH_IN_PIXELS — largura da janela do gráfico, sem a escala de preços
  • CHART_HEIGTH _IN_PIXELS — altura da subjanela, sem a escala de datas
  • CHART_PRICE_MIN — preço correspondente ao topo da subjanela
  • CHART_PRICE_MAX — preço correspondente à parte inferior da subjanela
  • CHART_SCALE — distância entre as barras. Após alguns testes, descobri que se trata de uma portência de dois pow(2,  CHART_SCALE)
  • CHART_FISRT_VISIBLE_BAR — primeira barra visível no gráfico, da esquerda para a direita
  • CHART_VISIBLE_BARS — número de barras visíveis no gráfico

Compreendendo as propriedades da janela do gráfico

Essas propriedades são claramente visíveis na imagem a seguir.

Propriedades do gráfico relacionadas às coordenadas

Usamos as propriedades CHART_WIDTH_IN_PIXELS e CHART_HEIGHT_IN_PIXELS para determinar o tamanho do canvas necessário. Ao alterar o tamanho da janela e as propriedades, é necessário ajustar o tamanho do canvas.

Para um melhor entendimento, criaremos um indicador simples que exibe as propriedades e como elas mudam com base na alteração do preço e na interação com o usuário. Também começaremos a usar o canvas para aprender o processo de desenho.


Indicador de visualização das propriedades do gráfico

Nesta etapa, supõe-se que você já saiba como criar um indicador personalizado. Se não souber, sugiro que primeiro leia os artigos "MQL5: Crie o seu próprio indicador" e "Explorando as possibilidades de criar gráficos de velas multicoloridas". Agora, vamos começar.

Criei meu indicador no caminho especificado abaixo. Por motivos organizacionais, sugiro que você faça o mesmo.

Quando a estrutura do indicador estiver pronta, precisamos adicionar a biblioteca CCanvas ao arquivo. Podemos fazer isso usando a diretiva #include.

Em seguida, criamos uma instância da classe CCanvas. Tudo isso deve ser colocado após as diretivas #property do indicador.

#property copyright "Copyright 2023, Samuel Manoel De Souza"
#property link      "https://www.mql5.com/en/users/samuelmnl"
#property version   "1.00"
#property indicator_chart_window

#include <Canvas/Canvas.mqh>
CCanvas Canvas;

A primeira coisa que precisamos fazer ao trabalhar com CCanvas é criar um OBJ_BITMAP_LABEL e adicionar um recurso a ele. Isso deve ser feito se você quiser adicioná-lo ao gráfico, geralmente no bloco de inicialização do indicador, usando o método CreateBitampLabel(...). Por fim, precisamos remover o OBJ_BITMAP_LABEL e o recurso anexado a ele. Isso deve ser feito se você quiser removê-lo do gráfico, geralmente no bloco de desinicialização, usando o método Destroy(void). Enquanto isso, realizamos o desenho principal, que consiste em apagar desenhos (limpar ou definir os valores padrão dos pixels do recurso), criar desenhos e atualizar o recurso. O ciclo de vida completo do canvas é mostrado no diagrama abaixo.

canvas_process

Para simplificar, armazenaremos Erase, Draw e Update em uma única função chamada Redraw. Ao colocar tudo isso no código, obtemos a seguinte estrutura.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   Canvas.CreateBitmapLabel(0, 0, "Canvas", 0, 0, 200, 150, COLOR_FORMAT_ARGB_NORMALIZE);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Canvas.Destroy();
  }
//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = ColorToARGB(clrBlack);
   uint text_color = ColorToARGB(clrWhite);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw

//--- add second draw

//--- add ... draw

//--- add last draw

//--- canvas update
   Canvas.Update();
  }

Para exibir as propriedades, escrevemos seus valores usando o método TextOut. Os valores dessas propriedades serão armazenados como uma string na variável struct

struct StrProperty
  {
   string name;
   string value;
  };
A estrutura pode ser a seguinte. Em seguida, podemos exibi-los brevemente em um laço. Como ainda não temos um array, passaremos um array como parâmetro para a função Redraw. A função Redraw fica assim:
void Redraw(StrProperty &array[])
  {
   uint default_color = ColorToARGB(clrBlack);
   uint text_color = ColorToARGB(clrWhite);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   int total = ArraySize(array);
   for(int i=0;i<total;i++)
     {
      int padding = 2;
      int left = padding, right = Canvas.Width() - padding, y = i * 20 + padding;
      Canvas.TextOut(left, y, array[i].name, text_color, TA_LEFT);
      Canvas.TextOut(right, y, array[i].value, text_color, TA_RIGHT);
     }
//--- canvas update
   Canvas.Update();
  }
Por fim, podemos obter os valores das propriedades e exibi-los. Se o seu código não tiver um manipulador de função OnChartEvent, você precisará adicioná-lo. Lá, verificaremos o identificador do evento CHARTEVENT_CHART_CHANGE. Quando o evento ocorrer, declararemos algumas variáveis que receberão os valores das propriedades e as passaremos para o array de estruturas, e então chamaremos a função Redraw. Podemos compilar o indicador, adicioná-lo ao gráfico e manipular o gráfico para ver as atualizações no canvas.
//+------------------------------------------------------------------+
//| Custom indicator chart event handler function                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
  {
   if(id != CHARTEVENT_CHART_CHANGE)
      return;
   int chart_width         = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   int chart_height        = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
   int chart_scale         = (int)ChartGetInteger(0, CHART_SCALE);
   int chart_first_vis_bar = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
   int chart_vis_bars      = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
   double chart_prcmin     = ChartGetDouble(0, CHART_PRICE_MIN);
   double chart_prcmax     = ChartGetDouble(0, CHART_PRICE_MAX);
//---
   StrProperty array[]
     {
        {"Width", (string)chart_width},
        {"Height", (string)chart_height},
        {"Scale", (string)chart_scale},
        {"First Vis. Bar", (string)chart_first_vis_bar},
        {"Visible Bars", (string)chart_vis_bars},
        {"Price Min", (string)chart_prcmin},
        {"Price Max", (string)chart_prcmax},
     };
   Redraw(array);
  }

Transformação de coordenadas

Nesta etapa, precisamos de algumas funções básicas para transformar data e hora ou índice de barra em coordenadas x em pixels, preço em coordenadas y em pixels, coordenadas x em índice de barra e coordenadas y em preço (algumas delas não serão usadas agora, mas podemos incluí-las todas de uma vez). Por isso, moveremos as variáveis de propriedades do gráfico para a área global, enquanto na função OnChartEvent apenas atualizaremos os valores e chamaremos a função Redraw quando necessário. A solução ideal seria encapsular as variáveis e funções de transformação em uma classe ou estrutura, mas não vamos complicar por enquanto. Sugiro que você comece a aprender OOP lendo o artigo "Fundamentos da programação orientada a objetos" e o tópico correspondente na documentação (Programação Orientada a Objetos). Vamos usar isso na próxima oportunidade.

As funções estão principalmente relacionadas a proporções.

//+------------------------------------------------------------------+
//| Converts the chart scale property to bar width/spacing           |
//+------------------------------------------------------------------+
int BarWidth(int scale) {return (int)pow(2, scale);}
//+------------------------------------------------------------------+
//| Converts the bar index(as series) to x in pixels                 |
//+------------------------------------------------------------------+
int ShiftToX(int shift) {return (chart_first_vis_bar - shift) * BarWidth(chart_scale) - 1;}
//+------------------------------------------------------------------+
//| Converts the price to y in pixels                                |
//+------------------------------------------------------------------+
int PriceToY(double price)
  {
// avoid zero divider
   if(chart_prcmax - chart_prcmin == 0.)
      return 0.;
   return (int)round(chart_height * (chart_prcmax - price) / (chart_prcmax - chart_prcmin) - 1);
  }
//+------------------------------------------------------------------+
//| Converts x in pixels to bar index(as series)                     |
//+------------------------------------------------------------------+
int XToShift(int x)
  {
// avoid zero divider
   if(BarWidth(chart_scale) == 0)
      return 0;
   return chart_first_vis_bar - (x + BarWidth(chart_scale) / 2) / BarWidth(chart_scale);
  }
//+------------------------------------------------------------------+
//| Converts y in pixels to price                                    |
//+------------------------------------------------------------------+
double YToPrice(int y)
  {
// avoid zero divider
   if(chart_height == 0)
      return 0;
   return chart_prcmax - y * (chart_prcmax - chart_prcmin) / chart_height;
  }

DRAW_FILLING com transparência

Agora temos tudo o que precisamos para implementar nosso DRAW_FILLING usando CCanvas.

Não vamos perder tempo criando um novo indicador. Em vez disso, vamos pegar um exemplo usado na plataforma MetaTrader 5 e adicionar o preenchimento entre duas linhas. Vou usar o indicador Envelopes em \\MQL5\\Indicators\\Examples\\, localizado na pasta de dados do terminal. Vou copiar o Envelopes.mq5 para o mesmo diretório onde criei o indicador ChartPropertiesViewer. Você pode escolher qualquer indicador, mas sugiro que use o mesmo indicador seguindo as etapas descritas neste artigo.

A primeira coisa que precisamos fazer é copiar tudo o que fizemos no indicador ChartPropertiesViewer para o Envelopes.

Conforme mencionado anteriormente, vamos preencher o canal entre duas linhas. Para isso, criaremos uma função que receberá arrays correspondentes aos valores das linhas. No indicador Envelopes, os arrays são definidos pelas variáveis ExtUpBuffer e ExtMABuffer.

double                   ExtUpBuffer[];
double                   ExtDownBuffer[];

Junto com os arrays, passaremos algumas variáveis adicionais que nos permitirão usar duas cores, definir um nível de transparência e deslocar o indicador para a esquerda ou direita do gráfico.

Parâmetros Descrição da variável
 serie1  Array de valores correspondentes à primeira linha
 serie2  Array de valores correspondentes à segunda linha
 clr1  Cor quando serie1 >= serie2
 clr2  Cor quando serie1 < serie2
 alpha  Valor de transparência do canal
 plot_shift  Deslocar o indicador para a direita ou para a esquerda do gráfico

A função, usando as variáveis existentes e os parâmetros mencionados, será assim:

//+------------------------------------------------------------------+
//| Fill the area between two lines                                  |
//+------------------------------------------------------------------+
void DrawFilling(double &serie1[], double &serie2[], color clr1, color clr2, uchar alpha = 255, int plot_shift = 0)
  {
   int start  = chart_first_vis_bar;
   int total  = chart_vis_bars + plot_shift;
   uint argb1 = ColorToARGB(clr1, alpha);
   uint argb2 = ColorToARGB(clr2, alpha);
   int limit  = fmin(ArraySize(serie1), ArraySize(serie2));
   int px, py1, py2;
   for(int i = 0; i < total; i++)
     {
      int bar_position = start - i;
      int bar_shift = start - i + plot_shift;
      int bar_index = limit - 1 - bar_shift;
      if(serie1[bar_index] == EMPTY_VALUE || serie1[bar_index] == EMPTY_VALUE || bar_shift >= limit)
         continue;
      int x  = ShiftToX(bar_position);
      int y1 = PriceToY(serie1[bar_index]);
      int y2 = PriceToY(serie2[bar_index]);
      uint argb = serie1[bar_index] < serie2[bar_index] ? argb2 : argb1;
      if(i > 0 && serie1[bar_index - 1] != EMPTY_VALUE && serie2[bar_index - 1] != EMPTY_VALUE)
        {
         if(py1 != py2)
            Canvas.FillTriangle(px, py1, px, py2, x, y1, argb);
         if(y1 != y2)
            Canvas.FillTriangle(px, py2, x, y1, x, y2, argb);
        }
      px  = x;
      py1 = y1;
      py2 = y2;
     }
  }

Até este ponto, usamos um canvas de tamanho fixo. No entanto, os indicadores exigem um canvas que preencha toda a área do gráfico. Além disso, sempre que o tamanho da janela do gráfico é alterado, seja por aumento, redução, alongamento em qualquer direção ou adição de indicadores em subjanelas, precisamos garantir que o canvas continue preenchendo toda a área do gráfico. Para isso, vamos alterar o tamanho do canvas, fazendo uma pequena modificação em nossa função OnChartEvent.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
  {
   if(id != CHARTEVENT_CHART_CHANGE)
      return;
   chart_width          = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   chart_height         = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
   chart_scale          = (int)ChartGetInteger(0, CHART_SCALE);
   chart_first_vis_bar  = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
   chart_vis_bars       = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
   chart_prcmin         = ChartGetDouble(0, CHART_PRICE_MIN, 0);
   chart_prcmax         = ChartGetDouble(0, CHART_PRICE_MAX, 0);
   if(chart_width != Canvas.Width() || chart_height != Canvas.Height())
      Canvas.Resize(chart_width, chart_height);

Agora faremos algumas pequenas atualizações para fazer a função funcionar.

  1. Vamos atualizar nossa função Redraw, removendo os parâmetros adicionados no indicador anterior e adicionando a função DrawFilling.
  2. Adicionaremos nossa função Redraw em OnCalculation para atualizar o desenho quando os valores do indicador forem alterados.
  3. Alteraremos o nome do objeto passado como parâmetro ao chamar CreateBitmapLabel.

//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = 0;
   color clrup = (color)PlotIndexGetInteger(0, PLOT_LINE_COLOR, 0);
   color clrdn = (color)PlotIndexGetInteger(1, PLOT_LINE_COLOR, 0);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   DrawFilling(ExtUpBuffer, ExtDownBuffer,clrup, clrdn, 128, InpMAShift);
//--- canvas update
   Canvas.Update();
  }
//--- the main loop of calculations
   for(int i=start; i<rates_total && !IsStopped(); i++)
     {
      ExtUpBuffer[i]=(1+InpDeviation/100.0)*ExtMABuffer[i];
      ExtDownBuffer[i]=(1-InpDeviation/100.0)*ExtMABuffer[i];
     }
   Redraw();
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
   Canvas.CreateBitmapLabel(0, 0, short_name, 0, 0, 200, 150, COLOR_FORMAT_ARGB_NORMALIZE);

Agora podemos ver como o gráfico fica com dois envelopes de diferentes períodos e um objeto retangular.

Envelopes usando CCanvas com canal alfa = 128

Envelopes com CCanvas e canal alfa = 255

Como você pode ver, o problema com os indicadores foi resolvido, mas o problema com os objetos do gráfico ainda permanece, mas isso é assunto para outro capítulo.


Expansão do método para trabalhar em indicadores de subjanela

Vejamos a figura abaixo. Aqui vemos um indicador de subjanela usando a função DRAW_FILLING. Esta imagem foi retirada da documentação do MQL. Faremos a mesma coisa, mas com transparência usando CCanvas e, o mais importante, evitaremos problemas com áreas de sobreposição.

Código de subjanela usando DRAW_FILLING

As alterações necessárias são as seguintes:

  • Criar uma etiqueta bitmap na mesma subjanela em que o indicador está localizado.
  • Alterar o tamanho do canvas com base no tamanho da subjanela em vez da janela principal do gráfico.

Para criar uma etiqueta bitmap na mesma subjanela e obter o tamanho da subjanela, precisamos descobrir em qual subjanela o indicador está localizado. Pode-se pensar que é apenas a última subjanela do gráfico, mas a plataforma permite que você coloque dois ou mais indicadores na mesma subjanela, e não necessariamente na última. Também precisamos de uma função que retorne o número da subjanela em que o indicador está localizado. Vamos dar uma olhada na seguinte função:

//+------------------------------------------------------------------+
//| return the number of the subwindow where the indicator is located|
//+------------------------------------------------------------------+
int ChartIndicatorFind(string shortname)
  {
   int subwin = ChartGetInteger(0, CHART_WINDOWS_TOTAL);
   while(subwin > 0)
     {
      subwin--;
      int total = ChartIndicatorsTotal(0, subwin);
      for(int i = 0; i < total; i++)
        {
         string name = ChartIndicatorName(0, subwin, i);
         if(name == shortname)
            return subwin;
        }
     }
   return -1;
  }

No último indicador, usamos o exemplo do indicador Envelopes. Agora, usaremos o código da documentação (DRAW_FILLING) como fonte para o nosso exemplo. Podemos criar um novo indicador no mesmo diretório onde criamos os dois indicadores anteriormente. Vamos chamá-lo de SubwindowIndicator. Em seguida, copiamos o código da documentação.

O indicador é construído usando a função DRAW_FILLING. Como usaremos CCanvas para preencher o canal, podemos substituir o tipo de desenho por linhas. Abaixo estão as alterações nas propriedades do indicador.

#property indicator_plots   2
//--- plot Intersection
#property indicator_label1  "Fast"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_width1  1
#property indicator_label2  "Slow"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrBlue
#property indicator_width2  1

Alterações na função OnInit.

//--- indicator buffers mapping
   SetIndexBuffer(0,IntersectionBuffer1,INDICATOR_DATA);
   SetIndexBuffer(1,IntersectionBuffer2,INDICATOR_DATA);
//---
   PlotIndexSetInteger(0,PLOT_SHIFT,InpMAShift);
   PlotIndexSetInteger(1,PLOT_SHIFT,InpMAShift);

Também não precisamos do indicador para alterar a aparência da linha. Podemos comentar essa linha na função OnCalculate.

//--- If a sufficient number of ticks has been accumulated
   if(ticks>=N)
     {
      //--- Change the line properties
      //ChangeLineAppearance();
      //--- Reset the counter of ticks to zero
      ticks=0;
     }

Agora podemos adicionar as variáveis de propriedades do gráfico e as funções criadas neste artigo. Neste indicador, os arrays que precisamos passar como parâmetros para a função DrawFilling têm nomes diferentes. Precisamos ajustá-los na função Redraw.

double         IntersectionBuffer1[];
double         IntersectionBuffer2[];

A função Redraw será assim:

//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = 0;
   color clrup = (color)PlotIndexGetInteger(0, PLOT_LINE_COLOR, 0);
   color clrdn = (color)PlotIndexGetInteger(1, PLOT_LINE_COLOR, 0);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   DrawFilling(IntersectionBuffer1, IntersectionBuffer2, clrup, clrdn, 128, InpMAShift);
//--- canvas update
   Canvas.Update();
  }

Após compilar o código, obtemos o resultado esperado.

Indicador de subjanela com preenchimento transparente de canal


Considerações finais

Neste artigo, exploramos o uso do CCanvas, algumas propriedades do gráfico e como obter seus valores, além de como usá-los para realizar algumas transformações básicas de coordenadas que são úteis e aplicáveis para diversos propósitos. Em seguida, desenvolvemos um indicador com transparência e expandimos o método para trabalhar com indicadores de subjanela.

Os arquivos dos indicadores desenvolvidos no artigo estão anexados abaixo.


Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/12357

Arquivos anexados |
Envelopes.mq5 (10.43 KB)
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 18):  Tiquete e mais tiquetes  (II) Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 18): Tiquete e mais tiquetes (II)
Neste, fica extremamente claro, que as métricas, estão muito longe, do tempo ideal de confecção das barras de 1 minuto. Assim então, a primeira coisa que de fato iremos corrigir, será justamente isto. Corrigir a questão da temporização, não é algo complicado. Por mais incrível que possa parecer, é na verdade até bem simples de ser feito. Porém não fiz a correção no artigo anterior, por que lá o desejo era explicar, como fazer para jogar os dados de tickets, que estavam sendo usados para gerar as barras de 1 minuto no gráfico, para dentro da janela de observação de mercado.
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 17): Tiquete e mais tiquetes (I) Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 17): Tiquete e mais tiquetes (I)
Aqui vamos começar a ver como implementar algo realmente bem interessante e curioso. Mas ao mesmo tempo extremamente complicado por conta de algumas questões que muitos confundem. Mas pior do que as confundir, é o fato de que alguns operadores que se dizem profissionais, não fazem ideia a importância de tais conceitos no mercado de capital. Sim, apesar do foco aqui ser programação, entender algumas questões que envolvem operações em mercados, é de extrema valia para o que iremos começar a implementar aqui.
Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 19): Ajustes necessários Desenvolvendo um sistema de Replay - Simulação de mercado (Parte 19): Ajustes necessários
O que de fato vamos fazer aqui, é preparar o terreno, de forma que quando for preciso adicionar algumas novas coisas ao código, isto aconteça de forma suave e tranquila. O código atual ainda não consegue cobrir ou dar cabo de algumas coisas, que serão necessárias para um avanço significativo. Precisamos que tudo seja construído de maneira que o esforço de implementação de algumas coisas seja o menor possível. Se isto for feito adequadamente teremos a possibilidade de ter um sistema realmente bastante versátil. Sendo capaz de se adaptar muito facilmente a qualquer situação que for preciso ser coberta.
Desenvolvimento de um sistema de negociação baseado no indicador Fibonacci Desenvolvimento de um sistema de negociação baseado no indicador Fibonacci
Esta é a continuação de uma série de artigos nos quais aprendemos como construir sistemas de negociação com base nos indicadores mais populares. Desta vez, cobriremos o indicador Fibonacci. Veremos como escrever um programa baseado nos sinais deste indicador.