The resource loading... loading...

Primary Tutorial of Strategy Writing with FMZ Quant Trading Platform (Must Read)

Author: FMZ~Lydia, Created: 2023-07-12 15:09:33, Updated: 2024-02-05 20:05:43

[TOC]

img

This tutorial contains basic knowledge of strategy writing, including API introduction, backtest, charts and more. After learning this basic tutorial, users will be able to use the basic API proficiently and write a stable bot strategy. Before learning the tutorial, you need to learn how to use Get Started FMZ Quant Platform.

Old Version Tutorial: FMZ Quant (FMZ.COM) Strategy Writing Manual 2.0 (Tutorial); there are many post indexes in the tutorial, which are recommended to read.

Preliminary Instruction on Strategy Writing

API Introduction

Program trading is to use programs to connect with platforms through API to achieve automatic buying and selling or other functions according to the design intent. API represents Application Programming Interface.

At present, there are two main interface protocols for cryptocurrency platforms: REST and Websocket. Each time the REST protocol obtains data, it needs to be accessed once. Let’s take the API of the simulated platform “Wex.app” as an example. Open the [link] (https://api.wex.app/api/v1/public/ticker?market=BTC_USDT) directly in the browser, and you can get the result as follows:

{"data:{"buy":"11351.73","high":"11595.77","last":"11351.85","low":"11118.45","open":"11358.74","quoteVol":"95995607137.00903936","sell":"11356.02","time":1565593489318,"vol":"3552.5153"}}

In this way, you can see that the trading following the latest market quotes of the trading pair BTC_USDT , will change every time it is refreshed; “market=”" is followed by the specific trading pair parameters, which can be modified to obtain other trading pair data. For public interfaces, such as market quotes, everyone can obtain them, so no verification is required. However, some interfaces need to determine the user’s identity when placing an order or obtaining an account. In this case, API-KEY is needed to sign. “Websocket” is a subscription mode. After sending the content that needs to be subscribed, the platform will send the updated data to the program, and it does not need to be revisited every time, so it is more efficient.

FMZ Quant trading platform encapsulates the REST interface of every platform, and uses a unified way to call and a unified data format, making strategy writing simpler and more general. Websocket can be easily supported on FMZ platform, which will be introduced in detail in the next tutorial.

Different Programming Languages

Most parts of the FMZ platform API document use JavaScript as an example, but due to the encapsulation, there is almost no difference between different languages, and you only need to pay attention to syntax issues. “C++”" is a little bit special, and future tutorials will have a specialized introduction. Since “Js” is relatively simple and has no compatibility issues, it is recommended for beginners to use. FMZ Quant platform supports complete “Python”, and can freely install various packages. It is recommended to users who have a certain programming foundation. For users who do not want to learn programming languages and just want to quickly write strategies, FMZ Quant platform also supports Mylanguage , which is basically compatible with Webstock strategies, and it is recommended for those who have relevant experience. The disadvantage is that Webstock is not as powerful and flexible as programming languages. FMZ also supports visual programming, which is similar to building blocks to implement strategies, but it is not recommended, and it is not as clear as codes. Due to the high similarity of programming languages, there is no need to struggle with which one to choose. It takes little effort to learn the basics of those programming languages.

Since Python has different versions, it can be specified at the beginning of the program, such as #!Python2 and #!Python3. Note that JavaScript has recently upgraded its ES6 syntax, and those who are interested can learn about it. The Python and Javascript codes with the same functions are shown below. It can be seen that there are only syntax differences, so the API document only gives examples of Javascript, and this tutorial will also take into account the special use cases of Python.

#python code
def main():
    while True:
        Log(exchange.GetAccount().Balance)
        Sleep(2000)
#the corresponding Js code
function main(){
    while(true){
        Log(exchange.GetAccount().Balance)
        Sleep(2000)
    }
}

Resource Recommendations

  • This tutorial will not give detailed introduction on each interface in FMZ platform API document, so you can check out this article for more details.
  • If you want to receive tradingview signal and place an order on FMZ, you can refer to this article.
  • For the quick start of Javascript and Python, writing simple strategies doesn’t need complex syntax, but only needs some basic concepts; you can study the tutorial while you’re learning how to program (https://www.fmz.com/bbs-topic/9123, https://www.fmz.com/bbs-topic/9124).
  • Mylanguage document; Mylanguage is still very convenient for trend strategies.
  • Here is an invocation example of C++. Those who are interested in C++ can have a look. Since it is not an interpretative language, the debugging is very difficult, so the example is not recommended.
  • “Cryptocurrency Quantitative Trading Course” of NetEase Cloud Classroom, officially produced by FMZ, only needs 20 yuan, with richly detailed content, from simple to deep, suitable for beginners! courselink
  • Here are some teaching strategies suitable for the beginning. You can try to write strategies while you’re studying the basics.
  • Detailed explanation of strategy source code: the link.

Debug Tool

FMZ Quant platform provides the “Debug Tool” for debugging API interfaces. The debugging tool only supports JavaScript and can only be executed for a period of time; the platform interface can be debugged without creating a bot; the return data will be returned as the result, and the code of the debugging tool will not be saved. As you work through this tutorial, you can experiment with the debugging tool at the same time. img

Strategy Program Framework

The strategy program is the same as a normal program, which is executed in code orders. The special part is that there must be a “main” function. Since the strategy needs to run uninterruptedly, usually a loop plus sleep time is required. Because the access frequency of platform APIs is limited, the sleep time needs to be adjusted accordingly. This framework is the typical execution at fixed intervals, and you an also use Websocket to write event-driven strategies. For example, immediate execution as long as the depth changes, which will be introduced in the advanced tutorial.

Other functions with special actions are shown as follows:

  • onexit() is a normal on-exit function; its maximum execution time is 5 minutes; it can be unspecified; if the time is out, an interrupt error will be reported.
  • onerror() is an abnormal exit function; its maximum execution time is 5 minutes; it can be unspecified.
  • init() is an initialization function; its strategy program will be called automatically when it starts running; it can be unspecified.
function onTick(){
   var ticker = exchange.GetTicker()
   var account = exchange.GetAccount()
    //write the strategy logic here, and it will be called ever 6 seconds
}
function main(){
    while(true){
        onTick()
        Sleep(6000)
    }
}

In the previous example, if there is an error in network access, the strategy may stop directly. If you want a strategy that is similar to automatic restart and will not stop, you can use the “try catch” fault-tolerant main loop in the bot strategy (do not use “try” for the backtest). Of course, this is only recommended when the strategy is stable, otherwise all errors will not be reported, making it difficult to find faults in the strategy.

function onTick(){
   var ticker = exchange.GetTicker()
   var account = exchange.GetAccount()
    //write the strategy logic here, and it will be called ever 6 seconds
}
function main(){
    try{
        while(true){
           onTick()
           Sleep(6000)
       }
    }catch(err){
        Log(err)
    }
}

Platform API Introduction

Set Platform and Trading Pair

When calling any platform-related API, you need to specify the platform and trading pair. If only one “platform-trading pair” is added when creating a bot, use exchange to represent this object. For example, what exchange.GetTicker() will obtain is the market ticker of this “exchange-trading pair”.

FMZ Quant platform supports adding multiple “exchange-trading pair” objects at the same time. For example, you can operate BTC and ETH of the same platform account at the same time, or you can operate BTC of one exchange and ETH of another exchange at the same time. Note that different accounts on the same platform can also be added at the same time, and they are distinguished according to the labels added to the FMZ website. When there are multiple “exchange-trading pair” objects, use exchanges array to represent them, namely exchanges[0] and exchanges[1]… and so on, according to the adding order when the bot is created. In the format of the trading pair, like BTC_USDT, the former “BTC” is the trading currency, and “USDT” is the quote currency.

img

Obviously, if we operate a lot of trading pairs, this method will be very inconvenient. In this situation, we can use SetCurrency to switch trading pairs, such as exchange.SetCurrency("BTC_USDT"); then, the trading pair bound to exchangebecomes BTC_USDT, which will remain valid until the next call to change the trading pair. Note that backtest supports switching trading pairs recently. Below is a specific example:

var symbols = ["BTC_USDT", "LTC_USDT", "EOS_USDT", "ETH_USDT"]
var buyValue = 1000
function main(){
  for(var i=0;i<symbols.length;i++){
      exchange.SetCurrency(symbols[i])
      var ticker = exchange.GetTicker()
      var amount = _N(buyValue/ticker.Sell, 3)
      exchange.Buy(ticker.Sell, amount)
      Sleep(1000)
  }
}

Get Public Interfaces

As mentioned in the previous example, the market interface is generally a public interface, which can be accessed by everyone. The common market interfaces are: GetTicker, GetDepth, GetRecords and GetTrades. The market quote is the basis for the strategy to make trading judgments. Later, I will introduce them one by one. It is better to try them in the “Debug Tool” by yourself. If you need a detailed explanation, you can check it in the API document.

Each interface generally has an Info field, which represents the original data string returned by the platform, and which can be used to supplement additional information. It needs to be parsed before use. JavaScript uses JSON.parse(), while Python uses json library. The Time field indicates the timestamp of the request, which can be used to judge the delay.

when using any API in the bot, the access may fail and return null, and Python returns None. At this time, the data in use will report an error and cause the bot to stop, so fault tolerance is very important. This tutorial will introduce the fault tolerance specially.

GetTicker

GetTicker is probably the most commonly used interface. You can find the last time executed price, buy1 price and sell1 price, and the latest trading volume. Before placing an order, the executed price can be determined according to the ticker information. An example of a bot return: {"Info:{}, "High":5226.69, "Low":5086.37,"Sell":5210.63, "Buy":5208.5, "Last":5208.51, "Volume":1703.1245, "OpenInterest":0, "Time":1554884195976}.

function main() {
    var ticker = exchange.GetTicker()
    Log(ticker) //return ticker in the debugging tool, and you can see the specific result
    Log('Last time executed price:',ticker.Last, 'Buy1 price:', ticker.Buy)
}

GetDepth

GetDepth to obtain the depth information of pending orders. Although GetTicker includes buy 1 and sell1 prices, if you want to query deeper pending orders, you can use this interface, to generally check up and down 200 pending orders. Shock prices can be calculated by using this interface. Below is a real return result. Among them, “Asks” represents pending sell order, and the array is “Sell1”, “Sell2”… So the price also rises in turn. “Bids” represents pending buy order, and the array is “buy1”, “buy2”… The price goes down in turn.

{
    "Info":null,
    "Asks":[
        {"Price":5866.38,"Amount":0.068644},
        {"Price":5866.39,"Amount":0.263985},
        ......
        ]
    "Bids":[
        {"Price":5865.13,"Amount":0.001898},
        {"Price":5865,"Amount":0.085575},
        ......
        ],
    "Time":1530241857399
}

Example of using GetDepth for Asks & Bids:

function main() {
    var depth = exchange.GetDepth()
    Log('Buy 1 price:', depth.Bids[0].Price, 'Sell 1 price:', depth.Asks[0].Price)
}

GetRecords

GetRecords is one of the most commonly used interfaces, can return price information in a long period at a time, which is the basis of calculating various indicators. If the K-line period is not specified, it means using the default period when adding a bot. The length of the K-line cannot be specified, and it will continue to increase over time. The maximum number is 2000, and in the first call the number is about 200 (different platforms return different numbers). The last K-line is the latest K-line, so the data will change as the market quotes change; the first K-line is the oldest data.

exchange.SetMaxBarLen(Len) can set the number of K-lines acquired for the first time (supported by some platforms), and set the maximum number of K-lines. Such as: exchange.SetMaxBarLen(500).

GetRecords can specify periods like PERIOD_M1: 1 minute, PERIOD_M5: 5 minutes, PERIOD_M15: 15 minutes, PERIOD_M30: 30 minutes, PERIOD_H1: 1 hour and PERIOD_D1: 1 day. The specific use is exchange.GetRecords(PERIOD_M1). After upgrading the latest docker, it will support customizing periods, which just pass the second number of the period as a parameter. The minute-level customization will be synthesized according to the 1-minute K-line, the K-line under 1 minute will be synthesized through GetTrades(), and the commodity futures will be synthesized according to tick. Note that there are also other full uppercase variables like PERIOD_M1 in the tutorial. They are the default global variables of FMZ. If you are interested, you can “log” their specific values by yourself, and you can use them directly in usual.

Return data example:

[
    {"Time":1526616000000,"Open":7995,"High":8067.65,"Low":7986.6,"Close":8027.22,"Volume":9444676.27669432},
    {"Time":1526619600000,"Open":8019.03,"High":8049.99,"Low":7982.78,"Close":8027,"Volume":5354251.80804935},
    {"Time":1526623200000,"Open":8027.01,"High":8036.41,"Low":7955.24,"Close":7955.39,"Volume":6659842.42025361},
    ......
]

Example of iterated K-line:

function main(){
    var close = []
    var records = exchange.GetRecords(PERIOD_H1)
    Log('total bars: ', records.length)
    for(var i=0;i<records.length;i++){
        close.push(records[i].Close)
    }
    return close
}

GetTrades

GetTrades obtains the trading data within a certain time range (not your own trading data), which is not supported by some platforms. It is not commonly used, and you can check the detailed introduction in the API document.

Get Account to Trade

Those interfaces are related to the account, so they cannot be obtained directly. To obtain them, you need to use API-KEY to sign. After unified automatic background processing of FMZ platform, you can directly use them.

GetAccount

GetAccount to obtain the account information. As one of the most commonly used interfaces, it needs to be called before placing an order, to avoid insufficient balance. The return result is like: {"Stocks":0.38594816,"FrozenStocks":0,"Balance":542.858308,"FrozenBalance":0,"Info":{}}. Where “Stocks” is the available balance of the trading currency of the trading pair, “FrozenStocks” is the frozen balance of non-executed orders, “Balance” is the available amount of the quote currency, and “FrozenBalance” is the frozen balance. If the trading pair is BTC_USDT, “Stocks” refers to BTC, and “Balance” refers to USDT.

Note that the return result is the result of the specified trading pair, and the information of other currencies in the trading account is in the “Info” field, so you don’t need to call it multiple times, when you operate multiple trading pairs.

A bot constantly printing the total value of the current trading pair:

function main(){
    while(true){
        var ticker = exchange.GetTicker()
        var account = exchange.GetAccount()
        var price = ticker.Buy
        var stocks = account.Stocks + account.FrozenStocks
        var balance = account.Balance + account.FrozenBalance
        var value = stocks*price + balance
        Log('Account value is: ', value)
        LogProfit(value)
        Sleep(3000)//sleep 3000ms(3s), A loop must has a sleep, or the rate-limit of the exchange will be exceed
        //when run in debug tool, add a break here
    }
}

Buy Order

Buy order. Invocation methods include exchange.Buy(Price, Amount) and exchange.Buy(Price, Amount, Msg), in which “Price” indicates the price, “Amount” is the amount, “Msg” is an extra string that can be displayed in the bot log, but not required. These methods are pending orders. If the order cannot be completely executed immediately, an unfinished order will be generated; the order id is returned if the order is successfully placed, and null will be returned if the order is unsuccessful, which is used to query the order status.

If you want to place a buy order at the market price, “Price” is -1, and “Amount” is the value of the order. For example, exchange.Buy(-1, 0.5); if the trading pair is ETH_BTC, which means that buy 0.5BTC of ETH at the market price. Some platforms do not support market orders, nor do the futures backtest.

Some platforms have the precision requirements for price and amount, which can be controlled with the precision function _N(). For futures trading, “Buy” and “Sell” have other meanings, which will be introduced specially.

An example of buying in once reaching the corresponding price:

function main(){
    while(true){
        var ticker = exchange.GetTicker()
        var price = ticker.Sell
        if(price >= 7000){
            exchange.Buy(_N(price+5,2), 1, 'BTC-USDT')
            break
        }
        Sleep(3000)//Sleep 3000ms
    }
    Log('done')
}

Sell Order

Sell order. The parameters are the same as “Buy”. The parameters of the market order have different meanings. A market sell order, such as exchange.Sell(-1, 0.2), means selling 0.2ETH at the market price.

GetOrder

GetOrder obtains the order information based on the order id. When this common interface calls the method exchange.GetOrder(OrderId), “OrderId” is the order id, which will be returned when placing an order. Note that the string of the order Type and the actual value of order Status are numbers, which represent different meanings, but are not conducive to memory. FMZ uses global constants to represent these values. For example, the Status value of an unfinished order is 0, which is equivalent to ORDER_STATE_PENDING. All these global constants can be viewed in the document… Return result:

{
    "Id":125723661, //Order id
    "Amount":0.01, //Order ammount 
    "Price":7000, //Order price 
    "DealAmount":0, //Executed amount 
    "AvgPrice":0, //executed average price
    "Status":0, //0: not completely executed; 1: executed; 2: canceled 
    "Type":1,//Order type; 0: buy order; 1: sell order 
    "ContractType":"",//contract type, used in futures trading
    "Info":{} //the platform returns the raw information
    }
}

A strategy to buy a specified amount of currency:

function main(){
    while(true){
        var amount = exchange.GetAccount().Stocks
        var ticker = exchange.GetTicker()
        var id = null
        if(5-amount>0.01){
            id = exchange.Buy(ticker.Sell, Math.min(5-amount,0.2))
        }else{
            Log('Job completed')
            return //return the main function, bot will stop
        }
        Sleep(3000) //Sleep 3000ms
        if(id){
            var status = exchange.GetOrder(id).Status
            if(status == 0){ //Here you can aslo use "status == ORDER_STATE_PENDING" to judge 
                exchange.CancelOrder(id)
            }
        }
    }
}

GetOrders

GetOrder obtains the list of all unfinished orders of the current trading pair. If there is no unfinished order, return an empty array. The specific result of the order list, such as “GetOrder”.

Example of canceling all orders of the current trading pair:

function CancelAll(){
    var orders = exchange.GetOrders()
    for(var i=0;i<orders.length;i++){
        exchange.CancelOrder(orders[i].Id) // cancel order by orderID
    }
}
function main(){
    CancelAll()
    while(true){
        //do something
        Sleep(10000)
    }
}

CancelOrder

According to the order id, cancel the order. exchange.CancelOrder(OrderId). If the canceling is successful, return “true”; if not, return “false”.

Futures & Perpetual Contract

For cryptocurrency, futures trading is different from spot trading. The above functions of spot trading are also applicable to futures trading, and single futures trading has its own functions. Before conducting program trading of cryptocurrency futures, you should be familiar with manual operations on the website and understand the basic concepts, such as open, close, crossed, isolated, leverage, close profit and loss, floating income, margin and other concepts, as well as the corresponding calculation formulas. The corresponding tutorials can be found on various futures platforms, and you need to learn them by yourself.

Perpetual contracts are similar to futures contracts, but the difference is that there is no such concept of holding long and short positions at the same time.

If the platform supports both futures and spot, such as futures of OKEX and Huobi, you need to select “OKEX Futures” and “Huobi Futures” separately on the platform interface to add, for those futures platforms are regarded as different platforms from the spot ones on FMZ.

SetContractType

The first step in futures trading is to set the contract to be traded. Taking OKEX futures as an example, select a BTC trading pair when creating a bot or backtesting, and you also need to set the weekly, next week or quarterly contract in the code. If it is not set, it will prompt invalid contract type. Different from spot trading pairs, futures contracts often use trading currency such as BTC as margin. Adding BTC to a trading pair usually represents a BTC_USD trading pair that uses BTC as margin. If there is a futures contract with USDT as margin, a bot needs to be created to add BTC_USDT trading pair. For example, perpetual contracts like Binance OKEX Futures, with both crypto-margined and USDT-margined contracts. After setting the trading pair, you must also set the specific contract type, such as perpetual, weekly, next week, etc. After setting up the contract, you can perform operations such as obtaining market quotes, buying and selling.

Binance, OKEX, HuobiDM, etc. have both crypto-margined and USDT-margined contracts, which need to be distinguished when adding a bot and setting a contract. The specific settings are as follows:

//OKEX Futures
exchange.SetContractType("swap")        // set to perpetual contract
exchange.SetContractType("this_week")   // set to weekly contract 
exchange.SetContractType("next_week")   // set to next week contract 
exchange.SetContractType("quarter")     // set to quarterly contract

//HuobiDM
exchange.SetContractType("this_week")   // set to weekly contract 
exchange.SetContractType("next_week")   // set to next week contract
exchange.SetContractType("quarter")     // set to quarterly contract 
exchange.SetContractType("swap")        // set to perpetual contract

//Binance Futures
exchange.SetContractType("swap")   // set to perpetual contract, and notice that crypto-margined and USDT-margined contracts are all in the perpetual contract
exchange.SetContractType("quarter")   // set to quarterly contract
exchange.SetContractType("next_quarter")  // set to next quarter contract

//BitMEX
exchange.SetContractType("XBTUSD")    // set to perpetual contract
exchange.SetContractType("XBTM19")  // the contract settled at a specific time; for more details, please log in BitMEX to check each contract code

//GateIO
exchange.SetContractType("swap")      // set to perpetual contract, and do not set the default as swap perpetual contract 
//Deribit
exchange.SetContractType("BTC-27APR18")  // the contract settled at a specific time; for more details, please log in Deribit to check out 

GetPosition

To get the current position information list, OKEX (OKCOIN) futures can pass in a parameter to specify the contract type to be obtained. Return an empty list [], if there is no position. The position information is returned as follows. There is a lot of specific information, which needs to be analyzed in combination with the trading pair.

Data Type Variable Name Description
object Info the raw structure that the platform returns
number MarginLevel leverage size; OKCoin is 10 or 20, and the crossed postion of OK futures returns 10 (fixed), for the raw API does not support
number Amount position amount; OKCoin indicates the contract quantity (integer over 1)
number FrozenAmount frozen position amount
number Price position average price
number Margin frozen margin
number Profit commodity futures: the profit and loss of position mark to market; cryptocurrency: cryptocurrency unit: BTC/LTC, traditional futures unit: RMB (note:In the case of a crossed position of OKCoin futures, it refers to the realized profit and loss, not the profit and loss of the position. Under the isolated position, it refers to the profit and loss of the position.)
const Type PD_LONG is long position (CTP uses “closebuy_today” to close a position ); PD_SHORT is short position (CTP uses “closesell_today” to close a position); in CTP futures, PD_LONG_YD indicates yesterday’s long position (which uses “closebuy” to close position); PD_SHORT_YD is yesterday’s short position (which uses “closesell” to close position)
string ContractType commodity futures are contract codes, and stocks are “platform code_stock code”; the passed type of specific parameters of SetContractType
function main(){
    exchange.SetContractType("this_week");
    var position = exchange.GetPosition();
    if(position.length>0){ //especially pay attention to judging the length of position before call, or an error will occur
        Log("Amount:", position[0].Amount, "FrozenAmount:", position[0].FrozenAmount, "Price:",
            position[0].Price, "Profit:", position[0].Profit, "Type:", position[0].Type,"ContractType:", position[0].ContractType)
    }
}

Futures Open & Close Positions

First of all, you need to set the leverage size; invocation method: exchange.SetMarginLevel(10), where “10” means 10 times of leverage, and the specific supported leverage size can be checked in the corresponding platforms, Note that the leverage should be set in the platform, and the code should be consistent with the settings set on the platform, otherwise an error will occur. You can also leave it unset and use the default leverage. Then, set the trading direction; invocation method: exchange.SetDirection(Direction), which corresponds to open and close positions. Unlike futures, if a perpetual contract does not hold the concepts of long and short at the same time, that is, a single position is not allowed. When you operate open short on a long position, the long position will be automatically closed, so you only need to set buy and sell. If it supports two-way positions, you need to set closebuy, closesell. Specific relationships:

Operation SetDirection Parameters Function of Placing Order
Open Long Position exchange.SetDirection(“buy”) exchange.Buy()
Close Long Position exchange.SetDirection(“closebuy”) exchange.Sell()
Open Short Position exchange.SetDirection(“sell”) exchange.Sell()
Close Short Position exchange.SetDirection(“closesell”) exchange.Buy()

Finally, there is the specific code for open and close positions. The amount of orders placed varies from platform to platform. For example, Huobi futures are based on the number of contract quantity, and one contract is 100 US dollars. Note that futures backtest does not support market orders.

function main(){
    exchange.SetContractType("this_week")    // for example, set OKEX futures to weekly contract 
    price = exchange.GetTicker().Last
    exchange.SetMarginLevel(10) // set to 10 times of leverage  
    exchange.SetDirection("buy") // set the order type as buy long 
    exchange.Buy(price+10, 20) // set contract quantity as 20 orders
    pos = exchange.GetPosition()
    Log(pos)
    Log(exchange.GetOrders()) // check out if there is any unfinished order 
    exchange.SetDirection("closebuy"); // if it is a perpetual contract, directly set exchange.SetDirection("sell")
    exchange.Sell(price-10, 20)
}

Give a specific strategy example of full close postions as follows:

function main(){
    while(true){
        var pos = exchange.GetPosition()
        var ticker = exchange.GetTicekr()
        if(!ticker){
            Log('not able to obtain ticker')
            return
        }
        if(!pos || pos.length == 0 ){
            Log('no position')
            return
        }
        for(var i=0;i<pos.length;i++){
            if(pos[i].Type == PD_LONG){
                exchange.SetContractType(pos[i].ContractType)
                exchange.SetDirection('closebuy')
                exchange.Sell(ticker.Buy, pos[i].Amount - pos[i].FrozenAmount)
            }
            if(pos[i].Type == PD_SHORT){
                exchange.SetContractType(pos[i].ContractType)
                exchange.SetDirection('closesell')
                exchange.Buy(ticker.Sell, pos[i].Amount - pos[i].FrozenAmount)
            }
        }
        var orders = exchange.Getorders()
        Sleep(500)
        for(var j=0;j<orders.length;j++){
            if(orders[i].Status == ORDER_STATE_PENDING){
                exchange.CancelOrder(orders[i].Id)
            }
        }
    }
}

Cryptocurrency Leverage Trading

Cryptocurrency leverage trading needs to switch to the leverage account in the code, and other parts are the same as spot trading.

Use exchange.IO("trade_margin") to switch to leverage account mode; placing an order and obtaining account assets will access the interface of the platform leverage. Use exchange.IO("trade_normal") to switch back to the ordinary account mode.

Supported Platforms:

  • OKEX V3: The trading pairs of the leverage account mode are different from the ordinary ones, and some trading pairs may not exist.
  • Huobi: the trading pairs of the leverage account mode are different from the ordinary ones, and some trading pairs may not exist.
  • ZB: the assets can only be transferred in QC. In the leverage trading sector, the assets between different trading pairs are independent, that is, the number of QC coins under the ETH_QC trading pair is not visible in BTC_QC.
  • FCoin
  • Binance

Commodity Futures Trading

Commodity futures trading and cryptocurrency futures trading are quite different. First of all, the trading time of commodity futures is very short, but cryptocurrency is traded for 24 hours; the protocol of commodity futures is not a commonly used REST API; the trading frequency and the pending order amount of commodity futures are limited, but those of cryptocurrency are very loose, and so on. Therefore, there are many points in need of special attention when trading commodity futures, and it is recommended to those who have rich experience in manual operations. FMZ supports simnow commodity futures simulated bot, and you can refer to: https://www.fmz.com/bbs-topic/325. For adding commodity futures companies: https://www.fmz.com/bbs-topic/371

Commodity futures have implemented see-through supervision in June 2019; for individual program, individual users need to open an account to apply for an authorization code for futures brokers (the specific application information template can be sent to WeChat group or QQ group), which generally takes 4-5 days; the procedures are quite complicated. As a programmatic trading provider, FMZ Quant platform has applied for software authorization codes from various futures service providers. Users can use them directly without applying. When adding a futures broker, search for “see through” to see the list that FMZ has applied for. Specific reference post: https://www.fmz.com/bbs-topic/3860 . If your futures broker is no longer on the list, you can only apply by yourself, or open a new account of a supported broker, which generally takes 2 days. FMZ has in-depth cooperative relations with some service providers; for example, Guotai Junan Hongyuan Futures has purchased the institutional version of FMZ platform, which can be used by its users, and users automatically become a VIP when opening an account, and the service fee is minimized. Account opening reference: https://www.fmz.com/bbs-topic/506.

Due to the advantages of FMZ Quant platform structure, users can also add multiple futures broker accounts, and implement some functions that other commodity futures program trading software cannot complete, such as the synthesis of high-frequency tick; you can refer to: https://www.fmz.com/bbs-topic/1184

Strategy Framework

First of all, because it is not a 24h trading and requires a login operation, it is necessary to judge the link status before trading. exchange.IO("status") is true, which inidcates successful connection to the platform. If the API is called when the login is not successful, ‘not login’ is not prompted. You can add “Sleep (2000)” after the strategy starts, giving it a certain time to log in. You can also retry to subscribe _C(exchange.SetContractType,"MA888"), which will ensure a successful login.

The market quote acquisition and trading codes of commodity futures are the same as those of cryptocurrency futures. Here we will introduce the differences and points that need to be paid attention to.

function main(){
    _C(exchange.SetContractType,"MA888") //If you do not log in successfully, you cannot subscribe to the contract, so better to try again
    while(true){
        if(exchange.IO("status")){
            var ticker = exchange.GetTicker()
            Log("MA888 ticker:", ticker)
            LogStatus(_D(), "Already connected with CTP !")//_D obtain event
        } else {
            LogStatus(_D(), "Not connected with CTP !")
            Sleep(1000)
        }
    }
}

It is recommended to use the commodity futures library for trading (which will be described later), the code will be very simple at this time, and there is no need to deal with tedious details. Source code copy address: https://www.fmz.com/strategy/57029

function main() {
    // Use the CTA strategy framework of commodity futures library 
    $.CTA(Symbols, function(st) {
        var r = st.records
        var mp = st.position.amount
        var symbol = st.symbol
        /*
        "r" represents K-line, "mp" indicates the position amount of the current variety; positive number means long position, negative number means short position, and 0 means no position; "symbol" is the variety name
        if the return value is n: 
            n = 0 : full close positions (no matter now they are long or short)
            n > 0 : if right now long positions are held, add n long positions; if now they are short positions, close n short posiitons; if n is over the position amount right now, reverse to open long positions 
            n < 0 : if right now short positions are held, add n short positions; if now they are long positions, close n long posiitons; if -n is over the position amount right now, reverse to open short positions 
        */
        if (r.length < SlowPeriod) {
            return
        }
        var cross = _Cross(TA.EMA(r, FastPeriod), TA.EMA(r, SlowPeriod));
        if (mp <= 0 && cross > ConfirmPeriod) {
            Log(symbol, "Golden Cross Period", cross, "the moment position", mp);
            return Lots * (mp < 0 ? 2 : 1)
        } else if (mp >= 0 && cross < -ConfirmPeriod) {
            Log(symbol, "Death Cross Period", cross, "the moment position", mp);
            return -Lots * (mp > 0 ? 2 : 1)
        }
    });
}

Modes of Getting CTP Data

Commodity futures use the CTP protocol, and all market quotes and order executions will be notified only after there are changes, while queries about orders, accounts, and positions are active queries. Therefore, it is suitable for writing event-driven high-frequency strategies. The interfaces for obtaining market quotes in the default mode, such as GetTicker, GetDepth and GetRecords, all need to have cached data to obtain the latest data. If there is no data, it will wait until there is data, so it is not necessary for the strategy to use “Sleep”. When there are market quote changes, ticker, depth and records will be updated. At this time, calling any of these interfaces will return immediately, and the state of the called interface will be set to “wait for update” mode. The next time when the same interface is called, it will wait until there is new data returned. Some unpopular contracts or the price limit will cause no trading for a long time, which means the strategy has stuck for a long time, also normal.

If you want to get data every time you get the market quotes, even if it is old data, you can switch to the immediate update mode of market quotes exchange.IO("mode", 0). At this time, the strategy cannot be written as event-driven, and a “SLeep” event needs to be added to avoid a fast infinite loop. Some infrequent strategies can use this mode, and the strategy design is simple. Use exchange.IO("mode", 1) to switch back to the default cache mode.

When operating a single contract, use the default mode. However, if there are multiple contracts, it is possible that one of the contracts does not update the market quotes, resulting in the interface blockage for obtaining market quotes, and the quotes updates of other contracts cannot be obtained, either. To solve this problem, the immediate update mode can be used, but it is inconvenient to write high-frequency strategies. At this time, you can use the event push mode to get the push of orders and quotes. The setting method is exchange.IO("wait"). If multiple exchange objects are added, which is rare in commodity futures, you can use exchange.IO("wait_any"), and the returned “Index” will indicate the returned platform index.

Push of market tick changes: {Event:"tick", Index: platform index (in the order of the platforms added in the bot), Nano: event of nanosecond-level time, Symbol: contract name} Order push: {Event:"order", Index:Exchange index, Nano:Event of nanosecond-level time, Order:Order information (same as GetOrder)}

By this time, the strategy structure can be written as:

function on_tick(symbol){
    Log("symbol update")
    exchange.SetContractType(symbol)
    Log(exchange.GetTicker())
}

function on_order(order){
    Log("order update", order)
}

function main(){
    while(true){
        if(exchange.IO("status")){ //Judge the link status 
            exchange.IO("mode", 0)
            _C(exchange.SetContractType, "MA888")//Subscribe to MA; only the subscription request for the first time is ture, and the later ones are program switches, which do not consume time
            _C(exchange.SetContractType, "rb888")//Subscribe to rb
            while(true){
                var e = exchange.IO("wait")
                if(e){
                    if(e.event == "tick"){
                        on_tick(e.Symbol)
                    }else if(e.event == "order"){
                        on_order(e.Order)
                    }
                }
           }
        }else{
            Sleep(10*1000)
        }
    }
}

Differences Between Commodity Futures & Cryptocurrency

Also note the difference between commodity futures and cryptocurrency platforms. For example, “GetDepth” actually only has one level of depth (5 levels of depth are expensive), and “GetTrades” cannot obtain trading history (all are simulated based on position changes, and there is no real trading record). Commodity futures have a price limit mechanism. When the limit is up, the price of a depth sell order to sell one is the limit price, and the order volume is 0. When the limit is down, the price of a buy order to buy one is the limit price, and the order volume is 0. In addition, the frequency of commodity futures query interface, such as obtaining accounts, orders, and positions, is strictly limited, generally every 2 seconds. Commodity futures also have restrictions on the amount of orders placed and cancelled.

Set Contracts

exchange.IO("instruments"): it returns the list of all contracts on the platform {contract name: details} in dictionary form, and only supports bots. exchange.IO("products"): it returns the list of all items on the platform {contract name: details} in dictionary form, and only supports bots. exchange.IO("subscribed"): it returns the subscribed market quotes on the platform in dictionary form, and only supports bots.

The ContractType of traditional CTP futures refers to the contract ID, which is case-sensitive. Such as exchange.SetContractType("au1506"). After the contract is set successfully, it will return the detailed information of the contract, such as the minimum purchase amount, service fee, delivery time, etc. When subscribing to multiple contracts, only the first time a subscription request is actually sent, and then the trading pair is just switched at the code level, which does not take time. The main continuous contract is code 888, such as MA888, the continuous rate contract is 000, such as MA000; 888 and 000 are virtual contract trades that only support backtest, and real bots only support market quotes. However, Mylanguage can operate the main contract, and the program will automatically change positions, that is, close the non-main positions and open new positions on the main positions.

Unsuccessful login cannot set contracts, but will returnn immediately, so you can try by “_c” again, to know the CTP login is completed. After the successful login, it does not consume any time to set contracts, and there will be no real network access.

Open Position & Close Position

The “Direction” of SetDirection can obtain four parameters: buy, closebuy, sell, closesell. Commodity futures have more closebuy_today and closesell_today, indicating closing the current positions; the default is closebuy/ closesell, indicating closing yesterday’s positions; only the varieties of the Shanghai Futures Exchange are divided into closing today and closing yesterday, which may affect the service fee, so it is necessary to give priority to closing yesterday’s positions. For CTP traditional futures, you can set the second parameter as “1” or “2” or “3”, which refers to “speculation”, “arbitrage”, and “hedge” respectively. If not set, the default is speculation. The specific operations such as buying and selling, obtaining positions, obtaining orders, canceling orders, and obtaining accounts are the same as cryptocurrency futures trading, so please refer to the previous section.

Operation SetDirection Parameters Function of Placing Order
Open Long Position exchange.SetDirection(“buy”) exchange.Buy()
Close Long Position exchange.SetDirection(“closebuy”) exchange.Sell()
Open Short Position exchange.SetDirection(“sell”) exchange.Sell()
Close Short Position exchange.SetDirection(“closesell”) exchange.Buy()

The following example is a specific closing position function. Note that this example is too simple. You should also consider whether it is within the trading time, how to retry the pending order if it is not completely filled, what is the maximum order volume, whether the frequency is too high, and whether it is sliding price or market price and so on. Only for reference, it is a library package of the suggested platforms for opening and closing positions in real bots:https://www.fmz.com/strategy/12961. There is a specific introduction in the library section, and it is also recommended to study the source codes of the library.

function Cover(contractType, amount, slide) {
    for (var i = 0; i < positions.length; i++) {
        if (positions[i].ContractType != contractType) {
            continue;
        }
        var depth = _C(e.GetDepth);
        if (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) {
            exchange.SetDirection(positions[i].Type == PD_LONG ? "closebuy_today" : "closebuy");
            exchange.Sell(depth.Bids[0]-slide, amount, contractType, positions[i].Type == PD_LONG ? "Close today" : "Close yesterday", 'Bid', depth.Bids[0]);
        } else {
            exchange.SetDirection(positions[i].Type == PD_SHORT ? "closesell_today" : "closesell");
            exchange.Buy(depth.Asks[0]+slide, amount, contractType, positions[i].Type == PD_SHORT ? "Close today" : "Close yesterday", 'Ask', depth.Asks[0]);
        }
    }
}

Commodity futures support custom order types (support for bots, but not for backtest), which are specified by suffix, appended to “_”, such as:

exchange.SetDirection("buy_ioc");
exchange.SetDirection("sell_gtd-20170111")

Specific suffixes:

  • ioc complete immediately, or cancel THOST_FTDC_TC_IOC
  • gfs valid in the node THOST_FTDC_TC_GFS
  • gfd valid in the day THOST_FTDC_TC_GFD
  • gtd valid before the specified date THOST_FTDC_TC_GTD
  • gtc valid before canceled THOST_FTDC_TC_GTC
  • gfa valid in auction bidding THOST_FTDC_TC_GFA

Esunny Interface

By default, the interfaces opened in commodity futures brokers are all CTP interfaces. If required, they can be replaced by Esunny interfaces. Through the encapsulation of FMZ, the invocation method is the same. The difference is that accounts, orders, and positions are all in push mode, so the docker will maintain these data locally, and will return immediately when the corresponding interface is called, without actually making a request.

Esunny Protocol Custom Order Types are:

  • gfd valid in the day TAPI_ORDER_TIMEINFORCE_GFD
  • gtc valid before canceled TAPI_ORDER_TIMEINFORCE_GTC
  • gtd valid before the specified date TAPI_ORDER_TIMEINFORCE_GTD
  • fak Partially executed, cancel the rest TAPI_ORDER_TIMEINFORCE_FAK
  • ioc execute immediately, or cancel TAPI_ORDER_TIMEINFORCE_FAK
  • fok not executed completely, cancel all TAPI_ORDER_TIMEINFORCE_FOK

Commonly Used Global Functions

Log - Log & WeChat Push

When log a log record on the bot interface, and add the character “@” after the string, the message will enter the push queue, and it will be pushed directly after binding to WeChat or telegram, such as Log('Push to WeChat@').

The log color can also be customized, such as Log('this is a log in red font #ff0000'). #ff0000 is the hexadecimal of RGB color, indicating that all log files are stored in the SqLit database of the bot in the directory where the docker is located, which can be downloaded and opened with database software, or can be used to copy backup and restore (database name and the bot id are the same).

LogProfit - Print Profits

It records the profits, and draws the profit curve on the bot interface, which can be retained after the bot is restarted. Invocation method: LogProfit(1000). Note that the parameter of LogProfit is not necessarily the profit, and it can be any number and needs to be filled in by yourself.

LogStatus - Status Display (Including Tables)

If the bot status, since the log will be saved first and refreshed continuously, needs the information only for display not for saving, you can use the LogStatus function. The paramenters of LogStatus are strings, which can also be used to represent the table information.

An example of a specific real bot position display table:

var table = {type: 'table', title: 'position information', cols: ['Column1', 'Column2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}; 
LogStatus('`' + JSON.stringify(table) + '`'); // After serialization, JSON will be added the character "'" on both sides, which is regarded as a comlpex messag format (now supporting tables)
LogStatus('The first line information\n`' + JSON.stringify(table) + '`\nthe third line information'); // the table information can be displayed in multiple lines
LogStatus('`' + JSON.stringify([table, table]) + '`'); // Multiple tables are supported to be displayed at the same time, which will be displayed in one group by TAB  
LogStatus('`' + JSON.stringify(tab1) + '`\n' + '`' + JSON.stringify(tab2) + '`\n

More