输入/搜索内容
内置函数
Global
Version
Sleep
IsVirtual
Mail
Mail_Go
SetErrorFilter
GetPid
GetLastError
GetCommand
GetMeta
Dial
HttpQuery
HttpQuery_Go
Encode
UnixNano
Unix
GetOS
MD5
DBExec
UUID
EventLoop
__Serve
_G
_D
_N
_C
_Cross
JSON.parse
JSON.stringify
SetChannelData
GetChannelData
Log
Market
Trade
Account
Futures
NetSettings
Threads
threading
Thread
getThread
mainThread
currentThread
Lock
Condition
Event
Dict
pending
Thread
ThreadLock
ThreadEvent
ThreadCondition
ThreadDict
Web3
TA
Talib
talib.CDL2CROWS
talib.CDL3BLACKCROWS
talib.CDL3INSIDE
talib.CDL3LINESTRIKE
talib.CDL3OUTSIDE
talib.CDL3STARSINSOUTH
talib.CDL3WHITESOLDIERS
talib.CDLABANDONEDBABY
talib.CDLADVANCEBLOCK
talib.CDLBELTHOLD
talib.CDLBREAKAWAY
talib.CDLCLOSINGMARUBOZU
talib.CDLCONCEALBABYSWALL
talib.CDLCOUNTERATTACK
talib.CDLDARKCLOUDCOVER
talib.CDLDOJI
talib.CDLDOJISTAR
talib.CDLDRAGONFLYDOJI
talib.CDLENGULFING
talib.CDLEVENINGDOJISTAR
talib.CDLEVENINGSTAR
talib.CDLGAPSIDESIDEWHITE
talib.CDLGRAVESTONEDOJI
talib.CDLHAMMER
talib.CDLHANGINGMAN
talib.CDLHARAMI
talib.CDLHARAMICROSS
talib.CDLHIGHWAVE
talib.CDLHIKKAKE
talib.CDLHIKKAKEMOD
talib.CDLHOMINGPIGEON
talib.CDLIDENTICAL3CROWS
talib.CDLINNECK
talib.CDLINVERTEDHAMMER
talib.CDLKICKING
talib.CDLKICKINGBYLENGTH
talib.CDLLADDERBOTTOM
talib.CDLLONGLEGGEDDOJI
talib.CDLLONGLINE
talib.CDLMARUBOZU
talib.CDLMATCHINGLOW
talib.CDLMATHOLD
talib.CDLMORNINGDOJISTAR
talib.CDLMORNINGSTAR
talib.CDLONNECK
talib.CDLPIERCING
talib.CDLRICKSHAWMAN
talib.CDLRISEFALL3METHODS
talib.CDLSEPARATINGLINES
talib.CDLSHOOTINGSTAR
talib.CDLSHORTLINE
talib.CDLSPINNINGTOP
talib.CDLSTALLEDPATTERN
talib.CDLSTICKSANDWICH
talib.CDLTAKURI
talib.CDLTASUKIGAP
talib.CDLTHRUSTING
talib.CDLTRISTAR
talib.CDLUNIQUE3RIVER
talib.CDLUPSIDEGAP2CROWS
talib.CDLXSIDEGAP3METHODS
talib.AD
talib.ADOSC
talib.OBV
talib.ACOS
talib.ASIN
talib.ATAN
talib.CEIL
talib.COS
talib.COSH
talib.EXP
talib.FLOOR
talib.LN
talib.LOG10
talib.SIN
talib.SINH
talib.SQRT
talib.TAN
talib.TANH
talib.MAX
talib.MAXINDEX
talib.MIN
talib.MININDEX
talib.MINMAX
talib.MINMAXINDEX
talib.SUM
talib.HT_DCPERIOD
talib.HT_DCPHASE
talib.HT_PHASOR
talib.HT_SINE
talib.HT_TRENDMODE
talib.ATR
talib.NATR
talib.TRANGE
talib.BBANDS
talib.DEMA
talib.EMA
talib.HT_TRENDLINE
talib.KAMA
talib.MA
talib.MAMA
talib.MIDPOINT
talib.MIDPRICE
talib.SAR
talib.SAREXT
talib.SMA
talib.T3
talib.TEMA
talib.TRIMA
talib.WMA
talib.LINEARREG
talib.LINEARREG_ANGLE
talib.LINEARREG_INTERCEPT
talib.LINEARREG_SLOPE
talib.STDDEV
talib.TSF
talib.VAR
talib.ADX
talib.ADXR
talib.APO
talib.AROON
talib.AROONOSC
talib.BOP
talib.CCI
talib.CMO
talib.DX
talib.MACD
talib.MACDEXT
talib.MACDFIX
talib.MFI
talib.MINUS_DI
talib.MINUS_DM
talib.MOM
talib.PLUS_DI
talib.PLUS_DM
talib.PPO
talib.ROC
talib.ROCP
talib.ROCR
talib.ROCR100
talib.RSI
talib.STOCH
talib.STOCHF
talib.STOCHRSI
talib.TRIX
talib.ULTOSC
talib.WILLR
talib.AVGPRICE
talib.MEDPRICE
talib.TYPPRICE
talib.WCLPRICE
OS
结构体
内置变量

Log()函数用于输出日志。

Log(...msgs)

