Global
Version
返回系统当前版本号。
Version()示例
javascript
function main() {
Log("version:", Version())
}
python
def main():
Log("version:", Version())
c++
void main() {
Log("version:", Version());
}返回值
| 类型 | 描述 |
string | 当前系统版本号,例如: |
备注
系统版本号即托管者程序的版本号。
Sleep
休眠函数,用于使程序暂停执行指定的时间。
Sleep(millisecond)示例
javascript
function main() {
Sleep(1000 * 10) // 等待10秒钟
Log("等待了10秒钟")
}
python
def main():
Sleep(1000 * 10)
Log("等待了10秒钟")
c++
void main() {
Sleep(1000 * 10);
Log("等待了10秒钟");
}参数
| 名称 | 类型 | 必填 | 描述 |
millisecond | number | 是 |
|
备注
例如执行Sleep(1000)函数时,程序将休眠1秒。支持小于1毫秒的休眠时间,例如设置Sleep(0.1)。支持的最小参数值为0.000001,即纳秒级休眠,1纳秒等于1e-6毫秒。
在使用Python语言编写策略时,对于轮询间隔、时间等待等操作应使用Sleep(millisecond)函数。不建议使用Python的time库中的time.sleep(second)函数。因为在策略中使用time.sleep(second)函数会在回测时让策略程序实际等待相应时间(而非在回测系统的时间序列上跳过),从而导致策略回测速度极慢。
IsVirtual
判断策略的运行环境是否为回测系统。
IsVirtual()示例
javascript
function main() {
if (IsVirtual()) {
Log("当前为回测系统环境。")
} else {
Log("当前为实盘环境。")
}
}
python
def main():
if IsVirtual():
Log("当前为回测系统环境。")
else:
Log("当前为实盘环境。")
c++
void main() {
if (IsVirtual()) {
Log("当前为回测系统环境。");
} else {
Log("当前为实盘环境。");
}
}返回值
| 类型 | 描述 |
bool | 策略运行在回测系统环境时返回真值,例如: |
备注
判断当前运行环境是否为回测系统,用于兼容回测与实盘环境的差异。
Mail
发送邮件。
Mail(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)示例
javascript
function main(){
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
}
python
def main():
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
c++
void main() {
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body");
}返回值
| 类型 | 描述 |
bool | 邮件发送成功返回 |
参数
| 名称 | 类型 | 必填 | 描述 |
smtpServer | string | 是 | 指定邮件发送方的 |
smtpUsername | string | 是 | 指定邮件发送方的邮箱地址。 |
smtpPassword | string | 是 | 邮件发送方邮箱的 |
mailTo | string | 是 | 指定邮件接收方的邮箱地址。 |
title | string | 是 | 邮件标题。 |
body | string | 是 | 邮件正文。 |
参考
备注
smtpPassword参数设置的是SMTP服务密码,而非邮箱登录密码。
设置smtpServer参数时,如需更改端口可直接在参数smtpServer中添加端口号。例如:QQ邮箱smtp.qq.com:587(该端口已测试可用)。
如果出现错误提示:unencryped connection,需要修改Mail函数的smtpServer参数格式为:ssl://xxx.com:xxx,例如QQ邮箱的SMTP SSL方式:ssl://smtp.qq.com:465或smtp://xxx.com:xxx。
在回测系统中不生效。
Mail_Go
Mail函数的异步版本。
Mail_Go(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)示例
javascript
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)
}
python
# 不支持
c++
// 不支持返回值
| 类型 | 描述 |
object |
|
参数
| 名称 | 类型 | 必填 | 描述 |
smtpServer | string | 是 | 用于指定邮件发送方的 |
smtpUsername | string | 是 | 用于指定邮件发送方的邮箱地址。 |
smtpPassword | string | 是 | 邮件发送方邮箱的 |
mailTo | string | 是 | 用于指定邮件接收方的邮箱地址。 |
title | string | 是 | 邮件标题。 |
body | string | 是 | 邮件正文。 |
参考
备注
在回测系统中不起作用。
SetErrorFilter
过滤错误日志。
SetErrorFilter(filters)示例
-
过滤常见错误。
javascriptfunction main() { SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused") }pythondef main(): SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")c++void main() { SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused"); } -
过滤特定接口的错误信息。
javascriptfunction 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) // 过滤HTTP 502错误、GetOrder接口错误,设置错误过滤后,第二次调用GetOrder不再报错 SetErrorFilter("502:|GetOrder") order = exchange.GetOrder("123") Log(order) }pythondef 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)c++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); }
参数
| 名称 | 类型 | 必填 | 描述 |
filters | string | 是 | 正则表达式字符串。 |
备注
被此正则表达式匹配的错误日志将不会上传到日志系统,可多次调用(无次数限制)设置多个过滤条件。多次设置的正则表达式会累积生效。可通过设置空字符串来重置错误日志过滤的正则表达式:SetErrorFilter("")。
被过滤的日志不再写入托管者目录下对应实盘ID的数据库文件中,防止频繁报错导致数据库文件过度膨胀。
GetPid
获取实盘进程ID。
GetPid()示例
javascript
function main(){
var id = GetPid()
Log(id)
}
python
def main():
id = GetPid()
Log(id)
c++
void main() {
auto id = GetPid();
Log(id);
}返回值
| 类型 | 描述 |
string | 返回实盘进程ID。 |
GetLastError
获取最近一次的错误信息。
GetLastError()示例
javascript
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)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.GetOrder("123")
error = GetLastError()
Log(error)
c++
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);
}返回值
| 类型 | 描述 |
string | 最近一次的错误信息。 |
备注
在回测系统中不起作用。
GetCommand
获取策略交互命令。
GetCommand()示例
-
检测交互命令,并在检测到交互命令时使用
Log函数输出交互命令。javascriptfunction main(){ while(true) { var cmd = GetCommand() if (cmd) { Log(cmd) } Sleep(1000) } }pythondef main(): while True: cmd = GetCommand() if cmd: Log(cmd) Sleep(1000)c++void main() { while(true) { auto cmd = GetCommand(); if(cmd != "") { Log(cmd); } Sleep(1000); } } -
例如,在策略交互控件中添加了一个不带输入框的控件,交互控件命名为:
buy,控件描述信息为:买入,这是一个按钮控件。继续添加一个带输入框的控件,交互控件名为:sell,控件描述信息为:卖出,这是一个由按钮和输入框组合而成的交互控件。策略中设计交互代码来响应不同的交互控件:
javascriptfunction 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) } }pythondef 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)c++#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); } }
返回值
| 类型 | 描述 |
string | 返回的命令格式为 |
备注
在回测系统中不起作用。
GetMeta
获取生成策略注册码时写入的Meta值。
GetMeta()示例
应用场景示例:使用Meta限制策略可操作的资产数量。
javascript
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)
}
}
python
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)
c++
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);
}
}返回值
| 类型 | 描述 |
string |
|
备注
应用场景:对不同的策略租用者进行资金限制。生成注册码时设置的Meta值长度不能超过190个字符,GetMeta()函数仅支持实盘。
如果生成策略注册码时未设置元数据(Meta),GetMeta()函数将返回空值。
在回测系统中不生效。
Dial
用于原始的Socket访问,支持tcp、udp、tls、unix协议。
支持4种流行的通信协议:mqtt、nats、amqp、kafka。
支持连接数据库,支持的数据库包括:sqlite3、mysql、postgres、clickhouse。
Dial(address)
Dial(address, timeout)
Dial(address, options)示例
-
Dial函数调用示例:
javascriptfunction 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() } }pythondef 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()c++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函数连接数据库时返回的连接对象具有2个独有的方法:
-
exec(sqlString): 用于执行SQL语句,使用方式类似于DBExec()函数。-
fd():fd()函数返回一个句柄(例如:句柄变量为handle),用于其他线程重连(即使Dial创建的对象已经执行close()函数关闭连接),将句柄传入Dial()函数,例如:Dial(handle)重用连接。以下是Dial函数连接
sqlite3数据库的示例。javascriptvar 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() }python// 不支持c++// 不支持
返回值
| 类型 | 描述 | ||||||||||||||||||||
object | 如果超时,
WebSocket协议推送的数据,如果策略中
|
参数
| 名称 | 类型 | 必填 | 描述 |
address | string | 是 | 请求地址。 |
timeout | number | 否 | 超时时间(秒)。 |
options | object | 否 | 配置选项。 |
备注
address参数的详细说明:在标准地址wss://xxx.xxx.xxx:10441/websocket?compress后,使用|符号作为分隔符。如果参数字符串中包含|字符,则使用||作为分隔符。分隔符后的部分为功能参数设置,各参数之间使用&字符连接。
例如,同时设置socks5代理和压缩参数时可以写作:
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协议启用compression设置:enableCompression=true | 使用enableCompression=false关闭该设置,默认不启用。 |
| WebSocket协议设置底层自动重连相关的参数:reconnect=参数值 | reconnect用于设置是否重连,reconnect=true为启用重连。不设置该参数时默认不重连。 |
| WebSocket协议设置底层自动重连相关的参数:interval=参数值 | interval为重试时间间隔,单位为毫秒,interval=10000表示重试间隔为10秒,不设置时默认为1秒,即interval=1000。 |
| WebSocket协议设置底层自动重连相关的参数:payload=参数值 | payload为WebSocket重连时需要发送的订阅消息,例如:payload=okok。 |
| socks5代理的相关参数:proxy=参数值 | proxy为socks5代理设置,参数值格式:socks5://name:pwd@192.168.0.1:1080,其中name为socks5服务端用户名,pwd为socks5服务端登录密码,1080为socks5服务端口。 |
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函数中使用mqtt、nats、amqp、kafka通信协议。以下为JavaScript语言策略代码示例,展示mqtt、nats、amqp、kafka四种协议的使用方法:
javascript
// 需要先配置、部署完成各个协议的代理服务器
// 为了便于演示,主题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], "连接")
}
}
Dial函数用于访问WebSocket接口时,支持设置WSS请求头:
javascript
let options = {"headers": {"Authorization": "Bearer token123"}}
let conn = Dial("wss://api.example.com/ws", options)
python
options = {"headers": {"Authorization": "Bearer token123"}}
conn = Dial("wss://api.example.com/ws", options)
c++
// 暂不支持
HttpQuery
发送HTTP请求。
HttpQuery(url)
HttpQuery(url, options)示例
HttpQuery函数使用代理设置。
javascript
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/")
}
python
# HttpQuery不支持Python,可以使用Python的urllib2库
c++
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/");
}返回值
| 类型 | 描述 |
string / object | 返回请求的响应数据。如果返回值为 参数 |
参数
| 名称 | 类型 | 必填 | 描述 |
url | string | 是 | HTTP请求的URL地址。 |
options | object | 否 | HTTP请求相关配置参数,可以使用以下结构:
此结构中的所有字段均为可选参数,例如可以不设置 |
参考
备注
HttpQuery()函数仅支持JavaScript、C++语言,Python语言可以使用urllib库直接发送HTTP请求。
回测系统中可以使用HttpQuery()发送请求(仅支持GET请求)获取数据。
回测时限制访问不同URL的次数为20次,并且HttpQuery()访问会缓存数据,
相同的URL第二次访问时HttpQuery()函数将返回缓存数据,不再发起实际的网络请求。
HttpQuery_Go
发送HTTP请求,是HttpQuery函数的异步版本。
HttpQuery_Go(url)
HttpQuery_Go(url, options)示例
HttpQuery_Go()函数使用示例:
javascript
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)
}
python
# 不支持
c++
// 不支持返回值
| 类型 | 描述 |
object |
|
参数
| 名称 | 类型 | 必填 | 描述 |
url | string | 是 | HTTP请求的URL地址。 |
options | object | 否 | HTTP请求相关配置参数,可以使用以下结构:
此结构中的所有字段均为可选字段,例如可以不设置 |
参考
备注
HttpQuery_Go()函数仅支持JavaScript语言,Python语言可以使用urllib库直接发送HTTP请求。
HttpQuery_Go()主要用于访问交易所无需签名的接口,例如行情信息等公共接口。回测系统不支持HttpQuery_Go函数。
Encode
该函数根据传入的参数对数据进行编码。
Encode(algo, inputFormat, outputFormat, data)
Encode(algo, inputFormat, outputFormat, data, keyFormat, key)示例
-
Encode函数调用示例。
javascriptfunction 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 }pythondef 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")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84bac++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 } -
参数
algo还支持以下值:"text.encoder.utf8"、"text.decoder.utf8"、"text.encoder.gbk"、"text.decoder.gbk",用于对字符串进行编码和解码。javascriptfunction 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) }pythondef 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, "", "")c++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); }
返回值
| 类型 | 描述 |
string |
|
参数
| 名称 | 类型 | 必填 | 描述 |
algo | string | 是 | 参数 支持的算法包括:"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"。 参数 参数 |
inputFormat | string | 是 | 指定 |
outputFormat | string | 是 | 指定输出的数据格式。 |
data | string | 是 | 参数 |
keyFormat | string | 否 | 指定 |
key | string | 否 | 参数 当参数 当参数 |
备注
Encode()函数仅支持实盘。如果不传入key、keyFormat参数,则不使用密钥加密。
UnixNano
获取当前时刻的纳秒级时间戳。
UnixNano()示例
如需获取毫秒级时间戳,可使用以下代码:
javascript
function main() {
var time = UnixNano() / 1000000
Log(_N(time, 0))
}
python
def main():
time = UnixNano()
Log(time)
c++
void main() {
auto time = UnixNano();
Log(time);
}返回值
| 类型 | 描述 |
number |
|
参考
Unix
获取当前时刻的秒级时间戳。
Unix()示例
javascript
function main() {
var t = Unix()
Log(t)
}
python
def main():
t = Unix()
Log(t)
c++
void main() {
auto t = Unix();
Log(t);
}返回值
| 类型 | 描述 |
number | 返回秒级时间戳。 |
参考
GetOS
获取托管者所在设备的操作系统信息。
GetOS()示例
javascript
function main() {
Log("GetOS:", GetOS())
}
python
def main():
Log("GetOS:", GetOS())
c++
void main() {
Log("GetOS:", GetOS());
}返回值
| 类型 | 描述 |
string | 操作系统信息字符串。 |
备注
例如,在Mac OS操作系统下运行的托管者,调用GetOS()函数可能返回:darwin/amd64。这是因为苹果电脑有多种硬件架构。darwin是Mac OS系统的内核名称。
MD5
计算参数data的MD5哈希值。
MD5(data)示例
javascript
function main() {
Log("MD5", MD5("hello world"))
}
python
def main():
Log("MD5", MD5("hello world"))
c++
void main() {
Log("MD5", MD5("hello world"));
}返回值
| 类型 | 描述 |
string | MD5哈希值。 |
参数
| 名称 | 类型 | 必填 | 描述 |
data | string | 是 | 需要进行MD5哈希计算的数据。 |
参考
备注
调用MD5("hello world")函数,返回值为:5eb63bbbe01eeed093cb22bb8f5acdc3。
DBExec
数据库接口函数。
DBExec(sql)示例
-
支持内存数据库。对于
DBExec函数的参数,如果sql语句以:开头,则在内存数据库中执行操作,无需写入文件,执行速度更快。适用于无需持久化保存的数据库操作场景,例如:
javascriptfunction 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;")) }pythondef 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;"))c++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;")); } -
创建数据表。
javascriptfunction 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) }pythondef 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)c++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); } -
数据表记录的增删查改操作。
javascriptfunction 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)) }pythondef 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))c++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)); }
返回值
| 类型 | 描述 |
object | 包含SQL语句执行结果的对象,例如:
|
参数
| 名称 | 类型 | 必填 | 描述 |
sql | string | 是 | SQL语句字符串。 |
参考
备注
函数DBExec()通过传入参数,可以操作实盘数据库(SQLite数据库),实现对实盘数据库中数据的增、删、查、改等操作,支持SQLite语法。实盘数据库中的系统保留表包括:kvdb、cfg、log、profit、chart,请勿对这些表进行操作。目前不支持事务操作,不建议执行此类操作,以免引起系统冲突。DBExec()函数仅支持实盘环境。
UUID
创建一个UUID。
UUID()示例
javascript
function main() {
var uuid1 = UUID()
var uuid2 = UUID()
Log(uuid1, uuid2)
}
python
def main():
uuid1 = UUID()
uuid2 = UUID()
Log(uuid1, uuid2)
c++
void main() {
auto uuid1 = UUID();
auto uuid2 = UUID();
Log(uuid1, uuid2);
}返回值
| 类型 | 描述 |
string | 返回32位的UUID字符串。 |
备注
UUID()函数仅支持实盘环境。
EventLoop
监听事件,当有任意事件发生时返回。
EventLoop()
EventLoop(timeout)示例
javascript
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 !")
}
}
}
python
# 不支持EventLoop函数
c++
// 不支持EventLoop函数返回值
| 类型 | 描述 |
object | 返回的对象如果不为空值,则返回内容中包含的
|
参数
| 名称 | 类型 | 必填 | 描述 |
timeout | number | 否 | 参数 参数 |
参考
备注
代码中第一次调用EventLoop()函数时才会初始化事件监听机制,如果在事件回调之后才首次调用EventLoop(),将会错过之前的事件。底层系统封装的队列结构会缓存最多500个事件回调,如果程序执行过程中没有及时调用EventLoop()函数取出事件,超出500个缓存的后续事件回调将会丢失。EventLoop()函数的调用不会影响系统底层WebSocket的缓存队列。
对于这些缓存数据仍需要使用各自的方法取出。对于在EventLoop()函数返回之前已经取出的数据,不会在EventLoop()函数中产生返回事件。EventLoop()函数的主要用途是通知策略层系统底层接收到了新的网络数据,以事件驱动整个策略。当EventLoop()函数返回事件时,只需遍历所有数据源即可。
EventLoop()函数仅支持实盘交易。
在主函数main()中调用时,监听主线程的事件。在JavaScript语言编写的策略中,通过threading.Thread()函数创建的线程,在线程的执行函数中也可以调用,用于监听当前线程的事件。
__Serve
__Serve函数用于创建Http服务、TCP服务、Websocket服务(基于Http协议)。
__Serve(serveURI, handler)
__Serve(serveURI, handler, ...args)示例
javascript
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)
}
}
python
# 不支持
c++
// 不支持返回值
| 类型 | 描述 |
string | 返回一个字符串,记录创建的服务的IP地址、端口。例如: |
参数
| 名称 | 类型 | 必填 | 描述 |
serveURI | string | 是 |
|
handler | function | 是 |
|
arg | string / number / bool / object / array / function / any (系统支持的所有类型) | 否 | 作为参数
调用 |
参考
备注
-
该函数仅支持JavaScript语言策略。
-
服务线程与全局作用域隔离,因此不支持闭包或引用外部变量、自定义函数等;但是可以调用平台所有的API函数。
-
Websocket服务基于Http协议实现,可以在path中设置一个路由分支,设计Websocket消息订阅/推送的实现代码,可以参考本节范例代码。
-
参数handler传入的回调处理函数接收一个ctx参数。ctx参数为一个上下文对象,用于获取数据和写入数据,有以下方法:
- ctx.proto()
适用于Http/TCP协议,调用时返回协议名称。例如:HTTP/1.1、tcp。 - 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.headers()
- ctx.header(key)
适用于Http协议,调用时返回指定的请求头中的某个key对应的值。例如获取当前请求的headers中的User-Agent:ctx.header("User-Agent")。 - ctx.method()
适用于Http协议,调用时返回请求方法,例如GET、POST等。- ctx.body()
适用于Http协议的POST请求,调用时返回请求的正文。
- ctx.body()
- ctx.setHeader(key, value)
适用于Http协议,设置应答报文的请求头信息。- ctx.setStatus(code)
适用于Http协议,设置Http报文状态码,通常在路由分支最后设置Http状态码,默认为200。
- ctx.setStatus(code)
- 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协议,可以使用该方法将编码后的字符串传递给客户端。
- ctx.upgrade("websocket")
_G
持久化保存数据,该函数实现了一个可保存的全局字典功能。数据结构为键值对表,永久保存在托管者本地数据库文件中。
_G()
_G(k)
_G(k, v)示例
javascript
// 不使用接口获取数据的测试,就无需使用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()
}
python
def main():
_G("num", 1)
_G("num", "ok")
_G("num", None)
Log(_G("num"))
_G(None)
robotId = _G()
c++
void main() {
_G("num", 1);
_G("num", "ok");
_G("num", NULL);
Log(_G("num"));
_G(NULL);
// 不支持 auto robotId = _G();
}返回值
| 类型 | 描述 |
string / number / bool / object / array / 空值 | 持久化保存的 |
参数
| 名称 | 类型 | 必填 | 描述 |
k | string / 空值 | 否 | 参数 |
v | string / number / bool / object / array / 空值 | 否 | 参数 |
参考
备注
每个实盘拥有独立的数据库,策略重启或托管者停止运行后,_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键值对。
以实盘Id为123456为例,使用_G()函数持久化保存的键值对数据存储在实盘(即策略实例程序)所属托管者目录下的/logs/storage/123456/123456.db3数据库文件中,数据记录在kvdb表中。
_D
将毫秒时间戳或Date对象转换为时间字符串。
_D()
_D(timestamp)
_D(timestamp, fmt)示例
-
获取并打印当前时间字符串:
javascript// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试 function main(){ var time = _D() Log(time) }pythondef main(): strTime = _D() Log(strTime)c++void main() { auto strTime = _D(); Log(strTime); } -
将时间戳1574993606000转换为时间字符串:
javascript// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试 function main() { Log(_D(1574993606000)) }pythondef main(): # 北京时间的服务器上运行:2019-11-29 10:13:26 ,另一台其它地区的服务器上的托管者运行此代码结果则为:2019-11-29 02:13:26 Log(_D(1574993606))c++void main() { Log(_D(1574993606000)); } -
使用参数
fmt进行格式化,JavaScript、Python、C++语言的格式化字符串有所不同,请参考以下示例:javascriptfunction main() { Log(_D(1574993606000, "yyyy--MM--dd hh--mm--ss")) // 2019--11--29 10--13--26 }pythondef main(): # 1574993606为秒级别时间戳 Log(_D(1574993606, "%Y--%m--%d %H--%M--%S")) # 2019--11--29 10--13--26c++void main() { Log(_D(1574993606000, "%Y--%m--%d %H--%M--%S")); // 2019--11--29 10--13--26 }
返回值
| 类型 | 描述 |
string | 时间字符串。 |
参数
| 名称 | 类型 | 必填 | 描述 |
timestamp | number / object | 否 | 毫秒时间戳或 |
fmt | string | 否 | 格式化字符串,默认格式为: |
参考
备注
不传递任何参数时返回当前时间字符串。Python策略中使用_D()函数时,需要注意传入的参数为秒级时间戳(JavaScript、C++策略中为毫秒级时间戳,1秒等于1000毫秒)。
在实盘中使用_D()函数解析时间戳为可读时间字符串时,需要注意托管者程序所在操作系统的时区和时间设置。_D()函数解析时间戳为可读时间字符串是基于托管者系统的时间设置。
_N
格式化浮点数。
_N()
_N(num)
_N(num, precision)示例
-
例如
_N(3.1415, 2)将删除3.1415小数点后两位以外的数值,函数返回3.14。javascript// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试 function main() { var i = 3.1415 Log(i) var ii = _N(i, 2) Log(ii) }pythondef main(): i = 3.1415 Log(i) ii = _N(i, 2) Log(ii)c++void main() { auto i = 3.1415; Log(i); auto ii = _N(i, 2); Log(ii); } -
如需将小数点左边的N位数字置为0,可以使用负数精度:
javascript// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试 function main() { var i = 1300 Log(i) var ii = _N(i, -3) // 查看日志得知为1000 Log(ii) }pythondef main(): i = 1300 Log(i) ii = _N(i, -3) Log(ii)c++void main() { auto i = 1300; Log(i); auto ii = _N(i, -3); Log(ii); }
返回值
| 类型 | 描述 |
number | 根据精度设置格式化后的浮点数。 |
参数
| 名称 | 类型 | 必填 | 描述 |
num | number | 是 | 需要格式化的浮点数。 |
precision | number | 否 | 格式化的精度设置,参数 |
参考
备注
参数precision可以为正整数或负整数。
_C
重试函数,用于接口调用的容错处理。
_C(pfn)
_C(pfn, ...args)示例
-
对于无参数函数的容错处理:
javascriptfunction 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) }pythondef 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)c++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); } -
对于有参数函数的容错处理:
javascriptfunction 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) }pythondef main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") records = _C(exchange.GetRecords, PERIOD_D1) Log(records)c++void main() { while(exchange.IO("status") == 0) { Sleep(1000); } exchange.SetContractType("rb888"); auto records = _C(exchange.GetRecords, PERIOD_D1); Log(records); } -
也可用于自定义函数的容错处理:
javascriptvar 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) }pythonimport 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++ 不支持此方式的自定义函数容错
返回值
| 类型 | 描述 |
除假值和空值以外的所有平台支持类型(any)。 | 回调函数执行后的返回值。 |
参数
| 名称 | 类型 | 必填 | 描述 |
pfn | function | 是 | 参数 |
arg | string / number / bool / object / array / function / any (系统支持的所有类型) | 否 | 回调函数的参数,参数 |
备注
_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的交叉周期数。
_Cross(arr1, arr2)示例
可以模拟一组数据来测试_Cross(Arr1, Arr2)函数:
javascript
// 快线指标
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))
}
python
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))
c++
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));
}返回值
| 类型 | 描述 |
number | 数组 |
参数
| 名称 | 类型 | 必填 | 描述 |
arr1 | array | 是 | 元素为 |
arr2 | array | 是 | 元素为 |
备注
_Cross()函数的返回值为正数时表示上穿的周期数,为负数时表示下穿的周期数,为0时表示当前价格相同。详细使用说明:内置函数_Cross分析及使用说明。
JSON.parse
JSON.parse函数是ECMAScript标准内置对象JSON的方法,用于解码(解析)JSON字符串。优宽量化交易平台对其扩展了一个safeStr参数。
JSON.parse(s)
JSON.parse(s, safeStr)示例
解码(解析)包含大数值的JSON字符串。
javascript
function main() {
let s1 = '{"num": 8754613216564987646512354656874651651358}'
Log("JSON.parse:", JSON.parse(s1)) // JSON.parse: {"num":8.754613216564987e+39}
Log("JSON.parse:", JSON.parse(s1, true)) // JSON.parse: {"num":"8754613216564987646512354656874651651358"}
let s2 = '{"num": 123}'
Log("JSON.parse:", JSON.parse(s2)) // JSON.parse: {"num":123}
Log("JSON.parse:", JSON.parse(s2, true)) // JSON.parse: {"num":123}
}
python
# 可以使用Python的第三方库处理大数值数据。
c++
// 可以使用其它方案处理。返回值
| 类型 | 描述 |
object | 返回 |
参数
| 名称 | 类型 | 必填 | 描述 |
s | string | 是 | 需要解码(解析)的 |
safeStr | bool | 否 | 当此参数设置为 |
备注
JSON.parse()函数可以正确解析包含大数值的JSON字符串,设置safeStr参数为true时,会将大数值解析为字符串类型。
safeStr参数也支持作为reviver参数使用,即:一个转换结果的函数,将为对象的每个成员调用此函数。详细信息请查阅相关资料。
仅支持JavaScript语言。
回测系统不支持JSON.parse()函数的safeStr参数功能。
JSON.stringify
JSON.stringify函数是ECMAScript标准内置对象JSON的方法,用于将JavaScript值编码为JSON字符串。
JSON.stringify(obj)示例
编码一个对象,输出编码后的JSON字符串。
javascript
function main() {
let s1 = {"num": "8754613216564987646512354656874651651358"}
Log("JSON.stringify:", JSON.stringify(s1))
// JSON.stringify: {"num":"8754613216564987646512354656874651651358"}
// JSON.stringify(s1) 返回的变量为一个字符串类型
}
python
// 略
c++
// 略返回值
| 类型 | 描述 |
string | 返回编码后的 |
参数
| 名称 | 类型 | 必填 | 描述 |
obj | string / number / bool / object / array / function / any (平台支持的任意类型) | 是 | 需要编码为JSON字符串的值。 |
备注
仅支持JavaScript语言。
SetChannelData
在频道上发布最新状态数据。该函数用于实盘间通信,可以将当前实盘的状态数据广播到频道上,供其他实盘订阅获取。
SetChannelData(data)示例
-
频道广播端示例 - 发布市场行情数据
javascriptfunction main() { var updateId = 0 var robotId = _G() // 获取当前实盘ID while(true) { if (!exchange.IO("status")) { Sleep(1000) continue } // 获取实际市场价格 var ticker = exchange.GetTicker("rb888") if (!ticker) { Sleep(5000) continue } // 准备当前频道状态数据 var channelState = { robotId: robotId, updateId: ++updateId, timestamp: Date.now(), symbol: "rb888", lastPrice: ticker.Last, volume: ticker.Volume, high: ticker.High, low: ticker.Low } // 在频道上发布最新状态(覆盖旧状态) SetChannelData(channelState) // 显示当前频道状态 LogStatus("频道广播端 [实盘ID: " + robotId + "]\n" + "更新ID: #" + channelState.updateId + "\n" + "时间: " + _D(channelState.timestamp) + "\n" + "交易对: " + channelState.symbol + "\n" + "最新价: $" + channelState.lastPrice.toFixed(2) + "\n" + "成交量: " + channelState.volume.toFixed(4) + "\n" + "最高价: $" + channelState.high.toFixed(2) + "\n" + "最低价: $" + channelState.low.toFixed(2)) Sleep(60000) // 每分钟更新一次频道状态 } }pythondef main(): updateId = 0 robotId = _G() # 获取当前实盘ID while True: if not exchange.IO("status"): Sleep(1000) continue # 获取实际市场价格 ticker = exchange.GetTicker("rb888") if not ticker: Sleep(5000) continue # 准备当前频道状态数据 channelState = { "robotId": robotId, "updateId": updateId + 1, "timestamp": time.time() * 1000, "symbol": "rb888", "lastPrice": ticker["Last"], "volume": ticker["Volume"], "high": ticker["High"], "low": ticker["Low"] } updateId += 1 # 在频道上发布最新状态(覆盖旧状态) SetChannelData(channelState) # 显示当前频道状态 LogStatus("频道广播端 [实盘ID: {}]\n".format(robotId) + "更新ID: #{}\n".format(channelState["updateId"]) + "时间: {}\n".format(_D(channelState["timestamp"])) + "交易对: {}\n".format(channelState["symbol"]) + "最新价: ${:.2f}\n".format(channelState["lastPrice"]) + "成交量: {:.4f}\n".format(channelState["volume"]) + "最高价: ${:.2f}\n".format(channelState["high"]) + "最低价: ${:.2f}".format(channelState["low"])) Sleep(60000) # 每分钟更新一次频道状态c++ -
跨平台发送示例 - 模拟外部平台(如TradingView)向优宽量化实盘发送数据
javascript// 此示例演示如何使用HttpQuery发送HTTP POST请求,模拟外部平台向优宽量化实盘发送数据 // 实际场景中,外部平台(如TradingView的Webhook告警URL、第三方交易系统等)会直接调用优宽量化API端点 function main() { let uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" let robotId = 123456 // 目标实盘ID(需要接收数据的实盘) let baseUrl = "https://www.youquant.com" while (true) { // 准备要发送的数据(可以是JSON、文本或其他格式) let sendData = { "action": "buy", "symbol": "rb888", "price": 3500, "timestamp": Date.now() } // 构造HTTP POST请求 let options = { method: "POST", body: JSON.stringify(sendData) // body可以是JSON字符串、普通文本等 } let url = `${baseUrl}/api/v1?method=pub&robot=${robotId}&channel=${uuid}` // 发送数据 let ret = HttpQuery(url, options) Log("Simulated external platform sending data, result:", ret) Sleep(10000) // 每10秒发送一次 } }python# 此示例演示如何使用HttpQuery发送HTTP POST请求,模拟外部平台向优宽量化实盘发送数据 # 实际场景中,外部平台(如TradingView的Webhook告警URL、第三方交易系统等)会直接调用优宽量化API端点 import json def main(): uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" robotId = 123456 # 目标实盘ID(需要接收数据的实盘) baseUrl = "https://www.youquant.com" while True: # 准备要发送的数据(可以是JSON、文本或其他格式) sendData = { "action": "buy", "symbol": "rb888", "price": 3500, "timestamp": time.time() * 1000 } # 构造HTTP POST请求 options = { "method": "POST", "body": json.dumps(sendData) # body可以是JSON字符串、普通文本等 } url = "{}/api/v1?method=pub&robot={}&channel={}".format(baseUrl, robotId, uuid) # 发送数据 ret = HttpQuery(url, options) Log("Simulated external platform sending data, result:", ret) Sleep(10000) # 每10秒发送一次c++
返回值
| 类型 | 描述 |
空值 | 函数无返回值。 |
参数
| 名称 | 类型 | 必填 | 描述 |
data | object / array / string / number / bool / 空值 | 是 | 需要发布到频道的数据,可以是任何可以 |
参考
备注
SetChannelData()函数是非阻塞调用,调用后立即返回,不会等待数据传输完成。
每个实盘都有一个独立的频道,频道ID即为实盘ID(可通过_G()函数获取)。
频道上只保存最新的状态数据,每次调用SetChannelData()会覆盖之前发布的数据,而非追加历史消息。
频道数据可以跨实盘、跨托管者、跨服务器进行广播,多个实盘可以订阅同一个频道。
订阅端使用GetChannelData()函数订阅频道数据。
频道通信适用于实盘环境,回测系统中该功能可能受限。
传入的数据参数data在JSON序列化后的字节长度不得超过1024字节,超出限制可能导致数据发布失败。建议仅传输必要的状态信息,避免传输过大的数据对象。
发布的数据应根据硬件设备的内存和网络带宽合理使用,避免发布过大的数据对象。
SetChannelData()函数发布的数据除了可以被优宽量化平台内的其他实盘订阅,还支持跨平台数据发送功能。外部平台(如TradingView的Webhook告警、第三方交易系统、监控软件等)可以通过HTTP POST请求向指定的优宽量化实盘发送数据。
**跨平台发送数据的方式:**外部系统通过HTTP POST请求发送数据到优宽量化平台API端点:https://www.youquant.com/api/v1?method=pub&robot={robotId}&channel={uuid},其中robotId为目标实盘ID,uuid为32位字符的频道标识符。发送的数据在请求body中传递,可以是JSON格式、文本或其他格式。注意:必须先有实盘订阅该UUID频道,外部系统才能成功发送数据;广播的数据会发送到robotId实盘所在托管者下的所有实盘,同一托管者下订阅了该UUID频道的实盘都可以接收数据。
GetChannelData
订阅指定实盘的频道数据。该函数用于实盘间通信,可以获取其他实盘通过SetChannelData()函数发布的最新状态数据。
GetChannelData(channelId)示例
-
频道订阅端示例 - 订阅两个实盘的频道数据
javascriptfunction main() { // 需要订阅的两个频道ID(根据实际情况修改) var channelId1 = "632799" // 频道1的实盘ID var channelId2 = "632800" // 频道2的实盘ID while(true) { // 订阅频道1的当前状态 var state1 = GetChannelData(channelId1) // 订阅频道2的当前状态 var state2 = GetChannelData(channelId2) // 构建状态显示 var statusMsg = "频道订阅端 - 当前订阅状态\n\n" // 显示频道1状态 statusMsg += "═══ 频道1 [" + channelId1 + "] ═══\n" if (state1 !== null) { statusMsg += "更新ID: #" + state1.updateId + "\n" statusMsg += "时间: " + _D(state1.timestamp) + "\n" statusMsg += "交易对: " + state1.symbol + "\n" statusMsg += "最新价: $" + state1.lastPrice.toFixed(2) + "\n" statusMsg += "成交量: " + state1.volume.toFixed(4) + "\n" } else { statusMsg += "状态: 等待中...(首次调用返回 null)\n" } statusMsg += "\n" // 显示频道2状态 statusMsg += "═══ 频道2 [" + channelId2 + "] ═══\n" if (state2 !== null) { statusMsg += "更新ID: #" + state2.updateId + "\n" statusMsg += "时间: " + _D(state2.timestamp) + "\n" statusMsg += "交易对: " + state2.symbol + "\n" statusMsg += "最新价: $" + state2.lastPrice.toFixed(2) + "\n" statusMsg += "成交量: " + state2.volume.toFixed(4) + "\n" } else { statusMsg += "状态: 等待中...(首次调用返回 null)\n" } LogStatus(statusMsg) Sleep(5000) // 每5秒订阅一次频道 } }pythondef main(): # 需要订阅的两个频道ID(根据实际情况修改) channelId1 = "632799" # 频道1的实盘ID channelId2 = "632800" # 频道2的实盘ID while True: # 订阅频道1的当前状态 state1 = GetChannelData(channelId1) # 订阅频道2的当前状态 state2 = GetChannelData(channelId2) # 构建状态显示 statusMsg = "频道订阅端 - 当前订阅状态\n\n" # 显示频道1状态 statusMsg += "═══ 频道1 [{}] ═══\n".format(channelId1) if state1 is not None: statusMsg += "更新ID: #{}\n".format(state1["updateId"]) statusMsg += "时间: {}\n".format(_D(state1["timestamp"])) statusMsg += "交易对: {}\n".format(state1["symbol"]) statusMsg += "最新价: ${:.2f}\n".format(state1["lastPrice"]) statusMsg += "成交量: {:.4f}\n".format(state1["volume"]) else: statusMsg += "状态: 等待中...(首次调用返回 None)\n" statusMsg += "\n" # 显示频道2状态 statusMsg += "═══ 频道2 [{}] ═══\n".format(channelId2) if state2 is not None: statusMsg += "更新ID: #{}\n".format(state2["updateId"]) statusMsg += "时间: {}\n".format(_D(state2["timestamp"])) statusMsg += "交易对: {}\n".format(state2["symbol"]) statusMsg += "最新价: ${:.2f}\n".format(state2["lastPrice"]) statusMsg += "成交量: {:.4f}\n".format(state2["volume"]) else: statusMsg += "状态: 等待中...(首次调用返回 None)\n" LogStatus(statusMsg) Sleep(5000) # 每5秒订阅一次频道c++ -
跨平台订阅示例 - 使用 UUID 订阅外部系统发送的数据
javascriptfunction main() { // 使用32位UUID作为频道标识符 let uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" while (true) { // 订阅UUID频道的数据 let data = GetChannelData(uuid) if (data !== null) { Log("Received cross-platform data:", data) } else { Log("Waiting for data... (first call returns null)") } Sleep(10000) // 每10秒检查一次 } }pythondef main(): # 使用32位UUID作为频道标识符 uuid = "6BC42A119B5DBFA2188A8279DA3B5C30" while True: # 订阅UUID频道的数据 data = GetChannelData(uuid) if data is not None: Log("Received cross-platform data:", data) else: Log("Waiting for data... (first call returns None)") Sleep(10000) # 每10秒检查一次c++
返回值
| 类型 | 描述 |
object / array / string / number / bool / 空值 | 返回订阅频道的最新状态数据。首次调用返回 |
参数
| 名称 | 类型 | 必填 | 描述 |
channelId | string / number | 是 | 频道标识符,支持两种类型:
|
参考
备注
GetChannelData()函数是非阻塞调用,调用后立即返回,不会等待数据接收完成。
首次调用GetChannelData()函数时会返回null,需要重试等待频道数据同步完成。
每次调用获取的都是频道上的最新状态数据,而非历史消息队列。
一个实盘可以同时订阅多个不同实盘的频道,只需多次调用GetChannelData()并传入不同的实盘ID。
当前实盘也可以订阅自己的频道,即robotId参数可以是当前实盘ID。
频道数据可以跨实盘、跨托管者、跨服务器进行传输。
广播端使用SetChannelData()函数发布频道数据。
频道通信适用于实盘环境,回测系统中该功能可能受限。
GetChannelData()函数支持跨平台订阅功能。使用32位UUID作为频道标识符时,可以接收来自优宽量化平台外部系统通过HTTP API发送的数据。外部系统需要指定实盘ID和UUID才能发送数据,同一托管者下的所有实盘都可以订阅该UUID频道的数据,不同托管者的实盘无法订阅。