Design of an order synchronization system based on FMZ quantification (1)

Author: The Little Dream, Created: 2022-02-14 19:46:30, Updated: 2023-09-15 20:44:11

img

Design of an order synchronization system based on FMZ quantification (1)

In previous articles in the FMZ archive, we have designed several orders, storage and synchronization strategies.

These are to put the reference account and the synchronized account in one strategy to manage order execution, hold synchronized. Today we are going to try a slightly different design, based on the powerful extension API interface of the FMZ quantitative trading platform, we are going to design an order synchronization management system.

Design ideas

First of all, we need some good suggestions, needs. The two previous orders, the stock synchronization strategy, have a few obvious pain points, let's discuss:

  • 1. The implementer of the synchronous strategy must have the API KEY of the exchange of the reference account. The problem with this use case is that it's okay for your other exchange account to follow your own account. But it can be a problem for a reference account and a synchronized account that are not the same owner. The owner of a synchronized account is sometimes reluctant to provide the API KEY of their exchange account for security reasons.

    The solution: Using FMZ's extended API interface, the owner of a sync account (the subscriber) only needs to register with the FMZ Quantum Trading Platform and then run a policy (in the system designed for this article):订单同步管理系统(Synchronous Server)The FMZ extension API KEY (note, not the API KEY of the exchange account) and the order synchronous server can then be provided to the owner of the reference account. When referring to the physical disk of the account holder (bandholder) in the system designed for this订单同步管理系统类库(Single Server)In this case, the transaction will be sent to the real disk of the synchronized account holder, which will receive the transaction signal and automatically place orders.

  • 2, Many developers have better strategies and cannot use the two forward orders described above, holding synchronization strategies. Because that requires combining their own strategies with these synchronization strategies, the strategy may require major changes and effort. Is there a good way to directly upgrade some of their established strategies to order synchronization? The solution: You can design an order synchronization template class library (in the system designed for this article)订单同步管理系统类库(Single Server)The policy allows the owner of the reference account to directly embed this template library into their policy to enable order, hold and sync functions.

  • 3⁄4 Reduce one additional disk. The last pain point is that if you use the two forward orders described above, you will need to open an additional holding account for a live monitoring reference account. The solution: Use the template library to embed features in the reference account policy.

So the system consists of two parts: 1 Order synchronization management system class library (single server) 2, Synchronous Server (SMS) for managing orders

Once you know what you need, start designing!

Design 1: Order synchronization management system class library (single server)

Note that this is not a policy. It is a template library for FMZ. The concept of a template library can be searched in the FMZ API documentation, which is not discussed here.

The template class library code is:

// 全局变量
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) {
        // 开仓
        var tradeDirection = type == PD_LONG ? "buy" : "sell"
        // 发送信号
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)        
    } else if (delta < 0) {
        // 平仓
        var tradeDirection = type == PD_LONG ? "closebuy" : "closesell"
        if (nowAmount <= 0) {
            Log("未检测到持仓")
            return 
        }
        // 发送信号
        msg = symbol + "," + ct + "," + tradeDirection + "," + Math.abs(delta)
    } else {
        throw "错误"
    }
    if (msg) {
        _.each(fmzExtendApis, function(extendApiConfig) {
            var ret = sendCommandRobotMsg(extendApiConfig[keyName_robotId], extendApiConfig[keyName_extendAccessKey], extendApiConfig[keyName_extendSecretKey], msg)
            Log("调用CommandRobot接口,", "label:", extendApiConfig[keyName_label], ", msg:", msg, ", ret:", ret)
            Sleep(1000)
        })
    }
}

