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

详解发明者量化交易平台API升级:提升策略设计体验

Author: 发明者量化-小小梦, Created: 2024-06-28 09:08:29, Updated: 2024-07-24 12:00:40

[TOC]

img

前言

发明者量化交易平台经过9年的技术迭代,进行了多次重构,虽然作为用户的我们可能未曾察觉。在过去的两年中,平台在用户体验方面进行了大量优化和升级,包括全面升级UI界面,丰富常用量化交易工具,以及增加更多回测数据支持。

为了让策略设计更加便捷、交易逻辑更加清晰,且初学者更容易上手,平台升级了策略使用的API接口。使用最新版本的托管者即可启用这些新功能。平台依然最大程度地兼容旧接口的调用。有关API接口新增功能等信息已同步更新至发明者量化交易平台的API文档:

语法手册:https://www.fmz.com/syntax-guide 用户指南:https://www.fmz.com/user-guide

那么通过本篇让我们一起来速览都有哪些接口升级更新以及对于老旧策略使用时需要哪些改动以兼容当前API。

1、API接口新增

新增exchange.GetTickers函数

对于设计多品种策略、全市场行情监控策略。这种聚合行情接口是必不可少的,为了让策略更加易于开发,避免重复造轮子。发明者量化交易平台封装了交易所这类API。

如果交易所没有这种接口(个别交易所),调用exchange.GetTickers()时会报错:不支持。

该函数没有任何参数,会返回交易所聚合行情接口中所有品种的实时行情数据。可以简单理解为:

exchange.GetTickers()函数是exchange.GetTicker()函数的全品种请求版本(仔细看,这两个函数名区分就是单复数而已)。

我们使用OKX现货模拟盘环境测试:

function main() {
    exchange.IO("simulate", true)

    var tickers = exchange.GetTickers()
    if (!tickers) {
        throw "tickers error"
    }

    var tbl = {type: "table", title: "test tickers", cols: ["Symbol", "High", "Open", "Low", "Last", "Buy", "Sell", "Time", "Volume"], rows: []}
    for (var i in tickers) {
        var ticker = tickers[i]
        tbl.rows.push([ticker.Symbol, ticker.High, ticker.Open, ticker.Low, ticker.Last, ticker.Buy, ticker.Sell, ticker.Time, ticker.Volume])
    }

    LogStatus("`" + JSON.stringify(tbl) +  "`")
    return tickers.length
}

img

新增exchange.CreateOrder函数

新增的exchange.CreateOrder()函数可谓是此次升级中的重点,exchange.CreateOrder()函数最大的功能是直接在该函数的参数中指定下单的品种、方向。这样就不再依赖系统当前设置的交易对、合约代码、交易方向等设置了。

在多品种交易下单场景中、并发场景中极大程度的降低了设计复杂度。exchange.CreateOrder()函数的四个参数分别是symbolsidepriceamount

使用OKX期货模拟盘环境测试:

function main() {
    exchange.IO("simulate", true)

    var id1 = exchange.CreateOrder("ETH_USDT.swap", "buy", 3300, 1)
    var id2 = exchange.CreateOrder("BTC_USDC.swap", "closebuy", 70000, 1)
    var id3 = exchange.CreateOrder("LTC_USDT.swap", "sell", 110, 1)

    Log("id1:", id1, ", id2:", id2, ", id3:", id3)
}

img

这样只用了3次exchange.CreateOrder()函数调用就下了三个不同品种、不同方向的期货订单。

新增exchange.GetHistoryOrders函数

新增的exchange.GetHistoryOrders()函数用来获取某个品种的历史成交订单,这个函数也需要交易所接口支持。

对于查询历史订单来说,各个交易所实现的接口差别非常大:

  • 有些支持分页查询,有些则不支持;
  • 有些交易所有查询窗口期,即超过N天的订单无法查询到;
  • 大部分交易所支持指定时间查询,有些则不支持;

对于这类接口以最大的兼容程度去封装,实际使用中需要注意是否符合策略的需求、期望。

详细的函数说明这里不再赘述,可以查阅API文档中语法手册:

https://www.fmz.com/syntax-guide#fun_exchange.gethistoryorders

使用币安现货实盘环境测试:

