The resource loading... loading...

Global

Version

Returns the current version number of the system.

Current system version number, such as 3.6. string

Version()

function main() {
    Log("version:", Version())
}
def main():
    Log("version:", Version())
void main() {
    Log("version:", Version());
}

The system version number is the version number of the docker’s program.

Sleep

The sleep function, causing the program to pause for a period of time.

Sleep(millisecond)

The millisecond parameter is used to set the duration of sleep and the number of milliseconds. millisecond true number

function main() {
    Sleep(1000 * 10)   // Wait for 10 seconds
    Log("Waited for 10 seconds")
}
def main():
    Sleep(1000 * 10)
    Log("Waited for 10 seconds")
void main() {
    Sleep(1000 * 10);
    Log("Waited for 10 seconds");
}

For example, when executing the Sleep(1000) function, the program will sleep for 1 second. It supports operations with sleep time less than 1 millisecond, for example setting Sleep(0.1). It supports a minimum parameter of 0.000001, i.e. nanosecond hibernation, where 1 nanosecond is equal to 1e-6 milliseconds. When writing strategies in the Python language, the Sleep(millisecond) function should be used for polling interval, time-to-wait operations. It is not recommended to use the time.sleep(second) function of Python's time library. This is because using the time.sleep(second) function in a strategy makes the strategy program wait for a period of time actually when backtesting (not skipping on the time series of the backtesting system), so it causes the strategy to backtest very slowly.

IsVirtual

Determine whether the running environment of the strategy is a backtesting system.

The strategy returns a true value, e.g.: true when running in the backtesting system environment. The strategy returns a false value, e.g.: false when running in a live trading environment. bool

IsVirtual()

function main() {
    if (IsVirtual()) {
        Log("The current backtest system environment.")
    } else {
        Log("The current live trading environment.")
    }
}
def main():
    if IsVirtual():
        Log("The current backtest system environment.")
    else:
        Log("The current live trading environment.")
void main() {
    if (IsVirtual()) {
        Log("The current backtest system environment.");
    } else {
        Log("The current live trading environment.");
    }
}

Determine whether the current running environment is a backtesting system, which is used to be compatible with the difference between backtesting and live trading.

Mail

Send an email.

A successful email delivery returns a true value, e.g., true, and a failed delivery returns a false value, e.g., false. bool

Mail(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)

Used to specify the SMTP service address of the email sender. smtpServer true string Used to specify the email address of the email sender. smtpUsername true string The SMTP password for the email sender’s mailbox. smtpPassword true string Used to specify the email address of the email recipient. mailTo true string Email title. title true string Email body. body true string

function main(){
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
}
def main():
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
void main() {
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body");
}

The smtpPassword parameter sets the password for the SMTP service, not the mailbox password. When setting the smtpServer parameter, if you need to change the port, you can add the port number directly in the parameter smtpServer. For example: QQ mail smtp.qq.com:587, which is available for testing. If an error is reported: unencryped connection, you need to modify the smtpServer of the Mail function. The parameter format is: ssl://xxx.com:xxx, for example, the ssl method of SMTP for QQ mail: ssl://smtp.qq.com:465 or smtp://xxx.com:xxx. It does not work in the backtesting system.

{@fun/Global/Mail_Go Mail_Go}

Mail_Go

Asynchronous version of the Mail function.

The Mail_Go function returns a concurrent object immediately, and you can use the wait method of that concurrent object to get the result of the mail delivery. A successful mail delivery returns a true value, e.g., true, and a failed delivery returns a false value, e.g., false. object

Mail_Go(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)

It’s used to specify the SMTP service address of the email sender. smtpServer true string It’s used to specify the email address of the email sender. smtpUsername true string The SMTP password for the email sender’s mailbox. smtpPassword true string It’s used to specify the email address of the email recipient. mailTo true string Email title. title true string Email body. body true string

function main() {
    var r1 = Mail_Go("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
    var r2 = Mail_Go("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
    
    var ret1 = r1.wait()
    var ret2 = r2.wait()
    
    Log("ret1:", ret1)
    Log("ret2:", ret2)
}
# Not supported.
// Not supported.

It does not work in the backtesting system.

{@fun/Global/Mail Mail}

SetErrorFilter

Filter error logs.

SetErrorFilter(filters)

Regular expression string. filters true string

function main() {
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
}
def main():
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
void main() {
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused");
}

Filtering common errors.

function main() {
    // A random query for a non-existent order with an id of 123, allowing the interface to report an error deliberately
    var order = exchange.GetOrder("123")
    Log(order)
    // Filter http502 errors, GetOrder interface errors, after setting the error filter, the second call to GetOrder will no longer report errors
    SetErrorFilter("502:|GetOrder")
    order = exchange.GetOrder("123")
    Log(order)
}
def main():
    order = exchange.GetOrder("123")
    Log(order)
    SetErrorFilter("502:|GetOrder")
    order = exchange.GetOrder("123")
    Log(order)
void main() {
    TId orderId;
    Order order = exchange.GetOrder(orderId);
    Log(order);
    SetErrorFilter("502:|GetOrder");
    order = exchange.GetOrder(orderId);
    Log(order);
}

Filter an interface error message.

Error logs matched by this regular expression will not be uploaded to the log system. You can call it multiple times (no limit on the number of times) to set multiple filter conditions. Regular expressions set multiple times will be accumulated and take effect at the same time. You can set an empty string to reset the regular expression used to filter error logs: SetErrorFilter(""). The filtered logs are no longer written to the database file corresponding to the live trading Id in the docker directory to prevent frequent error reporting from bloating the database file.

GetPid

Get the live trading process Id.

Return the live trading process Id. string

GetPid()

function main(){
    var id = GetPid()
    Log(id)
}
def main():
    id = GetPid()
    Log(id)
void main() {
    auto id = GetPid();
    Log(id);
}

GetLastError

Get the last error message.

Last error message. string

GetLastError()

function main(){
    // Because the order number 123 does not exist, so there will be an error.
    exchange.GetOrder("123")
    var error = GetLastError()
    Log(error)
}
def main():
    exchange.GetOrder("123")
    error = GetLastError()
    Log(error)
void main() {
    // Order ID type: TId, so you can't pass in a string, we place an order that doesn't meet the exchange specification to trigger
    exchange.GetOrder(exchange.Buy(1, 1));
    auto error = GetLastError();
    Log(error);
}

It does not work in the backtesting system.

GetCommand

Gets the strategy interaction command.

The format of the returned command is ControlName:Data. ControlName is the name of the control, and Data is the data entered in the control. If the interactive control does not have input boxes, drop-down boxes and other components (for example, a button control without input boxes) then the returned command format is ControlName, which returns the control name only. string

GetCommand()

function main(){
    while(true) { 
        var cmd = GetCommand()
        if (cmd) { 
            Log(cmd)
        }
        Sleep(1000) 
    }
}
def main():
    while True:
        cmd = GetCommand()
        if cmd:
            Log(cmd)
        Sleep(1000)
void main() {
    while(true) {
        auto cmd = GetCommand();
        if(cmd != "") {
            Log(cmd);
        }
        Sleep(1000);
    }
}

Detects the interaction command and uses the Log function to output the interaction command when it is detected.

function main() {
    while (true) {
        LogStatus(_D())
        var cmd = GetCommand()
        if (cmd) {
            Log("cmd:", cmd)    
            var arr = cmd.split(":")
            if (arr[0] == "buy") {
                Log("Buy, the control without number")
            } else if (arr[0] == "sell") {
                Log("Sell, the control with the number of:", arr[1])
            } else {
                Log("Other controls trigger:", arr)
            }
        }
        Sleep(1000)
    } 
}
def main():
    while True:
        LogStatus(_D())
        cmd = GetCommand()
        if cmd:
            Log("cmd:", cmd)
            arr = cmd.split(":")
            if arr[0] == "buy":
                Log("Buy, the control without number")
            elif arr[0] == "sell":
                Log("Sell, the control with the number of:", arr[1])
            else:
                Log("Other controls trigger:", arr)
        Sleep(1000)
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
void split(const string& s,vector<string>& sv,const char flag = ' ') {
    sv.clear();
    istringstream iss(s);
    string temp;            

    while (getline(iss, temp, flag)) {
        sv.push_back(temp);
    }
    return;
}            

void main() {
    while(true) {
        LogStatus(_D());
        auto cmd = GetCommand();
        if (cmd != "") {
            vector<string> arr;
            split(cmd, arr, ':');
            if(arr[0] == "buy") {
                Log("Buy, the control without number");
            } else if (arr[0] == "sell") {
                Log("Sell, the control with the number of:", arr[1]);
            } else {
                Log("Other controls trigger:", arr);
            }
        }
        Sleep(1000);
    }
}

For example, the strategy interactive control adds a control without an input box, the interactive control is named: buy, the control description information is: buy, which is a button control. Continue by adding a control with an input box. The interactive control is named: sell and the control description message is: sell, which is an interactive control that is a combination of a button and an input box. The interaction code is designed in the strategy to respond to the different interaction controls:

It does not work in the backtesting system.

GetMeta

Get the value of Meta written when generating the strategy registration code.

Meta data. string

GetMeta()

function main() {
    // The maximum asset value of the denominated currency allowed by the strategy.
    var maxBaseCurrency = null
    
    // Get the metadata when creating the registration code.
    var level = GetMeta()
    
    // Detecting the conditions corresponding to Meta.
    if (level == "level1") {
        // -1 for unrestricted
        maxBaseCurrency = -1       
    } else if (level == "level2") {
        maxBaseCurrency = 10     
    } else if (level == "level3") {
        maxBaseCurrency = 1
    } else {
        maxBaseCurrency = 0.5
    }
    
    while(1) {
        Sleep(1000)
        var ticker = exchange.GetTicker()
        
        // Detect asset values
        var acc = exchange.GetAccount()
        if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Stocks + acc.FrozenStocks) {
            // Stop executing strategy trading logic
            LogStatus(_D(), "level:", level, "Positions exceeding the usage limit of the registration code will no longer execute the strategy trading logic!")
            continue
        }
        
        // Other trading logic
        
        // Normal output of status bar information
        LogStatus(_D(), "level:", level, "The strategy is working properly! ticker data: \n", ticker)
    }
}
def main():
    maxBaseCurrency = null
    level = GetMeta()
    
    if level == "level1":
        maxBaseCurrency = -1       
    elif level == "level2":
        maxBaseCurrency = 10     
    elif level == "level3":
        maxBaseCurrency = 1
    else:
        maxBaseCurrency = 0.5
    
    while True:
        Sleep(1000)
        ticker = exchange.GetTicker()        
        acc = exchange.GetAccount()
        if maxBaseCurrency != -1 and maxBaseCurrency < acc["Stocks"] + acc["FrozenStocks"]:
            LogStatus(_D(), "level:", level, "Positions exceeding the usage limit of the registration code will no longer execute the strategy trading logic!")
            continue        
        
        # Other trading logic
        
        # Normal output of status bar information
        LogStatus(_D(), "level:", level, "The strategy is working properly! ticker data: \n", ticker)
void main() {
    auto maxBaseCurrency = 0.0;
    auto level = GetMeta();
    
    if (level == "level1") {
        maxBaseCurrency = -1;  
    } else if (level == "level2") {
        maxBaseCurrency = 10;
    } else if (level == "level3") {
        maxBaseCurrency = 1;
    } else {
        maxBaseCurrency = 0.5;
    }
    
    while(1) {
        Sleep(1000);
        auto ticker = exchange.GetTicker();  
        auto acc = exchange.GetAccount();
        if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Stocks + acc.FrozenStocks) {
            // Stop execution strategy trading logic.
            LogStatus(_D(), "level:", level, "Positions exceeding the usage limit of the registration code will no longer execute the strategy trading logic!");
            continue;
        }
        
        // Other trading logic
        
        // Normal output of status bar information
        LogStatus(_D(), "level:", level, "The strategy is working properly! ticker data: \n", ticker);
    }
}

