FX NOOKAI CODE LAB
Loading

Convert a TradingView Indicator to an MT5 EA: Steps

By FXNook Lab team

You cannot paste a TradingView indicator’s Pine Script into MetaEditor and get an MT5 Expert Advisor. You need to recreate its calculations and signal conditions in MQL5, then define what the EA does with each signal: when to enter and exit, how to size a position, and how to handle stops and opposite signals. A faithful conversion also has to match bar timing and account for differences in market data and order execution.

The safest workflow is to translate the indicator’s behavior, not its code line by line. A moving-average crossover makes a useful example: the signal itself is simple, but deciding whether to act on the live candle or a closed candle changes when an EA trades.

Can you convert a TradingView indicator directly into an MT5 EA?

No. Pine Script is TradingView’s language; MT5 programs use MQL5. The two platforms have different indicator APIs, bar access, order handling and runtime behavior. Pine code must be interpreted and implemented again in MQL5.

An indicator also does not necessarily define a complete trading system. It may draw an arrow without specifying whether the arrow means “open a trade now,” whether an existing position should be closed, or what to do if a new arrow appears while a trade is open. Those are EA rules, not automatic consequences of plotting a signal.

For a reliable conversion, separate the work into three parts: reproduce the indicator’s values, define the trading rules that use its signals, and test that MT5 produces the expected signals at the expected times. A matching-looking chart is not enough if the EA reads a different bar or sends orders under different assumptions.

Collect the source and define the trading rules

Before coding, gather the details that determine what the indicator means. Source code is the best reference because it reveals the calculations and conditions behind the plotted output. Also note the chart and alert settings: a signal based on a closed candle behaves differently from one based on the current candle.

Turn visual signals into statements that a program can test. For example: “Open one long position when the fast average crosses above the slow average on the last completed candle. Close it and open short if the fast average crosses below the slow average. Do not enter again while the existing position is in the same direction.” That is specific enough to implement; “trade the arrows” is not.

Also decide what “one position” means. MT5 netting accounts hold one net position per symbol, while hedging accounts can hold several separate positions. An EA designed to manage one net position may need different position-selection and closing logic on a hedging account.

Map Pine Script concepts to MQL5

The conversion is not a one-to-one replacement of function names. Pine’s series model makes historical values available through indexing, while MQL5 commonly obtains indicator values through handles and buffer copies. The EA must also explicitly send and manage trades.

Pine Script conceptTypical MQL5 approachConversion detail
Inputs such as input.int()input variablesRecreate the input type, default and valid range; keep the same meaning.
close, high, or a historical series valuePrice series or indicator data requested for a specific barBe precise about bar shifts. In MQL5, shift 0 is the current, unfinished bar; shift 1 is the last completed bar.
ta.sma() or ta.ema()iMA() handle, then CopyBuffer()Create the handle in OnInit(), read its buffer when needed, and release it in OnDeinit().
ta.crossover(a,b)Compare the previous and latest completed valuesFor a cross up, the earlier value must be at or below the other series and the later value above it.
plotshape() and alertcondition()Signal conditions in the EA, optionally drawn or loggedA plot or alert describes a signal; it does not itself place an MT5 order.
Trading actions in a Pine strategyCTrade, or MQL5 trade requestsDefine order size, stops, targets, position rules and how to handle rejected requests.

MQL5 indicator functions such as iMA() generally return a handle rather than the current indicator value. The EA reads the value with CopyBuffer(). That is different from treating an indicator call as if it returned a number directly, a common source of errors when developers move from other environments.

Values can still differ even when the formulas match. TradingView and the MT5 broker may use different price feeds, sessions, candle boundaries or available history. If Pine uses a higher timeframe, a custom source, or session filtering, reproduce those assumptions explicitly and compare the resulting timestamps rather than relying on visual similarity.

Moving-average crossover: from Pine signal to MT5 EA

Here is a simple Pine Script v5 indicator. It plots two simple moving averages and marks a cross on the current bar. The alert conditions use the same signal conditions; they do not make the script a trading EA.

// Pine Script v5
//@version=5
indicator("SMA crossover signals", overlay=true)
fastLen = input.int(10, "Fast period", minval=1)
slowLen = input.int(30, "Slow period", minval=1)
fast = ta.sma(close, fastLen)
slow = ta.sma(close, slowLen)
longSignal = ta.crossover(fast, slow)
shortSignal = ta.crossunder(fast, slow)
plot(fast, "Fast SMA", color=color.blue)
plot(slow, "Slow SMA", color=color.orange)
plotshape(longSignal, title="Cross up", style=shape.triangleup, location=location.belowbar, color=color.green)
plotshape(shortSignal, title="Cross down", style=shape.triangledown, location=location.abovebar, color=color.red)
alertcondition(longSignal, "Cross up", "Fast SMA crossed above slow SMA")
alertcondition(shortSignal, "Cross down", "Fast SMA crossed below slow SMA")

