Insights/Insights & Articles/Breakout Trading with Triple-Indicator Confirmation (EMA + CCI)

Breakout Trading with Triple-Indicator Confirmation (EMA + CCI)

We've watched traders blow up accounts chasing every breakout like it's Black Friday at a department store. Breakouts in choppy markets? Pure account destruction. Slow, painful, one fake signal at a time. So we stopped doing that.

Mar 16, 2026

Breakout Trading with Triple-Indicator Confirmation (EMA + CCI)

Breakout Trading with Triple-Indicator Confirmation (EMA + CCI)

We've watched traders blow up accounts chasing every breakout like it's Black Friday at a department store. Breakouts in choppy markets? Pure account destruction. Slow, painful, one fake signal at a time.

So we stopped doing that.

The fix was obvious once we'd lost enough money figuring it out: don't touch the trade unless three independent systems – trend, momentum, and trigger – all agree at the same time. We built this strategy across 11 versions for a client who kept getting burned by false breakouts on gold. Version 1 was embarrassingly naive. Version 11 filters out roughly 80% of the garbage signals. We're walking you through the whole thing here – MQL4, Pine Script, and the fractal-based level detection that holds it all together.

The Strategy Architecture

Three independent systems have to unanimously say "go" before a single lot gets placed:

FilterConditionRole
TrendEMA(21) > EMA(34) AND CCI(20) > 0Direction – no breakouts in a downtrend
MomentumClose > EMA(20) AND CCI(10) > 0Force – move has short-term conviction
TriggerClose > EMA(60) AND CCI(10) > 0Context – bigger picture agrees

ALL THREE TRUE → Check for resistance breakout. ANY ONE FALSE → No trade.

Why three filters instead of one? Because one filter is a suggestion. Three filters agreeing is a conviction.

  • Trend Filter – Stops you from buying breakouts in a downtrend. You'd be surprised how many EAs skip this.
  • Momentum Filter – Is the move actually going somewhere, or just a limp squeeze with no volume behind it?
  • Trigger Filter – Zooms out. The longer timeframe has to agree too, or you're trading noise.

Part 1: Fractal-Based Resistance Detection

You can't trade a breakout if you don't know what's breaking. This strategy finds resistance dynamically using a fractal-like pattern – a bar whose high towers above N bars on both sides.

A resistance level is a bar where:

  • The high is greater than the highs of the N bars to its right (recent bars)
  • The high is greater than the highs of the N bars to its left (older bars)

That's it. A local peak. A swing high. The market has memory, and these fractal pivots capture it better than arbitrary round numbers ever will.

MQL4 Implementation

// Configuration
input int RightBars = 2;   // Bars to check on the right side of the pivot
input int LeftBars  = 3;   // Bars to check on the left side of the pivot
 
double ResistanceLevels[5];
int    ResistanceBars[5];
 
void FindResistanceLevels()
{
    int startBar = RightBars + 1;
    int found = 0;
 
    for (int i = 0; i < 100 && found < 5; i++)
    {
        int pivotBar = startBar + i;
        bool rightValid = true;
        for (int r = RightBars; r > 0; r--)
        {
            if (High[pivotBar - r] >= High[pivotBar])
            { rightValid = false; break; }
        }
        if (!rightValid) continue;
 
        bool leftValid = true;
        for (int l = 1; l <= LeftBars; l++)
        {
            if (High[pivotBar + l] >= High[pivotBar])
            { leftValid = false; break; }
        }
        if (!leftValid) continue;
 
        bool strictRight = true;
        for (int r2 = RightBars; r2 > 0; r2--)
        {
            if (High[pivotBar - r2] >= High[pivotBar])
            { strictRight = false; break; }
        }
        bool strictLeft = true;
        for (int l2 = 1; l2 <= LeftBars; l2++)
        {
            if (High[pivotBar + l2] > High[pivotBar])
            { strictLeft = false; break; }
        }
 
        if (strictRight && strictLeft)
        {
            ResistanceLevels[found] = High[pivotBar];
            ResistanceBars[found] = pivotBar;
            found++;
        }
    }
}

Why 5 levels? Because resistance isn't a single line – it's a neighborhood. Tracking 5 levels means the algo picks the most relevant one instead of blindly trading the first ceiling it finds.

Support Level Detection (Mirror Logic)

Same fractal logic, flipped to lows instead of highs. The support level becomes your stop loss anchor: if price falls back below the nearest support, the breakout was a lie. Get out.

double SupportLevel;
 
void FindSupportLevel()
{
    int startBar = RightBars + 1;
    for (int i = 0; i < 100; i++)
    {
        int pivotBar = startBar + i;
        bool valid = true;
        for (int r = RightBars; r > 0; r--)
        {
            if (Low[pivotBar - r] <= Low[pivotBar])
            { valid = false; break; }
        }
        if (!valid) continue;
        for (int l = 1; l <= LeftBars; l++)
        {
            if (Low[pivotBar + l] <= Low[pivotBar])
            { valid = false; break; }
        }
        if (valid)
        {
            SupportLevel = Low[pivotBar];
            return;
        }
    }
}

Part 2: The Triple Confirmation Filter

input int TrendEMA_Short  = 21, TrendEMA_Long = 34, TrendCCI_Period = 20;
input int MomentumEMA = 20, MomentumCCI = 10, TriggerEMA = 60;
 
