Lendo qualquer indicador a partir de um Expert Advisor: setas, linhas e índices de cor no CopyBuffer
Introdução
Mais cedo ou mais tarde, todo trader que usa um indicador pede "a mesma coisa, mas operando sozinho". A resposta do programador parece simples: criar o indicador com iCustom() ou IndicatorCreate(), ler os valores dele com CopyBuffer() e abrir uma posição quando o sinal aparecer. Na prática, a primeira pergunta nem é sobre negociação. É esta: qual buffer guarda o sinal?
Um indicador de setas pode ter um buffer para as setas de compra e outro para as de venda, ou um único buffer para as duas com um índice de cor ao lado. Um indicador de "tendência" pode desenhar uma única linha que muda de cor, e a cor fica guardada em um buffer separado que nunca é desenhado. Um indicador compilado comprado no Market vem sem código-fonte, então não há como ler as chamadas SetIndexBuffer() dele. A Data Window (Janela de Dados) ajuda um pouco, mas mostra apenas os valores desenhados, de uma barra por vez, e esconde por completo os buffers de cor e de cálculo.
Este post mostra como responder a essa pergunta de fora, para qualquer indicador: como os buffers são armazenados, como ler todos eles a partir de outro programa, como distinguir um buffer de setas de uma linha e de um índice de cor apenas pelos valores, como verificar se os sinais repintam e como devolver a resposta ao gráfico, para que o usuário veja qual número corresponde a qual seta. O resultado são três pequenos programas com código-fonte: um script que inspeciona todos os indicadores de um gráfico (gratuito no Code Base), um indicador de demonstração que tem todos os tipos de buffer e um Expert Advisor que opera qualquer indicador segundo quatro regras genéricas (estes dois publicados na íntegra no final do post).
1. O que é, de fato, um buffer de indicador
Um indicador guarda a sua saída em arrays vinculados com SetIndexBuffer(). Existem três tipos:
- INDICATOR_DATA: valores que são desenhados (linhas, histogramas, setas) e exibidos na Data Window;
- INDICATOR_COLOR_INDEX: nos estilos DRAW_COLOR_*, o número da cor usada em cada barra;
- INDICATOR_CALCULATIONS: arrays intermediários que não são desenhados nem exibidos.
Os três tipos são numerados em uma única sequência, na ordem em que o indicador os declarou, e todos podem ser lidos com CopyBuffer(). Esta é a primeira surpresa para muitos programadores: um buffer que você nunca vê no gráfico continua disponível, e nos indicadores de cor muitas vezes é o mais útil.
Plotagens e buffers não são a mesma coisa. Uma plotagem DRAW_COLOR_LINE usa dois buffers (valor e índice de cor), DRAW_COLOR_CANDLES usa cinco (quatro preços e o índice de cor), DRAW_COLOR_ARROW usa dois (o preço da seta e a cor dela). O indicador Heiken Ashi que vem com o terminal é um bom exemplo: os buffers 0 a 3 são os preços do candle e o buffer 4 é o índice de cor, 0 para um candle de alta e 1 para um de baixa.
O segundo ponto é como o "nada" é armazenado. Um buffer de linha normalmente tem um valor em cada barra. Um buffer de setas tem um preço apenas nas barras com seta e um valor "vazio" nas demais. Qual valor significa vazio fica a critério do autor: EMPTY_VALUE (DBL_MAX) é a escolha habitual, definida com PlotIndexSetDouble(PLOT_EMPTY_VALUE), mas muitos indicadores usam 0.0. Um código de leitura que trate 0.0 como preço de seta válido verá uma seta em todas as barras.
Para ter algo concreto para observar, o indicador DemoBuffers.mq5 (código-fonte no final) declara todos os tipos de buffer: uma média móvel simples como linha, setas onde o fechamento cruza a média para cima, uma média exponencial desenhada como linha colorida e a inclinação dessa média como buffer de cálculo.
SetIndexBuffer(0,ExtSma,INDICATOR_DATA); // line SetIndexBuffer(1,ExtCross,INDICATOR_DATA); // arrows, EMPTY_VALUE where there is none SetIndexBuffer(2,ExtEma,INDICATOR_DATA); // colour line: the value SetIndexBuffer(3,ExtEmaColor,INDICATOR_COLOR_INDEX); // colour line: 0 rising, 1 falling SetIndexBuffer(4,ExtSlope,INDICATOR_CALCULATIONS); // not drawn, not in the Data Window PlotIndexSetInteger(1,PLOT_ARROW,233); PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,EMPTY_VALUE);
A Data Window mostra três valores dele: a média, a seta e a média exponencial. Os buffers 3 e 4 não aparecem ali, e mesmo assim a cor da linha - o sinal de "tendência" mais natural deste indicador - está no buffer 3. A Figura 1 mostra o que um programa vê quando lê os cinco.

