输出日志。
Log(…msgs)
参数msg
为输出的内容,参数msg
可以传多个。
msg
false
string、number、bool、object、array、空值等系统支持的任意类型
function main() {
Log("msg1", "msg2", "msg3")
}
def main():
Log("msg1", "msg2", "msg3")
void main() {
Log("msg1", "msg2", "msg3");
}
可以传多个msg
参数:
function main() {
Log("优宽量化你好 !@")
Sleep(1000 * 5)
// 字符串内加入#ff0000,打印日志显示为红色,并且推送消息
Log("你好, #ff0000@")
}
def main():
Log("优宽量化你好 !@")
Sleep(1000 * 5)
Log("你好, #ff0000@")
void main() {
Log("优宽量化你好 !@");
Sleep(1000 * 5);
Log("你好, #ff0000@");
}
支持设置输出消息的颜色,如果设置颜色和推送同时使用,需要先设置颜色,最后使用@
字符设置推送。
function main() {
Log("`data:image/png;base64,AAAA`")
}
def main():
Log("`data:image/png;base64,AAAA`")
void main() {
Log("`data:image/png;base64,AAAA`");
}
Log()
函数支持打印base64
编码后的图片,以`
开头,以`
结尾,例如:
import matplotlib.pyplot as plt
def main():
plt.plot([3,6,2,4,7,1])
Log(plt)
Log()
函数支持直接打印Python
的matplotlib.pyplot
对象, 只要对象包含savefig
方法就可以直接使用Log
函数打印,例如:
function main() {
Log("[trans]中文|abc[/trans]")
}
def main():
Log("[trans]中文|abc[/trans]")
void main() {
Log("[trans]中文|abc[/trans]");
}
Log()
函数支持语言切换,Log()
函数输出文本,会根据平台页面上语言设置自动切换为对应的语言,例如:
Log()
函数在实盘或者回测系统的日志区域输出一条日志信息,实盘运行时日志会保存在实盘的数据库中。 Log()
函数输出的内容以@
字符结尾则这条日志会进入消息推送队列。 推送到当前优宽量化交易平台账号推送设置中配置的邮箱、WebHook地址等。 调试工具、回测系统不支持消息推送。 消息推送有频率限制,具体限制规则如下:在一个实盘20秒周期内,只有最后一条推送消息会被保留并进行推送,其它消息将被过滤,不进行推送(通过 Log 函数输出的推送日志会正常打印显示在日志区域)。
关于WebHook
推送,可以使用Golang
编写的服务程序:
package main
import (
"fmt"
"net/http"
)
func Handle (w http.ResponseWriter, r *http.Request) {
defer func() {
fmt.Println("req:", *r)
}()
}
func main () {
fmt.Println("listen http://localhost:9090")
http.HandleFunc("/data", Handle)
http.ListenAndServe(":9090", nil)
}
在推送设置中设置WebHook
:http://XXX.XX.XXX.XX:9090/data?data=Hello_youquant
,
运行写好的Golang
服务程序后,开始实盘运行策略,以下为JavaScript
语言编写的策略,策略运行时执行Log()
函数并推送消息:
function main() {
Log("msg", "@")
}
Golang
语言编写的服务程序接收到推送,服务程序打印信息:
listen http://localhost:9090
req: {GET /data?data=Hello_youquant HTTP/1.1 1 1 map[User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.x.xxxx.xxx Safari/537.36] Accept-Encoding:[gzip]] {} <nil> 0 [] false 1XX.XX.X.XX:9090 map[] map[] <nil> map[] XXX.XX.XXX.XX:4xxx2 /data?data=Hello_youquant <nil> <nil> <nil> 0xc420056300
{@fun/Log/LogReset LogReset}, {@fun/Log/LogVacuum LogVacuum}
记录盈亏值,打印盈亏数值并根据盈亏数值绘制收益曲线。
LogProfit(profit) LogProfit(profit, …args)
参数profit
为收益数据,该数据由策略中设计算法、计算给出。
profit
true
number
扩展参数,可以输出附带信息到这条收益日志中,arg
参数可以传多个。
arg
false
string、number、bool、object、array、空值等系统支持的任意类型
function main() {
// 在收益图表上打印30个点
for(var i = 0; i < 30; i++) {
LogProfit(i, '&')
Sleep(500)
}
}
def main():
for i in range(30):
LogProfit(i, '&')
Sleep(500)
void main() {
for(int i = 0; i < 30; i++) {
LogProfit(i, '&');
Sleep(500);
}
}
LogProfit
函数如果以字符&
结尾,只绘制收益图表,不打印收益日志。例如:
{@fun/Log/LogProfitReset LogProfitReset}
清空所有收益日志、收益图表。
LogProfitReset() LogProfitReset(remain)
remain
参数用于指定保留的日志条数(整数值)。
remain
false
number
function main() {
// 在收益图表上打印30个点,然后重置,只保留最后10个点
for(var i = 0; i < 30; i++) {
LogProfit(i)
Sleep(500)
}
LogProfitReset(10)
}
def main():
for i in range(30):
LogProfit(i)
Sleep(500)
LogProfitReset(10)
void main() {
for(int i = 0; i < 30; i++) {
LogProfit(i);
Sleep(500);
}
LogProfitReset(10);
}
在收益图表上打印30个点,然后重置,只保留最后10个点。
{@fun/Log/LogProfit LogProfit}
在回测系统或者实盘页面状态栏输出信息。
LogStatus(…msgs)
参数msg
为输出的内容,参数msg
可以传多个。
msg
false
string、number、bool、object、array、空值等系统支持的任意类型
function main() {
LogStatus('这是一个普通的状态提示')
LogStatus('这是一个红色字体的状态提示#ff0000')
LogStatus('这是一个多行的状态信息\n我是第二行')
}
def main():
LogStatus('这是一个普通的状态提示')
LogStatus('这是一个红色字体的状态提示#ff0000')
LogStatus('这是一个多行的状态信息\n我是第二行')
void main() {
LogStatus("这是一个普通的状态提示");
LogStatus("这是一个红色字体的状态提示#ff0000");
LogStatus("这是一个多行的状态信息\n我是第二行");
}
支持设置输出内容的颜色:
function main() {
var table = {type: 'table', title: '持仓信息', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
// JSON序列化后两边加上`字符,视为一个复杂消息格式(当前支持表格)
LogStatus('`' + JSON.stringify(table) + '`')
// 表格信息也可以在多行中出现
LogStatus('第一行消息\n`' + JSON.stringify(table) + '`\n第三行消息')
// 支持多个表格同时显示,将以TAB显示到一组里
LogStatus('`' + JSON.stringify([table, table]) + '`')
// 也可以构造一个按钮在表格中,策略用GetCommand接收cmd属性的内容
var table = {
type: 'table',
title: '持仓操作',
cols: ['列1', '列2', 'Action'],
rows: [
['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': '平仓'}]
]
}
LogStatus('`' + JSON.stringify(table) + '`')
// 或者构造一单独的按钮
LogStatus('`' + JSON.stringify({'type':'button', 'cmd': 'coverAll', 'name': '平仓'}) + '`')
// 可以自定义按钮风格(bootstrap的按钮属性)
LogStatus('`' + JSON.stringify({'type':'button', 'class': 'btn btn-xs btn-danger', 'cmd': 'coverAll', 'name': '平仓'}) + '`')
}
import json
def main():
table = {"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}
LogStatus('`' + json.dumps(table) + '`')
LogStatus('第一行消息\n`' + json.dumps(table) + '`\n第三行消息')
LogStatus('`' + json.dumps([table, table]) + '`')
table = {
"type" : "table",
"title" : "持仓操作",
"cols" : ["列1", "列2", "Action"],
"rows" : [
["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
]
}
LogStatus('`' + json.dumps(table) + '`')
LogStatus('`' + json.dumps({"type": "button", "cmd": "coverAll", "name": "平仓"}) + '`')
LogStatus('`' + json.dumps({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "平仓"}) + '`')
void main() {
json table = R"({"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
LogStatus("`" + table.dump() + "`");
LogStatus("第一行消息\n`" + table.dump() + "`\n第三行消息");
json arr = R"([])"_json;
arr.push_back(table);
arr.push_back(table);
LogStatus("`" + arr.dump() + "`");
table = R"({
"type" : "table",
"title" : "持仓操作",
"cols" : ["列1", "列2", "Action"],
"rows" : [
["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
]
})"_json;
LogStatus("`" + table.dump() + "`");
LogStatus("`" + R"({"type": "button", "cmd": "coverAll", "name": "平仓"})"_json.dump() + "`");
LogStatus("`" + R"({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "平仓"})"_json.dump() + "`");
}
状态栏中数据输出示例:
function main() {
var table = {
type: "table",
title: "状态栏按钮样式",
cols: ["默认", "原始", "成功", "信息", "警告", "危险"],
rows: [
[
{"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
{"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
{"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
{"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
{"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
{"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
]
]
}
LogStatus("`" + JSON.stringify(table) + "`")
}
import json
def main():
table = {
"type": "table",
"title": "状态栏按钮样式",
"cols": ["默认", "原始", "成功", "信息", "警告", "危险"],
"rows": [
[
{"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
{"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
{"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
{"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
{"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
{"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
]
]
}
LogStatus("`" + json.dumps(table) + "`")
void main() {
json table = R"({
"type": "table",
"title": "状态栏按钮样式",
"cols": ["默认", "原始", "成功", "信息", "警告", "危险"],
"rows": [
[
{"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
{"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
{"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
{"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
{"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
{"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
]
]
})"_json;
LogStatus("`" + table.dump() + "`");
}
支持状态栏中设计按钮控件(旧版按钮结构):
function main() {
var table = {
type: "table",
title: "状态栏按钮的禁用、描述功能测试",
cols: ["列1", "列2", "列3"],
rows: []
}
var button1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
var button2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": true}
var button3 = {"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": false}
table.rows.push([button1, button2, button3])
LogStatus("`" + JSON.stringify(table) + "`")
}
import json
def main():
table = {
"type": "table",
"title": "状态栏按钮的禁用、描述功能测试",
"cols": ["列1", "列2", "列3"],
"rows": []
}
button1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
button2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": True}
button3 = {"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": False}
table["rows"].append([button1, button2, button3])
LogStatus("`" + json.dumps(table) + "`")
void main() {
json table = R"({
"type": "table",
"title": "状态栏按钮的禁用、描述功能测试",
"cols": ["列1", "列2", "列3"],
"rows": []
})"_json;
json button1 = R"({"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"})"_json;
json button2 = R"({"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": true})"_json;
json button3 = R"({"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": false})"_json;
json arr = R"([])"_json;
arr.push_back(button1);
arr.push_back(button2);
arr.push_back(button3);
table["rows"].push_back(arr);
LogStatus("`" + table.dump() + "`");
}
设置状态栏按钮的禁用、描述功能(旧版按钮结构):
function test1() {
Log("调用自定义函数")
}
function main() {
while (true) {
var table = {
type: 'table',
title: '操作',
cols: ['列1', '列2', 'Action'],
rows: [
['a', '1', {
'type': 'button',
'cmd': "CoverAll",
'name': '平仓'
}],
['b', '1', {
'type': 'button',
'cmd': 10,
'name': '发送数值'
}],
['c', '1', {
'type': 'button',
'cmd': _D(),
'name': '调用函数'
}],
['d', '1', {
'type': 'button',
'cmd': 'test1',
'name': '调用自定义函数'
}]
]
}
LogStatus(_D(), "\n", '`' + JSON.stringify(table) + '`')
var str_cmd = GetCommand()
if (str_cmd) {
Log("接收到的交互数据 str_cmd:", "类型:", typeof(str_cmd), "值:", str_cmd)
if(str_cmd == "test1") {
test1()
}
}
Sleep(500)
}
}
import json
def test1():
Log("调用自定义函数")
def main():
while True:
table = {
"type": "table",
"title": "操作",
"cols": ["列1", "列2", "Action"],
"rows": [
["a", "1", {
"type": "button",
"cmd": "CoverAll",
"name": "平仓"
}],
["b", "1", {
"type": "button",
"cmd": 10,
"name": "发送数值"
}],
["c", "1", {
"type": "button",
"cmd": _D(),
"name": "调用函数"
}],
["d", "1", {
"type": "button",
"cmd": "test1",
"name": "调用自定义函数"
}]
]
}
LogStatus(_D(), "\n", "`" + json.dumps(table) + "`")
str_cmd = GetCommand()
if str_cmd:
Log("接收到的交互数据 str_cmd", "类型:", type(str_cmd), "值:", str_cmd)
if str_cmd == "test1":
test1()
Sleep(500)
void test1() {
Log("调用自定义函数");
}
void main() {
while(true) {
json table = R"({
"type": "table",
"title": "操作",
"cols": ["列1", "列2", "Action"],
"rows": [
["a", "1", {
"type": "button",
"cmd": "CoverAll",
"name": "平仓"
}],
["b", "1", {
"type": "button",
"cmd": 10,
"name": "发送数值"
}],
["c", "1", {
"type": "button",
"cmd": "",
"name": "调用函数"
}],
["d", "1", {
"type": "button",
"cmd": "test1",
"name": "调用自定义函数"
}]
]
})"_json;
table["rows"][2][2]["cmd"] = _D();
LogStatus(_D(), "\n", "`" + table.dump() + "`");
auto str_cmd = GetCommand();
if(str_cmd != "") {
Log("接收到的交互数据 str_cmd", "类型:", typeid(str_cmd).name(), "值:", str_cmd);
if(str_cmd == "test1") {
test1();
}
}
Sleep(500);
}
}
结合GetCommand()
函数,构造状态栏按钮交互功能(旧版按钮结构):
function main() {
var tbl = {
type: "table",
title: "操作",
cols: ["列1", "列2"],
rows: [
["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
]
}
LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")
while (true) {
var cmd = GetCommand()
if (cmd) {
Log("cmd:", cmd)
}
Sleep(1000)
}
}
import json
def main():
tbl = {
"type": "table",
"title": "操作",
"cols": ["列1", "列2"],
"rows": [
["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
]
}
LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`")
while True:
cmd = GetCommand()
if cmd:
Log("cmd:", cmd)
Sleep(1000)
void main() {
json tbl = R"({
"type": "table",
"title": "操作",
"cols": ["列1", "列2"],
"rows": [
["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
]
})"_json;
LogStatus(_D(), "\n", "`" + tbl.dump() + "`");
while(true) {
auto cmd = GetCommand();
if(cmd != "") {
Log("cmd:", cmd);
}
Sleep(1000);
}
}
在构造状态栏按钮进行交互时也支持输入数据,交互指令最终由GetCommand()
函数捕获。 给状态栏中的按钮控件的数据结构中增加input
项(旧版按钮结构),例如给{"type": "button", "cmd": "open", "name": "开仓"}
增加: "input": {"name": "开仓数量", "type": "number", "defValue": 1}
, 就可以使按钮在被点击时弹出一个带输入框控件的弹窗(输入框中默认值为1,即defValue
设置的数据), 可以输入一个数据和按钮命令一起发送。例如以下测试代码运行时,在点击「开仓」按钮后,弹出一个带输入框的弹窗, 在输入框中输入111后点击「确定」。GetCommand()
函数就会捕获消息:open:111
。
function main() {
var tbl = {
type: "table",
title: "演示分组按钮控件",
cols: ["操作"],
rows: []
}
// 创建分组按钮控件结构
var groupBtn = {
type: "button",
cmd: "open",
name: "开仓",
group: [
{"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"},
{"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100},
{"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100},
{"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": true}
]
}
// 测试按钮1
var testBtn1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
var testBtn2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}
// 在tbl中添加groupBtn
tbl.rows.push([groupBtn])
// 支持状态栏表格的一个单元格内设置多个按钮,即一个单元格内的数据为一个按钮结构数组:[testBtn1, testBtn2]
tbl.rows.push([[testBtn1, testBtn2]])
while (true) {
LogStatus("`" + JSON.stringify(tbl) + "`", "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", "`" + JSON.stringify(groupBtn) + "`")
var cmd = GetCommand()
if (cmd) {
Log("cmd:", cmd)
}
Sleep(5000)
}
}
import json
def main():
tbl = {
"type": "table",
"title": "演示分组按钮控件",
"cols": ["操作"],
"rows": []
}
groupBtn = {
"type": "button",
"cmd": "open",
"name": "开仓",
"group": [
{"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"},
{"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100},
{"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100},
{"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": True}
]
}
testBtn1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
testBtn2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}
tbl["rows"].append([groupBtn])
tbl["rows"].append([[testBtn1, testBtn2]])
while True:
LogStatus("`" + json.dumps(tbl) + "`", "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", "`" + json.dumps(groupBtn) + "`")
cmd = GetCommand()
if cmd:
Log("cmd:", cmd)
Sleep(5000)
void main() {
json tbl = R"({
"type": "table",
"title": "演示分组按钮控件",
"cols": ["操作"],
"rows": []
})"_json;
json groupBtn = R"({
"type": "button",
"name": "开仓",
"cmd": "open",
"group": [
{"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"},
{"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100},
{"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100},
{"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": true}
]})"_json;
json testBtn1 = R"({"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"})"_json;
json testBtn2 = R"({"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}})"_json;
tbl["rows"].push_back({groupBtn});
tbl["rows"].push_back({{testBtn1, testBtn2}});
while(true) {
LogStatus("`" + tbl.dump() + "`", "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", "`" + groupBtn.dump() + "`");
auto cmd = GetCommand();
if(cmd != "") {
Log("cmd:", cmd);
}
Sleep(5000);
}
}
支持分组按钮控件(旧版按钮结构),功能与支持输入数据的状态栏按钮(使用"input"字段设置)一致。交互指令最终由GetCommand()
函数捕获。区别在于使用"group"
字段设置,当点击按钮触发交互时,页面弹出的对话框中有设置好的一组输入控件,可以一次性输入一组数据。
关于状态栏按钮控件和分组按钮控件结构中"group"
字段,需要注意的几点:
type
属性仅支持以下4种类型,defValue
属性为默认值。
“selected”:下拉框控件,在设置下拉框中每个选项时使用|
符号分隔。
“number”:数值输入框控件。
“string”:字符串输入框控件。
“boolean”:勾选框控件,勾选为(布尔值)真,不勾选为(布尔值)假。"name": "tradePrice@orderType==1"
设置,该设置使交易价格(tradePrice
)输入控件仅当下单方式(orderType)下拉框控件选择为挂单时可用。|
符号分隔中英文描述内容。name
、description
与按钮结构中的name
、description
虽然字段名一致,但是定义并不相同。
group中的name
与input中的name
定义也不同。Log("cmd:", cmd)
语句输出的内容:
cmd: open:{"orderType":1,"tradePrice":99,"orderAmount":"99","boolean":true}
,即发生交互操作时GetCommand()
函数返回的内容:open:{"orderType":1,"tradePrice":99,"orderAmount":"99","boolean":true}
。type
属性仅支持:"button"
。
支持输入数据的按钮控件,即设置了input
属性的控件,input
字段的配置信息中的type
属性支持多种控件类型。
参考以下例子:function main() {
// 状态栏按钮控件(设置input字段实现)testBtn1按钮触发的页面中的下拉框控件使用options字段设置选项,使用defValue字段设置默认选项。区别于本章其它例子中直接使用defValue设置选项。
var testBtn1 = {
type: "button",
name: "testBtn1",
cmd: "cmdTestBtn1",
input: {name: "testBtn1ComboBox", type: "selected", options: ["A", "B"], defValue: 1}
}
/*
状态栏按钮控件(设置input字段实现)testBtn2按钮触发的页面中的下拉框控件使用options字段设置选项,options字段中的选项不仅支持字符串,
也支持使用```{text: "描述", value: "值"}```结构。使用defValue字段设置默认选项,默认选项可以是多选(通过数组结构实现多选)。多选需要设置额外的字段multiple为真值(true)。
*/
var testBtn2 = {
type: "button",
name: "testBtn2",
cmd: "cmdTestBtn2",
input: {
name: "testBtn2MultiComboBox",
type: "selected",
description: "实现下拉框多选",
options: [{text: "选项A", value: "A"}, {text: "选项B", value: "B"}, {text: "选项C", value: "C"}],
defValue: ["A", "C"],
multiple: true
}
}
// 状态栏分组按钮控件(设置group字段实现)testBtn3按钮触发的页面中的下拉框控件使用options字段设置选项,也支持直接使用defValue设置选项。
var testBtn3 = {
type: "button",
name: "testBtn3",
cmd: "cmdTestBtn3",
group: [
{name: "comboBox1", label: "labelComboBox1", description: "下拉框1", type: "selected", defValue: 1, options: ["A", "B"]},
{name: "comboBox2", label: "labelComboBox2", description: "下拉框2", type: "selected", defValue: "A|B"},
{name: "comboBox3", label: "labelComboBox3", description: "下拉框3", type: "selected", defValue: [0, 2], multiple: true, options: ["A", "B", "C"]},
{
name: "comboBox4",
label: "labelComboBox4",
description: "下拉框4",
type: "selected",
defValue: ["A", "C"],
multiple: true,
options: [{text: "选项A", value: "A"}, {text: "选项B", value: "B"}, {text: "选项C", value: "C"}, {text: "选项D", value: "D"}]
}
]
}
while (true) {
LogStatus("`" + JSON.stringify(testBtn1) + "`\n", "`" + JSON.stringify(testBtn2) + "`\n", "`" + JSON.stringify(testBtn3) + "`\n")
var cmd = GetCommand()
if (cmd) {
Log(cmd)
}
Sleep(5000)
}
}
import json
def main():
testBtn1 = {
"type": "button",
"name": "testBtn1",
"cmd": "cmdTestBtn1",
"input": {"name": "testBtn1ComboBox", "type": "selected", "options": ["A", "B"], "defValue": 1}
}
testBtn2 = {
"type": "button",
"name": "testBtn2",
"cmd": "cmdTestBtn2",
"input": {
"name": "testBtn2MultiComboBox",
"type": "selected",
"description": "实现下拉框多选",
"options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}],
"defValue": ["A", "C"],
"multiple": True
}
}
testBtn3 = {
"type": "button",
"name": "testBtn3",
"cmd": "cmdTestBtn3",
"group": [
{"name": "comboBox1", "label": "labelComboBox1", "description": "下拉框1", "type": "selected", "defValue": 1, "options": ["A", "B"]},
{"name": "comboBox2", "label": "labelComboBox2", "description": "下拉框2", "type": "selected", "defValue": "A|B"},
{"name": "comboBox3", "label": "labelComboBox3", "description": "下拉框3", "type": "selected", "defValue": [0, 2], "multiple": True, "options": ["A", "B", "C"]},
{
"name": "comboBox4",
"label": "labelComboBox4",
"description": "下拉框4",
"type": "selected",
"defValue": ["A", "C"],
"multiple": True,
"options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}, {"text": "选项D", "value": "D"}]
}
]
}
while True:
LogStatus("`" + json.dumps(testBtn1) + "`\n", "`" + json.dumps(testBtn2) + "`\n", "`" + json.dumps(testBtn3) + "`\n")
cmd = GetCommand()
if cmd:
Log(cmd)
Sleep(5000)
void main() {
json testBtn1 = R"({
"type": "button",
"name": "testBtn1",
"cmd": "cmdTestBtn1",
"input": {"name": "testBtn1ComboBox", "type": "selected", "options": ["A", "B"], "defValue": 1}
})"_json;
json testBtn2 = R"({
"type": "button",
"name": "testBtn2",
"cmd": "cmdTestBtn2",
"input": {
"name": "testBtn2MultiComboBox",
"type": "selected",
"description": "实现下拉框多选",
"options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}],
"defValue": ["A", "C"],
"multiple": true
}
})"_json;
json testBtn3 = R"({
"type": "button",
"name": "testBtn3",
"cmd": "cmdTestBtn3",
"group": [
{"name": "comboBox1", "label": "labelComboBox1", "description": "下拉框1", "type": "selected", "defValue": 1, "options": ["A", "B"]},
{"name": "comboBox2", "label": "labelComboBox2", "description": "下拉框2", "type": "selected", "defValue": "A|B"},
{"name": "comboBox3", "label": "labelComboBox3", "description": "下拉框3", "type": "selected", "defValue": [0, 2], "multiple": true, "options": ["A", "B", "C"]},
{
"name": "comboBox4",
"label": "labelComboBox4",
"description": "下拉框4",
"type": "selected",
"defValue": ["A", "C"],
"multiple": true,
"options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}, {"text": "选项D", "value": "D"}]
}
]
})"_json;
while (true) {
LogStatus("`" + testBtn1.dump() + "`\n", "`" + testBtn2.dump() + "`\n", "`" + testBtn3.dump() + "`\n");
auto cmd = GetCommand();
if (cmd != "") {
Log(cmd);
}
Sleep(5000);
}
}
状态栏分组按钮控件(设置group
字段实现)与状态栏按钮控件(设置input
字段实现)被点击触发交互时(旧版按钮结构),页面弹出的对话框中的下拉框控件也支持多选,以下例子演示如何设计包含多选选项的下拉框控件:
var symbols = ["rb2410", "MA501", "hc2501", "i2501", "TA888"]
function createBtn(tmp, group) {
var btn = JSON.parse(JSON.stringify(tmp))
_.each(group, function(eleByGroup) {
btn["group"].unshift(eleByGroup)
})
return btn
}
function main() {
var arrManager = []
_.each(symbols, function(symbol) {
arrManager.push({
"symbol": symbol,
})
})
// Btn
var tmpBtnOpen = {
"type": "button",
"cmd": "open",
"name": "开仓下单",
"group": [{
"type": "selected",
"name": "tradeType",
"label": "下单类型",
"description": "市价单、限价单",
"default": 0,
"group": "交易设置",
"settings": {
"options": ["市价单", "限价单"],
"required": true,
}
}, {
"type": "selected",
"name": "direction",
"label": "交易方向",
"description": "买入、卖出",
"default": "buy",
"group": "交易设置",
"settings": {
"render": "segment",
"required": true,
"options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}],
}
}, {
"type": "number",
"name": "price",
"label": "价格",
"description": "订单的价格",
"group": "交易设置",
"filter": "tradeType==1",
"settings": {
"required": true,
}
}, {
"type": "number",
"name": "amount",
"label": "下单量",
"description": "订单的下单量",
"group": "交易设置",
"settings": {
"required": true,
}
}],
}
while (true) {
var tbl = {"type": "table", "title": "dashboard", "cols": ["symbol", "actionOpen"], "rows": []}
_.each(arrManager, function(m) {
var btnOpen = createBtn(tmpBtnOpen, [{"type": "string", "name": "symbol", "label": "交易品种", "default": m["symbol"], "settings": {"required": true}}])
tbl["rows"].push([m["symbol"], btnOpen])
})
var cmd = GetCommand()
if (cmd) {
Log("收到交互:", cmd)
// 解析交互消息: open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111}
// 根据第一个冒号:之前的指令判断是哪种按钮模板触发的消息
var arrCmd = cmd.split(":", 2)
if (arrCmd[0] == "open") {
var msg = JSON.parse(cmd.slice(5))
Log("交易品种:", msg["symbol"], ",交易方向:", msg["direction"], ",订单类型:", msg["tradeType"] == 0 ? "市价单" : "限价单", msg["tradeType"] == 0 ? ",订单价格:当前市价" : ",订单价格:" + msg["price"], ",订单数量:", msg["amount"])
}
}
LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")
Sleep(1000)
}
}
import json
symbols = ["rb2410", "MA501", "hc2501", "i2501", "TA888"]
def createBtn(tmp, group):
btn = json.loads(json.dumps(tmp))
for eleByGroup in group:
btn["group"].insert(0, eleByGroup)
return btn
def main():
arrManager = []
for symbol in symbols:
arrManager.append({"symbol": symbol})
# Btn
tmpBtnOpen = {
"type": "button",
"cmd": "open",
"name": "开仓下单",
"group": [{
"type": "selected",
"name": "tradeType",
"label": "下单类型",
"description": "市价单、限价单",
"default": 0,
"group": "交易设置",
"settings": {
"options": ["市价单", "限价单"],
"required": True,
}
}, {
"type": "selected",
"name": "direction",
"label": "交易方向",
"description": "买入、卖出",
"default": "buy",
"group": "交易设置",
"settings": {
"render": "segment",
"required": True,
"options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}],
}
}, {
"type": "number",
"name": "price",
"label": "价格",
"description": "订单的价格",
"group": "交易设置",
"filter": "tradeType==1",
"settings": {
"required": True,
}
}, {
"type": "number",
"name": "amount",
"label": "下单量",
"description": "订单的下单量",
"group": "交易设置",
"settings": {
"required": True,
}
}],
}
while True:
tbl = {"type": "table", "title": "dashboard", "cols": ["symbol", "actionOpen"], "rows": []}
for m in arrManager:
btnOpen = createBtn(tmpBtnOpen, [{"type": "string", "name": "symbol", "label": "交易品种", "default": m["symbol"], "settings": {"required": True}}])
tbl["rows"].append([m["symbol"], btnOpen])
cmd = GetCommand()
if cmd != "" and cmd != None:
Log("收到交互:", cmd)
# 解析交互消息: open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111}
# 根据第一个冒号:之前的指令判断是哪种按钮模板触发的消息
arrCmd = cmd.split(":")
if arrCmd[0] == "open":
msg = json.loads(cmd[5:])
Log("交易品种:", msg["symbol"], ",交易方向:", msg["direction"], ",订单类型:", "市价单" if msg["tradeType"] == 0 else "限价单", ",订单价格:当前市价" if msg["tradeType"] == 0 else ",订单价格:" + str(msg["price"]), ",订单数量:", msg["amount"])
# 输出状态栏信息
LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`")
Sleep(1000)
// 略...
使用当前最新的按钮结构,构造状态栏表格中的按钮,点击按钮触发交互时弹出一个多控件弹窗。 详细内容可以参考:用户指南-状态栏中的交互控件。
function main() {
var table = {
type: 'table',
title: '持仓操作',
cols: ['列1', '列2', 'Action'],
rows: [
['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': '平仓'}]
]
}
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var ticker = exchange.GetTicker()
// 添加一行数据,第一个和第二个单元格合并,并且输出ticker变量在合并后的单元格内
table.rows.push([{body : JSON.stringify(ticker), colspan : 2}, "abc"])
LogStatus('`' + JSON.stringify(table) + '`')
}
import json
def main():
table = {
"type" : "table",
"title" : "持仓操作",
"cols" : ["列1", "列2", "Action"],
"rows" : [
["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
]
}
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
ticker = exchange.GetTicker()
table["rows"].append([{"body": json.dumps(ticker), "colspan": 2}, "abc"])
LogStatus("`" + json.dumps(table) + "`")
void main() {
json table = R"({
"type" : "table",
"title" : "持仓操作",
"cols" : ["列1", "列2", "Action"],
"rows" : [
["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
]
})"_json;
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto ticker = exchange.GetTicker();
json jsonTicker = R"({"Buy": 0, "Sell": 0, "High": 0, "Low": 0, "Volume": 0, "Last": 0, "Time": 0})"_json;
jsonTicker["Buy"] = ticker.Buy;
jsonTicker["Sell"] = ticker.Sell;
jsonTicker["Last"] = ticker.Last;
jsonTicker["Volume"] = ticker.Volume;
jsonTicker["Time"] = ticker.Time;
jsonTicker["High"] = ticker.High;
jsonTicker["Low"] = ticker.Low;
json arr = R"([{"body": {}, "colspan": 2}, "abc"])"_json;
arr[0]["body"] = jsonTicker;
table["rows"].push_back(arr);
LogStatus("`" + table.dump() + "`");
}
横向合并LogStatus()
函数画出的表格内的单元格:
function main() {
var table = {
type: 'table',
title: '表格演示',
cols: ['列A', '列B', '列C'],
rows: [
['A1', 'B1', {'type':'button', 'cmd': 'coverAll', 'name': 'C1'}]
]
}
// 鉴于测试代码,不使用商品期货策略一般架构
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var ticker = exchange.GetTicker()
var name = exchange.GetName()
table.rows.push([{body : "A2 + B2:" + JSON.stringify(ticker), colspan : 2}, "C2"])
table.rows.push([{body : "A3 + A4 + A5:" + name, rowspan : 3}, "B3", "C3"])
// A3被上一行第一个单元格合并
table.rows.push(["B4", "C4"])
// A2被上一行第一个单元格合并
table.rows.push(["B5", "C5"])
table.rows.push(["A6", "B6", "C6"])
LogStatus('`' + JSON.stringify(table) + '`')
}
import json
def main():
table = {
"type" : "table",
"title" : "表格演示",
"cols" : ["列A", "列B", "列C"],
"rows" : [
["A1", "B1", {"type": "button", "cmd": "coverAll", "name": "C1"}]
]
}
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
ticker = exchange.GetTicker()
name = exchange.GetName()
table["rows"].append([{"body": "A2 + B2:" + json.dumps(ticker), "colspan": 2}, "C2"])
table["rows"].append([{"body": "A3 + A4 + A5:" + name, "rowspan": 3}, "B3", "C3"])
table["rows"].append(["B4", "C4"])
table["rows"].append(["B5", "C5"])
table["rows"].append(["A6", "B6", "C6"])
LogStatus("`" + json.dumps(table) + "`")
void main() {
json table = R"({
"type" : "table",
"title" : "表格演示",
"cols" : ["列A", "列B", "列C"],
"rows" : [
["A1", "B1", {"type": "button", "cmd": "coverAll", "name": "C1"}]
]
})"_json;
// 为了测试,代码简短易读,这里使用构造的数据
json jsonTicker = R"({"High": 0, "Low": 0, "Buy": 0, "Sell": 0, "Last": 0, "Time": 0, "Volume": 0})"_json;
auto name = exchange.GetName();
json arr1 = R"([{"body": "", "colspan": 2}, "C2"])"_json;
arr1[0]["body"] = "A2 + B2:" + jsonTicker.dump();
json arr2 = R"([{"body": "", "rowspan": 3}, "B3", "C3"])"_json;
arr2[0]["body"] = "A3 + A4 + A5:" + name;
table["rows"].push_back(arr1);
table["rows"].push_back(arr2);
table["rows"].push_back(R"(["B4", "C4"])"_json);
table["rows"].push_back(R"(["B5", "C5"])"_json);
table["rows"].push_back(R"(["A6", "B6", "C6"])"_json);
LogStatus("`" + table.dump() + "`");
}
纵向合并LogStatus()
函数画出的表格内的单元格:
function main() {
var table1 = {type: 'table', title: 'table1', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
var table2 = {type: 'table', title: 'table2', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
LogStatus('`' + JSON.stringify([table1, table2]) + '`')
}
import json
def main():
table1 = {"type": "table", "title": "table1", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]}
table2 = {"type": "table", "title": "table2", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]}
LogStatus("`" + json.dumps([table1, table2]) + "`")
void main() {
json table1 = R"({"type": "table", "title": "table1", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
json table2 = R"({"type": "table", "title": "table2", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
json arr = R"([])"_json;
arr.push_back(table1);
arr.push_back(table2);
LogStatus("`" + arr.dump() + "`");
}
状态栏表格分页显示:
function main(){
var tab1 = {
type : "table",
title : "表格1",
cols : ["1", "2"],
rows : []
}
var tab2 = {
type : "table",
title : "表格2",
cols : ["1", "2", "3"],
rows : []
}
var tab3 = {
type : "table",
title : "表格3",
cols : ["A", "B", "C"],
rows : []
}
tab1.rows.push(["jack", "lucy"])
tab2.rows.push(["A", "B", "C"])
tab3.rows.push(["A", "B", "C"])
LogStatus('`' + JSON.stringify(tab1) + '`\n' +
'`' + JSON.stringify(tab2) + '`\n' +
'`' + JSON.stringify(tab3) + '`')
Log("exit")
}
import json
def main():
tab1 = {
"type": "table",
"title": "表格1",
"cols": ["1", "2"],
"rows": []
}
tab2 = {
"type": "table",
"title": "表格2",
"cols": ["1", "2", "3"],
"rows": []
}
tab3 = {
"type": "table",
"title": "表格3",
"cols": ["A", "B", "C"],
"rows": []
}
tab1["rows"].append(["jack", "lucy"])
tab2["rows"].append(["A", "B", "C"])
tab3["rows"].append(["A", "B", "C"])
LogStatus("`" + json.dumps(tab1) + "`\n" +
"`" + json.dumps(tab2) + "`\n" +
"`" + json.dumps(tab3) + "`")
void main() {
json tab1 = R"({
"type": "table",
"title": "表格1",
"cols": ["1", "2"],
"rows": []
})"_json;
json tab2 = R"({
"type": "table",
"title": "表格2",
"cols": ["1", "2", "3"],
"rows": []
})"_json;
json tab3 = R"({
"type": "table",
"title": "表格3",
"cols": ["A", "B", "C"],
"rows": []
})"_json;
tab1["rows"].push_back(R"(["jack", "lucy"])"_json);
tab2["rows"].push_back(R"(["A", "B", "C"])"_json);
tab3["rows"].push_back(R"(["A", "B", "C"])"_json);
LogStatus("`" + tab1.dump() + "`\n" +
"`" + tab2.dump() + "`\n" +
"`" + tab3.dump() + "`");
}
除了可以分页显示表格,也可以多个表格自上而下排列显示:
function main() {
var tbl = {
type : "table",
title : "test scroll",
scroll : "auto",
cols : ["col 0", "col 1", "col 2", "col 3", "col 4", "col 5", "col 6", "col 7", "col 8", "col 9", "col 10",
"col 11", "col 12", "col 13", "col 14", "col 15", "col 16", "col 17", "col 18", "col 19", "col 20"],
rows : []
}
for (var i = 1 ; i < 100 ; i++) {
tbl.rows.push([i, "1," + i, "2," + i, "3," + i, "4," + i, "5," + i, "6," + i, "7," + i, "8," + i, "9," + i, "10," + i,
"11," + i, "12," + i, "13," + i, "14," + i, "15," + i, "16," + i, "17," + i, "18," + i, "19," + i, "20," + i])
}
LogStatus("`" + JSON.stringify(tbl) + "`")
}
import json
def main():
tbl = {
"type" : "table",
"title" : "test scroll",
"scroll" : "auto",
"cols" : ["col 0", "col 1", "col 2", "col 3", "col 4", "col 5", "col 6", "col 7", "col 8", "col 9", "col 10",
"col 11", "col 12", "col 13", "col 14", "col 15", "col 16", "col 17", "col 18", "col 19", "col 20"],
"rows" : []
}
for index in range(1, 100):
i = str(index)
tbl["rows"].append([i, "1," + i, "2," + i, "3," + i, "4," + i, "5," + i, "6," + i, "7," + i, "8," + i, "9," + i, "10," + i,
"11," + i, "12," + i, "13," + i, "14," + i, "15," + i, "16," + i, "17," + i, "18," + i, "19," + i, "20," + i])
LogStatus("`" + json.dumps(tbl) + "`")
void main() {
json table = R"({
"type" : "table",
"title" : "test scroll",
"scroll" : "auto",
"cols" : ["col 0", "col 1", "col 2", "col 3", "col 4", "col 5", "col 6", "col 7", "col 8", "col 9", "col 10",
"col 11", "col 12", "col 13", "col 14", "col 15", "col 16", "col 17", "col 18", "col 19", "col 20"],
"rows" : []
})"_json;
for (int index = 1; index < 100; ++index) {
std::string i = std::to_string(index);
table["rows"].push_back({i, "1," + i, "2," + i, "3," + i, "4," + i, "5," + i, "6," + i, "7," + i, "8," + i, "9," + i, "10," + i,
"11," + i, "12," + i, "13," + i, "14," + i, "15," + i, "16," + i, "17," + i, "18," + i, "19," + i, "20," + i});
}
LogStatus("`" + table.dump() + "`");
}
支持设置状态栏表格横向、纵向滚动模式。设置scroll
属性为"auto"
,当状态栏表格纵向的行数超过20行时内容进行滚动显示, 当横向的列数超出页面显示范围时进行横向滚动显示,使用scroll
属性可以缓解实盘时状态栏中大量写入数据的卡顿问题。参考以下测试例子:
实盘运行时LogStatus()
函数输出的信息不保存到实盘数据库,只更新当前实盘的状态栏内容。
LogStatus()
函数支持打印base64
编码后的图片,以`
开头,以`
结尾。例如: LogStatus("`data:image/png;base64,AAAA`")
。
LogStatus()
函数支持直接传入Python
的matplotlib.pyplot
对象,只要对象包含savefig
方法就可以作为参数传入LogStatus()
函数,例如:
import matplotlib.pyplot as plt
def main():
plt.plot([3,6,2,4,7,1])
LogStatus(plt)
策略实盘运行时,在实盘页面如果翻看历史记录,状态栏会进入休眠状态,停止更新。只有日志在第一页的时候状态栏数据才会刷新。 支持在状态栏输出base64
编码后的图片,也支持在状态栏显示的表格中输出base64
编码后的图片。 由于编码后的图片的字符串数据一般很长,所以不再展示范例代码。
{@fun/Global/GetCommand GetCommand}
打开或者关闭订单信息的日志记录。
EnableLog(enable)
enable
参数设置为假值,例如false
,则不打印订单日志(即exchange.Buy()
等函数产生的日志),不写入实盘的数据库。
enable
true
bool
function main() {
EnableLog(false)
}
def main():
EnableLog(False)
void main() {
EnableLog(false);
}
关闭订单信息的日志记录。
{@fun/Trade/exchange.Buy exchange.Buy}, {@fun/Trade/exchange.Sell exchange.Sell}, {@fun/Trade/exchange.CancelOrder exchange.CancelOrder}
自定义图表画图函数。
图表对象。 object
Chart(options)
options
参数为图表配置。 Chart()
函数的参数options
是可以JSON
序列化的HighStocks
的Highcharts.StockChart
参数。 比原生的参数增加一个__isStock
属性,如果指定__isStock:false
,则显示为普通图表。 如果设置__isStock
属性为假值例如:false
,即使用的图表为Highcharts
图表。 如果设置__isStock
属性为真值例如:true
,即使用的图表为Highstocks
图表(默认__isStock
为真值,例如true
)。 可以查询HighStocks图表库。
options
true
object、object数组
function main() {
var cfgA = {
extension: {
layout: 'single', // 不参于分组,单独显示, 默认为分组 'group'
height: 300, // 指定高度
},
title: {
text: '盘口图表'
},
xAxis: {
type: 'datetime'
},
series: [{
name: '买一',
data: [],
}, {
name: '卖一',
data: [],
}]
}
var cfgB = {
title: {
text: '差价图'
},
xAxis: {
type: 'datetime'
},
series: [{
name: '差价',
type: 'column',
data: [],
}]
}
var cfgC = {
__isStock: false,
title: {
text: '饼图'
},
series: [{
type: 'pie',
name: 'one',
data: [
["A", 25],
["B", 25],
["C", 25],
["D", 25],
] // 指定初始数据后不需要用add函数更新, 直接更改图表配置就可以更新序列.
}]
};
var cfgD = {
extension: {
layout: 'single',
col: 8, // 指定宽度占的单元值, 总值 为12
height: '300px',
},
title: {
text: '盘口图表'
},
xAxis: {
type: 'datetime'
},
series: [{
name: '买一',
data: [],
}, {
name: '卖一',
data: [],
}]
}
var cfgE = {
__isStock: false,
extension: {
layout: 'single',
col: 4,
height: '300px',
},
title: {
text: '饼图2'
},
series: [{
type: 'pie',
name: 'one',
data: [
["A", 25],
["B", 25],
["C", 25],
["D", 25],
]
}]
};
var chart = Chart([cfgA, cfgB, cfgC, cfgD, cfgE]);
chart.reset()
// 为饼图清加一个数点,add只能更新通过add方式添加的数据点, 内置的数据点无法后期更新
chart.add(3, {
name: "ZZ",
y: Math.random() * 100
});
while (true) {
Sleep(1000)
exchange.SetContractType("rb888")
var ticker = exchange.GetTicker()
if (!ticker) {
continue;
}
var diff = ticker.Sell - ticker.Buy
cfgA.subtitle = {
text: '买一 ' + ticker.Buy + ', 卖一 ' + ticker.Sell,
};
cfgB.subtitle = {
text: '价差 ' + diff,
};
chart.add([0, [new Date().getTime(), ticker.Buy]]);
chart.add([1, [new Date().getTime(), ticker.Sell]]);
// 相当于更新第二个图表的第一个数据序列
chart.add([2, [new Date().getTime(), diff]]);
chart.add(4, [new Date().getTime(), ticker.Buy]);
chart.add(5, [new Date().getTime(), ticker.Buy]);
cfgC.series[0].data[0][1] = Math.random() * 100;
cfgE.series[0].data[0][1] = Math.random() * 100;
// update实际上等于重置了图表的配置
chart.update([cfgA, cfgB, cfgC, cfgD, cfgE]);
}
}
import random
import time
def main():
cfgA = {
"extension" : {
"layout" : "single",
"height" : 300,
"col" : 8
},
"title" : {
"text" : "盘口图表"
},
"xAxis" : {
"type" : "datetime"
},
"series" : [{
"name" : "买一",
"data" : []
}, {
"name" : "卖一",
"data" : []
}]
}
cfgB = {
"title" : {
"text" : "差价图"
},
"xAxis" : {
"type" : "datetime",
},
"series" : [{
"name" : "差价",
"type" : "column",
"data" : []
}]
}
cfgC = {
"__isStock" : False,
"title" : {
"text" : "饼图"
},
"series" : [{
"type" : "pie",
"name" : "one",
"data" : [
["A", 25],
["B", 25],
["C", 25],
["D", 25],
]
}]
}
cfgD = {
"extension" : {
"layout" : "single",
"col" : 8,
"height" : "300px"
},
"title" : {
"text" : "盘口图表"
},
"series" : [{
"name" : "买一",
"data" : []
}, {
"name" : "卖一",
"data" : []
}]
}
cfgE = {
"__isStock" : False,
"extension" : {
"layout" : "single",
"col" : 4,
"height" : "300px"
},
"title" : {
"text" : "饼图2"
},
"series" : [{
"type" : "pie",
"name" : "one",
"data" : [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
}
chart = Chart([cfgA, cfgB, cfgC, cfgD, cfgE])
chart.reset()
chart.add(3, {
"name" : "ZZ",
"y" : random.random() * 100
})
while True:
Sleep(1000)
exchange.SetContractType("rb888")
ticker = exchange.GetTicker()
if not ticker :
continue
diff = ticker["Sell"] - ticker["Buy"]
cfgA["subtitle"] = {
"text" : "买一" + str(ticker["Buy"]) + "卖一" + str(ticker["Sell"])
}
cfgB["subtitle"] = {
"text" : "价差 " + str(diff)
}
chart.add(0, [time.time() * 1000, ticker["Buy"]])
chart.add(1, [time.time() * 1000, ticker["Sell"]])
chart.add(2, [time.time() * 1000, diff])
chart.add(4, [time.time() * 1000, ticker["Buy"]])
chart.add(5, [time.time() * 1000, ticker["Buy"]])
cfgC["series"][0]["data"][0][1] = random.random() * 100
cfgE["series"][0]["data"][0][1] = random.random() * 100
void main() {
json cfgA = R"({
"extension" : {
"layout" : "single",
"height" : 300,
"col" : 8
},
"title" : {
"text" : "盘口图表"
},
"xAxis" : {
"type" : "datetime"
},
"series" : [{
"name" : "买一",
"data" : []
}, {
"name" : "卖一",
"data" : []
}]
})"_json;
json cfgB = R"({
"title" : {
"text" : "差价图"
},
"xAxis" : {
"type" : "datetime"
},
"series" : [{
"name" : "差价",
"type" : "column",
"data" : []
}]
})"_json;
json cfgC = R"({
"__isStock" : false,
"title" : {
"text" : "饼图"
},
"series" : [{
"type" : "pie",
"name" : "one",
"data" : [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
})"_json;
json cfgD = R"({
"extension" : {
"layout" : "single",
"col" : 8,
"height" : "300px"
},
"title" : {
"text" : "盘口图表"
},
"series" : [{
"name" : "买一",
"data" : []
}, {
"name" : "卖一",
"data" : []
}]
})"_json;
json cfgE = R"({
"__isStock" : false,
"extension" : {
"layout" : "single",
"col" : 4,
"height" : "300px"
},
"title" : {
"text" : "饼图2"
},
"series" : [{
"type" : "pie",
"name" : "one",
"data" : [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
})"_json;
auto chart = Chart({cfgA, cfgB, cfgC, cfgD, cfgE});
chart.reset();
json zz = R"({
"name" : "ZZ",
"y" : 0
})"_json;
zz["y"] = rand() % 100;
chart.add(3, zz);
while(true) {
Sleep(1000);
exchange.SetContractType("rb888");
auto ticker = exchange.GetTicker();
if(!ticker.Valid) {
continue;
}
auto diff = ticker.Sell - ticker.Buy;
json cfgASubTitle = R"({"text" : ""})"_json;
cfgASubTitle["text"] = format("买一 %f , 卖一 %f", ticker.Buy, ticker.Sell);
cfgA["subtitle"] = cfgASubTitle;
json cfgBSubTitle = R"({"text" : ""})"_json;
cfgBSubTitle["text"] = format("价差 %f", diff);
cfgB["subtitle"] = cfgBSubTitle;
chart.add(0, {Unix() * 1000, ticker.Buy});
chart.add(1, {Unix() * 1000, ticker.Sell});
chart.add(2, {Unix() * 1000, diff});
chart.add(4, {Unix() * 1000, ticker.Buy});
chart.add(5, {Unix() * 1000, ticker.Buy});
cfgC["series"][0]["data"][0][1] = rand() % 100;
cfgE["series"][0]["data"][0][1] = rand() % 100;
chart.update({cfgA, cfgB, cfgC, cfgD, cfgE});
}
}
多图表画图配置:
extension.layout
属性
如果设置此属性,值为"single",则图表不会叠加(不会以分页标签方式显示),会单独显示(平铺显示)。extension.height
属性
此属性用于设置图表的高度,值可以为数值类型,或以"300px"方式设置。extension.col
属性
此属性用于设置图表的宽度,页面宽度一共划分为12个单元,设置8即该图表占用8个单元宽度。// 这个chart在JavaScript语言中是对象,在使用Chart函数之前我们需要声明一个配置图表的对象变量chart
var chart = {
// 该字段标记图表是否为一般图表,有兴趣的可以改成false运行看看
__isStock: true,
// 缩放工具
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
// 标题
title : { text : '差价分析图'},
// 选择范围
rangeSelector: {
buttons: [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}],
selected: 0,
inputEnabled: false
},
// 坐标轴横轴即:x轴,当前设置的类型是:时间
xAxis: { type: 'datetime'},
// 坐标轴纵轴即:y轴,默认数值随数据大小调整
yAxis : {
// 标题
title: {text: '差价'},
// 是否启用右边纵轴
opposite: false
},
// 数据系列,该属性保存的是各个数据系列(线,K线图,标签等...)
series : [
// 索引为0,data数组内存放的是该索引系列的数据
{name : "line1", id : "线1,buy1Price", data : []},
// 索引为1,设置了dashStyle:'shortdash'即:设置虚线
{name : "line2", id : "线2,lastPrice", dashStyle : 'shortdash', data : []}
]
}
function main(){
// 调用Chart函数,初始化图表
var ObjChart = Chart(chart)
// 清空
ObjChart.reset()
while(true){
// 判断连接期货公司前置机是否成功。股票证券无需使用exchange.IO("status")判断连接状态
if (!exchange.IO("status")) {
Sleep(1000)
continue
}
exchange.SetContractType("rb888")
// 获取本次轮询的时间戳,即一个毫秒的时间戳。用来确定写入到图表的X轴的位置
var nowTime = new Date().getTime()
// 获取行情数据
var ticker = _C(exchange.GetTicker)
// 从行情数据的返回值取得买一价
var buy1Price = ticker.Buy
// 取得最后成交价,为了2条线不重合在一起,我们加1
var lastPrice = ticker.Last + 1
// 用时间戳作为X值,买一价作为Y值传入索引0的数据序列
ObjChart.add(0, [nowTime, buy1Price])
// 同上
ObjChart.add(1, [nowTime, lastPrice])
Sleep(2000)
}
}
import time
chart = {
"__isStock" : True,
"tooltip" : {"xDateFormat" : "%Y-%m-%d %H:%M:%S, %A"},
"title" : {"text" : "差价分析图"},
"rangeSelector" : {
"buttons" : [{"type": "count", "count": 1, "text": "1h"}, {"type": "hour", "count": 3, "text": "3h"}, {"type": "hour", "count": 8, "text": "8h"}, {"type": "all", "text": "All"}],
"selected": 0,
"inputEnabled": False
},
"xAxis": {"type": "datetime"},
"yAxis": {
"title": {"text": "差价"},
"opposite": False
},
"series": [{
"name": "line1", "id": "线1,buy1Price", "data": []
}, {
"name": "line2", "id": "线2,lastPrice", "dashStyle": "shortdash", "data": []
}]
}
def main():
ObjChart = Chart(chart)
ObjChart.reset()
while True:
if not exchange.IO("status"):
Sleep(1000)
continue
exchange.SetContractType("rb888")
nowTime = time.time() * 1000
ticker = exchange.GetTicker()
buy1Price = ticker["Buy"]
lastPrice = ticker["Last"] + 1
ObjChart.add(0, [nowTime, buy1Price])
ObjChart.add(1, [nowTime, lastPrice])
Sleep(2000)
void main() {
// C++编写策略时,尽量不要声明非基础类型的全局变量,所以图表配置对象声明在main函数内
json chart = R"({
"__isStock" : true,
"tooltip" : {"xDateFormat" : "%Y-%m-%d %H:%M:%S, %A"},
"title" : {"text" : "差价分析图"},
"rangeSelector" : {
"buttons" : [{"type": "count", "count": 1, "text": "1h"}, {"type": "hour", "count": 3, "text": "3h"}, {"type": "hour", "count": 8, "text": "8h"}, {"type": "all", "text": "All"}],
"selected": 0,
"inputEnabled": false
},
"xAxis": {"type": "datetime"},
"yAxis": {
"title": {"text": "差价"},
"opposite": false
},
"series": [{
"name": "line1", "id": "线1,buy1Price", "data": []
}, {
"name": "line2", "id": "线2,lastPrice", "dashStyle": "shortdash", "data": []
}]
})"_json;
auto ObjChart = Chart(chart);
ObjChart.reset();
while(true) {
if (exchange.IO("status") == 0) {
Sleep(1000);
continue;
}
exchange.SetContractType("rb888");
auto nowTime = Unix() * 1000;
auto ticker = exchange.GetTicker();
auto buy1Price = ticker.Buy;
auto lastPrice = ticker.Last + 1.0;
ObjChart.add(0, {nowTime, buy1Price});
ObjChart.add(1, {nowTime, lastPrice});
Sleep(2000);
}
}
简单的画图例子:
// 用于初始化图表的对象
var chart = {
// 图表标题
title: {text: "line数值触发 plotLines 值"},
// Y轴相关设置
yAxis: {
// 垂直于Y轴的水平线,用作触发线,是一个结构数组,可以设置多条触发线
plotLines: [{
// 触发线的值,设置多少这条线就在相应的数值位置显示
value: 0,
// 设置触发线的颜色
color: 'red',
// 宽度
width: 2,
// 显示的标签
label: {
// 标签文本
text: '触发值',
// 标签位置居中
align: 'center'
}
}]
},
// X轴相关设置,这里设置类型是时间轴
xAxis: {type: "datetime"},
series: [
{name: "sin", type: "spline", data: []},
// 这个是比较重要的数据系列,可以设置多个数据系列,根据数组索引控制
{name: "cos", type: "spline", data: []}
]
}
function main(){
// 圆周率
var pi = 3.1415926535897
// 用于记录时间戳的变量
var time = 0
// 角度
var angle = 0
// 坐标y值,用于接收正弦值、余弦值
var y = 0
// 调用API接口用chart对象初始化图表
var objChart = Chart(chart)
// 初始,清空图表
objChart.reset()
// 设置触发线的值为1
chart.yAxis.plotLines[0].value = 1
// 循环
while(true){
// 获取当前时刻的时间戳
time = new Date().getTime()
// 每500ms角度angle增加5度,计算正弦值
y = Math.sin(angle * 2 * pi / 360)
// 把计算出来的y值写入图表相应索引的数据系列,add函数的第一个参数为指定的数据系列索引
objChart.add(0, [time, y])
// 计算余弦值
y = Math.cos(angle * 2 * pi / 360)
objChart.add(1, [time, y])
// 增加5度
angle += 5
// 暂停5秒,以免画图太频繁,数据增长过快
Sleep(5000)
}
}
import math
import time
chart = {
"title": {"text": "line数值触发 plotLines 值"},
"yAxis": {
"plotLines": [{
"value": 0,
"color": "red",
"width": 2,
"label": {
"text": "触发值",
"align": "center"
}
}]
},
"xAxis": {"type": "datetime"},
"series": [{"name": "sin", "type": "spline", "data": []},
{"name": "cos", "type": "spline", "data": []}]
}
def main():
pi = 3.1415926535897
ts = 0
angle = 0
y = 0
objChart = Chart(chart)
objChart.reset()
chart["yAxis"]["plotLines"][0]["value"] = 1
while True:
ts = time.time() * 1000
y = math.sin(angle * 2 * pi / 360)
objChart.add(0, [ts, y])
y = math.cos(angle * 2 * pi / 360)
objChart.add(1, [ts, y])
angle += 5
Sleep(5000)
void main() {
json chart = R"({
"title": {"text": "line数值触发 plotLines 值"},
"yAxis": {
"plotLines": [{
"value": 0,
"color": "red",
"width": 2,
"label": {
"text": "触发值",
"align": "center"
}
}]
},
"xAxis": {"type": "datetime"},
"series": [{"name": "sin", "type": "spline", "data": []},
{"name": "cos", "type": "spline", "data": []}]
})"_json;
auto pi = 3.1415926535897;
auto ts = 0;
auto angle = 0.0;
auto y = 0.0;
auto objChart = Chart(chart);
objChart.reset();
chart["yAxis"]["plotLines"][0]["value"] = 1;
while(true) {
ts = Unix() * 1000;
y = sin(angle * 2 * pi / 360);
objChart.add(0, {ts, y});
y = cos(angle * 2 * pi / 360);
objChart.add(1, {ts, y});
angle += 5;
Sleep(5000);
}
}
三角函数曲线画图例子:
var chartCfg = {
subtitle: {
text: "subtitle",
},
yAxis: [{
height: "40%",
lineWidth: 2,
title: {
text: 'PnL',
},
tickPixelInterval: 20,
minorGridLineWidth: 1,
minorTickWidth: 0,
opposite: true,
labels: {
align: "right",
x: -3,
}
}, {
title: {
text: 'Profit',
},
top: "42%",
height: "18%",
offset: 0,
lineWidth: 2
}, {
title: {
text: 'Vol',
},
top: '62%',
height: '18%',
offset: 0,
lineWidth: 2
}, {
title: {
text: 'Asset',
},
top: '82%',
height: '18%',
offset: 0,
lineWidth: 2
}],
series: [{
name: 'PnL',
data: [],
id: 'primary',
tooltip: {
xDateFormat: '%Y-%m-%d %H:%M:%S'
},
yAxis: 0
}, {
type: 'column',
lineWidth: 2,
name: 'Profit',
data: [],
yAxis: 1,
}, {
type: 'column',
name: 'Trade',
data: [],
yAxis: 2
}, {
type: 'area',
step: true,
lineWidth: 0,
name: 'Long',
data: [],
yAxis: 2
}, {
type: 'area',
step: true,
lineWidth: 0,
name: 'Short',
data: [],
yAxis: 2
}, {
type: 'line',
step: true,
color: '#5b4b00',
name: 'Asset',
data: [],
yAxis: 3
}, {
type: 'pie',
innerSize: '70%',
name: 'Random',
data: [],
center: ['3%', '6%'],
size: '15%',
dataLabels: {
enabled: false
},
startAngle: -90,
endAngle: 90,
}],
};
function main() {
let c = Chart(chartCfg);
let preTicker = null;
while (true) {
if (!exchange.IO("status")) {
Sleep(1000)
continue
}
exchange.SetContractType("rb888")
let t = exchange.GetTicker();
c.add(0, [t.Time, t.Last]); // PnL
c.add(1, [t.Time, preTicker ? t.Last - preTicker.Last : 0]); // profit
let r = Math.random();
var pos = parseInt(t.Time/86400);
c.add(2, [t.Time, pos/2]); // Vol
c.add(3, [t.Time, r > 0.8 ? pos : null]); // Long
c.add(4, [t.Time, r < 0.8 ? -pos : null]); // Short
c.add(5, [t.Time, Math.random() * 100]); // Asset
// update pie
chartCfg.series[chartCfg.series.length-1].data = [
["A", Math.random()*100],
["B", Math.random()*100],
];
c.update(chartCfg)
preTicker = t;
}
}
import random
chartCfg = {
"subtitle": {
"text": "subtitle"
},
"yAxis": [{
"height": "40%",
"lineWidth": 2,
"title": {
"text": 'PnL'
},
"tickPixelInterval": 20,
"minorGridLineWidth": 1,
"minorTickWidth": 0,
"opposite": True,
"labels": {
"align": "right",
"x": -3
}
}, {
"title": {
"text": 'Profit'
},
"top": "42%",
"height": "18%",
"offset": 0,
"lineWidth": 2
}, {
"title": {
"text": 'Vol'
},
"top": '62%',
"height": '18%',
"offset": 0,
"lineWidth": 2
}, {
"title": {
"text": 'Asset'
},
"top": '82%',
"height": '18%',
"offset": 0,
"lineWidth": 2
}],
"series": [{
"name": 'PnL',
"data": [],
"id": 'primary',
"tooltip": {
"xDateFormat": '%Y-%m-%d %H:%M:%S'
},
"yAxis": 0
}, {
"type": 'column',
"lineWidth": 2,
"name": 'Profit',
"data": [],
"yAxis": 1
}, {
"type": 'column',
"name": 'Trade',
"data": [],
"yAxis": 2
}, {
"type": 'area',
"step": True,
"lineWidth": 0,
"name": 'Long',
"data": [],
"yAxis": 2
}, {
"type": 'area',
"step": True,
"lineWidth": 0,
"name": 'Short',
"data": [],
"yAxis": 2
}, {
"type": 'line',
"step": True,
"color": '#5b4b00',
"name": 'Asset',
"data": [],
"yAxis": 3
}, {
"type": 'pie',
"innerSize": '70%',
"name": 'Random',
"data": [],
"center": ['3%', '6%'],
"size": '15%',
"dataLabels": {
"enabled": False
},
"startAngle": -90,
"endAngle": 90
}]
}
def main():
c = Chart(chartCfg)
preTicker = None
while True:
if not exchange.IO("status"):
Sleep(1000)
continue
exchange.SetContractType("rb888")
t = exchange.GetTicker()
c.add(0, [t["Time"], t["Last"]])
profit = t["Last"] - preTicker["Last"] if preTicker else 0
c.add(1, [t["Time"], profit])
r = random.random()
pos = t["Time"] / 86400
c.add(2, [t["Time"], pos / 2])
long = pos if r > 0.8 else None
c.add(3, [t["Time"], long])
short = -pos if r < 0.8 else None
c.add(4, [t["Time"], short])
c.add(5, [t["Time"], random.random() * 100])
# update pie
chartCfg["series"][len(chartCfg["series"]) - 1]["data"] = [
["A", random.random() * 100],
["B", random.random() * 100]
]
c.update(chartCfg)
preTicker = t
void main() {
json chartCfg = R"({
"subtitle": {
"text": "subtitle"
},
"yAxis": [{
"height": "40%",
"lineWidth": 2,
"title": {
"text": "PnL"
},
"tickPixelInterval": 20,
"minorGridLineWidth": 1,
"minorTickWidth": 0,
"opposite": true,
"labels": {
"align": "right",
"x": -3
}
}, {
"title": {
"text": "Profit"
},
"top": "42%",
"height": "18%",
"offset": 0,
"lineWidth": 2
}, {
"title": {
"text": "Vol"
},
"top": "62%",
"height": "18%",
"offset": 0,
"lineWidth": 2
}, {
"title": {
"text": "Asset"
},
"top": "82%",
"height": "18%",
"offset": 0,
"lineWidth": 2
}],
"series": [{
"name": "PnL",
"data": [],
"id": "primary",
"tooltip": {
"xDateFormat": "%Y-%m-%d %H:%M:%S"
},
"yAxis": 0
}, {
"type": "column",
"lineWidth": 2,
"name": "Profit",
"data": [],
"yAxis": 1
}, {
"type": "column",
"name": "Trade",
"data": [],
"yAxis": 2
}, {
"type": "area",
"step": true,
"lineWidth": 0,
"name": "Long",
"data": [],
"yAxis": 2
}, {
"type": "area",
"step": true,
"lineWidth": 0,
"name": "Short",
"data": [],
"yAxis": 2
}, {
"type": "line",
"step": true,
"color": "#5b4b00",
"name": "Asset",
"data": [],
"yAxis": 3
}, {
"type": "pie",
"innerSize": "70%",
"name": "Random",
"data": [],
"center": ["3%", "6%"],
"size": "15%",
"dataLabels": {
"enabled": false
},
"startAngle": -90,
"endAngle": 90
}]
})"_json;
Chart c = Chart(chartCfg);
Ticker preTicker;
while(true) {
if (exchange.IO("status") != 0) {
Sleep(1000);
continue;
}
exchange.SetContractType("rb888");
auto t = exchange.GetTicker();
c.add(0, {t.Time, t.Last});
auto profit = preTicker.Valid ? t.Last - preTicker.Last : 0;
c.add(1, {t.Time, profit});
auto r = rand() % 100;
auto pos = t.Time / 86400.0;
c.add(2, {t.Time, pos / 2.0});
auto longPos = r > 0.8 ? pos : NULL;
c.add(3, {t.Time, longPos});
auto shortPos = r < 0.8 ? -pos : NULL;
c.add(4, {t.Time, shortPos});
c.add(5, {t.Time, rand() % 100});
// update pie
json pie = R"([["A", 0], ["B", 0]])"_json;
pie[0][1] = rand() % 100;
pie[1][1] = rand() % 100;
chartCfg["series"][chartCfg["series"].size() - 1]["data"] = pie;
c.update(chartCfg);
preTicker = t;
}
}
使用混合图表的复杂例子:
// update pie
chartCfg.series[chartCfg.series.length-1].data = [
["A", Math.random()*100],
["B", Math.random()*100],
];
c.update(chartCfg)
# update pie
chartCfg["series"][len(chartCfg["series"]) - 1]["data"] = [
["A", random.random() * 100],
["B", random.random() * 100]
]
c.update(chartCfg)
// update pie
json pie = R"([["A", 0], ["B", 0]])"_json;
pie[0][1] = rand() % 100;
pie[1][1] = rand() % 100;
chartCfg["series"][chartCfg["series"].size() - 1]["data"] = pie;
c.update(chartCfg);
图表中pie
类型的图是没有时间轴的图表,在更新数据时需要直接更新图表配置。 例如以上范例中的代码,更新数据后使用c.update(chartCfg)
更新图表,如下所示:
Chart()
函数返回一个图表对象,该对象有4个方法:add()
、reset()
、update()
、del()
。
1、update()
方法:
update()
方法可以更新图表配置信息,该方法的参数为Chart图表配置对象(JSON)。
2、del()
方法:
del()
方法可以根据传入的series参数,删除指定索引的数据系列。
3、add()
方法:
add()
方法可以向图表中写入数据,参数依次为:
series
:用于设置数据系列索引,是整数。data
:用于设置写入的具体数据,是一个数组。index
(可选):用于设置数据索引,是整数。指定修改数据的具体索引位置,支持使用负数表示,设置为-1
指数据集的最后一个数据。
例如画线时,修改线的最后一个点上的数据:chart.add(0, [1574993606000, 13.5], -1)
,即更改图表series[0].data
的倒数第一个点的数据。
不设置index
参数表示向当前数据系列(series)最后添加数据。4、reset()
方法:
reset()
方法用于清空图表数据,reset()
方法可以带一个参数remain
用于指定保留数据的条数。不传参数remain
表示清除全部数据。
{@fun/Log/KLineChart KLineChart}
该函数用于使用类似Pine
语言的画图方式进行策略运行时的自定义画图。
图表对象。 KLineChart()
函数返回的图表对象有多个方法,其中需要注意begin()
和close()
, 在K线数据上遍历执行画图操作,画图操作中必须以begin()
函数调用作为起始,以close()
函数调用作为结束。
object
KLineChart(options)
options
参数为图表配置。
options
true
object、object数组
function main() {
// 调用KLineChart函数创建图表控制对象c
let c = KLineChart({
overlay: true
})
while(true) {
if (exchange.IO("status")) {
break
}
Sleep(1000)
}
exchange.SetContractType("rb888")
// 获取K线
let bars = exchange.GetRecords()
if (!bars) {
return
}
bars.forEach(function(bar, index) {
c.begin(bar)
c.barcolor(bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.2)')
if (bar.Close > bar.Open) {
c.bgcolor('rgba(0, 255, 0, 0.5)')
}
let h = c.plot(bar.High, 'high')
let l = c.plot(bar.Low, 'low')
c.fill(h, l, {
color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)'
})
c.hline(bar.High)
c.plotarrow(bar.Close - bar.Open)
c.plotshape(bar.Low, {
style: 'diamond'
})
c.plotchar(bar.Close, {
char: 'X'
})
c.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)
if (bar.Close > bar.Open) {
// long/short/closelong/closeshort
c.signal("long", bar.High, 1.5)
} else if (bar.Close < bar.Open) {
c.signal("closelong", bar.Low, 1.5)
}
c.close()
})
}
def main():
# 调用KLineChart函数创建图表控制对象c
c = KLineChart({
"overlay": True
})
while True:
if exchange.IO("status"):
break
Sleep(1000)
exchange.SetContractType("rb888")
# 获取K线
bars = exchange.GetRecords()
if not bars:
return
for bar in bars:
c.begin(bar)
c.barcolor('rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(0, 0, 0, 0.2)')
if bar.Close > bar.Open:
c.bgcolor('rgba(0, 255, 0, 0.5)')
h = c.plot(bar.High, 'high')
l = c.plot(bar.Low, 'low')
c.fill(h, l, 'rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(255, 0, 0, 0.2)')
c.hline(bar.High)
c.plotarrow(bar.Close - bar.Open)
c.plotshape(bar.Low, style = 'diamond')
c.plotchar(bar.Close, char = 'X')
c.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)
if bar.Close > bar.Open:
# long/short/closelong/closeshort
c.signal("long", bar.High, 1.5)
elif bar.Close < bar.Open:
c.signal("closelong", bar.Low, 1.5)
c.close()
// 暂不支持
如果在策略自定义画图区域画图必须有图表控制对象,使用KLineChart()
函数创建该对象。 KLineChart()
函数的参数为一个图表配置结构,在参考代码中使用的图表结构很简单:{overlay: true}
。 这个图表配置结构仅仅设置了画图内容在图表主图上输出,如果overlay
设置为假值,例如:false
,则图表上的内容都输出在副图上, 如果需要指定某个画图函数在主图上画出也可以在具体函数调用中指定参数overlay
为真值,例如:true
。
c.barcolor(bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.2)') // 使用本例中参考代码中的例子说明,不再赘述
c.barcolor('rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(0, 0, 0, 0.2)')
// 暂不支持
画图操作中支持的Pine
语言的画图接口函数有:
barcolor
,设置K线颜色。
barcolor(color, offset, editable, show_last, title, display) display参数可选:“none”, “all”
c.bgcolor('rgba(0, 255, 0, 0.5)')
c.bgcolor('rgba(0, 255, 0, 0.5)')
// 暂不支持
bgcolor
,用指定颜色填充K线的背景。
bgcolor(color, offset, editable, show_last, title, display, overlay) display参数可选:“none”, “all”
c.plot(bar.High, 'high')
c.plot(bar.Open < bar.Close ? NaN : bar.Close, "Close", {style: "linebr"}) // 支持画不连续的数据线
h = c.plot(bar.High, 'high')
h = c.plot(None if bar.Open < bar.Close else bar.Close, "Close", style = "linebr") # 支持画不连续的数据线
// 暂不支持
plot
,在图表上绘制一系列数据。
plot(series, title, color, linewidth, style, trackprice, histbase, offset, join, editable, show_last, display) style参数可选:“stepline_diamond”, “stepline”, “cross”, “areabr”, “area”, “circles”, “columns”, “histogram”, “linebr”, “line” display参数可选:“none”, “all”
let h = c.plot(bar.High, 'high')
let l = c.plot(bar.Low, 'low')
c.fill(h, l, {color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)'})
h = c.plot(bar.High, 'high')
l = c.plot(bar.Low, 'low')
c.fill(h, l, color = 'rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(255, 0, 0, 0.2)')
// 暂不支持
fill
,使用提供的颜色填充两个绘图或hline
之间的背景。
fill(hline1, hline2, color, title, editable, fillgaps, display) display参数可选:“none”, “all”
由于JavaScript
语言不能根据函数形参名称指定传入参数,为了解决这个问题可以使用一个{key: value}
结构指定传入某个形参名称的参数,
例如参考代码中使用{color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)'}
指定fill
函数的color
参数。
如果需要连续指定多个形参名称的参数,可以使用{key1: value1, key2: value2, key3: value3}
。
例如本例子中增加指定一个title
参数:{color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)', title: 'fill'}
。
对于颜色值可以使用'rgba(255, 0, 0, 0.2)'
方式设置,也可以使用'#FF0000'
方式设置。
c.hline(bar.High)
c.hline(bar.High)
// 暂不支持
hline
,在给定的固定价格水平上呈现水平线。
hline(price, title, color, linestyle, linewidth, editable, display) linestyle参数可选:“dashed”, “dotted”, “solid” display参数可选:“none”, “all”
c.plotarrow(bar.Close - bar.Open)
c.plotarrow(bar.Close - bar.Open)
// 暂不支持
plotarrow
,在图表上绘制向上和向下箭头。
plotarrow(series, title, colorup, colordown, offset, minheight, maxheight, editable, show_last, display) display参数可选:“none”, “all”
c.plotshape(bar.Low, {style: 'diamond'})
c.plotshape(bar.Low, style = 'diamond')
// 暂不支持
plotshape
,在图表上绘制可视形状。
plotshape(series, title, style, location, color, offset, text, textcolor, editable, size, show_last, display) style参数可选:“diamond”, “square”, “label_down”, “label_up”, “arrow_down”, “arrow_up”, “circle”, “flag”, “triangle_down”, “triangle_up”, “cross”, “xcross” location参数可选:“abovebar”, “belowbar”, “top”, “bottom”, “absolute” size参数可选:“10px”, “14px”, “20px”, “40px”, “80px”,对比Pine语言中的size.tiny、size.small、size.normal、size.large、size.huge size.auto即size.small。 display参数可选:“none”, “all”
c.plotchar(bar.Close, {char: 'X'})
c.plotchar(bar.Close, char = 'X')
// 暂不支持
plotchar
,在图表上使用任何给定的Unicode字符绘制可视形状。
plotchar(series, title, char, location, color, offset, text, textcolor, editable, size, show_last, display) location参数可选:“abovebar”, “belowbar”, “top”, “bottom”, “absolute” size参数可选:“10px”, “14px”, “20px”, “40px”, “80px”,对比Pine语言中的size.tiny、size.small、size.normal、size.large、size.huge size.auto即size.small。 display参数可选:“none”, “all”
c.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)
c.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)
// 暂不支持
plotcandle
,在图表上绘制K线图。
plotcandle(open, high, low, close, title, color, wickcolor, editable, show_last, bordercolor, display) display参数可选:“none”, “all”
c.signal("long", bar.High, 1.5)
c.signal("long", bar.High, 1.5)
// 暂不支持
signal
,Pine语言上没有的函数,这里用来画买卖信号。
signal(direction, price, qty, id) 传入的参数"long"表示交易方向,可选"long"、“closelong”、“short”、“closeshort”。传入的参数
bar.High
为标记信号的Y轴位置。 传入的参数1.5表示信号的交易数量。可以传入第四个参数用来替换默认画出的文本内容,画出的信号标记默认文本为交易方向,例如:“closelong”。
c.reset()
c.reset()
// 暂不支持
reset
,Pine语言上没有的函数,用于清空图表数据。
reset(remain)
reset()
方法可以带一个参数remain
用于指定保留数据的条数。不传参数remain
表示清除全部数据。
策略自定义画图只能使用KLineChart()
函数的方式或者Chart()
函数的方式中的一种。 KLineChart()
函数调用时用到的一些颜色、样式等设置,参看使用KLineChart函数画图的专题文章
{@fun/Log/Chart Chart}
清除日志。
LogReset(remain)
remain
参数用于设置保留的最近日志条数。
remain
false
number
function main() {
// 保留最近10条日志,清除其余日志
LogReset(10)
}
def main():
LogReset(10)
void main() {
LogReset(10);
}
使用LogReset()
函数清理日志。
策略实盘每次启动的启动日志算一条,所以如果不传入参数并且策略起始时没有日志输出,就会完全不显示日志,等待托管者日志回传(并非异常情况)。
{@fun/Log Log}, {@fun/Log/LogVacuum LogVacuum}
用于在调用LogReset()
函数清除日志后,回收SQLite删除数据时占用的储存空间。
LogVacuum()
function main() {
LogReset()
LogVacuum()
}
def main():
LogReset()
LogVacuum()
void main() {
LogReset()
LogVacuum()
}
测试LogVacuum()
函数。
原因是SQLite
删除数据时不回收占用的空间,需要执行VACUUM
清理表,释放空间。 该函数调用时会发生文件移动操作,延迟较大,建议在合适的时间间隔调用。
{@fun/Log/LogReset LogReset}
用于在实盘页面中的「调试信息」栏中输出调试信息。举例实盘Id为123456
,console.log
函数在实盘页面输出调试信息的同时在实盘所属托管者目录下/logs/storage/123456/
创建一个扩展名为.log
的日志文件并写入调试信息,文件名前缀为stdout_
。
console.log(…msgs)
参数msg
为输出的内容,参数msg
可以传多个。
msg
false
string、number、bool、object、array、空值等系统支持的任意类型
function main() {
console.log("test console.log")
}
# 不支持
// 不支持
测试console.log()
函数。
JavaScript
语言支持该函数。[object Object]
,所以尽量输出可读的信息。{@fun/Log/console.error console.error}
用于在实盘页面中的「调试信息」栏中错误输出。举例实盘Id为123456
,console.error
函数在实盘页面错误输出的同时在实盘所属托管者目录下/logs/storage/123456/
创建一个扩展名为.log
的日志文件并写入错误输出,文件名前缀为stderr_
。
console.error(…msgs)
参数msg
为输出的内容,参数msg
可以传多个。
msg
false
string、number、bool、object、array、空值等系统支持的任意类型
function main() {
console.error("test console.error")
}
# 不支持
// 不支持
测试console.error()
函数。
JavaScript
语言支持该函数。[object Object]
,所以尽量输出可读的信息。{@fun/Log/console.log console.log}
Global Market