Example application scenario: Use Meta to limit the amount of assets operated by the strategy.

Application scenario: need to do capital limits for different strategy renters. The length of the Meta value set when generating the registration code cannot exceed 190 characters, and the GetMeta() function only supports live trading. If no metadata (Meta) is set when generating a strategy registration code, the GetMeta() function returns null. It does not work in the backtesting system.

Dial

For primitive Socket access, supporting tcp, udp, tls, unix protocols. Support 4 popular communication protocols: mqtt, nats, amqp, kafka. Support for connecting to databases: sqlite3, mysql, postgres, clickhouse.

The Dial() function returns null if it times out. A normal call returns a connection object that has three methods: read, write and close. The read method is used to read data, the write method is used to send data and the close method is used to close the connection. The read method supports the following parameters:

  • When no parameters are passed, it blocks until a message is available and returns, such as ws.read().
  • When passed in as a parameter, the unit is milliseconds, specifying the message wait timeout period. For example: ws.read(2000) specifies a timeout of two seconds (2000 milliseconds).
  • The following two parameters are valid only for WebSocket: Passing the parameter -1 means that the function returns immediately, regardless of the presence or absence of messages, for example: ws.read(-1). Passing the parameter -2 means that the function returns immediately with or without a message, but only the latest message is returned, and the buffered message is discarded. For example, ws.read(-2).

read() function buffer description: The incoming data pushed by the WebSocket protocol may cause data accumulation if the time interval between strategy read() function calls is too long. These data are stored in the buffer, which has a data structure of a queue with a maximum of 2000. After 2000 is exceeded, the newest data enters the buffer and the oldest data is cleared out.

Scenario No parameter Parameter: -1 Parameter: -2 Parameter: 2000, in milliseconds
Data already in the buffer Return oldest data immediately Return oldest data immediately Return latest data immediately Return oldest data immediately
No data in the buffer Return when blocked to data Return null immediately Return null immediately Wait 2000 ms, return null if no data, return null if there is data
WebSocket connection is disconnected or reconnected by the underlying read() function returns the empty string, i.e.: “”, and write() function returns 0. The situation is detected. You can close the connection using the close() function, or if you have set up automatic reconnection, you don’t need to close it, the system underlying will reconnect it automatically.

object

Dial(address) Dial(address, timeout)

Request address. address true string timeout seconds, timeout false number

function main(){
    // Dial supports tcp://,udp://,tls://,unix://protocol, you can add a parameter to specify the number of seconds for the timeout
    var client = Dial("tls://www.baidu.com:443")  
    if (client) {
        // write can be followed by a numeric parameter to specify the timeout, write returns the number of bytes successfully sent
        client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
        while (true) {
            // read can be followed by a numeric parameter specifying the timeout in milliseconds. Returning null indicates an error or timeout or that the socket has been closed
            var buf = client.read()
            if (!buf) {
                 break
            }
            Log(buf)
        }
        client.close()
    }
}
def main():
    client = Dial("tls://www.baidu.com:443")
    if client:
        client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
        while True:
            buf = client.read()
            if not buf:
                break
            Log(buf)
        client.close()
void main() {
    auto client = Dial("tls://www.baidu.com:443");
    if(client.Valid) {
        client.write("GET / HTTP/1.1\nConnection: Closed\n\n");
        while(true) {
            auto buf = client.read();
            if(buf == "") {
                break;
            }
            Log(buf);
        }
        client.close();
    }
}

Example of a Dial function call:

function main() {
    LogStatus("Connecting...")
    // Accessing WebSocket interface of Binance
    var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
    if (!client) {
        Log("Connection failed, program exited")
        return
    }
    
    while (true) {
        // read returns only the data retrieved after the read call
        var buf = client.read()      
        if (!buf) {
            break
        }
        var table = {
            type: 'table',
            title: 'Ticker Chart',
            cols: ['Currency', 'Highest', 'Lowest', 'Buy 1', 'Sell 1', 'Last traded price', 'Volume', 'Update time'],
            rows: []
        }
        var obj = JSON.parse(buf)
        _.each(obj, function(ticker) {
            table.rows.push([ticker.s, ticker.h, ticker.l, ticker.b, ticker.a, ticker.c, ticker.q, _D(ticker.E)])
        })
        LogStatus('`' + JSON.stringify(table) + '`')
    }
    client.close()
}
import json
def main():
    LogStatus("Connecting...")
    client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
    if not client:
        Log("Connection failed, program exited")
        return 
    
    while True:
        buf = client.read()
        if not buf:
            break
        table = {
            "type" : "table", 
            "title" : "Ticker Chart", 
            "cols" : ['Currency', 'Highest', 'Lowest', 'Buy 1', 'Sell 1', 'Last traded price', 'Volume', 'Update time'], 
            "rows" : [] 
        }
        obj = json.loads(buf)
        for i in range(len(obj)):
            table["rows"].append([obj[i]["s"], obj[i]["h"], obj[i]["l"], obj[i]["b"], obj[i]["a"], obj[i]["c"], obj[i]["q"], _D(int(obj[i]["E"]))])
        LogStatus('`' + json.dumps(table) + '`')
    client.close()
void main() {
    LogStatus("Connecting...");
    auto client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr");
    if(!client.Valid) {
        Log("Connection failed, program exited");
        return;
    }
    
    while(true) {
        auto buf = client.read();
        if(buf == "") {
            break;
        }
        json table = R"({
            "type" : "table", 
            "title" : "Ticker Chart", 
            "cols" : ["Currency", "Highest", "Lowest", "Buy 1", "Sell 1", "Last traded price", "Volume", "Update time"], 
            "rows" : []
        })"_json;
        json obj = json::parse(buf);
        for(auto& ele : obj.items()) {
            table["rows"].push_back({ele.value()["s"], ele.value()["h"], ele.value()["l"], ele.value()["b"], ele.value()["a"], ele.value()["c"], 
                ele.value()["q"], _D(ele.value()["E"])});
        }
        LogStatus("`" + table.dump() + "`");
    }
    client.close();
}

To access the WebSocket ticker interface of Binance:

var ws = null 
function main(){
    var param = {
        "op": "subscribe",
        "args": [{
            "channel": "tickers",
            "instId": "BTC-USDT"
        }]
    }
    // When calling Dial function, specify reconnect=true to set reconnection mode and payload to be the message sent when reconnecting. When the WebSocket connection is disconnected, it will reconnect and send messages automatically.
    ws = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true&payload="+ JSON.stringify(param))
    if(ws){
        var pingCyc = 1000 * 20
        var lastPingTime = new Date().getTime()
        while(true){
            var nowTime = new Date().getTime()
            var ret = ws.read()
            Log("ret:", ret)
            if(nowTime - lastPingTime > pingCyc){
                var retPing = ws.write("ping")
                lastPingTime = nowTime
                Log("Send : ping", "#FF0000")
            }
            LogStatus("Current time:", _D())
            Sleep(1000)
        }
    }
}              

function onexit() {
    ws.close() 
    Log("exit")
}
import json
import time              

ws = None
def main():
    global ws 
    param = {
        "op": "subscribe",
        "args": [{
            "channel": "tickers",
            "instId": "BTC-USDT"
        }]
    }
    ws = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true&payload=" + json.dumps(param))
    if ws:
        pingCyc = 1000 * 20
        lastPingTime = time.time() * 1000
        while True:
            nowTime = time.time() * 1000
            ret = ws.read()
            Log("ret:", ret)
            if nowTime - lastPingTime > pingCyc:
                retPing = ws.write("ping")
                lastPingTime = nowTime
                Log("Send: ping", "#FF0000")
            LogStatus("Current time:", _D())
            Sleep(1000)              

def onexit():
    ws.close()
    Log("exit")