Figura 1. DemoBuffers no EURUSD H1: duas linhas, um buffer de setas, um índice de cor e um buffer de cálculo, conforme lidos pelo script Buffer Inspector Kb.
2. Lendo todos os buffers de um indicador
Para um indicador já anexado a um gráfico, o handle está disponível sem que se conheçam os parâmetros de entrada dele:
for(int w=0; w<(int)ChartGetInteger(0,CHART_WINDOWS_TOTAL); w++)
for(int i=0; i<ChartIndicatorsTotal(0,w); i++)
{
string name=ChartIndicatorName(0,w,i);
int h=ChartIndicatorGet(0,w,name); // the same instance the user sees
...
IndicatorRelease(h); // releases our handle, not the indicator
} Isso importa mais do que parece: o usuário já escolheu os parâmetros de entrada do indicador no gráfico, e o programa lê exatamente essa instância. Para um indicador que não está no gráfico, IndicatorCreate() com IND_CUSTOM recebe o nome relativo a MQL5\Indicators e um array de MqlParam. Quando os parâmetros vêm de um campo de texto, por exemplo "14,0,1.5,true", cada item precisa receber o tipo certo:
int CreateByName(const string name,const string params)
{
MqlParam p[];
ArrayResize(p,1);
p[0].type=TYPE_STRING; // the first element is always the name
p[0].string_value=name;
string parts[];
const int n=(StringLen(params)>0) ? StringSplit(params,',',parts) : 0;
ArrayResize(p,n+1);
for(int i=0; i<n; i++)
{
string s=parts[i];
StringTrimLeft(s);
StringTrimRight(s);
if(s=="true" || s=="false")
{ p[i+1].type=TYPE_BOOL; p[i+1].integer_value=(s=="true"); }
else if(StringFind(s,".")>=0 && MathIsValidNumber(StringToDouble(s)))
{ p[i+1].type=TYPE_DOUBLE; p[i+1].double_value=StringToDouble(s); }
else if(s=="0" || StringToInteger(s)!=0)
{ p[i+1].type=TYPE_INT; p[i+1].integer_value=StringToInteger(s); }
else
{ p[i+1].type=TYPE_STRING; p[i+1].string_value=s; }
}
return(IndicatorCreate(_Symbol,_Period,IND_CUSTOM,ArraySize(p),p));
} Três regras poupam muito tempo com esta função. Os valores precisam seguir a ordem da janela de parâmetros de entrada do indicador, e não é possível pular um parâmetro: para definir o terceiro, é preciso informar também os dois primeiros. Uma enumeração é passada pelo seu número, então MODE_EMA é 1 e PRICE_CLOSE é 1. E os parâmetros não informados mantêm os valores padrão, então uma string vazia cria o indicador exatamente como ele vem.
Quantos buffers existem? O handle não diz, mas CopyBuffer() diz: retorna -1 para um número de buffer que não existe. Ler os buffers 0, 1, 2 e assim por diante até a primeira falha dá a contagem, com os buffers de dados, de cor e de cálculo juntos.
Dois detalhes práticos:
- depois que o handle é criado, o indicador precisa de tempo para calcular; BarsCalculated(handle) retorna -1 até que isso aconteça, então um script espera por ele em um laço curto com Sleep(), e um Expert Advisor simplesmente tenta de novo no próximo tick;
- comece a copiar a partir da barra 1, a última barra fechada. Em muitos indicadores de setas, a barra em formação muda até fechar, e uma análise baseada nela mostra setas que depois desaparecem.
3. Distinguindo setas, linhas e índices de cor
Com N barras fechadas de um buffer em um array, três estatísticas simples bastam:
- válidos: valores que não são EMPTY_VALUE nem NaN;
- setas: valores válidos e diferentes de 0.0;
- inteiros pequenos: valores que são números inteiros com valor absoluto pequeno.
e a classificação decorre delas:
| O que o buffer contém | Como ele aparece em 500 barras |
|---|---|
| nada | nenhum valor válido, ou apenas zeros |
| setas | alguns valores, mas em 30% das barras ou menos |
| índice de cor | apenas números inteiros pequenos em uma faixa estreita (por exemplo, 0 e 1) |
| linha | todo o resto: um valor em (quase) todas as barras |
int valid=0, arrows=0, ints=0;
double lo=DBL_MAX, hi=-DBL_MAX;
for(int i=0; i<n; i++)
{
if(v[i]==EMPTY_VALUE || !MathIsValidNumber(v[i])) continue;
valid++; lo=MathMin(lo,v[i]); hi=MathMax(hi,v[i]);
if(v[i]!=0.0) arrows++;
if(MathAbs(v[i]-MathRound(v[i]))<1e-9 && MathAbs(v[i])<64) ints++;
}
if(valid==0 || (arrows==0 && hi==0.0 && lo==0.0)) kind="empty";
else if(arrows>0 && arrows<=n*0.3) kind="arrows";
else if(ints==valid && hi-lo<=16) kind="colour index";
else kind="line"; Por que 30%? Indicadores de setas marcam pontos de virada, que por natureza são raros: o Fractals marca uma pequena porcentagem das barras, e os indicadores de "sinal" típicos, menos de 10%. Uma linha que fica vazia em parte das barras (uma média móvel no início do histórico) ainda tem valores na maioria delas. O limiar é deliberadamente folgado; os casos próximos dele são informados ao usuário, e não adivinhados em silêncio.
Por que "inteiros pequenos em uma faixa estreita" para as cores? Um índice de cor assume os valores 0, 1, 2 e assim por diante, um para cada cor da plotagem. Uma linha também pode ser inteira (um oscilador arredondado pelo autor, uma função em degraus), mas nesse caso ela percorre uma faixa muito mais ampla do que o punhado de cores que uma plotagem pode ter.
A Figura 2 mostra o resultado para o Heiken Ashi: quatro linhas de preço e, no buffer 4, o índice de cor com os valores 0 e 1 - exatamente o que um Expert Advisor precisa para reagir a uma mudança na cor do candle.