function main() {
    var orders = exchange.GetHistoryOrders("ETH_USDT")

    // 写入图表
    var tbl = {type: "table", title: "test GetHistoryOrders", cols: ["Symbol", "Id", "Price", "Amount", "DealAmount", "AvgPrice", "Status", "Type", "Offset", "ContractType"], rows: []}
    for (var order of orders) {
        tbl.rows.push([order.Symbol, order.Id, order.Price, order.Amount, order.DealAmount, order.AvgPrice, order.Status, order.Type, order.Offset, order.ContractType])
    }
    
    LogStatus("orders.length:", orders.length, "\n", "`" + JSON.stringify(tbl) +  "`")
}

img

新增exchange.GetPositions函数

旧版本的获取持仓数据函数为exchange.GetPosition()。此次升级为了更加贴合函数命名语义增加了新的获取持仓函数:exchange.GetPositions()。同时依然兼容/升级GetPosition函数。

exchange.GetPositions()函数有三种调用形式:

  • exchange.GetPositions() 当不传任何参数时,根据当前交易对/合约代码设置,请求持仓数据。
  • exchange.GetPositions(“ETH_USDT.swap”) 当指定具体品种信息时(ETH_USDT.swap这种格式由FMZ平台定义),请求具体品种的持仓数据。
  • exchange.GetPositions("") 请求交易所持仓接口可获取的当前维度的全部持仓数据。(根据交易所接口产品维度划分)

使用OKX期货模拟盘环境测试:

function main() {
    exchange.IO("simulate", true)

    exchange.SetCurrency("BTC_USDT")
    exchange.SetContractType("swap")

    var p1 = exchange.GetPositions()
    var p2 = exchange.GetPositions("")

    var tbls = []
    for (var positions of [p1, p2]) {
        var tbl = {type: "table", title: "test GetPosition/GetPositions", cols: ["Symbol", "Amount", "Price", "FrozenAmount", "Type", "Profit", "Margin", "ContractType", "MarginLevel"], rows: []}
        for (var p of positions) {
            tbl.rows.push([p.Symbol, p.Amount, p.Price, p.FrozenAmount, p.Type, p.Profit, p.Margin, p.ContractType, p.MarginLevel])
        } 
        tbls.push(tbl)
    }

    LogStatus("`" + JSON.stringify(tbls) +  "`")
}

img

当传入exchange.GetPositions()函数的参数是ETH_USDT.swap时,就可以获取ETH的U本位永续合约的持仓数据。

img

当传入exchange.GetPositions()函数的参数是空字符串""时,就可以获取U本位所有的合约的持仓数据,是不是很方便。

2、API接口升级

升级exchange.GetTicker函数

行情函数exchange.GetTicker()本次升级主要是增加了symbol参数。使得该函数可以脱离当前交易对、合约代码直接按照参数指定的品种信息,请求行情数据。简化了代码编写过程。同时依然兼容不传参的调用方式,最大程度兼容平台旧策略。

参数symbol对于交易所对象exchange是现货/期货有不同的格式:

  • 现货交易所对象 格式为:AAA_BBB,AAA表示为baseCurrency即交易币种,BBB表示为quoteCurrency即计价币种。币种名称均为大写字母。 例如:BTC_USDT 现货交易对。
  • 期货交易所对象 格式为:AAA_BBB.XXX,AAA表示为baseCurrency即交易币种,BBB表示为quoteCurrency即计价币种,XXX表示为合约代码,例如永续合约swap。币种名称均为大写字母,合约代码为小写。 例如:BTC_USDT.swap ,BTC的U本位永续合约。

使用币安期货实盘环境测试:

var symbols = ["BTC_USDT.swap", "BTC_USDT.quarter", "BTC_USD.swap", "BTC_USD.next_quarter", "ETH_USDT.swap"]

function main() {
    exchange.SetCurrency("ETH_USD")
    exchange.SetContractType("swap")

    var arr = []
    var t = exchange.GetTicker()
    arr.push(t)

    for (var symbol of symbols) {
        var ticker = exchange.GetTicker(symbol)
        arr.push(ticker)
    }

    var tbl = {type: "table", title: "test GetTicker", cols: ["Symbol", "High", "Open", "Low", "Last", "Buy", "Sell", "Time", "Volume"], rows: []}
    for (var ticker of arr) {
        tbl.rows.push([ticker.Symbol, ticker.High, ticker.Open, ticker.Low, ticker.Last, ticker.Buy, ticker.Sell, ticker.Time, ticker.Volume])
    }

    LogStatus("`" + JSON.stringify(tbl) +  "`")
    return arr
}

