How Pine Script history maps to MQL arrays
Why close[1] means different things in each language, and how to index bars in MQL5 without off-by-one bugs.
The core difference
In Pine Script every variable is a series. Your script runs once per bar from left to right, and close[1] always means "the close one bar before the bar being calculated". You never manage arrays yourself.
In MQL5 an indicator receives whole arrays in OnCalculate. By default index 0 is the oldest bar on the chart, not the newest. Most conversion bugs start here: code that reads close[1] in MQL5 without changing the indexing reads the second-oldest bar in history.
Option 1: series indexing (newest bar = 0)
Calling ArraySetAsSeries(array, true) flips the indexing so 0 is the current bar, which matches Pine. Then loop from older bars toward 0 and only recalculate what changed since the last call, using prev_calculated.
Pine Script
//@version=5
indicator("Close change")
change = close - close[1]
plot(change)MQL5
double Buf[];
int OnInit(){ SetIndexBuffer(0, Buf, INDICATOR_DATA); return INIT_SUCCEEDED; }
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[])
{
ArraySetAsSeries(close, true);
ArraySetAsSeries(Buf, true);
int limit = (prev_calculated == 0) ? rates_total - 2 : rates_total - prev_calculated;
for(int i = limit; i >= 0; i--)
Buf[i] = close[i] - close[i + 1]; // i+1 is the previous (older) bar
return rates_total;
}Checklist
Set series indexing on every array you read, including your own buffers. Leave enough room for the lookback: if you read close[i + 1], start at rates_total - 2, not rates_total - 1. When prev_calculated is 0 the chart is new, so calculate everything once; after that, only the newest bars.