Loading ...

``

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

Filter the error information of an interface:

function main() {
    // Randomly check a non-existent order, intentionally make the interface report an error
    var order = exchange.GetOrder("123")
    Log(order)
    // Filter http502 errors, GetOrder interface errors, after setting the error filter, the second call to GetOrder no longer reports 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);
}

GetPid()

GetPid(), Returns the robot process ID. Return value: string type.

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

GetLastError()

GetLastError(), to get the latest error information, generally do not need to use, because the program will automatically upload the error information to the log system. Return value: string type. After calling the GetLastError() function, the error cache will be cleared, and when it is called again, the error information recorded last time will not be returned.

function main(){
    // Because there is no order with the number 123, an error will occur
    exchange.GetOrder("123")
    var error = GetLastError()
    Log(error)
}
def main():
    exchange.GetOrder("123")
    error = GetLastError()
    Log(error)
void main() {
    // Order ID type: TId, so no string can be passed in, we trigger the next order that does not meet the exchange specifications
    exchange.GetOrder(exchange.Buy(1, 1));
    auto error = GetLastError();
    Log(error);
}

GetCommand()

GetCommand(), Get interactive commands (utf-8). Get the command sent from the strategy interactive interface and clear it. If there is no command, it returns null. The command format returned is “button name: parameter”. If there is no parameter, the command is the button name.

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);
    }
}

The underlying system has a variable to record the interactive command. When the GetCommand() function is called, the interactive command will be taken out (if there is no interactive command, return null), and the variable content of the underlying system will be emptied. When GetCommand() is not called, the new interactive command in the underlying system will overwrite the old interactive command. For example, when the strategy program is processing an interactively triggered strategy code segment, if there are multiple interactive commands sent from the strategy interface at this time, if the strategy is still executing a certain section of code at this time, when the strategy again GetCommand() At this time, only the last interactive command can be obtained.

Examples of using interactive controls, set interactive controls on the strategy editing interface.

img

Design interactive code in the strategy.

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 quantity")
            } else if (arr[0] == "sell") {
                Log("Sell, the control with quantity: ", 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 quantity")
            elif arr[0] == "sell":
                Log("Sell, the control with quantity: ", 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 quantity");
            } else if (arr[0] == "sell") {
                Log("Sell, the control with quantity: ", arr[1]);
            } else {
                Log("Other controls trigger: ", arr);
            }
        }
        Sleep(1000);
    }
}

Dial(…)

Dial(Address, Timeout), original Socket access, support tcp, udp, tls, unix protocols. Parameter value: Address is a string type, fill in the address, TimeOut is the timeout time, timeout, Dial(...) function returns a null value, unit:second.

Address parameter details:

Parameter Details
Set the parameters of the Dial function At the normal address: wss://real.okex.com:10441/websocket?Compress separated by the “|” symbol, if there are | characters in the parameter string, Then use “||” as the delimiter. Connect each parameter with “&”, for example, ss5 proxy and compression parameters are set together: Dial(“wss://baidu.com/stream|proxy=socks5://xxx:9999&compress=gzip_raw&mode=recv”)
When used in WS protocol, data compression related parameters: compress=parameter value compress is the compression method, compress parameter, optional gzip_raw, gzip, etc. OKEX’s gzip method is non-standard gzip. So you need to use the extension method: gzip_raw, that is, add the setting compress=gzip_raw after the separator “|”, and use the “&” symbol and the next mode parameter to separate.
When used in WS protocol, data compression related parameters: mode=parameter value “mode” is the mode, and dual, send, recv are optional. dual is bidirectional, sending compressed data and receiving compressed data. send is to send compressed data. Recv is to receive compressed data and decompress it locally.
Related parameters for setting 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 user name, pwd is ss5 Server login password, 1080 is the port of ss5 service
When used in the WS protocol, set the underlying auto-reconnect related parameters: reconnect=parameter value “reconnect” is whether to set reconnect, reconnect=true is to enable reconnect, the default setting is not to reconnect
When used in the ws protocol, set 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, and the default setting is 1 second, that is, interval=1000
When used in the ws protocol, set the underlying auto-reconnect related parameters: payload=parameter value “payload” is the subscription message to be sent when ws reconnects, for example: payload=okok
function main(){
    // Dial supports tcp://, udp://, tls://, unix:// protocol, you can add a parameter to specify the number of seconds to 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 to specify the timeout, in milliseconds. Return null to indicate error or timeout or 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();
    }
}

