The Pine Script v6 code
An indicator in its own pane: the RSI is drawn, and the SMA, EMA and ATR of the same length appear in the Data Window so the numbers can be compared with MetaTrader 5.
Checked by Edgoro Pine Check with no errors and no warnings. Pine Check is a static checker, not TradingView's compiler, and this script was not run in TradingView.
//@version=6
indicator("SMA, EMA, RSI and ATR values", overlay = false)
len = input.int(14, "Length", minval = 1)
sma = ta.sma(close, len)
ema = ta.ema(close, len)
rsi = ta.rsi(close, len)
atr = ta.atr(len)
plot(rsi, "RSI", color.purple, 2)
hline(70, "Overbought", color.gray)
hline(30, "Oversold", color.gray)
// Shown in the Data Window only, to compare with MetaTrader 5
plot(sma, "SMA", color.gray, display = display.data_window)
plot(ema, "EMA", color.orange, display = display.data_window)
plot(atr, "ATR", color.teal, display = display.data_window)
The MQL5 program
A custom indicator with the same RSI line and, in the Data Window, the SMA, the EMA, a Wilder ATR equal to Pine's and MetaTrader 5's own iATR for comparison. It only draws.
Save it as MQL5/Indicators/ta-ema-sma-rsi-with-handles.mq5 and compile it in MetaEditor (F7): 0 errors, 0 warnings.
//+------------------------------------------------------------------+
//| ta.sma, ta.ema, ta.rsi and ta.atr in MQL5 |
//| RSI drawn; SMA, EMA and both ATRs in the Data Window (Ctrl+D). |
//| Custom indicator: save in MQL5/Indicators and compile (F7). |
//+------------------------------------------------------------------+
#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_level1 30
#property indicator_level2 70
#property indicator_buffers 6
#property indicator_plots 5
#property indicator_label1 "RSI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrMediumPurple
#property indicator_width1 2
#property indicator_label2 "SMA"
#property indicator_type2 DRAW_NONE
#property indicator_label3 "EMA"
#property indicator_type3 DRAW_NONE
#property indicator_label4 "ATR (Wilder, as ta.atr)"
#property indicator_type4 DRAW_NONE
#property indicator_label5 "ATR (MT5 iATR)"
#property indicator_type5 DRAW_NONE
input int InpLen = 14; // Length
double RsiBuf[], SmaBuf[], EmaBuf[], AtrBuf[], IatrBuf[], TrBuf[];
int smaHandle = INVALID_HANDLE;
int emaHandle = INVALID_HANDLE;
int rsiHandle = INVALID_HANDLE;
int iatrHandle = INVALID_HANDLE;
int OnInit()
{
if(InpLen < 1)
return INIT_PARAMETERS_INCORRECT;
SetIndexBuffer(0, RsiBuf, INDICATOR_DATA);
SetIndexBuffer(1, SmaBuf, INDICATOR_DATA);
SetIndexBuffer(2, EmaBuf, INDICATOR_DATA);
SetIndexBuffer(3, AtrBuf, INDICATOR_DATA);
SetIndexBuffer(4, IatrBuf, INDICATOR_DATA);
SetIndexBuffer(5, TrBuf, INDICATOR_CALCULATIONS); // true range, not shown
for(int p = 0; p < 5; p++)
PlotIndexSetDouble(p, PLOT_EMPTY_VALUE, EMPTY_VALUE);
IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1);
IndicatorSetString(INDICATOR_SHORTNAME, StringFormat("SMA EMA RSI ATR (%d)", InpLen));
smaHandle = iMA(_Symbol, _Period, InpLen, 0, MODE_SMA, PRICE_CLOSE); // ta.sma(close, len)
emaHandle = iMA(_Symbol, _Period, InpLen, 0, MODE_EMA, PRICE_CLOSE); // ta.ema(close, len)
rsiHandle = iRSI(_Symbol, _Period, InpLen, PRICE_CLOSE); // ta.rsi(close, len)
iatrHandle = iATR(_Symbol, _Period, InpLen); // NOT ta.atr: simple average of TR
if(smaHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE || rsiHandle == INVALID_HANDLE || iatrHandle == INVALID_HANDLE)
{
Print("Could not create an indicator handle, error ", GetLastError());
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
IndicatorRelease(smaHandle);
IndicatorRelease(emaHandle);
IndicatorRelease(rsiHandle);
IndicatorRelease(iatrHandle);
}
// Copies the last `count` values of a handle's first buffer into dst[start..], oldest first
bool CopyTo(const int handle, double &dst[], const int start, const int count)
{
double tmp[];
if(CopyBuffer(handle, 0, 0, count, tmp) != count)
return false;
for(int k = 0; k < count; k++)
dst[start + k] = tmp[k];
return true;
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(rates_total < InpLen + 1)
return 0;
if(BarsCalculated(smaHandle) < rates_total || BarsCalculated(emaHandle) < rates_total ||
BarsCalculated(rsiHandle) < rates_total || BarsCalculated(iatrHandle) < rates_total)
return prev_calculated;
int start = prev_calculated > 0 ? prev_calculated - 1 : 0;
int count = rates_total - start;
if(!CopyTo(smaHandle, SmaBuf, start, count) || !CopyTo(emaHandle, EmaBuf, start, count) ||
!CopyTo(rsiHandle, RsiBuf, start, count) || !CopyTo(iatrHandle, IatrBuf, start, count))
return prev_calculated;
// ta.atr(len) = ta.rma(ta.tr(true), len): Wilder's smoothing, seeded with a simple average
for(int i = start; i < rates_total; i++)
{
TrBuf[i] = i == 0 ? high[i] - low[i] : MathMax(high[i], close[i - 1]) - MathMin(low[i], close[i - 1]);
if(i < InpLen - 1)
AtrBuf[i] = EMPTY_VALUE; // na in Pine
else if(i == InpLen - 1)
{
double sum = 0.0;
for(int j = 0; j < InpLen; j++)
sum += TrBuf[j];
AtrBuf[i] = sum / InpLen;
}
else
AtrBuf[i] = (AtrBuf[i - 1] * (InpLen - 1) + TrBuf[i]) / InpLen;
}
return rates_total;
}
//+------------------------------------------------------------------+
Line by line
sma = ta.sma(close, len)smaHandle = iMA(_Symbol, _Period, InpLen, 0, MODE_SMA, PRICE_CLOSE);Arguments: symbol, timeframe, period, shift, method, price. The handle is a number that refers to an indicator the terminal keeps calculating.
ema = ta.ema(close, len)emaHandle = iMA(_Symbol, _Period, InpLen, 0, MODE_EMA, PRICE_CLOSE);Same weight, 2 / (len + 1). Only the start of the series is seeded differently.
rsi = ta.rsi(close, len)rsiHandle = iRSI(_Symbol, _Period, InpLen, PRICE_CLOSE);Wilder's RSI on both platforms.
// the value on each bardouble tmp[];
CopyBuffer(handle, 0, 0, count, tmp);
dst[start + k] = tmp[k];Buffer 0 is the line of a single-line indicator. Copied into a plain array, the values come oldest first, so tmp[k] belongs to bar start + k.
atr = ta.atr(len)TrBuf[i] = MathMax(high[i], close[i - 1]) - MathMin(low[i], close[i - 1]);
...
AtrBuf[i] = (AtrBuf[i - 1] * (InpLen - 1) + TrBuf[i]) / InpLen;ta.atr() is ta.rma() of the true range: the first value is the simple average of the first len true ranges, then each bar takes (previous × (len − 1) + TR) / len. MT5's iATR() keeps a simple average of the last len true ranges instead, so it is also shown for comparison.
plot(sma, "SMA", color.gray, display = display.data_window)#property indicator_type2 DRAW_NONEA DRAW_NONE plot draws nothing but shows its value in the Data Window: the easiest way to compare numbers with TradingView.
hline(70, "Overbought", color.gray)#property indicator_level2 70Fixed horizontal levels of an indicator window are properties.
Pitfalls
iATR()is another ATR. After a volatility spike, the simple average drops the spike all at oncelenbars later while Wilder's fades it slowly, so the two can differ by a lot exactly when stops matter. A stop at 2 × ATR is then a different distance.- Handles created on every tick. Calling
iMA()inOnTick()orOnCalculate()makes the terminal look up or build an indicator each time: slow, and in the Strategy Tester a common reason an EA crawls. Create handles inOnInit()and release them inOnDeinit(); a failed creation is error 4802. - Values not ready. Right after creation,
BarsCalculated()is below the number of bars andCopyBuffer()fails (error 4806). In an indicator, return and wait for the next tick; in an EA, skip this tick. - Buffer numbers. Indicators with several lines number their buffers from 0:
iMACD()buffer 1 is the signal line (a simple average, where Pine uses an EMA) and there is no histogram buffer;iBands()hasBASE_LINE,UPPER_BANDandLOWER_BAND. - The first EMA values. They depend on how each platform starts the average. After about ten times the period the difference no longer shows in the displayed digits.
Checking that both give the same values
- Add both indicators with the same length to the same symbol and timeframe.
- Pick a bar far from the start of the history and read SMA, EMA, RSI and ATR in TradingView's Data Window and in MetaTrader 5's (Ctrl+D), on the same bar time.
- SMA and RSI should agree to the displayed digits when the closes agree; the EMA too, away from the start. "ATR (Wilder, as ta.atr)" should match TradingView's ATR; "ATR (MT5 iATR)" usually won't.
- If even the SMA differs, compare the closes of the last
lenbars: the feeds differ, not the code.
Reading one value in an Expert Advisor
In an Expert Advisor you rarely need whole buffers. Create the handle in OnInit(), then read the last closed bar with a series array: double v[]; ArraySetAsSeries(v, true); if(CopyBuffer(rsiHandle, 0, 1, 1, v) == 1) ..., where v[0] is Pine's rsi at the close of the previous bar. For Pine's ATR in an EA, compute the Wilder average from CopyRates(), as WilderAtr() does in the worked port.
These programs show one technique each; they are not trading systems, and none of this 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.