前言
发明者量化交易平台经过9年的技术迭代,进行了多次重构,虽然作为用户的我们可能未曾察觉。在过去的两年中,平台在用户体验方面进行了大量优化和升级,包括全面升级UI界面,丰富常用量化交易工具,以及增加更多回测数据支持。
为了让策略设计更加便捷、交易逻辑更加清晰,且初学者更容易上手,平台升级了策略使用的API接口。使用最新版本的托管者即可启用这些新功能。平台依然最大程度地兼容旧接口的调用。有关API接口新增功能等信息已同步更新至发明者量化交易平台的API文档:
那么通过本篇让我们一起来速览都有哪些接口升级更新以及对于老旧策略使用时需要哪些改动以兼容当前API。
1、API接口新增
exchange.GetTickers函数
对于设计多品种策略、全市场行情监控策略。这种聚合行情接口是必不可少的,为了让策略更加易于开发,避免重复造轮子。发明者量化交易平台封装了交易所这类API。
如果交易所没有这种接口(个别交易所),调用exchange.GetTickers()时会报错:不支持。
该函数没有任何参数,会返回交易所聚合行情接口中所有品种的实时行情数据。可以简单理解为:
exchange.GetTickers()函数是exchange.GetTicker()函数的全品种请求版本(仔细看,这两个函数名区分就是单复数而已)。
我们使用OKX现货模拟盘环境测试:
javascript
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
}
exchange.CreateOrder函数
新增的exchange.CreateOrder()函数可谓是此次升级中的重点,exchange.CreateOrder()函数最大的功能是直接在该函数的参数中指定下单的品种、方向。这样就不再依赖系统当前设置的交易对、合约代码、交易方向等设置了。
在多品种交易下单场景中、并发场景中极大程度的降低了设计复杂度。exchange.CreateOrder()函数的四个参数分别是symbol、side、price、amount。
使用OKX期货模拟盘环境测试:
javascript
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)
}
这样只用了3次exchange.CreateOrder()函数调用就下了三个不同品种、不同方向的期货订单。
exchange.GetHistoryOrders函数
新增的exchange.GetHistoryOrders()函数用来获取某个品种的历史成交订单,这个函数也需要交易所接口支持。
对于查询历史订单来说,各个交易所实现的接口差别非常大:
- 有些支持分页查询,有些则不支持;
- 有些交易所有查询窗口期,即超过N天的订单无法查询到;
- 大部分交易所支持指定时间查询,有些则不支持;
对于这类接口以最大的兼容程度去封装,实际使用中需要注意是否符合策略的需求、期望。
详细的函数说明这里不再赘述,可以查阅API文档中语法手册:
https://www.fmz.com/syntax-guide#fun_exchange.gethistoryorders
使用币安现货实盘环境测试:
javascript
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) + "`")
}
exchange.GetPositions函数
旧版本的获取持仓数据函数为exchange.GetPosition()。此次升级为了更加贴合函数命名语义,增加了新的获取持仓函数:exchange.GetPositions()。同时依然兼容/升级GetPosition函数。
注意,两个函数名称只是差别一个结尾的s,因为GetPositions更加符合语义,所以建议后续都使用GetPositions。
exchange.GetPositions()函数有三种调用形式:
-
exchange.GetPositions()
当不传任何参数时,根据当前交易对 / 合约代码的设置,请求当前维度的全部品种的持仓数据。 -
exchange.GetPositions("ETH_USDT.swap")
当指定具体品种信息时(ETH_USDT.swap这种格式由FMZ平台定义),请求具体品种的持仓数据。
举例:BTC_USD.swap、ETH_USDT.swap、ETH_USDT.quarter等。
BTC_USD.swap:BTC的币本位永续合约。
ETH_USDT.swap:ETH的U本位永续合约。
ETH_USDC.swap:ETH的USDC本位永续合约。 (除了USDT,还可以指定不同的quoteCurrency,不再赘述)
ETH_USDT.quarter:ETH的U本位季度交割合约。
BTC_USD.BTC-USD-201226-24250-C:BTC的币本位期权合约。 -
exchange.GetPositions("USDT.swap")
根据指定的维度范围,请求全部品种的持仓数据。
USDT.swap:U本位永续合约范围。
USDT.futures:U本位交割合约范围。
USDC.swap:USDC本位永续合约范围。 (除了USDT,还可以指定不同的quoteCurrency,不再赘述)
USDC.futures:USDC本位交割合约范围。
USD.swap:币本位永续合约范围。
USD.futures:币本位交割合约范围。
USDT.option:U本位期权合约范围。
USD.option:币本位期权合约范围。一些特殊的交易所合约维度划分:
USDT.futures_combo:Futures_Deribit交易所的差价组合合约。
USD.futures_ff:Futures_Kraken交易所的混合保证金交割合约。
USD.swap_pf:Futures_Kraken交易所的混合保证金永续合约。对于交易所API接口不支持的维度,调用时会报错返回空值。
使用OKX期货模拟盘环境测试:
javascript
function main() {
exchange.IO("simulate", true)
exchange.SetCurrency("BTC_USDT")
exchange.SetContractType("swap")
var p1 = exchange.GetPositions()
var p2 = exchange.GetPositions("BTC_USDT.swap")
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) + "`")
}
当传入exchange.GetPositions()函数的参数是ETH_USDT.swap时,就可以获取ETH的U本位永续合约的持仓数据。
当不传入exchange.GetPositions()函数的参数时,就可以获取交易所上线的所有U本位永续合约的持仓数据(因为当前交易对为BTC_USDT,合约为swap,按照当前交易对、合约维度范围请求),此时等价于调用exchange.GetPositions("USDT.swap"),指定一个请求范围。
exchange.GetFundings函数
新增的GetFundings函数可以获取期货交易所永续合约的资金费率。函数有一个参数symbol。函数返回一个Funding结构数组。
- 指定symbol参数:返回指定品种的资金费率信息结构数组(Funding数组),symbol参数可以设置一个范围,类似GetOrders/GetPositions函数的symbol参数。
- 不指定symbol参数:按照当前交易对、合约代码所在维度返回当前维度所有品种数据,机制如同GetOrders/GetPositions函数。
- 如果交易所必须指定具体品种,则必须传入symbol参数,即具体品种的代码,例如:
BTC_USDT.swap。不传参数或者传入范围函数会报错symbol参数不支持。
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本位永续合约。
使用币安期货实盘环境测试:
javascript
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
}
请求一批指定品种的行情数据设计变得更加简单。
exchange.GetDepth函数
与GetTicker函数相同exchange.GetDepth()函数此次也增加了symbol参数。可以实现在请求深度数据时直接指定品种。
使用币安期货实盘环境测试:
javascript
function main() {
exchange.SetCurrency("LTC_USD")
exchange.SetContractType("swap")
Log(exchange.GetDepth())
Log(exchange.GetDepth("ETH_USDT.quarter"))
Log(exchange.GetDepth("BTC_USD.swap"))
}
exchange.GetTrades函数
与GetTicker函数相同exchange.GetTrades()函数此次也增加了symbol参数。可以实现在请求市场成交数据时直接指定品种。
使用币安期货实盘环境测试:
javascript
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) + "`")
}
此次升级同样兼容了通过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线接口)。
使用币安期货实盘环境测试:
javascript
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)
}
exchange.GetOrders函数
GetOrders函数也增加了symbol参数,可以指定具体品种,查询该品种的未完成订单(挂单);也支持查询指定维度范围的全部品种的未完成订单(挂单)。
exchange.GetOrders()函数的调用方式有:
- exchange.GetOrders()
对于期货交易所:当不传任何参数时,根据当前交易对 / 合约代码的设置,请求当前维度范围的全部品种的所有未完成订单(挂单)。
对于现货交易所:当不传任何参数时,请求所有现货品种的未完成订单(挂单)。 - exchange.GetOrders("BTC_USDT.swap") 或者 exchange.GetOrders("BTC_USDT")
对于期货交易所:exchange.GetOrders("BTC_USDT.swap"),查询BTC的USDT本位永续合约的所有未完成订单(挂单)。
对于现货交易所:exchange.GetOrders("BTC_USDT"),查询BTC_USDT现货交易对的所有未完成订单(挂单)。 - 仅对于期货交易所支持 exchange.GetOrders("USDT.swap") 指定维度范围请求全部品种的未成交订单(挂单)
维度划分范围与GetPositions函数中的范围一致。
例如:exchange.GetOrders("USDT.swap") 请求U本位永续合约范围所有品种的未成交订单(挂单)。
使用OKX期货模拟盘环境测试:
javascript
function main() {
exchange.IO("simulate", true)
exchange.SetCurrency("BTC_USDT")
exchange.SetContractType("swap")
// 写入图表
var tbls = []
for (var symbol of ["null", "ETH_USDT.swap", "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) + "`")
}
- 不传参数时,请求当前交易对(BTC_USDT),合约代码(swap)的维度范围的全部品种的未完成订单(挂单)。
- 指定参数
ETH_USDT.swap参数时,请求ETH的USDT本位永续合约的未完成订单(挂单)。 - 传入字符串
"USDT.swap"时,请求所有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期货模拟盘环境测试:
javascript
function main() {
exchange.IO("simulate", true)
return exchange.IO("api", "GET", "https://www.okx.com/api/v5/account/max-withdrawal", "ccy=BTC")
}
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。
exchange.GetPosition函数
仅期货交易所对象支持该函数,对于获取持仓数据函数exchange.GetPosition(),新增exchange.GetPositions()命名,两者行为完全一致。
旧定义:exchange.GetPosition()函数,不指定任何参数调用时,获取的是当前交易对、合约代码设置的具体合约的持仓数据。
调整、改动后,新定义:exchange.GetPosition()函数,不指定任何参数调用时,获取当前设置交易对、合约代码确定的维度范围的所有品种的持仓。
例如,当前交易对为BTC_USDT,合约代码为swap。此时调用:
javascript
exchange.GetPosition() // 等价于调用 exchange.GetPosition("USDT.swap")
该函数会请求所有币种的U本位永续合约的持仓数据。
exchange.GetOrders函数
1、对于现货交易所:
旧定义:exchange.GetOrders()函数,不指定任何参数调用时,获取的是当前交易对的所有未完成订单。
调整、改动后,新定义:exchange.GetOrders()函数,不指定任何参数调用时,获取的是所有现货交易对品种的未完成订单。
2、对于期货交易所:
旧定义:exchange.GetOrders()函数,不指定任何参数调用时,获取的是当前交易对、合约代码设置的具体合约的所有未完成订单。
调整、改动后,新定义:exchange.GetOrders()函数,不指定任何参数调用时,获取当前设置交易对、合约代码确定的维度范围的所有未完成订单。
例如,当前交易对为BTC_USD,合约代码为quarter。此时调用:
javascript
exchange.GetOrders() // 等价于调用 exchange.GetOrders("USD.futures")
该函数会请求所有币种的币本位交割合约的未完成订单数据。
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参数格式完全一致。
Funding结构体
GetFundings函数返回一个Funding结构体数组。
{
"Info": {...}, // 交易所资金费率接口原始应答数据
"Symbol": "BTC_USDT.swap", // FMZ平台定义的品种名称
"Interval": 28800000, // 8小时间隔,单位毫秒
"Time": 1729728000000, // 本期资金费率收取时间
"Rate": 0.0001, // 资金费率,即 0.01 %
}
5、回测系统
此次升级为了满足用户需求,先对实盘进行兼容,回测系统将在一周内完成适配. 如个别策略代码受到影响,请按此文章说明进行更改适配
根据此次平台策略API接口升级,平台的回测系统中各API接口已经同步更新完成;另外回测系统新增支持:
- 支持更多的交易所回测数据。
- 支持交易所全品种回测数据。
- U本位、币本位交割、永续合约混合交易。
- 回测时期货交易所对象支持切换交易对。
- 回测系统增加对GetTickers函数、GetMarkets函数等新增函数的支持。
补充更新
1、Account 结构新增字段Equity、UPnL
对于期货交易所对象的成员函数GetAccount返回的Account结构进行了字段扩充。
- Equity
当前保证金资产币种的总权益,除了极个别期货交易所不支持,大部分都支持该字段。主要用于计算实时的账户保证金盈亏情况。 - UPnL
当前保证金资产币种的所有持有仓位的未实现盈亏,除了极个别期货交易所不支持,大部分都支持该字段。
2、SetMarginLevel函数升级symbol参数支持
对于期货交易所对象的成员函数SetMarginLevel进行了升级,增加参数symbol。
测试例子:
javascript
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)
}
3、GetMarkets函数返回的Market结构增加CtValCcy字段
- 字段
CtValCcy记录一张合约的价值单位,一张合约的价值单位可能是:BTC、USD、ETH等。 - 字段
CtVal记录该交易品种在交易所的一张合约对应的价值,单位为CtValCcy字段记录的币种。例如:CtVal为0.01,CtValCcy为"BTC"表示一张合约价值0.01个BTC。
您好,订单ID这个是迫不得已的改动,因为升级了直接指定品种下单,订单ID中就必须包含品种信息,否则无法确定这个订单是哪个品种的,在撤单的时候无法调用(因为大多数交易所撤单的时候需要指定品种,并且指定ID)。
您说的下单后缀信息不显示,是指:exchange.Buy(price, amount, extMsg1, extMsg2)调用时,extMsg1, extMsg2 不在日志上显示吗?
- 1















