In the previous articles on FMZ Digest, we have designed several order and position synchronous strategies.

These designs take the reference account and the synchronous account into the same strategy to be managed to realize the synchronization of orders and positions. Today, we can try a different design; based on the powerful extended API interface of FMZ, here we design an order synchronous management system.

Design Thinking

First of all, we need some good suggestions and requirements. The above two previous orders and positions synchronization strategies have several obvious disadvantages. Let's discuss them together:

  • 1.The users who implement the strategy bot synchronization must have the API KEY of the reference account platform and the API KEY of the synchronization account.
    For the usage scenario, the problem is: it is fine for your other platform accounts to follow one of your own accounts. But it can be troublesome for the scenario where the reference account and the synchronization account do not have the same owner. The owner of the synchronization account is sometimes unwilling to provide the API KEY of his platform account due to security considerations. But how to place an order for trading synchronously without providing the API KEY? Solution:
    Use the FMZ extended API, the owner of the synchronization account (the order supervisor) only needs to register the FMZ Quant trading platform, and then run a strategy (in the system designed in this article: Order Synchronous Management System (Synchronous Server) strategy bot). Then, the extended API KEY of FMZ (note that it is not the API KEY of the platform account) and the bot ID of the order synchronization management system (Synchronous Server) will be provided to the owner of the reference account (the order owner).
    When the bot of the reference account owner (the order owner) (Order Synchronous Management System Library (Single Server) in the system designed in the article) sends a signal, the synchronization account owner's bot will receive the trading signal. The order will be automatically placed later.
  • 2.Many developers have better strategies and cannot use the two previous order and position synchronization strategies described above. Because that needs to merge their own strategies with these synchronization strategies, and their strategies may need to be greatly modified, which is time-consuming and labor-intensive. Is there a good way to directly upgrade some of your mature strategies to those with the order synchronization function? Solution:
    You can design an order synchronous template library (the Order Synchronous Management System Library (Single Server) strategy in the system designed in the article), so that the owner of the reference account (the order owner) can directly insert this template library in his own strategy to achieve order and position synchronization.
  • 3.Reduce an extra bot
    The last disadvantage is that if you use the two previous orders and position synchronization strategies described above, it is necessary to open an additional position of the reference account (account with orders) for bot monitoring.
    Solution:
    Use the template library to embed the function into the reference account strategy.

Therefore, the system consists of two parts:
1.order synchronous management system library (Single Server)
2.order synchronous management system (Synchronous Server)

Once we make sure our demands, let's start to design!

Design 1: Order Synchronous Management System Library (Single Server)

Pay attention that here it is not a strategy, but a FMZ template library, which can be searched in FMZ API ducumentation and we will not discuss here.

Template code:

// Global variable
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 position
        var tradeDirection = type == PD_LONG ? "buy" : "sell"
        // Send signal
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)        
    } else if (delta < 0) {
        // Open position 
        var tradeDirection = type == PD_LONG ? "closebuy" : "closesell"
        if (nowAmount <= 0) {
            Log("No position detected")
            return 
        }
        // Send signal 
        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 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 support futures order supervising"
    }

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

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

        // Monitor position 
        var keyInitRefPosAmount = "refPos-" + exIndex + "-" + symbol + "-" + ct    // refPos-exIndex-symbol-contractType
        var initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        if (!initRefPosAmount) {
            // The data is not initialized; initialize it          
            mapInitRefPosAmount[keyInitRefPosAmount] = getPosAmount(_C(ex.GetPosition), ct)
            initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        }

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

        // Detect changes 
        if (!(longPosDelta == 0 && shortPosDelta == 0)) {
            // Execute long position action 
            if (longPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "Execute long position order supervision, change volume:", longPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_LONG, longPosDelta)
            }
            // Execute short position action 
            if (shortPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "Execute short position order supervision, change volume:", shortPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_SHORT, shortPosDelta)
            }

            // After executing the order supervision operation, update  
            mapInitRefPosAmount[keyInitRefPosAmount] = nowRefPosAmount
        }

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

$.getTbl = function() {
    var tbl = {
        "type" : "table", 
        "title" : "Synchrodata", 
        "cols" : [], 
        "rows" : []
    }
    // Construct the table title 
    tbl.cols.push("Monitoring account:refPos-exIndex-symbol-contractType")
    tbl.cols.push(`Mintoring position:{"timestamp":xxx,"long position volume":xxx,"short position volume":xxx}`)
    _.each(fmzExtendApis, function(extendApiData, index) {
        tbl.cols.push(keyName_robotId + "-" + index)
    })

    // Write in the data 
    _.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
}

