The resource loading... loading...

Detailed Explanation of FMZ Quant API Upgrade: Improving the Strategy Design Experience

Author: FMZ~Lydia, Created: 2024-07-05 09:44:08, Updated: 2024-09-20 08:52:30

[TOC]

img

Preface

After 9 years of technical iteration, the FMZ Quant Trading Platform has been reconstructed many times, although as users we may not have noticed it. In the past two years, the platform has made a lot of optimizations and upgrades in terms of user experience, including a comprehensive upgrade of the UI interface, enrichment of commonly used quantitative trading tools, and the addition of more backtesting data support.

In order to make strategy design more convenient, trading logic clearer, and easier for beginners to get started, the platform has upgraded the API interface used by the strategy. Dockers using the latest version can enable these new features. The platform is still compatible with the old interface calls to the greatest extent. Information about the new features of the API interface has been updated to the API documentation of the FMZ Quant Trading Platform:

Syntax Guide: https://www.fmz.com/syntax-guide User Guide: https://www.fmz.com/user-guide

So let’s take a quick look at which interfaces have been upgraded and what changes are needed to use old strategies to make them compatible with the current API.

1. New API interface

Added exchange.GetTickers function

For designing multi-product strategies and full market monitoring strategies, the aggregated market interface is essential. In order to make the strategy easier to develop and avoid reinventing events, the FMZ Quant Trading Platform encapsulates this type of exchange API.

If the exchange does not have this interface (individual exchanges), when calling exchange.GetTickers(), an error message is displayed: Not supported.

This function does not have any parameters and it will return the real-time market data of all varieties in the exchange’s aggregated market interface. It can be simply understood as:

exchange.GetTickers() function is the full-featured request version of the exchange.GetTicker() function (look carefully, the difference between these two function names is just the singular and plural).

We use the OKX spot simulation environment for testing:

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

Added exchange.CreateOrder function

The newly added exchange.CreateOrder() function is the focus of this upgrade. The biggest function of exchange.CreateOrder() is to specify the type and direction of the order in the function parameters directly. In this way, it no longer depends on the current trading pair, contract code, trading direction and other settings of the system.

In multi-species trading order placement scenarios and concurrent scenarios , the design complexity is greatly reduced. The four parameters of the exchange.CreateOrder() function are symbol, side, price, amount.

Test using OKX futures simulation environment:

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

In this way, only three exchange.CreateOrder() function calls were used to place three futures orders of different varieties and directions.

Added exchange.GetHistoryOrders function

The newly added exchange.GetHistoryOrders() function is used to obtain the historical transaction orders of a certain variety. The function also requires the support of the exchange interface.

For querying historical orders, the interfaces implemented by various exchanges vary greatly:

  • Some support paginated queries, while others do not;
  • Some exchanges have a query window period, that is, orders older than N days cannot be queried;
  • Most exchanges support querying at a specified time, but some do not; Such interfaces are encapsulated with the highest degree of compatibility, and in actual use, attention should be paid to whether they meet the requirements and expectations of the strategy.

The detailed function description is not repeated here, you can refer to the syntax manual in the API documentation:

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

Tested using the Binance spot trading environment:

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

    // Write to chart
    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

Added exchange.GetPositions function

The old version of the position data acquisition function is exchange.GetPosition(). This upgrade adds a new position acquisition function to better match the function naming semantics: exchange.GetPositions(). At the same time, it is still compatible/upgraded with the GetPosition function.

Note that the two function names differ only by the trailing s. Because GetPositions is more semantically correct, it is recommended to use GetPositions in the future.

The exchange.GetPositions() function has three calling forms:

  • exchange.GetPositions() When no parameters are passed, the position data of all varieties in the current dimension is requested according to the settings of the current trading pair / contract code.

  • exchange.GetPositions(“ETH_USDT.swap”) When specifying specific product information (the format of ETH_USDT.swap is defined by the FMZ platform), request the position data of the specific product. For example: BTC_USD.swap, ETH_USDT.swap, ETH_USDT.quarter, etc. BTC_USD.swap: BTC’s currency-based perpetual contract. ETH_USDT.swap: ETH’s U-based perpetual contract. ETH_USDT.quarter: ETH’s U-based quarterly delivery contract. BTC_USD.BTC-USD-201226-24250-C: BTC coin-based option contract.

  • exchange.GetPositions(“USDT.swap”) Request position data for all products according to the specified dimension range. USDT.swap: U-based perpetual contract range. USDT.futures: U-based delivery contract range. USD.swap: Coin-based perpetual contract range. USD.futures: Coin-based delivery contract range. USDT.option: U-based option contract range. USD.option: Coin-based option contract range.

    Some special exchange contract dimension divisions: USDT.futures_combo: Futures_Deribit exchange’s spread combination contract. USD.futures_ff: Futures_Kraken exchange’s mixed margin delivery contract. USD.swap_pf: Futures_Kraken exchange’s mixed margin perpetual contract. For dimensions that are not supported by the exchange API interface, an error will be reported and a null value will be returned when calling.