示例

  • 可以传入多个msg参数:

    javascript
    function main() { Log("msg1", "msg2", "msg3") }
    python
    def main(): Log("msg1", "msg2", "msg3")
    rust
    fn main() { Log!("msg1", "msg2", "msg3"); }
    c++
    void main() { Log("msg1", "msg2", "msg3"); }
  • 支持设置输出消息的颜色。若需同时设置颜色和推送,需先设置颜色,最后再使用@字符设置推送。

    javascript
    function main() { Log("Hello FMZ Quant !@") Sleep(1000 * 5) // 字符串内加入#ff0000,打印日志显示为红色,并且推送消息 Log("Hello, #ff0000@") }
    python
    def main(): Log("Hello FMZ Quant !@") Sleep(1000 * 5) Log("Hello, #ff0000@")
    rust
    fn main() { Log!("Hello FMZ Quant !@"); Sleep(1000 * 5); // 字符串内加入#ff0000,打印日志显示为红色,并且推送消息 Log!("Hello, #ff0000@"); }
    c++
    void main() { Log("Hello FMZ Quant !@"); Sleep(1000 * 5); Log("Hello, #ff0000@"); }
  • Log()函数支持打印base64编码后的图片,内容以`开头,以`结尾,例如:

    javascript
    function main() { Log("`data:image/png;base64,AAAA`") }
    python
    def main(): Log("`data:image/png;base64,AAAA`")
    rust
    fn main() { Log!("`data:image/png;base64,AAAA`"); }
    c++
    void main() { Log("`data:image/png;base64,AAAA`"); }
  • Log()函数支持直接打印Pythonmatplotlib.pyplot对象,只要该对象包含savefig方法,即可直接使用Log函数打印,例如:

    python
    import matplotlib.pyplot as plt def main(): plt.plot([3,6,2,4,7,1]) Log(plt)
  • Log()函数支持语言切换,其输出的文本会根据平台页面的语言设置自动切换为对应的语言,例如:

    javascript
    function main() { Log("[trans]中文|abc[/trans]") }
    python
    def main(): Log("[trans]中文|abc[/trans]")
    rust
    fn main() { Log!("[trans]中文|abc[/trans]"); }
    c++
    void main() { Log("[trans]中文|abc[/trans]"); }

参数

名称类型必填描述

msg

string / number / bool / object / array / any (平台支持的任意类型)

参数msg为需要输出的内容,可传入多个msg参数。

参考

备注

Log()函数会在实盘或回测系统的日志区域输出一条日志信息,实盘运行时日志将保存在实盘的数据库中。若Log()函数输出的内容以@字符结尾,该条日志会进入消息推送队列,并推送至当前发明者量化交易平台账号在推送设置中配置的邮箱、WebHook地址等。调试工具和回测系统不支持消息推送。消息推送存在频率限制,具体规则如下:在实盘的每个20秒周期内,仅保留并推送最后一条推送消息,其余消息将被过滤而不推送(通过 Log 函数输出的推送日志仍会正常打印显示在日志区域)。

Log()函数输出的内容以&字符结尾,该条日志将被标记为私密日志。当实盘公开展示时,该条日志对其他用户隐藏,但在实盘拥有者账户视角下仍然可见。此功能可用于记录API密钥、账户余额等敏感信息。例如:Log("私密信息", "&")

关于WebHook推送,可以使用Golang编写的服务程序:

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

推送设置中设置WebHookhttp://XXX.XX.XXX.XX:9090/data?data=Hello_FMZ,运行编写好的Golang服务程序后,即可开始运行实盘策略。以下为使用JavaScript语言编写的策略,策略运行时会执行Log()函数并推送消息:

javascript
function main() { Log("msg", "@") }

Golang语言编写的服务程序接收到推送后,打印如下信息:

log
listen http://localhost:9090 req: {GET /data?data=Hello_FMZ 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_FMZ <nil> <nil> <nil> 0xc420056300

记录并打印盈亏数值,并根据盈亏数值绘制收益曲线。

LogProfit(profit)
LogProfit(profit, ...args)

示例

调用LogProfit函数时,如果最后一个参数为字符&,则不会将日志写入数据库,仅更新收益图表。使用&参数可以避免频繁的收益记录产生大量日志,从而保持日志整洁。例如:

javascript
function main() { // 在收益图表上打印30个点 for(var i = 0; i < 30; i++) { LogProfit(i, '&') Sleep(500) } }
python
def main(): for i in range(30): LogProfit(i, '&') Sleep(500)
rust
fn main() { // 在收益图表上打印30个点 // Rust 中 LogProfit 只接受收益数值参数,不支持 '&' 等扩展参数 for i in 0..30 { LogProfit(i); Sleep(500); } }
c++
void main() { for(int i = 0; i < 30; i++) { LogProfit(i, '&'); Sleep(500); } }

参数

名称类型必填描述

profit

number

参数profit为收益数据,该数据由策略中设计的算法计算得出。

arg

string / number / bool / object / array / any (平台支持的任意类型)

扩展参数,用于向该条收益日志中输出附带信息,arg参数可传入多个。

参考

清空所有收益日志及收益图表。

LogProfitReset()
LogProfitReset(remain)

示例

javascript
function main() { // 在收益图表上打印30个数据点,然后重置,仅保留最后10个数据点 for(var i = 0; i < 30; i++) { LogProfit(i) Sleep(500) } LogProfitReset(10) }
python
def main(): for i in range(30): LogProfit(i) Sleep(500) LogProfitReset(10)
rust
fn main() { // 在收益图表上打印30个数据点,然后重置,仅保留最后10个数据点 for i in 0..30 { LogProfit(i); Sleep(500); } LogProfitReset(10); }
c++
void main() { for(int i = 0; i < 30; i++) { LogProfit(i); Sleep(500); } LogProfitReset(10); }

参数

名称类型必填描述

remain

number

remain参数用于指定需要保留的日志条数(整数)。

参考

在回测系统或实盘页面的状态栏中输出信息。

LogStatus(...msgs)

示例

  • 支持设置输出内容的颜色:

    javascript
    function main() { LogStatus('This is a normal status message') LogStatus('This is a red font status message#ff0000') LogStatus('This is a multi-line status message\nI am the second line') }
    python
    def main(): LogStatus('This is a normal status message') LogStatus('This is a red font status message#ff0000') LogStatus('This is a multi-line status message\nI am the second line')
    rust
    fn main() { LogStatus!("This is a normal status message"); LogStatus!("This is a red font status message#ff0000"); LogStatus!("This is a multi-line status message\nI am the second line"); }
    c++
    void main() { LogStatus("This is a normal status message"); LogStatus("This is a red font status message#ff0000"); LogStatus("This is a multi-line status message\nI am the second line"); }
  • 状态栏中的数据输出示例:

    javascript
    function main() { var table = {type: 'table', title: 'Position Info', cols: ['Column 1', 'Column 2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]} // JSON 序列化后,在字符串两端添加 ` 字符,即可将其识别为复杂消息格式(当前支持表格) LogStatus('`' + JSON.stringify(table) + '`') // 表格信息也可以显示在多行文本中 LogStatus('First line message\n`' + JSON.stringify(table) + '`\nThird line message') // 支持同时显示多个表格,将以标签页(TAB)形式归为一组显示 LogStatus('`' + JSON.stringify([table, table]) + '`') // 也可以在表格中构造按钮,策略通过 GetCommand 接收 cmd 属性的内容 var table = { type: 'table', title: 'Position Operation', cols: ['Column 1', 'Column 2', 'Action'], rows: [ ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': 'Close All'}] ] } LogStatus('`' + JSON.stringify(table) + '`') // 或者构造一个单独的按钮 LogStatus('`' + JSON.stringify({'type':'button', 'cmd': 'coverAll', 'name': 'Close All'}) + '`') // 可以自定义按钮样式(bootstrap 的按钮属性) LogStatus('`' + JSON.stringify({'type':'button', 'class': 'btn btn-xs btn-danger', 'cmd': 'coverAll', 'name': 'Close All'}) + '`') }
    python
    import json def main(): table = {"type": "table", "title": "Position Info", "cols": ["Column 1", "Column 2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]} LogStatus('`' + json.dumps(table) + '`') LogStatus('First line message\n`' + json.dumps(table) + '`\nThird line message') LogStatus('`' + json.dumps([table, table]) + '`') table = { "type" : "table", "title" : "Position Operation", "cols" : ["Column 1", "Column 2", "Action"], "rows" : [ ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "Close All"}] ] } LogStatus('`' + json.dumps(table) + '`') LogStatus('`' + json.dumps({"type": "button", "cmd": "coverAll", "name": "Close All"}) + '`') LogStatus('`' + json.dumps({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "Close All"}) + '`')
    rust
    fn main() { let table = r#"{"type": "table", "title": "Position Info", "cols": ["Column 1", "Column 2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}"#; // 在 JSON 字符串两端添加 ` 字符,即可将其识别为复杂消息格式(当前支持表格) LogStatus!(format!("`{}`", table)); // 表格信息也可以显示在多行文本中 LogStatus!(format!("First line message\n`{}`\nThird line message", table)); // 支持同时显示多个表格,将以标签页(TAB)形式归为一组显示 LogStatus!(format!("`[{},{}]`", table, table)); // 也可以在表格中构造按钮,策略通过 GetCommand 接收 cmd 属性的内容 let table = String::from(r#"{"type": "table", "title": "Position Operation", "cols": ["Column 1", "Column 2", "Action"], "rows": ["#) + r#"["abc", "def", {"type": "button", "cmd": "coverAll", "name": "Close All"}]"# + r#"]}"#; LogStatus!(format!("`{}`", table)); // 或者构造一个单独的按钮 LogStatus!(format!("`{}`", r#"{"type": "button", "cmd": "coverAll", "name": "Close All"}"#)); // 可以自定义按钮样式(bootstrap 的按钮属性) LogStatus!(format!("`{}`", r#"{"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "Close All"}"#)); }
    c++
    void main() { json table = R"({"type": "table", "title": "Position Info", "cols": ["Column 1", "Column 2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]})"_json; LogStatus("`" + table.dump() + "`"); LogStatus("First line message\n`" + table.dump() + "`\nThird line message"); json arr = R"([])"_json; arr.push_back(table); arr.push_back(table); LogStatus("`" + arr.dump() + "`"); table = R"({ "type" : "table", "title" : "Position Operation", "cols" : ["Column 1", "Column 2", "Action"], "rows" : [ ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "Close All"}] ] })"_json; LogStatus("`" + table.dump() + "`"); LogStatus("`" + R"({"type": "button", "cmd": "coverAll", "name": "Close All"})"_json.dump() + "`"); LogStatus("`" + R"({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "Close All"})"_json.dump() + "`"); }
  • 支持在状态栏中设计按钮控件(旧版按钮结构):

    javascript
    function main() { var table = { type: "table", title: "Status Bar Button Styles", cols: ["Default", "Primary", "Success", "Info", "Warning", "Danger"], rows: [ [ {"type":"button", "class": "btn btn-xs btn-default", "name": "Default"}, {"type":"button", "class": "btn btn-xs btn-primary", "name": "Primary"}, {"type":"button", "class": "btn btn-xs btn-success", "name": "Success"}, {"type":"button", "class": "btn btn-xs btn-info", "name": "Info"}, {"type":"button", "class": "btn btn-xs btn-warning", "name": "Warning"}, {"type":"button", "class": "btn btn-xs btn-danger", "name": "Danger"} ] ] } LogStatus("`" + JSON.stringify(table) + "`") }
    python
    import json def main(): table = { "type": "table", "title": "Status Bar Button Styles", "cols": ["Default", "Primary", "Success", "Info", "Warning", "Danger"], "rows": [ [ {"type":"button", "class": "btn btn-xs btn-default", "name": "Default"}, {"type":"button", "class": "btn btn-xs btn-primary", "name": "Primary"}, {"type":"button", "class": "btn btn-xs btn-success", "name": "Success"}, {"type":"button", "class": "btn btn-xs btn-info", "name": "Info"}, {"type":"button", "class": "btn btn-xs btn-warning", "name": "Warning"}, {"type":"button", "class": "btn btn-xs btn-danger", "name": "Danger"} ] ] } LogStatus("`" + json.dumps(table) + "`")
    rust
    fn main() { let table = String::from(r#"{"type": "table", "title": "Status Bar Button Styles", "cols": ["Default", "Primary", "Success", "Info", "Warning", "Danger"], "rows": [["#) + r#"{"type": "button", "class": "btn btn-xs btn-default", "name": "Default"},"# + r#"{"type": "button", "class": "btn btn-xs btn-primary", "name": "Primary"},"# + r#"{"type": "button", "class": "btn btn-xs btn-success", "name": "Success"},"# + r#"{"type": "button", "class": "btn btn-xs btn-info", "name": "Info"},"# + r#"{"type": "button", "class": "btn btn-xs btn-warning", "name": "Warning"},"# + r#"{"type": "button", "class": "btn btn-xs btn-danger", "name": "Danger"}"# + r#"]]}"#; LogStatus!(format!("`{}`", table)); }
    c++
    void main() { json table = R"({ "type": "table", "title": "Status Bar Button Styles", "cols": ["Default", "Primary", "Success", "Info", "Warning", "Danger"], "rows": [ [ {"type":"button", "class": "btn btn-xs btn-default", "name": "Default"}, {"type":"button", "class": "btn btn-xs btn-primary", "name": "Primary"}, {"type":"button", "class": "btn btn-xs btn-success", "name": "Success"}, {"type":"button", "class": "btn btn-xs btn-info", "name": "Info"}, {"type":"button", "class": "btn btn-xs btn-warning", "name": "Warning"}, {"type":"button", "class": "btn btn-xs btn-danger", "name": "Danger"} ] ] })"_json; LogStatus("`" + table.dump() + "`"); }
  • 设置状态栏按钮的禁用与描述功能(旧版按钮结构):

    javascript
    function main() { var table = { type: "table", title: "Status Bar Button Disable and Description Test", cols: ["Column 1", "Column 2", "Column 3"], rows: [] } var button1 = {"type": "button", "name": "Button 1", "cmd": "button1", "description": "This is the first button"} var button2 = {"type": "button", "name": "Button 2", "cmd": "button2", "description": "This is the second button, set to disabled", "disabled": true} var button3 = {"type": "button", "name": "Button 3", "cmd": "button3", "description": "This is the third button, set to enabled", "disabled": false} table.rows.push([button1, button2, button3]) LogStatus("`" + JSON.stringify(table) + "`") }
    python
    import json def main(): table = { "type": "table", "title": "Status Bar Button Disable and Description Test", "cols": ["Column 1", "Column 2", "Column 3"], "rows": [] } button1 = {"type": "button", "name": "Button 1", "cmd": "button1", "description": "This is the first button"} button2 = {"type": "button", "name": "Button 2", "cmd": "button2", "description": "This is the second button, set to disabled", "disabled": True} button3 = {"type": "button", "name": "Button 3", "cmd": "button3", "description": "This is the third button, set to enabled", "disabled": False} table["rows"].append([button1, button2, button3]) LogStatus("`" + json.dumps(table) + "`")
    rust
    fn main() { let button1 = r#"{"type": "button", "name": "Button 1", "cmd": "button1", "description": "This is the first button"}"#; let button2 = r#"{"type": "button", "name": "Button 2", "cmd": "button2", "description": "This is the second button, set to disabled", "disabled": true}"#; let button3 = r#"{"type": "button", "name": "Button 3", "cmd": "button3", "description": "This is the third button, set to enabled", "disabled": false}"#; let table = format!( r#"{{"type": "table", "title": "Status Bar Button Disable and Description Test", "cols": ["Column 1", "Column 2", "Column 3"], "rows": [[{}, {}, {}]]}}"#, button1, button2, button3 ); LogStatus!(format!("`{}`", table)); }
    c++
    void main() { json table = R"({ "type": "table", "title": "Status Bar Button Disable and Description Test", "cols": ["Column 1", "Column 2", "Column 3"], "rows": [] })"_json; json button1 = R"({"type": "button", "name": "Button 1", "cmd": "button1", "description": "This is the first button"})"_json; json button2 = R"({"type": "button", "name": "Button 2", "cmd": "button2", "description": "This is the second button, set to disabled", "disabled": true})"_json; json button3 = R"({"type": "button", "name": "Button 3", "cmd": "button3", "description": "This is the third button, set to enabled", "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() + "`"); }
  • 结合 GetCommand() 函数,构建状态栏按钮的交互功能(旧版按钮结构):

    javascript
    function test1() { Log("Calling custom function") } function main() { while (true) { var table = { type: 'table', title: 'Operation', cols: ['Column 1', 'Column 2', 'Action'], rows: [ ['a', '1', { 'type': 'button', 'cmd': "CoverAll", 'name': 'Close All' }], ['b', '1', { 'type': 'button', 'cmd': 10, 'name': 'Send Number' }], ['c', '1', { 'type': 'button', 'cmd': _D(), 'name': 'Call Function' }], ['d', '1', { 'type': 'button', 'cmd': 'test1', 'name': 'Call Custom Function' }] ] } LogStatus(_D(), "\n", '`' + JSON.stringify(table) + '`') var str_cmd = GetCommand() if (str_cmd) { Log("Received interaction data str_cmd:", "Type:", typeof(str_cmd), "Value:", str_cmd) if(str_cmd == "test1") { test1() } } Sleep(500) } }
    python
    import json def test1(): Log("Calling custom function") def main(): while True: table = { "type": "table", "title": "Operation", "cols": ["Column 1", "Column 2", "Action"], "rows": [ ["a", "1", { "type": "button", "cmd": "CoverAll", "name": "Close All" }], ["b", "1", { "type": "button", "cmd": 10, "name": "Send Number" }], ["c", "1", { "type": "button", "cmd": _D(), "name": "Call Function" }], ["d", "1", { "type": "button", "cmd": "test1", "name": "Call Custom Function" }] ] } LogStatus(_D(), "\n", "`" + json.dumps(table) + "`") str_cmd = GetCommand() if str_cmd: Log("Received interaction data str_cmd", "Type:", type(str_cmd), "Value:", str_cmd) if str_cmd == "test1": test1() Sleep(500)
    rust
    fn test1() { Log!("Calling custom function"); } fn main() { loop { let table = String::from(r#"{"type": "table", "title": "Operation", "cols": ["Column 1", "Column 2", "Action"], "rows": ["#) + r#"["a", "1", {"type": "button", "cmd": "CoverAll", "name": "Close All"}],"# + r#"["b", "1", {"type": "button", "cmd": 10, "name": "Send Number"}],"# + &format!(r#"["c", "1", {{"type": "button", "cmd": "{}", "name": "Call Function"}}],"#, _D(None)) + r#"["d", "1", {"type": "button", "cmd": "test1", "name": "Call Custom Function"}]"# + r#"]}"#; LogStatus!(_D(None), "\n", format!("`{}`", table)); if let Some(str_cmd) = GetCommand(0) { Log!("Received interaction data str_cmd:", "Type:", "String", "Value:", &str_cmd); if str_cmd == "test1" { test1(); } } Sleep(500); } }
    c++
    void test1() { Log("Calling custom function"); } void main() { while(true) { json table = R"({ "type": "table", "title": "Operation", "cols": ["Column 1", "Column 2", "Action"], "rows": [ ["a", "1", { "type": "button", "cmd": "CoverAll", "name": "Close All" }], ["b", "1", { "type": "button", "cmd": 10, "name": "Send Number" }], ["c", "1", { "type": "button", "cmd": "", "name": "Call Function" }], ["d", "1", { "type": "button", "cmd": "test1", "name": "Call Custom Function" }] ] })"_json; table["rows"][2][2]["cmd"] = _D(); LogStatus(_D(), "\n", "`" + table.dump() + "`"); auto str_cmd = GetCommand(); if(str_cmd != "") { Log("Received interaction data str_cmd", "Type:", typeid(str_cmd).name(), "Value:", str_cmd); if(str_cmd == "test1") { test1(); } } Sleep(500); } }
  • 在构造状态栏按钮进行交互时,同样支持输入数据,交互指令最终由GetCommand()函数捕获。

    在状态栏按钮控件的数据结构中增加input项(旧版按钮结构),例如为{"type": "button", "cmd": "open", "name": "Open"}添加"input": {"name": "Quantity", "type": "number", "defValue": 1},即可使按钮在被点击时弹出一个带输入框控件的弹窗(输入框中的默认值为1,即defValue所设置的数据),从而可以输入一个数据并与按钮命令一起发送。例如运行以下测试代码时,点击「开仓」按钮后会弹出一个带输入框的弹窗,在输入框中输入111并点击「确定」,GetCommand()函数便会捕获消息:open:111

    javascript
    function main() { var tbl = { type: "table", title: "Operation", cols: ["Column 1", "Column 2"], rows: [ ["Open Position", {"type": "button", "cmd": "open", "name": "Open", "input": {"name": "Quantity", "type": "number", "defValue": 1}}], ["Close Position", {"type": "button", "cmd": "coverAll", "name": "Close All"}] ] } LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`") while (true) { var cmd = GetCommand() if (cmd) { Log("cmd:", cmd) } Sleep(1000) } }
    python
    import json def main(): tbl = { "type": "table", "title": "Operation", "cols": ["Column 1", "Column 2"], "rows": [ ["Open Position", {"type": "button", "cmd": "open", "name": "Open", "input": {"name": "Quantity", "type": "number", "defValue": 1}}], ["Close Position", {"type": "button", "cmd": "coverAll", "name": "Close All"}] ] } LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`") while True: cmd = GetCommand() if cmd: Log("cmd:", cmd) Sleep(1000)
    rust
    fn main() { let tbl = String::from(r#"{"type": "table", "title": "Operation", "cols": ["Column 1", "Column 2"], "rows": ["#) + r#"["Open Position", {"type": "button", "cmd": "open", "name": "Open", "input": {"name": "Quantity", "type": "number", "defValue": 1}}],"# + r#"["Close Position", {"type": "button", "cmd": "coverAll", "name": "Close All"}]"# + r#"]}"#; LogStatus!(_D(None), "\n", format!("`{}`", tbl)); loop { if let Some(cmd) = GetCommand(0) { Log!("cmd:", cmd); } Sleep(1000); } }
    c++
    void main() { json tbl = R"({ "type": "table", "title": "Operation", "cols": ["Column 1", "Column 2"], "rows": [ ["Open Position", {"type": "button", "cmd": "open", "name": "Open", "input": {"name": "Quantity", "type": "number", "defValue": 1}}], ["Close Position", {"type": "button", "cmd": "coverAll", "name": "Close All"}] ] })"_json; LogStatus(_D(), "\n", "`" + tbl.dump() + "`"); while(true) { auto cmd = GetCommand(); if(cmd != "") { Log("cmd:", cmd); } Sleep(1000); } }
  • 支持分组按钮控件(旧版按钮结构),其功能与支持输入数据的状态栏按钮(通过"input"字段设置)一致,交互指令最终均由GetCommand()函数捕获。区别在于分组按钮通过"group"字段设置:当点击按钮触发交互时,页面弹出的对话框中会显示预先设置好的一组输入控件,可一次性输入一组数据。
    关于状态栏按钮控件和分组按钮控件结构中的"group"字段,需要注意以下几点:

    • group中type属性仅支持以下4种类型,defValue属性用于设置默认值。
      "selected":下拉框控件,设置下拉框中的各个选项时使用|符号分隔。
      "number":数值输入框控件。
      "string":字符串输入框控件。
      "boolean":勾选框控件,勾选表示(布尔值)真,不勾选表示(布尔值)假。
    • 交互输入时的控件支持依赖设置:
      例如以下例子中的"name": "tradePrice@orderType==1"设置,使交易价格tradePrice)输入控件仅在下单方式(orderType)下拉框控件选择为挂单时可用。
    • 交互输入时的控件名称支持双语设置。
      例如以下例子中的"description": "下单方式|order type"设置,使用|符号分隔中英文描述内容。
    • group中的namedescription与按钮结构中的namedescription虽然字段名一致,但定义并不相同。
      group中的name与input中的name定义也不相同。
    • 分组按钮控件触发后,发送的交互内容格式为:按钮的cmd字段值加group字段相关数据。例如以下例子测试时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属性支持多种控件类型。

    参考以下例子:

    javascript
    function main() { var tbl = { type: "table", title: "Group Button Control Demo", cols: ["Operation"], rows: [] } // 创建分组按钮控件结构 var groupBtn = { type: "button", cmd: "open", name: "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} ] } // 测试按钮1 var testBtn1 = {"type": "button", "name": "Button 1", "cmd": "button1", "description": "This is the first button"} var testBtn2 = {"type": "button", "name": "Button 2", "cmd": "button2", "description": "This is the second button", "input": {"name": "Quantity", "type": "number", "defValue": 1}} // 在tbl中添加groupBtn tbl.rows.push([groupBtn]) // 支持状态栏表格的一个单元格内设置多个按钮,即一个单元格内的数据为一个按钮结构数组:[testBtn1, testBtn2] tbl.rows.push([[testBtn1, testBtn2]]) while (true) { LogStatus("`" + JSON.stringify(tbl) + "`", "\n", "Group button controls can be set directly on the status bar in addition to status bar tables:", "`" + JSON.stringify(groupBtn) + "`") var cmd = GetCommand() if (cmd) { Log("cmd:", cmd) } Sleep(5000) } }
    python
    import json def main(): tbl = { "type": "table", "title": "Group Button Control Demo", "cols": ["Operation"], "rows": [] } groupBtn = { "type": "button", "cmd": "open", "name": "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} ] } testBtn1 = {"type": "button", "name": "Button 1", "cmd": "button1", "description": "This is the first button"} testBtn2 = {"type": "button", "name": "Button 2", "cmd": "button2", "description": "This is the second button", "input": {"name": "Quantity", "type": "number", "defValue": 1}} tbl["rows"].append([groupBtn]) tbl["rows"].append([[testBtn1, testBtn2]]) while True: LogStatus("`" + json.dumps(tbl) + "`", "\n", "Group button controls can be set directly on the status bar in addition to status bar tables:", "`" + json.dumps(groupBtn) + "`") cmd = GetCommand() if cmd: Log("cmd:", cmd) Sleep(5000)
    rust
    fn main() { // 创建分组按钮控件结构 let group_btn = String::from(r#"{"type": "button", "cmd": "open", "name": "Open", "group": ["#) + r#"{"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"},"# + r#"{"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100},"# + r#"{"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100},"# + r#"{"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": true}"# + r#"]}"#; // 测试按钮1、测试按钮2 let test_btn1 = r#"{"type": "button", "name": "Button 1", "cmd": "button1", "description": "This is the first button"}"#; let test_btn2 = r#"{"type": "button", "name": "Button 2", "cmd": "button2", "description": "This is the second button", "input": {"name": "Quantity", "type": "number", "defValue": 1}}"#; // 在tbl中添加groupBtn;支持状态栏表格的一个单元格内设置多个按钮,即一个单元格内的数据为一个按钮结构数组:[testBtn1, testBtn2] let tbl = format!( r#"{{"type": "table", "title": "Group Button Control Demo", "cols": ["Operation"], "rows": [[{}], [[{}, {}]]]}}"#, group_btn, test_btn1, test_btn2 ); loop { LogStatus!(format!("`{}`", tbl), "\n", "Group button controls can be set directly on the status bar in addition to status bar tables:", format!("`{}`", group_btn)); if let Some(cmd) = GetCommand(0) { Log!("cmd:", cmd); } Sleep(5000); } }
    c++
    void main() { json tbl = R"({ "type": "table", "title": "Group Button Control Demo", "cols": ["Operation"], "rows": [] })"_json; json groupBtn = R"({ "type": "button", "name": "Open", "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": "Button 1", "cmd": "button1", "description": "This is the first button"})"_json; json testBtn2 = R"({"type": "button", "name": "Button 2", "cmd": "button2", "description": "This is the second button", "input": {"name": "Quantity", "type": "number", "defValue": 1}})"_json; tbl["rows"].push_back({groupBtn}); tbl["rows"].push_back({{testBtn1, testBtn2}}); while(true) { LogStatus("`" + tbl.dump() + "`", "\n", "Group button controls can be set directly on the status bar in addition to status bar tables:", "`" + groupBtn.dump() + "`"); auto cmd = GetCommand(); if(cmd != "") { Log("cmd:", cmd); } Sleep(5000); } }
  • 当状态栏分组按钮控件(通过设置group字段实现)与状态栏按钮控件(通过设置input字段实现)被点击触发交互时(旧版按钮结构),页面弹出的对话框中的下拉框控件同样支持多选。以下示例演示如何设计包含多选选项的下拉框控件:

    javascript
    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: "Implement multi-select dropdown", options: [{text: "Option A", value: "A"}, {text: "Option B", value: "B"}, {text: "Option 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: "Dropdown 1", type: "selected", defValue: 1, options: ["A", "B"]}, {name: "comboBox2", label: "labelComboBox2", description: "下拉框2", type: "selected", defValue: "A|B"}, {name: "comboBox3", label: "labelComboBox3", description: "Dropdown 3", type: "selected", defValue: [0, 2], multiple: true, options: ["A", "B", "C"]}, { name: "comboBox4", label: "labelComboBox4", description: "Dropdown 4", type: "selected", defValue: ["A", "C"], multiple: true, options: [{text: "Option A", value: "A"}, {text: "Option B", value: "B"}, {text: "Option C", value: "C"}, {text: "Option 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) } }
    python
    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": "Implement multi-select dropdown", "options": [{"text": "Option A", "value": "A"}, {"text": "Option B", "value": "B"}, {"text": "Option C", "value": "C"}], "defValue": ["A", "C"], "multiple": True } } testBtn3 = { "type": "button", "name": "testBtn3", "cmd": "cmdTestBtn3", "group": [ {"name": "comboBox1", "label": "labelComboBox1", "description": "Dropdown 1", "type": "selected", "defValue": 1, "options": ["A", "B"]}, {"name": "comboBox2", "label": "labelComboBox2", "description": "Dropdown 2", "type": "selected", "defValue": "A|B"}, {"name": "comboBox3", "label": "labelComboBox3", "description": "Dropdown 3", "type": "selected", "defValue": [0, 2], "multiple": True, "options": ["A", "B", "C"]}, { "name": "comboBox4", "label": "labelComboBox4", "description": "Dropdown 4", "type": "selected", "defValue": ["A", "C"], "multiple": True, "options": [{"text": "Option A", "value": "A"}, {"text": "Option B", "value": "B"}, {"text": "Option C", "value": "C"}, {"text": "Option 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)
    rust
    fn main() { // 状态栏按钮控件(通过设置input字段实现)testBtn1按钮所触发的页面中,下拉框控件使用options字段设置选项,并使用defValue字段设置默认选项。区别于本章其它示例中直接使用defValue设置选项的方式。 let test_btn1 = r#"{"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)。 */ let test_btn2 = String::from(r#"{"type": "button", "name": "testBtn2", "cmd": "cmdTestBtn2", "input": {"#) + r#""name": "testBtn2MultiComboBox", "type": "selected", "description": "Implement multi-select dropdown","# + r#""options": [{"text": "Option A", "value": "A"}, {"text": "Option B", "value": "B"}, {"text": "Option C", "value": "C"}],"# + r#""defValue": ["A", "C"], "multiple": true}}"#; // 状态栏分组按钮控件(通过设置group字段实现)testBtn3按钮所触发的页面中,下拉框控件使用options字段设置选项,也支持直接使用defValue设置选项。 let test_btn3 = String::from(r#"{"type": "button", "name": "testBtn3", "cmd": "cmdTestBtn3", "group": ["#) + r#"{"name": "comboBox1", "label": "labelComboBox1", "description": "Dropdown 1", "type": "selected", "defValue": 1, "options": ["A", "B"]},"# + r#"{"name": "comboBox2", "label": "labelComboBox2", "description": "下拉框2", "type": "selected", "defValue": "A|B"},"# + r#"{"name": "comboBox3", "label": "labelComboBox3", "description": "Dropdown 3", "type": "selected", "defValue": [0, 2], "multiple": true, "options": ["A", "B", "C"]},"# + r#"{"name": "comboBox4", "label": "labelComboBox4", "description": "Dropdown 4", "type": "selected", "defValue": ["A", "C"], "multiple": true, "options": [{"text": "Option A", "value": "A"}, {"text": "Option B", "value": "B"}, {"text": "Option C", "value": "C"}, {"text": "Option D", "value": "D"}]}"# + r#"]}"#; loop { LogStatus!(format!("`{}`\n", test_btn1), format!("`{}`\n", test_btn2), format!("`{}`\n", test_btn3)); if let Some(cmd) = GetCommand(0) { Log!(cmd); } Sleep(5000); } }
    c++
    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": "Implement multi-select dropdown", "options": [{"text": "Option A", "value": "A"}, {"text": "Option B", "value": "B"}, {"text": "Option C", "value": "C"}], "defValue": ["A", "C"], "multiple": true } })"_json; json testBtn3 = R"({ "type": "button", "name": "testBtn3", "cmd": "cmdTestBtn3", "group": [ {"name": "comboBox1", "label": "labelComboBox1", "description": "Dropdown 1", "type": "selected", "defValue": 1, "options": ["A", "B"]}, {"name": "comboBox2", "label": "labelComboBox2", "description": "Dropdown 2", "type": "selected", "defValue": "A|B"}, {"name": "comboBox3", "label": "labelComboBox3", "description": "Dropdown 3", "type": "selected", "defValue": [0, 2], "multiple": true, "options": ["A", "B", "C"]}, { "name": "comboBox4", "label": "labelComboBox4", "description": "Dropdown 4", "type": "selected", "defValue": ["A", "C"], "multiple": true, "options": [{"text": "Option A", "value": "A"}, {"text": "Option B", "value": "B"}, {"text": "Option C", "value": "C"}, {"text": "Option 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); } }
  • 基于当前最新的按钮结构,构造状态栏表格中的按钮;点击按钮触发交互时,弹出一个包含多个控件的弹窗。

    详细内容可参考:用户指南-状态栏中的交互控件

    javascript
    var symbols = ["BTC_USDT.swap", "ETH_USDT.swap", "LTC_USDT.swap", "BNB_USDT.swap", "SOL_USDT.swap"] 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": "Open Position", "group": [{ "type": "selected", "name": "tradeType", "label": "Order Type", "description": "Market order, Limit order", "default": 0, "group": "Trade Settings", "settings": { "options": ["Market Order", "Limit Order"], "required": true, } }, { "type": "selected", "name": "direction", "label": "Trade Direction", "description": "Buy, Sell", "default": "buy", "group": "Trade Settings", "settings": { "render": "segment", "required": true, "options": [{"name": "Buy", "value": "buy"}, {"name": "Sell", "value": "sell"}], } }, { "type": "number", "name": "price", "label": "Price", "description": "Order price", "group": "Trade Settings", "filter": "tradeType==1", "settings": { "required": true, } }, { "type": "number", "name": "amount", "label": "Order Amount", "description": "Order amount", "group": "Trade Settings", "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": "Symbol", "default": m["symbol"], "settings": {"required": true}}]) tbl["rows"].push([m["symbol"], btnOpen]) }) var cmd = GetCommand() if (cmd) { Log("Received interaction:", 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("Symbol:", msg["symbol"], ", Direction:", msg["direction"], ", Order type:", msg["tradeType"] == 0 ? "Market order" : "Limit order", msg["tradeType"] == 0 ? ", Price: Current market price" : ", Price:" + msg["price"], ", Amount:", msg["amount"]) } } LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`") Sleep(1000) } }
    python
    import json symbols = ["BTC_USDT.swap", "ETH_USDT.swap", "LTC_USDT.swap", "BNB_USDT.swap", "SOL_USDT.swap"] 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": "Open Position", "group": [{ "type": "selected", "name": "tradeType", "label": "Order Type", "description": "Market order, Limit order", "default": 0, "group": "Trade Settings", "settings": { "options": ["Market Order", "Limit Order"], "required": True, } }, { "type": "selected", "name": "direction", "label": "Trade Direction", "description": "Buy, Sell", "default": "buy", "group": "Trade Settings", "settings": { "render": "segment", "required": True, "options": [{"name": "Buy", "value": "buy"}, {"name": "Sell", "value": "sell"}], } }, { "type": "number", "name": "price", "label": "Price", "description": "Order price", "group": "Trade Settings", "filter": "tradeType==1", "settings": { "required": True, } }, { "type": "number", "name": "amount", "label": "Order Amount", "description": "Order amount", "group": "Trade Settings", "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": "Symbol", "default": m["symbol"], "settings": {"required": True}}]) tbl["rows"].append([m["symbol"], btnOpen]) cmd = GetCommand() if cmd != "" and cmd != None: Log("Received interaction:", 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("Symbol:", msg["symbol"], ", Direction:", msg["direction"], ", Order type:", "Market order" if msg["tradeType"] == 0 else "Limit order", ", Price: Current market price" if msg["tradeType"] == 0 else ", Price:" + str(msg["price"]), ", Amount:", msg["amount"]) # 输出状态栏信息 LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`") Sleep(1000)
    rust
    fn main() { let symbols = ["BTC_USDT.swap", "ETH_USDT.swap", "LTC_USDT.swap", "BNB_USDT.swap", "SOL_USDT.swap"]; // Btn:按钮模板,"group"的第一个元素为交易品种控件,__SYMBOL__为占位符,构造按钮时替换 let tmp_btn_open = String::from(r#"{"type": "button", "cmd": "open", "name": "Open Position", "group": ["#) + r#"{"type": "string", "name": "symbol", "label": "Symbol", "default": "__SYMBOL__", "settings": {"required": true}},"# + r#"{"type": "selected", "name": "tradeType", "label": "Order Type", "description": "Market order, Limit order", "default": 0, "group": "Trade Settings", "settings": {"options": ["Market Order", "Limit Order"], "required": true}},"# + r#"{"type": "selected", "name": "direction", "label": "Trade Direction", "description": "Buy, Sell", "default": "buy", "group": "Trade Settings", "settings": {"render": "segment", "required": true, "options": [{"name": "Buy", "value": "buy"}, {"name": "Sell", "value": "sell"}]}},"# + r#"{"type": "number", "name": "price", "label": "Price", "description": "Order price", "group": "Trade Settings", "filter": "tradeType==1", "settings": {"required": true}},"# + r#"{"type": "number", "name": "amount", "label": "Order Amount", "description": "Order amount", "group": "Trade Settings", "settings": {"required": true}}"# + r#"]}"#; loop { let mut rows: Vec<String> = Vec::new(); for symbol in &symbols { let btn_open = tmp_btn_open.replace("__SYMBOL__", symbol); rows.push(format!(r#"["{}", {}]"#, symbol, btn_open)); } let tbl = format!(r#"{{"type": "table", "title": "dashboard", "cols": ["symbol", "actionOpen"], "rows": [{}]}}"#, rows.join(",")); if let Some(cmd) = GetCommand(0) { Log!("Received interaction:", &cmd); // 解析交互消息: open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111} // 根据第一个冒号:之前的指令判断是哪种按钮模板触发的消息 if cmd.starts_with("open:") { let msg = JSONParse(&cmd[5..]).unwrap(); let trade_type = msg["tradeType"].as_i64().unwrap_or(0); Log!("Symbol:", msg["symbol"].as_str().unwrap_or(""), ", Direction:", msg["direction"].as_str().unwrap_or(""), ", Order type:", if trade_type == 0 { "Market order" } else { "Limit order" }, if trade_type == 0 { ", Price: Current market price".to_string() } else { format!(", Price:{}", msg["price"].as_f64().unwrap_or(0.0)) }, ", Amount:", msg["amount"].as_f64().unwrap_or(0.0)); } } LogStatus!(_D(None), "\n", format!("`{}`", tbl)); Sleep(1000); } }
    c++
    // 略...
  • 横向合并LogStatus()函数绘制的表格中的单元格:

    javascript
    function main() { var table = { type: 'table', title: 'Position Operation', cols: ['Column 1', 'Column 2', 'Action'], rows: [ ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': 'Close'}] ] } var ticker = exchange.GetTicker() // 添加一行数据,将第一个和第二个单元格合并,并在合并后的单元格内输出 ticker 变量 table.rows.push([{body : JSON.stringify(ticker), colspan : 2}, "abc"]) LogStatus('`' + JSON.stringify(table) + '`') }
    python
    import json def main(): table = { "type" : "table", "title" : "Position Operation", "cols" : ["Column 1", "Column 2", "Action"], "rows" : [ ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "Close"}] ] } ticker = exchange.GetTicker() table["rows"].append([{"body": json.dumps(ticker), "colspan": 2}, "abc"]) LogStatus("`" + json.dumps(table) + "`")
    rust
    fn main() { let table_tpl = String::from(r#"{"type": "table", "title": "Position Operation", "cols": ["Column 1", "Column 2", "Action"], "rows": ["#) + r#"["abc", "def", {"type": "button", "cmd": "coverAll", "name": "Close"}],"# + r#"__ROW2__"# + r#"]}"#; let ticker = exchange.GetTicker(None).unwrap(); let json_ticker = format!( r#"{{"Buy": {}, "Sell": {}, "High": {}, "Low": {}, "Volume": {}, "Last": {}, "Time": {}}}"#, ticker.Buy, ticker.Sell, ticker.High, ticker.Low, ticker.Volume, ticker.Last, ticker.Time ); // 添加一行数据,将第一个和第二个单元格合并,并在合并后的单元格内输出 ticker 数据 // body 为字符串(对应 JS 的 JSON.stringify(ticker)),内部引号需转义后嵌入 JSON let row2 = format!(r#"[{{"body": "{}", "colspan": 2}}, "abc"]"#, json_ticker.replace('"', "\\\"")); let table = table_tpl.replace("__ROW2__", &row2); LogStatus!(format!("`{}`", table)); }
    c++
    void main() { json table = R"({ "type" : "table", "title" : "Position Operation", "cols" : ["Column 1", "Column 2", "Action"], "rows" : [ ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "Close"}] ] })"_json; 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() 函数绘制的表格中的单元格:

    javascript
    function main() { var table = { type: 'table', title: 'Table Demo', cols: ['Column A', 'Column B', 'Column C'], rows: [ ['A1', 'B1', {'type':'button', 'cmd': 'coverAll', 'name': 'C1'}] ] } 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) + '`') }
    python
    import json def main(): table = { "type" : "table", "title" : "Table Demo", "cols" : ["Column A", "Column B", "Column C"], "rows" : [ ["A1", "B1", {"type": "button", "cmd": "coverAll", "name": "C1"}] ] } 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) + "`")
    rust
    fn main() { // 为便于测试,此处使用构造的数据以保持代码简短易读 let json_ticker = r#"{"High": 0, "Low": 0, "Buy": 0, "Sell": 0, "Last": 0, "Time": 0, "Volume": 0}"#; let name = exchange.GetName(); let mut rows: Vec<String> = Vec::new(); rows.push(String::from(r#"["A1", "B1", {"type": "button", "cmd": "coverAll", "name": "C1"}]"#)); // body 为字符串,其内部的引号需要转义后才能嵌入 JSON let body = format!("A2 + B2:{}", json_ticker).replace('"', "\\\""); rows.push(format!(r#"[{{"body": "{}", "colspan": 2}}, "C2"]"#, body)); rows.push(format!(r#"[{{"body": "A3 + A4 + A5:{}", "rowspan": 3}}, "B3", "C3"]"#, name)); // A3 被上一行的第一个单元格合并 rows.push(String::from(r#"["B4", "C4"]"#)); // A2 被上一行的第一个单元格合并 rows.push(String::from(r#"["B5", "C5"]"#)); rows.push(String::from(r#"["A6", "B6", "C6"]"#)); let table = format!(r#"{{"type": "table", "title": "Table Demo", "cols": ["Column A", "Column B", "Column C"], "rows": [{}]}}"#, rows.join(",")); LogStatus!(format!("`{}`", table)); }
    c++
    void main() { json table = R"({ "type" : "table", "title" : "Table Demo", "cols" : ["Column A", "Column B", "Column 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() + "`"); }
  • 状态栏中分页显示表格:

    javascript
    function main() { var table1 = {type: 'table', title: 'table1', cols: ['Column 1', 'Column 2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]} var table2 = {type: 'table', title: 'table2', cols: ['Column 1', 'Column 2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]} LogStatus('`' + JSON.stringify([table1, table2]) + '`') }
    python
    import json def main(): table1 = {"type": "table", "title": "table1", "cols": ["Column 1", "Column 2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]} table2 = {"type": "table", "title": "table2", "cols": ["Column 1", "Column 2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]} LogStatus("`" + json.dumps([table1, table2]) + "`")
    rust
    fn main() { let table1 = r#"{"type": "table", "title": "table1", "cols": ["Column 1", "Column 2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}"#; let table2 = r#"{"type": "table", "title": "table2", "cols": ["Column 1", "Column 2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}"#; LogStatus!(format!("`[{},{}]`", table1, table2)); }
    c++
    void main() { json table1 = R"({"type": "table", "title": "table1", "cols": ["Column 1", "Column 2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]})"_json; json table2 = R"({"type": "table", "title": "table2", "cols": ["Column 1", "Column 2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]})"_json; json arr = R"([])"_json; arr.push_back(table1); arr.push_back(table2); LogStatus("`" + arr.dump() + "`"); }
  • 除了可以分页显示表格之外,还可以将多个表格自上而下排列显示:

    javascript
    function main(){ var tab1 = { type : "table", title : "Table 1", cols : ["1", "2"], rows : [] } var tab2 = { type : "table", title : "Table 2", cols : ["1", "2", "3"], rows : [] } var tab3 = { type : "table", title : "Table 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") }
    python
    import json def main(): tab1 = { "type": "table", "title": "Table 1", "cols": ["1", "2"], "rows": [] } tab2 = { "type": "table", "title": "Table 2", "cols": ["1", "2", "3"], "rows": [] } tab3 = { "type": "table", "title": "Table 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) + "`")
    rust
    fn main() { // Rust 中直接把行数据写入JSON字符串中 let tab1 = r#"{"type": "table", "title": "Table 1", "cols": ["1", "2"], "rows": [["jack", "lucy"]]}"#; let tab2 = r#"{"type": "table", "title": "Table 2", "cols": ["1", "2", "3"], "rows": [["A", "B", "C"]]}"#; let tab3 = r#"{"type": "table", "title": "Table 3", "cols": ["A", "B", "C"], "rows": [["A", "B", "C"]]}"#; LogStatus!(format!("`{}`\n`{}`\n`{}`", tab1, tab2, tab3)); Log!("exit"); }
    c++
    void main() { json tab1 = R"({ "type": "table", "title": "Table 1", "cols": ["1", "2"], "rows": [] })"_json; json tab2 = R"({ "type": "table", "title": "Table 2", "cols": ["1", "2", "3"], "rows": [] })"_json; json tab3 = R"({ "type": "table", "title": "Table 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() + "`"); }
  • 支持设置状态栏表格的横向和纵向滚动模式。将scroll属性设置为"auto"后,当状态栏表格的纵向行数超过 20 行时,内容将自动滚动显示;当横向列数超出页面显示范围时,则进行横向滚动显示。使用scroll属性可以缓解实盘运行时因状态栏写入大量数据而导致的卡顿问题。

    参考以下测试例子:

    javascript
    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) + "`") }
    python
    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) + "`")
    rust
    fn main() { let tbl_tpl = String::from(r#"{"type": "table", "title": "test scroll", "scroll": "auto", "cols": ["#) + r#""col 0", "col 1", "col 2", "col 3", "col 4", "col 5", "col 6", "col 7", "col 8", "col 9", "col 10","# + r#""col 11", "col 12", "col 13", "col 14", "col 15", "col 16", "col 17", "col 18", "col 19", "col 20""# + r#"], "rows": [__ROWS__]}"#; let mut rows: Vec<String> = Vec::new(); for index in 1..100 { let i = index.to_string(); rows.push(format!( r#"[{}, "1,{}", "2,{}", "3,{}", "4,{}", "5,{}", "6,{}", "7,{}", "8,{}", "9,{}", "10,{}", "11,{}", "12,{}", "13,{}", "14,{}", "15,{}", "16,{}", "17,{}", "18,{}", "19,{}", "20,{}"]"#, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i )); } let tbl = tbl_tpl.replace("__ROWS__", &rows.join(",")); LogStatus!(format!("`{}`", tbl)); }
    c++
    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() + "`"); }

参数

名称类型必填描述

msg

string / number / bool / object / array / any (平台支持的任意类型)

参数msg为要输出的内容,可传入多个msg参数。

参考

备注

实盘运行时,LogStatus()函数输出的信息不会保存到实盘数据库,仅更新当前实盘的状态栏内容。

LogStatus()函数支持打印base64编码后的图片,图片字符串以`开头,以`结尾。例如: LogStatus("`data:image/png;base64,AAAA`")

LogStatus()函数支持直接传入Pythonmatplotlib.pyplot对象。只要对象包含savefig方法,即可作为参数传入LogStatus()函数,例如:

python
import matplotlib.pyplot as plt def main(): plt.plot([3,6,2,4,7,1]) LogStatus(plt)

策略实盘运行时,在实盘页面翻看历史记录时,状态栏会进入休眠状态,停止更新;只有当日志处于第一页时,状态栏数据才会刷新。状态栏支持输出base64编码后的图片,也支持在状态栏显示的表格中输出base64编码后的图片。由于编码后的图片字符串数据通常很长,因此此处不再展示示例代码。

启用或禁用订单信息的日志记录。

EnableLog(enable)

示例

javascript
function main() { EnableLog(false) }
python
def main(): EnableLog(False)
rust
fn main() { EnableLog(false); }
c++
void main() { EnableLog(false); }

参数

名称类型必填描述

enable

bool

enable参数设置为假值(例如false)时,将不打印订单日志(即exchange.Buy()等函数产生的日志),也不会写入实盘的数据库。

参考

自定义图表绘图函数。

Chart(options)

示例

  • 多图表绘制配置说明:

    • extension.layout 属性
      当此属性设置为 "single" 时,该图表不会与其他图表叠加显示(即不以分页标签方式呈现),而是单独平铺显示。
    • extension.height 属性
      此属性用于设置图表的高度,取值可以为数值类型,也可以采用 "300px" 的形式设置。
    • extension.col 属性
      此属性用于设置图表的宽度。页面宽度共划分为 12 个单元,设置为 8 即表示该图表占用 8 个单元的宽度。
    javascript
    function main() { var cfgA = { extension: { layout: 'single', // 不参与分组,单独显示,默认为分组 'group' height: 300, // 指定高度 }, title: { text: 'Order Book Chart' }, xAxis: { type: 'datetime' }, series: [{ name: 'Bid 1', data: [], }, { name: 'Ask 1', data: [], }] } var cfgB = { title: { text: 'Spread Chart' }, xAxis: { type: 'datetime' }, series: [{ name: 'Spread', type: 'column', data: [], }] } var cfgC = { __isStock: false, title: { text: 'Pie Chart' }, 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: 'Order Book Chart' }, xAxis: { type: 'datetime' }, series: [{ name: 'Bid 1', data: [], }, { name: 'Ask 1', data: [], }] } var cfgE = { __isStock: false, extension: { layout: 'single', col: 4, height: '300px', }, title: { text: 'Pie Chart 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) var ticker = exchange.GetTicker() if (!ticker) { continue; } var diff = ticker.Sell - ticker.Buy cfgA.subtitle = { text: 'Bid ' + ticker.Buy + ', Ask ' + ticker.Sell, }; cfgB.subtitle = { text: 'Spread ' + 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]); } }
    python
    import random import time def main(): cfgA = { "extension" : { "layout" : "single", "height" : 300, "col" : 8 }, "title" : { "text" : "Order Book Chart" }, "xAxis" : { "type" : "datetime" }, "series" : [{ "name" : "Bid 1", "data" : [] }, { "name" : "Ask 1", "data" : [] }] } cfgB = { "title" : { "text" : "Spread Chart" }, "xAxis" : { "type" : "datetime", }, "series" : [{ "name" : "Spread", "type" : "column", "data" : [] }] } cfgC = { "__isStock" : False, "title" : { "text" : "Pie Chart" }, "series" : [{ "type" : "pie", "name" : "one", "data" : [ ["A", 25], ["B", 25], ["C", 25], ["D", 25], ] }] } cfgD = { "extension" : { "layout" : "single", "col" : 8, "height" : "300px" }, "title" : { "text" : "Order Book Chart" }, "series" : [{ "name" : "Bid 1", "data" : [] }, { "name" : "Ask 1", "data" : [] }] } cfgE = { "__isStock" : False, "extension" : { "layout" : "single", "col" : 4, "height" : "300px" }, "title" : { "text" : "Pie Chart 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) ticker = exchange.GetTicker() if not ticker : continue diff = ticker["Sell"] - ticker["Buy"] cfgA["subtitle"] = { "text" : "Bid " + str(ticker["Buy"]) + " Ask " + str(ticker["Sell"]) } cfgB["subtitle"] = { "text" : "Spread " + 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
    rust
    fn main() { // Rust 中图表配置为 JSON 字符串,可变部分使用占位符表示,更新时替换占位符以重新构建配置 let cfg_a_tpl = r#"{ "extension": { "layout": "single", "height": 300 }, "title": {"text": "Order Book Chart"}, "subtitle": {"text": "__SUBTITLE__"}, "xAxis": {"type": "datetime"}, "series": [{"name": "Bid 1", "data": []}, {"name": "Ask 1", "data": []}] }"#; let cfg_b_tpl = r#"{ "title": {"text": "Spread Chart"}, "subtitle": {"text": "__SUBTITLE__"}, "xAxis": {"type": "datetime"}, "series": [{"name": "Spread", "type": "column", "data": []}] }"#; let cfg_c_tpl = r#"{ "__isStock": false, "title": {"text": "Pie Chart"}, "series": [{ "type": "pie", "name": "one", "data": [["A", __Y__], ["B", 25], ["C", 25], ["D", 25]] }] }"#; let cfg_d = r#"{ "extension": { "layout": "single", "col": 8, "height": "300px" }, "title": {"text": "Order Book Chart"}, "xAxis": {"type": "datetime"}, "series": [{"name": "Bid 1", "data": []}, {"name": "Ask 1", "data": []}] }"#; let cfg_e_tpl = r#"{ "__isStock": false, "extension": { "layout": "single", "col": 4, "height": "300px" }, "title": {"text": "Pie Chart 2"}, "series": [{ "type": "pie", "name": "one", "data": [["A", __Y__], ["B", 25], ["C", 25], ["D", 25]] }] }"#; let cfg_a = cfg_a_tpl.replace("__SUBTITLE__", ""); let cfg_b = cfg_b_tpl.replace("__SUBTITLE__", ""); let cfg_c = cfg_c_tpl.replace("__Y__", "25"); let cfg_e = cfg_e_tpl.replace("__Y__", "25"); let chart = Chart::new(&format!("[{},{},{},{},{}]", cfg_a, cfg_b, cfg_c, cfg_d, cfg_e)); chart.reset(0); // 为饼图追加一个数据点,add 只能更新通过 add 方式添加的数据点,内置的数据点无法在后期更新 let y = (UnixNano() % 100) as f64; // 用时间戳模拟随机数 chart.add(3, &format!(r#"{{"name": "ZZ", "y": {}}}"#, y), -1); loop { Sleep(1000); let ticker = match exchange.GetTicker(None) { Ok(t) => t, Err(_) => continue, }; let diff = ticker.Sell - ticker.Buy; let cfg_a = cfg_a_tpl.replace("__SUBTITLE__", &format!("Bid {}, Ask {}", ticker.Buy, ticker.Sell)); let cfg_b = cfg_b_tpl.replace("__SUBTITLE__", &format!("Spread {}", diff)); let now = Unix() * 1000; chart.add(0, &format!("[{}, {}]", now, ticker.Buy), -1); chart.add(1, &format!("[{}, {}]", now, ticker.Sell), -1); // 相当于更新第二个图表的第一个数据序列 chart.add(2, &format!("[{}, {}]", now, diff), -1); chart.add(4, &format!("[{}, {}]", now, ticker.Buy), -1); chart.add(5, &format!("[{}, {}]", now, ticker.Buy), -1); let cfg_c = cfg_c_tpl.replace("__Y__", &format!("{}", (UnixNano() % 100) as f64)); let cfg_e = cfg_e_tpl.replace("__Y__", &format!("{}", (UnixNano() % 100) as f64)); // update 实际上等同于重置图表的配置 chart.update(&format!("[{},{},{},{},{}]", cfg_a, cfg_b, cfg_c, cfg_d, cfg_e)); } }
    c++
    void main() { json cfgA = R"({ "extension" : { "layout" : "single", "height" : 300, "col" : 8 }, "title" : { "text" : "Order Book Chart" }, "xAxis" : { "type" : "datetime" }, "series" : [{ "name" : "Bid 1", "data" : [] }, { "name" : "Ask 1", "data" : [] }] })"_json; json cfgB = R"({ "title" : { "text" : "Spread Chart" }, "xAxis" : { "type" : "datetime" }, "series" : [{ "name" : "Spread", "type" : "column", "data" : [] }] })"_json; json cfgC = R"({ "__isStock" : false, "title" : { "text" : "Pie Chart" }, "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" : "Order Book Chart" }, "series" : [{ "name" : "Bid 1", "data" : [] }, { "name" : "Ask 1", "data" : [] }] })"_json; json cfgE = R"({ "__isStock" : false, "extension" : { "layout" : "single", "col" : 4, "height" : "300px" }, "title" : { "text" : "Pie Chart 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); auto ticker = exchange.GetTicker(); if(!ticker.Valid) { continue; } auto diff = ticker.Sell - ticker.Buy; json cfgASubTitle = R"({"text" : ""})"_json; cfgASubTitle["text"] = str_format("买一 %f , 卖一 %f", ticker.Buy, ticker.Sell); cfgA["subtitle"] = cfgASubTitle; json cfgBSubTitle = R"({"text" : ""})"_json; cfgBSubTitle["text"] = str_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}); } }
  • 简单的绘图示例:

    javascript
    // 在 JavaScript 中,chart 是一个对象;在调用 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 : "Line 1,buy1Price", data : []}, // 索引为 1,设置了 dashStyle: 'shortdash',即将其设置为虚线 {name : "line2", id : "Line 2,lastPrice", dashStyle : 'shortdash', data : []} ] } function main(){ // 调用 Chart 函数,初始化图表 var ObjChart = Chart(chart) // 清空 ObjChart.reset() while(true){ // 获取本次轮询的时间戳(即毫秒级时间戳),用于确定写入图表的 X 轴位置 var nowTime = new Date().getTime() // 获取行情数据 var ticker = _C(exchange.GetTicker) // 从行情数据的返回值中取得买一价 var buy1Price = ticker.Buy // 取得最新成交价,为避免两条线相互重合,此处将其加 1 var lastPrice = ticker.Last + 1 // 以时间戳作为 X 值、买一价作为 Y 值,传入索引 0 的数据序列 ObjChart.add(0, [nowTime, buy1Price]) // 同上 ObjChart.add(1, [nowTime, lastPrice]) Sleep(2000) } }
    python
    import time chart = { "__isStock" : True, "tooltip" : {"xDateFormat" : "%Y-%m-%d %H:%M:%S, %A"}, "title" : {"text" : "Spread Analysis Chart"}, "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": "Spread"}, "opposite": False }, "series": [{ "name": "line1", "id": "Line 1,buy1Price", "data": [] }, { "name": "line2", "id": "Line 2,lastPrice", "dashStyle": "shortdash", "data": [] }] } def main(): ObjChart = Chart(chart) ObjChart.reset() while True: 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)
    rust
    fn main() { // 在 Rust 中,图表配置为 JSON 字符串;在调用 Chart::new 函数之前,先定义图表配置 let chart = r#"{ "__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 }, "xAxis": {"type": "datetime"}, "yAxis": { "title": {"text": "差价"}, "opposite": false }, "series": [ {"name": "line1", "id": "Line 1,buy1Price", "data": []}, {"name": "line2", "id": "Line 2,lastPrice", "dashStyle": "shortdash", "data": []} ] }"#; // 调用 Chart::new 函数,初始化图表 let obj_chart = Chart::new(chart); // 清空 obj_chart.reset(0); loop { // 获取本次轮询的时间戳(即毫秒级时间戳),用于确定写入图表的 X 轴位置 let now_time = Unix() * 1000; // 获取行情数据 let ticker = _C!(exchange.GetTicker(None)); // 从行情数据的返回值中取得买一价 let buy1_price = ticker.Buy; // 取得最新成交价,为避免两条线相互重合,此处将其加 1 let last_price = ticker.Last + 1.0; // 以时间戳作为 X 值、买一价作为 Y 值,传入索引 0 的数据序列 obj_chart.add(0, &format!("[{}, {}]", now_time, buy1_price), -1); // 同上 obj_chart.add(1, &format!("[{}, {}]", now_time, last_price), -1); Sleep(2000); } }
    c++
    void main() { // 使用 C++ 编写策略时,尽量不要声明非基础类型的全局变量,因此将图表配置对象声明在 main 函数内 json chart = R"({ "__isStock" : true, "tooltip" : {"xDateFormat" : "%Y-%m-%d %H:%M:%S, %A"}, "title" : {"text" : "Spread Analysis Chart"}, "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": "Spread"}, "opposite": false }, "series": [{ "name": "line1", "id": "Line 1,buy1Price", "data": [] }, { "name": "line2", "id": "Line 2,lastPrice", "dashStyle": "shortdash", "data": [] }] })"_json; auto ObjChart = Chart(chart); ObjChart.reset(); while(true) { 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); } }
  • 绘制三角函数曲线的示例:

    javascript
    // 用于初始化图表的配置对象 var chart = { // 图表标题 title: {text: "Line value triggers plotLines value"}, // Y 轴相关设置 yAxis: { // 垂直于 Y 轴的水平线,用作触发线;这是一个结构体数组,可设置多条触发线 plotLines: [{ // 触发线的值,该线将显示在对应的数值位置 value: 0, // 设置触发线的颜色 color: 'red', // 线宽 width: 2, // 显示的标签 label: { // 标签文本 text: 'Trigger Value', // 标签居中对齐 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) } }
    python
    import math import time chart = { "title": {"text": "Line value triggers plotLines value"}, "yAxis": { "plotLines": [{ "value": 0, "color": "red", "width": 2, "label": { "text": "Trigger Value", "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)
    rust
    fn main() { // 用于初始化图表的 JSON 配置字符串,触发线的值在配置中直接设置为 1 let chart = r#"{ "title": {"text": "Line value triggers plotLines value"}, "yAxis": { "plotLines": [{ "value": 1, "color": "red", "width": 2, "label": { "text": "Trigger Value", "align": "center" } }] }, "xAxis": {"type": "datetime"}, "series": [{"name": "sin", "type": "spline", "data": []}, {"name": "cos", "type": "spline", "data": []}] }"#; // 圆周率 let pi = 3.1415926535897_f64; // 角度 let mut angle = 0.0_f64; // 调用 API 接口,使用 chart 配置初始化图表 let obj_chart = Chart::new(chart); // 初始化时清空图表 obj_chart.reset(0); // 循环 loop { // 获取当前时刻的毫秒时间戳 let ts = Unix() * 1000; // 将角度 angle 增加 5 度,并计算正弦值 let mut y = (angle * 2.0 * pi / 360.0).sin(); // 将计算得到的 y 值写入图表对应索引的数据系列,add 函数的第一个参数为指定的数据系列索引 obj_chart.add(0, &format!("[{}, {}]", ts, y), -1); // 计算余弦值 y = (angle * 2.0 * pi / 360.0).cos(); obj_chart.add(1, &format!("[{}, {}]", ts, y), -1); // 增加 5 度 angle += 5.0; // 暂停 5 秒,避免绘图过于频繁、数据增长过快 Sleep(5000); } }
    c++
    void main() { json chart = R"({ "title": {"text": "Line value triggers plotLines value"}, "yAxis": { "plotLines": [{ "value": 0, "color": "red", "width": 2, "label": { "text": "Trigger Value", "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); } }
  • 使用混合图表的复杂示例:

    javascript
    /*backtest start: 2020-03-11 00:00:00 end: 2020-04-09 23:59:00 period: 1d exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}] */ 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) { 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; } }
    python
    '''backtest start: 2020-03-11 00:00:00 end: 2020-04-09 23:59:00 period: 1d exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}] ''' 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: 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
    rust
    /*backtest start: 2020-03-11 00:00:00 end: 2020-04-09 23:59:00 period: 1d exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}] */ fn main() { // 在 Rust 中,图表配置以 JSON 字符串形式表示;饼图数据使用占位符 __PIE_DATA__ 标记,更新时替换该占位符后重建配置 let chart_cfg_tpl = 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": __PIE_DATA__, "center": ["3%", "6%"], "size": "15%", "dataLabels": {"enabled": false}, "startAngle": -90, "endAngle": 90 }] }"##; let c = Chart::new(&chart_cfg_tpl.replace("__PIE_DATA__", "[]")); let mut pre_ticker: Option<Ticker> = None; loop { let t = exchange.GetTicker(None).unwrap(); c.add(0, &format!("[{}, {}]", t.Time, t.Last), -1); // PnL let profit = if let Some(p) = &pre_ticker { t.Last - p.Last } else { 0.0 }; c.add(1, &format!("[{}, {}]", t.Time, profit), -1); // profit let r = (UnixNano() % 100) as f64 / 100.0; // 使用时间戳模拟随机数 let pos = (t.Time / 86400) as f64; c.add(2, &format!("[{}, {}]", t.Time, pos / 2.0), -1); // Vol c.add(3, &format!("[{}, {}]", t.Time, if r > 0.8 { pos.to_string() } else { "null".to_string() }), -1); // Long c.add(4, &format!("[{}, {}]", t.Time, if r < 0.8 { (-pos).to_string() } else { "null".to_string() }), -1); // Short c.add(5, &format!("[{}, {}]", t.Time, (UnixNano() % 10000) as f64 / 100.0), -1); // Asset // update pie let pie = format!(r#"[["A", {}], ["B", {}]]"#, (UnixNano() % 100) as f64, (UnixNano() % 100) as f64); c.update(&chart_cfg_tpl.replace("__PIE_DATA__", &pie)); pre_ticker = Some(t); } }
    c++
    /*backtest start: 2020-03-11 00:00:00 end: 2020-04-09 23:59:00 period: 1d exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}] */ 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) { 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; } }
  • 图表中pie类型的图表没有时间轴,因此在更新数据时需要直接更新图表配置。例如,在上述范例的代码中,更新数据后调用c.update(chartCfg)即可刷新图表,如下所示:

    javascript
    // update pie chartCfg.series[chartCfg.series.length-1].data = [ ["A", Math.random()*100], ["B", Math.random()*100], ]; c.update(chartCfg)
    python
    # update pie chartCfg["series"][len(chartCfg["series"]) - 1]["data"] = [ ["A", random.random() * 100], ["B", random.random() * 100] ] c.update(chartCfg)
    rust
    // update pie // Rust 中图表配置为 JSON 字符串,重建包含新数据的配置后调用 update 更新图表 let pie = format!(r#"[["A", {}], ["B", {}]]"#, (UnixNano() % 100) as f64, (UnixNano() % 100) as f64); c.update(&chart_cfg_tpl.replace("__PIE_DATA__", &pie));
    c++
    // 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);

返回值

类型描述

object

图表对象。

参数

名称类型必填描述

options

object / object数组

options参数为图表配置。Chart()函数的参数options是可以进行JSON序列化的HighStocksHighcharts.StockChart参数,相比原生参数增加了一个__isStock属性。如果将__isStock属性设置为假值(例如false),则显示为普通图表,即使用Highcharts图表;如果将__isStock属性设置为真值(例如true),则使用Highstocks图表(默认__isStock为真值,例如true)。详情可查询HighStocks图表库

参考

备注

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()方法用于清空图表数据,可带一个参数remain,用于指定保留数据的条数。不传入参数remain时,表示清除全部数据。

该函数用于采用类似Pine语言的绘图方式,在策略运行时进行自定义绘图。

KLineChart(options)

示例

  • 如果需要在策略自定义画图区域进行画图,必须先创建图表控制对象,使用KLineChart()函数即可创建该对象。KLineChart()函数的参数为一个图表配置结构,参考代码中使用的图表配置结构非常简单:{overlay: true}

    该图表配置结构仅设置将画图内容输出在图表主图上。如果overlay设置为假值(例如false),则图表内容将全部输出在副图上;如果需要指定某个画图函数在主图上绘制,也可以在具体的函数调用中将参数overlay指定为真值(例如true)。

    javascript
    function main() { // 调用KLineChart函数创建图表控制对象c let c = KLineChart({ overlay: true }) // 使用现货交易所对象测试,获取K线数据。如果使用期货交易所对象测试,需要先设置合约 let bars = exchange.GetRecords() if (!bars) { return } // 遍历K线数据执行画图操作,每次画图操作必须以```c.begin(bar)```函数调用作为起始,以```c.close(bar)```函数调用作为结束。 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(bar) }) }
    python
    def main(): # 调用KLineChart函数创建图表控制对象c c = KLineChart({ "overlay": True }) # 使用现货交易所对象测试,获取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(bar)
    rust
    fn main() { // 调用KLineChart::new创建图表控制对象c let mut c = KLineChart::new(r#"{"overlay": true}"#); // 使用现货交易所对象测试,获取K线数据。如果使用期货交易所对象测试,需要先设置合约 let bars = exchange.GetRecords(None, None, None).unwrap(); // 遍历K线数据执行画图操作,每次画图操作必须以c.begin(bar)函数调用作为起始,以c.close()函数调用作为结束。 for bar in &bars { c.begin(bar); c.barcolor(if bar.Close > bar.Open { "rgba(255, 0, 0, 0.2)" } else { "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, r#"{"title": "high"}"#); let l = c.plot(bar.Low, r#"{"title": "low"}"#); c.fill(h, l, if bar.Close > bar.Open { r#"{"color": "rgba(255, 0, 0, 0.2)"}"# } else { r#"{"color": "rgba(255, 0, 0, 0.2)"}"# }); c.hline(bar.High, "{}"); c.plotarrow(bar.Close - bar.Open, "{}"); c.plotshape(bar.Low > 0.0, r#"{"style": "diamond"}"#); c.plotchar(bar.Close > 0.0, r#"{"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, "long"); } else if bar.Close < bar.Open { c.signal("closelong", bar.Low, 1.5, "closelong"); } c.close(); } }
    c++
    // 暂不支持
  • 使用 pricePrecisionvolumePrecision 参数控制图表数据的显示精度。可根据实际需求设置价格与成交量的显示精度,例如对于价格波动较大的品种,可将精度设置为 0 以显示整数;对于价格较为精细的品种,可设置为 2 或更高精度。

    javascript
    function main() { // 创建图表控制对象,将价格精度与成交量精度均设置为 0(即显示整数) let c = KLineChart({ overlay: true, pricePrecision: 0, // 价格数据精度,设置为 2 即保留 2 位小数 volumePrecision: 0 // 成交量数据精度 }) // 根据交易所类型选择合适的交易对 let symbol = exchange.GetName().includes("Futures_") ? "ETH_USDT.swap" : "ETH_USDT" Log("Test symbol:", symbol) // 获取 K 线数据 let bars = exchange.GetRecords(symbol) if (!bars) { return } // 遍历 K 线数据并绘制图表 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)') c.plot(bar.High, 'high') c.plot(bar.Low, 'low') c.close(bar) }) }
    python
    def main(): # 创建图表控制对象,将价格精度与成交量精度均设置为 0(即显示整数) c = KLineChart({ "overlay": True, "pricePrecision": 0, # 价格数据精度,设置为 2 即保留 2 位小数 "volumePrecision": 0 # 成交量数据精度 }) # 根据交易所类型选择合适的交易对 exName = exchange.GetName() symbol = "ETH_USDT.swap" if "Futures_" in exName else "ETH_USDT" Log("Test symbol:", symbol) # 获取 K 线数据 bars = exchange.GetRecords(symbol) if not bars: return # 遍历 K 线数据并绘制图表 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)') c.plot(bar.High, 'high') c.plot(bar.Low, 'low') c.close(bar)
    rust
    fn main() { // 创建图表控制对象,将价格精度与成交量精度均设置为 0(即显示整数) // pricePrecision 为价格数据精度,设置为 2 即保留 2 位小数;volumePrecision 为成交量数据精度 let mut c = KLineChart::new(r#"{"overlay": true, "pricePrecision": 0, "volumePrecision": 0}"#); // 根据交易所类型选择合适的交易对 let symbol = if exchange.GetName().contains("Futures_") { "ETH_USDT.swap" } else { "ETH_USDT" }; Log!("Test symbol:", symbol); // 获取 K 线数据 let bars = exchange.GetRecords(symbol, None, None).unwrap(); // 遍历 K 线数据并绘制图表 for bar in &bars { c.begin(bar); c.barcolor(if bar.Close > bar.Open { "rgba(255, 0, 0, 0.2)" } else { "rgba(0, 0, 0, 0.2)" }, "{}"); c.plot(bar.High, r#"{"title": "high"}"#); c.plot(bar.Low, r#"{"title": "low"}"#); c.close(); } }
    c++
    // 暂不支持
  • 绘图操作中支持的Pine语言绘图接口函数如下:

    barcolor:设置K线颜色。

    barcolor(color, offset, editable, show_last, title, display)

    display参数的可选值为:"none", "all"

    javascript
    c.barcolor(bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.2)') // 用法同上例中的参考代码,此处不再赘述
    python
    c.barcolor('rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(0, 0, 0, 0.2)')
    rust
    c.barcolor(if bar.Close > bar.Open { "rgba(255, 0, 0, 0.2)" } else { "rgba(0, 0, 0, 0.2)" }, "{}"); // 用法同上例中的参考代码,此处不再赘述
    c++
    // 暂不支持
  • bgcolor:使用指定颜色填充K线背景。

    bgcolor(color, offset, editable, show_last, title, display, overlay)

    display参数的可选值为:"none", "all"

    javascript
    c.bgcolor('rgba(0, 255, 0, 0.5)')
    python
    c.bgcolor('rgba(0, 255, 0, 0.5)')
    rust
    c.bgcolor("rgba(0, 255, 0, 0.5)", "{}");
    c++
    // 暂不支持
  • 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"

    javascript
    c.plot(bar.High, 'high') c.plot(bar.Open < bar.Close ? NaN : bar.Close, "Close", {style: "linebr"}) // 支持绘制不连续的数据线
    python
    h = c.plot(bar.High, 'high') h = c.plot(None if bar.Open < bar.Close else bar.Close, "Close", style = "linebr") # 支持绘制不连续的数据线
    rust
    let h = c.plot(bar.High, r#"{"title": "high"}"#); c.plot(if bar.Open < bar.Close { f64::NAN } else { bar.Close }, r#"{"title": "Close", "style": "linebr"}"#); // 支持绘制不连续的数据线
    c++
    // 暂不支持
  • 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'方式设置。

    javascript
    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)'})
    python
    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)')
    rust
    let h = c.plot(bar.High, r#"{"title": "high"}"#); let l = c.plot(bar.Low, r#"{"title": "low"}"#); c.fill(h, l, if bar.Close > bar.Open { r#"{"color": "rgba(255, 0, 0, 0.2)"}"# } else { r#"{"color": "rgba(255, 0, 0, 0.2)"}"# });
    c++
    // 暂不支持
  • hline,在给定的固定价格水平上绘制水平线。

    hline(price, title, color, linestyle, linewidth, editable, display)

    linestyle参数可选:"dashed", "dotted", "solid"

    display参数可选:"none", "all"

    javascript
    c.hline(bar.High)
    python
    c.hline(bar.High)
    rust
    c.hline(bar.High, "{}");
    c++
    // 暂不支持
  • plotarrow,在图表上绘制向上和向下的箭头。

    plotarrow(series, title, colorup, colordown, offset, minheight, maxheight, editable, show_last, display)

    display参数可选:"none", "all"

    javascript
    c.plotarrow(bar.Close - bar.Open)
    python
    c.plotarrow(bar.Close - bar.Open)
    rust
    c.plotarrow(bar.Close - bar.Open, "{}");
    c++
    // 暂不支持
  • 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"

    javascript
    c.plotshape(bar.Low, {style: 'diamond'})
    python
    c.plotshape(bar.Low, style = 'diamond')
    rust
    c.plotshape(bar.Low > 0.0, r#"{"style": "diamond"}"#);
    c++
    // 暂不支持
  • 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"

    javascript
    c.plotchar(bar.Close, {char: 'X'})
    python
    c.plotchar(bar.Close, char = 'X')
    rust
    c.plotchar(bar.Close > 0.0, r#"{"char": "X"}"#);
    c++
    // 暂不支持
  • plotcandle,在图表上绘制K线图。

    plotcandle(open, high, low, close, title, color, wickcolor, editable, show_last, bordercolor, display)
    display参数可选:"none", "all"

    javascript
    c.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)
    python
    c.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)
    rust
    c.plotcandle(bar.Open * 0.9, bar.High * 0.9, bar.Low * 0.9, bar.Close * 0.9, "{}");
    c++
    // 暂不支持
  • signal,此为Pine语言中不存在的函数,此处用于绘制买卖信号。

    signal(direction, price, qty, id)

    传入的参数"long"表示交易方向,可选"long"、"closelong"、"short"、"closeshort"。传入的参数bar.High表示标记信号在Y轴上的位置。
    传入的参数1.5表示信号的交易数量。可传入第四个参数以替换默认绘制的文本内容;信号标记的默认文本为交易方向,例如:"closelong"。

    javascript
    c.signal("long", bar.High, 1.5)
    python
    c.signal("long", bar.High, 1.5)
    rust
    c.signal("long", bar.High, 1.5, "long");
    c++
    // 暂不支持
  • reset,此为Pine语言中不存在的函数,用于清空图表数据。

    reset(remain)

    reset()方法可接受一个参数remain,用于指定保留数据的条数。若不传入remain参数,则表示清除全部数据。

    javascript
    c.reset()
    python
    c.reset()
    rust
    c.reset(0);
    c++
    // 暂不支持

返回值

类型描述

object

图表对象。

KLineChart()函数返回的图表对象包含多个方法,其中需要特别注意begin(bar)close(bar)。在遍历K线数据执行绘图操作时,绘图操作必须以begin(bar)函数调用作为起始,并以close(bar)函数调用作为结束。

参数

名称类型必填描述

options

object / object数组

options参数为图表配置对象,支持以下属性:

  • overlay:布尔值,用于设置绘图内容是否叠加输出到主图。设置为true时在主图显示,设置为false时在副图显示。

  • pricePrecision:数字,价格数据精度,用于控制图表中价格数据的小数位数。例如,设置为2表示保留2位小数,设置为0表示不保留小数(四舍五入为整数)。

  • volumePrecision:数字,成交量数据精度,用于控制图表中成交量数据的小数位数。例如,设置为2表示保留2位小数,设置为0表示不保留小数(四舍五入为整数)。

参考

备注

策略自定义绘图只能选用KLineChart()函数或Chart()函数两种方式中的一种。有关KLineChart()函数调用时所涉及的颜色、样式等设置,请参阅使用KLineChart函数绘图的专题文章

pricePrecisionvolumePrecision参数用于控制图表中数据的显示精度。当未设置这些参数时,图表将使用默认精度显示数据。设置精度参数后,图表中的价格和成交量数据将按照指定的小数位数进行四舍五入显示,这有助于简化图表显示、提升可读性。

清除日志。

LogReset(remain)

示例

javascript
function main() { // 保留最近10条日志,清除其余日志 LogReset(10) }
python
def main(): LogReset(10)
rust
fn main() { // 保留最近10条日志,清除其余日志 LogReset(10); }
c++
void main() { LogReset(10); }

参数

名称类型必填描述

remain

number

remain 参数用于设置需要保留的最近日志条数。

参考

备注

策略实盘每次启动时的启动日志会计为一条,因此如果不传入参数,且策略启动时没有任何日志输出,日志将完全不予显示,需等待托管者回传日志(此为正常现象,并非异常情况)。

用于在调用 LogReset() 函数清除日志后,回收 SQLite 删除数据时所占用的存储空间。

LogVacuum()

示例

javascript
function main() { LogReset() LogVacuum() }
python
def main(): LogReset() LogVacuum()
rust
fn main() { LogReset(0); LogVacuum(); }
c++
void main() { LogReset(); LogVacuum(); }

参考

备注

原因在于 SQLite 删除数据时并不会立即回收所占用的存储空间,需要执行 VACUUM 命令清理数据表以释放空间。该函数在调用时会触发文件移动操作,延迟较大,建议按合适的时间间隔调用。

用于在实盘页面的「调试信息」栏中输出调试信息。例如,实盘ID为123456时,console.log函数在实盘页面输出调试信息的同时,会在实盘所属托管者目录/logs/storage/123456/下创建一个扩展名为.log的日志文件并写入调试信息,文件名前缀为stdout_

console.log(...msgs)

示例

javascript
function main() { console.log("test console.log") }
python
# 不支持
c++
// 不支持

参数

名称类型必填描述

msg

string / number / bool / object / array / any (平台支持的任意类型)

参数msg为输出的内容,可以传递多个参数。

参考

备注

注意事项:

  • JavaScript语言支持此函数。
  • 仅实盘环境支持此函数,「调试工具」和「回测系统」均不支持。
  • 输出对象时会被转换为字符串[object Object],因此建议输出可读的信息。

用于在实盘页面的「调试信息」栏中输出错误信息。例如,实盘ID为123456时,console.error函数在实盘页面输出错误信息的同时,会在实盘所属托管者目录/logs/storage/123456/下创建一个以stderr_为前缀、.log为扩展名的日志文件,并将错误信息写入该文件。

console.error(...msgs)

示例

javascript
function main() { console.error("test console.error") }
python
# 不支持
c++
// 不支持

参数

名称类型必填描述

msg

string / number / bool / object / array / any (平台支持的任意类型)

参数msg为需要输出的内容,可以传入多个参数。

参考

备注

注意事项:

  • JavaScript语言支持此函数。
  • 仅实盘环境支持此函数,「调试工具」和「回测系统」不支持。
  • 输出对象时会被转换为字符串[object Object],建议输出可读性强的信息。