To trade only confirmed crosses, the MT5 version should compare values from completed candles. In the code below, CopyBuffer() requests two values starting at shift 1. The copied arrays hold the older completed value at index 0 and the most recently completed value at index 1. A cross up therefore requires the fast average to be at or below the slow average at index 0, then above it at index 1.

This example is a complete MT5 EA for a netting account. It processes once when a new chart bar appears, uses fixed lots, sets stop-loss and take-profit distances in points, and reverses its position on an opposite cross. Set the input values to ones accepted by the broker; stop-distance restrictions and volume steps vary. The example does not calculate risk-based volume. Its order settings are demonstration rules, not recommendations.

#include <Trade/Trade.mqh>

input int FastPeriod = 10;
input int SlowPeriod = 30;
input double Lots = 0.10;
input int StopLossPoints = 500;
input int TakeProfitPoints = 1000;
input ulong MagicNumber = 26001;

CTrade trade;
int fastHandle = INVALID_HANDLE;
int slowHandle = INVALID_HANDLE;
datetime lastBarTime = 0;

int OnInit()
{
   if(FastPeriod < 1 || SlowPeriod < 1 || FastPeriod >= SlowPeriod ||
      Lots <= 0.0 || StopLossPoints <= 0 || TakeProfitPoints <= 0)
      return INIT_PARAMETERS_INCORRECT;

   fastHandle = iMA(_Symbol, _Period, FastPeriod, 0, MODE_SMA, PRICE_CLOSE);
   slowHandle = iMA(_Symbol, _Period, SlowPeriod, 0, MODE_SMA, PRICE_CLOSE);
   if(fastHandle == INVALID_HANDLE || slowHandle == INVALID_HANDLE)
      return INIT_FAILED;

   trade.SetExpertMagicNumber(MagicNumber);
   lastBarTime = iTime(_Symbol, _Period, 0);
   return INIT_SUCCEEDED;
}

void OnDeinit(const int reason)
{
   if(fastHandle != INVALID_HANDLE)
      IndicatorRelease(fastHandle);
   if(slowHandle != INVALID_HANDLE)
      IndicatorRelease(slowHandle);
}

