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.
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.
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.
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}
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}
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.
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);
}
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.
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.
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.
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:
ws.read()
.ws.read(2000)
specifies a timeout of two seconds (2000 milliseconds).-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×tamp=" + 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
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
}
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"
,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.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}
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
}
tls
fingerprints.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.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}
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.
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}
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}
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.
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}
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}
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.
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 __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}
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
.
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
.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
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:
HTTP/1.1
, tcp
.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.User-Agent
in the headers of the current request: ctx.header("User-Agent")
.GET
, POST
, etc.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.read
method is not supported in ordinary Http protocol. You can specify the timeout parameter timeout_ms
in milliseconds.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}
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}
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}
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}
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())
.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.
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.