auto objWS = Dial("wss://ws.okx.com:8443/ws/v5/public|compress=gzip_raw&mode=recv&reconnect=true");              

void main() {
    json param = R"({
        "op": "subscribe",
        "args": [{
            "channel": "tickers",
            "instId": "BTC-USDT"
        }]
    })"_json;
    
    objWS.write(param.dump());
    if(objWS.Valid) {
        uint64_t pingCyc = 1000 * 20;
        uint64_t lastPingTime = Unix() * 1000;
        while(true) {
            uint64_t nowTime = Unix() * 1000;
            auto ret = objWS.read();
            Log("ret:", ret);
            if(nowTime - lastPingTime > pingCyc) {
                auto retPing = objWS.write("ping");
                lastPingTime = nowTime;
                Log("Send: ping", "#FF0000");
            }
            LogStatus("Current time:", _D());
            Sleep(1000);
        }
    }
}              

void onexit() {
    objWS.close();
    Log("exit");
}

Access to OKX’s WebSocket ticker interface:

var ws = null               

function main(){
    var param = {"sub": "market.btcusdt.detail", "id": "id1"}
    ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload="+ JSON.stringify(param))
    if(ws){
        while(1){
            var ret = ws.read()
            Log("ret:", ret)
            // Respond to heartbeat packet operations
            try {
                var jsonRet = JSON.parse(ret)
                if(typeof(jsonRet.ping) == "number") {
                    var strPong = JSON.stringify({"pong" : jsonRet.ping})
                    ws.write(strPong)
                    Log("Respond to ping, send pong:", strPong, "#FF0000")
                }
            } catch(e) {
                Log("e.name:", e.name, "e.stack:", e.stack, "e.message:", e.message)
            }
            
            LogStatus("Current time:", _D())
            Sleep(1000)
        }
    }
}              

function onexit() {
    ws.close() 
    Log("Execute the ws.close() function")
}
import json
ws = None              

def main():
    global ws
    param = {"sub" : "market.btcusdt.detail", "id" : "id1"}
    ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload=" + json.dumps(param))
    if ws:
        while True:
            ret = ws.read()
            Log("ret:", ret)              
            # Respond to heartbeat packet operations
            try:
                jsonRet = json.loads(ret)
                if "ping" in jsonRet and type(jsonRet["ping"]) == int:
                    strPong = json.dumps({"pong" : jsonRet["ping"]})
                    ws.write(strPong)
                    Log("Respond to ping, send pong:", strPong, "#FF0000")
            except Exception as e:
                Log("e:", e)
                
            LogStatus("Current time:", _D())
            Sleep(1000)
    
def onexit():
    ws.close()
    Log("Execute the ws.close() function")  
using namespace std;
void main() {
    json param = R"({"sub" : "market.btcusdt.detail", "id" : "id1"})"_json;
    auto ws = Dial("wss://api.huobi.pro/ws|compress=gzip&mode=recv&reconnect=true&payload=" + param.dump());
    if(ws.Valid) {
        while(true) {
            auto ret = ws.read();
            Log("ret:", ret);              
            // Respond to heartbeat packet operations
            try 
            {
                auto jsonRet = json::parse(ret);
                if(jsonRet["ping"].is_number()) {
                    json pong = R"({"pong" : 0})"_json;
                    pong["pong"] = jsonRet["ping"];
                    auto strPong = pong.dump();
                    ws.write(strPong);
                    Log("Respond to ping, send pong:", strPong, "#FF0000");
                }
            } catch(exception &e) 
            {
                Log("e:", e.what());
            }
            
            LogStatus("Current time:", _D());
            Sleep(1000);
        }
    }
}              

void onexit() {
    // ws.close();
    Log("Execute the ws.close() function");
}

Access to Huobi’s WebSocket ticker interface:

function getLogin(pAccessKey, pSecretKey, pPassphrase) {
    // Signature function for login
    var ts = (new Date().getTime() / 1000).toString()
    var login = {
        "op": "login",
        "args":[{
            "apiKey"    : pAccessKey,
            "passphrase" : pPassphrase,
            "timestamp" : ts,
            "sign" : exchange.HMAC("sha256", "base64", ts + "GET" + "/users/self/verify", pSecretKey)   // exchange.HMAC has been deprecated and is temporarily supported. Please use the latest exchange.Encode function instead.
        }]
    }    
    return login
}                

var client_private = null 
function main() {
    // Because the read function uses a timeout setting, filtering the timeout reports errors that would otherwise be output with redundant errors
    SetErrorFilter("timeout")
    
    // Position channel subscription information
    var posSubscribe = {
        "op": "subscribe",
        "args": [{
            "channel": "positions",
            "instType": "ANY"
        }]
    }                

    var accessKey = "xxx"
    var secretKey = "xxx"
    var passphrase = "xxx"            

    client_private = Dial("wss://ws.okx.com:8443/ws/v5/private")
    client_private.write(JSON.stringify(getLogin(accessKey, secretKey, passphrase)))
    Sleep(3000)  // When logging in, you cannot subscribe to private channels immediately, you need to wait for server response
    client_private.write(JSON.stringify(posSubscribe))
    if (client_private) {
        var lastPingTS = new Date().getTime()
        while (true) {
            var buf = client_private.read(-1)
            if (buf) {
                Log(buf)
            }
            
            // Detect disconnection, reconnect
            if (buf == "" && client_private.write(JSON.stringify(posSubscribe)) == 0) {
                Log("Disconnection detected, close connection, reconnect")
                client_private.close()
                client_private = Dial("wss://ws.okx.com:8443/ws/v5/private")
                client_private.write(JSON.stringify(getLogin(accessKey, secretKey, passphrase)))
                Sleep(3000)
                client_private.write(JSON.stringify(posSubscribe))
            }
            
            // Send heartbeat packets
            var nowPingTS = new Date().getTime()
            if (nowPingTS - lastPingTS > 10 * 1000) {
                client_private.write("ping")
                lastPingTS = nowPingTS
            }            
        }        
    }
}                

function onexit() {    
    var ret = client_private.close()
    Log("Close the connection!", ret)
}
import json
import time
  
def getLogin(pAccessKey, pSecretKey, pPassphrase):
    ts = str(time.time())
    login = {
        "op": "login",
        "args":[{
            "apiKey"    : pAccessKey,
            "passphrase" : pPassphrase,
            "timestamp" : ts,
            "sign" : exchange.HMAC("sha256", "base64", ts + "GET" + "/users/self/verify", pSecretKey)
        }]
    }
    return login                 

client_private = None 
def main():
    global client_private
    SetErrorFilter("timeout")
    
    posSubscribe = {
        "op": "subscribe",
        "args": [{
            "channel": "positions",
            "instType": "ANY"
        }]
    }                  

    accessKey = "xxx"
    secretKey = "xxx"
    passphrase = "xxx"
    
    client_private = Dial("wss://ws.okx.com:8443/ws/v5/private")
    client_private.write(json.dumps(getLogin(accessKey, secretKey, passphrase)))
    Sleep(3000)
    client_private.write(json.dumps(posSubscribe))
    if client_private:
        lastPingTS = time.time() * 1000
        while True:
            buf = client_private.read(-1)
            if buf:
                Log(buf)
            
            if buf == "" and client_private.write(json.dumps(posSubscribe)) == 0:
                Log("Disconnection detected, close connection, reconnect")
                ret = client_private.close()
                client_private = Dial("wss://ws.okx.com:8443/ws/v5/private")
                client_private.write(json.dumps(getLogin(accessKey, secretKey, passphrase)))
                Sleep(3000)
                client_private.write(json.dumps(posSubscribe))
            
            nowPingTS = time.time() * 1000
            if nowPingTS - lastPingTS > 10 * 1000:
                client_private.write("ping")
                lastPingTS = nowPingTS                

def onexit():
    ret = client_private.close()
    Log("Close the connection!", ret)
auto client_private = Dial("wss://ws.okx.com:8443/ws/v5/private");                  

json getLogin(string pAccessKey, string pSecretKey, string pPassphrase) {
    auto ts = std::to_string(Unix());
    json login = R"({
        "op": "login",
        "args": [{
            "apiKey": "",
            "passphrase": "",
            "timestamp": "",
            "sign": ""
        }]
    })"_json;
    login["args"][0]["apiKey"] = pAccessKey;
    login["args"][0]["passphrase"] = pPassphrase;
    login["args"][0]["timestamp"] = ts;
    login["args"][0]["sign"] = exchange.HMAC("sha256", "base64", ts + "GET" + "/users/self/verify", pSecretKey);
    return login;
}                  

void main() {
    SetErrorFilter("timeout");
    json posSubscribe = R"({
        "op": "subscribe",
        "args": [{
            "channel": "positions",
            "instType": "ANY"
        }]
    })"_json;
    
    auto accessKey = "xxx";
    auto secretKey = "xxx";
    auto passphrase = "xxx";
    
    client_private.write(getLogin(accessKey, secretKey, passphrase).dump());
    Sleep(3000);
    client_private.write(posSubscribe.dump());                

    if (client_private.Valid) {
        uint64_t lastPingTS = Unix() * 1000;                  

        while (true) {
            auto buf = client_private.read(-1);
            if (buf != "") {
                Log(buf);
            }
            if (buf == "") {
                if (client_private.write(posSubscribe.dump()) == 0) {
                    Log("Disconnection detected, close connection, reconnect");
                    client_private.close();
                    client_private = Dial("wss://ws.okx.com:8443/ws/v5/private");
                    client_private.write(getLogin(accessKey, secretKey, passphrase).dump());
                    Sleep(3000);
                    client_private.write(posSubscribe.dump());
                }
            }
            
            uint64_t nowPingTS = Unix() * 1000;
            if (nowPingTS - lastPingTS > 10 * 1000) {
                client_private.write("ping");
                lastPingTS = nowPingTS;
            }
        }
    }
}                  