$.PosMonitor = function(exIndex, symbol, ct) {    
    var ts = new Date().getTime()
    var ex = exchanges[exIndex]
    // 判断ex类型
    var exName = ex.GetName()
    var isFutures = exName.includes("Futures_")
    var exType = isFutures ? "futures" : "spot"
    if (!isFutures) {
        throw "仅支持期货跟单"
    }

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

        // 切换到对应的交易对、合约代码
        ex.SetCurrency(symbol)
        if (!ex.SetContractType(ct)) {
            throw "SetContractType failed"
        }

        // 监控持仓
        var keyInitRefPosAmount = "refPos-" + exIndex + "-" + symbol + "-" + ct    // refPos-exIndex-symbol-contractType
        var initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        if (!initRefPosAmount) {
            // 没有初始化数据,初始化          
            mapInitRefPosAmount[keyInitRefPosAmount] = getPosAmount(_C(ex.GetPosition), ct)
            initRefPosAmount = mapInitRefPosAmount[keyInitRefPosAmount]
        }

        // 监控
        var nowRefPosAmount = getPosAmount(_C(ex.GetPosition), ct)
        // 计算仓位变动
        var longPosDelta = nowRefPosAmount.long - initRefPosAmount.long
        var shortPosDelta = nowRefPosAmount.short - initRefPosAmount.short

        // 检测变动
        if (!(longPosDelta == 0 && shortPosDelta == 0)) {
            // 执行多头动作
            if (longPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "执行多头跟单,变动量:", longPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_LONG, longPosDelta)
            }
            // 执行空头动作
            if (shortPosDelta != 0) {
                Log(ex.GetName(), ex.GetLabel(), symbol, ct, "执行空头跟单,变动量:", shortPosDelta)
                follow(nowRefPosAmount, symbol, ct, PD_SHORT, shortPosDelta)
            }

            // 执行跟单操作后,更新
            mapInitRefPosAmount[keyInitRefPosAmount] = nowRefPosAmount
        }

        // 恢复 symbol ct
        ex.SetCurrency(buffSymbol)
        ex.SetContractType(buffCt)
    } else if (exType == "spot") {
        // 现货
    }
}

$.getTbl = function() {
    var tbl = {
        "type" : "table", 
        "title" : "同步数据", 
        "cols" : [], 
        "rows" : []
    }
    // 构造表头
    tbl.cols.push("监控账户:refPos-exIndex-symbol-contractType")
    tbl.cols.push(`监控持仓:{"时间戳":xxx,"多头持仓量":xxx,"空头持仓量":xxx}`)
    _.each(fmzExtendApis, function(extendApiData, index) {
        tbl.cols.push(keyName_robotId + "-" + index)
    })
    
    // 写入数据
    _.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
}