The read function supports the following parameters:

  • Not passing parameters means blocking until there is a message, such as ws.read().
  • Milliseconds specify the message wait timeout, such as ws.read(2000) specifies the timeout to be two seconds.
  • The following two parameters are only valid for websocket: -1 means to return immediately regardless of whether there is any message, such as ws.read(-1). -2 means to return immediately regardless of whether there is any message, but only the latest message is returned, and the message in the buffer is discarded as ws.read(-2).

read() function buffer description: If the data pushed by the ws protocol is too long between the strategy read() function calls, it may cause data accumulation, and these data are stored in the buffer. The buffer data structure is a queue, with an upper limit of 2000. After exceeding 2000, the latest data enters the buffer, and the oldest data is cleared.

read function parameter: no parameter parameter: -1 parameter: -2 parameter: 2000, unit is ms
Buffer already has data Return the oldest data immediately Return the oldest data immediately Return the latest data immediately Return the oldest data immediately
No data in the buffer Return when there is data blocked Return null immediately Return null immediately Wait for 2000ms, return null if there is no data, return if there is data
ws connection is disconnected, when the bottom layer is reconnected the read() function returns an empty string, that is: “”, the write() function returns 0. If this situation is detected, the close() function can be used to close the connection. If set to reconnect automatically, it won’t need to close. the bottom layer of the system will automatically reconnect.
  • Support wss (WebSocket) protocol

    Access Binance’s websocket market quotes interface:

    function main() {
        LogStatus("connecting...")
        // Access Binance's websocket interface
        var client = Dial("wss://stream.binance.com:9443/ws/!ticker@arr")
        if (!client) {
            Log("Connection failed, program exited")
            return
        }
        Log("The connection is successful, and the lower layer will automatically reconnect")
        while (true) {
            // read only returns the data obtained after calling read
            var buf = client.read()      
            if (!buf) {
                break
            }
            var table = {
                type: 'table',
                title: 'Quote Chart',
                cols: ['Currency', 'Highest', 'Lowest', 'Buy One', 'Sell One', 'Last Transaction 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 
        Log("The connection is successful, and the lower layer will automatically reconnect")
        while True:
            buf = client.read()
            if not buf:
                break
            table = {
                "type" : "table", 
                "title" : "Quote Chart", 
                "cols" : ['Currency', 'Highest', 'Lowest', 'Buy One', 'Sell One', 'Last Transaction 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;
        }
        Log("The connection is successful, and the lower layer will automatically reconnect");
        while(true) {
            auto buf = client.read();
            if(buf == "") {
                break;
            }
            json table = R"({
                "type" : "table", 
                "title" : "Quote Chart", 
                "cols" : ['Currency', 'Highest', 'Lowest', 'Buy One', 'Sell One', 'Last Transaction 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();
    }
    

    Access OKEX websocket interface verification

    function main(){
        // OKEX websocket Login subscription format:{"op":"login","args":["<api_key>","<passphrase>","<timestamp>","<sign>"]}
        // api_key Secret key for own account
        var api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        var passphrase = "xxxx"
        var timestamp = new Date().getTime() / 1000 + ""
        
        // OKEX websocket Signature method:sign=CryptoJS.enc.Base64.Stringify(CryptoJS.HmacSHA256(timestamp +'GET'+ '/users/self/verify', secret))
        // The last parameter of exchange.HMAC is secretKey
        var sign = exchange.HMAC("sha256", "base64", timestamp + "GET" + "/users/self/verify", "xxxxxx")
        
        var param = {"op":"login","args":[api_key, passphrase, timestamp, sign]}
        var ws = Dial("wss://real.okex.com:8443/ws/v3|compress=gzip_raw&mode=recv")
        ws.write(JSON.stringify(param))
        if(ws){
            var ret = ws.read()
            Log("ret:", ret)
            // OKEX websocket Subscription for holding positions: {"op": "subscribe", "args": ["futures/position:BTC-USD-170317"]} 
            ws.write(JSON.stringify({"op": "subscribe", "args": ["futures/position:BTC-USD-200925"]}))
            var pingCyc = 1000 * 20
            var lastPingTime = new Date().getTime()
            while(1){
                var nowTime = new Date().getTime()
                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)
            }
            ws.close() 
        }
    }
    
    import json
    import time
    def main():
        api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        passphrase = "xxxx"
        timestamp = str(time.time())
        sign = exchange.HMAC("sha256", "base64", timestamp + "GET" + "/users/self/verify", "xxxxxx")
        
        param = {"op":"login","args":[api_key, passphrase, timestamp, sign]}
        ws = Dial("wss://real.okex.com:8443/ws/v3|compress=gzip_raw&mode=recv")
        ws.write(json.dumps(param))
        if ws:
            ret = ws.read()
            Log("ret:", ret)
            
            ws.write(json.dumps({"op": "subscribe", "args": ["futures/position:BTC-USD-200925"]}))
            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)
            ws.close()
    
    void main() {
        auto api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
        auto passphrase = "xxxx";
        auto timestamp = std::to_string(Unix());
        auto sign = exchange.HMAC("sha256", "base64", timestamp + "GET" + "/users/self/verify", "xxxxxx");
        
        json param = R"({"op":"login","args":[]})"_json;
        param["args"].push_back(api_key);
        param["args"].push_back(passphrase);
        param["args"].push_back(timestamp);
        param["args"].push_back(sign);    
        auto ws = Dial("wss://real.okex.com:8443/ws/v3|compress=gzip_raw&mode=recv");
        ws.write(param.dump());
        if(ws.Valid) {
            auto ret = ws.read();
            Log("ret:", ret);
            
            ws.write(R"({"op": "subscribe", "args": ["futures/position:BTC-USD-200925"]})"_json.dump());
            uint64_t pingCyc = 1000 * 20;
            uint64_t lastPingTime = Unix() * 1000;
            while(true) {
                uint64_t nowTime = Unix() * 1000;
                auto ret = ws.read();
                Log("ret:", ret);
                if(nowTime - lastPingTime > pingCyc) {
                    auto retPing = ws.write("ping");
                    lastPingTime = nowTime;
                    Log("send: ping", "#FF0000");
                }
                LogStatus("current time: ", _D());
                Sleep(1000);
            }
            ws.close();
        }
    }
    


#### HttpQuery(...)

```HttpQuery(Url, PostData, Cookies, Headers, IsReturnHeader)```Web URL access. Parameter value: both are of type string.

Note:
* The ```HttpQuery(...)``` function only supports **JavaScript** language.
* For the **Python** language, you can use the **urllib** library to directly send http requests.

```HttpQuery(...)``` is mainly used to access connections that do not require signatures on the exchange, such as public interfaces and market information. An example of an API that does not require a signature to access OKEX: the return value is a JSON string, which can be parsed using the ```JSON.parse()``` function.

```js
function main(){
   // An example of GET access without parameters
   var info = JSON.parse(HttpQuery("https://www.okex.com/api/spot/v3/instruments"))
   Log(info)
   // An example of GET access with parameters
   var ticker = JSON.parse(HttpQuery("https://www.okex.com/api/spot/v3/instruments/BTC-USDT/book?size=5&depth=0.2"))
   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.okex.com/api/spot/v3/instruments").read().decode('utf-8'))
    Log(info)
    ticker = json.loads(urllib.request.urlopen("https://www.okex.com/api/spot/v3/instruments/BTC-USDT/book?size=5&depth=0.2").read().decode('utf-8'))
    Log(ticker)
void main() {
    auto info = json::parse(HttpQuery("https://www.okex.com/api/spot/v3/instruments"));
    Log(info);
    auto ticker = json::parse(HttpQuery("https://www.okex.com/api/spot/v3/instruments/BTC-USDT/book?size=5&depth=0.2"));
    Log(ticker);
}

Get the return content of a Url, if the second parameter PostData is a string a=1&b=2&c=abc formsubmit by POST, others by PUT, etc. {method:'PUT', data:'a=1&b=2&c=abc'}

PostData can also be a JSON string. The form of cookies is as follows: a=10; b=20each parameter uses a semicolon to separate. The format of the headers parameter is: User-Agent: Mobile\nContent-Type: text/html each parameter is separated by a newline character ‘\n’.

The second parameter, postData, can be customized, for example: HttpQuery("http://www.abc.com", {method:'PUT', data:'a=1&b=2&c=abc'})Note: if you need to set the timeout for the httpquery function, you can add the timeout attribute in {method:'put',data:'a=1&B=2&C=ABC'} (the default is 60 seconds).

Set 1 second timeout: HttpQuery("http://www.abc.com", {method:'PUT', data:'a=1&b=2&c=abc', timeout:1000})

The third parameter is required to pass the Cookie string, but POST is not required to set the second parameter to null. During the simulation test, because the URL cannot be simulated, the function returns a fixed string Dummy Data. You can use this interface to send text messages or interact with other APIs.

Get method:HttpQuery("http://www.baidu.com"); Post method:HttpQuery("http://www.163.com", "a=1&b=2&c=abc"); Return the calling example of Header: HttpQuery("http://www.baidu.com", null, "a=10; b=20", "User-Agent: Mobile\nContent-Type: text/html", true); //will return {Header: HTTP Header, Body: HTML}

  • HttpQuery function uses proxy settings

    function main() {
        // Set proxy and send http request this time, no username, no password, this time http request will be sent through the proxy
        HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/")
    
        // Set the proxy and send http request this time, enter the user name and password, only the current call of HttpQuery takes effect, then call HttpQuery ("http://www.baidu.com") again 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 Python's urllib2 library
    
    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/");
    }
    
  • Use of the HttpQuery(...) function in the backtest system

    Data can be retrieved by sending requests (only to Chinese domestic accessible links) using HttpQuery(...) in the backtest system. A limit of 20 times is imposed on the backtest and the HttpQuery(...) access aches the data, while the HttpQuery(...) function returns the cached ata on the second access of the same URL (no more actual web requests). We can run a service program on a server or device that responds to requests sent by HttpQuery(...) in the strategy program, tested in the Go language as follows:

    package main
    import (
        "fmt"
        "net/http"
        "encoding/json"
    )
    
    func Handle (w http.ResponseWriter, r *http.Request) {
        defer func() {
            fmt.Println("req:", *r)
            ret := map[string]interface{}{
                "schema" : []string{"time","open","high","low","close","vol"},
                "data" : []interface{}{
                    []int64{1564315200000,9531300,9531300,9497060,9497060,787},
                    []int64{1564316100000,9495160,9495160,9474260,9489460,338},
                },
            }
            b, _ := json.Marshal(ret)
            w.Write(b)
        }()
    }
    

    The strategy backtest uses the HttpQuery(...) function to send requests.

    function main() {
        // You can write the IP address of the device where you run the service program
        Log(HttpQuery("http://xxx.xx.x.xxx:9090/data?msg=hello"));
        Log(exchange.GetAccount());
    }
    
    # HttpQuery does not support Python and can use Python's urllib2 library
    
    void main() {
        // You can write the IP address of the device where you run the service program
        Log(HttpQuery("http://xxx.xx.x.xxx:9090/data?msg=hello"));
        Log(exchange.GetAccount());
    }
    

Hash(…)

Hash(Algo, OutputAlgo, Data)it supports hash calculation of md5/sha256/sha512/sha1, only support real market. Parameter value: both are of type string. The second parameter can be set to raw/hex/base64, which refers to the output of the encrypted original content /hex encoded /base64 encoded, respectively.

function main(){
    Log(Hash("md5", "hex", "hello"))
    Log(Hash("sha512", "base64", "hello"))
}
def main():
    Log(Hash("md5", "hex", "hello"))
    Log(Hash("sha512", "base64", "hello"))
void main() {
    Log(Hash("md5", "hex", "hello"));
    Log(Hash("sha512", "base64", "hello"));
}

HMAC(…)

HMAC(Algo, OutputAlgo, Data, Key)it supports HMAC encryption calculation of md5/sha256/sha512/sha1, only support real market. Parameter value: both are of type string. The second parameter can be set to raw /hex/base64, which refers to the output of the encrypted original content /hex encoded /base64 encoded, respectively.

function main(){
    Log(HMAC("md5", "hex", "hello", "pass"))
    Log(HMAC("sha512", "base64", "hello", "pass"))
}
def main():
    Log(HMAC("md5", "hex", "hello", "pass"))
    Log(HMAC("sha512", "base64", "hello", "pass"))
void main() {
    Log(HMAC("md5", "hex", "hello", "pass"));
    Log(HMAC("sha512", "base64", "hello", "pass"));
}

UnixNano()

UnixNano()Return nanosecond timestamp, if you need to get millisecond timestamp, you can use the following code:

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

Unix()

Unix()Returns the timestamp in seconds.

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

GetOS()

GetOS()Returns information about the system where the docker is located.

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

Docker running under the MAC operating system. Log output:

GetOS:darwin/amd64

darwin:Mac OS system name.

MD5(String)

MD5(String)Parameter value:string type.

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

Log output:

MD5 5eb63bbbe01eeed093cb22bb8f5acdc3

Built-in Functions

_G(K, V)

_G(K, V)a global dictionary that can be saved, both backtest and real market support. After the backtest, the saved data will be cleared. The KV table is permanently stored in a local file. Each robot has a separate database. It will always exists after restarting or the docker exits. K must be a string, which is not case sensitive. V can be any JSON serializable content.

function main(){
    // Set a global variable num with a value of 1
    _G("num", 1)     
    // Change a global variable num with the value ok
    _G("num", "ok")    
    // Delete global variable num
    _G("num", null)
    // Returns the value of the global variable num
    Log(_G("num"))
    // Delete all global variables
    _G(null)
}
def main():
    _G("num", 1)     
    _G("num", "ok")    
    _G("num", None)
    Log(_G("num"))
    _G(None)
void main() {
    _G("num", 1);
    _G("num", "ok");
    _G("num", NULL);
    Log(_G("num"));
    _G(NULL);
}

_D(Timestamp, Fmt)

_D(Timestamp, Fmt), returns the specified timestamp. Parameter value:Timestamp is a numeric type, in milliseconds. Fmt is a string type Fmt defaults to : yyyy-MM-dd hh:mm:ssreturn value: string type. Returns the specified timestamp(ms) string, returns the current time without passing any parameters, for example: _D() or _D(1478570053241)the default format is yyyy-MM-dd hh:mm:ss.

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

Note: When using _D() in the Python writing strategyyou need to pay attention that the parameters passed in are timestamps in seconds(in the JavaScript and C ++ strategies, timestamps in the millisecond level, 1 second = 1000 milliseconds). In the real marketwhen using the _D() function to parse a time string with a readable timestamp, you need to pay attention to the operation of the docker program.$$

As the time zone and time of the system, the _D() function parses a timestamp as a readable time string based on the time of the docker system, that is, if a timestamp is: 1574993606000, use code to parse:

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

_N(Num, Precision)

_N(Num, Precision)format a floating point number. Parameter value, Num is number typePrecision is integer number. Return value:number type. For example:_N(3.1415, 2), will delete the value after two decimal places and return 3.14.

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);
}

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

function main(){
    var i = 1300
    Log(i)
    var ii = _N(i, -3)
    // Checking the log shows 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);
}

_C(…)

_C(function, args...)Retry function

The specified function will be called until it returns successfully (when the function returns null or false, it will keep retry), such as _C(exchange.GetTicker), the default retry interval is 3 seconds, and you can call _CDelay(...) function to control the retry interval, such as _CDelay(1000), refers to changing the _C function retry interval to 1 second, it is recommended:

  • exchange.GetTicker()
  • exchange.GetDepth()
  • exchange.GetTrade()
  • exchange.GetRecords()
  • exchange.GetAccount()
  • exchange.GetOrders()
  • exchange.GetOrder()
  • exchange.GetPosition() All are called through _C(...) to prevent errors, _C(function, args...) functions are not limited to the interface fault tolerance listed above, parameters function is a function reference, not a function call. Note that it is _C(exchange.GetTicker), not _C(exchange.GetTicker()).
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 functions with parameters, when using _C(...) to do fault tolerance:

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);
}

It can also be used for fault-tolerant processing of custom functions:

var test = function(a, b){
    var time = new Date().getTime() / 1000
    if(time % b == 3){
        Log("Meet the criteria!", "#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("Meet the criteria!", "#FF0000")
        return True
    Log("Retry!", "#FF0000")
    return False

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

_Cross(Arr1,Arr2)

_Cross(Arr1, Arr2)returns the number of crossing cycles of the arrays arr1 and arr2. A positive number is the period of upswing, a negative number is the period of downswings, and 0 means the same as the current price. Parameter value: number array.

You can simulate a set of data to test the _Cross(Arr1, Arr2) function:

// 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));
}

Visualize the simulated data to observe

img

Specific instructions:Built-in functions_Cross analysis and instructions

Custom Color

Each message string can end with an RGB value such as #ff0000, which represents the foreground color to be displayed.If it is in a format such as #ff0000112233, the last six posteriors represent the background color.

Color hex diagram:

function main() {
    Log("Red", "#FF0000")
}
def main():
    Log("Red", "#FF0000")
void main() {
    Log("Red", "#FF0000");
}

Log Information

Log(…)

Log(message), save a message to the log list. Parameter value: message can be any type. If you add the @ character after the string, the message will enter the push queue and be pushed to the WeChat account using the binding (bound in the account security) (50 entries/hour, 1 entry /5 seconds limit) Note:

  • The information pushed by WeChat cannot be repeated! (Otherwise, only one will be pushed.)
  • “Debugging Tool” does not support WeChat push.

Bind WeChat settings:

function main() {
    Log("Hello FMZ! @")
    Sleep(1000 * 5)
    // Add # ff0000 to the string, the print log is displayed in red, note: the backtest system does not support WeChat push!
    Log("Hello WeChat, #ff0000@")
}
def main():
    Log("Hello FMZ! @")
    Sleep(1000 * 5)
    Log("Hello WeChat, #ff0000@")
void main() {
    Log("Hello FMZ! @");
    Sleep(1000 * 5);
    Log("Hello WeChat, #ff0000@");
}

WebHook Push:

Use the service program DEMO written in Golang:

package main
import (
    "fmt"
    "net/http"
)

func Handle (w http.ResponseWriter, r *http.Request) {
    defer func() {
        fmt.Println("req:", *r)
    }()
}

func main () {
    fmt.Println("listen http://localhost:9090")
    http.HandleFunc("/data", Handle)
    http.ListenAndServe(":9090", nil)
}

Set WebHook

After running the service program, execute the strategy and push the information:

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

After receiving the push, the service program prints the information:

listen http://localhost:9090
req: {GET /data?data=Hello_FMZ HTTP/1.1 1 1 map[User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.x.xxxx.xxx Safari/537.36] Accept-Encoding:[gzip]] {} <nil> 0 [] false 1XX.XX.X.XX:9090 map[] map[] <nil> map[] XXX.XX.XXX.XX:4xxx2 /data?data=Hello_FMZ <nil> <nil> <nil> 0xc420056300}

Log supports printing base64 encoded pictures, starting with ` and ending with `, such as:

function main() {
    Log("`data:image/png;base64,AAAA`")
}
def main():
    Log("`data:image/png;base64,AAAA`")
void main() {
    Log("`data:image/png;base64,AAAA`");
}

Log supports direct printing of Python’s matplotlib.pyplot object, as long as the object contains the savefig method, you can directly log print, such as: Python example:

import matplotlib.pyplot as plt 
def main(): 
    plt.plot([3,6,2,4,7,1]) 
    Log(plt)

The Log function supports language switching, the Log function outputs text, and it will automatically switch to the corresponding language according to the language setting on the platform page, for example:

function main() {
    Log("[trans]Chinese|abc[/trans]")
}
def main():
    Log("[trans]Chinese|abc[/trans]")
void main() {
    Log("[trans]Chinese|abc[/trans]");
}

LogProfit(Profit)

LogProfit(Profit), record the profit value, this is the value of the total profit, print the profit value, and draw a profit curve according to the value of the profit, the parameter value: Profit is of type number.

It can end with the characters &, to realize only drawing the revenue chart, and not printing the revenue log: LogProfit(10, '&').

LogProfitReset()

LogProfitReset(), to clear all revenue logs, you can take a numeric parameter to specify the number of reserved items.

function main() {
    // Print 30 points on the income chart, then reset, only the last 10 points are retained
    for(var i = 0; i < 30; i++) {
        LogProfit(i)
        Sleep(500)
    }
    LogProfitReset(10)
}
def main():
    for i in range(30):
        LogProfit(i)
        Sleep(500)
    LogProfitReset(10)
void main() {
    for(int i = 0; i < 30; i++) {
        LogProfit(i);
        Sleep(500);
    }
    LogProfitReset(10);
}

LogStatus(Msg)

LogStatus(Msg)This information is not saved in the log list, only the current status information of the robot is updated. It is displayed above the log and can be called multiple times to update the status. Parameter value: Msg can be any type.

function main() {
    LogStatus('This is a normal status prompt')
    LogStatus('This is a status prompt in red font # ff0000')
    LogStatus('This is a multi-line status message \nI am the second line')
}
def main():
    LogStatus('This is a normal status prompt')
    LogStatus('This is a status prompt in red font # ff0000')
    LogStatus('This is a multi-line status message \nI am the second line')
void main() {
    LogStatus("This is a normal status prompt");
    LogStatus("This is a status prompt in red font # ff0000");
    LogStatus("This is a multi-line status message \nI am the second line");
}

LogStatus(Msg) supports printing base64-encoded pictures, starting with `and ending with`, such as: LogStatus("`data:image/png;base64,AAAA`"). LogStatus(Msg) supports direct import of Python’s matplotlib.pyplot object, as long as the object contains the savefig method, you can pass in the LogStatus(Msg) function, such as:

import matplotlib.pyplot as plt 
def main():
    plt.plot([3,6,2,4,7,1])
    LogStatus(plt) 

Example of data output in the status bar:

function main() {
    var table = {type: 'table', title: 'Position Information', cols: ['Column1', 'Column2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
    // After the JSON order is columnized, add the ` character on both sides, which is regarded as a complex message format (currently supports tables)
    LogStatus('`' + JSON.stringify(table) + '`')                    
    // Table information can also appear in multiple lines
    LogStatus('First line message\n`' + JSON.stringify(table) + '`\nThird line message')
    // Support multiple tables displayed at the same time, will be displayed in a group with TAB
    LogStatus('`' + JSON.stringify([table, table]) + '`')
    
    // You can also construct a button in the table, and the strategy uses GetCommand to receive the content of the cmd property                                
    var table = { 
        type: 'table', 
        title: 'Position operation', 
        cols: ['Column1', 'Column2', 'Action'], 
        rows: [ 
            ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': 'close position'}]
        ]
    }
    LogStatus('`' + JSON.stringify(table) + '`') 
    // Or construct a separate button
    LogStatus('`' + JSON.stringify({'type':'button', 'cmd': 'coverAll', 'name': 'close position'}) + '`') 
    // Can customize the button style (button attribute of bootstrap)
    LogStatus('`' + JSON.stringify({'type':'button', 'class': 'btn btn-xs btn-danger', 'cmd': 'coverAll', 'name': 'close position'}) + '`')
}
import json
def main():
    table = {"type": "table", "title": "holding position information", "cols": ["Column1", "Column2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}
    LogStatus('`' + json.dumps(table) + '`')
    LogStatus('the first line message\n`' + json.dumps(table) + '`\nthe third line message')
    LogStatus('`' + json.dumps([table, table]) + '`')

    table = {
        "type" : "table", 
        "title" : "position operation", 
        "cols" : ["Column1", "Column2", "Action"], 
        "rows" : [
            ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "close position"}]
        ] 
    }
    LogStatus('`' + json.dumps(table) + '`')
    LogStatus('`' + json.dumps({"type": "button", "cmd": "coverAll", "name": "close position"}) + '`')
    LogStatus('`' + json.dumps({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "close position"}) + '`')
void main() {
    json table = R"({"type": "table", "title": "holding position information", "cols": ["Column1", "Column2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
    LogStatus("`" + table.dump() + "`");
    LogStatus("the first line message\n`" + table.dump() + "`\nthe third line message");
    json arr = R"([])"_json;
    arr.push_back(table);
    arr.push_back(table);
    LogStatus("`" + arr.dump() + "`");

    table = R"({
        "type" : "table", 
        "title" : "position operation", 
        "cols" : ["Column1", "Column2", "Action"], 
        "rows" : [
            ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "close position"}]
        ] 
    })"_json;
    LogStatus("`" + table.dump() + "`");
    LogStatus("`" + R"({"type": "button", "cmd": "coverAll", "name": "close position"})"_json.dump() + "`");
    LogStatus("`" + R"({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "close position"})"_json.dump() + "`");
}