void onexit() {
    client_private.close();
    Log("exit");
}

To access OKX’s WebSocket authentication interface:

var client = null 
function main() {
    // client = Dial("sqlite3://:memory:")   // Using an in-memory database
    client = Dial("sqlite3://test1.db")      // Open/connect to the database file in the docker's directory
    
    // record handle
    var sqlite3Handle = client.fd()
    Log("sqlite3Handle:", sqlite3Handle)
    
    // Querying tables in the database
    var ret = client.exec("SELECT name FROM sqlite_master WHERE type='table'")
    Log(ret)
}

function onexit() {
    Log("Execute client.close()")
    client.close()
}
// Not supported
// Not supported

The connection object returned by the Dial function when connecting to a database has two method functions that are unique to it:

  • exec(sqlString): Used to execute SQL statements in a manner similar to the DBExec() function.
  • fd(): The fd() function returns a handle (e.g., the handle variable is handle) to be used by other threads to reconnect (even if the object created by Dial has already been closed by the execution of the close() function to close the connection) by passing the handle into the Dial() function, for example, Dial(handle) reuse connection. The following is an example of the Dial function connecting to a sqlite3 database.

Details of the address parameter, separated by the | symbol after the normal address: wss://ws.okx.com:8443/ws/v5/public. If there are | characters in the parameter string, then || is used as the separator sign. The part after that is some function parameter settings, and each parameter is connected with & characters. For example, the ss5 proxy and compression parameters can be set together as follows: Dial("wss://ws.okx.com:8443/ws/v5/public|proxy=socks5://xxx:9999&compress=gzip_raw&mode=recv")

Functions supported by the address parameter of the Dial function Parameter description
Parameters related to WebSocket protocol data compression: compress=parameter value compress is the compression method, compress parameter options are: gzip_raw, gzip, etc. If the gzip method is not standard gzip, you can use the extended method: gzip_raw
Parameters related to WebSocket protocol data compression: mode=parameter value mode is the compression mode, mode parameter can be dual, send, recv. dual is two-way compression, send compressed data, receive compressed data. send is send compressed data. recv is receive compressed data, local decompression.
WebSocket protocol sets the underlying auto-reconnect related parameters: reconnect=parameter value reconnect is whether to set reconnect, reconnect=true is to enable reconnect. The default is no reconnect when this parameter is not set.
WebSocket protocol sets the underlying auto-reconnect related parameters: interval=parameter value interval is the retry interval, in milliseconds, interval=10000 is the retry interval of 10 seconds, the default is 1 second when it’s not set, that is, interval=1000.
WebSocket protocol sets the underlying auto-reconnect related parameters: payload=parameter value payload is the subscription message that needs to be sent when the WebSocket is reconnected, e.g.: payload=okokok.
Parameters related to socks5 proxy: proxy=parameter value proxy is ss5 proxy setting, parameter value format: socks5://name:pwd@192.168.0.1:1080, name is ss5 server username, pwd is ss5 server login password, 1080 is ss5 service port.

The Dial() function is only supported for live trading. When connecting to a database using the Dial function, the connection string is written with reference to the go language driver project for each database.

Databases supported Driving projects Connection string Remarks
sqlite3 github.com/mattn/go-sqlite3 sqlite3://file:test.db?cache=shared&mode=memory The sqlite3:// prefix indicates that a sqlite3 database is being used, example call: Dial("sqlite3://test1.db")
mysql github.com/go-sql-driver/mysql mysql://username:yourpassword@tcp(localhost:3306)/yourdatabase?charset=utf8mb4
postgres github.com/lib/pq postgres://user=postgres dbname=yourdatabase sslmode=disable password=yourpassword host=localhost port=5432
clickhouse github.com/ClickHouse/clickhouse-go clickhouse://tcp://host:9000?username=username&password=yourpassword&database=youdatabase

Please note that when the payload content set in the address parameter contains characters = or other special characters, it may affect the parsing of the address parameter of the Dial function, such as the following example.

backPack Exchange websocket private interface call example:

var client = null

function main() {
    // Base64-encoded public key of the key pair, i.e. the access key configured on FMZ
    var base64ApiKey = "xxx"

    var ts = String(new Date().getTime())
    var data = "instruction=subscribe&timestamp=" + ts + "&window=5000"

    // Since signEd25519 returns a base64 encoding, it contains the character "="
    var signature = signEd25519(data)
    
    // The payload may contain the character "=" after being encoded by JSON
    payload = {
        "method": "SUBSCRIBE",
        "params": ["account.orderUpdate"],
        "signature": [base64ApiKey, signature, ts, "5000"]
    }

    client = Dial("wss://ws.backpack.exchange")
    client.write(JSON.stringify(payload))
    if (!client) {
        Log("Connection failed, program exited")
        return
    }
    
    while (true) {
        var buf = client.read()      
        Log(buf)
    }    
}

function onexit() {
    client.close()
}

function signEd25519(data) {
    return exchange.Encode("ed25519.seed", "raw", "base64", data, "base64", "{{secretkey}}")
}

The following call in the code works fine:

client = Dial("wss://ws.backpack.exchange")
client.write(JSON.stringify(payload))

If you write it directly in payload, it will not work properly, for example:

client = Dial("wss://ws.backpack.exchange|payload=" + JSON.stringify(payload))

Currently, only JavaScript supports the use of the mqtt, nats, amqp, and kafka communication protocols in the Dial function. The JavaScript language strategy code is used as an example to show the use of the four protocols: mqtt, nats, amqp, and kafka:

// We need to configure and deploy proxy servers for each protocol first.
// For the sake of demonstration, the subscription (read operation) and publishing (write operation) of the topic test_topic are all performed in the current strategy.
var arrConn = []
var arrName = []

function main() {
    LogReset(1)
    conn_nats = Dial("nats://admin@127.0.0.1:4222?topic=test_topic")
    conn_mqtt = Dial("mqtt://127.0.0.1:1883?topic=test_topic")
    conn_amqp = Dial("amqp://q:admin@127.0.0.1:5672/?queue=test_Queue")
    conn_kafka = Dial("kafka://localhost:9092/test_topic")
    arrConn = [conn_nats, conn_amqp, conn_mqtt, conn_kafka]
    arrName = ["nats", "amqp", "mqtt", "kafka"]

    while (true) {
        for (var i in arrConn) {
            var conn = arrConn[i]
            var name = arrName[i]

            // Write data
            conn.write(name + ", time: " + _D() + ", test msg.")
            
            // Read data
            var readMsg = conn.read(1000)
            Log(name + " readMsg: ", readMsg, "#FF0000")
        }

        Sleep(1000)
    }
}

function onexit() {
    for (var i in arrConn) {
        arrConn[i].close()
        Log("close", arrName[i], "connect")
    }
}

Detailed documentation reference: Exploring FMZ: Practice of Communication Protocol Between Live Trading Strategies

HttpQuery

Send an Http request.

Returns the response data of the request. If the return value is a JSON string, it can be parsed by the JSON.parse() function in the JavaScript language strategy, and by the json::parse() function in the C++ language strategy. If debug is set to true in the options structure, the return value is an object (JSON); if debug is set to false, the return value is a string. string, object

HttpQuery(url) HttpQuery(url, options)

Http request url. url true string Http request-related settings, for example, can be structured as follows:

{
    method: "POST",
    body: "a=10&b=20&c=30",
    charset: "UTF-8",
    cookie: "session_id=12345; lang=en",
    profile: "chrome_103",
    debug: false,
    headers: {"TEST-HTTP-QUERY": "123"},
    timeout: 1000
}
  • profile: Used to simulate browser tls fingerprints. The supported settings include the following options: chrome_:"chrome_103", "chrome_104", "chrome_105", "chrome_106", "chrome_107", "chrome_108", "chrome_109", "chrome_110", "chrome_111", "chrome_112", "chrome_117", safari_:"safari_15_6_1", "safari_16_0", "safari_ipad_15_6", "safari_ios_15_5", "safari_ios_15_6", "safari_ios_16_0", firefox_:"firefox_102", "firefox_104", "firefox_105", "firefox_106", "firefox_108", "firefox_110", "firefox_117", opera_:"opera_89", "opera_90", "opera_91", zalando_:"zalando_android_mobile", "zalando_ios_mobile", nike_:"nike_ios_mobile", "nike_android_mobile", cloudscraper:"cloudscraper", mms_:"mms_ios", mesh_:"mesh_ios", "mesh_ios_1", "mesh_ios_2", "mesh_android", "mesh_android_1", "mesh_android_2", confirmed_:"confirmed_ios", "confirmed_android", okhttp4_:"okhttp4_android_7", "okhttp4_android_8", "okhttp4_android_9", "okhttp4_android_10", "okhttp4_android_11", "okhttp4_android_12", "okhttp4_android_13",
  • debug: When it’s set to true, the HttpQuery function call returns the full reply message. When it’s set to false, only the data in the Body of the reply message is returned.
  • timeout: Timeout setting, set 1000 means 1 second timeout.
  • charset: It supports transcoding of the requested response data, such as GB18030. It supports common encoding. All fields in this structure are optional, for example, the profile field can be left out.

options false object

function main(){
    // An example of GET access without parameters
    var info = JSON.parse(HttpQuery("https://www.okx.com/api/v5/public/time"))
    Log(info)
    // An example of GET access with parameters
    var ticker = JSON.parse(HttpQuery("https://www.okx.com/api/v5/market/books?instId=BTC-USDT"))
    Log(ticker)
}
import json
import urllib.request
def main():
    # HttpQuery does not support Python, you can use the urllib/urllib2 library instead
    info = json.loads(urllib.request.urlopen("https://www.okx.com/api/v5/public/time").read().decode('utf-8'))
    Log(info)
    ticker = json.loads(urllib.request.urlopen("https://www.okx.com/api/v5/market/books?instId=BTC-USDT").read().decode('utf-8'))
    Log(ticker)
