Kaique Mitsuo Silva Yamamoto
Mercado financeiroAutomação de EstratégiasMetaTrader 5 / MQL5

MQL5 — Análise Multi-Timeframe (MTF)

Como ler e combinar dados de múltiplos timeframes em MQL5: CopyRates, CopyBuffer com timeframe externo, sincronização de barras e padrões MTF em Expert Advisors.

MQL5 — Análise Multi-Timeframe (MTF)

A análise multi-timeframe é um dos princípios mais poderosos do trading profissional: usar o timeframe maior para direção e o menor para timing de entrada. Em MQL5, isso é direto — todas as funções de cópia de dados aceitam um parâmetro de timeframe independente do gráfico atual.

Fundamentos: MQL5 — Fundamentos


Lendo OHLC de qualquer Timeframe

MqlRates ratesH4[];
ArraySetAsSeries(ratesH4, true);

// Copia 100 barras do H4, independente do gráfico atual
int copied = CopyRates(_Symbol, PERIOD_H4, 0, 100, ratesH4);

if(copied <= 0)
{
    Print("Erro ao copiar H4: ", GetLastError());
    return;
}

double h4Close_0 = ratesH4[0].close;  // barra atual H4 (em formação)
double h4Close_1 = ratesH4[1].close;  // última barra H4 fechada
double h4High_1  = ratesH4[1].high;
double h4Low_1   = ratesH4[1].low;

CopyBuffer de indicador em outro timeframe

// Criar handle de MA no H4
int maH4Handle = iMA(_Symbol, PERIOD_H4, 200, 0, MODE_SMA, PRICE_CLOSE);

double maH4[];
ArraySetAsSeries(maH4, true);
CopyBuffer(maH4Handle, 0, 0, 3, maH4);

double maH4_value = maH4[1];  // valor da MA200 H4 na última barra fechada

Padrão: Direção no H4, Entrada no M15

Estrutura clássica de EA MTF:

#include <Trade/Trade.mqh>

input int    MA_H4_Period  = 200;
input int    MA_M15_Period = 20;
input double RiskPercent   = 1.0;

int g_maH4Handle;
int g_maM15Handle;
int g_atrHandle;
CTrade trade;

int OnInit()
{
    g_maH4Handle  = iMA(_Symbol, PERIOD_H4,  MA_H4_Period,  0, MODE_SMA, PRICE_CLOSE);
    g_maM15Handle = iMA(_Symbol, PERIOD_M15, MA_M15_Period, 0, MODE_EMA, PRICE_CLOSE);
    g_atrHandle   = iATR(_Symbol, PERIOD_M15, 14);

    trade.SetExpertMagicNumber(20250101);
    return(INIT_SUCCEEDED);
}

// Retorna +1 (bullish), -1 (bearish), 0 (neutro)
int GetH4Bias()
{
    double maH4[], closeH4[];
    ArraySetAsSeries(maH4,   true);
    ArraySetAsSeries(closeH4, true);

    CopyBuffer(g_maH4Handle, 0, 0, 3, maH4);

    MqlRates ratesH4[];
    ArraySetAsSeries(ratesH4, true);
    CopyRates(_Symbol, PERIOD_H4, 0, 3, ratesH4);

    if(ratesH4[1].close > maH4[1]) return  1;
    if(ratesH4[1].close < maH4[1]) return -1;
    return 0;
}

// Verifica sinal no M15 (cruzamento de EMA)
bool IsM15BuySignal()
{
    double maM15[];
    ArraySetAsSeries(maM15, true);
    CopyBuffer(g_maM15Handle, 0, 0, 3, maM15);

    MqlRates rates[];
    ArraySetAsSeries(rates, true);
    CopyRates(_Symbol, PERIOD_M15, 0, 3, rates);

    return (rates[1].close > maM15[1] && rates[2].close <= maM15[2]);
}

bool IsM15SellSignal()
{
    double maM15[];
    ArraySetAsSeries(maM15, true);
    CopyBuffer(g_maM15Handle, 0, 0, 3, maM15);

    MqlRates rates[];
    ArraySetAsSeries(rates, true);
    CopyRates(_Symbol, PERIOD_M15, 0, 3, rates);

    return (rates[1].close < maM15[1] && rates[2].close >= maM15[2]);
}

void OnTick()
{
    // Executar apenas na abertura de nova barra M15
    static datetime lastBar = 0;
    datetime currentBar = iTime(_Symbol, PERIOD_M15, 0);
    if(currentBar == lastBar) return;
    lastBar = currentBar;

    if(PositionsTotal() > 0) return;

    int bias = GetH4Bias();

    double atr[];
    ArraySetAsSeries(atr, true);
    CopyBuffer(g_atrHandle, 0, 0, 3, atr);
    double stopDist = atr[1] * 1.5;

    if(bias == 1 && IsM15BuySignal())
    {
        double ask  = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double sl   = ask - stopDist;
        double tp   = ask + stopDist * 2.0;
        double lots = CalculateLotByRisk(RiskPercent, stopDist / _Point);
        trade.Buy(lots, _Symbol, ask, sl, tp, "MTF Buy");
    }
    else if(bias == -1 && IsM15SellSignal())
    {
        double bid  = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double sl   = bid + stopDist;
        double tp   = bid - stopDist * 2.0;
        double lots = CalculateLotByRisk(RiskPercent, stopDist / _Point);
        trade.Sell(lots, _Symbol, bid, sl, tp, "MTF Sell");
    }
}

