教你使用FMZ扩展API批量修改实盘参数

Author: 小小梦, Created: 2023-12-09 20:39:41, Updated: 2023-12-12 21:43:48

img

在FMZ上如何批量修改实盘参数呢?当实盘个数超过几十个,达到上百个时如果一个一个手动配置实盘就非常不便于维护了。这个时候就可以使用FMZ的扩展API来完成这些操作。那么本篇我们就来一起探索群控实盘、更新参数的一些细节。

上篇文章我们解决了如何使用FMZ的扩展API监控所有实盘、群控实盘,给实盘发送指令。依然是使用上篇我们封装的接口调用代码作为基础,继续编写代码,实现批量修改实盘参数。

参数设置:

img

策略代码:

// 全局变量
var isLogMsg = true   // 控制日志是否打印
var isDebug = false   // 调试模式
var arrIndexDesc = ["all", "running", "stop"]
var descRobotStatusCode = ["空闲中", "运行中", "停止中", "已退出", "被停止", "策略有错误"]
var dicRobotStatusCode = {
    "all" : -1,
    "running" : 1,
    "stop" : 4,
}

// 扩展的日志函数
function LogControl(...args) {
    if (isLogMsg) {
        Log(...args)
    }
}

// FMZ扩展API调用函数
function callFmzExtAPI(accessKey, secretKey, funcName, ...args) {
    var params = {
        "version" : "1.0",
        "access_key" : accessKey,
        "method" : funcName,
        "args" : JSON.stringify(args),
        "nonce" : Math.floor(new Date().getTime())
    }

    var data = `${params["version"]}|${params["method"]}|${params["args"]}|${params["nonce"]}|${secretKey}`
    params["sign"] = Encode("md5", "string", "hex", data)
    
    var arrPairs = []
    for (var k in params) {
        var pair = `${k}=${params[k]}`
        arrPairs.push(pair)
    }
    var query = arrPairs.join("&")
    
    var ret = null
    try {
        LogControl("url:", baseAPI + "/api/v1?" + query)
        ret = JSON.parse(HttpQuery(baseAPI + "/api/v1?" + query))
        if (isDebug) {
            LogControl("Debug:", ret)
        }
    } catch(e) {
        LogControl("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
    }
    Sleep(100)  // 控制频率
    return ret 
}

// 获取指定策略Id的所有运行中的实盘信息
function getAllRobotByIdAndStatus(accessKey, secretKey, strategyId, robotStatusCode, maxRetry) {
    var retryCounter = 0
    var length = 100
    var offset = 0
    var arr = []

    if (typeof(maxRetry) == "undefined") {
        maxRetry = 10
    }

    while (true) {
        if (retryCounter > maxRetry) {
            LogControl("超过最大重试次数", maxRetry)
            return null
        }
        var ret = callFmzExtAPI(accessKey, secretKey, "GetRobotList", offset, length, robotStatusCode)
        if (!ret || ret["code"] != 0) {
            Sleep(1000)
            retryCounter++
            continue
        }

        var robots = ret["data"]["result"]["robots"]
        for (var i in robots) {
            if (robots[i].strategy_id != strategyId) {
                continue
            }
            arr.push(robots[i])
        }

        if (robots.length < length) {
            break
        }
        offset += length
    }

    return arr 
}

首先认识FMZ扩展API的RestartRobot函数

当我们需要批量修改实盘参数,然后运行实盘时,这个场景首先有2种情况。

  • 1、实盘已经创建 对于已经创建出来的实盘,重新启动自然是使用FMZ的扩展API接口–RestartRobot函数。
  • 2、实盘还未创建 对于没有创建实盘,就不存在“修改”实盘参数这个概念了,就属于批量创建实盘运行了,这个时候使用的是FMZ的扩展API接口–NewRobot函数。

但是不论是那种方式,接下来的思路以及操作都是大同小异,所以我们就以RestartRobot这个扩展API函数使用来作为范例讲解。

RestartRobot函数使用时有两种方式:

  • 1、只传入实盘ID,不传实盘的参数配置。 这种方式保留实盘停止时的参数配置不变,仅仅是重新启动实盘。
  • 2、传入实盘ID,也传入实盘的参数配置。 这种方式就以新的参数配置启动实盘运行。

第一种方式对于我们需求场景来说没有什么用,因为我们本身需求就是要批量修改大量的实盘参数。所以问题就来了,实盘的参数配置是很复杂的,有交易所对象配置、策略参数配置、K线周期设置等。

不用担心,我们来一一探究。

获取所要操作的实盘信息

在FMZ上,如果要修改一个实盘的参数配置,那么这个实盘一定是非运行状态的。因为只有不在运行状态的实盘才能修改参数配置。不在运行状态的实盘可能处于:

  • 策略停止。
  • 策略有错误,停止。

所以首先我们要获取指定策略的实盘,并且这些实盘是处于停止状态或者有错误停止的状态。

function main() {
    var stopRobotList = getAllRobotByIdAndStatus(accessKey, secretKey, strategyId, 4)
    var errorRobotList = getAllRobotByIdAndStatus(accessKey, secretKey, strategyId, 5)
    var robotList = stopRobotList.concat(errorRobotList)
}

这样我们就拿到了需要修改配置的所有实盘信息,接下来我们就要获取实盘的详细配置。

修改实盘配置参数

例如,我们需要修改参数的实盘策略如下(即策略ID为strategyId变量的策略):

img

img

这个策略有3个参数,作为试验。

修改实盘的策略参数,但是可能我们并不想修改策略的交易所配置,但是对于扩展API接口RestartRobot函数来说,要么不指定任何参数(原封不动只是启动实盘),要么所有参数配置都必须指定。

这也就是接下来我们在使用RestartRobot函数启动实盘之前,必须先使用扩展API接口GetRobotDetail函数获取到实盘当前的配置,然后我们把需要修改的参数部分替换进去,重新构成实盘启动(即调用RestartRobot时要用到的参数)的配置参数,然后重启实盘。

所以接下来我们遍历robotList,并且逐个先获取当前的参数配置,以下代码中/**/注释的部分就是实盘的详细信息,接下来我们需要处理这些数据。

function main() {
    var stopRobotList = getAllRobotByIdAndStatus(accessKey, secretKey, strategyId, 4)
    var errorRobotList = getAllRobotByIdAndStatus(accessKey, secretKey, strategyId, 5)

    var robotList = stopRobotList.concat(errorRobotList)
    _.each(robotList, function(robotInfo) {
        var robotDetail = callFmzExtAPI(accessKey, secretKey, "GetRobotDetail", robotInfo.id)
        
        /*
        {
            "code": 0,
            "data": {
                "result": {
                    "robot": {
                        ...
                        "id": 130350,
                        ...
                        "name": "测试1B",
                        "node_id": 3022561,
                        ...
                        "robot_args": "[[\"pairs\",\"BTC_USDT,ETH_USDT,EOS_USDT,LTC_USDT\"],[\"col\",3],[\"htight\",300]]",
                        "start_time": "2023-11-19 21:16:12",
                        "status": 5,
                        "strategy_args": "[[\"pairs\",\"币种列表\",\"英文逗号间隔\",\"BTC_USDT,ETH_USDT,EOS_USDT,LTC_USDT\"],[\"col\",\"宽度\",\"页面总宽度为12\",6],[\"htight\",\"高度\",\"单位px\",600],[\"$$$__cmd__$$$coverSymbol\",\"平仓\",\"平仓交易对\",\"\"]]",
                        "strategy_exchange_pairs": "[3600,[186193],[\"BTC_USD\"]]",
                        "strategy_id": 131242,
                        "strategy_last_modified": "2023-12-09 23:14:33",
                        "strategy_name": "测试1",
                        ...
                    }
                },
                "error": null
            }
        }
        */

        // 解析交易所配置数据
        var exchangePairs = JSON.parse(robotDetail.data.result.robot.strategy_exchange_pairs)

        // 拿到交易所对象索引、交易对,这些设置是不打算修改的
        var arrExId = exchangePairs[1]
        var arrSymbol = exchangePairs[2]

        // 解析参数配置数据
        var params = JSON.parse(robotDetail.data.result.robot.robot_args)

        // 更新参数
        var dicParams = {
            "pairs" : "AAA_BBB,CCC_DDD",
            "col" : "999",
            "htight" : "666"
        }
        
        var newParams = []
        _.each(params, function(param) {
            for (var k in dicParams) {
                if (param[0] == k) {
                    newParams.push([k, dicParams[k]])  // 构造策略参数,更新上新参数值
                }
            }
        })
        
        // 注意如果数据中有空格需要转码,否则请求的时候会报错
        settings = {
            "name": robotDetail.data.result.robot.name,
            // 策略参数
            "args": newParams,         
            // 策略ID,可以用GetStrategyList方法获取到
            "strategy": robotDetail.data.result.robot.strategy_id,
            // K线周期参数,60即为60秒
            "period": exchangePairs[0],
            // 指定在哪个托管者上运行,不写该属性就是自动分配运行
            "node" : robotDetail.data.result.robot.node_id,
            "exchanges": []
        }
                                
        for (var i = 0 ; i < arrExId.length ; i++) {
            settings["exchanges"].push({"pid": arrExId[i], "pair": arrSymbol[i]})
        }
        Log(settings) // 测试
        var retRestart = callFmzExtAPI(accessKey, secretKey, "RestartRobot", robotInfo.id, settings)
        Log("retRestart:", retRestart)
    })
}

运行这个批量修改参数的策略之后,我的实盘:

  • 测试1A
  • 测试1B

在配置的交易所对象、交易对、K线周期不变的情况下,修改了参数:

实盘页面上自动改为了:

img

并且启动运行。因为我们在上面代码里指定了修改的参数:

        // 更新参数
        var dicParams = {
            "pairs" : "AAA_BBB,CCC_DDD",
            "col" : "999",
            "htight" : "666"
        }

END

对于几十个,上百个实盘的批量修改参数,用这个方法是比较方便的。本例子中是修改为统一的参数,当然您也可以在代码中定制自己的修改规则,给每个实盘指定不同的参数配置。或者指定不同的交易所对象、交易对等。

对于FMZ平台,这些需求都是可以灵活定制实现的,有什么需求思路,欢迎大家留言,我们共同探讨、研究,学习问题的解决方案。


More

allez-z 几十、上百个实盘,光月租就很多钱了

小小梦 有一些使用FMZ扩展API 包装产品的,有很多实盘。