void main() {
    auto info = json::parse(HttpQuery("https://www.okx.com/api/v5/public/time"));
    Log(info);
    auto ticker = json::parse(HttpQuery("https://www.okx.com/api/v5/market/books?instId=BTC-USDT"));
    Log(ticker);
}

An example of accessing the OKX public ticker API interface.

function main() {
    // Setting proxy and sending an http request for this time, no username, no password, this http request will be sent through the proxy
    HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/")            

    // Setting proxy and sending an http request for this time, enter the user name and password, only the current call to HttpQuery takes effect, and then call HttpQuery again ("http://www.baidu.com") so that the proxy will not be used.
    HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/")
}
# HttpQuery does not support Python, you can use the urllib/urllib2 library instead
void main() {
    HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/");
    HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/");
}

The HttpQuery function uses proxy settings.

The HttpQuery() function only supports JavaScript, C++ language, Python language can use the urllib library to send Http requests directly. The HttpQuery() is mainly used to access the interfaces of the exchange that do not require a signature, such as public interfaces like ticker information. The HttpQuery() can be used in the backtesting system to send requests (only GET requests are supported) to obtain data. Backtesting is limited to using 20 visits to different URLs, and HttpQuery() visits will cache data. When the same URL is accessed a second time, the HttpQuery() function returns the cached data and no more actual network requests occur.

{@fun/Global/HttpQuery_Go HttpQuery_Go}

HttpQuery_Go

Sends an Http request, an asynchronous version of the HttpQuery function.

The HttpQuery_Go() function immediately returns a concurrent object that can be used to obtain the result of an Http request using the wait method of the The JSON.parse() function can be used to parse the JSON.parse() function in the JavaScript language’s strategy.
object

HttpQuery_Go(url) HttpQuery_Go(url, options)

Http request url. url true string Http request-related settings, for example, can be structured as follows:

{
    method: "POST",
    body: "a=10&b=20&c=30",
    charset: "UTF-8",
    cookie: "session_id=12345; lang=en",
    // profile: "",
    debug: false,
    headers: {"TEST-HTTP-QUERY": "123"},
    timeout: 1000
}
  • profile: Used to simulate browser tls fingerprints.
  • debug: When it’s set to true, this HttpQuery_Go function call returns the full reply message. When it’s set to false, only the data in the Body of the reply message is returned.
  • timeout: Timeout setting, set 1000 means 1 second timeout. All fields in this structure are optional, for example, the profile field can be left out.

options false object

function main() {
    // Create the first asynchronous thread
    var r1 = HttpQuery_Go("https://www.okx.com/api/v5/market/tickers?instType=SPOT")
    // Create the second asynchronous thread
    var r2 = HttpQuery_Go("https://api.huobi.pro/market/tickers")
    
    // Get the return value of the first asynchronous thread call
    var tickers1 = r1.wait()
    // Get the return value of the second asynchronous thread call
    var tickers2 = r2.wait()
    
    // Print results
    Log("tickers1:", tickers1)
    Log("tickers2:", tickers2)
}
# Not supported
// Not supported

Asynchronous access to the exchange’s public interface for aggregated ticker data.

The HttpQuery_Go() function only supports JavaScript, the Python language can be used with the urllib library to send Http requests directly. The HttpQuery_Go() is mainly used to access interfaces that do not require a signature on the exchange, such as public interfaces like ticker information. The HttpQuery_Go function is not supported in the backtesting system.

{@fun/Global/HttpQuery HttpQuery}

Encode

This function encodes the data according to the parameters passed in.

The Encode function returns the data after encoding and encryption. string

Encode(algo, inputFormat, outputFormat, data) Encode(algo, inputFormat, outputFormat, data, keyFormat, key)

The parameter algo is the algorithm used in the encoding calculation. The support setting is: raw (no algorithm is used), the"sign", “signTx”, “md4”, “md5”, “sha256”, “sha512”, “sha1”, “keccak256”, “sha3.224”, “sha3.256”, “sha3.384”, “sha3.512”, “sha3.keccak256”, “sha3.keccak512”, “sha512.384”, “sha512.256”, “sha512.224”, “ripemd160”, “blake2b.256”, “blake2b.512”, “blake2s.128”, “blake2s.256”. The parameter algo also supports: “text.encoder.utf8”, “text.decoder.utf8”, “text.encoder.gbk”, “text.decoder.gbk”, encode and decode strings. The parameter algo also supports: “ed25519” algorithm. Supports the use of different hash algorithms, for example, the parameter algo can be written as “ed25519.md5”, “ed25519.sha512”, etc. It supports ed25519.seed calculation. algo true string Used to specify the data format of the data parameter. The inputFormat parameter can be set as one of the followings: raw, hex, base64, string. “raw” means the data is raw data, “hex” means the data is hex encoded, “base64” means the data is base64 encoded, and “string” means the data is a string. inputFormat true string Used to specify the data format of the output. The outputFormat parameter can be set as one of the followings: raw, hex, base64, string. “raw” means the data is raw data, “hex” means the data is hex encoded, “base64” means the data is base64 encoded, and “string” means the data is a string. outputFormat true string The parameter data is the data to be processed. data true string Used to specify the data format of the key parameter. The key parameter can be set as one of the followings: raw, hex, base64, string. “raw” means the data is raw data, “hex” means the data is hex encoded, “base64” means the data is base64 encoded, and “string” means the data is a string. keyFormat false string The parameter key is the secret key used for HMAC encryption. The parameter key is required when the parameter algo is set to sign or signTx. The key parameter is not used for HMAC encryption when the algo parameter is set to “raw” (because the algorithm must be specified for HMAC encryption). key false string

function main() {
    Log(Encode("raw", "raw", "hex", "example", "raw", "123"))            // 6578616d706c65
    Log(Encode("raw", "raw", "hex", "example"))                          // 6578616d706c65
    Log(Encode("sha256", "raw", "hex", "example", "raw", "123"))         // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
    Log(Encode("sha256", "raw", "hex", "example", "", "123"))            // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
    Log(Encode("sha256", "raw", "hex", "example", null, "123"))          // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
    Log(Encode("sha256", "raw", "hex", "example", "string", "123"))      // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
    
    Log(Encode("raw", "raw", "hex", "123"))           // 313233
    Log(Encode("raw", "raw", "base64", "123"))        // MTIz
    
    Log(Encode("sha256", "raw", "hex", "example", "hex", "313233"))      // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
    Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz"))     // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
}
def main():
    Log(Encode("raw", "raw", "hex", "example", "raw", "123"))            # 6578616d706c65
    Log(Encode("raw", "raw", "hex", "example", "", ""))                  # 6578616d706c65
    Log(Encode("sha256", "raw", "hex", "example", "raw", "123"))         # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
    Log(Encode("sha256", "raw", "hex", "example", "", "123"))            # 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c            

    Log(Encode("sha256", "raw", "hex", "example", "string", "123"))      # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
    
    Log(Encode("raw", "raw", "hex", "123", "", ""))           # 313233
    Log(Encode("raw", "raw", "base64", "123", "", ""))        # MTIz
    
    Log(Encode("sha256", "raw", "hex", "example", "hex", "313233"))      # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
    Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz"))     # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
void main() {
    Log(Encode("raw", "raw", "hex", "example", "raw", "123"));            // 6578616d706c65
    Log(Encode("raw", "raw", "hex", "example"));                          // 6578616d706c65
    Log(Encode("sha256", "raw", "hex", "example", "raw", "123"));         // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
    Log(Encode("sha256", "raw", "hex", "example", "", "123"));            // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c            

    Log(Encode("sha256", "raw", "hex", "example", "string", "123"));      // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
                
    Log(Encode("raw", "raw", "hex", "123"));           // 313233
    Log(Encode("raw", "raw", "base64", "123"));        // MTIz
                
    Log(Encode("sha256", "raw", "hex", "example", "hex", "313233"));      // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
    Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz"));     // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
}

Example of Encode function call.

function main(){
    var ret1 = Encode("text.encoder.utf8", "raw", "hex", "hello")     // e4bda0e5a5bd
    Log(ret1)    
    var ret2 = Encode("text.decoder.utf8", "hex", "string", ret1)   
    Log(ret2)            

    var ret3 = Encode("text.encoder.gbk", "raw", "hex", "hello")      // c4e3bac3
    Log(ret3)
    var ret4 = Encode("text.decoder.gbk", "hex", "string", ret3)
    Log(ret4)
}
def main():
    ret1 = Encode("text.encoder.utf8", "raw", "hex", "hello", "", "")     # e4bda0e5a5bd
    Log(ret1)    
    ret2 = Encode("text.decoder.utf8", "hex", "string", ret1, "", "")   
    Log(ret2)            

    ret3 = Encode("text.encoder.gbk", "raw", "hex", "hello", "", "")      # c4e3bac3
    Log(ret3)
    ret4 = Encode("text.decoder.gbk", "hex", "string", ret3, "", "")
    Log(ret4)
void main(){
    auto ret1 = Encode("text.encoder.utf8", "raw", "hex", "hello");     // e4bda0e5a5bd
    Log(ret1);    
    auto ret2 = Encode("text.decoder.utf8", "hex", "string", ret1);   
    Log(ret2);            

    auto ret3 = Encode("text.encoder.gbk", "raw", "hex", "hello");      // c4e3bac3
    Log(ret3);
    auto ret4 = Encode("text.decoder.gbk", "hex", "string", ret3);
    Log(ret4);
}

The parameter algo also supports: “text.encoder.utf8”, “text.decoder.utf8”, “text.encoder.gbk”, “text.decoder.gbk” to encode and decode strings.

The Encode() function is only supported for live trading. If key and keyFormat parameters are not passed, then key encryption is not used.

UnixNano

Get the nanosecond timestamp of the current moment.

The UnixNano() function returns the nanosecond timestamp. number

