Blog
Pine Script ta.rsi and ta.ema vs MQL5: Why Values Differ
Quick Answer
ta.ema, ta.sma, ta.rma and ta.rsi in Pine Script each have a matching MQL5 indicator: iMA with MODE_EMA, MODE_SMA and MODE_SMMA, and iRSI. The formulas are the same family, so the values are normally close. They are not guaranteed to be identical.
Most differences come from the data (a different feed or a different start of history), the price source (close vs another source), and the warm-up period. To check a conversion, compare the two platforms on the same closed bars, after enough history has loaded, and accept a small tolerance.
Key Facts
| Pine Script | MQL5 | Notes |
|---|---|---|
| ta.sma(src, n) | iMA(..., n, 0, MODE_SMA, price) | Simple average |
| ta.ema(src, n) | iMA(..., n, 0, MODE_EMA, price) | Exponential average |
| ta.rma(src, n) | iMA(..., n, 0, MODE_SMMA, price) | Smoothed (Wilder-style) average |
| ta.wma(src, n) | iMA(..., n, 0, MODE_LWMA, price) | Linear weighted average |
| ta.rsi(src, n) | iRSI(_Symbol, _Period, n, price) | Relative Strength Index |
Check each platform's documentation for the exact formula and how it starts the calculation, because the first bars are where results differ most.
The Pine Script Version
emaValue = ta.ema(close, 20)
rsiValue = ta.rsi(close, 14)
Both lines use close as the source. In MQL5 the source is the applied_price argument, so it has to be chosen to match.
Match the Price Source
| Pine Script source | MQL5 applied price |
|---|---|
| close | PRICE_CLOSE |
| open | PRICE_OPEN |
| high | PRICE_HIGH |
| low | PRICE_LOW |
| hl2 | PRICE_MEDIAN |
| hlc3 | PRICE_TYPICAL |
| hlcc4 | PRICE_WEIGHTED |
| ohlc4 | No built-in constant, so you need a custom calculation |
Using PRICE_CLOSE by default when the Pine script used hlc3 is a quiet way to get different numbers.
Why the Values Can Differ
- Different data feed. TradingView and your broker may build bars from different sources, so open, high, low and close can differ slightly. Every indicator built on them inherits the difference.
- Different history length. An EMA or RSI is recursive, so each value depends on earlier bars. If the two platforms start from different points, the first values differ, and the gap shrinks as more bars accumulate.
- Warm-up. The first bars after the start of history are the least reliable. Compare bars well after the start, for example several times the indicator period.
- Different source.
hl2,hlc3or a custom source in Pine versusPRICE_CLOSEin MQL5. - Different time zones for bars. Broker server time often differs from the chart time on TradingView. The same bar can carry a different label, so match bars by their open, high, low and close instead of by clock time.
- Different symbol. The broker symbol and the TradingView symbol are not always the same instrument, and may have different sessions or prices.
- Rounding. The two platforms may show a different number of decimals.
How to Compare One Bar Against the Other
- Use the same symbol, timeframe and settings on both sides.
- Wait until plenty of bars are loaded on both sides.
- Pick two or three bars from the middle of the data, not the first bars.
- Match each bar by its OHLC values, not by its time label.
- Read the Pine value from the data window, or plot it.
- Print the MQL5 value for the same bar.
- Compare to a reasonable number of decimals and decide on a tolerance.
MQL5 Code to Print Indicator Values
This example creates an EMA handle and an RSI handle, then prints the values of the last closed bar once per new bar:
int emaHandle = INVALID_HANDLE;
int rsiHandle = INVALID_HANDLE;
int OnInit()
{
emaHandle = iMA(_Symbol, _Period, 20, 0, MODE_EMA, PRICE_CLOSE);
rsiHandle = iRSI(_Symbol, _Period, 14, PRICE_CLOSE);
if(emaHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE)
{
Print("Failed to create indicator handles. Error: ", GetLastError());
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(emaHandle != INVALID_HANDLE) IndicatorRelease(emaHandle);
if(rsiHandle != INVALID_HANDLE) IndicatorRelease(rsiHandle);
}
bool ReadValue(int handle, int shift, double &value)
{
if(BarsCalculated(handle) < shift + 1)
return false;
double buf[];
ArraySetAsSeries(buf, true);
if(CopyBuffer(handle, 0, shift, 1, buf) != 1)
return false;
value = buf[0];
return true;
}
bool IsNewBar()
{
static datetime lastBarTime = 0;
datetime currentBarTime = iTime(_Symbol, _Period, 0);
if(currentBarTime == 0)
return false;
if(currentBarTime == lastBarTime)
return false;
lastBarTime = currentBarTime;
return true;
}
void OnTick()
{
if(!IsNewBar())
return;
double ema, rsi;
if(!ReadValue(emaHandle, 1, ema)) return;
if(!ReadValue(rsiHandle, 1, rsi)) return;
datetime barTime = iTime(_Symbol, _Period, 1);
PrintFormat("%s O=%.5f H=%.5f L=%.5f C=%.5f EMA(20)=%.5f RSI(14)=%.4f",
TimeToString(barTime, TIME_DATE | TIME_MINUTES),
iOpen(_Symbol, _Period, 1),
iHigh(_Symbol, _Period, 1),
iLow(_Symbol, _Period, 1),
iClose(_Symbol, _Period, 1),
ema,
rsi);
}
The line prints the open, high, low and close of the bar, so you can match it to the same bar on the Pine side even when the time labels differ. Shift 1 is the last closed bar, as explained in Pine Script close[1] in MQL5. The safe buffer-reading pattern is covered in the MQL5 CopyBuffer guide, and skipping those checks is a common cause of an array out of range error.
Common Mistakes
- Comparing the first bars. Early values are the least stable, so differences look bigger than they are.
- Matching bars by time label. Broker time and chart time can differ.
- Ignoring the price source. Pine's
hlc3is notPRICE_CLOSE. - Using the wrong mode.
ta.rmamaps toMODE_SMMA, notMODE_EMA. - Comparing a forming bar. Shift 0 changes on every tick, so compare closed bars only.
- Expecting identical decimals. Look for close values, not an exact string match.
- Different symbol or session. A different instrument gives different bars.
What to Do if the Gap Is Large
If the values still differ clearly after warm-up, check these in order:
- Is the price source the same?
- Is it the same timeframe and symbol?
- Do the bars have the same OHLC values?
- Did the Pine script change any setting, for example a different
lengthor an offset? - Is the MQL5 indicator reading shift 1 on a new bar?
For a wider list of reasons a strategy behaves differently after conversion, see why a Pine Script strategy gives different results in MQL5. To rebuild an indicator itself as an EA, see how to convert an MT5 indicator to an EA.
FAQ
Is ta.ema the same as iMA with MODE_EMA?
They are the same kind of exponential average. Values are usually close, but not guaranteed to match exactly, because of different data and different starting history.
Which MQL5 mode matches ta.rma?
MODE_SMMA, the smoothed moving average.
Why is my MQL5 RSI different from the TradingView RSI?
Common causes are a different data feed, different history length, a different price source, and comparing bars close to the start of the data. Compare closed bars in the middle of the data, matched by OHLC.
Does the broker's time zone matter?
It affects the label on each bar. Match bars by their OHLC values, or convert the time before you compare.
Do I need to match every decimal?
No. Decide on a tolerance. Differences far below the size of a typical price move usually do not change the logic of a strategy, but test that for your own rules.
Let the Converter Pick the Matching Indicator
Choosing the right mode, source and shift for every ta.* call takes care, and one wrong constant quietly changes a signal. CodeFlowOS translates Pine Script into MQL5 and lets you review the generated code before you test it in the MT5 Strategy Tester. You can try it on the converter page.
This article is educational. It does not describe trading performance and is not financial advice. Always compile and test code in MetaEditor and the Strategy Tester before using it on a live account.