资源加载中... loading...

Global

Version

返回系统当前版本号。

当前系统版本号,例如:3.6。 string

Version()

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

系统版本号即托管者程序的版本号。

Sleep

休眠函数,使程序暂停一段时间。

Sleep(millisecond)

millisecond参数用于设置休眠时长,毫秒数。 millisecond true number

function main() {
    Sleep(1000 * 10)   // 等待10秒钟
    Log("等待了10秒钟")
}
def main():
    Sleep(1000 * 10)
    Log("等待了10秒钟")
void main() {
    Sleep(1000 * 10);
    Log("等待了10秒钟");
}

例如执行Sleep(1000)函数时,程序会休眠1秒。 支持休眠时间小于1毫秒的操作,例如设置Sleep(0.1)。 支持最小参数为0.000001,即纳秒级休眠,1纳秒等于1e-6毫秒。 在使用Python语言编写策略时,对于轮询间隔、时间等待的操作应当使用Sleep(millisecond)函数。 不建议使用Pythontime库的time.sleep(second)函数。 因为策略中使用time.sleep(second)函数在回测时会让策略程序实际等待一段时间(并非在回测系统的时间序列上跳过),所以导致策略回测非常慢。

IsVirtual

判断策略的运行环境是否为回测系统。

策略运行在回测系统环境时返回真值,例如:true。 策略运行在实盘环境时返回假值,例如:false。 bool

IsVirtual()

function main() {
    if (IsVirtual()) {
        Log("当前为回测系统环境。")
    } else {
        Log("当前为实盘环境。")
    }
}
def main():
    if IsVirtual():
        Log("当前为回测系统环境。")
    else:
        Log("当前为实盘环境。")
void main() {
    if (IsVirtual()) {
        Log("当前为回测系统环境。");
    } else {
        Log("当前为实盘环境。");
    }
}

判断当前运行环境是否是回测系统,用来兼容回测与实盘的差异。

Mail

发送邮件。

邮件发送成功返回真值,例如:true,发送失败返回假值,例如:false。 bool

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

用于指定邮件发送方的SMTP服务地址。 smtpServer true string 用于指定邮件发送方的邮箱地址。 smtpUsername true string 邮件发送方邮箱的SMTP密码。 smtpPassword true string 用于指定邮件接收方的邮箱地址。 mailTo true string 邮件标题。 title true string 邮件正文。 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");
}

smtpPassword参数设置的是SMTP服务的密码,不是邮箱密码。 设置smtpServer参数时,如果需要更改端口可以直接在参数smtpServer中加入端口号。 例如:QQ邮箱smtp.qq.com:587该端口测试可用。 如果出现报错:unencryped connection,需要修改Mail函数的smtpServer。 参数格式为:ssl://xxx.com:xxx,例如QQ邮箱的SMTPssl方式:ssl://smtp.qq.com:465或者smtp://xxx.com:xxx。 回测系统中不起作用。

{@fun/Global/Mail_Go Mail_Go}

Mail_Go

Mail函数的异步版本。

Mail_Go函数立即返回一个并发对象,可以使用该并发对象的wait方法获取邮件发送结果, 邮件发送成功返回真值,例如:true,发送失败返回假值,例如:false。 object

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

用于指定邮件发送方的SMTP服务地址。 smtpServer true string 用于指定邮件发送方的邮箱地址。 smtpUsername true string 邮件发送方邮箱的SMTP密码。 smtpPassword true string 用于指定邮件接收方的邮箱地址。 mailTo true string 邮件标题。 title true string 邮件正文。 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)
}
# 不支持
// 不支持

回测系统中不起作用。

{@fun/Global/Mail Mail}

SetErrorFilter

过滤错误日志。

SetErrorFilter(filters)

正则表达式字符串。 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");
}

过滤常见错误。

function main() {
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    // 设置合约代码
    exchange.SetContractType("rb888")
    
    // 随便查询一个不存在的订单,id为123,故意让接口报错
    var order = exchange.GetOrder("123")
    Log(order)
    // 过滤http502错误、GetOrder接口错误,设置错误过滤之后,第二次调用GetOrder不再报错
    SetErrorFilter("502:|GetOrder")
    order = exchange.GetOrder("123")
    Log(order)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    
    exchange.SetContractType("rb888")
    
    order = exchange.GetOrder("123")
    Log(order)
    SetErrorFilter("502:|GetOrder")
    order = exchange.GetOrder("123")
    Log(order)
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    
    exchange.SetContractType("rb888");
    
    TId orderId;
    Order order = exchange.GetOrder(orderId);
    Log(order);
    SetErrorFilter("502:|GetOrder");
    order = exchange.GetOrder(orderId);
    Log(order);
}

过滤某个接口错误信息。

被此正则表达式匹配的错误日志将不上传到日志系统,可多次调用(没有次数限制)设置多个过滤条件。多次设置的正则表达式会累积在一起同时生效。可以设置空字符串来重置用以过滤错误日志的正则表达式:SetErrorFilter("")。 被过滤的日志不再写入托管者目录下对应实盘Id的数据库文件中,防止频繁报错导致数据库文件膨胀。

GetPid

获取实盘进程Id。

返回实盘进程Id。 string

GetPid()

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

