What each Pine construct becomes in MQL5
The planner's whole map: 92 rows, each linked to the MetaQuotes and TradingView pages it was read on, 1 November 2026.
What the script is (5)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
indicator() | A custom indicator (.mq5): #property indicator_chart_window when overlay = true, indicator_separate_window otherwise File header (#property). Its plots become indicator buffers; its calculation moves into OnCalculate. MQL5 Reference: Program Properties (#property), MQL5 Reference: Event Handling Functions | Direct |
strategy() | An Expert Advisor (.mq5): OnInit, OnTick, and CTrade for orders OnInit + OnTick. A strategy decides on closed bars by default and fills at the next bar's open; an EA runs OnTick on each tick it receives. Gate the logic on a new bar (first trap). MQL5 Reference: OnTick, MQL5 Reference: CTrade | Needs care |
library() | An include file (.mqh) pulled in with #include, or a compiled module called through #import Separate file. Port the library's exported functions first; every script that imports it depends on them. MQL5 Reference: Importing Functions (#import) | Needs care |
import user/library/1 | The library's MQL5 port, included with #include File header. MQL5 can't load a Pine library: its source has to be ported too, or the calls rebuilt in place. MQL5 Reference: Importing Functions (#import) | No direct equivalent: rebuild |
type MyType | A struct (data only) or a class (data and methods) Global scope. Pine objects are references; copy MQL5 structs by value or use a class with care. MQL5 Reference: Structures, Classes and Interfaces | Needs care |
Inputs (9)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
input.int(), input.float(), input.price(), input() | input int / input double Global scope. MQL5 inputs are read-only inside the program; minval and maxval become checks in OnInit (return INIT_PARAMETERS_INCORRECT). MQL5 Reference: Input Variables | Direct |
input.bool() | input bool Global scope. One to one. MQL5 Reference: Input Variables | Direct |
input.string(), input.text_area(), input.symbol() | input string, or an enum input where Pine lists options Global scope. An MQL5 string input's value can be at most 191 to 253 characters long, depending on the name's length; a list of options reads better as an enum. MQL5 Reference: Input Variables | Needs care |
input.color() | input color Global scope. Pine's colour carries transparency; MQL5's input color doesn't, so set it on the plot or object. MQL5 Reference: Input Variables | Direct |
input.timeframe() | input ENUM_TIMEFRAMES (PERIOD_H1, PERIOD_D1 …) Global scope. Pine returns a string such as "60" or "D"; MQL5 functions take an ENUM_TIMEFRAMES value, where 0 means the chart's own. MQL5 Reference: Input Variables, MQL5 Reference: iMA | Needs care |
input.session() | Two inputs (start and end, e.g. minutes after midnight) compared with bar times Global scope. Pine reads the session in the exchange's time zone; an MT5 chart is on the server's clock (see the sessions trap). MQL5 Reference: Input Variables, MQL5 Book: Local and server time | Needs care |
input.source() | input ENUM_APPLIED_PRICE, passed as applied_price Global scope. Pine accepts any series, even another script's plot; applied_price takes a price constant or another indicator's handle. MQL5 Reference: iMA | Needs care |
input.time() | input datetime Global scope. Pine's value is in milliseconds; MQL5's datetime is in seconds. MQL5 Reference: Input Variables, MQL5 Reference: TimeToStruct | Direct |
input.enum() | enum MyChoice { … }; input MyChoice Global scope. One to one. MQL5 Reference: Input Variables | Direct |
Indicators (ta.*) (31)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
ta.sma(source, length) | iMA(symbol, period, length, 0, MODE_SMA, applied_price), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Create the handle once and read it with CopyBuffer: shift 0 is the bar still forming, shift 1 the last closed bar. MQL5 Reference: iMA, MQL5 Reference: Technical Indicator Functions | Direct |
ta.ema(source, length) | iMA(symbol, period, length, 0, MODE_EMA, applied_price), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Same recursion on both sides. TradingView documents its first value (the source itself); MetaTrader's help doesn't state its seed, so compare values after a warm-up of several lengths. MQL5 Reference: iMA, MetaTrader 5 help: Moving Average, MQL5 Reference: Technical Indicator Functions | Direct |
ta.rma(source, length) | iMA(symbol, period, length, 0, MODE_SMMA, applied_price), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Both docs give the same recursion (weight 1 / length) seeded with a simple average. MQL5 Reference: iMA, MetaTrader 5 help: Moving Average | Direct |
ta.wma(source, length) | iMA(symbol, period, length, 0, MODE_LWMA, applied_price), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Both weight the bars in arithmetical progression, newest heaviest. MQL5 Reference: iMA, MetaTrader 5 help: Moving Average | Direct |
ta.rsi(source, length) | iRSI(symbol, period, length, applied_price), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Pine smooths the up and down moves with ta.rma(); MetaTrader's help gives RSI = 100 - 100 / (1 + U / D) without naming the average. Compare a few bars before trusting a level. MQL5 Reference: iRSI, MetaTrader 5 help: Relative Strength Index | Needs care |
ta.atr(length) | An RMA of true range computed in code (weight 1 / length, as ta.rma); iATR only where the Pine uses ta.sma(ta.tr, length) OnCalculate or OnTick. iATR won't match ta.atr. TradingView's ATR is an RMA of true range; MetaQuotes' own ATR.mq5 is a sliding simple average of true range, so it matches ta.sma(ta.tr, length) instead. A stop at k × ATR lands elsewhere unless the port computes the RMA itself. MetaQuotes' ATR.mq5 (MQL5\Indicators\Examples), Copyright 2000-2025 MetaQuotes Ltd., MetaTrader 5 help: Average True Range, MQL5 Reference: iATR | No direct equivalent: rebuild |
ta.macd(source, fast, slow, signal) | iMACD(symbol, period, fast, slow, signal, applied_price): buffer 0 MAIN_LINE, 1 SIGNAL_LINE Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). iMACD has no histogram buffer: take main minus signal. MetaTrader's help gives SIGNAL = SMA(MACD, 9), and TradingView's reference doesn't name its signal average, so compare the signal line. MQL5 Reference: iMACD, MetaTrader 5 help: MACD | Needs care |
ta.bb(series, length, mult) | iBands(symbol, period, length, 0, mult, applied_price): 0 BASE_LINE, 1 UPPER_BAND, 2 LOWER_BAND Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Pine returns [middle, upper, lower]; iBands numbers its buffers base, upper, lower. MetaTrader's deviation divides by N; compare one band on a few bars. MQL5 Reference: iBands, MetaTrader 5 help: Bollinger Bands | Needs care |
ta.stoch(source, high, low, length) | iStochastic(symbol, period, Kperiod, Dperiod, slowing, ma_method, price_field): 0 MAIN_LINE, 1 SIGNAL_LINE Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Pine's ta.stoch is the raw %K, which a script smooths itself; iStochastic smooths %K by `slowing` (1 is the raw, fast one) and adds %D. MQL5 Reference: iStochastic, MetaTrader 5 help: Stochastic Oscillator | Needs care |
ta.cci(source, length) | iCCI(symbol, period, length, applied_price), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). MetaTrader's help builds CCI on the typical price (high + low + close) / 3; Pine takes the source you pass. Feed both the same price. MQL5 Reference: iCCI, MetaTrader 5 help: Commodity Channel Index | Needs care |
ta.mfi(series, length) | iMFI(symbol, period, length, applied_volume), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). iMFI asks which volume to use (ENUM_APPLIED_VOLUME); match the volume TradingView shows for the symbol. MQL5 Reference: iMFI | Needs care |
ta.dmi(diLength, adxSmoothing) | iADXWilder(symbol, period, length): 0 MAIN_LINE (ADX), 1 PLUSDI_LINE, 2 MINUSDI_LINE Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Pine returns [+DI, -DI, ADX], a different order, and takes two lengths where iADXWilder and iADX take one. Compare both MQL5 versions against the Pine values. MQL5 Reference: iADXWilder, MQL5 Reference: iADX | Needs care |
ta.sar(start, inc, max) | iSAR(symbol, period, step, maximum), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Pine takes a start and an increment; iSAR takes one step. It maps directly only when start equals the increment. MQL5 Reference: iSAR | Needs care |
ta.mom(source, length) | Compute source[shift] - source[shift + length] (iMomentum is a different formula) OnCalculate or OnTick. Pine's momentum is a difference; MetaTrader's Momentum is CLOSE(i) / CLOSE(i - n) × 100, a ratio. Don't reuse its levels. MQL5 Reference: iMomentum, MetaTrader 5 help: Momentum | Needs care |
ta.stdev(source, length, biased) | iStdDev(symbol, period, length, 0, MODE_SMA, applied_price), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Pine's `biased` (default true) picks the population or the sample estimate; check which one the MQL5 value matches. MQL5 Reference: iStdDev | Needs care |
ta.wpr(length) | iWPR(symbol, period, length), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). Create the handle once and read it with CopyBuffer: shift 0 is the bar still forming, shift 1 the last closed bar. MQL5 Reference: iWPR | Direct |
ta.obv | iOBV(symbol, period, applied_volume), then CopyBuffer Handle in OnInit; CopyBuffer in OnCalculate (indicator) or OnTick (EA). OBV sums from the first bar each platform has, so its level differs; compare its changes, not its value. MQL5 Reference: iOBV | Needs care |
ta.highest(source, length) | On a price: iHighest(symbol, period, MODE_HIGH, length, start), then iHigh at that shift. On a computed series (an RSI): ArrayMaximum on its buffer OnCalculate or OnTick. iHighest searches a timeseries (ENUM_SERIESMODE: high, low, close …), not your own values. start = 0 includes the bar still forming, as Pine does on the live bar; start = 1 reads closed bars only. MQL5 Reference: iHighest, MQL5 Reference: ArrayMaximum | Needs care |
ta.lowest(source, length) | On a price: iLowest(symbol, period, MODE_LOW, length, start), then iLow at that shift. On a computed series: ArrayMinimum on its buffer OnCalculate or OnTick. iLowest searches a timeseries (ENUM_SERIESMODE), not your own values. start = 0 includes the bar still forming; start = 1 reads closed bars only. MQL5 Reference: iLowest, MQL5 Reference: iHighest, MQL5 Reference: ArrayMaximum | Needs care |
ta.crossover(a, b), ta.crossunder(a, b), ta.cross(a, b) | Two comparisons on two shifts: a[1] > b[1] && a[2] <= b[2] for the bar that just closed OnCalculate or OnTick. Which shifts you compare decides whether the signal can vanish: a cross read on shift 0 and 1 can appear and disappear while the bar forms. MQL5 Reference: CopyBuffer, MQL5 Reference: Indexing Direction in Arrays, Buffers and Timeseries | Needs care |
ta.change(source, length) | value[shift] - value[shift + length] OnCalculate or OnTick. A subtraction on two shifts. MQL5 Reference: CopyBuffer | Direct |
ta.tr | MathMax(high - low, MathMax(MathAbs(high - prevClose), MathAbs(low - prevClose))) OnCalculate or OnTick. The same formula. On the first bar, where there is no previous close, the variable ta.tr is na; ta.tr(true) (and ta.atr) use high - low there. MQL5 Reference: MathMax | Direct |
ta.roc(source, length) | 100 * (value[shift] - value[shift + length]) / value[shift + length] OnCalculate or OnTick. Arithmetic on two shifts. MQL5 Reference: CopyBuffer | Direct |
ta.pivothigh(), ta.pivotlow() | A loop over leftbars and rightbars around a candidate bar OnCalculate or OnTick. A pivot is only known rightbars bars after it; Pine's own example plots it with offset = -rightbars, back in time. Port the delay, not the drawing. MQL5 Reference: iHighest | No direct equivalent: rebuild |
ta.vwap | A running sum of price × volume over volume, reset when the day changes OnCalculate or OnTick. Pine resets on a new day by default, in the exchange's time zone; an MT5 day is a server-clock day (see the sessions trap). MQL5 Book: Local and server time | No direct equivalent: rebuild |
ta.hma(source, length) | Weighted averages in code (or two iMA LWMA handles, then a weighted average of their difference) OnCalculate or OnTick. MQL5's indicator list has no Hull average; rebuild it as HMA = WMA(2 × WMA(n/2) − WMA(n), √n) and compare. MQL5 Reference: iMA, MetaTrader 5 help: Moving Average | No direct equivalent: rebuild |
ta.vwma(source, length) | sum(price × volume) / sum(volume) over length bars OnCalculate or OnTick. TradingView defines it as sma(source × volume, length) / sma(volume, length); use the same volume on both sides. MQL5 Reference: CopyRates | No direct equivalent: rebuild |
ta.supertrend(factor, atrPeriod) | A state machine on ATR bands, with its ATR computed as Pine's RMA of true range OnCalculate or OnTick. It inherits the ATR difference (ta.atr row) and flips on closes: port it on closed bars. MQL5 Reference: iATR, MetaTrader 5 help: Average True Range | No direct equivalent: rebuild |
ta.valuewhen(), ta.barssince() | Globals updated once per closed bar: the last value and bar time when the condition was true Global scope + OnCalculate or OnTick. Keep the state yourself, and update it only on a new bar (tick-counters trap). MQL5 Reference: Global Variables | No direct equivalent: rebuild |
ta.cum(source) | A running total in a global, added once per closed bar Global scope + OnCalculate or OnTick. It starts from the first bar each platform loads, so the level differs between them. MQL5 Reference: Global Variables | Needs care |
Another ta.* function | Its formula, in code, unless MQL5's indicator list has a built-in with the same definition OnCalculate or OnTick. Rebuild it from TradingView's definition and compare values bar by bar. MQL5 Reference: Technical Indicator Functions | No direct equivalent: rebuild |
Other timeframes (5)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
request.security(symbol, timeframe, expression) | The same handle on that timeframe (e.g. iMA(_Symbol, PERIOD_D1, …)) or iClose/iHigh/iLow(_Symbol, PERIOD_D1, shift) Handles in OnInit; reads in OnCalculate or OnTick. Shift 0 is the higher-timeframe bar still forming. For Pine's [1] with lookahead_on, take the higher-timeframe bar before the one that holds the chart bar you decide on: iBarShift(_Symbol, tf, that bar's time) + 1 (see the lookahead trap). MQL5 Reference: Indexing Direction in Arrays, Buffers and Timeseries, MQL5 Reference: iBarShift, MQL5 Reference: CopyBuffer | Needs care |
request.security_lower_tf() | CopyRates on the lower timeframe for the chart bar's time range OnCalculate or OnTick. Pine hands you an array per chart bar; MQL5 makes you fetch the lower bars and group them by time. MQL5 Reference: CopyRates, MQL5 Reference: iBarShift | No direct equivalent: rebuild |
request.currency_rate() | The close of your broker's own currency-pair symbol, e.g. iClose(pair, PERIOD_D1, 1) OnCalculate or OnTick. Pine's is a daily rate from TradingView's data; in MT5 the rate comes from your broker's quotes, under the symbol name the broker uses. MQL5 Reference: iClose, MQL5 Reference: Organizing Data Access | Needs care |
request.economic() | MQL5's Economic Calendar functions (CalendarValueHistory and the rest), by country or currency OnInit or OnTimer. Pine requests an economic series for a country (GDP, inflation rate); the MQL5 calendar returns event values. Map the series to the calendar's events and check that the values agree. MQL5 Reference: Economic Calendar | No direct equivalent: rebuild |
request.financial(), request.earnings(), request.dividends() … | Another source for the data, or drop the condition —. These are TradingView's data sets; MetaTrader's prices come from your broker's trade server. MQL5 Reference: Organizing Data Access | No direct equivalent: rebuild |
Plots and colours (8)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
plot() | An indicator buffer: double Buf[]; SetIndexBuffer in OnInit; #property indicator_typeN DRAW_LINE Global buffer + OnInit + OnCalculate. Count #property indicator_buffers and indicator_plots as you go (buffers trap). MQL5 Reference: SetIndexBuffer, MQL5 Reference: Program Properties (#property) | Direct |
plot() in a strategy | Chart objects from the EA, or the plots in a separate indicator attached with iCustom + ChartIndicatorAdd OnInit (attach) or OnTick (objects). OnCalculate runs only in custom indicators, so an EA has no buffers to draw with. MQL5 Reference: Event Handling Functions, MQL5 Reference: iCustom, MQL5 Reference: ChartIndicatorAdd | Needs care |
plotshape(), plotchar(), plotarrow() | A DRAW_ARROW buffer (PLOT_ARROW picks the symbol); EMPTY_VALUE where nothing shows Global buffer + OnInit + OnCalculate. Write EMPTY_VALUE on bars without a shape, or every bar gets one. MQL5 Reference: Drawing Styles | Needs care |
plotcandle(), plotbar() | DRAW_CANDLES or DRAW_BARS: 4 buffers (open, high, low, close) Global buffers + OnInit + OnCalculate. Four buffers per call, five with a colour per bar. MQL5 Reference: Drawing Styles | Needs care |
hline() | #property indicator_levelN in a separate window, or an OBJ_HLINE object File header, or OnInit. A fixed level needs no buffer. MQL5 Reference: Program Properties (#property), MQL5 Reference: Object Types | Direct |
fill() | DRAW_FILLING: two buffers, the area between them filled Global buffers + OnInit + OnCalculate. Two more buffers on top of the plots it fills between. MQL5 Reference: Drawing Styles | Needs care |
bgcolor() | Rectangles (OBJ_RECTANGLE) spanning the bars to shade OnCalculate or OnTick; delete in OnDeinit. None of MQL5's 18 drawing styles colours a bar's background: draw objects and clean them up. MQL5 Reference: Drawing Styles, MQL5 Reference: ObjectCreate, MQL5 Reference: ObjectDelete | No direct equivalent: rebuild |
barcolor() | DRAW_COLOR_CANDLES: 4 data buffers and 1 colour buffer, drawn over the chart's own candles Global buffers + OnInit + OnCalculate. MQL5 recolours by drawing candles of its own. MQL5 Reference: Drawing Styles | Needs care |
Drawings (5)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
line.new() | ObjectCreate(chart_id, name, OBJ_TREND, 0, time1, price1, time2, price2) OnCalculate or OnTick; ObjectDelete in OnDeinit. Name every object; calling ObjectCreate again on the same name moves it. Pine keeps a limited number of drawings; MQL5 keeps what you don't delete. MQL5 Reference: ObjectCreate, MQL5 Reference: Object Types, MQL5 Reference: ObjectDelete | Needs care |
label.new() | OBJ_TEXT (anchored to a time and price) or OBJ_LABEL (fixed on the screen) OnCalculate or OnTick; ObjectDelete in OnDeinit. Same naming and clean-up as lines. MQL5 Reference: ObjectCreate, MQL5 Reference: Object Types, MQL5 Reference: ObjectDelete | Needs care |
box.new() | OBJ_RECTANGLE with two anchor points OnCalculate or OnTick; ObjectDelete in OnDeinit. Same naming and clean-up as lines. MQL5 Reference: ObjectCreate, MQL5 Reference: Object Types, MQL5 Reference: ObjectDelete | Needs care |
table.new() | OBJ_RECTANGLE_LABEL and OBJ_LABEL objects laid out by hand, or Comment() for plain text OnInit + OnCalculate or OnTick. There's no table object: position each cell yourself. MQL5 Reference: Object Types, MQL5 Reference: Comment | No direct equivalent: rebuild |
polyline.new(), linefill.new() | Segments of OBJ_TREND, or a filled shape built from objects OnCalculate or OnTick; ObjectDelete in OnDeinit. Draw it piece by piece. MQL5 Reference: ObjectCreate, MQL5 Reference: Object Types | No direct equivalent: rebuild |
Alerts (2)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
alertcondition() | Alert() or SendNotification() called by your code when the condition is true OnCalculate or OnTick, once per closed bar. Pine only offers the condition to TradingView's alert dialog; in MQL5 the code sends the alert. SendNotification allows 2 calls a second and 10 a minute, and neither works in the Strategy Tester. MQL5 Reference: Alert, MQL5 Reference: SendNotification | Needs care |
alert() | Alert(), SendNotification(), or WebRequest() for a webhook (EA or script only) OnTick (EA), once per closed bar. alert() defaults to the first call on each realtime bar, which can fire mid-bar. WebRequest needs the URL allowed in the terminal's options and doesn't run in the Strategy Tester. MQL5 Reference: Alert, MQL5 Reference: SendNotification, MQL5 Reference: WebRequest | Needs care |
Orders and positions (7)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
strategy.entry() | CTrade: trade.Buy(volume, symbol, price, sl, tp) / trade.Sell(…); check ResultRetcode() OnTick, after the new-bar check. An opposite entry reverses a Pine position. In MT5 an opposite deal of the same volume only closes the position on a netting account, and opens a second one on a hedging account: close the other side first, then enter. Pine sizes in contracts or units, MT5 in lots. MQL5 Reference: CTrade::Buy, MQL5 Reference: CTrade::Sell, MetaTrader 5 help: Position accounting | Needs care |
strategy.exit() | sl and tp on trade.Buy/Sell, or trade.PositionModify(symbol, sl, tp) later OnTick. Pine's emulator fills exits inside a bar by its own price-path rule; MT5 fills SL and TP on ticks, real or generated. MQL5 Reference: CTrade::PositionModify, MetaTrader 5 help: Strategy Tester | Needs care |
strategy.close(), strategy.close_all() | trade.PositionClose(symbol) or PositionClose(ticket) OnTick. strategy.close closes the entries with that ID, and like other market orders fills at the next bar's open by default; PositionClose sends its order when the EA calls it. On a hedging account close each position by ticket. MQL5 Reference: CTrade::PositionClose, MetaTrader 5 help: Position accounting | Needs care |
strategy.order() | trade.Buy/Sell, or pending orders (trade.BuyLimit, BuyStop …) OnTick. strategy.order doesn't reverse a position automatically, as strategy.entry does; it adds to or reduces the position by its size. In MT5 a netting account nets an opposite order against the position, and a hedging account opens a second position. MQL5 Reference: CTrade::BuyLimit, MetaTrader 5 help: Position accounting | Needs care |
strategy.cancel(), strategy.cancel_all() | trade.OrderDelete(ticket) OnTick. Keep the pending order's ticket when you place it. MQL5 Reference: CTrade::OrderDelete | Direct |
strategy.position_size, strategy.position_avg_price … | PositionSelect(symbol), then PositionGetDouble(POSITION_VOLUME / POSITION_PRICE_OPEN) OnTick. On a hedging account PositionSelect picks the position with the lowest ticket: loop over positions and filter by your magic number. MQL5 Reference: PositionSelect, MQL5 Reference: PositionGetDouble | Needs care |
strategy.risk.*() | Your own checks before each order OnTick. MQL5 has no strategy-level risk rules; write them. MQL5 Reference: CTrade | No direct equivalent: rebuild |
State across bars (2)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
var | A global variable, or a static local Global scope. Pine drops changes a realtime bar makes before its close; MQL5 keeps every change. Update it once per closed bar (tick-counters trap). MQL5 Reference: Global Variables, MQL5 Reference: Static Variables | Needs care |
varip | A plain global variable Global scope. varip keeps every tick's change, which is what an MQL5 global does. MQL5 Reference: Global Variables | Direct |
Arrays, maps and matrices (3)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
array.new<…>(), array.* | A dynamic array sized with ArrayResize Global scope (if it persists) + where it's used. Mind the direction: ArraySetAsSeries flips indexing, and Pine's array.get(id, -1) (the last element) has no MQL5 shorthand. MQL5 Reference: ArrayResize, MQL5 Reference: ArraySetAsSeries | Needs care |
map.new<…>(), map.* | CHashMap<TKey,TValue> from #include <Generic\HashMap.mqh> Global scope + where it's used. Pine keeps a map's insertion order: map.keys() and map.values() return keys and values in the order they were put. CHashMap stores unordered pairs, so a port that walks the keys must keep its own order, for example a parallel array of keys. MQL5 Reference: CHashMap<TKey,TValue> | Needs care |
matrix.new<…>(), matrix.* | matrix (of double), or a two-dimensional array for other types Wherever it's used. An MQL5 matrix holds doubles; a Pine matrix can hold any one type. MQL5 Reference: Matrices and vectors | Needs care |
Time and sessions (7)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
time(timeframe, session, timezone), time_close() | TimeToStruct(iTime(_Symbol, _Period, shift) + your offset) and a minutes-after-midnight test OnCalculate or OnTick. Pine reads the session in the zone you pass, or the exchange's; MT5 bar times are server time. The offset changes when the two clocks change on different dates. MQL5 Book: Local and server time, MQL5 Reference: TimeToStruct, MQL5 Reference: iTime | Needs care |
hour, minute, dayofweek, dayofmonth, month, year | TimeToStruct(iTime(_Symbol, _Period, shift), t): t.hour, t.min, t.day_of_week … OnCalculate or OnTick. Pine's are in the exchange's time zone, MT5's in the server's. MQL5 Reference: TimeToStruct, MQL5 Book: Local and server time | Needs care |
timestamp() | A datetime on the server clock (seconds, not milliseconds) Wherever it's used. Convert the zone and the unit. MQL5 Reference: TimeToStruct, MQL5 Book: Local and server time | Needs care |
timenow | TimeCurrent() (last quote's server time) or TimeTradeServer() (computed) Wherever it's used. Pine's timenow is milliseconds since 1970 in UTC; TimeCurrent() is the server's time in seconds (a datetime). In the Strategy Tester TimeGMT() equals the simulated server time. MQL5 Reference: TimeCurrent, MQL5 Reference: TimeToStruct, MQL5 Reference: TimeTradeServer, MQL5 Reference: TimeGMT | Needs care |
session.ismarket, session.isfirstbar … | SymbolInfoSessionTrade for the symbol's sessions, compared with bar times OnInit + OnCalculate or OnTick. Pine knows the exchange's regular hours; in MT5 you read the broker's trading sessions per weekday. MQL5 Reference: SymbolInfoSessionTrade | No direct equivalent: rebuild |
syminfo.timezone | Your measured offset between the server clock and the exchange's Input. TimeGMT() comes from your computer's clock, and in the Strategy Tester it equals the simulated server time, so it can't give you the offset there. MQL5 Reference: TimeGMT, MQL5 Book: Local and server time | No direct equivalent: rebuild |
timeframe.change(timeframe) | A new bar on that timeframe: iTime(_Symbol, PERIOD_D1, 0) differs from the last one seen OnCalculate or OnTick. A D1 bar in MT5 opens at the server's midnight. MQL5 Reference: iTime, MQL5 Book: Local and server time | Needs care |
Bar state (4)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
barstate.isconfirmed | A new-bar check (iTime at shift 0 changed), then read shift 1 OnTick (EA) or the closed bars in OnCalculate. An EA can't know which tick is a bar's last, so it acts on the next bar's first tick. MQL5 Reference: iTime, MQL5 Reference: Indexing Direction in Arrays, Buffers and Timeseries | Needs care |
barstate.isnew | The same new-bar check OnTick or OnCalculate. iTime at shift 0 differs from the last value you stored. MQL5 Reference: iTime | Direct |
barstate.islast, barstate.isrealtime, barstate.ishistory | In OnCalculate, the bar at index rates_total - 1 (left to right) is the one forming; an EA only sees live ticks (or the tester's) OnCalculate or OnTick. History versus live isn't a flag in MQL5: decide from the bar's index. MQL5 Reference: Indexing Direction in Arrays, Buffers and Timeseries, MQL5 Reference: OnTick | Needs care |
barstate.isfirst | prev_calculated == 0 in OnCalculate, or OnInit for one-time setup OnCalculate or OnInit. prev_calculated also drops to 0 when the terminal reloads history, so a first-bar block can run again. MQL5 Reference: OnCalculate | Needs care |
Missing values (1)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
na, na(), nz(), fixnan() | EMPTY_VALUE in buffers (PLOT_EMPTY_VALUE for drawing), checked before any comparison Wherever a value may be missing. Pine's comparisons with na are false; EMPTY_VALUE is DBL_MAX, a number above every price, and an indicator can set its own empty value instead. MQL5 Reference: Other Constants | Needs care |
History and bar numbers (3)
| Pine | MQL5, and where it goes | How close |
|---|---|---|
x[n] (the history operator) | An array read at shift n after ArraySetAsSeries(arr, true), or buf[i - n] in OnCalculate's left-to-right buffers OnCalculate or OnTick. Pine keeps each series' past for you; in MQL5 you copy it (CopyBuffer, CopyRates) and pick the direction. MQL5 Reference: CopyBuffer, MQL5 Reference: ArraySetAsSeries | Needs care |
bar_index | The loop index i in OnCalculate's left-to-right arrays; rates_total - 1 - shift in timeseries order OnCalculate. Both count from the oldest loaded bar, and each platform loads a different amount of history. MQL5 Reference: Indexing Direction in Arrays, Buffers and Timeseries | Needs care |
math.max(), math.min(), math.abs() … | MathMax, MathMin, MathAbs … Wherever it's used. One to one for two arguments; Pine's take more. MQL5 Reference: MathMax | Direct |
The nine traps it checks
The planner names a trap only when your script has what causes it, and says why the others don't apply.
Reading shift 0 instead of the bar that just closed
A Pine strategy calculates after a bar closes by default, and barstate.isconfirmed is true only on a bar's closing update, so both decide once per closed bar. (An indicator without barstate.isconfirmed recalculates on every realtime update, and its live signals can come and go too.) An EA's OnTick runs on every tick, and shift 0 is the bar still forming, so a signal read there can appear mid-bar, send its order, and be gone by the close.
The fix: Check for a new bar (iTime at shift 0 changed), then read shift 1 and 2 for a cross. You act on the next bar's first tick, the closest an EA gets to Pine's fill at the next open.
Sources: Pine User Manual: Strategies: broker emulator, Pine User Manual: Bar states, Pine User Manual: Execution model, MQL5 Reference: OnTick, MQL5 Reference: Indexing Direction in Arrays, Buffers and Timeseries
Indicator handles created in OnTick
In Pine, ta.ema() is a function you call on every bar. In MQL5, iMA() creates an indicator and returns a handle; its values take time to calculate, so a handle made where it's used may have nothing to give yet.
The fix: Create each handle once in OnInit (fail with INIT_FAILED if it's INVALID_HANDLE), read it with CopyBuffer, test the count it returns, and release it in OnDeinit.
Sources: Pine v6 reference: ta.ema(), MQL5 Reference: Technical Indicator Functions, MQL5 Reference: OnInit, MQL5 Reference: IndicatorRelease
Arrays read in the wrong direction (ArraySetAsSeries)
Pine's x[1] always means one bar back. MQL5 arrays index left to right by default, CopyBuffer stores the oldest value first whatever the flag, and a buffer bound with SetIndexBuffer goes back to common indexing. buf[1] can be the second-oldest bar, not the previous one.
The fix: Call ArraySetAsSeries(arr, true) on every array you read as Pine reads a series, after SetIndexBuffer for buffers, and set the direction of OnCalculate's arrays yourself rather than trust the default.
Sources: Pine User Manual: Execution model, MQL5 Reference: Indexing Direction in Arrays, Buffers and Timeseries, MQL5 Reference: CopyBuffer, MQL5 Reference: SetIndexBuffer, MQL5 Reference: OnCalculate
Counters that count ticks, not bars
A Pine var changed on a realtime bar is rolled back until the bar closes, so `count += 1` adds one per bar. An MQL5 global keeps every change: the same line in OnTick adds one per tick it handles, and OnTick doesn't even see every tick.
The fix: Update counters and running state only inside the new-bar block, from the closed bar's values. Pine's varip is the exception: it already behaves like an MQL5 global.
Sources: Pine User Manual: Variable declarations, Pine User Manual: Execution model, MQL5 Reference: Global Variables, MQL5 Reference: OnTick
Sessions on the broker's server clock, not the exchange's
Pine reads a session in the zone the script names, or the exchange's. MT5 charts are stamped in the broker's server time, and in the Strategy Tester TimeGMT() equals the simulated server time, so it can't tell you the offset there. A fixed offset is right only while the server and the exchange change their clocks on the same dates.
The fix: Measure the server's offset from the exchange for a winter and a summer week, convert bar times before the session test, and run the weeks when the two clocks differ.
Sources: Pine User Manual: Sessions, Pine User Manual: Time, MQL5 Book: Local and server time, MQL5 Reference: TimeGMT
request.security: lookahead and higher-timeframe bars
Without lookahead, Pine's history gets each higher-timeframe value at the end of its period, and live bars show the forming value, which repaints on reload. With lookahead_on and no [1], history sees the finished bar early: a future leak that a live EA, or a Strategy Tester run, can't reproduce, because the finished bar doesn't exist yet when they run. Don't port the leak. In MT5, shift 0 on another timeframe is the bar still forming, and a D1 bar runs on the server's day.
The fix: Port the confirmed pattern (expression[1] with lookahead_on) as Pine reads it: find the higher-timeframe bar that holds the chart bar being decided, then take the one before it. In an EA deciding on the bar that just closed, that is iBarShift(_Symbol, tf, iTime(_Symbol, _Period, 1)) + 1; in an indicator drawing past bars, iBarShift(_Symbol, tf, time[i]) + 1. Plain shift 1 on the higher timeframe reads the wrong day on each day's last chart bar. Check iBarShift for -1, the price for 0 (iClose and iHigh return 0 on an error), and CopyBuffer's count (-1 while that history loads). Compare daily highs and lows on both platforms before the signals.
Sources: Pine User Manual: Other timeframes and data: lookahead, Pine v6 reference: request.security(), Pine User Manual: Other timeframes and data, MQL5 Reference: Indexing Direction in Arrays, Buffers and Timeseries, MQL5 Reference: iBarShift, MQL5 Reference: iClose, MQL5 Reference: CopyBuffer
na is not EMPTY_VALUE
In Pine every comparison with na is false: `value > level` is false while value is na (though `not (value > level)` is then true). MQL5 marks a missing buffer value with an empty value: EMPTY_VALUE, which is DBL_MAX, unless the indicator sets its own with PLOT_EMPTY_VALUE (0.0, for example). DBL_MAX compared as a number is above every price, so `value > level` is true.
The fix: Write EMPTY_VALUE where Pine plots na (set PLOT_EMPTY_VALUE), and test the value against the empty value before every comparison on a value that can be missing. When you read another indicator with CopyBuffer, check which empty value it uses.
Sources: Pine User Manual: Type system: na value, MQL5 Reference: Other Constants, Pine User Manual: Other timeframes and data: gaps
Plots, buffers and what an EA can draw
Each MQL5 drawing style needs one to four data buffers, plus a colour buffer for a colour per bar; fill() becomes DRAW_FILLING with two more, and a strategy's plots have nowhere to go, because only custom indicators run OnCalculate.
The fix: Count buffers and plots before you write the header (#property indicator_buffers, indicator_plots); for a strategy, put the plots in a separate indicator and attach it with iCustom and ChartIndicatorAdd, or draw objects.
Sources: Pine User Manual: Limitations: plot counts, MQL5 Reference: Drawing Styles, MQL5 Reference: Technical Indicator Functions, MQL5 Reference: Event Handling Functions
Strategy fills and the tester's price model
Pine's broker emulator fills at the next bar's open by default (at the signal close with process_orders_on_close), guesses the path inside a bar from its open, high, low and close, and reverses a position on an opposite entry. MT5's Strategy Tester fills on ticks, real or generated by the mode you pick. An opposite deal of the same volume only closes the position on a netting account, and opens a second position on a hedging account.
The fix: Expect fills to differ even when the signals agree. Test MT5 on real ticks where the broker has them, and before an opposite entry close the other side first, on either account type, then enter. Compare signals bar by bar before you compare results.
Sources: Pine User Manual: Strategies: broker emulator, Pine User Manual: Strategies: process_orders_on_close, Pine User Manual: Strategies: broker emulator, Pine User Manual: Strategies: reversing positions, MetaTrader 5 help: Strategy Tester, MQL5 Reference: CTrade::Buy, MetaTrader 5 help: Position accounting
What the planner doesn't do
It plans the port and names the traps; the port, the compile and the comparison are yours.
No EA written, nothing compiled, nothing run
- It doesn't write MQL5. The rows name the calls and where they go; the sketches under the traps show one fix each and say whether MetaEditor has compiled them.
- It reads text, not a compiled script: your own functions, imported libraries and what a variable holds at run time stay unread. A line it doesn't recognise is simply not on the map.
- It can't see your broker: the server's clock, the account's position system (netting or hedging) and the symbol's volume are yours to check.
- The size is a count of what the script uses, not a quote or a time estimate.
The porting post walks one rule from our Opening Range Desk through the clock, the bar and the fills: Pine Script to MQL5. A port is done when both versions agree bar by bar on the same data, or when you can name the reason for every bar where they don't.