The EA skeleton: new-bar gate, position check, magic number, and one clean entry.
Intermediate10 min readBeginner → AdvancedLesson 10 / 17
Try it in MetaEditor
A minimal EA: new-bar gate, magic number, one entry
Save as MQL5/Experts/Lesson10FirstEA.mq5. DEMO ACCOUNT ONLY — it opens a real buy on the first new bar. Watch the Experts tab for the retcode.
//+------------------------------------------------------------------+
//| Lesson 10 - First EA: new-bar gate, magic number, one entry |
//| WARNING: sends real market orders. Use a DEMO account. |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version "1.00"
#property strict
#include <Trade/Trade.mqh>
input double Lots = 0.10;
input int StopPoints = 200;
input ulong MagicNumber = 10101010;
CTrade trade;
datetime lastBarTime = 0;
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(10);
trade.SetTypeFillingBySymbol(_Symbol);
Print("Lesson 10 EA ready on ", _Symbol, " / ", EnumToString((ENUM_TIMEFRAMES)_Period));
return(INIT_SUCCEEDED);
}
void OnTick()
{
// 1) Only act on the first tick of a new bar.
if(!IsNewBar())
return;
// 2) Never stack positions - check first.
if(HasPosition(_Symbol, MagicNumber))
{
Print("Already in a position on ", _Symbol, " - standing down.");
return;
}
// 3) Send the order and always log the retcode.
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double sl = NormalizeDouble(ask - StopPoints * _Point, _Digits);
bool ok = trade.Buy(Lots, _Symbol, 0.0, sl, 0.0, "lesson10");
Print("Buy request ", ok ? "accepted" : "FAILED",
" retcode=", trade.ResultRetcode(),
" (", trade.ResultRetcodeDescription(), ")");
if(ok)
Print("Deal: ", trade.ResultDeal(), " volume: ", trade.ResultVolume(),
" price: ", DoubleToString(trade.ResultPrice(), _Digits));
}
//+------------------------------------------------------------------+
//| Has the current bar's open time changed since we last looked? |
//+------------------------------------------------------------------+
bool IsNewBar()
{
datetime t = iTime(_Symbol, _Period, 0);
if(t == lastBarTime)
return false;
lastBarTime = t;
return true;
}
//+------------------------------------------------------------------+
//| Is there a position on this symbol carrying our magic number? |
//+------------------------------------------------------------------+
bool HasPosition(const string sym, const ulong magic)
{
if(!PositionSelect(sym))
return false;
return ((ulong)PositionGetInteger(POSITION_MAGIC) == magic);
}
To run it
1. Open MetaEditor from MT5 (press F4), and pick the folder this program belongs in — Experts, Indicators, or Scripts.
2. Create a new file, paste this over it, then press F7 to compile. Fix anything the Errors tab reports.
3. Back in MT5, drag it onto a chart — or open the Strategy Tester if it is an Expert Advisor.
What changes when you can trade
An indicator only draws. An EA acts, and acting means three new responsibilities: decide when to act, avoid acting twice, and take responsibility only for your own positions.
The new-bar gate
OnTick fires on every price update. If your logic is bar-based — and most trend-following logic is — you must ignore all but the first tick of each new bar. Store the current bar's open time; if it has not changed, return. Without this gate an EA can open dozens of positions in a single minute when a condition first becomes true.
The magic number
A magic number is just an integer you attach to every order. It is the only reliable way for an EA to tell its own positions apart from yours and from other EAs on the same account. Check it before closing or modifying anything. Pick something distinctive; do not reuse 0.
Check for an existing position first
Before every entry, ask whether you are already in. Use PositionSelect(symbol) then verify POSITION_MAGIC. On hedging accounts multiple positions can exist per symbol, so you may need to iterate PositionsTotal() and match each ticket — the simple version here assumes one position per symbol, which is what netting CFD accounts enforce anyway.
Log the retcode
Every trade call returns a result code. Print it. "It did not trade" is not a diagnosis; retcode 10019 (no money) and 10016 (invalid stops) are two completely different problems with completely different fixes.
Run this on a demo account only
This lesson's EA sends a real market order. That is the point — but run it on demo until you have read Part 3 all the way through and understand the risk lesson.
What you just did
Lesson 10 of 17 in MetaTrader 5 MQL5 for CFD Traders. When you have run the examples or read the section, tick it off and move to the next lesson.