Indicators & reading market data
Indicator handles (iMA, iRSI, iATR), CopyBuffer to pull values into arrays, reading the latest price with SymbolInfoDouble, and checking open positions with PositionsTotal.
A strategy needs numbers: a moving average, an RSI reading, the current spread, how many positions you already hold. MQL5's way of getting these is specific and, at first, surprising — so let us get it exactly right, because everything else builds on it.
Indicator handles: create once, read many times
You do not compute a moving average yourself. You ask the terminal to run its built-in indicator and give you a handle — an int ticket that refers to a running indicator instance. You create the handle once in OnInit(), then read fresh values from it on every tick. Creating a handle every tick is a classic beginner mistake that leaks resources and slows the EA to a crawl.
class="c">// iMA(symbol, timeframe, period, shift, method, applied_price)
int maHandle = iMA(_Symbol, _Period, class="n">20, class="n">0, MODE_EMA, PRICE_CLOSE);
class="c">// iRSI(symbol, timeframe, period, applied_price)
int rsiHandle = iRSI(_Symbol, _Period, class="n">14, PRICE_CLOSE);
class="c">// iATR(symbol, timeframe, period) — Average True Range, for volatility
int atrHandle = iATR(_Symbol, _Period, class="n">14);
class="c">// Every handle can fail. ALWAYS check.
if(maHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE || atrHandle == INVALID_HANDLE)
{
Print(class="s">"Failed to create an indicator handle: ", GetLastError());
return(INIT_FAILED);
}CopyBuffer: pulling values into an array
A handle is just a reference. To read actual numbers you call CopyBuffer, which copies values from the indicator into a double array you own. An indicator can have several output lines (buffers) — a moving average has one (buffer 0); MACD has three. You say which buffer, where to start, and how many values you want.
double maBuffer[]; class="c">// our destination array
ArraySetAsSeries(maBuffer, true); class="c">// index class="n">0 = most recent bar
class="c">// CopyBuffer(handle, buffer_index, start_pos, count, dest_array)
int copied = CopyBuffer(maHandle, class="n">0, class="n">0, class="n">3, maBuffer);
if(copied < class="n">3)
{
Print(class="s">"Not enough MA data yet: ", GetLastError());
return; class="c">// bail out of this tick; try again on the next
}
double maNow = maBuffer[class="n">0]; class="c">// MA on the current (forming) bar
double maPrev = maBuffer[class="n">1]; class="c">// MA on the last closed barReading price and symbol facts
For raw price and contract details you use the SymbolInfoDouble / SymbolInfoInteger family. These return the live properties of the symbol your EA is attached to.
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); class="c">// buy at the ask
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); class="c">// sell at the bid
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); class="c">// smallest price step
double spread = (ask - bid) / point; class="c">// current spread in points
Print(class="s">"Ask=", ask, class="s">" Bid=", bid, class="s">" Spread=", spread, class="s">" points");This mirrors Part 1 exactly: you buy at the ask, sell at the bid, and the gap between them is the spread you pay. An EA that ignores the live spread will place orders the backtest never charged it for.
Do I already hold a position?
Before an EA opens a trade it almost always needs to know what it already holds. The PositionsTotal() function returns how many positions are open across the account; you loop over them and inspect each. Here is a helper that answers "does this EA already have a position on this symbol?" — the guard that keeps a strategy to one position at a time.
class="c">// Returns true if a position opened by THIS EA exists on the current symbol.
bool HasOpenPosition(ulong magic)
{
for(int i = PositionsTotal() - class="n">1; i >= class="n">0; i--)
{
ulong ticket = PositionGetTicket(i); class="c">// select position i
if(ticket == class="n">0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != magic) continue;
return(true); class="c">// found one of ours
}
return(false);
}You now have every ingredient: create handles in OnInit, CopyBuffer fresh values in OnTick, read live price with SymbolInfoDouble, and check your own positions. The next lesson assembles them into a complete moving-average-cross strategy.