img

请求一批指定品种的行情数据设计变得更加简单。

升级exchange.GetDepth函数

与GetTicker函数相同exchange.GetDepth()函数此次也增加了symbol参数。可以实现在请求深度数据时直接指定品种。

使用币安期货实盘环境测试:

function main() {
    exchange.SetCurrency("LTC_USD")
    exchange.SetContractType("swap")

    Log(exchange.GetDepth())
    Log(exchange.GetDepth("ETH_USDT.quarter"))
    Log(exchange.GetDepth("BTC_USD.swap"))
}

img

升级exchange.GetTrades函数

与GetTicker函数相同exchange.GetTrades()函数此次也增加了symbol参数。可以实现在请求市场成交数据时直接指定品种。

使用币安期货实盘环境测试:

function main() {
    var arr = []
    var arrR = []
    var symbols = ["LTC_USDT.swap", "ETH_USDT.quarter", "BTC_USD.swap"]    

    for (var symbol of symbols) {
        var r = exchange.Go("GetTrades", symbol)
        arrR.push(r)
    }

    for (var r of arrR) {
        arr.push(r.wait())
    }
    
    var tbls = []
    for (var i = 0; i < arr.length; i++) {
        var trades = arr[i]
        var symbol = symbols[i]

        var tbl = {type: "table", title: symbol, cols: ["Time", "Amount", "Price", "Type", "Id"], rows: []}
        for (var trade of trades) {
            tbl.rows.push([trade.Time, trade.Amount, trade.Price, trade.Type, trade.Id])
        }

        tbls.push(tbl)
    }

    LogStatus("`" + JSON.stringify(tbls) +  "`")
}

img

此次升级同样兼容了通过exchange.Go()函数并发调用平台API接口时传入symbol参数指定品种信息。

升级exchange.GetRecords函数

GetRecords函数本次做出了较大调整,除了支持symbol参数直接指定请求的K线数据的品种信息。保留了原有的period参数用来指定K线周期,还增加了一个limit参数用来指定请求时期望的K线长度。同时也兼容旧版本的GetRecords函数只传入period周期参数的调用方式。

exchange.GetRecords()函数的调用方式:

  • exchange.GetRecords() 不指定任何参数时请求当前交易对/合约代码对应的品种的K线数据,K线周期是策略回测界面或者实盘时设置的默认K线周期。
  • exchange.GetRecords(60 * 15) 仅指定K线周期参数时,请求当前交易对/合约代码对应的品种的K线数据。
  • exchange.GetRecords(“BTC_USDT.swap”) 仅指定品种信息时,请求指定品种的K线数据,K线周期是策略回测界面或者实盘时设置的默认K线周期。
  • exchange.GetRecords(“BTC_USDT.swap”, 60 * 60) 指定品种信息,指定具体K线周期请求K线数据。
  • exchange.GetRecords(“BTC_USDT.swap”, 60, 1000) 指定品种信息,指定具体K线周期,指定期望获取的K线长度请求K线数据。 注意当limit参数超过交易所一次请求的最大长度时,会产生分页请求(即多次调用交易所K线接口)。

使用币安期货实盘环境测试:

function main() {
    exchange.SetCurrency("ETH_USDT")
    exchange.SetContractType("swap")
    
    var r1 = exchange.GetRecords()
    var r2 = exchange.GetRecords(60 * 60)
    var r3 = exchange.GetRecords("BTC_USDT.swap")
    var r4 = exchange.GetRecords("BTC_USDT.swap", 60)
    var r5 = exchange.GetRecords("LTC_USDT.swap", 60, 3000)

    Log("r1相邻Bar时间差值:", r1[1].Time - r1[0].Time, "毫秒, Bar长度:", r1.length)
    Log("r2相邻Bar时间差值:", r2[1].Time - r2[0].Time, "毫秒, Bar长度:", r2.length)
    Log("r3相邻Bar时间差值:", r3[1].Time - r3[0].Time, "毫秒, Bar长度:", r3.length)
    Log("r4相邻Bar时间差值:", r4[1].Time - r4[0].Time, "毫秒, Bar长度:", r4.length)
    Log("r5相邻Bar时间差值:", r5[1].Time - r5[0].Time, "毫秒, Bar长度:", r5.length)
}