// Invocation example of the strategy in the template
function main() {
    // Clear all logs
    LogReset(1)

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

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

    // Timed trading time interval
    var tradeInterval = 1000 * 60 * 3        // trade every three minutes, to observe the order supervising signal  
    var lastTradeTS = new Date().getTime()

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

        // Used to test the simulated trade trigger  
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("Simulated strategy with orders has trades, and positions changed", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // Call the interface function in the template 
        $.PosMonitor(0, "ETH_USDT", "swap")    // You can set multiple monitors, to minitor different exchange objects in the strategy with orders
        var tbl = $.getTbl()

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

The design is very simple, the library has 2 functions. When a program trading strategy on the FMZ platform calls the Order Synchronous Management System Library (Single Server) template class library. This strategy can then use the following functions.

  • $.PosMonitor
    The effect of this function is to monitor the position changes of the exchange objects in the strategy, and then send trading signals to the bot set in the parameters of the template: order synchronous management system library (Single Server).
  • $.getTbl
    The function returns the monitored synchronous data.

The using example is at the main function in the order synchronous management system library (Single Server):

// Invocation example of the strategy in the template 
function main() {
    // Clear all logs 
    LogReset(1)

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

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

    // Timed trading time interval
    var tradeInterval = 1000 * 60 * 3        // trade every three minutes, to observe the order supervising signal  
    var lastTradeTS = new Date().getTime()

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

        // Used to test the simulated trade trigger  
        var ts = new Date().getTime()
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("Simulated strategy with orders has trades, and positions changed", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // Use the interface function of the template 
        $.PosMonitor(0, "ETH_USDT", "swap")    // You can set multiple monitors to monitor different exchange objects on an strategy with orders
        var tbl = $.getTbl()

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

A template library itself can also create a strategy bot, which is usually used to test the template library. For example, a test for this template. You can understand that the main function in the template is the main function of one of your own strategies.

The test code is written to use OKEX simulated bot to test, and the API KEY of OKEX simulated bot needs to be configured on FMZ as a reference account (with orders), and the main function starts to switch to the simulated bot. Then set the trading pair to ETH_USDT, and set the contract to swap. Then enter a while loop. In the loop, an order is placed every 3 minutes to simulate the trigger of the strategy trades. $.PosMonitor(0, "ETH_USDT", "swap") is called in the while loop, and the first parameter of the called function is 0, which means to monitor the exchange object exchanges[0], the trading pair ETH_USDT, and swap contract. Then call $.getTbl() to get the chart information, and use LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`") to make the chart data displayed on the status bar.

So you see, as long as $.PosMonitor(0, "ETH_USDT", "swap") is used in a strategy that calls the template, the strategy can have the function of monitoring a certain symbol position and pushing the position change message.

Before the test, explain the parameter design of the Order Synchronous Management System Library (Single Server) strategy:
I just talked about how to use the interface function of the template to make a strategy upgrade with a function of carrying orders. So who is the signal sent when the position changes?
The question of who to send to is configured by the parameters of order synchronous management system library (Single Server).

/upload/asset/2688238f91c3fc5792b52.png

You can see five parameters, which can maximum support five pushes (if you need to increase the push number, you can extend it by yourself); the default of the parameters is an empty string, namely not processed. In the configured string format: label, robotId, accessKey, secretKey

  • label
    The label of the synchronous account, used to label an account; the label name can be set randomly.
  • robotId
    The bot ID; the bot ID of the order synchronous management system (Synchronous Server) created by the synchronnous account owner.
  • accessKey
    The accessKey of FMZ extended API.
  • secretKey
    The secretKey of FMZ extended API.

Then, we can operate a simple test.

Order Synchronous Management System Library (Single Server) bot operation:

/upload/asset/17049758dc6089a338b1.png

Order Synchronous Management System (Synchronous Server) bot received the signal:
Order Synchronous Management System (Synchronous Server) now has not been completely designed by us, and we can use a simple code to realize it, without trading, only printing signal:

Order Synchronous Management System (Synchronous Server) temporary code:

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

/upload/asset/171c8303caf70b252bdd.png

As you can see, the bot of the synchronous account owner received the message: ETH_USDT,swap,buy,1.
Thus, in next step, we can supervise orders automatically, according to the trading pair, contract code, trading direction and volume.

At present, order synchronous management system (Synchronous Server) is only temporary code, and we can further discuss its design in next article.

Leave a Reply

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