Combine the GetCommand() function to construct the interactive function of the status bar button:

function test1() {
    Log("Call a custom function")
}

function main() {
    while (true) {
        var table = {
            type: 'table',
            title: 'operation',
            cols: ['Column1', 'Column2', 'Action'],
            rows: [
                ['a', '1', {
                    'type': 'button',                       
                    'cmd': "CoverAll",                      
                    'name': 'close position'                           
                }],
                ['b', '1', {
                    'type': 'button',
                    'cmd': 10,                              
                    'name': 'Send value'
                }],
                ['c', '1', {
                    'type': 'button',
                    'cmd': _D(),                          
                    'name': 'call function'
                }],
                ['d', '1', {
                    'type': 'button',
                    'cmd': 'test1',       
                    'name': 'Call a custom function'
                }]
            ]
        }
        LogStatus(_D(), "\n", '`' + JSON.stringify(table) + '`')

        var str_cmd = GetCommand()
        if (str_cmd) {
            Log("Received interactive data str_cmd:", "Types of:", typeof(str_cmd), "Value:", str_cmd)
            if(str_cmd == "test1") {
                test1()
            }
        }

        Sleep(500)
    }
}
import json
def test1():
    Log("Call a custom function")

def main():
    while True:
        table = {
            "type": "table", 
            "title": "operation", 
            "cols": ["Column1", "Column2", "Action"],
            "rows": [
                ["a", "1", {
                    "type": "button", 
                    "cmd": "CoverAll",
                    "name": "close position"
                }],
                ["b", "1", {
                    "type": "button",
                    "cmd": 10,
                    "name": "Send value" 
                }], 
                ["c", "1", {
                    "type": "button",
                    "cmd": _D(),
                    "name": "call function" 
                }],
                ["d", "1", {
                    "type": "button",
                    "cmd": "test1",
                    "name": "Call a custom function" 
                }]
            ]
        }

        LogStatus(_D(), "\n", "`" + json.dumps(table) + "`")
        str_cmd = GetCommand()
        if str_cmd:
            Log("Received interactive data str_cmd", "Types of:", type(str_cmd), "value:", str_cmd)
            if str_cmd == "test1":
                test1()
        Sleep(500)
