Expert Advisor Tutorial ~ Manage Multiple EAs.

Expert Advisor Tutorial ~ Manage Multiple EAs.

# Expert Advisor Tutorial ~ A useful tool to manage multiple EAs.

This expert advisor tutorial shares with you a very useful tool that help you manage multiple EAs on expert advisor studio.

# I decided to use several EA.

After several times of backtesting, I decided to use several expert advisors (EAs) at the same time to reduce the risk of trading. But I found when a couple of EAs with different trading strategies run simultaneously, the trades may be messed up and become difficult to manage, sometimes you may encounter conflicts.

For example, we opened two trades respectively by different EAs(EA1 and EA2), we call the trade opened by EA1 as Trade1, and the trade opened by EA2 as Trade2.

  • EA1: Trade1, EUR/USD, Long
  • EA2: Trade2, EUR/USD, Long

Both trade EUR/USD, the orientations of both are the same.

Then a signal to go short occurred, the EA1 closed Trade1 by the program logic shown below, the EA2 has the same position-operation algorithms, so EA2 aimed to close a trade going Long as well.

// op stands for the current signal, preOp stands for the previous signal.

var getOpenTrd = function (op) {
    var openTradeLength = getOpenTradesListLength(context)

    for (var i = openTradeLength - 1; i >= 0; i--) {
        var openTrade = getOpenTrade(context, i)

        if (getOrderType(openTrade) == op) {
            return openTrade
        }
    }

    return null
}

var volume = 0.01

if (op == 0) {
    if (preOp != -1) {
        var openTrade = getOpenTrd(ORDER_TYPE.OP_SELL)

        if (openTrade != null) {
            var tradeId = getTradeId(openTrade)
            closeTrade(brokerName, accountId, tradeId)

            sendOrder(brokerName, accountId, symbolName, ORDER_TYPE.OP_BUY, 0, 0, volume, 0, 0, "")
        }
    } else {
        sendOrder(brokerName, accountId, symbolName, ORDER_TYPE.OP_BUY, 0, 0, volume, 0, 0, "")
    }
} else if (op == 1) {
    if (preOp != -1) {
        var openTrade = getOpenTrd(ORDER_TYPE.OP_BUY)

        if (openTrade != null) {
            var tradeId = getTradeId(openTrade)
            closeTrade(brokerName, accountId, tradeId)

            sendOrder(brokerName, accountId, symbolName, ORDER_TYPE.OP_SELL, 0, 0, volume, 0, 0, "")
        }
    } else {
        sendOrder(brokerName, accountId, symbolName, ORDER_TYPE.OP_SELL, 0, 0, volume, 0, 0, "")
    }
}

As the comments imply, op stands for the current signal, preOp stands for the previous signal.

# If the source codes attached to both EAs, what will happen when the signal to go short occurs?

Fintechee's trading subsystem is based on async. So, when you run the polling loop to find an available open trade by the two EAs, you will get the same result. Even if you have called closeTrade(brokerName, accountId, tradeId) to close one open trade, the trade that you are going to close remains in the trades list(though the order to close it has been sent and executed) until the frontend of Fintechee gets the notification that the trade has been closed from the server-side. The next turn of the onTick callback function will remove the closed trade from the trades list which is transferred by the context object.

So, when EA1 closed Trade1, Trade1 remained in the trades list until the next turn of the onTick function was executed. EA2 would find Trade1 as well, though Trade1 had been closed. Then, if EA2 tried to close Trade1, an error would be prompted to notify you that the Trade1 didn't exist.

How to make EA2 find Trade2? How to close Trade2 when a signal comes to EA2.

# We need an EA to manage them.

I recommend you build an independent EA to manage the trades. So, we code three EAs, two EAs just to generate signals, one EA just to manage trades.

We share the source codes below. You can copy-paste them to your WEB trader and try it out.

Please note, in Fintechee WEB trader, the calling order of EAs' callback functions is subject to the boosting order of EAs.

For example, If you start running EA1 first, EA2 as the second one, EA3 as the third one, then the onTick callback functions of EA1, EA2, EA3 will be called as the order of EA1->EA2->EA3. So, please make the manager EA boost as the last one.