UnixNano()

function main() {
    var time = UnixNano() / 1000000
    Log(_N(time, 0))
}
def main():
    time = UnixNano()
    Log(time)
void main() {
    auto time = UnixNano();
    Log(time);
}

If you need to get millisecond timestamps, you can use the following code:

{@fun/Global/Unix Unix}

Unix

Get the timestamp of the current moment at the second level.

Returns the second-level timestamp. number

Unix()

function main() {
    var t = Unix()
    Log(t)
}
def main():
    t = Unix()
    Log(t)
void main() {
    auto t = Unix();
    Log(t);
}

{@fun/Global/UnixNano UnixNano}

GetOS

Get the system information of the device where the docker is located.

System information. string

GetOS()

function main() {
    Log("GetOS:", GetOS())
}
def main():
    Log("GetOS:", GetOS())
void main() {
    Log("GetOS:", GetOS());
}

For example, a call to the GetOS() function for a docker running on the Mac OS operating system might return: darwin/amd64. Because Apple computers have multiple hardware architectures. darwin is the name of the Mac OS system.

MD5

Computes the MD5 hash of the parameter data.

MD5 hash value. string

MD5(data)

The data that requires MD5 calculation. data true string

function main() {
    Log("MD5", MD5("hello world"))
}
def main():
    Log("MD5", MD5("hello world"))
void main() {
    Log("MD5", MD5("hello world"));
}

Calling the MD5("hello world") function, the return value is: 5eb63bbbe01eeed093cb22bb8f5acdc3.

{@fun/Global/Encode Encode}

DBExec

Database interface functions.

An object containing the result of the execution of a sql statement, for example:


{"columns":["TS","HIGH","OPEN","LOW","CLOSE","VOLUME"],"values":[[1518970320000,100,99.1,90,100,12345.6]]}

object

DBExec(sql)

sql statement string. sql true string

function main() {
    var strSql = [
        ":CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ].join("")
    var ret = DBExec(strSql)
    Log(ret)
    
    // Add a piece of data
    Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    // Query data
    Log(DBExec(":SELECT * FROM TEST_TABLE;"))
}
def main():
    arr = [
        ":CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ]
    strSql = ""
    for i in range(len(arr)):
        strSql += arr[i]
    ret = DBExec(strSql)
    Log(ret)
    
    # Add a piece of data
    Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    # Query data
    Log(DBExec(":SELECT * FROM TEST_TABLE;"))
void main() {
    string strSql = ":CREATE TABLE TEST_TABLE(\
        TS INT PRIMARY KEY NOT NULL,\
        HIGH REAL NOT NULL,\
        OPEN REAL NOT NULL,\
        LOW REAL NOT NULL,\
        CLOSE REAL NOT NULL,\
        VOLUME REAL NOT NULL)";
    auto ret = DBExec(strSql);
    Log(ret);
    
    // Add a piece of data
    Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
    
    // Query data
    Log(DBExec(":SELECT * FROM TEST_TABLE;"));
}

Support in-memory database, for DBExec function parameters, if sql statement starts with : then operate in in-memory database, it will be faster without writing files. It’s suitable for database operations that do not require persistent saving, for example:

function main() {
    var strSql = [
        "CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ].join("")
    var ret = DBExec(strSql)
    Log(ret)
}
def main():
    arr = [
        "CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ]
    strSql = ""
    for i in range(len(arr)):
        strSql += arr[i]
    ret = DBExec(strSql)
    Log(ret)
void main() {
    string strSql = "CREATE TABLE TEST_TABLE(\
        TS INT PRIMARY KEY NOT NULL,\
        HIGH REAL NOT NULL,\
        OPEN REAL NOT NULL,\
        LOW REAL NOT NULL,\
        CLOSE REAL NOT NULL,\
        VOLUME REAL NOT NULL)";
    auto ret = DBExec(strSql);
    Log(ret);
}

Create a table.

function main() {
    var strSql = [
        "CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ].join("")
    Log(DBExec(strSql))
    
    // Add a piece of data
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    // Query data
    Log(DBExec("SELECT * FROM TEST_TABLE;"))
    
    // Modify data
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))    
    
    // Delete data
    Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))
}
def main():
    arr = [
        "CREATE TABLE TEST_TABLE(", 
        "TS INT PRIMARY KEY NOT NULL,",
        "HIGH REAL NOT NULL,", 
        "OPEN REAL NOT NULL,", 
        "LOW REAL NOT NULL,", 
        "CLOSE REAL NOT NULL,", 
        "VOLUME REAL NOT NULL)"
    ]
    strSql = ""
    for i in range(len(arr)):
        strSql += arr[i]
    Log(DBExec(strSql))
    
    # Add a piece of data
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    # Query data
    Log(DBExec("SELECT * FROM TEST_TABLE;"))
    
    # Modify data
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))
    
    # Delete data
    Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))
void main() {
    string strSql = "CREATE TABLE TEST_TABLE(\
        TS INT PRIMARY KEY NOT NULL,\
        HIGH REAL NOT NULL,\
        OPEN REAL NOT NULL,\
        LOW REAL NOT NULL,\
        CLOSE REAL NOT NULL,\
        VOLUME REAL NOT NULL)";
    Log(DBExec(strSql));            

    // Add a piece of data
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
    
    // Query data
    Log(DBExec("SELECT * FROM TEST_TABLE;"));
    
    // Modify data
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000));
    
    // Delete data
    Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110));
}

Add, delete, check and change the records in the table.

The function DBExec() can operate the live trading database (SQLite database) by passing in parameters. Realize the operation of adding, deleting, checking and changing data in the live trading database, supporting SQLite grammar. The system reserved tables in the live trading database: kvdb, cfg, log, profit, chart, do not operate on these tables. Currently Transactions are not supported and it is not recommended to perform such operations, which can cause conflicts in the system. The DBExec() function is only supported for live trading.

{@fun/Global/_G _G}

UUID

Create a UUID.

32-bit UUID. string

UUID()

function main() {
    var uuid1 = UUID()
    var uuid2 = UUID()
    Log(uuid1, uuid2)
}
def main():
    uuid1 = UUID()
    uuid2 = UUID()
    Log(uuid1, uuid2)
void main() {
    auto uuid1 = UUID();
    auto uuid2 = UUID();
    Log(uuid1, uuid2);
}

The UUID() function supports live trading only.

EventLoop

Listen for events, it returns when there is any WebSocket readable data or concurrent tasks, such as exchange.Go(), HttpQuery_Go(), etc. are completed.

If the returned object is not null value, the Event contained in the return content is the event trigger type. For example, the following return value structure:

{"Seq":1,"Event":"Exchange_GetTrades","ThreadId":0,"Index":3,"Nano":1682068771309583400}

object

EventLoop() EventLoop(timeout)

The parameter timeout is the timeout setting, in milliseconds. The parameter timeout waits for an event to occur before returning if it is set to 0. If it is greater than 0, it sets the event to wait for a timeout, and returns the most recent event immediately if it is less than 0. timeout false number

function main() {
    var routine_getTicker = exchange.Go("GetTicker")
    var routine_getDepth = exchange.Go("GetDepth")
    var routine_getTrades = exchange.Go("GetTrades")
    
    // Sleep(2000), if the Sleep statement is used here, it will cause the subsequent EventLoop function to miss the previous events, because after waiting for 2 seconds, the concurrent function has received the data, and the subsequent EventLoop listening mechanism started, it misses these events.
    // These events will not be missed unless EventLoop(-1) is called at the beginning of the first line of code to first initialize the EventLoop's listening mechanism.            

    // Log("GetDepth:", routine_getDepth.wait()) If the wait function is called in advance to retrieve the result of a concurrent call to the GetDepth function, the event that the GetDepth function receives the result of the request will not be returned in the EventLoop function.
    var ts1 = new Date().getTime()
    var ret1 = EventLoop(0)
    
    var ts2 = new Date().getTime()
    var ret2 = EventLoop(0)
    
    var ts3 = new Date().getTime()
    var ret3 = EventLoop(0)
    
    Log("The first concurrent task completed was:", _D(ts1), ret1)
    Log("The second concurrent task completed was:", _D(ts2), ret2)
    Log("The third concurrent task completed was:", _D(ts3), ret3)
    
    Log("GetTicker:", routine_getTicker.wait())
    Log("GetDepth:", routine_getDepth.wait())
    Log("GetTrades:", routine_getTrades.wait())
}
import time
def main():
    routine_getTicker = exchange.Go("GetTicker")
    routine_getDepth = exchange.Go("GetDepth")
    routine_getTrades = exchange.Go("GetTrades")
    
    ts1 = time.time()
    ret1 = EventLoop(0)
    
    ts2 = time.time()
    ret2 = EventLoop(0)
    
    ts3 = time.time()
    ret3 = EventLoop(0)
    
    Log("The first concurrent task completed was:", _D(ts1), ret1)
    Log("The second concurrent task completed was:", _D(ts2), ret2)
    Log("The third concurrent task completed was:", _D(ts3), ret3)
    
    Log("GetTicker:", routine_getTicker.wait())
    Log("GetDepth:", routine_getDepth.wait())
    Log("GetTrades:", routine_getTrades.wait())
void main() {
    auto routine_getTicker = exchange.Go("GetTicker");
    auto routine_getDepth = exchange.Go("GetDepth");
    auto routine_getTrades = exchange.Go("GetTrades");
    
    auto ts1 = Unix() * 1000;
    auto ret1 = EventLoop(0);
    
    auto ts2 = Unix() * 1000;
    auto ret2 = EventLoop(0);
    
    auto ts3 = Unix() * 1000;
    auto ret3 = EventLoop(0);
    
    Log("The first concurrent task completed was:", _D(ts1), ret1);
    Log("The second concurrent task completed was:", _D(ts2), ret2);
    Log("The third concurrent task completed was:", _D(ts3), ret3);
    
    Ticker ticker;
    Depth depth;
    Trades trades;
    routine_getTicker.wait(ticker);
    routine_getDepth.wait(depth);
    routine_getTrades.wait(trades);
    
    Log("GetTicker:", ticker);
    Log("GetDepth:", depth);
    Log("GetTrades:", trades);
}