GetLastError

获取最近一次出错信息。

最近一次出错信息。 string

GetLastError()

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    
    exchange.SetContractType("rb888")
    
    // 因为不存在编号为123的订单,所以会出错
    exchange.GetOrder("123")
    var error = GetLastError()
    Log(error)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    
    exchange.SetContractType("rb888")
    
    exchange.GetOrder("123")
    error = GetLastError()
    Log(error)
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    
    exchange.SetContractType("rb888");
    
    // 订单ID类型:TId,所以不能传入字符串,我们下一个不符合交易所规范的订单来触发
    exchange.GetOrder(exchange.Buy(1, 1));
    auto error = GetLastError();
    Log(error);
}

回测系统中不起作用。

GetCommand

获取策略交互命令。

返回的命令格式为ControlName:DataControlName是控件名称,Data是控件中输入的数据。 如果交互控件没有输入框、下拉框等组件(例如:不带输入框的按钮控件)则返回的命令格式为ControlName,仅返回控件名称。 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);
    }
}

检测交互命令并且在检测到交互命令时使用Log函数输出交互命令。

function main() {
    while (true) {
        LogStatus(_D())
        var cmd = GetCommand()
        if (cmd) {
            Log("cmd:", cmd)    
            var arr = cmd.split(":")
            if (arr[0] == "buy") {
                Log("买入,该控件不带数量")
            } else if (arr[0] == "sell") {
                Log("卖出,该控件带数量:", arr[1])
            } else {
                Log("其它控件触发:", arr)
            }
        }
        Sleep(1000)
    } 
}
def main():
    while True:
        LogStatus(_D())
        cmd = GetCommand()
        if cmd:
            Log("cmd:", cmd)
            arr = cmd.split(":")
            if arr[0] == "buy":
                Log("买入,该控件不带数量")
            elif arr[0] == "sell":
                Log("卖出,该控件带数量:", arr[1])
            else:
                Log("其它控件触发:", 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("买入,该控件不带数量");
            } else if (arr[0] == "sell") {
                Log("卖出,该控件带数量:", arr[1]);
            } else {
                Log("其它控件触发:", arr);
            }
        }
        Sleep(1000);
    }
}

例如策略交互控件中添加了一个不带输入框的控件,交互控件命名为:buy,控件描述信息为:买入,这是一个按钮控件。 继续添加一个带输入框的控件,交互控件名为:sell,控件描述信息为:卖出,这是一个由按钮和输入框组合起来的交互控件。 策略中设计交互代码来响应不同交互控件:

回测系统中不起作用。

GetMeta

获取生成策略注册码时写入的Meta的值。

Meta数据。 string

GetMeta()