registerEA(
"ea1",
"An EA to test multiple EAs(v1.0)",
[],
function (context) { // Init()
		},
function (context) { // Deinit()
		},
function (context) { // OnTick()
    var signal = Math.random() < 0.5 ? 0 : 1

    if (typeof window.trdSys == "undefined") {
        window.trdSys = []
    }

    if (typeof window.trdSys["ea1"] == "undefined") {
        window.trdSys["ea1"] = {
            preSignal: -1,
            signal: signal
        }
    } else {
        window.trdSys["ea1"].preSignal = window.trdSys["ea1"].signal
        window.trdSys["ea1"].signal = signal
    }
})

registerEA(
"ea2",
"An EA to test multiple EAs(v1.0)",
[],
function (context) { // Init()
		},
function (context) { // Deinit()
		},
function (context) { // OnTick()
    var signal = Math.random() < 0.5 ? 0 : 1

    if (typeof window.trdSys == "undefined") {
        window.trdSys = []
    }

    if (typeof window.trdSys["ea2"] == "undefined") {
        window.trdSys["ea2"] = {
            preSignal: -1,
            signal: signal
        }
    } else {
        window.trdSys["ea2"].preSignal = window.trdSys["ea2"].signal
        window.trdSys["ea2"].signal = signal
    }
})

registerEA(
"multiple_ea_manager",
"An EA to manage multiple EAs(v1.0)",
[],
function (context) { // Init()
		},
function (context) { // Deinit()
		},
function (context) { // OnTick()
    if (typeof window.trdSys == "undefined") return

    var opCount = 0

    for (var i in window.trdSys) {
        if (window.trdSys[i].preSignal == -1 && window.trdSys[i].signal == 0) {
            opCount++
        } else if (window.trdSys[i].preSignal == -1 && window.trdSys[i].signal == 1) {
            opCount--
        } else if (window.trdSys[i].preSignal == 0 && window.trdSys[i].signal == -1) {
            opCount--
        } else if (window.trdSys[i].preSignal == 1 && window.trdSys[i].signal == -1) {
            opCount++
        } else if (window.trdSys[i].preSignal == window.trdSys[i].signal) {
        } else if (window.trdSys[i].preSignal == 0 && window.trdSys[i].signal == 1) {
            opCount -= 2
        } else if (window.trdSys[i].preSignal == 1 && window.trdSys[i].signal == 0) {
            opCount += 2
        }
    }

    var checkTrades = function (cursor, op) {
        var cursorTmp = cursor

        while (cursorTmp >= 0) {
            var openTrade = getOpenTrade(context, cursorTmp)

            if (openTrade != null) {
                if (getOrderType(openTrade) == op) {
                    var tradeId = getTradeId(openTrade)
                    closeTrade(brokerName, accountId, tradeId)
                    return cursorTmp
                }
            }

            cursorTmp--
        }

        return cursorTmp
    }

    var openTradeLength = getOpenTradesListLength(context)
    var cursor = openTradeLength - 1
    var volume = 0.01

    if (opCount > 0) {
        while (opCount > 0) {
            if (cursor >= 0) {
                cursor = checkTrades(cursor, ORDER_TYPE.OP_SELL)

                if (cursor >= 0) {
                    cursor--
                } else {
                    sendOrder(brokerName, accountId, symbolName, ORDER_TYPE.OP_BUY, 0, 0, volume, 0, 0, "")
                }
            } else {
                sendOrder(brokerName, accountId, symbolName, ORDER_TYPE.OP_BUY, 0, 0, volume, 0, 0, "")
            }

            opCount--
        }
    } else if (opCount < 0) {
        while (opCount < 0) {
            if (cursor >= 0) {
                cursor = checkTrades(cursor, ORDER_TYPE.OP_BUY)

                if (cursor >= 0) {
                    cursor--
                } else {
                    sendOrder(brokerName, accountId, symbolName, ORDER_TYPE.OP_SELL, 0, 0, volume, 0, 0, "")
                }
            } else {
                sendOrder(brokerName, accountId, symbolName, ORDER_TYPE.OP_SELL, 0, 0, volume, 0, 0, "")
            }

            opCount++
        }
    }
})

I will write more tutorials to help you know how to use it.

We started trading since Feb 17th, 2020. Please feel free to track our trading records.

Read More