The first call to the EventLoop() function in the code initializes the mechanism for that listened event, and if the first EventLoop() call starts after the event callback, it will miss the previous events. The underlying system wraps a queue structure that caches a maximum of 500 event callbacks. If the EventLoop() function is not called in time to take them out during program execution, later event callbacks outside the 500 cache will be lost. Calls to the EventLoop() function do not affect the cache queue of the underlying system WebSocket or the caches of concurrent functions such as exchange.Go(). For these caches, it is still necessary to use the respective methods to retrieve the data. No return event will be generated in the EventLoop() function for data that has been retrieved before the EventLoop() function returns. The main purpose of the EventLoop() function is to notify the strategy layer that new network data has been received by the underlying system. The whole strategy is driven by events. When the EventLoop() function returns an event, just traverses all data sources. For example, WebSocket connections, objects created by exchange.Go() try to get data. The EventLoop() function supports live trading only. Listen for events in the main thread when called from the main function main(). In strategies written in the JavaScript language, the threading.Thread() function creates a thread, which can also be called in the thread’s execution function, to listen for events in the current thread.

{@fun/Global/Dial Dial}, {@fun/Trade/exchange.Go exchange.Go}, {@fun/Global/HttpQuery_Go HttpQuery_Go}

__Serve

The __Serve function is used to create Http service, TCP service, and Websocket service (based on Http protocol).

Returns a string that records the IP address and port of the created service. For example: 127.0.0.1:8088, [::]:8089.

string

__Serve(serveURI, handler) __Serve(serveURI, handler, …args)

The serveURI parameter is used to configure the protocol, IP address, port and other settings of the service binding, such as http://0.0.0.0:8088?gzip=true, that is, http://:8088?gzip=true.

  • TCP protocol serveURI parameter setting, such as tcp://127.0.0.1:6666?tls=true; you can add certificates and private keys, such as tls=true&cert_pem=xxxx&cert_key_pem=xxxx.
  • Http protocol serveURI parameter settings, such as http://127.0.0.1:6666?gzip=true; you can set compression settings: gzip=true. The serveURI parameter is used for Https, such as https://127.0.0.1:6666?tls=true&gzip=true; you can add cert_pem and cert_key_pem parameters to load the certificate.

serveURI true string The handler parameter is used to pass in the routing processing function (Http protocol), message processing function (TCP protocol), and Stream processing function (Websocket). The callback function passed in by the parameter handler can define multiple parameters, the first parameter is the ctx object (context object).

handler true function The actual parameter of the callback function passed as the parameter handler. There may be multiple parameters arg, for example:

__Serve("http://:8088", function(ctx, a, b, c) {
    Log(`ctx.host():`, ctx.host(), ", a=", a, ", b=", b, ", c=", c)
}, 1, 2, 3)

The parameters 1, 2, 3 passed in when calling the __Serve() function correspond to the parameters a, b, c passed in the callback function.

arg false string, number, bool, object, array, function, null value and other types supported by the system

function main() {
    let httpServer = __Serve("http://:8088?gzip=true", function (ctx) {
        Log("http connect from: ", ctx.remoteAddr(), "->", ctx.localAddr())
        let path = ctx.path()
        if (path == "/") {
            ctx.write(JSON.stringify({
                path: ctx.path(),
                method: ctx.method(),
                headers: ctx.headers(),
                cookie: ctx.header("Cookie"),
                remote: ctx.remoteAddr(),
                query: ctx.rawQuery()
            }))
        } else if (path == "/tickers") {
            let ret = exchange.GetTickers()
            if (!ret) {
                ctx.setStatus(500)
                ctx.write(GetLastError())
            } else {
                ctx.write(JSON.stringify(ret))
            }
        } else if (path == "/wss") {
            if (ctx.upgrade("websocket")) { // upgrade to websocket
                while (true) {
                    let r = ctx.read(10)
                    if (r == "") {
                        break
                    } else if (r) {
                        if (r == "ticker") {
                            ctx.write(JSON.stringify(exchange.GetTicker()))
                        } else {
                            ctx.write("not support")
                        }
                    }
                }
                Log("websocket closed", ctx.remoteAddr())
            }
        } else {
            ctx.setStatus(404)
        }
    })
    let echoServer = __Serve("tcp://:8089", function (ctx) {
        Log("tcp connect from: ", ctx.remoteAddr(), "->", ctx.localAddr())
        while (true) {
            let d = ctx.read()
            if (!d) {
                break
            }
            ctx.write(d)
        }
        Log("connect closed")
    })
    Log("http serve on", httpServer, "tcp serve on", echoServer)
    
    for (var i = 0; i < 5; i++) {
        if (i == 2) {
            // test Http
            var retHttp = HttpQuery("http://127.0.0.1:8088?num=123&limit=100", {"debug": true})
            Log("retHttp:", retHttp)
        } else if (i == 3) {
            // test TCP
            var tcpConn = Dial("tcp://127.0.0.1:8089")
            tcpConn.write("Hello TCP Server")
            var retTCP = tcpConn.read()
            Log("retTCP:", retTCP)
        } else if (i == 4) {
            // test Websocket
            var wsConn = Dial("ws://127.0.0.1:8088/wss|compress=gzip")
            wsConn.write("ticker")
            var retWS = wsConn.read(1000)
            Log("retWS:", retWS)
            // no depth
            wsConn.write("depth")
            retWS = wsConn.read(1000)
            Log("retWS:", retWS)
        }
        Sleep(1000)
    }
}
# Unsupported
// Unsupported
  • This function supports JavaScript language strategies only.
  • The service thread is isolated from the global scope, so it does not support closures or references to external variables, custom functions, etc.; however, it can call all platform API functions.
  • The Websocket service is implemented based on the Http protocol. You can set a routing branch in the path and design the implementation code for Websocket message subscription/push. You can refer to the sample code in this section.

The callback function passed in by the parameter handler receives a ctx parameter. The ctx parameter is a context object used to get and write data, with the following methods:

  • ctx.proto() Applied to Http/TCP protocol, returns the protocol name when called. For example: HTTP/1.1, tcp.
  • ctx.host() Applied to Http protocol, it returns host information when called IP address and port.
  • ctx.path() Applied to the Http protocol, returns the request path when called.
  • ctx.query(key) Applied to Http protocol, returns the value corresponding to the key in the query in the request when called. For example, the request sent is: http://127.0.0.1:8088?num=123, and the callback processing function passed in by the parameter handler returns "123" when ctx.query("num") is called.
  • ctx.rawQuery() Applied to the Http protocol, when called, returns the original query in the request (the query of the Http request).
  • ctx.headers() Applied to the Http protocol, and returns the request header information in the request when called.
  • ctx.header(key) Applied to Http protocol, it returns the value of a key in the specified request header when called. For example, get the User-Agent in the headers of the current request: ctx.header("User-Agent").
  • ctx.method() Applied to the Http protocol, returns the request method when called, such as GET, POST, etc.
  • ctx.body() Applied to POST request of Http protocol, and returns the body of the request when called.
  • ctx.setHeader(key, value) Applied to the Http protocol to set the request header information of the response message.
  • ctx.setStatus(code) Applied to Http protocol, set Http message status code. Usually, Http status code is set at the end of the routing branch. The default value is 200.
  • ctx.remoteAddr() Applied to Http/TCP protocol, returns the remote client address and port in the request when called.
  • ctx.localAddr() Applied to Http/TCP protocol, returns the local address and port of the service when called.
  • ctx.upgrade(“websocket”) Applied to the Websocket protocol implementation based on the Http protocol, switching the ctx context object to the Websocket protocol; returning a Boolean value (true) if the switch is successful, and a Boolean value (false) if it fails.
  • ctx.read(timeout_ms) Applied to Websocket protocol implementation/TCP protocol based on Http protocol, reads data of Websocket connection and TCP connection. The read method is not supported in ordinary Http protocol. You can specify the timeout parameter timeout_ms in milliseconds.
  • ctx.write(s) Applied to Http/TCP protocol, used to write string data. You can use JSON.stringify() to encode the JSON object into a string and then write it. For the WebSocket protocol, you can use this method to pass the encoded string to the client.

{@fun/Global/HttpQuery HttpQuery}, {@fun/Global/HttpQuery_Go HttpQuery_Go}

_G

Persistently save data, the function implements a global dictionary function that can be saved. The data structure is a KV table that is permanently saved in the docker’s local database file.

Persistent saved key-value data in k-v key-value pairs. string, number, bool, object, array, null value

_G() _G(k) _G(k, v)

The parameter k is the name of the key in the saved key-value pair, and is not case-sensitive. k false string, null value The parameter v is the key value in the saved key-value pair, which can be any data that can be JSON serialized. v false string, number, bool, object, array, null value

function main(){
    // Set a global variable num with a value of 1
    _G("num", 1)     
    // Change a global variable num to the value of the string ok
    _G("num", "ok")    
    // Delete the global variable num
    _G("num", null)
    // Returns the value of the global variable num
    Log(_G("num"))
    // Delete all global variables
    _G(null)
    // Return to live trading ID
    var robotId = _G()
}
def main():
    _G("num", 1)     
    _G("num", "ok")    
    _G("num", None)
    Log(_G("num"))
    _G(None)
    robotId = _G()
void main() {
    _G("num", 1);
    _G("num", "ok");
    _G("num", NULL);
    Log(_G("num"));
    _G(NULL);
    // Not support auto robotId = _G();
}