function main() {
    // 策略允许的计价币最大资产数值
    var maxBaseCurrency = null
    
    // 获取创建注册码时的元数据
    var level = GetMeta()
    
    // 检测Meta对应的条件
    if (level == "level1") {
        // -1为不限制
        maxBaseCurrency = -1       
    } else if (level == "level2") {
        maxBaseCurrency = 10     
    } else if (level == "level3") {
        maxBaseCurrency = 1
    } else {
        maxBaseCurrency = 0.5
    }
    
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    
    exchange.SetContractType("rb888")
    
    while(true) {
        Sleep(1000)
        var ticker = exchange.GetTicker()
        
        // 检测资产数值
        var acc = exchange.GetAccount()
        if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Balance + acc.FrozenBalance) {
            // 停止执行策略交易逻辑
            LogStatus(_D(), "level:", level, "持仓超过注册码的使用限定,不再执行策略交易逻辑!")
            continue
        }
        
        // 其它交易逻辑
        
        // 正常输出状态栏信息
        LogStatus(_D(), "level:", level, "策略正常运行!ticker数据:\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 not exchange.IO("status"):
        Sleep(1000)
    
    exchange.SetContractType("rb888")
    
    while True:
        Sleep(1000)
        ticker = exchange.GetTicker()        
        acc = exchange.GetAccount()
        if maxBaseCurrency != -1 and maxBaseCurrency < acc["Balance"] + acc["FrozenBalance"]:
            LogStatus(_D(), "level:", level, "持仓超过注册码的使用限定,不再执行策略交易逻辑!")
            continue        
        
        # 其它交易逻辑
        
        # 正常输出状态栏信息
        LogStatus(_D(), "level:", level, "策略正常运行!ticker数据:\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(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    
    exchange.SetContractType("rb888");
    
    while(true) {
        Sleep(1000);
        auto ticker = exchange.GetTicker();  
        auto acc = exchange.GetAccount();
        if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Balance + acc.FrozenBalance) {
            // 停止执行策略交易逻辑
            LogStatus(_D(), "level:", level, "持仓超过注册码的使用限定,不再执行策略交易逻辑!");
            continue;
        }
        
        // 其它交易逻辑
        
        // 正常输出状态栏信息
        LogStatus(_D(), "level:", level, "策略正常运行!ticker数据:\n", ticker);
    }
}

应用场景范例:使用Meta限制策略操作的资产量。

应用场景:需要对不同的策略租用者做资金限制。 生成注册码时设置的Meta值长度不能超过190个字符,GetMeta()函数仅支持实盘。 如果生成策略注册码时没有设置元数据(Meta),GetMeta()函数返回空值。 回测系统中不起作用。

Dial

用于原始的Socket访问,支持tcpudptlsunix协议。 支持4种流行的通信协议:mqttnatsamqpkafka。 支持连接数据库,支持的数据库有:sqlite3mysqlpostgresclickhouse

如果超时Dial()函数返回空值。 正常调用时返回一个连接对象,该对象有三个方法:readwritecloseread方法用于读取数据,write方法用于发送数据。 close方法用于关闭连接。 read方法支持以下参数:

  • 不传参数时,阻塞到有消息时就返回。例如:ws.read()
  • 传入参数时,单位为毫秒,指定消息等待超时时间。例如:ws.read(2000)指定超时时间为两秒(2000毫秒)。
  • 以下两个参数只对WebSocket有效: 传入参数-1指不管有无消息,函数立即返回,例如:ws.read(-1)。 传入参数-2指不管有无消息,函数立即返回,但只返回最新的消息,缓冲区的消息会被丢弃。例如ws.read(-2)

read()函数缓冲区说明: WebSocket协议推送的来的数据,如果在策略read()函数调用之间时间间隔过长,就可能造成数据累积。这些数据储存在缓冲区,缓冲区数据结构为队列,上限2000个。超出2000后最新的数据进入缓冲区,最旧的数据清除掉。

场景 无参数 参数:-1 参数:-2 参数:2000,单位是毫秒
缓冲区已有数据 立即返回最旧数据 立即返回最旧数据 立即返回最新数据 立即返回最旧数据
缓冲区无数据 阻塞到有数据时返回 立即返回空值 立即返回空值 等待2000毫秒,无数据返回空值,有数据则返回
WebSocket连接断开或者底层重连时 read()函数返回空字符串,即:"",write()函数返回0,检测到该情况。可以使用close()函数关闭连接,如果设置了自动重连则不用关闭,系统底层会自动重连。

object

Dial(address) Dial(address, timeout)

请求地址。 address true string 超时秒数, timeout false number

function main(){
    // Dial支持tcp://,udp://,tls://,unix://协议,可加一个参数指定超时的秒数
    var client = Dial("tls://www.baidu.com:443")  
    if (client) {
        // write可再跟一个数字参数指定超时,write返回成功发送的字节数
        client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
        while (true) {
            // read可再跟一个数字参数指定超时,单位:毫秒。返回null指出错或者超时或者socket已经关闭
            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();
    }
}

Dial函数调用例子:

var client = null 
function main() {
    // client = Dial("sqlite3://:memory:")   // 使用内存数据库
    client = Dial("sqlite3://test1.db")      // 打开/连接托管者所在目录的数据库文件
    
    // 记录句柄
    var sqlite3Handle = client.fd()
    Log("sqlite3Handle:", sqlite3Handle)
    
    // 查询数据库中的表
    var ret = client.exec("SELECT name FROM sqlite_master WHERE type='table'")
    Log(ret)
}

function onexit() {
    Log("执行client.close()")
    client.close()
}
// 不支持
// 不支持

Dial函数连接数据库时返回的连接对象有2个其独有的方法函数:

  • exec(sqlString): 用于执行SQL语句,使用方式类似于DBExec()函数。
  • fd(): fd()函数返回一个句柄(例如:句柄变量为handle),用于其它线程重连(即使Dial创建的对象已经被执行close()函数关闭连接),将句柄传入Dial()函数,例如:Dial(handle)重用连接。 以下是Dial函数连接sqlite3数据库的例子。

address参数的详细说明,在正常的地址:wss://xxx.xxx.xxx:10441/websocket?compress后,以|符号分隔。 如果参数字符串中有|字符,则以||作为分隔符号。之后的部分是一些功能参数设置,各个参数之间用&字符连接。 例如ss5代理和压缩参数一起设置时可以写作: Dial("wss://baidu.com/stream|proxy=socks5://xxx:9999&compress=gzip_raw&mode=recv")

Dial函数的address参数支持的功能 参数说明
WebSocket协议数据压缩相关的参数:compress=参数值 compress为压缩方式,compress参数可选gzip_raw、gzip等。如果gzip方式非标准gzip,可以使用扩展的方式:gzip_raw
WebSocket协议数据压缩相关的参数:mode=参数值 mode为压缩模式,mode参数可选dual,send,recv三种。dual为双向压缩,发送压缩数据,接收压缩数据。send为发送压缩数据。recv为接收压缩数据,本地解压缩。
WebSocket协议设置底层自动重连相关的参数:reconnect=参数值 reconnect为是否设置重连,reconnect=true为启用重连。不设置该参数时默认不重连。
WebSocket协议设置底层自动重连相关的参数:interval=参数值 interval为重试时间间隔,单位毫秒,interval=10000为重试间隔10秒,不设置默认1秒,即interval=1000。
WebSocket协议设置底层自动重连相关的参数:payload=参数值 payload为WebSocket重连时需要发送的订阅消息,例如:payload=okok。
socks5代理的相关参数:proxy=参数值 proxy为ss5代理设置,参数值格式:socks5://name:pwd@192.168.0.1:1080,name为ss5服务端用户名,pwd为ss5服务端登录密码,1080为ss5服务的端口。

Dial()函数仅支持实盘。 使用Dial函数连接数据库时,编写的连接字符串参考各数据库的go语言驱动项目。

支持的数据库 驱动项目 连接字符串(Connection String) 备注
sqlite3 github.com/mattn/go-sqlite3 sqlite3://file:test.db?cache=shared&mode=memory sqlite3://前缀表示使用的是sqlite3数据库,调用例子: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

目前仅JavaScript语言支持Dial函数中使用mqttnatsamqpkafka通信协议,以JavaScript语言策略代码为例展示mqttnatsamqpkafka四种协议使用例子:

// 需要先配置、部署完成各个协议的代理服务器
// 为了便于演示,主题test_topic的订阅(read操作)、发布(write操作)都在当前这个策略中进行
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]

            // 写数据
            conn.write(name + ", time: " + _D() + ", test msg.")
            
            // 读数据
            var readMsg = conn.read(1000)
            Log(name + " readMsg: ", readMsg, "#FF0000")
        }

        Sleep(1000)
    }
}