Figura 2. Heiken Ashi: quatro linhas de preço e o índice de cor no buffer 4, que a Data Window não mostra.
Alguns casos merecem um comentário, porque é neles que uma olhada rápida dá a resposta errada:
- Setas armazenadas com 0.0 em vez de EMPTY_VALUE. A classificação acima já trata 0.0 como "sem seta", mas um programa que compara apenas com EMPTY_VALUE vai disparar em todas as barras. Teste sempre os dois.
- Pontos em todas as barras, como no Parabolic SAR. No gráfico eles parecem setas, mas o buffer tem valor em todas as barras, então é uma linha, e o sinal é o momento em que os pontos saltam para o outro lado do preço, não a presença de um ponto.
- Setas coloridas (DRAW_COLOR_ARROW). O buffer de setas dá a barra e o preço, e o índice de cor ao lado diz se é uma seta de compra ou de venda. Os dois buffers são necessários para um único sinal.
- Buffers de cálculo. Em geral são classificados como linhas, muitas vezes com valores longe do preço. Vale a pena olhá-los: alguns indicadores guardam ali o sinal "bruto", antes da suavização ou da filtragem.
4. Mostrando ao usuário qual seta é qual
Uma tabela com "buffer 1: setas, 37, a última em 2026.10.07 08:00" está correta, mas o trader ainda precisa comparar as datas com o gráfico para saber se o buffer 1 é a seta para cima ou a seta para baixo. É muito mais rápido escrever o número do buffer ao lado das próprias setas:
for(int i=n-1, done=0; i>=0 && done<20; i--)
{
if(v[i]==EMPTY_VALUE || v[i]==0.0) continue;
int shift=n-i; // v[] runs from shift n (oldest) to shift 1
datetime t=iTime(_Symbol,_Period,shift);
string obj=StringFormat("BufInspector_%d_%d",buffer,shift);
ObjectCreate(0,obj,OBJ_TEXT,0,t,v[i]); // at the arrow's own price
ObjectSetString(0,obj,OBJPROP_TEXT,"B"+(string)buffer);
done++;
} O rótulo é colocado no valor armazenado no buffer, que nos indicadores de setas é o preço em que a seta é desenhada, então "B0" e "B1" aparecem bem em cima das setas. Uma cor por buffer facilita distinguir dois buffers de setas.

