In previous articles in FMZ's library, we have designed several kinds of synchronization strategies for order and position.

These manage reference accounts and synchronized accounts in one strategy to achieve order and position synchronization. And today we will try a different design, we will design an order synchronization management system based on the powerful extended API interface of FMZ Quant Trading Platform.

Design Ideas

First, we need some good suggestions and needs. The two previous order and position synchronization strategies above, which have several obvious shortcomings, which we will discuss together.

  • 1. Users of synchronization strategies real bot must have the exchange API KEY of the reference account, and the exchange API KEY of the synchronization account.
    This issue is fine for the usage situation where one's other exchange accounts follow his or her own account. However, it can be troublesome for the situation where the reference account and the sync account are not the same owner. Sometimes the owner of the synchronized account does not want to provide the API KEY of his own exchange account for security reasons, but how to synchronize the order transactions without providing the API KEY? Solutions:
    Use the FMZ's extended API interface, the owner of the synchronized account (order follower) only needs to register an account on FMZ Quant Trading Platform, then run a strategy (in the system designed in this article: Order Synchronous Server Strategy in Real Bot). Then just provide the FMZ extended API KEY (note that it is not the API KEY of the exchange account) and the Order Synchronous Server real bot ID to the owner of the reference account (order leader).
    When the reference account owner's (order follower's) real bot (the Order Synchronization Management System Class Library (Single Server) in the system designed in this article) sends a signal, the synchronization account owner's real bot will receive the trade signal and place the subsequent order automatically.
  • 2. Many developers have good strategies, but they cannot use the 2 past order and position synchronization strategies described above. Because they need to integrate their own strategies with these synchronized strategies, and the strategies may need to be changed drastically, which will cost a lot of work and effort. Is there a good way to upgrade some of your mature strategies directly to the order synchronization function?
    Solutions:
    You can design an order synchronization template class library (the Order Synchronization Management System Class Library (Single Server) strategy in the system designed in this article), so that the owner of the reference account (order-leader) can embed this template class library into his own strategy directly to achieve the function of order and position synchronization.
  • 3. Reduce an additional real bot.
    The last shortcoming is that if you use the 2 past orders, positions synchronization strategy described above. It is necessary to open an additional real bot to monitor the positions of reference account (account for order leaders).
    Solutions:
    Use the template class library to embed functionality in the reference account strategy.

So the system consists of 2 parts:

  1. Order synchronization management system class library (Single Server)
  2. Order synchronization management system (Synchronous Server)

Once we've defined our needs, let's start designing!

Design 1: Order synchronization management system class library (Single Server)

Note that this is not a strategy. It is a template class library of FMZ. The concept of a template class library can be searched in the FMZ API documentation and we will not repeat it again.

Template class library code:

// Global variables
var keyName_label = "label"
var keyName_robotId = "robotId"
var keyName_extendAccessKey = "extendAccessKey"
var keyName_extendSecretKey = "extendSecretKey"
var fmzExtendApis = parseConfigs([config1, config2, config3, config4, config5])
var mapInitRefPosAmount = {}

function parseConfigs(configs) {
    var arr = []
    _.each(configs, function(config) {
        if (config == "") {
            return 
        }
        var strArr = config.split(",")
        if (strArr.length != 4) {
            throw "configs error!"
        }
        var obj = {}
        obj[keyName_label] = strArr[0]
        obj[keyName_robotId] = strArr[1]
        obj[keyName_extendAccessKey] = strArr[2]
        obj[keyName_extendSecretKey] = strArr[3]
        arr.push(obj)
    })
    return arr 
}

function getPosAmount(pos, ct) {
    var longPosAmount = 0
    var shortPosAmount = 0
    _.each(pos, function(ele) {
        if (ele.ContractType == ct && ele.Type == PD_LONG) {
            longPosAmount = ele.Amount
        } else if (ele.ContractType == ct && ele.Type == PD_SHORT) {
            shortPosAmount = ele.Amount
        }
    })
    var timestamp = new Date().getTime()
    return {ts: timestamp, long: longPosAmount, short: shortPosAmount}
}

function sendCommandRobotMsg (robotId, accessKey, secretKey, msg) {
    // https://www.fmz.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
    var url = "https://www.fmz.com/api/v1?access_key=" + accessKey + "&secret_key=" + secretKey + "&method=CommandRobot&args=[" + robotId + ',"' + msg + '"]'
    Log(url)
    var ret = HttpQuery(url)
    return ret 
}