img

升级exchange.GetOrders函数

GetOrders函数也增加了symbol参数,可以直接指定查询当前未完成订单(挂单)的品种;也支持查询所有挂单(不分品种);兼容原有的调用方式。

exchange.GetOrders()函数的调用方式有:

  • exchange.GetOrders() 查询当前交易对/合约代码的所有未完成订单。
  • exchange.GetOrders(“BTC_USDT.swap”) 查询BTC的USDT本位永续合约的所有未完成订单。
  • exchange.GetOrders("") 查询交易所中当前维度划分的所有未完成的订单(根据交易所API接口维度划分)。

使用OKX期货模拟盘环境测试:

function main() {
    exchange.IO("simulate", true)

    exchange.SetCurrency("BTC_USDT")
    exchange.SetContractType("swap")

    // 写入图表
    var tbls = []
    for (var symbol of ["null", "ETH_USDT.swap", ""]) {
        var tbl = {type: "table", title: symbol, cols: ["Symbol", "Id", "Price", "Amount", "DealAmount", "AvgPrice", "Status", "Type", "Offset", "ContractType"], rows: []}

        var orders = null
        if (symbol == "null") {
            orders = exchange.GetOrders()
        } else {
            orders = exchange.GetOrders(symbol)
        }

        for (var order of orders) {
            tbl.rows.push([order.Symbol, order.Id, order.Price, order.Amount, order.DealAmount, order.AvgPrice, order.Status, order.Type, order.Offset, order.ContractType])
        }

        tbls.push(tbl)
    }
    
    LogStatus("`" + JSON.stringify(tbls) +  "`")
}

img

不传参数时,默认请求当前BTC_USDT交易对,swap永续合约的所有未完成挂单。

img

指定参数ETH_USDT.swap参数时,请求ETH_USDT交易对的永续合约的所有未完成挂单。

img

传入空字符串""时,请求所有USDT本位合约的所有未完成挂单。

升级exchange.GetPosition函数

依然兼容旧的持仓获取函数命名,同样增加了symbol参数,可以指定具体请求的持仓数据的品种信息。 函数使用方面和exchange.GetPositions()完全一致。

升级exchange.IO函数

对于exchange.IO("api", ...)函数调用方式,对所有交易所对象都升级支持了直接传入完整的请求地址功能。 例如希望调用OKX的接口:

// GET https://www.okx.com /api/v5/account/max-withdrawal ccy: BTC

支持直接写入基地址https://www.okx.com,不必先切换基地址再调用IO函数。

使用OKX期货模拟盘环境测试:

function main() {
    exchange.IO("simulate", true)

    return exchange.IO("api", "GET", "https://www.okx.com/api/v5/account/max-withdrawal", "ccy=BTC")
}

img

3、API接口影响

影响exchange.GetOrder函数

此次升级主要影响了exchange.GetOrder(id)函数的参数id,id参数由原来的交易所订单id改为了一种包含交易品种的字符串格式。 FMZ平台上所有封装的订单Id都为此格式。

例如:

  • 交易所订单中定义的交易所原始订单Id为:123456 此次升级之前,若要调用GetOrder函数,传入的订单Id就是123456
  • 交易所订单中定义的交易所命名的产品代码:BTC-USDT。 注意这里说的是交易所命名的交易品种代码,并不是FMZ平台定义的交易对。

那么此次升级后,exchange.GetOrder(id)函数需要传入的参数id的格式就调整为:BTC-USDT,123456

先说明一下为什么要这样设计: 因为此次升级了CreateOrder函数直接指定品种下单(下单的品种和当前设置的交易对、合约代码有可能不同),如果返回的订单Id中不包含品种信息。那么这个订单Id将无法使用。因为到具体查单的时候不知道这个订单是什么品种(合约)的。大部分交易所查单、撤单都需要指定描述品种代码的参数。

如何兼容这种影响: 如果下单使用的是exchange.IO函数直接调用交易所下单接口去下单的,返回值中一般都有交易所原始symbol(品种代码)和原始订单id。那么把这两者用英文逗号拼接起来就是符合FMZ平台定义的订单Id了。 同样,如果使用的是FMZ平台封装的下单接口下单的,由于订单Id开头部分是交易品种代码,如果需要使用订单原始Id,只需要删除品种代码和逗号即可。