function onexit() {
    for (var i in arrConn) {
        arrConn[i].close()
        Log("关闭", arrName[i], "连接")
    }
}

HttpQuery

发送Http请求。

返回请求的应答数据,如果返回值为JSON字符串,JavaScript语言的策略中可以用JSON.parse()函数解析,C++语言的策略中可以用json::parse()函数解析。 参数options结构中如果debug设置为true,返回值为对象(JSON);如果debug设置为false,返回值为字符串。 string、object

HttpQuery(url) HttpQuery(url, options)

Http请求url。 url true string Http请求相关设置,例如可以是以下结构:

{
    method: "POST",
    body: "a=10&b=20&c=30",
    charset: "UTF-8",
    cookie: "session_id=12345; lang=en",
    profile: "chrome_103",
    debug: false,
    headers: {"TEST-HTTP-QUERY": "123"},
    timeout: 1000
}
  • profile:用来模拟浏览器tls指纹。 支持的设置有以下选项: chrome_:"chrome_103""chrome_104""chrome_105""chrome_106""chrome_107""chrome_108""chrome_109""chrome_110""chrome_111""chrome_112""chrome_117"、 safari_:"safari_15_6_1""safari_16_0""safari_ipad_15_6""safari_ios_15_5""safari_ios_15_6""safari_ios_16_0"、 firefox_:"firefox_102""firefox_104""firefox_105""firefox_106""firefox_108""firefox_110""firefox_117"、 opera_:"opera_89""opera_90""opera_91"、 zalando_:"zalando_android_mobile""zalando_ios_mobile"、 nike_:"nike_ios_mobile""nike_android_mobile"、 cloudscraper:"cloudscraper"、 mms_:"mms_ios"、 mesh_:"mesh_ios""mesh_ios_1""mesh_ios_2""mesh_android""mesh_android_1""mesh_android_2"、 confirmed_:"confirmed_ios""confirmed_android"、 okhttp4_:"okhttp4_android_7""okhttp4_android_8""okhttp4_android_9""okhttp4_android_10""okhttp4_android_11""okhttp4_android_12""okhttp4_android_13"
  • debug:设置为true时,此次HttpQuery函数调用返回完整的应答报文。设置为false时只返回应答报文Body中的数据。
  • timeout:超时设置,设置1000表示1秒钟超时。
  • charset:支持对请求的应答数据进行转码,例如:GB18030。支持常用编码。

此结构中的所有字段均为可选,例如可以不设置profile字段。

options false object

function main() {
    // 本次设置代理并发送http请求,无用户名,无密码,此次http请求会通过代理发送
    HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/")            

    // 本次设置代理并发送http请求,输入用户名和密码,仅HttpQuery当前调用生效,之后再次调用HttpQuery("http://www.baidu.com")这样不会使用代理
    HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/")
}
# HttpQuery不支持Python,可以使用Python的urllib2库
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/");
}

HttpQuery函数使用代理设置。

HttpQuery()函数只支持JavaScriptC++语言,Python语言可以使用urllib库,直接发送Http请求。 回测系统中可以使用HttpQuery()发送请求(只支持GET请求)获取数据。 回测时限制使用20次访问不同的URL,并且HttpQuery()访问会缓存数据, 相同的URL第二次访问时HttpQuery()函数返回缓存数据,不再发生实际网络请求。

{@fun/Global/HttpQuery_Go HttpQuery_Go}

HttpQuery_Go

发送Http请求,HttpQuery函数的异步版本。

HttpQuery_Go()函数立即返回一个并发对象,可以使用该并发对象的wait方法获取Http请求的结果, JavaScript语言的策略中可以用JSON.parse()函数解析。
object

HttpQuery_Go(url) HttpQuery_Go(url, options)

Http请求url。 url true string Http请求相关设置,例如可以是以下结构:

{
    method: "POST",
    body: "a=10&b=20&c=30",
    charset: "UTF-8",
    cookie: "session_id=12345; lang=en",
    // profile: "",
    debug: false,
    headers: {"TEST-HTTP-QUERY": "123"},
    timeout: 1000
}              
  • profile:用来模拟浏览器tls指纹。
  • debug:设置为true时,此次HttpQuery_Go函数调用返回完整的应答报文。设置为false时只返回应答报文Body中的数据。
  • timeout:超时设置,设置1000表示1秒钟超时。