function follow(nowPosAmount, symbol, ct, type, delta) {
    var msg = ""
    var nowAmount = type == PD_LONG ? nowPosAmount.long : nowPosAmount.short
    if (delta > 0) {
        // open the position
        var tradeDirection = type == PD_LONG ? "buy" : "sell"
        // send signals
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)        
    } else if (delta < 0) {
        // close the position
        var tradeDirection = type == PD_LONG ? "closebuy" : "closesell"
        if (nowAmount <= 0) {
            Log("no positions found")
            return 
        }
        // send signals
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)
    } else {
        throw "error"
    }
    if (msg) {
        _.each(fmzExtendApis, function(extendApiConfig) {
            var ret = sendCommandRobotMsg(extendApiConfig[keyName_robotId], extendApiConfig[keyName_extendAccessKey], extendApiConfig[keyName_extendSecretKey], msg)
            Log("call the CommandRobot interface, ", "label:", extendApiConfig[keyName_label], ",  msg:", msg, ",  ret:", ret)
            Sleep(1000)
        })
    }
}

$.PosMonitor = function(exIndex, symbol, ct) {    
    var ts = new Date().getTime()
    var ex = exchanges[exIndex]
    // judge the type of ex
    var exName = ex.GetName()
    var isFutures = exName.includes("Futures_")
    var exType = isFutures ? "futures" : "spot"
    if (!isFutures) {
        throw "only future-following is supported"
    }

    if (exType == "futures") {
        // caching symbol ct
        var buffSymbol = ex.GetCurrency()
        var buffCt = ex.GetContractType()

        // switch to the corresponding contract pair, contract code
        ex.SetCurrency(symbol)
        if (!ex.SetContractType(ct)) {
            throw "SetContractType failed"
        }

        // monitor positions
        var keyInitRefPosAmount = "refPos-" + exIndex + "-" + symbol + "-" + ct    // refPos-exIndex-symbol-contractType
        var initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        if (!initRefPosAmount) {
            // no initialization data, initialize it
            mapInitRefPosAmount[keyInitRefPosAmount] = getPosAmount(_C(ex.GetPosition), ct)
            initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        }

        // monitor
        var nowRefPosAmount = getPosAmount(_C(ex.GetPosition), ct)
        // calculate the position changes
        var longPosDelta = nowRefPosAmount.long - initRefPosAmount.long
        var shortPosDelta = nowRefPosAmount.short - initRefPosAmount.short

        // detect changes
        if (!(longPosDelta == 0 && shortPosDelta == 0)) {
            // Perform long positions
            if (longPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "Perform long position-following, changes in volume:", longPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_LONG, longPosDelta)
            }
            // Perform short positions
            if (shortPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "Perform short position-following, changes in volume:", shortPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_SHORT, shortPosDelta)
            }

            // Update after performing the order-following operation
            mapInitRefPosAmount[keyInitRefPosAmount] = nowRefPosAmount
        }

        // restore symbol ct
        ex.SetCurrency(buffSymbol)
        ex.SetContractType(buffCt)
    } else if (exType == "spot") {
        // Spots
    }
}

$.getTbl = function() {
    var tbl = {
        "type" : "table", 
        "title" : "synchronization of data", 
        "cols" : [], 
        "rows" : []
    }
    // construct the table headers
    tbl.cols.push("monitor the account: refPos-exIndex-symbol-contractType")
    tbl.cols.push(`monitor the position: {"timestamp":xxx,"long positions":xxx,"short positions":xxx}`)
    _.each(fmzExtendApis, function(extendApiData, index) {
        tbl.cols.push(keyName_robotId + "-" + index)
    })

    // Write data in
    _.each(mapInitRefPosAmount, function(initRefPosAmount, key) {
        var arr = [key, JSON.stringify(initRefPosAmount)]
        _.each(fmzExtendApis, function(extendApiData) {
            arr.push(extendApiData[keyName_robotId])
        })
        tbl.rows.push(arr)
    })

    return tbl
}