Figura 3. Examples\Fractals: B0 aparece acima dos fractais superiores, B1 abaixo dos inferiores.
5. O indicador repinta?
Um buffer que contém setas é só metade da resposta. A outra metade é saber se uma seta, depois de aparecer em uma barra fechada, permanece ali. Alguns indicadores movem ou apagam as suas setas quando chegam novas barras - o Fractals faz isso por projeto nas duas últimas barras, e os indicadores do tipo ZigZag por muito mais tempo. Um Expert Advisor que opera uma seta que depois desaparece está operando um sinal que, em retrospecto, nunca existiu, e os seus resultados no Testador de Estratégia não vão corresponder ao gráfico.
A verificação é simples de automatizar: memorizar os valores das últimas barras fechadas pelo horário de abertura, esperar algumas barras novas, ler as mesmas barras de novo e comparar.
datetime ExtTimes[]; // open times of the remembered bars
double ExtValues[]; // buffer values on those bars
//--- remember the last 100 closed bars of buffer b
int Remember(const int handle,const int b)
{
if(CopyTime(_Symbol,_Period,1,100,ExtTimes)!=100) return(0);
return(CopyBuffer(handle,b,1,100,ExtValues));
}
//--- later: how many of the remembered bars changed their value?
int Changed(const int handle,const int b)
{
int changed=0;
for(int i=0; i<ArraySize(ExtTimes); i++)
{
double v[];
if(CopyBuffer(handle,b,ExtTimes[i],1,v)==1 && v[0]!=ExtValues[i])
changed++;
}
return(changed);
} Ler pelo horário, e não pelo índice, é essencial aqui: depois que chegam novas barras, a mesma barra tem um índice maior. O resultado diz até quantas barras para trás o indicador ainda muda de ideia, e é esse número que define a barra de sinal a usar. No Fractals a resposta é sempre duas barras, e é por isso que um fractal é lido na barra 3, e não na barra 1. Um indicador que continua mudando as suas setas dez barras para trás não pode ser operado na barra em que a seta aparece pela primeira vez, por melhor que pareça a sua imagem no Testador de Estratégia.
6. Quando não há buffer para ler
Alguns indicadores desenham as suas setas com ObjectCreate() em vez de buffers. As setas estão no gráfico, a Data Window está vazia e CopyBuffer() retorna apenas linhas ou nada. Nenhum programa consegue ler esses sinais por meio do handle do indicador. Vale a pena dizer isso claramente ao usuário, porque senão a busca pelo "número de buffer certo" nunca termina. Um script pode detectar a situação de forma indireta: um indicador que não tem buffer de setas, embora o usuário veja setas, quase certamente está desenhando objetos. O Buffer Inspector Kb diz isso no seu relatório.
7. Dos buffers às regras de negociação
Uma vez conhecido o buffer, quatro regras cobrem quase todos os indicadores que um trader traz:
| Regra | Indicadores típicos | Condição na barra de sinal |
|---|---|---|
| surge uma seta | Fractals, indicadores de setas | o valor não é vazio nem 0 |
| um buffer cruza um nível | RSI 30/70, CCI +-100, osciladores em torno de 0 | anterior < nível <= atual |
| um buffer cruza outro | MACD principal/sinal, Stochastic %K/%D, linhas do Alligator | anterior a < b e atual a >= b |
| um índice de cor assume um valor | Heiken Ashi, médias coloridas, barras de "tendência" | anterior != k e atual == k |
O BufferSignalEA.mq5 (código-fonte no final) implementa exatamente essas regras, com o nome do indicador, os seus parâmetros de entrada, a regra e os números dos buffers como parâmetros de entrada, de modo que um único Expert Advisor pode ser apontado para qualquer indicador. O coração dele é uma função que lê dois valores, o da barra de sinal e o da barra anterior:
bool RuleFires(const ENUM_SIGNAL_RULE rule,const int buffer,const double level,const int buffer2)
{
if(rule==SIGNAL_OFF)
return(false);
double a[],b[];
//--- a[0] = the bar before the signal bar, a[1] = the signal bar
if(CopyBuffer(ExtHandle,buffer,InpSignalBar,2,a)!=2)
return(false);
switch(rule)
{
case SIGNAL_ARROW:
return(!IsEmpty(a[1]) && a[1]!=0.0);
case SIGNAL_CROSS_UP:
return(!IsEmpty(a[0]) && !IsEmpty(a[1]) && a[0]<level && a[1]>=level);
case SIGNAL_CROSS_DOWN:
return(!IsEmpty(a[0]) && !IsEmpty(a[1]) && a[0]>level && a[1]<=level);
case SIGNAL_BECOMES:
return(!IsEmpty(a[1]) && MathAbs(a[1]-level)<0.5 && (IsEmpty(a[0]) || MathAbs(a[0]-level)>=0.5));
case SIGNAL_LINE_UP:
case SIGNAL_LINE_DOWN:
{
if(CopyBuffer(ExtHandle,buffer2,InpSignalBar,2,b)!=2)
return(false);
if(IsEmpty(a[0]) || IsEmpty(a[1]) || IsEmpty(b[0]) || IsEmpty(b[1]))
return(false);
if(rule==SIGNAL_LINE_UP)
return(a[0]<b[0] && a[1]>=b[1]);
return(a[0]>b[0] && a[1]<=b[1]);
}
}
return(false);
} Um índice de cor é comparado com uma tolerância de 0,5 porque é armazenado como double. A decisão é tomada uma vez a cada nova barra, antes de qualquer outra coisa em OnTick():
const datetime bar=iTime(_Symbol,_Period,0); if(bar==ExtLastBar) return; if(BarsCalculated(ExtHandle)<InpSignalBar+2) return; // the indicator is not ready yet: try on the next tick ExtLastBar=bar; const bool buy =RuleFires(InpBuyRule, InpBuyBuffer, InpBuyLevel, InpBuyBuffer2); const bool sell=RuleFires(InpSellRule,InpSellBuffer,InpSellLevel,InpSellBuffer2); if(buy==sell || HasPosition()) return; // no signal, both at once, or already in the market
Uma regra avaliada a cada tick da barra em formação dispararia várias vezes e com sinais que depois desaparecem; uma decisão por barra, tomada sobre uma barra fechada, mantém o Testador de Estratégia e o gráfico ao vivo em concordância.
Para verificar que cada regra realmente dispara, o Expert Advisor foi executado no Testador de Estratégia com cinco indicadores, com a mesma saída simples para todos (0,1 lote, stop loss de 300 pontos, take profit de 600 pontos, uma posição por vez). Os números abaixo são um teste funcional das regras, não um resultado de negociação: um indicador operado sem nenhum filtro nem gestão de posição não é uma estratégia.
| Indicador e regra | Buffers | Operações | Lucrativas |
|---|---|---|---|
| Fractals, surge uma seta (barra de sinal 3) | compra 1, venda 0 | 184 | 53 |
| MACD, a linha principal cruza a de sinal | 0 e 1 | 160 | 46 |
| RSI(14), cruza 30 para cima / 70 para baixo | 0 | 113 | 36 |
| Heiken Ashi, a cor passa a 0 / 1 | 4 | 170 | 53 |
| DemoBuffers, seta para compra, cor 1 para venda | 1 e 3 | 152 | 39 |
EURUSD H1, 2025.10.01 - 2026.10.01, modelo "OHLC em 1 minuto". Os cinco testes rodam sem uma única solicitação com falha; as diferentes quantidades de operações vêm da frequência com que cada indicador dá sinal enquanto não há posição aberta.
8. Lendo um indicador em outro timeframe
A mesma leitura funciona para um indicador em um timeframe maior, e é assim que se constrói a maioria dos filtros ("comprar apenas quando o preço estiver acima da EMA 200 no H4"). O handle é criado com o timeframe, e a barra 1 desse handle é a última barra fechada desse timeframe:
int h4=iMA(_Symbol,PERIOD_H4,200,0,MODE_EMA,PRICE_CLOSE); ... double ma[]; if(CopyBuffer(h4,0,1,1,ma)==1 && iClose(_Symbol,PERIOD_H4,1)>ma[0]) allowBuy=true;
Duas observações. Primeiro, os dados de outro timeframe podem não estar prontos quando o programa inicia: BarsCalculated() retorna -1 e CopyBuffer() falha até que o terminal tenha montado o histórico, então a verificação precisa ser repetida, e não interpretada como "sem sinal". Segundo, nunca leia a barra 0 do timeframe maior em uma decisão tomada no menor. Em um gráfico H1, a barra H4 em formação muda a cada hora, então um filtro que a lê vai parecer perfeito no testador, que já sabe como a barra H4 termina, e vai se comportar de outra forma em um gráfico ao vivo.
9. Os programas
- Buffer Inspector Kb (script, MQL5\Scripts, gratuito no Code Base): coloque-o em um gráfico que já tenha o indicador. Ele inspeciona todos os indicadores em todas as janelas, imprime a tabela no diário Experts, mostra-a no gráfico com Comment() e rotula as últimas setas de cada buffer de setas. Com o nome de um indicador nos parâmetros de entrada, ele mesmo cria o indicador, o que é útil para indicadores compilados do Market. Executá-lo com "Only remove the labels" limpa o gráfico.
- DemoBuffers.mq5 (indicador, MQL5\Indicators): um único indicador com uma linha, setas, uma linha colorida com o seu índice de cor e um buffer de cálculo, para experimentos. Código-fonte abaixo.
- BufferSignalEA.mq5 (Expert Advisor, MQL5\Experts): opera qualquer indicador segundo as quatro regras acima; o indicador, os seus parâmetros de entrada, as regras e os números dos buffers são parâmetros de entrada. Código-fonte abaixo.
Conclusão
Os buffers de um indicador são a sua interface pública, mesmo quando o código-fonte não está disponível. Ler todos eles, classificá-los pelos valores, rotular as setas no gráfico e verificar até quantas barras para trás os valores mudam transforma "qual número de buffer devo usar?" de uma pergunta para o autor do indicador em uma verificação de dez segundos. A mesma classificação é a base de quatro regras de negociação genéricas - seta, nível, cruzamento de linhas e mudança de cor - que permitem a um único Expert Advisor trabalhar com indicadores que ele nunca viu, em barras fechadas, de modo que o que o Testador de Estratégia mostra é o que um gráfico ao vivo vai fazer.
Código-fonte
Salve cada listagem com o seu nome na pasta indicada (Arquivo → Abrir Pasta de Dados no terminal) e compile-a no MetaEditor com F7.
DemoBuffers.mq5 (MQL5\Indicators)
//+------------------------------------------------------------------+ //| DemoBuffers.mq5 | //| Bohdan Klok | //| https://www.mql5.com/en/users/kbposht | //+------------------------------------------------------------------+ #property copyright "Bohdan Klok" #property link "https://www.mql5.com/en/users/kbposht" #property version "1.00" #property description "Example: one indicator with every kind of buffer - a line, arrows," #property description "a colour line with its colour index, and a calculation buffer that is not drawn." #property indicator_chart_window #property indicator_buffers 5 #property indicator_plots 3 //--- plot 0: a plain line (buffer 0) #property indicator_label1 "SMA" #property indicator_type1 DRAW_LINE #property indicator_color1 clrSilver #property indicator_width1 1 //--- plot 1: arrows (buffer 1), drawn only where the close crosses the SMA upward #property indicator_label2 "Cross up" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrDodgerBlue #property indicator_width2 2 //--- plot 2: a colour line (buffer 2 = value, buffer 3 = colour index) #property indicator_label3 "Trend EMA" #property indicator_type3 DRAW_COLOR_LINE #property indicator_color3 clrSeaGreen,clrOrangeRed #property indicator_width3 2 //--- input parameters input int InpSmaPeriod=20; // SMA period input int InpEmaPeriod=50; // EMA period //--- indicator buffers double ExtSma[]; // 0: line double ExtCross[]; // 1: arrows, EMPTY_VALUE where there is none double ExtEma[]; // 2: colour line value double ExtEmaColor[]; // 3: colour index, 0 rising, 1 falling double ExtSlope[]; // 4: calculations, EMA change over one bar //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0,ExtSma,INDICATOR_DATA); SetIndexBuffer(1,ExtCross,INDICATOR_DATA); SetIndexBuffer(2,ExtEma,INDICATOR_DATA); SetIndexBuffer(3,ExtEmaColor,INDICATOR_COLOR_INDEX); SetIndexBuffer(4,ExtSlope,INDICATOR_CALCULATIONS); PlotIndexSetInteger(1,PLOT_ARROW,233); PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,EMPTY_VALUE); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpSmaPeriod); PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,InpEmaPeriod); IndicatorSetString(INDICATOR_SHORTNAME,StringFormat("DemoBuffers(%d,%d)",InpSmaPeriod,InpEmaPeriod)); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { if(rates_total<InpEmaPeriod+2) return(0); const double k=2.0/(InpEmaPeriod+1.0); int start=(prev_calculated>1) ? prev_calculated-1 : 1; if(prev_calculated==0) { ExtEma[0]=close[0]; ExtSlope[0]=0.0; ExtEmaColor[0]=0.0; } for(int i=start; i<rates_total; i++) { //--- SMA, empty until there are enough bars if(i>=InpSmaPeriod-1) { double sum=0.0; for(int j=0; j<InpSmaPeriod; j++) sum+=close[i-j]; ExtSma[i]=sum/InpSmaPeriod; } else ExtSma[i]=EMPTY_VALUE; //--- EMA, its slope and the colour of the slope ExtEma[i]=ExtEma[i-1]+k*(close[i]-ExtEma[i-1]); ExtSlope[i]=ExtEma[i]-ExtEma[i-1]; ExtEmaColor[i]=(ExtSlope[i]>=0.0) ? 0.0 : 1.0; //--- an arrow under the bar where the close crosses the SMA upward ExtCross[i]=EMPTY_VALUE; if(i>=InpSmaPeriod && ExtSma[i-1]!=EMPTY_VALUE && close[i-1]<ExtSma[i-1] && close[i]>=ExtSma[i]) ExtCross[i]=low[i]; } return(rates_total); } //+------------------------------------------------------------------+
BufferSignalEA.mq5 (MQL5\Experts)
//+------------------------------------------------------------------+ //| BufferSignalEA.mq5 | //| Bohdan Klok | //| https://www.mql5.com/en/users/kbposht | //+------------------------------------------------------------------+ #property copyright "Bohdan Klok" #property link "https://www.mql5.com/en/users/kbposht" #property version "1.00" #property description "Example: trades the signals of any indicator by four generic rules on its" #property description "buffers - an arrow appears, a level is crossed, one buffer crosses another, a colour" #property description "index becomes a value. Signals are read on the closed bar, once per bar." #include <Trade\Trade.mqh> //--- the four generic rules enum ENUM_SIGNAL_RULE { SIGNAL_OFF=0, // Disabled SIGNAL_ARROW=1, // Arrow appears in the buffer SIGNAL_CROSS_UP=2, // Buffer crosses the level upward SIGNAL_CROSS_DOWN=3, // Buffer crosses the level downward SIGNAL_LINE_UP=4, // Buffer crosses the second buffer upward SIGNAL_LINE_DOWN=5, // Buffer crosses the second buffer downward SIGNAL_BECOMES=6 // Buffer becomes equal to the level (colour index) }; //--- input parameters input string InpIndicator="Examples\\Fractals"; // Indicator name inside MQL5\Indicators input string InpParams=""; // Its inputs, comma separated (empty = defaults) input int InpSignalBar=3; // Signal bar (1 = last closed bar) input ENUM_SIGNAL_RULE InpBuyRule=SIGNAL_ARROW; // Buy rule input int InpBuyBuffer=1; // Buy: buffer input double InpBuyLevel=0.0; // Buy: level input int InpBuyBuffer2=0; // Buy: second buffer input ENUM_SIGNAL_RULE InpSellRule=SIGNAL_ARROW; // Sell rule input int InpSellBuffer=0; // Sell: buffer input double InpSellLevel=0.0; // Sell: level input int InpSellBuffer2=1; // Sell: second buffer input double InpLots=0.1; // Lot input int InpStopPoints=300; // Stop loss, points input int InpTakePoints=600; // Take profit, points input ulong InpMagic=20261010; // Magic number //--- global variables CTrade ExtTrade; int ExtHandle=INVALID_HANDLE; datetime ExtLastBar=0; //+------------------------------------------------------------------+ //| Indicator inputs from "14,0,1.5,true,text" | //+------------------------------------------------------------------+ int CreateByName(const string name,const string params) { MqlParam p[]; ArrayResize(p,1); p[0].type=TYPE_STRING; p[0].string_value=name; string parts[]; const int n=(StringLen(params)>0) ? StringSplit(params,',',parts) : 0; ArrayResize(p,n+1); for(int i=0; i<n; i++) { string s=parts[i]; StringTrimLeft(s); StringTrimRight(s); if(s=="true" || s=="false") { p[i+1].type=TYPE_BOOL; p[i+1].integer_value=(s=="true"); } else if(StringFind(s,".")>=0 && MathIsValidNumber(StringToDouble(s))) { p[i+1].type=TYPE_DOUBLE; p[i+1].double_value=StringToDouble(s); } else if(s=="0" || StringToInteger(s)!=0) { p[i+1].type=TYPE_INT; p[i+1].integer_value=StringToInteger(s); } else { p[i+1].type=TYPE_STRING; p[i+1].string_value=s; } } return(IndicatorCreate(_Symbol,_Period,IND_CUSTOM,ArraySize(p),p)); } //+------------------------------------------------------------------+ //| A value is empty if it is EMPTY_VALUE or not a number | //+------------------------------------------------------------------+ bool IsEmpty(const double v) { return(v==EMPTY_VALUE || !MathIsValidNumber(v)); } //+------------------------------------------------------------------+ //| Does the rule fire on the signal bar? | //+------------------------------------------------------------------+ bool RuleFires(const ENUM_SIGNAL_RULE rule,const int buffer,const double level,const int buffer2) { if(rule==SIGNAL_OFF) return(false); double a[],b[]; //--- two values: a[0] = the bar before the signal bar, a[1] = the signal bar if(CopyBuffer(ExtHandle,buffer,InpSignalBar,2,a)!=2) return(false); switch(rule) { case SIGNAL_ARROW: return(!IsEmpty(a[1]) && a[1]!=0.0); case SIGNAL_CROSS_UP: return(!IsEmpty(a[0]) && !IsEmpty(a[1]) && a[0]<level && a[1]>=level); case SIGNAL_CROSS_DOWN: return(!IsEmpty(a[0]) && !IsEmpty(a[1]) && a[0]>level && a[1]<=level); case SIGNAL_BECOMES: return(!IsEmpty(a[1]) && MathAbs(a[1]-level)<0.5 && (IsEmpty(a[0]) || MathAbs(a[0]-level)>=0.5)); case SIGNAL_LINE_UP: case SIGNAL_LINE_DOWN: { if(CopyBuffer(ExtHandle,buffer2,InpSignalBar,2,b)!=2) return(false); if(IsEmpty(a[0]) || IsEmpty(a[1]) || IsEmpty(b[0]) || IsEmpty(b[1])) return(false); if(rule==SIGNAL_LINE_UP) return(a[0]<b[0] && a[1]>=b[1]); return(a[0]>b[0] && a[1]<=b[1]); } } return(false); } //+------------------------------------------------------------------+ //| Is there an open position of this EA? | //+------------------------------------------------------------------+ bool HasPosition() { for(int i=PositionsTotal()-1; i>=0; i--) if(PositionGetSymbol(i)==_Symbol && PositionGetInteger(POSITION_MAGIC)==(long)InpMagic) return(true); return(false); } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { if(InpSignalBar<1) { Print("Signal bar must be 1 or more: the forming bar can still change"); return(INIT_PARAMETERS_INCORRECT); } ExtHandle=CreateByName(InpIndicator,InpParams); if(ExtHandle==INVALID_HANDLE) { PrintFormat("Cannot load %s (error %d)",InpIndicator,GetLastError()); return(INIT_FAILED); } ExtTrade.SetExpertMagicNumber(InpMagic); ExtLastBar=0; return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(ExtHandle!=INVALID_HANDLE) IndicatorRelease(ExtHandle); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- one decision per bar const datetime bar=iTime(_Symbol,_Period,0); if(bar==ExtLastBar) return; if(BarsCalculated(ExtHandle)<InpSignalBar+2) return; ExtLastBar=bar; const bool buy=RuleFires(InpBuyRule,InpBuyBuffer,InpBuyLevel,InpBuyBuffer2); const bool sell=RuleFires(InpSellRule,InpSellBuffer,InpSellLevel,InpSellBuffer2); if(buy==sell || HasPosition()) return; const double point=SymbolInfoDouble(_Symbol,SYMBOL_POINT); if(buy) { const double ask=SymbolInfoDouble(_Symbol,SYMBOL_ASK); ExtTrade.Buy(InpLots,_Symbol,ask,ask-InpStopPoints*point,ask+InpTakePoints*point); } else { const double bid=SymbolInfoDouble(_Symbol,SYMBOL_BID); ExtTrade.Sell(InpLots,_Symbol,bid,bid+InpStopPoints*point,bid-InpTakePoints*point); } } //+------------------------------------------------------------------+