此结构中的所有字段均为可选,例如可以不设置profile字段。

options false object

function main() {
    // 创建第一个异步线程
    var r1 = HttpQuery_Go("https://xxx.xxx.xxx")   // https://xxx.xxx.xxx 仅为演示地址,可以是某个数据源
    // 创建第二个异步线程
    var r2 = HttpQuery_Go("https://xxx.xxx.xxx")
    
    // 获取第一个异步线程调用的返回值
    var tickers1 = r1.wait()
    // 获取第二个异步线程调用的返回值
    var tickers2 = r2.wait()
    
    // 打印结果
    Log("tickers1:", tickers1)
    Log("tickers2:", tickers2)
}
# 不支持
// 不支持

HttpQuery_Go()函数使用范例:

HttpQuery_Go()函数只支持JavaScriptPython语言可以使用urllib库,直接发送Http请求。 HttpQuery_Go()主要用于访问交易所不需要签名的接口,例如行情信息等公共接口。回测系统中不支持HttpQuery_Go函数。

{@fun/Global/HttpQuery HttpQuery}

Encode

该函数根据传入的参数对数据进行编码。

Encode函数返回编码、加密之后的数据。 string

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

参数algo为编码计算时使用的算法。 支持设置为:“raw”(不使用算法),“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”。 参数algo也支持:“text.encoder.utf8”,“text.decoder.utf8”,“text.encoder.gbk”,“text.decoder.gbk”,对字符串编码、解码。 参数algo也支持:“ed25519"算法。支持使用不同的哈希算法,例如参数algo可写为"ed25519.md5”、"ed25519.sha512"等。支持ed25519.seed计算。 algo true string 用于指定data参数的数据格式。 inputFormat参数支持设置为:“raw”,“hex”,“base64”,"string"其中之一。 "raw"表示数据为原始数据,"hex"表示数据为hex编码,"base64"表示数据为base64编码,"string"表示数据为字符串。 inputFormat true string 用于指定输出的数据格式。 outputFormat参数支持设置为:“raw”,“hex”,“base64”,"string"其中之一。 "raw"表示数据为原始数据,"hex"表示数据为hex编码,"base64"表示数据为base64编码,"string"表示数据为字符串。 outputFormat true string 参数data为所要处理的数据。 data true string 用于指定key参数的数据格式。 key参数支持设置为:“raw”,“hex”,“base64”,"string"其中之一。 "raw"表示数据为原始数据,"hex"表示数据为hex编码,"base64"表示数据为base64编码,"string"表示数据为字符串。 keyFormat false string 参数keyHMAC加密时使用的秘钥。 参数algo设置为"sign"或者"signTx"时需要参数key。 参数algo设置为"raw"时不会使用key参数进行HMAC加密(因为HMAC加密必须指定算法)。 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
}

Encode函数调用例子。

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

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

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

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

参数algo也支持:“text.encoder.utf8”,“text.decoder.utf8”,“text.encoder.gbk”,“text.decoder.gbk”,对字符串编码、解码。

Encode()函数仅支持实盘。 不传keykeyFormat参数,则不使用key加密。

UnixNano

获取当前时刻的纳秒级时间戳。

UnixNano()函数返回纳秒级时间戳。 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);
}

如果需要获取毫秒级时间戳,可以使用如下代码:

{@fun/Global/Unix Unix}

Unix

获取当前时刻的秒级别时间戳。

返回秒级别时间戳。 number

Unix()

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

{@fun/Global/UnixNano UnixNano}

GetOS

获取托管者所在设备的系统信息。

系统信息。 string

GetOS()

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

例如在Mac OS操作系统下运行的托管者,调用GetOS()函数可能返回:darwin/amd64, 因为苹果电脑有多种硬件架构。darwinMac OS系统的名称。

MD5

计算参数data的MD5哈希值。

MD5哈希值。 string

MD5(data)

需要进行MD5计算的数据。 data true string

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

调用MD5("hello world")函数,返回值为:5eb63bbbe01eeed093cb22bb8f5acdc3

{@fun/Global/Encode Encode}

DBExec

数据库接口函数。

包含sql语句执行结果的对象,例如:

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

object

DBExec(sql)

sql语句字符串。 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)
    
    // 增加一条数据
    Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    // 查询数据
    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)
    
    # 增加一条数据
    Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    # 查询数据
    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);
    
    // 增加一条数据
    Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
    
    // 查询数据
    Log(DBExec(":SELECT * FROM TEST_TABLE;"));
}

支持内存数据库,对于DBExec函数的参数,如果sql语句是以:开头则在内存数据库中操作,不写文件速度更快。 适合不需要持久化保存的数据库操作,例如:

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

创建表。

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))
    
    // 增加一条数据
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    // 查询数据
    Log(DBExec("SELECT * FROM TEST_TABLE;"))
    
    // 修改数据
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))    
    
    // 删除数据
    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))
    
    # 增加一条数据
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
    
    # 查询数据
    Log(DBExec("SELECT * FROM TEST_TABLE;"))
    
    # 修改数据
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))
    
    # 删除数据
    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));            

    // 增加一条数据
    Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
    
    // 查询数据
    Log(DBExec("SELECT * FROM TEST_TABLE;"));
    
    // 修改数据
    Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000));
    
    // 删除数据
    Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110));
}

