MT5 Awesome Oscillator: Built-in vs Example iCustom Color Buffer Mismatch (the Tie Case)
MetaTrader 5 has a fun little habit: two things that look like “the same indicator” can behave almost identically—until a rare edge case shows up and ruins your day.
This post documents a parity issue I hit on JP225Cash while building a deterministic benchmark pipeline (MQL5 exporter → CPU/GPU parity): Awesome Oscillator (AO) matched perfectly on the main value buffer (b0) but occasionally diverged on the color buffer (b1) when comparing built-in AO vs the Examples/Awesome_Oscillator.mq5 ( iCustom) implementation.
The punchline: it wasn’t numeric drift. It was a tie-break rule.
1) The symptom: b0 matches, b1 sometimes doesn’t
Across a multi-year export window (2021–2026), AO behaved like this:
- b0 (AO value): 100% identical between built-in and iCustom
- b1 (color index): mismatched in only two positions out of years of data
That’s the worst kind of bug: rare enough to doubt your sanity, deterministic enough to block strict parity. On those mismatch bars, the AO value was identical, but the color differed:
- Built-in
b1 = 0 - iCustom
b1 = 1
2) Why only two ordinals over five years?
Because the mismatch only happened when:
AO[i] == AO[i-1]
AO is computed as a difference of moving averages, so it typically changes every bar. But in very rare situations (price patterns + median price + MA windows lining up), you can get exactly equal consecutive AO values. Over five years, that happened only twice in my dataset—which perfectly explains why the mismatch was so rare.
3) Root cause: tie (“equal”) is treated differently
The stock iCustom sample in MQL5/Indicators/Examples/Awesome_Oscillator.mq5 assigns the color buffer like this (conceptually):
- If
AO[i] > AO[i-1]→ Green (0) - Else → Red (1)
That means: if AO[i] == AO[i-1], the sample forces Red.
But the built-in AO appears to behave differently: on ties, it does not force red. Instead, it effectively keeps the previous color (carry-forward).
So the mismatch wasn’t about AO’s value. It was about the semantic definition of “no change”:
- Sample iCustom: “equal counts as not rising → red”
- Built-in: “equal counts as no new direction → keep last color”
4) The fix: carry-forward color on ties
I first tried a shortcut: switching to >= (treat ties as green). That did not match built-in behavior reliably.
The correct fix was to explicitly handle all three cases:
>→ green<→ red==→ carry forward previous color
Here is the exact change:
diff --git a/MQL5/Indicators/Examples/Awesome_Oscillator.mq5 b/MQL5/Indicators/Examples/Awesome_Oscillator.mq5
index 676b17d..3bf135c 100644
--- a/MQL5/Indicators/Examples/Awesome_Oscillator.mq5
+++ b/MQL5/Indicators/Examples/Awesome_Oscillator.mq5
@@ -115,10 +115,12 @@ int OnCalculate(const int rates_total,
for(i=start; i<rates_total && !IsStopped(); i++)
{
ExtAOBuffer[i]=ExtFastBuffer[i]-ExtSlowBuffer[i];
- if(ExtAOBuffer[i]>=ExtAOBuffer[i-1])
+ if(ExtAOBuffer[i]>ExtAOBuffer[i-1])
ExtColorBuffer[i]=0.0; // set color Green
- else
+ else if(ExtAOBuffer[i]<ExtAOBuffer[i-1])
ExtColorBuffer[i]=1.0; // set color Red
+ else
+ ExtColorBuffer[i]=ExtColorBuffer[i-1];
}
//--- return value of prev_calculated for next call
return(rates_total);
After this change, the exporter produced iCustom output that matched the built-in output—including the color buffer— across the entire multi-year window.
5) Why this matters: “visual buffers” are still outputs
A common mistake is to treat buffers like b1 (colors) as “just UI details.” But in a deterministic benchmark/parity system, a color index buffer is still a buffer:
- It’s exported
- It’s compared
- It participates in truth and reproducibility
If you care about strict parity, you must define semantics not only for numeric values but also for metadata-like outputs (colors, states, regime flags, etc.).
6) Source of truth strategy: you can’t change built-in behavior
In my setup, MQL5 exporter outputs are treated as an external source of truth used to validate CPU (NumPy) and GPU (CuPy/Numba) implementations. That raises an unavoidable question:
If built-in and iCustom differ, which one is “correct”?
In practice:
- You can’t patch the built-in indicator.
- You can patch your iCustom reference implementation.
So the cleanest strategy is to adjust the iCustom reference to match the built-in behavior where it’s clearly a semantic tie-break difference, not an algorithmic discrepancy.
This is not “relaxing tests.” It’s aligning definitions so your truth source is consistent.
7) Takeaway
This bug wasn’t floating-point drift. It wasn’t rounding. It wasn’t a tolerance policy problem. It was a tie-break rule hidden in a single line of code:
“What should the color be when the value doesn’t change?”
Once you define the tie case explicitly (carry-forward), the built-in and iCustom outputs converge, and the parity pipeline becomes stable again.
The universe remains strange, but at least the AO color buffer stops gaslighting you.
Share
Leave a Comment
You can share your questions, comments or criticisms about this post with me. Your e-mail address will not be shared with anyone.
Comments (0)
There are no comments for this post yet. Be the first to comment.