影响exchange.CancelOrder函数

此次升级对于exchange.CancelOrder()函数的影响与exchange.GetOrder()函数相同。

影响exchange.Buy函数

此次升级对于exchange.Buy()函数的影响与exchange.GetOrder()函数相同。 exchange.Buy()函数返回的订单Id为新结构,例如在OKX交易所期货下单时返回的Id:LTC-USDT-SWAP,1578360858053058560

影响exchange.Sell函数

此次升级对于exchange.Sell()函数的影响与exchange.GetOrder()函数相同。 exchange.Sell()函数返回的订单Id为新结构,例如在OKX交易所期货下单时返回的Id:ETH-USDT-SWAP,1578360832820125696

4、结构体调整

Ticker结构体

此次更新给Ticker结构体增加了Symbol字段,该字段记录当前Ticker结构为哪个品种的行情信息。 该字段格式与exchange.GetTicker()函数的symbol参数格式完全一致。

Order结构体

此次更新给Order结构体增加了Symbol字段,该字段格式与exchange.GetTicker()函数的symbol参数格式完全一致。 此次更新也修改了Order结构体的Id字段,以新的订单Id格式记录品种信息、原始订单信息。参考exchange.GetOrder()函数中订单Id的说明,这里不再赘述。

Position结构体

此次更新给Position结构体增加了Symbol字段,该字段格式与exchange.GetTicker()函数的symbol参数格式完全一致。

5、回测系统

此次升级为了满足用户需求,先对实盘进行兼容,回测系统将在一周内完成适配. 如个别策略代码受到影响,请按此文章说明进行更改适配

补充更新

1、Account 结构新增字段Equity、UPnL

对于期货交易所对象的成员函数GetAccount返回的Account结构进行了字段扩充。

  • Equity 当前保证金资产币种的总权益,除了极个别期货交易所不支持,大部分都支持该字段。主要用于计算实时的账户保证金盈亏情况。

  • UPnL 当前保证金资产币种的所有持有仓位的未实现盈亏,除了极个别期货交易所不支持,大部分都支持该字段。

2、SetMarginLevel函数升级symbol参数支持

对于期货交易所对象的成员函数SetMarginLevel进行了升级,增加参数symbol。

测试例子:

function main() {
    exchange.SetCurrency("ETH_USDT")
    exchange.SetContractType("swap")
    
    // 当前交易对为ETH_USDT,合约代码为swap,设置杠杆值为10
    exchange.SetMarginLevel(10)
    
    // 直接指定交易对BTC_USDT,合约代码swap,设置杠杆值20
    exchange.SetMarginLevel("BTC_USDT.swap", 20)
}

More

Wa-emmnn_ 我说我新开的机器人怎么回事,返回ID还带交易对名称,研究了好久,还有下单后缀的log信息现在不显示了,也是因为托管者更新的缘故吗?

张小宝lre /upload/asset/2ffc0f961149326b78aed.png 请问这个问题是这次接口更新导致的吗?

ecnemuse 希望exchange.Buy函数能增加开止损单的功能。。

NanSeg 前排围观

Wa-emmnn_ 好UwU

发明者量化-小小梦 好的,这边测试下看下。感谢您提出问题。这边会尽快处理。

Wa-emmnn_ 对的,extMsg1, extMsg2不显示了。

发明者量化-小小梦 您好,订单ID这个是迫不得已的改动,因为升级了直接指定品种下单,订单ID中就必须包含品种信息,否则无法确定这个订单是哪个品种的,在撤单的时候无法调用(因为大多数交易所撤单的时候需要指定品种,并且指定ID)。 您说的下单后缀信息不显示,是指:exchange.Buy(price, amount, extMsg1, extMsg2)调用时,extMsg1, extMsg2 不在日志上显示吗?

发明者量化-小小梦 您好,您发下当前的交易所设置、交易对、合约代码设置。

发明者量化 请详细发送测试代码与配置到工单,工程师会在第一时间回复您

发明者量化-小小梦 交易所的条件单机制等差别有些大,支持程度各不相同,这个这边调研下看是否可行。

发明者量化-小小梦 感谢支持,如果有使用问题,发工单或留言都可以。