资源加载中... loading...

探索FMZ:状态栏按钮的全新应用(一)

Author: 发明者量化-小小梦, Created: 2024-07-25 15:20:47, Updated: 2024-07-26 16:10:27

img

随着量化交易平台API接口的一次重大更新,平台的策略界面参数、交互控件等功能进行了调整,并新增了许多功能。之前的文章详细介绍了界面参数和交互控件的更新内容。本篇继续探讨FMZ.COM新引入的状态栏按钮的应用。

每位策略开发者都希望构建一个简单易用、功能强大且设计简约的UI界面。为了实现这一标准,FMZ.COM不遗余力地升级平台功能,提升用户体验。直接在策略页面状态栏中设计交互控件,正是基于这一需求的升级。

接下来,让我们看看多品种策略场景下的应用。

多品种策略场景

不论是全自动的多品种套利策略,还是半手动的多品种择时策略。策略UI界面上都会需要有一些功能性的交互按钮,例如对某个既定品种止盈、止损、全部清仓、计划委托等等。

那么我们就以一个最简单的使用场景来探索状态栏按钮的新增功能。假设我们的策略交易多个品种:

BTC_USDT 永续合约, ETH_USDT 永续合约, LTC_USDT 永续合约, BNB_USDT 永续合约, SOL_USDT 永续合约

在策略执行自动交易的同时,我们希望在策略界面状态栏中设计每个品种的开仓按钮。但是这个开仓按钮需要一系列的详细设置,例如:

  • 订单类型:限价单/市价单。
  • 订单的下单量:数量。
  • 订单的价格:价格。
  • 交易方向:买入(多)、卖出(空)。

在本次平台升级之前状态栏按钮仅仅只是触发一个按钮交互消息。没有办法绑定一系列的控件来设置复杂消息。这次对于交互的升级解决了这种需求,我们一起来看一下代码设计,代码中增加了详细注释很容易了解如何构建这样的功能。

设计范例:

// 设置操作的各个品种 BTC_USDT.swap 为FMZ平台定义的品种格式,表示BTC的U本位永续合约
var symbols = ["BTC_USDT.swap", "ETH_USDT.swap", "LTC_USDT.swap", "BNB_USDT.swap", "SOL_USDT.swap"]

// 根据按钮模板构造按钮
function createBtn(tmp, group) {
    var btn = JSON.parse(JSON.stringify(tmp))
    _.each(group, function(eleByGroup) {
        btn["group"].unshift(eleByGroup)
    })

    return btn
}

function main() {
    LogReset(1)

    var arrManager = []
    _.each(symbols, function(symbol) {
        arrManager.push({
            "symbol": symbol,
        })
    })

    // Btn
    var tmpBtnOpen = {
        "type": "button",
        "cmd": "open",
        "name": "开仓下单",
        "group": [{
            "type": "selected",
            "name": "tradeType",
            "label": "下单类型",
            "description": "市价单、限价单",
            "default": 0,
            "group": "交易设置",
            "settings": {
                "options": ["市价单", "限价单"],
                "required": true,
            }
        }, {
            "type": "selected",
            "name": "direction",
            "label": "交易方向",
            "description": "买入、卖出",
            "default": "buy",
            "group": "交易设置",
            "settings": {
                "render": "segment",
                "required": true,
                "options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}],
            }
        }, {
            "type": "number",
            "name": "price",
            "label": "价格",
            "description": "订单的价格",
            "group": "交易设置",
            "filter": "tradeType==1",
            "settings": {
                "required": true,
            }
        }, {
            "type": "number",
            "name": "amount",
            "label": "下单量",
            "description": "订单的下单量",
            "group": "交易设置",
            "settings": {
                "required": true,
            }
        }],
    }

    while (true) {
        var tbl = {"type": "table", "title": "dashboard", "cols": ["symbol", "actionOpen"], "rows": []}
        _.each(arrManager, function(m) {
            var btnOpen = createBtn(tmpBtnOpen, [{"type": "string", "name": "symbol", "label": "交易品种", "default": m["symbol"], "settings": {"required": true}}])
            tbl["rows"].push([m["symbol"], btnOpen])
        })
        
        var cmd = GetCommand()
        if (cmd) {
            Log("收到交互:", cmd)
            
            // 解析交互消息: open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111}
            // 根据第一个冒号:之前的指令判断是哪种按钮模板触发的消息
            var arrCmd = cmd.split(":", 2)
            if (arrCmd[0] == "open") {
                var msg = JSON.parse(cmd.slice(5))
                Log("交易品种:", msg["symbol"], ",交易方向:", msg["direction"], ",订单类型:", msg["tradeType"] == 0 ? "市价单" : "限价单", msg["tradeType"] == 0 ? ",订单价格:当前市价" : ",订单价格:" + msg["price"], ",订单数量:", msg["amount"])
            }
        }

        // 输出状态栏信息
        LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")
        Sleep(1000)
    }
}

先看一下运行效果,在对按钮控件的设计做详细说明。策略代码运行起来如图:

img

点击一个按钮,会出现一个弹框配置具体下单信息:

img

填写我们设计好的开仓信息之后。

img

可以看到策略收到了消息,并且代码中我们对这个消息做了解析,输出这个订单的各项设置。接下来我们就来一起看下这个按钮是如何构造的:

