Global
Version
返回系统当前的版本号。
Version()示例
javascript
function main() {
Log("version:", Version())
}
python
def main():
Log("version:", Version())
rust
fn 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秒钟")
rust
fn main() {
Sleep(1000 * 10); // 等待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("当前为实盘环境。")
rust
fn 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")
rust
fn 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")rustfn 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)rustfn main() { // 由于这是测试代码,未采用商品期货策略的通用架构,此处仅通过 exchange.IO("status") 函数判断是否成功连接期货公司前置机,连接成功后立即执行测试代码。股票、证券交易无需使用 exchange.IO("status") 判断连接状态 while exchange.IO("status").unwrap_or_default() != "true" { Sleep(1000); } // 设置合约代码 let _ = exchange.SetContractType("rb888"); // 查询一个不存在的订单(id 为 123),故意触发接口报错 let orderId = OrderId { S: "123".to_string(), ..Default::default() }; let order = exchange.GetOrder(&orderId); Log!(order); // 过滤 http 502 错误及 GetOrder 接口错误;设置错误过滤后,再次调用 GetOrder 将不再报错 SetErrorFilter("502:|GetOrder"); let order = exchange.GetOrder(&orderId); 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)
rust
fn main() {
let 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)
rust
fn main() {
// 由于这是测试代码,未采用商品期货策略的通用架构,此处仅通过 exchange.IO("status") 函数进行判断,在成功连接期货公司前置机后立即执行测试代码。股票、证券交易无需使用 exchange.IO("status") 判断连接状态
while exchange.IO("status").unwrap_or_default() != "true" {
Sleep(1000);
}
exchange.SetContractType("rb888").unwrap();
// 由于不存在编号为 123 的订单,因此会触发错误
let id = OrderId { S: "123".to_string(), ..Default::default() };
let _ = exchange.GetOrder(&id);
let 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)rustfn main() { loop { if let Some(cmd) = GetCommand(0) { 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)rustfn main() { loop { LogStatus!(_D(None)); if let Some(cmd) = GetCommand(0) { Log!("cmd:", cmd); let arr: Vec<&str> = cmd.split(':').collect(); if arr[0] == "buy" { Log!("买入,该控件不带数量"); } else if 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)
rust
fn main() {
// 策略允许的计价币最大资产数量
let maxBaseCurrency;
// 获取创建注册码时的元数据
let meta = GetMeta();
let level = meta.as_str().unwrap_or("");
// 检测 Meta 对应的条件
if level == "level1" {
// -1 表示不限制
maxBaseCurrency = -1.0;
} else if level == "level2" {
maxBaseCurrency = 10.0;
} else if level == "level3" {
maxBaseCurrency = 1.0;
} else {
maxBaseCurrency = 0.5;
}
// 鉴于此为测试代码,未采用商品期货策略的通用架构,这里仅通过 exchange.IO("status") 函数判断连接期货公司前置机成功后,立即执行测试代码。股票证券无需使用 exchange.IO("status") 判断连接状态
while exchange.IO("status").unwrap_or_default() != "true" {
Sleep(1000);
}
exchange.SetContractType("rb888").unwrap();
loop {
Sleep(1000);
let ticker = exchange.GetTicker(None).unwrap();
// 检测资产数值
let acc = exchange.GetAccount().unwrap();
if maxBaseCurrency != -1.0 && maxBaseCurrency < acc.Balance + acc.FrozenBalance {
// 停止执行策略交易逻辑
LogStatus!(_D(None), "level:", level, "持仓超过注册码的使用限制,不再执行策略交易逻辑!");
continue;
}
// 其他交易逻辑
// 正常输出状态栏信息
LogStatus!(_D(None), "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()rustfn main() { // Dial 支持 tcp://、udp://、tls://、unix:// 协议,可使用 Dial::new(addr, timeout) 指定超时的秒数 let mut client = Dial("tls://www.baidu.com:443"); if client.Valid() { // write 的第二个数字参数指定超时,write 返回成功发送的字节数 client.write("GET / HTTP/1.1\nConnection: Closed\n\n", 0); loop { // read 的数字参数指定超时,单位:毫秒。返回空字符串表示出错、超时或 socket 已关闭 let buf = client.read(0); if 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// 不支持rustfn main() { // let mut client = Dial("sqlite3://:memory:"); // 使用内存数据库 let mut client = Dial("sqlite3://test1.db"); // 打开/连接托管者所在目录的数据库文件 // Rust 的连接对象不支持 fd() 方法 // 查询数据库中的表 let ret = client.exec("SELECT name FROM sqlite_master WHERE type='table'"); Log!(format!("{:?}", ret)); Log!("执行client.close()"); client.close(); }c++// 不支持
返回值
| 类型 | 描述 | ||||||||||||||||||||
object | 如果连接超时,
WebSocket协议推送过来的数据,如果策略中两次
|
参数
| 名称 | 类型 | 必填 | 描述 |
address | string | 是 | 请求地址。 |
timeout | number | 否 | 超时时间,单位为秒。 |
options | object | 否 | 连接配置选项。 |
备注
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用于指定压缩方式,可选值包括gzip_raw、gzip等。如果所用的gzip并非标准gzip,可使用扩展方式:gzip_raw |
| WebSocket协议数据压缩相关的参数: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用于设置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 |
|---|
| postgres | github.com/lib/pq | postgres://user=postgres dbname=yourdatabase sslmode=disable
password=yourpassword host=localhost port=5432 | -- |
| clickhouse |
|---|
目前仅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库
rust
fn main() {
// 本次设置代理并发送http请求,无用户名,无密码,此次http请求会通过代理发送
let ret1: String = HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/", None);
// 本次设置代理并发送http请求,输入用户名和密码,仅HttpQuery当前调用生效,之后再次调用HttpQuery("http://www.baidu.com")这样不会使用代理
let ret2: String = HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/", None);
}
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请求)以获取数据。
回测时限制访问20个不同的URL,并且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")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84barustfn 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 }c++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, "", "") Log(ret4)rustfn main() { let ret1 = Encode("text.encoder.utf8", "raw", "hex", "你好", "", ""); // e4bda0e5a5bd Log!(ret1); let ret2 = Encode("text.decoder.utf8", "hex", "string", &ret1, "", ""); Log!(ret2); let ret3 = Encode("text.encoder.gbk", "raw", "hex", "你好", "", ""); // c4e3bac3 Log!(ret3); let ret4 = Encode("text.decoder.gbk", "hex", "string", &ret3, "", ""); Log!(ret4); }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参数,则不使用key进行加密。
UnixNano
获取当前时刻的纳秒级时间戳。
UnixNano()示例
如需获取毫秒级时间戳,可参考以下代码:
javascript
function main() {
var time = UnixNano() / 1000000
Log(_N(time, 0))
}
python
def main():
time = UnixNano()
Log(time)
rust
fn main() {
let time = UnixNano() / 1000000;
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)
rust
fn main() {
let 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())
rust
fn 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"))
rust
fn 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;"))rustfn main() { let 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)", ]; let strSql = arr.join(""); let ret = DBExec(&strSql); Log!(format!("{:?}", ret)); // 增加一条数据 Log!(format!("{:?}", DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))); // 查询数据 Log!(format!("{:?}", 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)rustfn main() { let 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)", ]; let strSql = arr.join(""); let ret = DBExec(&strSql); Log!(format!("{:?}", 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))rustfn main() { let 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)", ]; let strSql = arr.join(""); Log!(format!("{:?}", DBExec(&strSql))); // 增加一条数据 Log!(format!("{:?}", DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))); // 查询数据 Log!(format!("{:?}", DBExec("SELECT * FROM TEST_TABLE;"))); // 修改数据,Rust 的 DBExec() 函数只接受单个 SQL 语句字符串参数,不支持 ? 占位符传参,参数值需直接写入语句中 Log!(format!("{:?}", DBExec("UPDATE TEST_TABLE SET HIGH=110 WHERE TS=1518970320000;"))); // 删除数据 Log!(format!("{:?}", 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 语句执行结果的对象,例如: ```json {"columns":["TS","HIGH","OPEN","LOW","CLOSE","VOLUME"],"values":[[1518970320000,100,99.1,90,100,12345.6]]} ``` |
参数
| 名称 | 类型 | 必填 | 描述 |
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)
rust
fn main() {
let uuid1 = UUID();
let 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函数
rust
fn main() {
loop {
// 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数
if exchange.IO("status").unwrap_or_default() == "true" {
exchange.SetContractType("rb888").unwrap();
LogStatus!(_D(None), "已经连接CTP !");
let msg = EventLoop(0);
Log!(msg);
} else {
LogStatus!(_D(None), "未连接CTP !");
}
}
}
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
该函数用于持久化保存数据,实现了一个可保存的全局字典功能。数据以键值对(KV)表的结构存储,并永久保存在托管者的本地数据库文件中。
_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()
rust
fn main() {
// 设置一个全局变量num,值为1
_G!("num", 1);
// 更改一个全局变量num,值为字符串ok
_G!("num", "ok");
// 删除全局变量num
_G!("num", null);
// 返回全局变量num的值
Log!(_G!("num"));
// Rust 不支持 _G!(null) 删除所有全局变量的形式
// 返回实盘ID
let 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()函数持久化保存的K-V键值数据存储在实盘(即策略实例程序)所属托管者目录下的/logs/storage/123456/123456.db3数据库文件中(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)rustfn main() { let strTime = _D(None); 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))rustfn main() { Log!(_D(1574993606000)); }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--26rustfn main() { // Rust 的 _D() 函数不支持 fmt 参数,仅支持默认格式:yyyy-MM-dd hh:mm:ss Log!(_D(1574993606000)); // 2019-11-29 10:13:26 }c++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)rust// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试 fn main() { let i = 3.1415; Log!(i); let 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)rust// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试 fn main() { let i = 1300; Log!(i); let ii = _N(i, -3); // 查看日志得知为1000 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)rustfn main() { // 由于是测试代码,未采用商品期货策略的通用架构,此处仅通过exchange.IO("status")函数进行判断,在成功连接期货公司前置机后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态 while exchange.IO("status").unwrap_or_default() != "true" { Sleep(1000); } // 测试代码部分 // 将合约设置为rb888,即螺纹钢主力连续合约;或设置为股票代码 exchange.SetContractType("rb888").unwrap(); let ticker = _C!(exchange.GetTicker(None)); // 将_C!()宏的重试时间间隔调整为2秒 _CDelay(2000); let depth = _C!(exchange.GetDepth(None)); 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)rustfn main() { while exchange.IO("status").unwrap_or_default() != "true" { Sleep(1000); } exchange.SetContractType("rb888").unwrap(); let records = _C!(exchange.GetRecords(None, PERIOD_D1, None)); 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)rustfn test(a: i64, b: i64) -> Result<bool> { let time = Unix(); if time % b == 3 { Log!("符合条件!", "#FF0000"); return Ok(true); } Log!("重试!", "#FF0000"); Err(Error::Api("retry".to_string())) } fn main() { // 此测试不通过接口获取数据,因此无需调用 exchange.IO("status") 函数判断连接状态,也无需设置合约代码,因为这里仅用于测试自定义函数 // 在 Rust 中,自定义函数返回 Result 类型即可使用 _C! 宏进行容错,返回 Err 时会自动重试 let ret = _C!(test(1, 5)); Log!(ret); }c++// C++ 不支持以这种方式对自定义函数进行容错处理
返回值
| 类型 | 描述 |
除假值和空值以外的所有平台支持类型(any)。 | 回调函数执行后的返回值。 |
参数
| 名称 | 类型 | 必填 | 描述 |
pfn | function | 是 | 参数 |
arg | string / number / bool / object / array / function / any (系统支持的所有类型) | 否 | 回调函数的参数,参数 |
备注
_C()函数会反复调用指定的函数,直到其成功返回为止(当参数pfn所引用的函数返回空值或假值时,_C()会重试调用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))
rust
fn main() {
// 快线指标
let arr1 = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 8.0, 8.0, 9.0];
// 慢线指标
let arr2 = [2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 7.0, 7.0, 7.0];
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 的第三方库处理大数值数据。
rust
fn main() {
let s1 = r#"{"num": 8754613216564987646512354656874651651358}"#;
Log!("JSONParse:", JSONParse(s1).unwrap()); // JSONParse: {"num":8.754613216564987e+39}
// Rust 的 JSONParse() 函数不支持 safeStr 参数,大数值可使用第三方库处理
let s2 = r#"{"num": 123}"#;
Log!("JSONParse:", JSONParse(s2).unwrap()); // JSONParse: {"num":123}
}
c++
// 可以使用其它方案处理。返回值
| 类型 | 描述 |
object | 返回值为 |
参数
| 名称 | 类型 | 必填 | 描述 |
s | string | 是 | 该参数为需要解码(解析)的 |
safeStr | bool | 否 | 当该参数设置为 |
备注
JSON.parse() 函数可以正确解析包含较大数值的 JSON 字符串;当 safeStr 参数设置为真值时,会将较大的数值解析为字符串类型。
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) # 每分钟更新一次频道状态rustfn main() { let mut updateId = 0; let robotId = _G!(); // 获取当前实盘ID loop { if exchange.IO("status").unwrap_or_default() != "true" { Sleep(1000); continue; } // 获取实时市场价格 let ticker = match exchange.GetTicker("rb888") { Ok(t) => t, Err(_) => { Sleep(5000); continue; } }; // 构建当前频道状态数据 updateId += 1; let timestamp = Unix() * 1000; let channelState = format!( r#"{{"robotId": {}, "updateId": {}, "timestamp": {}, "symbol": "rb888", "lastPrice": {}, "volume": {}, "high": {}, "low": {}}}"#, robotId, updateId, timestamp, ticker.Last, ticker.Volume, ticker.High, ticker.Low ); // 向频道发布最新状态(覆盖旧状态) SetChannelData(&channelState); // 显示当前频道状态 LogStatus!(format!( "频道广播端 [实盘ID: {}]\n更新ID: #{}\n时间: {}\n交易对: rb888\n最新价: ${:.2}\n成交量: {:.4}\n最高价: ${:.2}\n最低价: ${:.2}", robotId, updateId, _D(timestamp), ticker.Last, ticker.Volume, ticker.High, ticker.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 秒发送一次rust// 本示例演示如何使用 HttpQuery 发送 HTTP POST 请求,模拟外部平台向优宽量化实盘发送数据 // 在实际场景中,外部平台(如 TradingView 的 Webhook 告警 URL、第三方交易系统等)会直接调用优宽量化 API 端点 fn main() { let uuid = "6BC42A119B5DBFA2188A8279DA3B5C30"; let robotId = 123456; // 目标实盘 ID(即需要接收数据的实盘) let baseUrl = "https://www.youquant.com"; loop { // 准备要发送的数据(可以是 JSON、文本或其他格式) let sendData = format!( r#"{{"action": "buy", "symbol": "rb888", "price": 3500, "timestamp": {}}}"#, Unix() * 1000 ); // 构造 HTTP POST 请求 let options = format!(r#"{{"method": "POST", "body": {:?}}}"#, sendData); let url = format!("{}/api/v1?method=pub&robot={}&channel={}", baseUrl, robotId, uuid); // 发送数据 let ret: String = HttpQuery(&url, options.as_str()); 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秒读取一次频道数据rustfn main() { // Rust 的 GetChannelData() 函数不接受频道 ID 参数,只能读取当前实盘自身频道 // (即本实盘通过 SetChannelData() 发布)的最新数据,无法订阅其它实盘或 UUID 频道 loop { // 读取频道的当前状态 let state = GetChannelData(); // 构建状态显示内容 let mut statusMsg = String::from("频道订阅端 - 当前订阅状态\n\n"); if !state.is_null() { statusMsg += &format!("更新ID: #{}\n", state["updateId"].as_i64().unwrap_or(0)); statusMsg += &format!("时间: {}\n", _D(state["timestamp"].as_i64().unwrap_or(0))); statusMsg += &format!("交易对: {}\n", state["symbol"].as_str().unwrap_or("")); statusMsg += &format!("最新价: ${:.2}\n", state["lastPrice"].as_f64().unwrap_or(0.0)); statusMsg += &format!("成交量: {:.4}\n", state["volume"].as_f64().unwrap_or(0.0)); } else { statusMsg += "状态: 等待中... (首次调用返回 null)\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 秒检查一次rustfn main() { // Rust 的 GetChannelData() 函数不接受频道 ID 参数,只能读取当前实盘自身频道 // (即本实盘通过 SetChannelData() 发布)的最新数据,无法订阅其它实盘或 UUID 频道 loop { // 订阅当前频道的数据 let data = GetChannelData(); if !data.is_null() { Log!("Received cross-platform data:", data); } else { Log!("Waiting for data... (first call returns null)"); } 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频道的数据,而不同托管者的实盘则无法订阅。