Sincronização de Barras — Executar Apenas em Barra Nova

Um erro crítico: executar lógica em cada tick em vez de em cada nova barra. Em M15, isso pode significar centenas de avaliações desnecessárias por barra.

// Forma correta: comparar tempo da barra atual
bool IsNewBar(ENUM_TIMEFRAMES tf = PERIOD_CURRENT)
{
    static datetime lastTime = 0;
    datetime currentTime = iTime(_Symbol, tf, 0);

    if(currentTime != lastTime)
    {
        lastTime = currentTime;
        return true;
    }
    return false;
}

// Uso no OnTick
void OnTick()
{
    if(!IsNewBar(PERIOD_M15)) return;  // só processa na abertura de barra M15
    // ... lógica ...
}

Para múltiplos timeframes simultaneamente

struct BarTracker
{
    ENUM_TIMEFRAMES tf;
    datetime        lastTime;

    bool IsNew(const string symbol)
    {
        datetime t = iTime(symbol, tf, 0);
        if(t != lastTime) { lastTime = t; return true; }
        return false;
    }
};

BarTracker g_barH4  = { PERIOD_H4,  0 };
BarTracker g_barM15 = { PERIOD_M15, 0 };

void OnTick()
{
    bool newH4  = g_barH4.IsNew(_Symbol);
    bool newM15 = g_barM15.IsNew(_Symbol);

    if(newH4)  UpdateH4Context();   // atualiza viés e zonas H4
    if(newM15) CheckM15Entries();   // verifica setup de entrada
}

Detectar Estrutura de Mercado em MTF

// Detectar se H4 está em tendência de alta (HH + HL)
bool IsH4Uptrend(int lookback = 5)
{
    MqlRates rates[];
    ArraySetAsSeries(rates, true);
    int copied = CopyRates(_Symbol, PERIOD_H4, 1, lookback * 2, rates);
    if(copied < lookback * 2) return false;

    // Verificar sequência de topos e fundos ascendentes
    double lastHigh = rates[0].high;
    double lastLow  = rates[0].low;
    int    higherHighs = 0;
    int    higherLows  = 0;

    for(int i = 1; i < copied; i++)
    {
        if(rates[i].high > lastHigh) { higherHighs++; lastHigh = rates[i].high; }
        if(rates[i].low  > lastLow)  { higherLows++;  lastLow  = rates[i].low;  }
    }

    return (higherHighs >= lookback / 2 && higherLows >= lookback / 2);
}

Ler nível de suporte/resistência do H4 no M15

Identifica o suporte/resistência mais próximo do H4 e usa como referência no M15.

void GetH4SupportResistance(double &support, double &resistance, int lookback = 20)
{
    MqlRates rates[];
    ArraySetAsSeries(rates, true);
    CopyRates(_Symbol, PERIOD_H4, 1, lookback, rates);

    double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    support    =  DBL_MAX * -1;
    resistance =  DBL_MAX;

    for(int i = 0; i < lookback; i++)
    {
        // Pivô de alta (barra com high maior que vizinhas)
        if(i > 0 && i < lookback - 1)
        {
            if(rates[i].high > rates[i-1].high && rates[i].high > rates[i+1].high)
            {
                if(rates[i].high > price && rates[i].high < resistance)
                    resistance = rates[i].high;
            }
            if(rates[i].low < rates[i-1].low && rates[i].low < rates[i+1].low)
            {
                if(rates[i].low < price && rates[i].low > support)
                    support = rates[i].low;
            }
        }
    }
}

Boas práticas MTF

PráticaRazão
Criar handles no OnInit(), não no OnTick()Handles são custosos — criar 1x e reusar
Liberar handles no OnDeinit()Evita memory leak
Usar barras fechadas (índice 1) para sinaisBarras em formação (índice 0) mudam a cada tick
Não criar loops internos ao OnTick() sem controlePode travar o terminal em ativos com muitos ticks
Validar CopyRates() > 0 antes de usar o arrayFalhas de cópia retornam -1 ou 0
Usar PERIOD_CURRENT só quando intencionalExplicitar sempre o timeframe evita bugs sutis

Combinações MTF recomendadas

EstratégiaTimeframe macroTimeframe de entrada
Swing trade forexD1 / H4H1 / M30
Day trade índicesH4 / H1M15 / M5
ScalpingH1 / M30M5 / M1
Position tradeW1 / D1H4 / H1

Referências

Referências externas

On this page