void test1() {
    Log("Call a custom function");
}

void main() {
    while(true) {
        json table = R"({
            "type": "table", 
            "title": "operation", 
            "cols": ["Column1", "Column2", "Action"],
            "rows": [
                ["a", "1", {
                    "type": "button", 
                    "cmd": "CoverAll",
                    "name": "close position"
                }],
                ["b", "1", {
                    "type": "button",
                    "cmd": 10,
                    "name": "Send value" 
                }], 
                ["c", "1", {
                    "type": "button",
                    "cmd": "",
                    "name": "call function" 
                }],
                ["d", "1", {
                    "type": "button",
                    "cmd": "test1",
                    "name": "Call a custom function" 
                }]
            ]
        })"_json;
        table["rows"][2][2]["cmd"] = _D();
        LogStatus(_D(), "\n", "`" + table.dump() + "`");
        auto str_cmd = GetCommand();
        if(str_cmd != "") {
            Log("Received interactive data str_cmd", "Types of:", typeid(str_cmd).name(), "value:", str_cmd);
            if(str_cmd == "test1") {
                test1();
            }
        }
        Sleep(500);
    }
}

Combine the cells in the table drawn by the LogStatus(Msg) function:

  • Horizontal merge

    function main() {
        var table = { 
            type: 'table', 
            title: 'position operation', 
            cols: ['Column1', 'Column2', 'Action'], 
            rows: [ 
                ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': 'close position'}]
            ]
        } 
        var ticker = exchange.GetTicker()
        // Add a row of data, merge the first and second cells, and output the ticker variable in the merged cell
        table.rows.push([{body : JSON.stringify(ticker), colspan : 2}, "abc"])    
        LogStatus('`' + JSON.stringify(table) + '`')
    }
    
    import json
    def main():
        table = {
            "type" : "table",
            "title" : "position operation",
            "cols" : ["Column1", "Column2", "Action"],
            "rows" : [
                ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "close position"}]
            ]
        }
        ticker = exchange.GetTicker()
        table["rows"].append([{"body": json.dumps(ticker), "colspan": 2}, "abc"])
        LogStatus("`" + json.dumps(table) + "`")
    
    void main() {
        json table = R"({
            "type" : "table",
            "title" : "position operation",
            "cols" : ["Column1", "Column2", "Action"],
            "rows" : [
                ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "close position"}]
            ]
        })"_json;
    
        auto ticker = exchange.GetTicker();
        json jsonTicker = R"({"Buy": 0, "Sell": 0, "High": 0, "Low": 0, "Volume": 0, "Last": 0, "Time": 0})"_json;
        jsonTicker["Buy"] = ticker.Buy;
        jsonTicker["Sell"] = ticker.Sell;
        jsonTicker["Last"] = ticker.Last;
        jsonTicker["Volume"] = ticker.Volume;
        jsonTicker["Time"] = ticker.Time;
        jsonTicker["High"] = ticker.High;
        jsonTicker["Low"] = ticker.Low;
    
        json arr = R"([{"body": {}, "colspan": 2}, "abc"])"_json;
        arr[0]["body"] = jsonTicker;
        table["rows"].push_back(arr);
        LogStatus("`" + table.dump() + "`");
    }