表中记录的增删查改操作。

函数DBExec()通过传入参数,可以操作实盘数据库(SQLite数据库)。 实现对实盘数据库中数据的增、删、查、改等操作,支持SQLite语法。 实盘数据库中系统保留表:kvdbcfglogprofitchart,切勿对这些表进行操作。 目前不支持事务,不建议执行此类操作,会引起系统中的冲突。 DBExec()函数仅支持实盘。

{@fun/Global/_G _G}

UUID

创建一个UUID。

32位的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);
}

UUID()函数仅支持实盘。

EventLoop

监听事件,有任意事件发生时返回。

返回的对象如果不为空值,则返回内容中包含的Event为事件触发类型。例如以下返回值结构:

{"Seq":10,"Event":"tick","ThreadId":0,"Index":0,"Nano":1689216222796834300,"Symbol":"rb2311","Ticker": {...}}

object

EventLoop() EventLoop(timeout)

参数timeout为超时设置,单位为毫秒。 参数timeout如果设置为0则等待有事件发生才返回,如果大于0就是设置事件等待超时,小于0立即返回最近事件。 timeout false number

function main(){
    while(true){
        // 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数
        if(exchange.IO("status")){
            exchange.SetContractType("rb888")            
            LogStatus(_D(), "已经连接CTP !")
            var msg = EventLoop()
            Log(msg)
        } else {
            LogStatus(_D(), "未连接CTP !")
        }
    }
}
# 不支持EventLoop函数
// 不支持EventLoop函数

代码中第一次调用EventLoop()函数才会初始化该监听事件的机制,如果在事件回调之后才开始首次EventLoop()调用,会错过之前的事件。 底层系统封装的队列结构会缓存最大500个事件回调,如果程序执行过程中没有及时调用EventLoop()函数取出,会丢失500个缓存之外较晚的事件回调。 EventLoop()函数的调用不会影响系统底层WebSocket的缓存队列。 对于这些缓存依然需要使用各自的方法取出数据。对于在EventLoop()函数返回之前,已经取出的数据,不会在EventLoop()函数中产生返回事件。 EventLoop()函数的主要用途就是通知策略层,系统底层接收到了新的网络数据。以事件驱动整个策略。当EventLoop()函数返回事件时, 只需遍历所有数据来源。 EventLoop()函数仅支持实盘。 在主函数main()中调用时,监听主线程的事件。 在JavaScript语言编写的策略中,threading.Thread()函数创建的线程,在线程的执行函数中也可以调用,监听当前线程的事件。

{@fun/Global/Dial Dial}

__Serve

__Serve函数用于创建Http服务、TCP服务、Websocket服务(基于Http协议)。

返回一个字符串,记录创建的服务的IP地址、端口。例如:127.0.0.1:8088[::]:8089

string

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

serveURI参数用于配置服务绑定的协议、IP地址、端口等设置,例如:http://0.0.0.0:8088?gzip=true,即:http://:8088?gzip=true

  • TCP协议 serveURI参数设置例如:tcp://127.0.0.1:6666?tls=true;可以添加证书、私钥例如:tls=true&cert_pem=xxxx&cert_key_pem=xxxx
  • Http协议 serveURI参数设置例如:http://127.0.0.1:6666?gzip=true;可以设置压缩设置:gzip=trueserveURI参数用于Https例如:https://127.0.0.1:6666?tls=true&gzip=true;可以加入cert_pemcert_key_pem参数来加载证书。

serveURI true string handler参数用于传入路由处理函数(Http协议)、消息处理函数(TCP协议)、Stream处理函数(Websocket)。 参数handler传入的回调函数可以定义多个参数,第一个参数为ctx对象(上下文对象)。

handler true function 作为参数handler传入的回调函数的参数的实参,参数arg可能有多个,例如:

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

调用__Serve()函数时传入的参数1, 2, 3对应传入回调函数的参数a, b, c

arg false string、number、bool、object、array、function、空值等系统支持的所有类型

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)
    }
}
# 不支持
// 不支持
  • 该函数仅支持JavaScript语言策略。
  • 服务线程与全局作用域隔离,因此不支持闭包或引用外部变量、自定义函数等;但是可以调用平台所有的API函数。
  • Websocket服务基于Http协议实现,可以在path中设置一个路由分支,设计Websocket消息订阅/推送的实现代码,可以参考本节范例代码。

