Edgoro
Expert Advisor · MQL5

Read indicator values in MQL5: iMA, iRSI and CopyBuffer

Short answer

In MQL5, iMA() and iRSI() return a handle, not a value. Create the handle once in OnInit(), then copy values with CopyBuffer(handle, 0, shift, count, array): after ArraySetAsSeries(array, true), array[0] is the bar at shift, and shift 1 is the last closed bar.

UsesiMAiRSICopyBufferBarsCalculatedArraySetAsSeriesIndicatorRelease

The code

An Expert Advisor that trades nothing: on each new bar it prints the last closed bar's close, moving average and RSI in the Experts tab.

Save it as MQL5/Experts/read-indicator-values-copybuffer.mq5 and compile it in MetaEditor (F7): 0 errors, 0 warnings.

MQL5
input int            InpMaPeriod  = 50;       // Moving average period
input ENUM_MA_METHOD InpMaMethod  = MODE_EMA; // Moving average method
input int            InpRsiPeriod = 14;       // RSI period

int maHandle  = INVALID_HANDLE;
int rsiHandle = INVALID_HANDLE;

int OnInit()
  {
   // Create each indicator once. The handle is a reference to it, not a value.
   maHandle  = iMA(_Symbol, PERIOD_CURRENT, InpMaPeriod, 0, InpMaMethod, PRICE_CLOSE);
   rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, InpRsiPeriod, PRICE_CLOSE);
   if(maHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE)
     {
      PrintFormat("Indicator creation failed: error %d", GetLastError());
      return INIT_FAILED;
     }
   return INIT_SUCCEEDED;
  }

void OnDeinit(const int reason)
  {
   if(maHandle != INVALID_HANDLE)
      IndicatorRelease(maHandle);
   if(rsiHandle != INVALID_HANDLE)
      IndicatorRelease(rsiHandle);
  }

// Copies `count` values of buffer `buffer`, starting at bar `shift`, newest first: values[0] is bar `shift`.
bool ReadBuffer(const int handle, const int buffer, const int shift, const int count, double &values[])
  {
   if(BarsCalculated(handle) < shift + count)   // still calculating, or not enough bars
      return false;
   ArraySetAsSeries(values, true);
   ResetLastError();
   int copied = CopyBuffer(handle, buffer, shift, count, values);
   if(copied != count)
     {
      PrintFormat("CopyBuffer copied %d of %d values, error %d", copied, count, GetLastError());
      return false;
     }
   return true;
  }

void OnTick()
  {
   static datetime lastBar = 0;
   datetime bar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(bar == 0 || bar == lastBar)
      return;

   double ma[], rsi[];
   // Bars 1 and 2: the last two closed bars. Bar 0 is still forming and its value moves with every tick.
   if(!ReadBuffer(maHandle, 0, 1, 2, ma) || !ReadBuffer(rsiHandle, 0, 1, 2, rsi))
      return;   // lastBar isn't updated, so the next tick tries again
   lastBar = bar;

   double close1 = iClose(_Symbol, PERIOD_CURRENT, 1);
   PrintFormat("Last closed bar: close %s, %s(%d) %s, RSI(%d) %.1f (bar before: %.1f)",
               DoubleToString(close1, _Digits), StringSubstr(EnumToString(InpMaMethod), 5), InpMaPeriod,
               DoubleToString(ma[0], _Digits), InpRsiPeriod, rsi[0], rsi[1]);
   if(close1 > ma[0] && rsi[1] < 50.0 && rsi[0] >= 50.0)
      Print("Close above the moving average and RSI back above 50");
  }

How it works

  • Handles in OnInit(). iMA() and iRSI() start an indicator and return a number that refers to it. Checking for INVALID_HANDLE there, and refusing to start, is better than failing on every tick.
  • BarsCalculated(handle). How many bars the indicator has calculated; -1 or a small number while it's still working, typically on the first ticks after start. Copying before that fails.
  • ArraySetAsSeries(values, true). Makes index 0 the newest value copied. Without it, CopyBuffer() fills the array oldest first and values[0] is the older bar.
  • CopyBuffer(handle, buffer, shift, count, values). Buffer 0 is the main line of single-line indicators. The return value is the number of values copied; anything other than count means try again later.
  • Shift 1 and 2. Bar 0 is still forming: its moving average and RSI change with every tick. Values of closed bars don't, so a decision made on them is the same live and in the Strategy Tester.
  • lastBar set after the read. If the read fails, the bar isn't marked as done and the next tick retries it.

Pitfalls

  • Expecting a value, as in MQL4. if(iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE) > iClose(_Symbol, PERIOD_CURRENT, 1)) compiles in MQL5 without a warning, and compares the handle number with a price. The MQL4 call with a shift argument doesn't compile at all: wrong parameters count.
  • Creating the handle in OnTick(). The indicator needs time to calculate after it's created, so the first copies fail with 4806 requested data not found, and handles that are never released keep indicators loaded.
  • An invalid handle. A wrong period, a symbol not in Market Watch or a missing custom indicator gives INVALID_HANDLE and 4802 indicator cannot be created.
  • The wrong buffer. Multi-line indicators have several buffers: iBands() 0 middle, 1 upper, 2 lower; iMACD() 0 main, 1 signal; iStochastic() 0 main, 1 signal; iADX() 0 ADX, 1 +DI, 2 -DI. Reading the wrong one gives plausible but wrong numbers.
  • Reading past what was copied. Copy 1 value and read values[1], and the EA stops with array out of range. A timeframe kept in an int input from MQL4 code fails to compile with cannot convert enum.

Variations

  • Another timeframe. iRSI(_Symbol, PERIOD_H4, 14, PRICE_CLOSE) on an M15 chart works the same way; shift 1 is then the last closed H4 bar.
  • ATR, Bollinger Bands, MACD. Same pattern with iATR(), iBands() or iMACD() and the buffer numbers above. The RSI and ATR guides explain what the values mean.
  • A custom indicator. iCustom() returns a handle too, read with the same CopyBuffer(): see reading a custom indicator.
  • A signal from the values. The moving average crossover EA turns two of these reads into entries and exits.

Questions

Why does CopyBuffer() return -1?

Most often because the indicator hasn't finished calculating, right after the EA starts or after a timeframe change (error 4806). Return and try again on the next tick. If it never succeeds, check that the handle isn't INVALID_HANDLE and that the chart has enough history for the period.

How do I get the RSI value of the previous candle in MQL5?

Copy one value starting at shift 1: CopyBuffer(rsiHandle, 0, 1, 1, rsi) with rsi set as series, then read rsi[0]. Shift 0 would be the candle still forming.

Why are my EA's indicator values different from the chart's?

Check the shift (bar 0 or 1), the timeframe, the method and the applied price first. Exponential averages can also differ slightly in the last decimals when the EA and the chart start from a different amount of history.

This program is a minimal example written to show one technique, not a trading system, and none of it is a recommendation to trade. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.

All MQL5 errors

Want it inside a complete EA?

Edgoro's MT5 EA generator creates the indicator handles in OnInit(), checks them, reads closed bars and releases them on exit, for any built-in indicator your strategy uses. A clean build proves the code compiles, not that a strategy works: test it on a demo account.