bool CheckTripleConfirmation()
{
    double shortEMA = iMA(NULL, 0, TrendEMA_Short, 0, MODE_EMA, PRICE_CLOSE, 0);
    double longEMA  = iMA(NULL, 0, TrendEMA_Long,  0, MODE_EMA, PRICE_CLOSE, 0);
    double trendCCI = iCCI(NULL, 0, TrendCCI_Period, PRICE_TYPICAL, 0);
    bool trendUp = (shortEMA > longEMA) && (trendCCI > 0);
 
    double momEMA = iMA(NULL, 0, MomentumEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
    double momCCI = iCCI(NULL, 0, MomentumCCI, PRICE_TYPICAL, 0);
    bool momentumUp = (Close[0] > momEMA) && (momCCI > 0);
 
    double trigEMA = iMA(NULL, 0, TriggerEMA, 0, MODE_EMA, PRICE_CLOSE, 0);
    bool triggerUp = (Close[0] > trigEMA) && (momCCI > 0);
    bool emaAlignment = (momEMA > trigEMA);
 
    return (trendUp && momentumUp && triggerUp && emaAlignment);
}

Why These Specific Indicators?

FilterIndicatorPeriodWhat It Catches
TrendEMA(21) vs EMA(34)Short-to-mediumDirection. Fibonacci-based combo.
TrendCCI(20)MediumOverextension. CCI > 0 = bullish.
MomentumEMA(20) + CCI(10)ShortSpeed. Confirms breakout has momentum.
TriggerEMA(60)LongContext. Macro trend supports the trade.

Part 3: The Breakout Entry

Entry fires when price crosses above resistance by a configurable buffer. Not at resistance. Above it. And not too far – the "not too far" check kills chasing:

input double BreakoutBuffer = 3.0;  // Pips above resistance to confirm breakout
 
void OnTick()
{
    if (OrdersTotal() > 0) return;
    if (!CheckTripleConfirmation()) return;
    FindResistanceLevels();
    double buffer = BreakoutBuffer * Point * 10;
 
    for (int level = 0; level < 5; level++)
    {
        if (ResistanceLevels[level] == 0) continue;
        bool brokeOut = (Close[0] > ResistanceLevels[level] + buffer);
        bool notTooFar = (Close[0] < ResistanceLevels[level] + 1.25 * buffer);
 
        if (brokeOut && notTooFar)
        {
            FindSupportLevel();
            double slDistance = Close[0] - (SupportLevel - BreakoutBuffer * Point * 10);
            double tpDistance = slDistance;  // 1:1 risk-reward
            OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3,
                     Bid - slDistance, Ask + tpDistance,
                     "Breakout", MagicNumber, 0, clrBlue);
            return;
        }
    }
}

The "not too far" checkClose[0] < ResistanceLevels[level] + 1.25 * buffer – is the thing we're proudest of. If price has already moved 20 pips past the breakout point? The trade is gone. Let it go.

Part 4: Stop Loss at Support

We hate fixed pip stops. This strategy places the stop at the nearest support level – the market tells you where the invalidation point is.

  • SL distance = EntryPrice − (SupportLevel − Buffer)
  • TP = EntryPrice + SL_distance (1:1 R:R)

Wide resistance-to-support gap → wide stop and wide target. Tight gap → tight everything. Risk-reward stays 1:1; position sizing adapts to volatility.

Pine Script Version (TradingView)

Same logic, cleaner syntax – Pine's ta.pivothigh() / ta.pivotlow() do the fractal heavy lifting:

//@version=5
strategy("Breakout Triple Confirm", overlay=true)
leftBars   = input.int(3, "Left Bars for Pivot")
rightBars  = input.int(2, "Right Bars for Pivot")
buffer     = input.float(3.0, "Breakout Buffer (pips)") * syminfo.mintick * 10
trendShort = input.int(21, "Trend Short EMA")
trendLong  = input.int(34, "Trend Long EMA")
trdCCI     = ta.cci(close, 20)
mEMA       = ta.ema(close, 20)
mCCI       = ta.cci(close, 10)
trgEMA     = ta.ema(close, 60)
trendUp    = ta.ema(close, trendShort) > ta.ema(close, trendLong) and trdCCI > 0
momentumUp = close > mEMA and mCCI > 0
triggerUp  = close > trgEMA and mCCI > 0
aligned    = mEMA > trgEMA
allConfirm = trendUp and momentumUp and triggerUp and aligned
pivotHigh  = ta.pivothigh(high, leftBars, rightBars)
pivotLow   = ta.pivotlow(low, leftBars, rightBars)
var float resistance = na
var float support = na
if not na(pivotHigh) then resistance := pivotHigh
if not na(pivotLow) then support := pivotLow
brokeOut   = close > resistance + buffer
notTooFar  = close < resistance + buffer * 1.25
entryOK    = allConfirm and brokeOut and notTooFar and not na(support)
slDist = close - (support - buffer)
tpDist = slDist
if entryOK and strategy.position_size == 0
    strategy.entry("Breakout Long", strategy.long)
    strategy.exit("Exit", "Breakout Long", stop=close - slDist, limit=close + tpDist)

What 11 Versions Taught Us

VersionChangeWhy
V1Basic resistance breakToo many false breakouts
V2Added EMA trend filterEliminated counter-trend entries
V3Added CCI momentumFiltered low-conviction moves
V4–V5Multi-level resistance (5 levels)Single level missed opportunities
V6–V8Trigger EMA + alignment checksReduced trades in choppy markets
V9"Not too far" buffer limitStopped chasing extended moves
V10Support-based stop lossSL adapted to market structure
V11Double-pass fractal validationEliminated weak pivot levels

The real takeaway? Nobody writes a good strategy on the first try. V11 trades maybe a third as often – but when it trades, it means it.

Need a Custom Breakout Strategy?

Breakout systems are kind of our thing. We've built over a hundred of them across MT4, TradingView, and Python – each with adaptive levels, multi-indicator confirmation, and obsessive iterative refinement.

Get Your Breakout Strategy Built →

Algorithmic Trading Insights by Quantumcona.

4of4