A separate database for each live trading, the data saved by the _G() function will always be there if the strategy is restarted or the docker stops running. If the backtesting is finished, the data saved in the backtesting system by the _G() function will be cleared. When using the _G() function to persist the saved data, it should be used reasonably according to the memory and hard disk space of the hardware device, and should not be abused. When calling the _G() function in a live trading and no parameters are passed, the _G() function returns the Id of the current live trading. When calling the _G() function, the parameter v is passed as null to indicate the deletion of the k-v key-value pair. When calling the _G() function, only the parameter k is passed in the string, and the _G() function returns the key value corresponding to the saved parameter k. When calling the _G() function, only the parameter k is passed in a null value, indicating that all records of the k-v key-value pair is deleted. When the k-v key-value pairs have been saved persistently, the _G() function is called again, passing in the name of the key that has been saved persistently as parameter k. Passing in the new key value as parameter v will update that k-v key-value pair.

{@fun/Global/DBExec DBExec}

_D

Converts millisecond timestamps or Date objects to time strings.

Time string. string

_D() _D(timestamp) _D(timestamp, fmt)

Millisecond timestamp or Date object. timestamp false number、object Format string, JavaScript language default format: yyyy-MM-dd hh:mm:ss; Python language default format: %Y-%m-%d %H:%M:%S; C++ language default format: %Y-%m-%d %H:%M:%S. fmt false string

function main(){
    var time = _D()
    Log(time)
}
def main():
    strTime = _D()
    Log(strTime)
void main() {
    auto strTime = _D();
    Log(strTime);
}

Get and print the current time string:

function main() {
    Log(_D(1574993606000))
}
def main():
    # Running this code on a server in Beijing time: 2019-11-29 10:13:26 , a docker on another server in another region results in: 2019-11-29 02:13:26
    Log(_D(1574993606))
void main() {
    Log(_D(1574993606000));
}

The timestamp is 1574993606000, using the code conversion:

function main() {
    Log(_D(1574993606000, "yyyy--MM--dd hh--mm--ss"))   // 2019--11--29 10--13--26
}
def main():
    # 1574993606 is timestamped in seconds.
    Log(_D(1574993606, "%Y--%m--%d %H--%M--%S"))        #  2019--11--29 10--13--26
void main() {
    Log(_D(1574993606000, "%Y--%m--%d %H--%M--%S"));    // 2019--11--29 10--13--26
}

Formatting with the parameter fmt is different for JavaScript, Python, and C++ languages, as shown in the following examples:

Returns the current time string without passing any parameters. When using the _D() function in the Python strategy, you need to be aware that the parameters passed are second-level timestamps (millisecond-level timestamps in the JavaScript and C++ strategies, where 1 second is equal to 1000 milliseconds). When using the _D() function to parse a time string with a readable timestamp in the live trading, you need to pay attention to the time zone and time setting of the operating system where the docker program is located. The _D() function parses a timestamp into a readable time string depending on the time of the docker’s system.

{@fun/Global/UnixNano UnixNano}, {@fun/Global/Unix Unix}

_N

Format a floating point number.

The formatted floating point number according to the precision setting. number

_N() _N(num) _N(num, precision)

The floating point number that needs to be formatted. num true number The precision setting for formatting, the parameter precision is an integer, and the parameter precision defaults to 4. precision false number

function main(){
    var i = 3.1415
    Log(i)
    var ii = _N(i, 2)
    Log(ii)
}
def main():
    i = 3.1415
    Log(i)
    ii = _N(i, 2)
    Log(ii)
void main() {
    auto i = 3.1415;
    Log(i);
    auto ii = _N(i, 2);
    Log(ii);
}

For example, _N(3.1415, 2) will delete the value after 3.1415 two decimal places and the function returns 3.14.

function main(){
    var i = 1300
    Log(i)
    var ii = _N(i, -3)
    // Check the logs and see that it is 1000
    Log(ii)
}
def main():
    i = 1300
    Log(i)
    ii = _N(i, -3)
    Log(ii)
void main() {
    auto i = 1300;
    Log(i);
    auto ii = _N(i, -3);
    Log(ii);
}

If you need to change all the N digits to the left of the decimal point to 0, you can write it like this:

The parameter precision can be a positive integer, negative integer.

{@fun/Trade/exchange.SetPrecision exchange.SetPrecision}

_C

Retry function for interface fault tolerance.

The return value of the callback function when it is executed. All types are supported by the system except logical false value and null value.

_C(pfn) _C(pfn, …args)

The parameter pfn is a function reference, which is a callback function. pfn true function Parameters to callback functions, there may be more than one parameter arg. The type and number of parameters arg depends on the parameters of the callback function. arg false string, number, bool, object, array, function, all types are supported by the system, such as null values

function main(){
    var ticker = _C(exchange.GetTicker)
    // Adjust _C() function retry interval to 2 seconds
    _CDelay(2000)
    var depth = _C(exchange.GetDepth)
    Log(ticker)
    Log(depth)
}
def main():
    ticker = _C(exchange.GetTicker)
    _CDelay(2000)
    depth = _C(exchange.GetDepth)
    Log(ticker)
    Log(depth)
void main() {
    auto ticker = _C(exchange.GetTicker);
    _CDelay(2000);
    auto depth = _C(exchange.GetDepth);
    Log(ticker);
    Log(depth);
}

For error-tolerant functions without parameters:

function main(){
    var records = _C(exchange.GetRecords, PERIOD_D1)
    Log(records)
}
def main():
    records = _C(exchange.GetRecords, PERIOD_D1)
    Log(records)
void main() {
    auto records = _C(exchange.GetRecords, PERIOD_D1);
    Log(records);
}

For functions with parameters that are error tolerant:

var test = function(a, b){
    var time = new Date().getTime() / 1000
    if(time % b == 3){
        Log("Eligible!", "#FF0000")
        return true
    }
    Log("Retry!", "#FF0000")
    return false
}            

function main(){
    var ret = _C(test, 1, 5)
    Log(ret)
}
import time
def test(a, b):
    ts = time.time()
    if ts % b == 3:
        Log("Eligible!", "#FF0000")
        return True
    Log("Retry!", "#FF0000")
    return False            

def main():
    ret = _C(test, 1, 5)
    Log(ret)
// C++ does not support fault tolerance for custom functions in this way

It can also be used for fault tolerance of custom functions:

The _C() function will keep calling the specified function until it returns successfully (the function referenced by the parameter pfn returns null or false when called will retry calling pfn). For example _C(exchange.GetTicker). The default retry interval is 3 seconds, you can call the _CDelay() function to set the retry interval. For example, _CDelay(1000) means to change the retry interval of the _C() function to 1 second. Fault tolerance can be done for, but is not limited to, the following functions:

  • exchange.GetTicker()
  • exchange.GetDepth()
  • exchange.GetTrades()
  • exchange.GetRecords()
  • exchange.GetAccount()
  • exchange.GetOrders()
  • exchange.GetOrder()
  • exchange.GetPositions() All can be called by the _C() function for fault tolerance. The _C() function is not limited to the above listed function fault tolerance, the parameter pfn is a function reference rather than a function call. Note that it is _C(exchange.GetTicker), not _C(exchange.GetTicker()).

_Cross

Returns the number of intersection periods of the array arr1 and the array arr2.

The number of cross periods of the array arr1 and the array arr2. number

_Cross(arr1, arr2)

Elements are arrays of type number. arr1 true array Elements are arrays of type number. arr2 true array

// Fast line indicator
var arr1 = [1,2,3,4,5,6,8,8,9]
// Slow line indicator
var arr2 = [2,3,4,5,6,7,7,7,7]
function main(){
    Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
    Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
}
arr1 = [1,2,3,4,5,6,8,8,9]     
arr2 = [2,3,4,5,6,7,7,7,7]
def main():
    Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
    Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
void main() {
    vector<double> arr1 = {1,2,3,4,5,6,8,8,9};
    vector<double> arr2 = {2,3,4,5,6,7,7,7,7};
    Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2));
    Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1));
}

A set of data can be simulated to test the _Cross(Arr1, Arr2) function:

If the return value of the _Cross() function is a positive number, it indicates the period of upward penetration, if it’s a negative number, it indicates the period of downward penetration, 0 means the same as the current price. Specific instructions for use: Analysis and instructions for use about built-in function _Cross.

JSONParse

The function JSONParse() is used to parse JSON strings.

JSON object. object

JSONParse(s)

JSON string. s true string

function main() {
    let s1 = '{"num": 8754613216564987646512354656874651651358}'
    Log("JSON.parse:", JSON.parse(s1))    // JSON.parse: {"num":8.754613216564988e+39}
    Log("JSONParse:", JSONParse(s1))      // JSONParse:  {"num":"8754613216564987646512354656874651651358"}
    
    let s2 = '{"num": 123}'
    Log("JSON.parse:", JSON.parse(s2))    // JSON.parse: {"num":123}
    Log("JSONParse:", JSONParse(s2))      // JSONParse:  {"num":123}
}
import json

def main():
    s1 = '{"num": 8754613216564987646512354656874651651358}'
    Log("json.loads:", json.loads(s1))    # json.loads: map[num:8.754613216564987e+39]
    Log("JSONParse:", JSONParse(s1))      # JSONParse:  map[num:8754613216564987646512354656874651651358]
    
    s2 = '{"num": 123}'
    Log("json.loads:", json.loads(s2))    # json.loads: map[num:123]
    Log("JSONParse:", JSONParse(s2))      # JSONParse:  map[num:123]
void main() {
    auto s1 = "{\"num\":8754613216564987646512354656874651651358}";
    Log("json::parse:", json::parse(s1));
    // Log("JSONParse:", JSONParse(s1));   // The function is not supported.
    
    auto s2 = "{\"num\":123}";
    Log("json::parse:", json::parse(s2));
    // Log("JSONParse:", JSONParse(s2));   // The function is not supported.
}

JSON strings with large values can be parsed correctly, and it will parse large values as string types. The JSONParse() function is not supported in the backtest system.

Log