参数handler传入的回调处理函数接收一个ctx参数。ctx参数为一个上下文对象,用于获取数据和写入数据,有以下方法:

  • ctx.proto() 应用于Http/TCP协议,调用时返回协议名称。例如:HTTP/1.1tcp
  • ctx.host() 应用于Http协议,调用时返回主机信息:IP地址、端口。
  • ctx.path() 应用于Http协议,调动时返回请求路径。
  • ctx.query(key) 应用于Http协议,调用时返回请求中query查询中key对应的值。例如发送的请求为:http://127.0.0.1:8088?num=123,参数handler传入的回调处理函数中ctx.query("num")调用时返回"123"
  • ctx.rawQuery() 应用于Http协议,调用时返回请求中的原始查询(Http请求的query)。
  • ctx.headers() 应用于Http协议,调用时返回请求中的请求头信息。
  • ctx.header(key) 应用于Http协议,调用时返回指定的请求头中的某个key对应的值。例如获取当前请求的headers中的User-Agentctx.header("User-Agent")
  • ctx.method() 应用于Http协议,调用时返回请求方法,例如GETPOST等。
  • ctx.body() 应用于Http协议的POST请求,调用时返回请求的正文。
  • ctx.setHeader(key, value) 应用于Http协议,设置应答报文的请求头信息。
  • ctx.setStatus(code) 应用于Http协议,设置Http报文状态码,通常在路由分支最后设置Http状态码,默认为200。
  • ctx.remoteAddr() 应用于Http/TCP协议,调用时返回请求中的远程客户端地址、端口。
  • ctx.localAddr() 应用于Http/TCP协议,调用时返回服务本地地址、端口。
  • ctx.upgrade(“websocket”) 应用于基于Http协议的Websocket协议实现,切换ctx上下文对象为Websocket协议;切换成功返回布尔值(真),失败返回布尔值(假)。
  • ctx.read(timeout_ms) 应用于基于Http协议的Websocket协议实现/TCP协议,读取Websocket连接的数据,TCP连接的数据,普通Http协议中不支持使用该read方法;可以指定超时时间参数timeout_ms,单位毫秒。
  • ctx.write(s) 应用于Http/TCP协议,用于写入字符串数据,可以使用JSON.stringify()编码JSON对象为字符串之后写入。对于WebSocket协议,可以使用该方法将编码后的字符串传递给客户端。

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

_G

持久化保存数据,该函数实现了一个可保存的全局字典功能。数据结构为KV表,永久保存在托管者本地数据库文件。

持久化保存的k-v键值对中的键值数据。 string、number、bool、object、array、空值

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

参数k为保存的键值对中的键名,不区分大小写。 k false string、空值 参数v为保存的键值对中的键值,可以是任何可以JSON序列化的数据。 v false string、number、bool、object、array、空值

// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main(){                
    // 设置一个全局变量num,值为1
    _G("num", 1)     
    // 更改一个全局变量num,值为字符串ok
    _G("num", "ok")    
    // 删除全局变量num
    _G("num", null)
    // 返回全局变量num的值
    Log(_G("num"))
    // 删除所有全局变量
    _G(null)
    // 返回实盘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);
    // 不支持 auto robotId = _G();
}

每个实盘单独一个数据库,策略重启或者托管者停止运行,_G()函数保存的数据会一直存在。 如果是回测结束后,_G()函数在回测系统中保存的数据会被清除。 使用_G()函数持久化保存数据时,应当根据硬件设备的内存、硬盘空间合理使用,不可滥用。 在实盘运行中当调用_G()函数并且不传任何参数时,_G()函数返回当前实盘的Id。 调用_G()函数时,参数v传入空值表示删除该k-v键值对。 调用_G()函数时,仅参数k传入字符串,_G()函数返回保存的参数k对应的键值。 调用_G()函数时,仅参数k传入空值,表示删除所有记录的k-v键值对。 当k-v键值对已经持久化保存,再次调用_G()函数,传入已经持久化保存的键名作为参数k, 传入新的键值作为参数v,会更新该k-v键值对。

{@fun/Global/DBExec DBExec}

_D

将毫秒时间戳或者Date对象转换为时间字符串。

时间字符串。 string

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

毫秒时间戳或者Date对象。 timestamp false number、object 格式化字符串,默认格式:yyyy-MM-dd hh:mm:ss。 fmt false string

// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main(){
    var time = _D()
    Log(time)
}
def main():
    strTime = _D()
    Log(strTime)
void main() {
    auto strTime = _D();
    Log(strTime);
}

获取、打印当前时间字符串:

// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main() {
    Log(_D(1574993606000))
}
def main():
    # 北京时间的服务器上运行:2019-11-29 10:13:26 ,另一台其它地区的服务器上的托管者运行此代码结果则为:2019-11-29 02:13:26
    Log(_D(1574993606))
void main() {
    Log(_D(1574993606000));
}

时间戳为1574993606000,使用代码转换:

function main() {
    Log(_D(1574993606000, "yyyy--MM--dd hh--mm--ss"))   // 2019--11--29 10--13--26
}
def main():
    # 1574993606为秒级别时间戳
    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
}

使用参数fmt格式化,JavaScriptPythonC++语言的格式化字符串有所不同,具体参看以下例子:

不传任何参数就返回当前时间字符串。 Python策略中使用_D()函数时,需要注意传入的参数为秒级别时间戳(JavaScript、C++策略中为毫秒级别时间戳,1秒等于1000毫秒)。 在实盘时使用_D()函数解析一个时间戳为可读的时间字符串时,需要注意托管者程序所在的操作系统的时区、时间设置。 _D()函数解析一个时间戳为可读时间字符串是根据托管者系统的时间而定的。

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

_N

格式化一个浮点数。

根据精度设置,格式化后的浮点数。 number

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

需要格式化的浮点数。 num true number 格式化的精度设置,参数precision为整数,参数precision默认为4。 precision false number

// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
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);
}

例如_N(3.1415, 2)将删除3.1415小数点两位以后的值,函数返回3.14

// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main() {
    var i = 1300
    Log(i)
    var ii = _N(i, -3)
    // 查看日志得知为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);
}

如果需要将小数点左边的N个位数都变为0,可以这么写:

参数precision可以为正整数、负整数。

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

_C

重试函数,用于接口容错。

回调函数执行时的返回值。 除了逻辑假值空值以外系统支持的所有类型。

_C(pfn) _C(pfn, …args)

参数pfn为函数引用,是一个回调函数。 pfn true function 回调函数的参数,参数arg可能有多个。参数arg的类型与个数根据回调函数的参数而定。 arg false string、number、bool、object、array、function、空值等系统支持的所有类型

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    
    // 测试代码部分
    // 设置合约为rb888即螺纹钢主力连续合约,或者设置股票代码
    exchange.SetContractType("rb888")
    
    var ticker = _C(exchange.GetTicker)
    // 调整_C()函数重试时间间隔为2秒
    _CDelay(2000)
    var depth = _C(exchange.GetDepth)
    Log(ticker)
    Log(depth)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
        
    exchange.SetContractType("rb888")
    
    ticker = _C(exchange.GetTicker)
    _CDelay(2000)
    depth = _C(exchange.GetDepth)
    Log(ticker)
    Log(depth)
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    
    exchange.SetContractType("rb888");
    
    auto ticker = _C(exchange.GetTicker);
    _CDelay(2000);
    auto depth = _C(exchange.GetDepth);
    Log(ticker);
    Log(depth);
}

对于无参数的函数容错:

function main(){
    // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
    while(!exchange.IO("status")) {
        Sleep(1000)
    }
    
    // 测试代码部分
    // 设置合约为rb888即螺纹钢主力连续合约,或者设置股票代码
    exchange.SetContractType("rb888")
    
    var records = _C(exchange.GetRecords, PERIOD_D1)
    Log(records)
}
def main():
    while not exchange.IO("status"):
        Sleep(1000)
    
    exchange.SetContractType("rb888")
    
    records = _C(exchange.GetRecords, PERIOD_D1)
    Log(records)
void main() {
    while(exchange.IO("status") == 0) {
        Sleep(1000);
    }
    
    exchange.SetContractType("rb888");
    
    auto records = _C(exchange.GetRecords, PERIOD_D1);
    Log(records);
}

对于有参数的函数容错:

var test = function(a, b){
    var time = new Date().getTime() / 1000
    if(time % b == 3){
        Log("符合条件!", "#FF0000")
        return true
    }
    Log("重试!", "#FF0000")
    return false
}

function main(){
    // 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试自定义的函数
    var ret = _C(test, 1, 5)
    Log(ret)
}
import time
def test(a, b):
    ts = time.time()
    if ts % b == 3:
        Log("符合条件!", "#FF0000")
        return True
    Log("重试!", "#FF0000")
    return False

def main():
    ret = _C(test, 1, 5)
    Log(ret)
// C++ 不支持这种方式对于自定义函数容错

也可以用于自定义函数的容错处理:

_C()函数会一直调用指定函数直到成功返回(参数pfn引用的函数调用时返回空值或者假值会重试调用pfn)。 例如_C(exchange.GetTicker)。默认重试间隔为3秒,可以调用_CDelay()函数来设置重试间隔。 例如_CDelay(1000),指改变_C()函数的重试间隔为1秒。 可以但不限于对以下函数做容错处理:

  • exchange.GetTicker()
  • exchange.GetDepth()
  • exchange.GetTrades()
  • exchange.GetRecords()
  • exchange.GetAccount()
  • exchange.GetOrders()
  • exchange.GetOrder()
  • exchange.GetPositions()

都可以通过_C()函数来调用进行容错。_C()函数不局限于以上列出的函数容错,参数pfn是函数引用并非函数调用, 注意是_C(exchange.GetTicker)并不是_C(exchange.GetTicker())

_Cross

返回数组arr1与数组arr2的交叉周期数。

数组arr1与数组arr2的交叉周期数。 number

_Cross(arr1, arr2)

元素为number类型的数组。 arr1 true array 元素为number类型的数组。 arr2 true array

// 快线指标
var arr1 = [1,2,3,4,5,6,8,8,9]
// 慢线指标
var arr2 = [2,3,4,5,6,7,7,7,7]

// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
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));
}

可以模拟一组数据测试_Cross(Arr1, Arr2)函数:

_Cross()函数的返回值是正数表示上穿的周期,负数表示下穿的周期,0指当前价格一样。 具体使用说明:内置函数_Cross分析及使用说明

JSONParse

函数JSONParse()用于解析JSON字符串。

JSON对象。 object

JSONParse(s)

JSON字符串。 s true string

// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
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));   // 不支持该函数
    
    auto s2 = "{\"num\":123}";
    Log("json::parse:", json::parse(s2));
    // Log("JSONParse:", JSONParse(s2));   // 不支持该函数
}

可以正确解析包含有较大数值的JSON字符串,会将较大的数值解析为字符串类型。 回测系统中不支持JSONParse()函数。

Log