Test using OKX futures simulation environment:

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) +  "`")
}

img

When the parameter passed to the exchange.GetPositions() function is ETH_USDT.swap, the position data of ETH’s U-based perpetual contract can be obtained.

When the parameters of the exchange.GetPositions() function are not passed in, the position data of all U-based perpetual contracts listed on the exchange can be obtained (because the current trading pair is BTC_USDT and the contract is swap, the request is based on the current trading pair and contract dimension range). This is equivalent to calling exchange.GetPositions("USDT.swap") and specifying a request range.

2. API Interface Upgrade

Update exchange.GetTicker function

The main upgrade of the market function exchange.GetTicker() is to add the symbol parameter. This allows the function to request market data directly according to the product information specified by the parameter without the current trading pair and contract code. It simplifies the code writing process. At the same time, it is still compatible with the call method without passing parameters, and is compatible with the old platform strategy to the greatest extent.

The parameter symbol has different formats for spot/futures for the exchange object exchange:

  • Spot exchange object The format is: AAA_BBB, AAA represents baseCurrency, i.e. trading currency, and BBB represents quoteCurrency, i.e. pricing currency. Currency names are all in uppercase letters. For example: BTC_USDT spot trading pair.
  • Futures exchange object The format is: AAA_BBB.XXX, AAA represents baseCurrency, i.e. trading currency, BBB represents quoteCurrency, i.e. pricing currency, and XXX represents contract code, such as perpetual contract swap. Currency names are all in uppercase letters, and contract codes are in lowercase. For example: BTC_USDT.swap, BTC’s U-based perpetual contract. Tested using the Binance Futures live environment:
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

Requesting a batch of market data for a specified symbol has become much simpler.

Update exchange.GetDepth function

Similar to the GetTicker function, the exchange.GetDepth() function also adds a symbol parameter. This allows us to directly specify the symbol when requesting depth data.

Tested using the Binance Futures live environment:

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

Update exchange.GetTrades function

Similar to the GetTicker function, the exchange.GetTrades() function also adds a symbol parameter. This allows us to specify the symbol directly when requesting market transaction data.

Tested using the Binance Futures live environment:

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

This upgrade is also compatible with the symbol parameter specified by the exchange.Go() function when calling the platform API interface concurrently.

Update exchange.GetRecords function

The GetRecords function has been greatly adjusted this time. In addition to supporting the symbol parameter to directly specify the type information of the requested K-line data, the original period parameter is retained to specify the K-line period, and a limit parameter is added to specify the expected K-line length when requesting. At the same time, it is also compatible with the old version of the GetRecords function that only passes in the period parameter.

The calling method of exchange.GetRecords() function is:

  • exchange.GetRecords() If no parameters are specified, the K-line data of the product corresponding to the current trading pair/contract code is requested. The K-line period is the default K-line period set in the strategy backtesting interface or in live trading.
  • exchange.GetRecords(60 * 15) When only the K-line period parameter is specified, the K-line data of the product corresponding to the current trading pair/contract code is requested.
  • exchange.GetRecords(“BTC_USDT.swap”) When only the product information is specified, the K-line data of the specified product is requested. The K-line period is the default K-line period set in the strategy backtesting interface or in live trading.
  • exchange.GetRecords(“BTC_USDT.swap”, 60 * 60) Specify the product information and the specific K-line period to request K-line data.
  • exchange.GetRecords(“BTC_USDT.swap”, 60, 1000) Specify the product information, specify the specific K-line period, and specify the expected K-line length to request K-line data. Note that when the limit parameter exceeds the maximum length of a single request from the exchange, a paging request will be generated (i.e., multiple calls to the exchange K-line interface).

Tested using the Binance Futures live environment:

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 time difference between adjacent bars:", r1[1].Time - r1[0].Time, "Milliseconds, Bar length:", r1.length)
    Log("r2 time difference between adjacent bars:", r2[1].Time - r2[0].Time, "Milliseconds, Bar length:", r2.length)
    Log("r3 time difference between adjacent bars:", r3[1].Time - r3[0].Time, "Milliseconds, Bar length:", r3.length)
    Log("r4 time difference between adjacent bars:", r4[1].Time - r4[0].Time, "Milliseconds, Bar length:", r4.length)
    Log("r5 time difference between adjacent bars:", r5[1].Time - r5[0].Time, "Milliseconds, Bar length:", r5.length)
}

