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:
| Filter | Condition | Role |
|---|---|---|
| Trend | EMA(21) > EMA(34) AND CCI(20) > 0 | Direction – no breakouts in a downtrend |
| Momentum | Close > EMA(20) AND CCI(10) > 0 | Force – move has short-term conviction |
| Trigger | Close > EMA(60) AND CCI(10) > 0 | Context – 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?
| Filter | Indicator | Period | What It Catches |
|---|---|---|---|
| Trend | EMA(21) vs EMA(34) | Short-to-medium | Direction. Fibonacci-based combo. |
| Trend | CCI(20) | Medium | Overextension. CCI > 0 = bullish. |
| Momentum | EMA(20) + CCI(10) | Short | Speed. Confirms breakout has momentum. |
| Trigger | EMA(60) | Long | Context. 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" check –
Close[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
| Version | Change | Why |
|---|---|---|
| V1 | Basic resistance break | Too many false breakouts |
| V2 | Added EMA trend filter | Eliminated counter-trend entries |
| V3 | Added CCI momentum | Filtered low-conviction moves |
| V4–V5 | Multi-level resistance (5 levels) | Single level missed opportunities |
| V6–V8 | Trigger EMA + alignment checks | Reduced trades in choppy markets |
| V9 | "Not too far" buffer limit | Stopped chasing extended moves |
| V10 | Support-based stop loss | SL adapted to market structure |
| V11 | Double-pass fractal validation | Eliminated 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.


