Elliott Wave is from the 1930s. DeMark showed up sixty years later. Completely different philosophies – one counts waves, the other counts sequential price exhaustion. They shouldn't work together.
But they do. Absurdly well, actually.
We spent three months wiring these two systems together, and the conflict resolution logic alone cut our false entry rate nearly in half during backtesting. That's not marketing – that's what the equity curve showed us. Here's the full production EA, with MQL4 source and a Pine Script port.
The Architecture
Dead simple principle at the core: two independent signals must agree before entry.
ENTRY CONDITION:
- (Elliott Wave bias = LONG OR DeMark signal = BUY) AND
- NOT (conflicting opposite signal is active)
One signal alone can trigger a trade – but only if the other system isn't screaming the opposite direction. We call it "agree or sit on your hands" logic. And it's wrapped inside a risk management shell that's honestly stricter than most traders would tolerate.
Signal 1: ZigZag-Based Elliott Wave Detection
Elliott Wave traders who just eyeball it drive us crazy. We didn't avoid it – we got practical. Instead of encoding Ralph Nelson Elliott's complete rule set, we use a simplified pivot analysis via the ZigZag indicator. It's not purist. It works.
Extracting Pivots from ZigZag
int GetZigZagPivots(double &prices[], int ×[], int maxPivots)
{
ArraySetAsSeries(prices, false);
ArraySetAsSeries(times, false);
int scanned = MathMin(200, Bars - 1);
int count = 0;
for (int i = scanned; i >= 1 && count < maxPivots; i--)
{
double zLow = iCustom(NULL, 0, "ZigZag",
ZigZagDepth, ZigZagDeviation, ZigZagBackstep,
1, i);
double zHigh = iCustom(NULL, 0, "ZigZag",
ZigZagDepth, ZigZagDeviation, ZigZagBackstep,
0, i);
double val = (zLow == 0 ? zHigh : zLow);
if (val != 0)
{
ArrayResize(prices, count + 1);
ArrayResize(times, count + 1);
prices[count] = val;
times[count] = (int)Time[i];
count++;
}
}
ReverseArrayD(prices, count);
ReverseArrayI(times, count);
return count;
}ZigZag defaults that took us weeks: Depth = 12, Deviation = 5, Backstep = 3.
From Pivots to Wave Count
int DetectElliottApprox()
{
double pivPrice[];
int pivTime[];
int pivCount = GetZigZagPivots(pivPrice, pivTime, 10);
if (pivCount < 5) return 0;
int last = pivCount - 1;
int upDownCount = 0;
for (int i = last; i > last - 4; i--)
{
double diff = pivPrice[i] - pivPrice[i - 1];
if (diff > 0) upDownCount++;
else upDownCount--;
}
if (upDownCount >= 4) return +1; // long bias
if (upDownCount <= -4) return -1; // short bias
return 0;
}Grab the last 5 ZigZag pivots → 4 price movements. If all 4 swing the same direction? Strong impulse wave. That's your Elliott approximation.
Signal 2: DeMark via iCustom
We pull the DeMark signal from an external custom indicator through iCustom() – probably the most underrated function in MQL4:
int DeMarkSignal()
{
double val = iCustom(Symbol(), 0, DeMarkIndicatorName,
DeMarkBufferIndex, 0);
if (val != EMPTY_VALUE && val != 0)
{
if (val > 0.5) return +1; // buy signal
if (val < -0.5) return -1; // sell signal
}
return 0;
}DeMark fires when a trend is exhausted. Elliott reads trend continuation. When both say "go long"? You're catching a pullback that's done pulling back. That's the sweet spot.
The Conflict Resolution Engine
When you mash two independent signal systems together, you get four possible states. How you handle conflicts matters more than the signals themselves:
| Elliott | DeMark | Action |
|---|---|---|
| Long | Buy | Strong Buy – both agree |
| Long | None | Buy (single signal, no conflict) |
| None | Buy | Buy (single signal, no conflict) |
| Long | Sell | No Trade – conflict |
| Short | Buy | No Trade – conflict |
| Short | Sell | Strong Sell – both agree |
bool ConflictingShortSignal(bool deMarkShort, bool elliottShort)
{
if (deMarkShort && elliottShort) return true;
if (deMarkShort && UseDeMark && UseElliott && !elliottShort) return true;
return false;
}The EA is more afraid of conflict than it is hungry for confirmation. One opposing signal? Veto. Hard stop. No trade. The veto logic isn't conservative. It's survival.
The Risk Management Shell
Before any order fires, the EA runs six pre-checks. Fail any single one and the bar gets skipped:
void OnTick()
{
static datetime lastTickTime = 0;
if (Time[0] == lastTickTime) return;
lastTickTime = Time[0];
if (!IsTradingHour()) return;
if (!IsAllowedWeekday()) return;
if (SpreadInPoints() > MaxSpreadPoints) return;
if (AccountFreeMargin() < MinimumAccountFreeMargin) return;
if (ExceededDrawdown()) return;
if (CountSymbolTradesToday() >= MaxTradesPerDay) return;
// ... then compute signals and enter
}ATR-Based Position Sizing
SL = ATR × 1.5, TP = SL × 2.0 – 1:2 risk-reward. Lot size from risk % so volatile markets → wider stops → smaller lots automatically.
double LotsByRisk(double riskPct, double slPrice)
{
double riskMoney = AccountBalance() * riskPct / 100.0;
if (Bid == 0 || Ask == 0)
return MarketInfo(Symbol(), MODE_MINLOT);
double pipValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double slPips = MathAbs((Ask - slPrice) / Point);
if (pipValue <= 0) pipValue = 1.0;
double lots = riskMoney / (slPips * pipValue);
double step = MarketInfo(Symbol(), MODE_LOTSTEP);
int steps = (int)(lots / step);
return NormalizeDouble(steps * step, 2);
}Tuning the Parameters
| Parameter | Default | Purpose | Tuning Guidance |
|---|---|---|---|
| ZigZagDepth | 12 | Pivot sensitivity | Lower = more pivots, noisier. Higher = fewer, smoother |
| ATRPeriod | 14 | Volatility window | 14 for H1, 20 for H4, 10 for M15 |
| ATRMultiplierSL | 1.5 | Stop width | 1.0 = tight. 2.0 = wide |
| TP_Multiplier | 2.0 | Risk:reward | 1.5 = more wins. 3.0 = larger payoff |
| MaxTradesPerDay | 3 | Overtrading limit | Lower in chop, raise in trending |
| MaxDailyDrawdownPct | 10% | Circuit breaker | 5% conservative, 15% aggressive |
| RiskPercent | 1.0% | Per-trade risk | 0.5% beginners, 2% max experienced |
What Makes This Strategy Work
- Signal diversity – Elliott (trend continuation) + DeMark (exhaustion). When both agree, you're catching the moment a pullback ends and the trend resumes.
- Asymmetric conflict resolution – Any conflict = no trade. You'll miss valid entries. Accounts don't die from missed winners; they die from false signals.
- Volatility-adaptive risk – ATR stops + % lot sizing = the system tunes itself.
- Defense in depth – Six pre-checks before any signal evaluation: trading hours, weekday, spread, margin, drawdown, daily trade count.
Build a Multi-Signal Strategy
Elliott + DeMark is just one combination. The architecture pattern – independent signals, conflict resolution, risk shell – works with almost any pair of indicators. RSI + Bollinger. MACD + Ichimoku.