img

Update exchange.GetOrders function

The GetOrders function also adds symbol parameters, which can be used to specify a specific symbol and query the unfinished orders (pending orders) of that symbol; it also supports querying the unfinished orders (pending orders) of all symbols in the specified dimension range.

The exchange.GetOrders() function can be called in the following ways:

  • exchange.GetOrders() For futures exchanges: When no parameters are passed, all outstanding orders (pending orders) for all instruments in the current dimension range are requested according to the current trading pair / contract code settings. For spot exchanges: When no parameters are passed, requests are made for all outstanding orders (pending orders) of all spot products.
  • exchange.GetOrders(“BTC_USDT.swap”) or exchange.GetOrders(“BTC_USDT”) For futures exchanges: exchange.GetOrders(“BTC_USDT.swap”), query all outstanding orders (pending orders) for BTC’s USDT-based perpetual contract. For spot exchanges: exchange.GetOrders(“BTC_USDT”), query all outstanding orders (pending orders) for the BTC_USDT spot trading pair.
  • Only supported for futures exchanges exchange.GetOrders(“USDT.swap”) specifies the dimension range to request outstanding orders (pending orders) for all varieties The dimension range is consistent with the range in the GetPositions function. For example: exchange.GetOrders(“USDT.swap”) requests all outstanding orders (pending orders) of all varieties in the U-based perpetual contract range.

Test using OKX futures simulation environment:

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

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

    // Write to chart
    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) +  "`")
}

When no parameters are passed, unfinished orders (pending orders) of all varieties in the dimension range of the current trading pair (BTC_USDT) and contract code (swap) are requested.

When the parameter ETH_USDT.swap is specified, unfinished orders (pending orders) of ETH’s USDT-based perpetual contract are requested.

When the string "USDT.swap" is passed, the unfinished orders (pending orders) of all USDT-based perpetual contracts are requested.

Update exchange.GetPosition function

It is still compatible with the old position acquisition function naming, and also adds the symbol parameter, which can specify the type information of the specific requested position data. The usage of this function is exactly the same as exchange.GetPositions().

Update exchange.IO function

For exchange.IO("api", ...) function calls, all exchange objects have been upgraded to support the direct passing of complete request addresses. For example, if you want to call the OKX interface:

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

Supports direct writing to the base address https://www.okx.com without having to switch the base address first and then call the IO function.

Test using OKX futures simulation environment:

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 Interface Impact

Affects exchange.GetOrder function

This upgrade mainly affects the parameter id of the exchange.GetOrder(id) function. The id parameter is changed from the original exchange order id to a string format containing the trading product. All encapsulated order IDs on the FMZ platform are in this format.

For example:

  • The original order Id of the exchange defined in the exchange order is: 123456 Before this upgrade, if you want to call the GetOrder function, the order Id passed in is 123456.
  • The product code named by the exchange defined in the exchange order: BTC-USDT. Note that this refers to the trading product code named by the exchange, not the trading pair defined by the FMZ platform.

After this upgrade, the format of the parameter id that needs to be passed into the exchange.GetOrder(id) function is adjusted to: BTC-USDT,123456.

First, let me explain why this design is done: Because the CreateOrder function has been upgraded to specify the type of order directly (the type of order placed may be different from the currently set trading pair and contract code). If the returned order ID does not contain the type information, then this order ID will be unusable. Because when checking the order, we don’t know what type (contract) the order is for. Most exchanges require the specification of parameters describing the type code when checking and canceling orders.

How to be compatible with this impact: If you use the exchange.IO function to call the exchange order interface directly to place an order, the return value generally contains the exchange’s original symbol (product code) and the original order id. Then concatenating the two with English commas will be the order ID that complies with the definition of the FMZ platform. Similarly, if you use the FMZ platform encapsulated order interface to place an order, since the beginning of the order ID is the trading product code, if you need to use the original order ID, just delete the product code and comma.

Affects exchange.CancelOrder function

The impact of this upgrade on the exchange.CancelOrder() function is the same as the exchange.GetOrder() function.

Affects exchange.Buy function

The impact of this upgrade on the exchange.Buy() function is the same as the exchange.GetOrder() function. The order ID returned by the exchange.Buy() function is a new structure, for example, the ID returned when placing a futures order on the OKX exchange is: LTC-USDT-SWAP,1578360858053058560.

