Building a moving-average-cross EA
The full strategy skeleton: fast/slow MA inputs, both handles created in OnInit, once-per-bar cross detection in OnTick — the complete signal engine, ready for orders.
Time to build a real strategy. The moving-average cross is the "hello world" of trend following, and it is a genuinely deployable idea: when a fast average crosses *above* a slow one, momentum has turned up → go long; when it crosses *below*, momentum has turned down → go short. We built the concept in Python back in Part 4; now we build it as an EA.
Detect the cross, not the state
The signal is not "fast is above slow" — that is true for dozens of bars in a row and would fire endlessly. The signal is the moment of crossing, and to see a moment you need *two* snapshots: the last closed bar and the one before it.
- Bullish cross — on the previous bar fast was *below* slow, and on the last closed bar fast is *above* slow.
- Bearish cross — on the previous bar fast was *above* slow, and on the last closed bar fast is *below* slow.
Act once per bar, not once per tick
Because OnTick runs constantly, we gate the whole strategy behind a new-bar check: remember the open time of the last bar we processed, and only run the logic when a fresh bar appears. Everything else on the tick is a no-op. This is the standard MQL5 idiom for a bar-based strategy.
datetime lastBarTime = class="n">0; class="c">// global: open time of the bar we last handled
class="c">// Returns true exactly once per newly-opened bar.
bool IsNewBar()
{
datetime t = iTime(_Symbol, _Period, class="n">0); class="c">// open time of the current bar
if(t == lastBarTime)
return(false);
lastBarTime = t;
return(true);
}The complete signal EA
Here is the full strategy engine — inputs, both handles built in OnInit, the new-bar gate, and cross detection in OnTick. It does not place orders yet: it prints the signal so you can verify the logic in isolation before wiring in real trades next lesson. Building the signal and the execution separately is exactly the discipline from Part 1's five-component blueprint.
class="c">#property copyright class="s">"AlgoPro University"
class="c">#property version class="s">"class="n">1.00"
class="c">#property description class="s">"MA-cross signal engine — demo/testing, not financial advice."
input int FastPeriod = class="n">12; class="c">// Fast EMA period
input int SlowPeriod = class="n">26; class="c">// Slow EMA period
input ENUM_MA_METHOD MaMethod = MODE_EMA; class="c">// MA method
input ENUM_APPLIED_PRICE MaPrice = PRICE_CLOSE; class="c">// Applied price
int fastHandle = INVALID_HANDLE;
int slowHandle = INVALID_HANDLE;
datetime lastBarTime = class="n">0;
int OnInit()
{
if(FastPeriod >= SlowPeriod)
{
Print(class="s">"FastPeriod must be smaller than SlowPeriod.");
return(INIT_PARAMETERS_INCORRECT);
}
fastHandle = iMA(_Symbol, _Period, FastPeriod, class="n">0, MaMethod, MaPrice);
slowHandle = iMA(_Symbol, _Period, SlowPeriod, class="n">0, MaMethod, MaPrice);
if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE)
{
Print(class="s">"Could not create MA handles: ", GetLastError());
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
IndicatorRelease(fastHandle); class="c">// free the indicator resources
IndicatorRelease(slowHandle);
}
class="c">// Fills fast[] and slow[] with the last two CLOSED bar values.
class="c">// Returns false if data is not ready.
bool ReadMAs(double &fast[], double &slow[])
{
ArraySetAsSeries(fast, true);
ArraySetAsSeries(slow, true);
class="c">// Start at index class="n">1 (last closed bar), copy class="n">2 values -> [class="n">1] and [class="n">2].
if(CopyBuffer(fastHandle, class="n">0, class="n">1, class="n">2, fast) < class="n">2) return(false);
if(CopyBuffer(slowHandle, class="n">0, class="n">1, class="n">2, slow) < class="n">2) return(false);
return(true);
}
void OnTick()
{
datetime t = iTime(_Symbol, _Period, class="n">0);
if(t == lastBarTime) return; class="c">// only act on a new bar
lastBarTime = t;
double fast[], slow[];
if(!ReadMAs(fast, slow)) return;
double fastPrev = fast[class="n">1], fastLast = fast[class="n">0]; class="c">// [class="n">0]=last closed, [class="n">1]=one before
double slowPrev = slow[class="n">1], slowLast = slow[class="n">0];
bool crossUp = (fastPrev <= slowPrev) && (fastLast > slowLast);
bool crossDown = (fastPrev >= slowPrev) && (fastLast < slowLast);
if(crossUp) Print(class="s">"BULLISH cross on ", _Symbol, class="s">" — long signal");
if(crossDown) Print(class="s">"BEARISH cross on ", _Symbol, class="s">" — short signal");
}Compile this, attach it to a demo chart, and watch the Experts log print BULLISH/BEARISH as crosses occur. The signal engine works. Now we give it hands: the next lesson turns each printed signal into a real order with the CTrade class.