// 引用该模板类库的策略调用范例
function main() {
    // 清除所有日志
    LogReset(1)

    // 切换到OKEX 模拟盘测试
    exchanges[0].IO("simulate", true)

    // 设置合约
    exchanges[0].SetCurrency("ETH_USDT")
    exchanges[0].SetContractType("swap")

    // 定时交易时间间隔
    var tradeInterval = 1000 * 60 * 3        // 三分钟交易一次,用于观察跟单信号
    var lastTradeTS = new Date().getTime()
    
    while (true) {
        // 策略其它逻辑...

        // 用于测试的模拟交易触发
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("模拟带单策略发生交易,持仓变化", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // 使用模板的接口函数
        $.PosMonitor(0, "ETH_USDT", "swap")    // 可以设置多个监控,监控带单策略上的不同的exchange对象  
        var tbl = $.getTbl()
        
        // 显示状态栏
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

It is very simple in design and has two functional functions. When one of the programmatic trading strategies on the FMZ platform cited订单同步管理系统类库(Single Server)After the template class library. This policy can be used with the following function.

  • I'm not sure. The role of this function is to monitor the change in the holdings of an exchange object in the policy, and then send a real-time trading signal set in the parameters of Template:Order synchronization management system library (Single Server).

  • I'm not sure. The synchronization data returned from the monitoring.

An example of using this is the Order Synchronization Management System class library (Single Server) template.mainIn the function:

// 引用该模板类库的策略调用范例
function main() {
    // 清除所有日志
    LogReset(1)

    // 切换到OKEX 模拟盘测试
    exchanges[0].IO("simulate", true)

    // 设置合约
    exchanges[0].SetCurrency("ETH_USDT")
    exchanges[0].SetContractType("swap")

    // 定时交易时间间隔
    var tradeInterval = 1000 * 60 * 3        // 三分钟交易一次,用于观察跟单信号
    var lastTradeTS = new Date().getTime()
    
    while (true) {
        // 策略其它逻辑...

        // 用于测试的模拟交易触发
        var ts = new Date().getTime()
        if (ts - lastTradeTS > tradeInterval) {
            Log("模拟带单策略发生交易,持仓变化", "#FF0000")
            exchanges[0].SetDirection("buy")
            exchanges[0].Buy(-1, 1)
            lastTradeTS = ts
        }

        // 使用模板的接口函数
        $.PosMonitor(0, "ETH_USDT", "swap")    // 可以设置多个监控,监控带单策略上的不同的exchange对象  
        var tbl = $.getTbl()
        
        // 显示状态栏
        LogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

A template library itself can also create a policy disk, which is often used to test the template library. For example, testing the template.mainSo a function is your own strategy.mainThe function ≠ ∞

The test code was written to test using the OKEX analog disk, requiring the API KEY of the OKEX analog disk to be configured on the FMZ as the reference account (band), start switching to the analog disk in the main function. Then set the transaction pair to ETH_USDT, and set the contract to permanent (swap). Then enter a while loop.$.PosMonitor(0, "ETH_USDT", "swap")The first parameter of this function to be invoked is 0, which indicates that the exchange is monitoring exchanges[0] and that the exchange is monitoring the ETH_USDT transaction pair, the swap contract. The first parameter to be invoked is 0, which indicates that the exchange is monitoring exchanges[0].$.getTbl()How to access and use graph informationLogStatus(_D(), "\n" + "`" + JSON.stringify(tbl) + "`")Let the graph data appear on the status bar.

So you see, as long as it's used in some strategy that's referenced to this template$.PosMonitor(0, "ETH_USDT", "swap")This allows the strategy to have the function of monitoring the holding of a certain variety, the holding changes to push the message.

Explain before you test.订单同步管理系统类库(Single Server)The parameters of the strategy design: We just talked about how to use the interface function of the template to upgrade a policy to have a bandwidth function. The question of who to send it to is up to you.订单同步管理系统类库(Single Server)The parameters are configured.

img

You can see that there are 5 parameters, supporting up to 5 pushes (adding can be self-expanded), the default parameter is an empty string, that is, it is not processed.

  • label Synchronized account tags are used to mark an account, and the name can be set arbitrarily.

  • RobotId is Disk ID, created by the owner of the sync account订单同步管理系统(Synchronous Server)The ID of the real disk.

  • accessKey AccessKey for the FMZ extension API

  • SecretKey The secret key for FMZ's extended API

We can then do a simple test.

The order synchronization management system class library (Single Server) runs on the following disk:

img

The order synchronous management system (Synchronous Server) received a signal: The Synchronous Server is not yet fully designed, but we implemented it with a simple code that does not do transactions, but only prints signals:

The temporary code for the order synchronous 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)
    }
}

img

You can see that the real disk of the owner of the sync account received the following message:ETH_USDT,swap,buy,1I'm not sure. The next step is to automatically keep track of your transactions based on the information, such as pairs, contract codes, direction and quantity of transactions.

Currently订单同步管理系统(Synchronous Server)This is just a temporary code, and we'll continue to explore its design in the next issue.


Related

More

mingxi1005In order to implement a directory, you need two disks, one is a class library disk and one is an order management system disk.

mingxi1005This is a tutorial that shows a configuration error.

AllyWhat parameters need to be changed in the reverse list?

AllyIf you have two disks, one that sends a signal and one that receives a signal, can these two disks be combined together?

The Little DreamYou may not understand the article, this library is a tool that can be embedded directly in the policy line of the user, and then the policy has the policy function, it will send messages to the set up account, and the bot will receive the message. In other words, this is the scenario.

The Little DreamYou can see the article, configuration information: tag, disk ID, accesskey, secretkey. This error should be a mistake, you should check again.

mingxi1005Error configs error!, in the order synchronization management system library (Single Server), both the bandwidth and the 2 KEYs are filled in, and then the real disk is referenced to the order synchronization management system library (Single Server), returning an error, error configs error!

mingxi1005Config error is encountered!

The Little DreamI've been trying to find out exactly what the wrong information is.

The Little DreamI'm not going to say that I'm not.

The Little DreamThe code is open source, you can modify it as needed, and it can be implemented.