Affects exchange.Sell function

The impact of this upgrade on the exchange.Sell() function is the same as the exchange.GetOrder() function. The order ID returned by the exchange.Sell() function is a new structure, for example, the ID returned when placing a futures order on the OKX exchange is: ETH-USDT-SWAP,1578360832820125696.

Affects exchange.GetPosition function

Only futures exchange objects support this function. For the exchange.GetPosition() function for obtaining position data, a new exchange.GetPositions() name is added, and the two behaviors are exactly the same.

Old definition: exchange.GetPosition() function, when called without specifying any parameters, obtains the position data of the specific contract set by the current trading pair and contract code. After adjustment and modification, the new definition: exchange.GetPosition() function, when called without specifying any parameters, obtains the positions of all varieties in the dimension range determined by the current set trading pair and contract code.

For example, the current trading pair is BTC_USDT and the contract code is swap. At this time, call:

exchange.GetPosition() // Equivalent to calling exchange.GetPosition("USDT.swap")

This function will request the position data of U-based perpetual contracts of all currencies.

Affects exchange.GetOrders function

  1. For spot exchanges:

Old definition: exchange.GetOrders() function, when called without specifying any parameters, gets all the uncompleted orders of the current trading pair. After adjustment and modification, the new definition is: exchange.GetOrders() function, when called without specifying any parameters, gets the uncompleted orders of all spot trading pairs.

  1. For futures exchanges:

Old definition: exchange.GetOrders() function, when called without specifying any parameters, gets all the uncompleted orders of the specific contract set by the current trading pair and contract code. After adjustment and modification, the new definition is: exchange.GetOrders() function, when called without specifying any parameters, gets all the uncompleted orders of the dimension range determined by the current trading pair and contract code.

For example, the current trading pair is BTC_USD and the contract code is quarter. At this time, call:

exchange.GetOrders()   // Equivalent to calling exchange.GetOrders("USD.futures")

This function will request the outstanding order data of all coin-based futures contracts.

4. Structural Adjustment

Ticker Structure

This update adds a Symbol field to the Ticker structure, which records the market information of the current Ticker structure. The format of this field is exactly the same as the symbol parameter format of the exchange.GetTicker() function.

Order Structure

This update adds a Symbol field to the Order structure, and the format of this field is exactly the same as the symbol parameter format of the exchange.GetTicker() function. This update also modifies the Id field of the Order structure, recording the product information and original order information in the new order ID format. Refer to the description of the order ID in the exchange.GetOrder() function, which will not be repeated here.

Position Structure

This update adds a Symbol field to the Position structure. The format of this field is exactly the same as the symbol parameter format of the exchange.GetTicker() function.

5. Backtesting system

According to the platform strategy API interface upgrade, the platform’s backtesting system has been updated synchronously; In addition, the backtesting system has added support for:

  • Supports more exchange backtesting data.
  • Supports backtesting data for all varieties of exchanges.
  • Mixed trading for U-based, currency-based delivery, and perpetual contract.
  • Futures exchange objects support switching trading pairs during backtesting.

Supplementary Updates

1. New fields Equity and UPnL in Account structure

The fields of the Account structure returned by the GetAccount member function of the futures exchange object have been expanded.

  • Equity The total equity of the current margin asset currency. Except for a few futures exchanges that do not support this field, most exchanges support this field. It is mainly used to calculate the real-time account margin profit and loss.

  • UPnL The unrealized profit and loss of all positions held in the current margin asset currency. Most futures exchanges support this field, except for a few that do not.

2. SetMarginLevel function upgraded to support symbol parameter

The member function SetMarginLevel of the futures exchange object has been upgraded and the parameter symbol has been added.

Test example:

function main() {
    exchange.SetCurrency("ETH_USDT")
    exchange.SetContractType("swap")
    
    // The current trading pair is ETH_USDT, the contract code is swap, and the leverage value is set to 10
    exchange.SetMarginLevel(10)
    
    // Directly specify the trading pair BTC_USDT, contract code swap, and set the leverage value to 20
    exchange.SetMarginLevel("BTC_USDT.swap", 20)
}

3. The Market structure returned by the GetMarkets function added the CtValCcy field

The field CtValCcy records the value unit of a contract. The value unit of a contract may be: BTC, USD, ETH, etc. The field CtVal records the value of a contract of the trading product on the exchange, and the unit is the currency recorded in the CtValCcy field. For example: CtVal is 0.01, and CtValCcy is “BTC”, which means that a contract is worth 0.01 BTC.


More