首先我们定义了一个按钮模板,是一个JSON对象,我们把它赋值给变量tmpBtnOpen。我把具体说明直接写在以下代码注释中。

    {
        "type": "button",       // 状态栏输出控件的类型,目前仅支持按钮
        "cmd": "open",          // 当按钮触发时,策略中GetCommand函数收到的消息前缀,例如这个例子:open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111}
        "name": "开仓下单",      // 策略界面,状态栏上的按钮上显示的内容,参考上图
        "group": [{             // 当按钮触发时,弹框中的控件配置设置,这一层的group字段值是一个数组,弹框中的控件根据这个数组顺序自上而下排列
            "type": "selected",               // 第一个控件类型是:selected ,下拉框 
            "name": "tradeType",              // 当状态栏按钮触发时,消息中包含该控件的设置内容,tradeType就是当前这个下拉框控件输入的值的键名。如果选择第一个选项「市价单」GetCommand函数收到的消息中就包含 "tradeType":0 的键值对信息。
            "label": "下单类型",               // 按钮触发时,弹框中当前控件的标题
            "description": "市价单、限价单",    // 当前控件的说明信息,鼠标放在控件右侧“小问号”图标上就会显示这个说明信息。
            "default": 0,                     // 当前控件的默认值,例如当前是下拉框控件,如果不做选择,默认为下拉框第一个选项,通常下拉框的默认值指的是下拉框选项的索引,即第一个是0,然后是1,以此类推。如果下拉框的选项options是key-value形式则默认值指的是value。
            "group": "交易设置",               // 弹框中的控件如果很多,可以分组,这个字段可以设置分组信息
            "settings": {                     // 当前这个下拉框的具体设置
                "options": ["市价单", "限价单"],   // options和下拉框相关的设置,用于设置下拉框中的选项,这个字段值是一个数组,依次排列下拉框中的选项。
                "required": true,                // required表示是否设置为必选(必填)内容。
            }
        }, {
            "type": "selected",         // 这也是一个下拉框类型
            "name": "direction",
            "label": "交易方向",
            "description": "买入、卖出",
            "default": "buy",
            "group": "交易设置",
            "settings": {
                "render": "segment",   // 和默认下拉框控件不同的是,通过render字段可以把下拉框替换为“分段选择器”,如上图中的“买入/卖出”控件。
                "required": true,
                "options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}],   // 使用 key-value方式设置options
            }
        }, {
            "type": "number",           // 数值输入框类型控件
            "name": "price",
            "label": "价格",
            "description": "订单的价格",
            "group": "交易设置",
            "filter": "tradeType==1",   // 过滤器,可以用于确定是否显示当前控件,当tradeType==1时,表示是市价单,所以不需要该控件设置价格,所以不显示。
            "settings": {
                "required": true,       // 当该控件激活显示时为必选(必填)
            }
        }, {
            "type": "number",
            "name": "amount",
            "label": "下单量",
            "description": "订单的下单量",
            "group": "交易设置",
            "settings": {
                "required": true,
            }
        }],
    }
  • group 因为这里只是举出一个例子,实际设计使用中可能有更多需求,不局限于开仓时设置的订单方向、价格、数量、订单类型。还可能有止盈、止损计划委托等出场规则的设计。所以新版的UI支持了group字段,方便把弹框中一组控件划归在一起显示,例如以上截图中「交易设置」折叠设置。

  • required 按钮结构中group字段里设置的控件增加了required设置字段,用来设置是否为必选(必填),如果设置为必选(必填)但是在使用中没有填写(选择)就无法点击确定按钮发送交互信息,并且有红色提示信息显示。

  • filter 增加了filter字段用于设置过滤依赖,例如以上例子中如果选择了市价单类型下单,那么订单价格就是非必要的,可以把这个type为"number",name为"price"的控件隐藏。

  • render 对于这几种基础类型的控件(type字段设置):number, string, selected, boolean。增加了字段render用于设置控件渲染,每种控件都有各自的多种渲染组件。例如以上例子中把selected这种下拉框控件渲染为「分段选择器」比较合适,因为下拉框需要点击两次(第一次展开下拉框,第二次选择选项),使用分段选择器组件,只用点击一下,选择需要的选项即可。

最后细心的读者可能会问,上面截图中没有看到你写「交易品种」这个弹框中的控件信息呀,并且这个「交易品种」是不属于「交易设置」分组的(即:"group": "交易设置"这个设置实现)。

这里演示了一种把状态栏表格中按钮绑定状态栏中其它信息的设计,使用createBtn函数根据模板tmpBtnOpen构造最终的按钮结构,构造时把其它信息写入按钮结构。

// 构造按钮的时候,绑定当前行的品种名称等信息,给按钮的弹框增加一个控件,并排在首位
var btnOpen = createBtn(tmpBtnOpen, [{"type": "string", "name": "symbol", "label": "交易品种", "default": m["symbol"], "settings": {"required": true}}])

所以最终实现的效果就是当点击策略界面上状态栏中symbolBNB_USDT.swap这一行的按钮时,弹框中「交易品种」输入框里自动就已经填写好了BNB_USDT.swap

本篇仅仅介绍了新版本UI的一小部分应用,鉴于整体篇幅不能太长,我们下一篇继续讨论其它需求场景的设计。

感谢支持!


More