How To Build An Automated Trading Bot Pt 2: Connecting TradingView to MetaTrader 4 (MT4)
August 3, 2024 Ed Jordan

Intro

Continuing on from our previous post on How To Build An Automated Trading Bot in TradingView, where we created a trading system using moving average crossovers to enter and exit trades.  Having completed the logic and automation algorithms for that system, we backtested it and are now ready to move on to implementing it in a live context. To do so we will use alerts to send trade data to MT4 from TradingView using Control Tower as the bridge.
If you haven’t read the previous article in this series, then I suggest you do and come back with the full code in your editor.

Sending Alerts to MT4 from TradingView

To convert our strategy to an automated bot which will communicate with MT4, we need to add alerts with the proper syntax to send the trade data to MT4 (and our broker).

ControlTower has a handy library which makes the process of encoding the trade data into MT4 format quick and easy.

First we will need to import the library.  To do so we will add a single line of code to the top of our script on the below the the strategy declaration:

strategy('EMA Crossover Strategy', overlay=true)
import ParametricTrading/JSONFormatter4ControlTower/4 as JSON

Next we will add the call to the formatting function within an alert() along with the necessary variables.  We will be replacing our strategy.entry() and strategy.close() calls from above with alerts that send trade orders to MT4.  All together that block of code will be:

//ALERT CALLS WHICH SEND TRADE ORDER DATA TO MT4 VIA CONTROL TOWER
if time_cond //test if current candle is within the time parameters
    if action == "LongEntry" or action == "ShortEntry"
        alert(JSON.formatMarket(productID,symbol,action,risk,lots,0,0,takeProfit),alert.freq_once_per_bar_close)
    if exitString == "LongExit" or exitString == "ShortExit"
        alert(JSON.formatPartial(productID,symbol,exitString,closePercentage),alert.freq_once_per_bar_close)

All that’s left is to define the variables and insert them in the script before the alerts are called.

Defining Variables

Adding the variables can be as easy as this:

var productID = "f1"
var symbol = "EURUSD"
var action = "LongEntry"
var risk = 0
var lots = 1.0
var takeProfit = 1.20
var closePercentage = 100

While this is functional it is extremely limited.  Instead of hardcoding the values let’s add a few of them to the user adjustable section above:

//TRADE PARAMETERStakeProfit = input.price(defval=0,title='Take Profit Price', group="Trade Parameters")lots = input.float(defval=0.5,  title='Lot Size',  step=.1, group="Trade Parameters")closePercentage = input.int(defval=100,title="Take Profit Percent", group="Trade Parameters")var symbol = syminfo.tickervar productID = "f1"risk = 0

To make things a bit sexier, let’s add the ability to plot the Take Profit price.  Remember that we still have the time range parameters in effect which means that a trade won’t execute unless within the user assigned time range, so let’s add a little test of the time condition again and only assign a color value to a variable if the time_cond is true.

 

plotTakeProfitColor = time_cond ? color.green : na //only assign a color if within time parameters

 

And then when we plot the value of takeProfit we will use the variable plotTakeProfitColor as the value for the color argument of the plot.  Like this:

 

plot(takeProfit,linewidth=4,color=plotTakeProfitColor,title="Take Profit Price", style=plot.style_line) //note this will only plot with a color if within the time range

 

Now we’ll only see the take profit price line within the time constraints defined in the user input section.  Neat!

The green plot line for the take profit price is only visible when within the time constraints defined by the user.

TradingView Webhook

Make sure you have the alert set up within TradingView to use a Webhook URL and that your custom webhook from ControlTower is inputted into the dialog box.

Going Live

Before we can use this strategy live we have to convert it from a strategy to an indicator.  Strategies in TradingView are used for backtesting.  Indicators work on live data and do not include backtesting functionality.  In order to trigger real time alerts (and thus send our trade signals to our broker) we need to be using an indicator.  The change is extremely simple:

 

We will change the opening line of the script to

indicator(‘EMA Crossover Strategy’, overlay=true)

And that’s it!  We have now created an automated trading system in TradingView which connects to MT4.  Our system is based on trading trends by following user-adjustable moving averages crosses.

 

Our final script is below:

 

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/

//@version=5
indicator('EMA Crossover Strategy', overlay=true)
import ParametricTrading/JSONFormatter4ControlTower/4 as JSON
////////////////////
// USER INPUT SECTION
// 
// BACKTESTING RANGE
startDate  = input.time    (timestamp("01 Jan 2024"),     '',     group='Backtesting Date Range',     inline='Start Period', tooltip="Select the start and end dates and times for the testing range.  The strategy will only be tested within these dates.")
finishDate = input.time    (timestamp("01 Jan 2025"),     '',     group='Backtesting Date Range',     inline='Start Period')

time_cond = time >= startDate and time <= finishDate //we will use time_cond later to test if the current candle is within the selected dates by seeing if time_cond is TRUE


//CREATE USER-INPUT VARIABLES

periodShort = input.int(8, minval=1, title='Short Moving Average Length', group="Short Moving Average Selection",inline="shortMA", tooltip="Select the length and the type of moving average to use for the Short moving average.")
smoothingShort = input.string(title='Type', defval='EMA', options=['RMA', 'SMA', 'EMA', 'WMA', 'VWMA', 'SMMA', 'DEMA', 'TEMA', 'HullMA', 'LSMA'],inline="shortMA", group="Short Moving Average Selection")