void OnTick()
{
   datetime barTime = iTime(_Symbol, _Period, 0);
   if(barTime == 0 || barTime == lastBarTime)
      return;
   lastBarTime = barTime;

   double fast[2], slow[2];
   if(CopyBuffer(fastHandle, 0, 1, 2, fast) != 2 ||
      CopyBuffer(slowHandle, 0, 1, 2, slow) != 2)
      return;

   bool crossUp = fast[0] <= slow[0] && fast[1] > slow[1];
   bool crossDown = fast[0] >= slow[0] && fast[1] < slow[1];
   if(!crossUp && !crossDown)
      return;

   if(PositionSelect(_Symbol))
   {
      if((ulong)PositionGetInteger(POSITION_MAGIC) != MagicNumber)
         return;

      ENUM_POSITION_TYPE type =
         (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      if((crossUp && type == POSITION_TYPE_BUY) ||
         (crossDown && type == POSITION_TYPE_SELL))
         return;
      if(!trade.PositionClose(_Symbol))
      {
         Print("Position close failed. Retcode: ", trade.ResultRetcode());
         return;
      }
   }

   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   if(point <= 0.0)
      return;

   bool sent;
   if(crossUp)
   {
      double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      double sl = NormalizeDouble(ask - StopLossPoints * point, _Digits);
      double tp = NormalizeDouble(ask + TakeProfitPoints * point, _Digits);
      sent = trade.Buy(Lots, _Symbol, 0.0, sl, tp, "SMA cross up");
   }
   else
   {
      double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
      double sl = NormalizeDouble(bid + StopLossPoints * point, _Digits);
      double tp = NormalizeDouble(bid - TakeProfitPoints * point, _Digits);
      sent = trade.Sell(Lots, _Symbol, 0.0, sl, tp, "SMA cross down");
   }

   if(!sent)
      Print("Order failed. Retcode: ", trade.ResultRetcode());
}

The new-bar gate prevents the EA from re-evaluating the same completed-bar signal on every tick. The position check also avoids repeatedly opening in the same direction. On a netting account, an EA that closes by symbol is managing the symbol’s single net position; do not run this sample as-is where its position could be mixed with manual trades or another EA’s trades.

This example intentionally keeps the signal logic separate from the order calls. In a production EA, add the position and risk rules required by the strategy, validate volume against SYMBOL_VOLUME_MIN, SYMBOL_VOLUME_MAX and SYMBOL_VOLUME_STEP, and account for broker stop distances and execution settings. A server can reject an order even when the EA compiles; inspect the trade result code and the Experts and Journal logs to find the reason.

Make the trade rules explicit

The crossover indicator supplies a direction change, not a full set of instructions. Before using it in an EA, settle each decision and implement it deliberately.

Keep these rules separate from signal detection. That makes it easier to test whether a difference comes from the indicator calculation or from trade management. For MT5 trade operations, CTrade provides methods such as Buy(), Sell() and PositionClose(); a successful method call should still be followed by a review of the result code and platform logs.

Check repainting and signal timing

A signal based on the current candle can appear and disappear before that candle closes. A moving average changes as new ticks update the current close, so a crossover detected on shift 0 may not remain a crossover at the close. A backtest or chart that shows only final candle values can make that behavior hard to notice.

The example avoids this specific timing mismatch by acting on completed candles. That means the cross is confirmed after a bar closes and the EA first evaluates it on the next bar. If the TradingView indicator is configured to alert during the live bar, its alert timestamp and the EA’s entry timing will not match by design. Decide which behavior you want before comparing results.

For indicators that can revise historical signals, inspect how they calculate past values and whether they request data from another timeframe. A plotted arrow that moves or disappears after more bars arrive is not equivalent to a fixed, closed-bar signal. The [MT5 non-repaint indicator guide](/blog/mt5-non-repaint-indicator) explains checks that can help identify this behavior.

Compare matching signals by timestamp

  1. Use the same symbol, timeframe, session assumptions and comparable date range on both platforms.
  2. Record the TradingView signal time and whether its alert is configured for the candle close or intrabar updates.
  3. Log the MT5 bar time, the fast and slow values used, and the cross condition. Compare the same completed candle, not just the time an order appears.
  4. Allow for different broker feeds and candle boundaries. If the averages diverge, compare the underlying bar prices before changing the MQL5 logic.
  5. Inspect the first bars after initialization. An indicator handle may not have enough calculated data yet, and CopyBuffer() may not return the requested values.

For a source-code conversion, the FXNook Lab Indicator to EA page describes a workflow for identifying indicator buffers and clarifying which value means buy or sell and on which bar. If you have only a compiled indicator file, the Lab notes that it cannot be read; an EA can call it with iCustom() when its inputs and signal-buffer mapping are known.

Compile, backtest and verify the MT5 EA

  1. Compile in MetaEditor. Save the EA as an .mq5 file and compile it. Read the first compiler error in context; later errors can be consequences of one earlier issue. A clean compile proves the code is syntactically acceptable, not that it reproduces the indicator correctly or behaves as intended in trading.
  2. Run the Strategy Tester. Select the same symbol and timeframe used for the comparison, then set a date range with enough bars for the indicator periods to initialize. Use a tester model with tick data when the strategy depends on intrabar timing; for a closed-bar signal, still inspect when the order is placed relative to the completed signal bar.
  3. Compare trades with signals. Review the chart, trade list, Experts log and Journal. Confirm each entry follows the specified signal, that exits match the rules, and that the EA does not act more than once on a bar.
  4. Test broker constraints. Pay attention to rejected orders, invalid volume, insufficient funds, trading permission and stop-level messages. Adjust the EA’s inputs or handling to the actual symbol specifications rather than assuming every broker accepts the same order parameters.
  5. Forward-test on a demo account. Observe live signal timing, spread and order handling before drawing conclusions from a backtest. Past or tested results do not guarantee future results.

For a fuller MT5 testing workflow, see the MT5 EA builder guide and the MT5 EA source code example. If you already have Pine logic and want to turn its signals into a MetaTrader program, begin by writing down the signal bar and trade rules in exact terms. Then recreate the calculations, compare timestamped signals, and test the EA in the Strategy Tester before demo forward-testing. The FXNook Lab Indicator to EA page explains how the Lab handles indicator-to-EA conversions and what information is needed when signal details are unclear.

Questions

Can Pine Script be converted directly to MQL5?

No. Pine Script cannot be compiled as MQL5, so its calculations and conditions must be recreated using MQL5 functions and data access. The trading rules also need to be specified separately if the Pine program only displays signals.

Can an MT5 EA trade TradingView alerts?

An MT5 EA does not automatically receive TradingView alerts as native MetaTrader events. Trading directly from alerts requires a separately designed communication method, or the indicator’s conditions can be recreated inside the EA so it evaluates them in MT5.

Why do TradingView and MT5 indicator signals differ?

Common causes include different price feeds, sessions, candle boundaries, historical data and current-bar versus closed-bar evaluation. Higher-timeframe calculations and a signal that changes before candle close can also produce different values or timestamps.

Do I need the TradingView indicator source code to make an MT5 EA?

Source code makes it possible to inspect the calculations and signal conditions, so it is usually the clearest route. Without it, an equivalent implementation may be difficult to specify; screenshots or alerts alone do not reveal all of the indicator’s rules.