返回系统当前版本号。
当前系统版本号,例如:3.6
。
string
Version()
function main() {
Log("version:", Version())
}
def main():
Log("version:", Version())
void main() {
Log("version:", Version());
}
系统版本号即托管者程序的版本号。
休眠函数,使程序暂停一段时间。
Sleep(millisecond)
millisecond
参数用于设置休眠时长,毫秒数。
millisecond
true
number
function main() {
Sleep(1000 * 10) // 等待10秒钟
Log("等待了10秒钟")
}
def main():
Sleep(1000 * 10)
Log("等待了10秒钟")
void main() {
Sleep(1000 * 10);
Log("等待了10秒钟");
}
例如执行Sleep(1000)
函数时,程序会休眠1秒。 支持休眠时间小于1毫秒的操作,例如设置Sleep(0.1)
。 支持最小参数为0.000001
,即纳秒级休眠,1纳秒等于1e-6
毫秒。
在使用Python
语言编写策略时,对于轮询间隔、时间等待的操作应当使用Sleep(millisecond)
函数。 不建议使用Python
的time
库的time.sleep(second)
函数。 因为策略中使用time.sleep(second)
函数在回测时会让策略程序实际等待一段时间(并非在回测系统的时间序列上跳过),所以导致策略回测非常慢。
判断策略的运行环境是否为回测系统。
策略运行在回测系统环境时返回真值,例如:true
。 策略运行在实盘环境时返回假值,例如:false
。
bool
IsVirtual()
function main() {
if (IsVirtual()) {
Log("当前为回测系统环境。")
} else {
Log("当前为实盘环境。")
}
}
def main():
if IsVirtual():
Log("当前为回测系统环境。")
else:
Log("当前为实盘环境。")
void main() {
if (IsVirtual()) {
Log("当前为回测系统环境。");
} else {
Log("当前为实盘环境。");
}
}
判断当前运行环境是否是回测系统,用来兼容回测与实盘的差异。
发送邮件。
邮件发送成功返回真值,例如:true
,发送失败返回假值,例如:false
。
bool
Mail(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)
用于指定邮件发送方的SMTP
服务地址。
smtpServer
true
string
用于指定邮件发送方的邮箱地址。
smtpUsername
true
string
邮件发送方邮箱的SMTP
密码。
smtpPassword
true
string
用于指定邮件接收方的邮箱地址。
mailTo
true
string
邮件标题。
title
true
string
邮件正文。
body
true
string
function main(){
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
}
def main():
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
void main() {
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body");
}
smtpPassword
参数设置的是SMTP
服务的密码,不是邮箱密码。
设置smtpServer
参数时,如果需要更改端口可以直接在参数smtpServer
中加入端口号。 例如:QQ邮箱smtp.qq.com:587
该端口测试可用。
如果出现报错:unencryped connection
,需要修改Mail
函数的smtpServer
。 参数格式为:ssl://xxx.com:xxx
,例如QQ邮箱的SMTP
的ssl
方式:ssl://smtp.qq.com:465
或者smtp://xxx.com:xxx
。
回测系统中不起作用。
{@fun/Global/Mail_Go Mail_Go}
Mail
函数的异步版本。
Mail_Go
函数立即返回一个并发对象,可以使用该并发对象的wait
方法获取邮件发送结果, 邮件发送成功返回真值,例如:true
,发送失败返回假值,例如:false
。
object
Mail_Go(smtpServer, smtpUsername, smtpPassword, mailTo, title, body)
用于指定邮件发送方的SMTP
服务地址。
smtpServer
true
string
用于指定邮件发送方的邮箱地址。
smtpUsername
true
string
邮件发送方邮箱的SMTP
密码。
smtpPassword
true
string
用于指定邮件接收方的邮箱地址。
mailTo
true
string
邮件标题。
title
true
string
邮件正文。
body
true
string
function main() {
var r1 = Mail_Go("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
var r2 = Mail_Go("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
var ret1 = r1.wait()
var ret2 = r2.wait()
Log("ret1:", ret1)
Log("ret2:", ret2)
}
# 不支持
// 不支持
回测系统中不起作用。
{@fun/Global/Mail Mail}
过滤错误日志。
SetErrorFilter(filters)
正则表达式字符串。 filters true string
function main() {
SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
}
def main():
SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
void main() {
SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused");
}
过滤常见错误。
function main() {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 随便查询一个不存在的订单,id为123,故意让接口报错
var order = exchange.GetOrder("123")
Log(order)
// 过滤http502错误、GetOrder接口错误,设置错误过滤之后,第二次调用GetOrder不再报错
SetErrorFilter("502:|GetOrder")
order = exchange.GetOrder("123")
Log(order)
}
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
order = exchange.GetOrder("123")
Log(order)
SetErrorFilter("502:|GetOrder")
order = exchange.GetOrder("123")
Log(order)
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
TId orderId;
Order order = exchange.GetOrder(orderId);
Log(order);
SetErrorFilter("502:|GetOrder");
order = exchange.GetOrder(orderId);
Log(order);
}
过滤某个接口错误信息。
被此正则表达式匹配的错误日志将不上传到日志系统,可多次调用(没有次数限制)设置多个过滤条件。多次设置的正则表达式会累积在一起同时生效。可以设置空字符串来重置用以过滤错误日志的正则表达式:SetErrorFilter("")
。 被过滤的日志不再写入托管者目录下对应实盘Id的数据库文件中,防止频繁报错导致数据库文件膨胀。
获取实盘进程Id。
返回实盘进程Id。 string
GetPid()
function main(){
var id = GetPid()
Log(id)
}
def main():
id = GetPid()
Log(id)
void main() {
auto id = GetPid();
Log(id);
}
获取最近一次出错信息。
最近一次出错信息。 string
GetLastError()
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
// 因为不存在编号为123的订单,所以会出错
exchange.GetOrder("123")
var error = GetLastError()
Log(error)
}
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.GetOrder("123")
error = GetLastError()
Log(error)
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
// 订单ID类型:TId,所以不能传入字符串,我们下一个不符合交易所规范的订单来触发
exchange.GetOrder(exchange.Buy(1, 1));
auto error = GetLastError();
Log(error);
}
回测系统中不起作用。
获取策略交互命令。
返回的命令格式为ControlName:Data
。ControlName
是控件名称,Data
是控件中输入的数据。 如果交互控件没有输入框、下拉框等组件(例如:不带输入框的按钮控件)则返回的命令格式为ControlName
,仅返回控件名称。
string
GetCommand()
function main(){
while(true) {
var cmd = GetCommand()
if (cmd) {
Log(cmd)
}
Sleep(1000)
}
}
def main():
while True:
cmd = GetCommand()
if cmd:
Log(cmd)
Sleep(1000)
void main() {
while(true) {
auto cmd = GetCommand();
if(cmd != "") {
Log(cmd);
}
Sleep(1000);
}
}
检测交互命令并且在检测到交互命令时使用Log
函数输出交互命令。
function main() {
while (true) {
LogStatus(_D())
var cmd = GetCommand()
if (cmd) {
Log("cmd:", cmd)
var arr = cmd.split(":")
if (arr[0] == "buy") {
Log("买入,该控件不带数量")
} else if (arr[0] == "sell") {
Log("卖出,该控件带数量:", arr[1])
} else {
Log("其它控件触发:", arr)
}
}
Sleep(1000)
}
}
def main():
while True:
LogStatus(_D())
cmd = GetCommand()
if cmd:
Log("cmd:", cmd)
arr = cmd.split(":")
if arr[0] == "buy":
Log("买入,该控件不带数量")
elif arr[0] == "sell":
Log("卖出,该控件带数量:", arr[1])
else:
Log("其它控件触发:", arr)
Sleep(1000)
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
void split(const string& s,vector<string>& sv,const char flag = ' ') {
sv.clear();
istringstream iss(s);
string temp;
while (getline(iss, temp, flag)) {
sv.push_back(temp);
}
return;
}
void main() {
while(true) {
LogStatus(_D());
auto cmd = GetCommand();
if (cmd != "") {
vector<string> arr;
split(cmd, arr, ':');
if(arr[0] == "buy") {
Log("买入,该控件不带数量");
} else if (arr[0] == "sell") {
Log("卖出,该控件带数量:", arr[1]);
} else {
Log("其它控件触发:", arr);
}
}
Sleep(1000);
}
}
例如策略交互控件中添加了一个不带输入框的控件,交互控件命名为:buy
,控件描述信息为:买入
,这是一个按钮控件。 继续添加一个带输入框的控件,交互控件名为:sell
,控件描述信息为:卖出
,这是一个由按钮和输入框组合起来的交互控件。 策略中设计交互代码来响应不同交互控件:
回测系统中不起作用。
获取生成策略注册码时写入的Meta的值。
Meta
数据。
string
GetMeta()
function main() {
// 策略允许的计价币最大资产数值
var maxBaseCurrency = null
// 获取创建注册码时的元数据
var level = GetMeta()
// 检测Meta对应的条件
if (level == "level1") {
// -1为不限制
maxBaseCurrency = -1
} else if (level == "level2") {
maxBaseCurrency = 10
} else if (level == "level3") {
maxBaseCurrency = 1
} else {
maxBaseCurrency = 0.5
}
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
while(true) {
Sleep(1000)
var ticker = exchange.GetTicker()
// 检测资产数值
var acc = exchange.GetAccount()
if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Balance + acc.FrozenBalance) {
// 停止执行策略交易逻辑
LogStatus(_D(), "level:", level, "持仓超过注册码的使用限定,不再执行策略交易逻辑!")
continue
}
// 其它交易逻辑
// 正常输出状态栏信息
LogStatus(_D(), "level:", level, "策略正常运行!ticker数据:\n", ticker)
}
}
def main():
maxBaseCurrency = null
level = GetMeta()
if level == "level1":
maxBaseCurrency = -1
elif level == "level2":
maxBaseCurrency = 10
elif level == "level3":
maxBaseCurrency = 1
else:
maxBaseCurrency = 0.5
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
while True:
Sleep(1000)
ticker = exchange.GetTicker()
acc = exchange.GetAccount()
if maxBaseCurrency != -1 and maxBaseCurrency < acc["Balance"] + acc["FrozenBalance"]:
LogStatus(_D(), "level:", level, "持仓超过注册码的使用限定,不再执行策略交易逻辑!")
continue
# 其它交易逻辑
# 正常输出状态栏信息
LogStatus(_D(), "level:", level, "策略正常运行!ticker数据:\n", ticker)
void main() {
auto maxBaseCurrency = 0.0;
auto level = GetMeta();
if (level == "level1") {
maxBaseCurrency = -1;
} else if (level == "level2") {
maxBaseCurrency = 10;
} else if (level == "level3") {
maxBaseCurrency = 1;
} else {
maxBaseCurrency = 0.5;
}
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
while(true) {
Sleep(1000);
auto ticker = exchange.GetTicker();
auto acc = exchange.GetAccount();
if (maxBaseCurrency != -1 && maxBaseCurrency < acc.Balance + acc.FrozenBalance) {
// 停止执行策略交易逻辑
LogStatus(_D(), "level:", level, "持仓超过注册码的使用限定,不再执行策略交易逻辑!");
continue;
}
// 其它交易逻辑
// 正常输出状态栏信息
LogStatus(_D(), "level:", level, "策略正常运行!ticker数据:\n", ticker);
}
}
应用场景范例:使用Meta
限制策略操作的资产量。
应用场景:需要对不同的策略租用者做资金限制。 生成注册码时设置的Meta
值长度不能超过190个字符,GetMeta()
函数仅支持实盘。 如果生成策略注册码时没有设置元数据(Meta
),GetMeta()
函数返回空值。 回测系统中不起作用。
用于原始的Socket
访问,支持tcp
,udp
,tls
,unix
协议。 支持4种流行的通信协议:mqtt
、nats
、amqp
、kafka
。 支持连接数据库,支持的数据库有:sqlite3
、mysql
、postgres
、clickhouse
。
如果超时Dial()
函数返回空值。 正常调用时返回一个连接对象,该对象有三个方法:read
、write
、close
。read
方法用于读取数据,write
方法用于发送数据。 close
方法用于关闭连接。
read
方法支持以下参数:
ws.read()
。ws.read(2000)
指定超时时间为两秒(2000毫秒)。-1
指不管有无消息,函数立即返回,例如:ws.read(-1)
。
传入参数-2
指不管有无消息,函数立即返回,但只返回最新的消息,缓冲区的消息会被丢弃。例如ws.read(-2)
。read()
函数缓冲区说明:
WebSocket协议推送的来的数据,如果在策略read()
函数调用之间时间间隔过长,就可能造成数据累积。这些数据储存在缓冲区,缓冲区数据结构为队列,上限2000个。超出2000后最新的数据进入缓冲区,最旧的数据清除掉。
场景 | 无参数 | 参数:-1 | 参数:-2 | 参数:2000,单位是毫秒 |
---|---|---|---|---|
缓冲区已有数据 | 立即返回最旧数据 | 立即返回最旧数据 | 立即返回最新数据 | 立即返回最旧数据 |
缓冲区无数据 | 阻塞到有数据时返回 | 立即返回空值 | 立即返回空值 | 等待2000毫秒,无数据返回空值,有数据则返回 |
WebSocket连接断开或者底层重连时 | read()函数返回空字符串,即:"",write()函数返回0,检测到该情况。可以使用close()函数关闭连接,如果设置了自动重连则不用关闭,系统底层会自动重连。 |
object
Dial(address) Dial(address, timeout)
请求地址。 address true string 超时秒数, timeout false number
function main(){
// Dial支持tcp://,udp://,tls://,unix://协议,可加一个参数指定超时的秒数
var client = Dial("tls://www.baidu.com:443")
if (client) {
// write可再跟一个数字参数指定超时,write返回成功发送的字节数
client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
while (true) {
// read可再跟一个数字参数指定超时,单位:毫秒。返回null指出错或者超时或者socket已经关闭
var buf = client.read()
if (!buf) {
break
}
Log(buf)
}
client.close()
}
}
def main():
client = Dial("tls://www.baidu.com:443")
if client:
client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
while True:
buf = client.read()
if not buf:
break
Log(buf)
client.close()
void main() {
auto client = Dial("tls://www.baidu.com:443");
if(client.Valid) {
client.write("GET / HTTP/1.1\nConnection: Closed\n\n");
while(true) {
auto buf = client.read();
if(buf == "") {
break;
}
Log(buf);
}
client.close();
}
}
Dial函数调用例子:
var client = null
function main() {
// client = Dial("sqlite3://:memory:") // 使用内存数据库
client = Dial("sqlite3://test1.db") // 打开/连接托管者所在目录的数据库文件
// 记录句柄
var sqlite3Handle = client.fd()
Log("sqlite3Handle:", sqlite3Handle)
// 查询数据库中的表
var ret = client.exec("SELECT name FROM sqlite_master WHERE type='table'")
Log(ret)
}
function onexit() {
Log("执行client.close()")
client.close()
}
// 不支持
// 不支持
Dial函数连接数据库时返回的连接对象有2个其独有的方法函数:
exec(sqlString)
: 用于执行SQL语句,使用方式类似于DBExec()
函数。fd()
: fd()
函数返回一个句柄(例如:句柄变量为handle),用于其它线程重连(即使Dial创建的对象已经被执行close()
函数关闭连接),将句柄传入Dial()
函数,例如:Dial(handle)
重用连接。
以下是Dial函数连接sqlite3
数据库的例子。address
参数的详细说明,在正常的地址:wss://xxx.xxx.xxx:10441/websocket?compress
后,以|
符号分隔。 如果参数字符串中有|
字符,则以||
作为分隔符号。之后的部分是一些功能参数设置,各个参数之间用&
字符连接。 例如ss5
代理和压缩参数一起设置时可以写作: Dial("wss://baidu.com/stream|proxy=socks5://xxx:9999&compress=gzip_raw&mode=recv")
Dial函数的address参数支持的功能 | 参数说明 |
---|---|
WebSocket协议数据压缩相关的参数:compress=参数值 | compress为压缩方式,compress参数可选gzip_raw、gzip等。如果gzip方式非标准gzip,可以使用扩展的方式:gzip_raw |
WebSocket协议数据压缩相关的参数:mode=参数值 | mode为压缩模式,mode参数可选dual,send,recv三种。dual为双向压缩,发送压缩数据,接收压缩数据。send为发送压缩数据。recv为接收压缩数据,本地解压缩。 |
WebSocket协议设置底层自动重连相关的参数:reconnect=参数值 | reconnect为是否设置重连,reconnect=true为启用重连。不设置该参数时默认不重连。 |
WebSocket协议设置底层自动重连相关的参数:interval=参数值 | interval为重试时间间隔,单位毫秒,interval=10000为重试间隔10秒,不设置默认1秒,即interval=1000。 |
WebSocket协议设置底层自动重连相关的参数:payload=参数值 | payload为WebSocket重连时需要发送的订阅消息,例如:payload=okok。 |
socks5代理的相关参数:proxy=参数值 | proxy为ss5代理设置,参数值格式:socks5://name:pwd@192.168.0.1:1080,name为ss5服务端用户名,pwd为ss5服务端登录密码,1080为ss5服务的端口。 |
Dial()
函数仅支持实盘。
使用Dial函数连接数据库时,编写的连接字符串参考各数据库的go语言驱动项目。
支持的数据库 | 驱动项目 | 连接字符串(Connection String) | 备注 |
---|---|---|---|
sqlite3 | github.com/mattn/go-sqlite3 | sqlite3://file:test.db?cache=shared&mode=memory | sqlite3:// 前缀表示使用的是sqlite3数据库,调用例子:Dial("sqlite3://test1.db") |
mysql | github.com/go-sql-driver/mysql | mysql://username:yourpassword@tcp(localhost:3306)/yourdatabase?charset=utf8mb4 | – |
postgres | github.com/lib/pq | postgres://user=postgres dbname=yourdatabase sslmode=disable password=yourpassword host=localhost port=5432 | – |
clickhouse | github.com/ClickHouse/clickhouse-go | clickhouse://tcp://host:9000?username=username&password=yourpassword&database=youdatabase | – |
目前仅JavaScript语言支持Dial函数中使用mqtt
、nats
、amqp
、kafka
通信协议,以JavaScript语言策略代码为例展示mqtt
、nats
、amqp
、kafka
四种协议使用例子:
// 需要先配置、部署完成各个协议的代理服务器
// 为了便于演示,主题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], "连接")
}
}
发送Http请求。
返回请求的应答数据,如果返回值为JSON
字符串,JavaScript
语言的策略中可以用JSON.parse()
函数解析,C++
语言的策略中可以用json::parse()
函数解析。 参数options
结构中如果debug
设置为true,返回值为对象(JSON);如果debug
设置为false,返回值为字符串。
string、object
HttpQuery(url) HttpQuery(url, options)
Http请求url。 url true string Http请求相关设置,例如可以是以下结构:
{
method: "POST",
body: "a=10&b=20&c=30",
charset: "UTF-8",
cookie: "session_id=12345; lang=en",
profile: "chrome_103",
debug: false,
headers: {"TEST-HTTP-QUERY": "123"},
timeout: 1000
}
tls
指纹。
支持的设置有以下选项:
chrome_:"chrome_103"
、"chrome_104"
、"chrome_105"
、"chrome_106"
、"chrome_107"
、"chrome_108"
、"chrome_109"
、"chrome_110"
、"chrome_111"
、"chrome_112"
、"chrome_117"
、
safari_:"safari_15_6_1"
、"safari_16_0"
、"safari_ipad_15_6"
、"safari_ios_15_5"
、"safari_ios_15_6"
、"safari_ios_16_0"
、
firefox_:"firefox_102"
、"firefox_104"
、"firefox_105"
、"firefox_106"
、"firefox_108"
、"firefox_110"
、"firefox_117"
、
opera_:"opera_89"
、"opera_90"
、"opera_91"
、
zalando_:"zalando_android_mobile"
、"zalando_ios_mobile"
、
nike_:"nike_ios_mobile"
、"nike_android_mobile"
、
cloudscraper:"cloudscraper"
、
mms_:"mms_ios"
、
mesh_:"mesh_ios"
、"mesh_ios_1"
、"mesh_ios_2"
、"mesh_android"
、"mesh_android_1"
、"mesh_android_2"
、
confirmed_:"confirmed_ios"
、"confirmed_android"
、
okhttp4_:"okhttp4_android_7"
、"okhttp4_android_8"
、"okhttp4_android_9"
、"okhttp4_android_10"
、"okhttp4_android_11"
、"okhttp4_android_12"
、"okhttp4_android_13"
、true
时,此次HttpQuery
函数调用返回完整的应答报文。设置为false
时只返回应答报文Body
中的数据。此结构中的所有字段均为可选,例如可以不设置profile
字段。
options false object
function main() {
// 本次设置代理并发送http请求,无用户名,无密码,此次http请求会通过代理发送
HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/")
// 本次设置代理并发送http请求,输入用户名和密码,仅HttpQuery当前调用生效,之后再次调用HttpQuery("http://www.baidu.com")这样不会使用代理
HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/")
}
# HttpQuery不支持Python,可以使用Python的urllib2库
void main() {
HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/");
HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/");
}
HttpQuery函数使用代理设置。
HttpQuery()
函数只支持JavaScript
、C++
语言,Python
语言可以使用urllib
库,直接发送Http请求。 回测系统中可以使用HttpQuery()
发送请求(只支持GET
请求)获取数据。 回测时限制使用20次访问不同的URL
,并且HttpQuery()
访问会缓存数据, 相同的URL
第二次访问时HttpQuery()
函数返回缓存数据,不再发生实际网络请求。
{@fun/Global/HttpQuery_Go HttpQuery_Go}
发送Http请求,HttpQuery
函数的异步版本。
HttpQuery_Go()
函数立即返回一个并发对象,可以使用该并发对象的wait
方法获取Http请求的结果, JavaScript
语言的策略中可以用JSON.parse()
函数解析。
object
HttpQuery_Go(url) HttpQuery_Go(url, options)
Http请求url。 url true string Http请求相关设置,例如可以是以下结构:
{
method: "POST",
body: "a=10&b=20&c=30",
charset: "UTF-8",
cookie: "session_id=12345; lang=en",
// profile: "",
debug: false,
headers: {"TEST-HTTP-QUERY": "123"},
timeout: 1000
}
tls
指纹。true
时,此次HttpQuery_Go
函数调用返回完整的应答报文。设置为false
时只返回应答报文Body
中的数据。此结构中的所有字段均为可选,例如可以不设置profile
字段。
options false object
function main() {
// 创建第一个异步线程
var r1 = HttpQuery_Go("https://xxx.xxx.xxx") // https://xxx.xxx.xxx 仅为演示地址,可以是某个数据源
// 创建第二个异步线程
var r2 = HttpQuery_Go("https://xxx.xxx.xxx")
// 获取第一个异步线程调用的返回值
var tickers1 = r1.wait()
// 获取第二个异步线程调用的返回值
var tickers2 = r2.wait()
// 打印结果
Log("tickers1:", tickers1)
Log("tickers2:", tickers2)
}
# 不支持
// 不支持
HttpQuery_Go()
函数使用范例:
HttpQuery_Go()
函数只支持JavaScript
,Python
语言可以使用urllib
库,直接发送Http请求。 HttpQuery_Go()
主要用于访问交易所不需要签名的接口,例如行情信息等公共接口。回测系统中不支持HttpQuery_Go
函数。
{@fun/Global/HttpQuery HttpQuery}
该函数根据传入的参数对数据进行编码。
Encode
函数返回编码、加密之后的数据。
string
Encode(algo, inputFormat, outputFormat, data) Encode(algo, inputFormat, outputFormat, data, keyFormat, key)
参数algo
为编码计算时使用的算法。 支持设置为:“raw”(不使用算法),“sign”,“signTx”,“md4”,“md5”,“sha256”,“sha512”,“sha1”,“keccak256”, “sha3.224”,“sha3.256”,“sha3.384”,“sha3.512”,“sha3.keccak256”,“sha3.keccak512”,“sha512.384”, “sha512.256”,“sha512.224”,“ripemd160”,“blake2b.256”,“blake2b.512”,“blake2s.128”,“blake2s.256”。 参数algo
也支持:“text.encoder.utf8”,“text.decoder.utf8”,“text.encoder.gbk”,“text.decoder.gbk”,对字符串编码、解码。 参数algo
也支持:“ed25519"算法。支持使用不同的哈希算法,例如参数algo
可写为"ed25519.md5”、"ed25519.sha512"等。支持ed25519.seed
计算。
algo
true
string
用于指定data
参数的数据格式。 inputFormat
参数支持设置为:“raw”,“hex”,“base64”,"string"其中之一。 "raw"表示数据为原始数据,"hex"表示数据为hex
编码,"base64"表示数据为base64
编码,"string"表示数据为字符串。
inputFormat
true
string
用于指定输出的数据格式。 outputFormat
参数支持设置为:“raw”,“hex”,“base64”,"string"其中之一。 "raw"表示数据为原始数据,"hex"表示数据为hex
编码,"base64"表示数据为base64
编码,"string"表示数据为字符串。
outputFormat
true
string
参数data
为所要处理的数据。
data
true
string
用于指定key
参数的数据格式。 key
参数支持设置为:“raw”,“hex”,“base64”,"string"其中之一。 "raw"表示数据为原始数据,"hex"表示数据为hex
编码,"base64"表示数据为base64
编码,"string"表示数据为字符串。
keyFormat
false
string
参数key
为HMAC
加密时使用的秘钥。 参数algo
设置为"sign"或者"signTx"时需要参数key
。 参数algo
设置为"raw"时不会使用key
参数进行HMAC
加密(因为HMAC加密必须指定算法)。
key
false
string
function main() {
Log(Encode("raw", "raw", "hex", "example", "raw", "123")) // 6578616d706c65
Log(Encode("raw", "raw", "hex", "example")) // 6578616d706c65
Log(Encode("sha256", "raw", "hex", "example", "raw", "123")) // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("sha256", "raw", "hex", "example", "", "123")) // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
Log(Encode("sha256", "raw", "hex", "example", null, "123")) // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
Log(Encode("sha256", "raw", "hex", "example", "string", "123")) // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("raw", "raw", "hex", "123")) // 313233
Log(Encode("raw", "raw", "base64", "123")) // MTIz
Log(Encode("sha256", "raw", "hex", "example", "hex", "313233")) // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz")) // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
}
def main():
Log(Encode("raw", "raw", "hex", "example", "raw", "123")) # 6578616d706c65
Log(Encode("raw", "raw", "hex", "example", "", "")) # 6578616d706c65
Log(Encode("sha256", "raw", "hex", "example", "raw", "123")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("sha256", "raw", "hex", "example", "", "123")) # 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
Log(Encode("sha256", "raw", "hex", "example", "string", "123")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("raw", "raw", "hex", "123", "", "")) # 313233
Log(Encode("raw", "raw", "base64", "123", "", "")) # MTIz
Log(Encode("sha256", "raw", "hex", "example", "hex", "313233")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz")) # 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
void main() {
Log(Encode("raw", "raw", "hex", "example", "raw", "123")); // 6578616d706c65
Log(Encode("raw", "raw", "hex", "example")); // 6578616d706c65
Log(Encode("sha256", "raw", "hex", "example", "raw", "123")); // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("sha256", "raw", "hex", "example", "", "123")); // 50d858e0985ecc7f60418aaf0cc5ab587f42c2570a884095a9e8ccacd0f6545c
Log(Encode("sha256", "raw", "hex", "example", "string", "123")); // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("raw", "raw", "hex", "123")); // 313233
Log(Encode("raw", "raw", "base64", "123")); // MTIz
Log(Encode("sha256", "raw", "hex", "example", "hex", "313233")); // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
Log(Encode("sha256", "raw", "hex", "example", "base64", "MTIz")); // 698d54f0494528a759f19c8e87a9f99e75a5881b9267ee3926bcf62c992d84ba
}
Encode函数调用例子。
function main(){
var ret1 = Encode("text.encoder.utf8", "raw", "hex", "你好") // e4bda0e5a5bd
Log(ret1)
var ret2 = Encode("text.decoder.utf8", "hex", "string", ret1)
Log(ret2)
var ret3 = Encode("text.encoder.gbk", "raw", "hex", "你好") // c4e3bac3
Log(ret3)
var ret4 = Encode("text.decoder.gbk", "hex", "string", ret3)
Log(ret4)
}
def main():
ret1 = Encode("text.encoder.utf8", "raw", "hex", "你好", "", "") # e4bda0e5a5bd
Log(ret1)
ret2 = Encode("text.decoder.utf8", "hex", "string", ret1, "", "")
Log(ret2)
ret3 = Encode("text.encoder.gbk", "raw", "hex", "你好", "", "") # c4e3bac3
Log(ret3)
ret4 = Encode("text.decoder.gbk", "hex", "string", ret3, "", "")
Log(ret4)
void main(){
auto ret1 = Encode("text.encoder.utf8", "raw", "hex", "你好"); // e4bda0e5a5bd
Log(ret1);
auto ret2 = Encode("text.decoder.utf8", "hex", "string", ret1);
Log(ret2);
auto ret3 = Encode("text.encoder.gbk", "raw", "hex", "你好"); // c4e3bac3
Log(ret3);
auto ret4 = Encode("text.decoder.gbk", "hex", "string", ret3);
Log(ret4);
}
参数algo
也支持:“text.encoder.utf8”,“text.decoder.utf8”,“text.encoder.gbk”,“text.decoder.gbk”,对字符串编码、解码。
Encode()
函数仅支持实盘。 不传key
、keyFormat
参数,则不使用key
加密。
获取当前时刻的纳秒级时间戳。
UnixNano()
函数返回纳秒级时间戳。
number
UnixNano()
function main() {
var time = UnixNano() / 1000000
Log(_N(time, 0))
}
def main():
time = UnixNano()
Log(time)
void main() {
auto time = UnixNano();
Log(time);
}
如果需要获取毫秒级时间戳,可以使用如下代码:
{@fun/Global/Unix Unix}
获取当前时刻的秒级别时间戳。
返回秒级别时间戳。 number
Unix()
function main() {
var t = Unix()
Log(t)
}
def main():
t = Unix()
Log(t)
void main() {
auto t = Unix();
Log(t);
}
{@fun/Global/UnixNano UnixNano}
获取托管者所在设备的系统信息。
系统信息。 string
GetOS()
function main() {
Log("GetOS:", GetOS())
}
def main():
Log("GetOS:", GetOS())
void main() {
Log("GetOS:", GetOS());
}
例如在Mac OS操作系统下运行的托管者,调用GetOS()
函数可能返回:darwin/amd64
, 因为苹果电脑有多种硬件架构。darwin
即Mac OS系统的名称。
计算参数data
的MD5哈希值。
MD5哈希值。 string
MD5(data)
需要进行MD5计算的数据。 data true string
function main() {
Log("MD5", MD5("hello world"))
}
def main():
Log("MD5", MD5("hello world"))
void main() {
Log("MD5", MD5("hello world"));
}
调用MD5("hello world")
函数,返回值为:5eb63bbbe01eeed093cb22bb8f5acdc3
。
{@fun/Global/Encode Encode}
数据库接口函数。
包含sql语句执行结果的对象,例如:
{"columns":["TS","HIGH","OPEN","LOW","CLOSE","VOLUME"],"values":[[1518970320000,100,99.1,90,100,12345.6]]}
object
DBExec(sql)
sql语句字符串。 sql true string
function main() {
var strSql = [
":CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
].join("")
var ret = DBExec(strSql)
Log(ret)
// 增加一条数据
Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
// 查询数据
Log(DBExec(":SELECT * FROM TEST_TABLE;"))
}
def main():
arr = [
":CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
]
strSql = ""
for i in range(len(arr)):
strSql += arr[i]
ret = DBExec(strSql)
Log(ret)
# 增加一条数据
Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
# 查询数据
Log(DBExec(":SELECT * FROM TEST_TABLE;"))
void main() {
string strSql = ":CREATE TABLE TEST_TABLE(\
TS INT PRIMARY KEY NOT NULL,\
HIGH REAL NOT NULL,\
OPEN REAL NOT NULL,\
LOW REAL NOT NULL,\
CLOSE REAL NOT NULL,\
VOLUME REAL NOT NULL)";
auto ret = DBExec(strSql);
Log(ret);
// 增加一条数据
Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
// 查询数据
Log(DBExec(":SELECT * FROM TEST_TABLE;"));
}
支持内存数据库,对于DBExec
函数的参数,如果sql语句是以:
开头则在内存数据库中操作,不写文件速度更快。 适合不需要持久化保存的数据库操作,例如:
function main() {
var strSql = [
"CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
].join("")
var ret = DBExec(strSql)
Log(ret)
}
def main():
arr = [
"CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
]
strSql = ""
for i in range(len(arr)):
strSql += arr[i]
ret = DBExec(strSql)
Log(ret)
void main() {
string strSql = "CREATE TABLE TEST_TABLE(\
TS INT PRIMARY KEY NOT NULL,\
HIGH REAL NOT NULL,\
OPEN REAL NOT NULL,\
LOW REAL NOT NULL,\
CLOSE REAL NOT NULL,\
VOLUME REAL NOT NULL)";
auto ret = DBExec(strSql);
Log(ret);
}
创建表。
function main() {
var strSql = [
"CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
].join("")
Log(DBExec(strSql))
// 增加一条数据
Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
// 查询数据
Log(DBExec("SELECT * FROM TEST_TABLE;"))
// 修改数据
Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))
// 删除数据
Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))
}
def main():
arr = [
"CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
]
strSql = ""
for i in range(len(arr)):
strSql += arr[i]
Log(DBExec(strSql))
# 增加一条数据
Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
# 查询数据
Log(DBExec("SELECT * FROM TEST_TABLE;"))
# 修改数据
Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))
# 删除数据
Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))
void main() {
string strSql = "CREATE TABLE TEST_TABLE(\
TS INT PRIMARY KEY NOT NULL,\
HIGH REAL NOT NULL,\
OPEN REAL NOT NULL,\
LOW REAL NOT NULL,\
CLOSE REAL NOT NULL,\
VOLUME REAL NOT NULL)";
Log(DBExec(strSql));
// 增加一条数据
Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
// 查询数据
Log(DBExec("SELECT * FROM TEST_TABLE;"));
// 修改数据
Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000));
// 删除数据
Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110));
}
表中记录的增删查改操作。
函数DBExec()
通过传入参数,可以操作实盘数据库(SQLite数据库)。 实现对实盘数据库中数据的增、删、查、改等操作,支持SQLite语法。 实盘数据库中系统保留表:kvdb
、cfg
、log
、profit
、chart
,切勿对这些表进行操作。 目前不支持事务,不建议执行此类操作,会引起系统中的冲突。 DBExec()
函数仅支持实盘。
{@fun/Global/_G _G}
创建一个UUID。
32位的UUID。 string
UUID()
function main() {
var uuid1 = UUID()
var uuid2 = UUID()
Log(uuid1, uuid2)
}
def main():
uuid1 = UUID()
uuid2 = UUID()
Log(uuid1, uuid2)
void main() {
auto uuid1 = UUID();
auto uuid2 = UUID();
Log(uuid1, uuid2);
}
UUID()
函数仅支持实盘。
监听事件,有任意事件发生时返回。
返回的对象如果不为空值,则返回内容中包含的Event
为事件触发类型。例如以下返回值结构:
{"Seq":10,"Event":"tick","ThreadId":0,"Index":0,"Nano":1689216222796834300,"Symbol":"rb2311","Ticker": {...}}
object
EventLoop() EventLoop(timeout)
参数timeout
为超时设置,单位为毫秒。 参数timeout
如果设置为0则等待有事件发生才返回,如果大于0就是设置事件等待超时,小于0立即返回最近事件。
timeout
false
number
function main(){
while(true){
// 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数
if(exchange.IO("status")){
exchange.SetContractType("rb888")
LogStatus(_D(), "已经连接CTP !")
var msg = EventLoop()
Log(msg)
} else {
LogStatus(_D(), "未连接CTP !")
}
}
}
# 不支持EventLoop函数
// 不支持EventLoop函数
代码中第一次调用EventLoop()
函数才会初始化该监听事件的机制,如果在事件回调之后才开始首次EventLoop()
调用,会错过之前的事件。 底层系统封装的队列结构会缓存最大500个事件回调,如果程序执行过程中没有及时调用EventLoop()
函数取出,会丢失500个缓存之外较晚的事件回调。 EventLoop()
函数的调用不会影响系统底层WebSocket的缓存队列。 对于这些缓存依然需要使用各自的方法取出数据。对于在EventLoop()
函数返回之前,已经取出的数据,不会在EventLoop()
函数中产生返回事件。 EventLoop()
函数的主要用途就是通知策略层,系统底层接收到了新的网络数据。以事件驱动整个策略。当EventLoop()
函数返回事件时, 只需遍历所有数据来源。 EventLoop()
函数仅支持实盘。
在主函数main()
中调用时,监听主线程的事件。 在JavaScript
语言编写的策略中,threading.Thread()
函数创建的线程,在线程的执行函数中也可以调用,监听当前线程的事件。
{@fun/Global/Dial Dial}
__Serve
函数用于创建Http服务、TCP服务、Websocket服务(基于Http协议)。
返回一个字符串,记录创建的服务的IP地址、端口。例如:127.0.0.1:8088
、[::]:8089
。
string
__Serve(serveURI, handler) __Serve(serveURI, handler, …args)
serveURI
参数用于配置服务绑定的协议、IP地址、端口等设置,例如:http://0.0.0.0:8088?gzip=true
,即:http://:8088?gzip=true
。
serveURI
参数设置例如:tcp://127.0.0.1:6666?tls=true
;可以添加证书、私钥例如:tls=true&cert_pem=xxxx&cert_key_pem=xxxx
。serveURI
参数设置例如:http://127.0.0.1:6666?gzip=true
;可以设置压缩设置:gzip=true
。
serveURI
参数用于Https例如:https://127.0.0.1:6666?tls=true&gzip=true
;可以加入cert_pem
和cert_key_pem
参数来加载证书。serveURI
true
string
handler
参数用于传入路由处理函数(Http协议)、消息处理函数(TCP协议)、Stream处理函数(Websocket)。
参数handler
传入的回调函数可以定义多个参数,第一个参数为ctx对象(上下文对象)。
handler
true
function
作为参数handler
传入的回调函数的参数的实参,参数arg
可能有多个,例如:
__Serve("http://:8088", function(ctx, a, b, c) {
Log(`ctx.host():`, ctx.host(), ", a=", a, ", b=", b, ", c=", c)
}, 1, 2, 3)
调用__Serve()
函数时传入的参数1
, 2
, 3
对应传入回调函数的参数a
, b
, c
。
arg false string、number、bool、object、array、function、空值等系统支持的所有类型
function main() {
let httpServer = __Serve("http://:8088?gzip=true", function (ctx) {
Log("http connect from: ", ctx.remoteAddr(), "->", ctx.localAddr())
let path = ctx.path()
if (path == "/") {
ctx.write(JSON.stringify({
path: ctx.path(),
method: ctx.method(),
headers: ctx.headers(),
cookie: ctx.header("Cookie"),
remote: ctx.remoteAddr(),
query: ctx.rawQuery()
}))
} else if (path == "/tickers") {
let ret = exchange.GetTickers()
if (!ret) {
ctx.setStatus(500)
ctx.write(GetLastError())
} else {
ctx.write(JSON.stringify(ret))
}
} else if (path == "/wss") {
if (ctx.upgrade("websocket")) { // upgrade to websocket
while (true) {
let r = ctx.read(10)
if (r == "") {
break
} else if (r) {
if (r == "ticker") {
ctx.write(JSON.stringify(exchange.GetTicker()))
} else {
ctx.write("not support")
}
}
}
Log("websocket closed", ctx.remoteAddr())
}
} else {
ctx.setStatus(404)
}
})
let echoServer = __Serve("tcp://:8089", function (ctx) {
Log("tcp connect from: ", ctx.remoteAddr(), "->", ctx.localAddr())
while (true) {
let d = ctx.read()
if (!d) {
break
}
ctx.write(d)
}
Log("connect closed")
})
Log("http serve on", httpServer, "tcp serve on", echoServer)
for (var i = 0; i < 5; i++) {
if (i == 2) {
// test Http
var retHttp = HttpQuery("http://127.0.0.1:8088?num=123&limit=100", {"debug": true})
Log("retHttp:", retHttp)
} else if (i == 3) {
// test TCP
var tcpConn = Dial("tcp://127.0.0.1:8089")
tcpConn.write("Hello TCP Server")
var retTCP = tcpConn.read()
Log("retTCP:", retTCP)
} else if (i == 4) {
// test Websocket
var wsConn = Dial("ws://127.0.0.1:8088/wss|compress=gzip")
wsConn.write("ticker")
var retWS = wsConn.read(1000)
Log("retWS:", retWS)
// no depth
wsConn.write("depth")
retWS = wsConn.read(1000)
Log("retWS:", retWS)
}
Sleep(1000)
}
}
# 不支持
// 不支持
Websocket
服务基于Http协议实现,可以在path中设置一个路由分支,设计Websocket
消息订阅/推送的实现代码,可以参考本节范例代码。参数handler
传入的回调处理函数接收一个ctx
参数。ctx
参数为一个上下文对象,用于获取数据和写入数据,有以下方法:
HTTP/1.1
、tcp
。http://127.0.0.1:8088?num=123
,参数handler
传入的回调处理函数中ctx.query("num")
调用时返回"123"
。User-Agent
:ctx.header("User-Agent")
。GET
、POST
等。ctx
上下文对象为Websocket协议;切换成功返回布尔值(真),失败返回布尔值(假)。read
方法;可以指定超时时间参数timeout_ms
,单位毫秒。JSON.stringify()
编码JSON对象为字符串之后写入。对于WebSocket
协议,可以使用该方法将编码后的字符串传递给客户端。{@fun/Global/HttpQuery HttpQuery}, {@fun/Global/HttpQuery_Go HttpQuery_Go}
持久化保存数据,该函数实现了一个可保存的全局字典功能。数据结构为KV表,永久保存在托管者本地数据库文件。
持久化保存的k-v
键值对中的键值数据。
string、number、bool、object、array、空值
_G() _G(k) _G(k, v)
参数k
为保存的键值对中的键名,不区分大小写。
k
false
string、空值
参数v
为保存的键值对中的键值,可以是任何可以JSON
序列化的数据。
v
false
string、number、bool、object、array、空值
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main(){
// 设置一个全局变量num,值为1
_G("num", 1)
// 更改一个全局变量num,值为字符串ok
_G("num", "ok")
// 删除全局变量num
_G("num", null)
// 返回全局变量num的值
Log(_G("num"))
// 删除所有全局变量
_G(null)
// 返回实盘ID
var robotId = _G()
}
def main():
_G("num", 1)
_G("num", "ok")
_G("num", None)
Log(_G("num"))
_G(None)
robotId = _G()
void main() {
_G("num", 1);
_G("num", "ok");
_G("num", NULL);
Log(_G("num"));
_G(NULL);
// 不支持 auto robotId = _G();
}
每个实盘单独一个数据库,策略重启或者托管者停止运行,_G()
函数保存的数据会一直存在。 如果是回测结束后,_G()
函数在回测系统中保存的数据会被清除。 使用_G()
函数持久化保存数据时,应当根据硬件设备的内存、硬盘空间合理使用,不可滥用。
在实盘运行中当调用_G()
函数并且不传任何参数时,_G()
函数返回当前实盘的Id
。 调用_G()
函数时,参数v
传入空值表示删除该k-v
键值对。 调用_G()
函数时,仅参数k
传入字符串,_G()
函数返回保存的参数k
对应的键值。 调用_G()
函数时,仅参数k
传入空值,表示删除所有记录的k-v
键值对。 当k-v
键值对已经持久化保存,再次调用_G()
函数,传入已经持久化保存的键名作为参数k
, 传入新的键值作为参数v
,会更新该k-v
键值对。
{@fun/Global/DBExec DBExec}
将毫秒时间戳或者Date
对象转换为时间字符串。
时间字符串。 string
_D() _D(timestamp) _D(timestamp, fmt)
毫秒时间戳或者Date
对象。
timestamp
false
number、object
格式化字符串,默认格式:yyyy-MM-dd hh:mm:ss
。
fmt
false
string
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main(){
var time = _D()
Log(time)
}
def main():
strTime = _D()
Log(strTime)
void main() {
auto strTime = _D();
Log(strTime);
}
获取、打印当前时间字符串:
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main() {
Log(_D(1574993606000))
}
def main():
# 北京时间的服务器上运行:2019-11-29 10:13:26 ,另一台其它地区的服务器上的托管者运行此代码结果则为:2019-11-29 02:13:26
Log(_D(1574993606))
void main() {
Log(_D(1574993606000));
}
时间戳为1574993606000,使用代码转换:
function main() {
Log(_D(1574993606000, "yyyy--MM--dd hh--mm--ss")) // 2019--11--29 10--13--26
}
def main():
# 1574993606为秒级别时间戳
Log(_D(1574993606, "%Y--%m--%d %H--%M--%S")) # 2019--11--29 10--13--26
void main() {
Log(_D(1574993606000, "%Y--%m--%d %H--%M--%S")); // 2019--11--29 10--13--26
}
使用参数fmt
格式化,JavaScript
、Python
、C++
语言的格式化字符串有所不同,具体参看以下例子:
不传任何参数就返回当前时间字符串。 Python
策略中使用_D()
函数时,需要注意传入的参数为秒级别时间戳(JavaScript、C++策略中为毫秒级别时间戳,1秒等于1000毫秒)。 在实盘时使用_D()
函数解析一个时间戳为可读的时间字符串时,需要注意托管者程序所在的操作系统的时区、时间设置。 _D()
函数解析一个时间戳为可读时间字符串是根据托管者系统的时间而定的。
{@fun/Global/UnixNano UnixNano}, {@fun/Global/Unix Unix}
格式化一个浮点数。
根据精度设置,格式化后的浮点数。 number
_N() _N(num) _N(num, precision)
需要格式化的浮点数。
num
true
number
格式化的精度设置,参数precision
为整数,参数precision
默认为4。
precision
false
number
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main() {
var i = 3.1415
Log(i)
var ii = _N(i, 2)
Log(ii)
}
def main():
i = 3.1415
Log(i)
ii = _N(i, 2)
Log(ii)
void main() {
auto i = 3.1415;
Log(i);
auto ii = _N(i, 2);
Log(ii);
}
例如_N(3.1415, 2)
将删除3.1415
小数点两位以后的值,函数返回3.14
。
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main() {
var i = 1300
Log(i)
var ii = _N(i, -3)
// 查看日志得知为1000
Log(ii)
}
def main():
i = 1300
Log(i)
ii = _N(i, -3)
Log(ii)
void main() {
auto i = 1300;
Log(i);
auto ii = _N(i, -3);
Log(ii);
}
如果需要将小数点左边的N个位数都变为0,可以这么写:
参数precision
可以为正整数、负整数。
{@fun/Trade/exchange.SetPrecision exchange.SetPrecision}
重试函数,用于接口容错。
回调函数执行时的返回值。 除了逻辑假值、空值以外系统支持的所有类型。
_C(pfn) _C(pfn, …args)
参数pfn
为函数引用,是一个回调函数。
pfn
true
function
回调函数的参数,参数arg
可能有多个。参数arg
的类型与个数根据回调函数的参数而定。
arg
false
string、number、bool、object、array、function、空值等系统支持的所有类型
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 测试代码部分
// 设置合约为rb888即螺纹钢主力连续合约,或者设置股票代码
exchange.SetContractType("rb888")
var ticker = _C(exchange.GetTicker)
// 调整_C()函数重试时间间隔为2秒
_CDelay(2000)
var depth = _C(exchange.GetDepth)
Log(ticker)
Log(depth)
}
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
ticker = _C(exchange.GetTicker)
_CDelay(2000)
depth = _C(exchange.GetDepth)
Log(ticker)
Log(depth)
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto ticker = _C(exchange.GetTicker);
_CDelay(2000);
auto depth = _C(exchange.GetDepth);
Log(ticker);
Log(depth);
}
对于无参数的函数容错:
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 测试代码部分
// 设置合约为rb888即螺纹钢主力连续合约,或者设置股票代码
exchange.SetContractType("rb888")
var records = _C(exchange.GetRecords, PERIOD_D1)
Log(records)
}
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
records = _C(exchange.GetRecords, PERIOD_D1)
Log(records)
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto records = _C(exchange.GetRecords, PERIOD_D1);
Log(records);
}
对于有参数的函数容错:
var test = function(a, b){
var time = new Date().getTime() / 1000
if(time % b == 3){
Log("符合条件!", "#FF0000")
return true
}
Log("重试!", "#FF0000")
return false
}
function main(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试自定义的函数
var ret = _C(test, 1, 5)
Log(ret)
}
import time
def test(a, b):
ts = time.time()
if ts % b == 3:
Log("符合条件!", "#FF0000")
return True
Log("重试!", "#FF0000")
return False
def main():
ret = _C(test, 1, 5)
Log(ret)
// C++ 不支持这种方式对于自定义函数容错
也可以用于自定义函数的容错处理:
_C()
函数会一直调用指定函数直到成功返回(参数pfn
引用的函数调用时返回空值或者假值会重试调用pfn
)。 例如_C(exchange.GetTicker)
。默认重试间隔为3秒,可以调用_CDelay()
函数来设置重试间隔。 例如_CDelay(1000)
,指改变_C()
函数的重试间隔为1秒。
可以但不限于对以下函数做容错处理:
exchange.GetTicker()
exchange.GetDepth()
exchange.GetTrades()
exchange.GetRecords()
exchange.GetAccount()
exchange.GetOrders()
exchange.GetOrder()
exchange.GetPositions()
都可以通过_C()
函数来调用进行容错。_C()
函数不局限于以上列出的函数容错,参数pfn
是函数引用并非函数调用,
注意是_C(exchange.GetTicker)
并不是_C(exchange.GetTicker())
。
返回数组arr1
与数组arr2
的交叉周期数。
数组arr1
与数组arr2
的交叉周期数。
number
_Cross(arr1, arr2)
元素为number
类型的数组。
arr1
true
array
元素为number
类型的数组。
arr2
true
array
// 快线指标
var arr1 = [1,2,3,4,5,6,8,8,9]
// 慢线指标
var arr2 = [2,3,4,5,6,7,7,7,7]
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main(){
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
}
arr1 = [1,2,3,4,5,6,8,8,9]
arr2 = [2,3,4,5,6,7,7,7,7]
def main():
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
void main() {
vector<double> arr1 = {1,2,3,4,5,6,8,8,9};
vector<double> arr2 = {2,3,4,5,6,7,7,7,7};
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2));
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1));
}
可以模拟一组数据测试_Cross(Arr1, Arr2)函数:
_Cross()
函数的返回值是正数表示上穿的周期,负数表示下穿的周期,0指当前价格一样。 具体使用说明:内置函数_Cross分析及使用说明。
函数JSONParse()
用于解析JSON
字符串。
JSON
对象。
object
JSONParse(s)
JSON
字符串。
s
true
string
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
function main() {
let s1 = '{"num": 8754613216564987646512354656874651651358}'
Log("JSON.parse:", JSON.parse(s1)) // JSON.parse: {"num":8.754613216564988e+39}
Log("JSONParse:", JSONParse(s1)) // JSONParse: {"num":"8754613216564987646512354656874651651358"}
let s2 = '{"num": 123}'
Log("JSON.parse:", JSON.parse(s2)) // JSON.parse: {"num":123}
Log("JSONParse:", JSONParse(s2)) // JSONParse: {"num":123}
}
import json
def main():
s1 = '{"num": 8754613216564987646512354656874651651358}'
Log("json.loads:", json.loads(s1)) # json.loads: map[num:8.754613216564987e+39]
Log("JSONParse:", JSONParse(s1)) # JSONParse: map[num:8754613216564987646512354656874651651358]
s2 = '{"num": 123}'
Log("json.loads:", json.loads(s2)) # json.loads: map[num:123]
Log("JSONParse:", JSONParse(s2)) # JSONParse: map[num:123]
void main() {
auto s1 = "{\"num\":8754613216564987646512354656874651651358}";
Log("json::parse:", json::parse(s1));
// Log("JSONParse:", JSONParse(s1)); // 不支持该函数
auto s2 = "{\"num\":123}";
Log("json::parse:", json::parse(s2));
// Log("JSONParse:", JSONParse(s2)); // 不支持该函数
}
可以正确解析包含有较大数值的JSON字符串,会将较大的数值解析为字符串类型。 回测系统中不支持JSONParse()
函数。