Pine Script to MQL5: How to Convert TradingView Strategies to MT5 EAs
This tutorial walks through the key conceptual and syntactic differences between Pine Script (TradingView) and MQL5 (MetaTrader 5), showing you step-by-step how to translate common strategy patterns — moving averages, RSI filters, entry/exit logic — into production-ready Expert Advisors. Whether you are migrating an existing Pine Script strategy or building a parallel MT5 version, this guide maps every critical concept so you can avoid the most common porting mistakes.
Strategy Logic
Entry Conditions
N/A — this is a tutorial/indicator reference page. The embedded example EA demonstrates a classic dual-SMA crossover with an RSI confirmation filter, entering a buy when the fast SMA crosses above the slow SMA and RSI is below 60, mirroring a typical Pine Script strategy.entry() call.
Exit Conditions
N/A — this is a tutorial/indicator reference page. The example EA closes positions when the opposite SMA crossover occurs, replicating the strategy.close() / strategy.exit() pattern found in Pine Script.
MQL5 Expert Advisor Code
//+------------------------------------------------------------------+
//| PineToMQL5Tutorial.mq5 |
//| Teaching example: Dual-SMA crossover with RSI filter |
//| Mirrors a typical Pine Script strategy for educational purposes |
//+------------------------------------------------------------------+
#property copyright "Pineify (pineify.app)"
#property version "1.00"
#property strict
//--- Input parameters (equivalent to Pine Script 'input.*' calls)
input int FastMAPeriod = 10; // Fast SMA period
input int SlowMAPeriod = 30; // Slow SMA period
input int RSIPeriod = 14; // RSI period
input double RSIBuyMax = 60.0; // RSI must be below this to buy
input double RSISellMin = 40.0; // RSI must be above this to sell
input double LotSize = 0.1; // Trade lot size
input int MagicNumber = 202401; // Unique EA identifier
input int Slippage = 10; // Max slippage in points
//--- Indicator handles (Pine Script uses built-in series; MQL5 uses handles)
int handleFastMA;
int handleSlowMA;
int handleRSI;
//+------------------------------------------------------------------+
//| OnInit — runs once on EA load (like Pine Script's init block) |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Create indicator handles
// Pine: fastMA = ta.sma(close, FastMAPeriod)
handleFastMA = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
handleSlowMA = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
// Pine: rsiVal = ta.rsi(close, RSIPeriod)
handleRSI = iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE);
if(handleFastMA == INVALID_HANDLE || handleSlowMA == INVALID_HANDLE || handleRSI == INVALID_HANDLE)
{
Print("ERROR: Failed to create indicator handles");
return INIT_FAILED;
}
Print("PineToMQL5Tutorial initialized successfully");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| OnDeinit — cleanup (no direct Pine Script equivalent) |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(handleFastMA);
IndicatorRelease(handleSlowMA);
IndicatorRelease(handleRSI);
Print("PineToMQL5Tutorial deinitialized. Reason: ", reason);
}
//+------------------------------------------------------------------+
//| Helper: count open positions for this EA |
//+------------------------------------------------------------------+
int CountPositions(ENUM_POSITION_TYPE posType)
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 &&
PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber &&
PositionGetInteger(POSITION_TYPE) == posType)
count++;
}
return count;
}
//+------------------------------------------------------------------+
//| Helper: close all positions for this EA |
//| Pine equivalent: strategy.close_all() |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) continue;
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.ticket = ticket;
request.volume = PositionGetDouble(POSITION_VOLUME);
request.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
? ORDER_TYPE_SELL : ORDER_TYPE_BUY;
request.price = (request.type == ORDER_TYPE_SELL)
? SymbolInfoDouble(_Symbol, SYMBOL_BID)
: SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.deviation = Slippage;
request.magic = MagicNumber;
request.comment = "PineToMQL5 close";
if(!OrderSend(request, result))
Print("CloseAllPositions error: ", result.retcode, " ", result.comment);
}
}
//+------------------------------------------------------------------+
//| Helper: open a market order |
//| Pine equivalent: strategy.entry(id, strategy.long/short) |
//+------------------------------------------------------------------+
bool OpenPosition(ENUM_ORDER_TYPE orderType)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = LotSize;
request.type = orderType;
request.price = (orderType == ORDER_TYPE_BUY)
? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
: SymbolInfoDouble(_Symbol, SYMBOL_BID);
request.deviation = Slippage;
request.magic = MagicNumber;
request.comment = (orderType == ORDER_TYPE_BUY) ? "PineToMQL5 buy" : "PineToMQL5 sell";
request.type_filling = ORDER_FILLING_IOC;
if(!OrderSend(request, result))
{
Print("OpenPosition error: ", result.retcode, " ", result.comment);
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| OnTick — main logic loop (Pine Script runs top-to-bottom on each |
//| new bar; here we gate execution to bar-close events) |
//+------------------------------------------------------------------+
void OnTick()
{
//--- Only act on a newly closed bar (Pine Script default: executes on bar close)
static datetime lastBarTime = 0;
datetime currentBarTime = (datetime)SeriesInfoInteger(_Symbol, PERIOD_CURRENT, SERIES_LASTBAR_DATE);
if(currentBarTime == lastBarTime) return;
lastBarTime = currentBarTime;
//--- Read indicator buffers (index 1 = previous closed bar, like Pine's [1])
double fastMA[], slowMA[], rsi[];
ArraySetAsSeries(fastMA, true);
ArraySetAsSeries(slowMA, true);
ArraySetAsSeries(rsi, true);
if(CopyBuffer(handleFastMA, 0, 0, 3, fastMA) < 3) return;
if(CopyBuffer(handleSlowMA, 0, 0, 3, slowMA) < 3) return;
if(CopyBuffer(handleRSI, 0, 0, 3, rsi) < 3) return;
//--- Pine Script crossover/crossunder detection
// ta.crossover(fastMA, slowMA) => fastMA[1] < slowMA[1] && fastMA[0] > slowMA[0]
bool crossOver = (fastMA[1] < slowMA[1]) && (fastMA[0] > slowMA[0]);
bool crossUnder = (fastMA[1] > slowMA[1]) && (fastMA[0] < slowMA[0]);
bool rsiOkBuy = rsi[0] < RSIBuyMax;
bool rsiOkSell = rsi[0] > RSISellMin;
int buys = CountPositions(POSITION_TYPE_BUY);
int sells = CountPositions(POSITION_TYPE_SELL);
//--- Entry logic (Pine: if crossover(fast, slow) and rsi < RSIBuyMax => strategy.entry("Long", strategy.long))
if(crossOver && rsiOkBuy && buys == 0)
{
if(sells > 0) CloseAllPositions();
OpenPosition(ORDER_TYPE_BUY);
}
//--- Exit / reverse logic (Pine: if crossunder(fast, slow) and rsi > RSISellMin => strategy.entry("Short", strategy.short))
if(crossUnder && rsiOkSell && sells == 0)
{
if(buys > 0) CloseAllPositions();
OpenPosition(ORDER_TYPE_SELL);
}
}
//+------------------------------------------------------------------+
Copy this code into MetaEditor (F4 in MT5), save in the Experts folder, and compile with F7.
Generate a Custom Multi-pair Educational EA →
Pineify AI generates syntactically validated MQL5 Expert Advisors from plain English descriptions. Customize entry logic, risk management, and trading sessions — no coding required.
Pine Script vs MQL5: Same Strategy, Different Platforms
| Aspect | Pine Script (TradingView) | MQL5 (MetaTrader 5) |
|---|---|---|
| Execution | Bar-based, backtesting only | Tick-based, live trading |
| Deployment | TradingView alerts | Runs 24/5 on VPS/MT5 |
| Broker access | Via TradingView broker integration | Direct broker connectivity |
| Backtesting | Built-in, no data download needed | Strategy Tester, tick data required |
| Code complexity | Simpler, functional syntax | C++-like, more powerful |
Pineify supports both platforms. Prototype your strategy visually in TradingView Pine Script, then generate the equivalent MQL5 EA for live MT5 trading.