// Example of the strategy call that references the template class library
function main() {
    // Clear all logs
    LogReset(1)

    // Switch to OKEX demo to test
    exchanges[0].IO("simulate", true)

    // Set the contract
    exchanges[0].SetCurrency("ETH_USDT")
    exchanges[0].SetContractType("swap")

    // Timed trading interval
    var tradeInterval = 1000 * 60 * 3        // Trade for every three minutes to observe the order-following signals
    var lastTradeTS = new Date().getTime()

    while (true) {
        // Other logic of the strategy...

        // Simulated trading triggers for testing
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("Trade the simulation order-leading strategies, position changes", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // Interface functions that use templates
        $.PosMonitor(0, "ETH_USDT", "swap")    // Multiple monitors can be set up to monitor different exchange objects on the order-following strategy  
        var tbl = $.getTbl()

        // Display status bar
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

The design is very simple, the class library has 2 functional functions. When a programmatic trading strategy on the FMZ platform references the template class library of Order Synchronization Management System Class Library (Single Server). Then the strategy can use the following functions.

  • $.PosMonitor
    The purpose of this function is to monitor the position changes of the exchange objects in the strategy and then send trading signals to the real bot market set in the parameters of the template: Order Synchronization Management System class library (Single Server).
  • $.getTbl
    Return to the monitored synchronization data.

Example of use is in the main function of the Order Synchronization Management System Class Library (Single Server) template:

// Example of the strategy call that references the template class library
function main() {
    // Clear all logs
    LogReset(1)

    // Switch to OKEX demo to test
    exchanges[0].IO("simulate", true)

    // Set the contract
    exchanges[0].SetCurrency("ETH_USDT")
    exchanges[0].SetContractType("swap")

    // Timed trading interval
    var tradeInterval = 1000 * 60 * 3        // Trade for every three minutes to observe the order-following signals
    var lastTradeTS = new Date().getTime()

    while (true) {
        // Other logic of the strategy...

        // Simulated trading triggers for testing
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("Trade the simulation order-leading strategies, position changes", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // Interface functions by using templates
        $.PosMonitor(0, "ETH_USDT", "swap")    // Multiple monitors can be set up to monitor different exchange objects on the order-following strategy  
        var tbl = $.getTbl()

        // Display status bar
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

A template class library can also create a strategy real bot by itself, which is usually used to test the template class library, such as the testing of the template. You can deem that the main function in a template is the main function of one of your own strategies.

The test code is written as using the OKEX demo to test, you need to configure the OKEX demo API KEY on FMZ as a reference account (order-leading), and it starts switching to demo in the main function. Then set the trading pair to ETH_USDT and set the contract to swap. Then it enters a while loop. In the loop, an order is placed every 3 minutes to simulate the triggering of strategy transactions. $.PosMonitor(0, "ETH_USDT", "swap") is called in the while loop, the first parameter of this function is passed to 0, which means to monitor thes exchange object exchanges[0], monitor ETH_USDT trading pair, swap contract. Then it will call $.getTbl() to get chart information, using LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`") to make the chart data displayed on the status bar.

So we can see that we can make the strategy have the ability to monitor the positions of a certain species, and the position changes to send messages by using $.PosMonitor(0, "ETH_USDT", "swap") in a strategy that references the template.

Before testing, we will explain the strategy parameters design of the Order Synchronization Management System Class Library (Single Server).
We have just talked about how to use the interface function of a template to upgrade a strategy to have a function of order-leading. What about the signal sent when the position changes, to whom will it sent?
The question of whom to send is configured by the parameters of the Order Synchronization Management System Class Library (Single Server).

We can see that there are 5 parameters, supporting up to 5 pushes (it can be extended by themselves if it need increase), the default parameters are empty strings, that is, not processed. Configuration string format: label,robotId,accessKey,secretKey

  • label
    A label for a sync account, it is used to set a label for an account with a name that can be set at will.
  • robotId
    Robot ID, the ID of the Order Synchronous Server real bot created by the owner of the synchronous account.
  • accessKey
    Extended API accessKey of FMZ
  • secretKey
    Extended API secretKey of FMZ

The temporary code of Order Synchronization Management System (Synchronous Server):

function main() {
    LogReset(1)
    while (true) {
        var cmd = GetCommand()
        if (cmd) {
            // cmd: ETH_USDT,swap,buy,1
            Log("cmd: ", cmd)
        }
        Sleep(1000)
    }
}

We can see that the real bot of synchronized account owner received the message: ETH_USDT,swap,buy,1.
Then it will allow us to make our own automatic order-following in the next step based on the tradiing pairs, contract codes, trade directions, and amount in the information.

So far, the Order Synchronization Management System (Synchronous Server) is the temporary code, we will continue to explore its design in the next issue.

Leave a Reply

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