periodLong = input.int(21, minval=1, title='Long Moving Average Length', group="Long Moving Average Selection",inline="longMA", tooltip="Select the length and the type of moving average to use for the Long moving average.")
smoothingLong = input.string(title='Type', defval='EMA', options=['RMA', 'SMA', 'EMA', 'WMA', 'VWMA', 'SMMA', 'DEMA', 'TEMA', 'HullMA', 'LSMA'], group="Long Moving Average Selection",inline="longMA")

periodExit = input.int(200, minval=1, title='Exit Moving Average Length', group="Exit Moving Average Selection",inline="exitMA", tooltip="Select the length and the type of moving average to use for the Exit moving average.")
smoothingExit = input.string(title='Type', defval='EMA', options=['RMA', 'SMA', 'EMA', 'WMA', 'VWMA', 'SMMA', 'DEMA', 'TEMA', 'HullMA', 'LSMA'], group="Exit Moving Average Selection",inline="exitMA")

//TRADE PARAMETERS
takeProfit = input.price(defval=0,title='Take Profit Price', group="Trade Parameters")
lots = input.float(defval=0.5,  title='Lot Size',  step=.1, group="Trade Parameters")
closePercentage = input.int(defval=100,title="Take Profit Percent", group="Trade Parameters") 

var symbol = syminfo.ticker
var productID = "f1"
risk = 0
//////////////////////////////



//MOVING AVERAGE FUNCTION
//Moving Average Calculation for each type of moving average
ma(smoothing, period) =>
    if smoothing == 'RMA'
        ta.rma(close, period)
    else
        if smoothing == 'SMA'
            ta.sma(close, period)
        else
            if smoothing == 'EMA'
                ta.ema(close, period)
            else
                if smoothing == 'WMA'
                    ta.wma(close, period)
                else
                    if smoothing == 'VWMA'
                        ta.vwma(close, period)
                    else
                        if smoothing == 'SMMA'
                            na(close[1]) ? ta.sma(close, period) : (close[1] * (period - 1) + close) / period
                        else

                            if smoothing == 'HullMA'
                                ta.wma(2 * ta.wma(close, period / 2) - ta.wma(close, period), math.round(math.sqrt(period)))




//ASSIGN A MOVING AVERAGE RESULT TO A VARIABLE
shortMA = ma(smoothingShort, periodShort)
longMA = ma(smoothingLong, periodLong)
exitMA = ma(smoothingExit, periodExit)

//PLOT THOSE VARIABLES
plotTakeProfitColor = time_cond ? color.green : na //only assign a color if within time parameters

plot(shortMA, linewidth=4, color=color.new(color.yellow, 0), title='The Short Moving Average')
plot(longMA, linewidth=4, color=color.new(color.blue, 0), title='The Long Moving Average')
plot(exitMA, linewidth=1, color=color.new(color.red, 0), title='The Exit Moving Average')
plot(takeProfit,linewidth=4,color=plotTakeProfitColor,title="Take Profit Price", style=plot.style_line) //note this will only plot with a color if within the time range


//ENTRY LOGIC
buy = ta.crossover(shortMA, longMA) ? true : na
sell = ta.crossunder(shortMA, longMA) ? true :na

action = buy ? "LongEntry" : sell ? "ShortEntry" : na //assign strings for use later with MT4 and Control Tower integration

//EXIT LOGIC
exitLong = ta.crossunder(close, exitMA) ? true : na
exitShort = ta.crossover(close, exitMA) ? true : na

exitString = exitLong ? "LongExit" : exitShort ? "ShortExit" : na //assign strings for use later with MT4 and Control Tower integration


//ALERT CALLS WHICH SEND TRADE ORDER DATA TO MT4 VIA CONTROL TOWER
if time_cond //test if current candle is within the time parameters
    if action == "LongEntry" or action == "ShortEntry"
        alert(JSON.formatMarket(productID,symbol,action,risk,lots,0,0,takeProfit),alert.freq_once_per_bar_close)
    if exitString == "LongExit" or exitString == "ShortExit"
        alert(JSON.formatPartial(productID,symbol,exitString,closePercentage),alert.freq_once_per_bar_close)

Save the indicator we’ve been working on as an indicator (using whatever name you would like) and then add it to your chart window.

Now we just need to send the alerts.  Load a ticker or currency that you have backtested and feel confident will be profitable using our system,  and click on the Alert icon.

Then from the pop up window, select the indicator name from the Condition menu and from the middle dropdown menu (which defaults to “Crossing”) select “Any alert() function call”.

Make sure you have Webhook URL enabled and have entered the correct URL from the Control Tower app.

Then click Create.  Your alert will be listed in the alerts pane and will trigger when the crossover/crossunder conditions are met.

Make sure you have MT4 and Control Tower open.  TradingView does not need to remain open.

When the short EMA crosses the long EMA, an alert will be sent to Control Tower which will immediately send it to MT4 for execution to your broker.

Congratulations!  You have an automated bot trading system in TradingView executing orders to your broker of choice via MT4!

Comments (0)

Leave a reply

Your email address will not be published. Required fields are marked *

*