Log
Log
The Log() function is used to output logs.
Log(...msgs)Examples
-
Multiple
msgparameters can be passed:javascriptfunction main() { Log("msg1", "msg2", "msg3") }pythondef main(): Log("msg1", "msg2", "msg3")c++void main() { Log("msg1", "msg2", "msg3"); } -
Supports setting the color of output messages. If using both color settings and message push functionality, set the color first, then use the
@character at the end to set the push.javascriptfunction main() { Log("Hello FMZ Quant !@") Sleep(1000 * 5) // Add #ff0000 in the string, the printed log will be displayed in red, and the message will be pushed Log("Hello, #ff0000@") }pythondef main(): Log("Hello FMZ Quant !@") Sleep(1000 * 5) Log("Hello, #ff0000@")c++void main() { Log("Hello FMZ Quant !@"); Sleep(1000 * 5); Log("Hello, #ff0000@"); } -
The
Log()function supports printingbase64encoded images, starting with`and ending with`, for example:javascriptfunction main() { Log("`data:image/png;base64,AAAA`") }pythondef main(): Log("`data:image/png;base64,AAAA`")c++void main() { Log("`data:image/png;base64,AAAA`"); } -
The
Log()function supports directly printingPython'smatplotlib.pyplotobjects. As long as the object contains asavefigmethod, it can be printed directly using theLogfunction, for example:pythonimport matplotlib.pyplot as plt def main(): plt.plot([3,6,2,4,7,1]) Log(plt) -
The
Log()function supports language switching. The text output by theLog()function will automatically switch to the corresponding language based on the language settings on the platform page, for example:javascriptfunction main() { Log("[trans]中文|abc[/trans]") }pythondef main(): Log("[trans]中文|abc[/trans]")c++void main() { Log("[trans]中文|abc[/trans]"); }
Arguments
| Name | Type | Required | Description |
msg | string / number / bool / object / array / any (any type supported by the platform) | No | The parameter |
See Also
Remarks
The Log() function outputs a log message in the log area of the live trading or backtesting system. During live trading, logs are saved in the live trading database. When the content output by the Log() function ends with the @ character, the log will enter the message push queue and be pushed to the email address, WebHook URL, etc. configured in the Push Settings of the current FMZ Quant Trading platform account. The Debug Tool and backtesting system do not support message pushing. Message pushing has frequency limits. The specific limit rules are as follows: within a 20-second cycle of a live trading, only the last push message will be retained and pushed, while other messages will be filtered and not pushed (push logs output through the Log function will still be printed and displayed normally in the log area).
When the content output by the Log() function ends with the & character, the log will be marked as a private log. When the live trading is publicly displayed, this log will be hidden from other users, but will not be hidden from the live trading owner's account perspective. This can be used to record sensitive information such as API keys, account balances, etc. For example: Log("Private information", "&").
Regarding WebHook pushing, you can use a service program written in 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)
}
Set the WebHook in Push Settings: http://XXX.XX.XXX.XX:9090/data?data=Hello_FMZ. After running the compiled Golang service program, start the live trading strategy. The following is a strategy written in JavaScript. When the strategy runs, it executes the Log() function and pushes a message:
javascript
function main() {
Log("msg", "@")
}
After the service program written in Golang receives the push, the service program prints the information:
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
Record profit/loss values, print the profit/loss figures and draw the equity curve based on the profit/loss values.
LogProfit(profit)
LogProfit(profit, ...args)Examples
If the LogProfit function ends with the character &, it will not write the log to the database, but only update the profit chart. Using the & parameter can avoid generating a large number of logs from frequent profit recording, keeping the logs concise. For example:
javascript
function main() {
// Print 30 points on the profit chart
for(var i = 0; i < 30; i++) {
LogProfit(i, '&')
Sleep(500)
}
}
python
def main():
for i in range(30):
LogProfit(i, '&')
Sleep(500)
c++
void main() {
for(int i = 0; i < 30; i++) {
LogProfit(i, '&');
Sleep(500);
}
}Arguments
| Name | Type | Required | Description |
profit | number | Yes | The parameter |
arg | string / number / bool / object / array / any (any type supported by the platform) | No | Extended parameters that can output additional information to this profit log entry. Multiple |
See Also
LogProfitReset
Clear all profit logs and profit chart data.
LogProfitReset()
LogProfitReset(remain)Examples
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)
c++
void main() {
for(int i = 0; i < 30; i++) {
LogProfit(i);
Sleep(500);
}
LogProfitReset(10);
}Arguments
| Name | Type | Required | Description |
remain | number | No | The |
See Also
LogStatus
Output information in the status bar of the backtesting system or live trading page.
LogStatus(...msgs)Examples
-
Supports setting the color of output content:
javascriptfunction 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') }pythondef 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"); } -
Status bar data output example:
javascriptfunction main() { var table = {type: 'table', title: 'Position Info', cols: ['Column 1', 'Column 2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]} // After JSON serialization, add ` characters on both sides to be treated as complex message format (currently supports tables) LogStatus('`' + JSON.stringify(table) + '`') // Table information can also be displayed in multiple lines LogStatus('First line message\n`' + JSON.stringify(table) + '`\nThird line message') // Supports displaying multiple tables simultaneously, will be displayed as TABs in a group LogStatus('`' + JSON.stringify([table, table]) + '`') // You can also construct buttons in the table, the strategy receives the cmd attribute content through GetCommand 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) + '`') // Or construct a standalone button LogStatus('`' + JSON.stringify({'type':'button', 'cmd': 'coverAll', 'name': 'Close All'}) + '`') // You can customize button styles (Bootstrap button attributes) LogStatus('`' + JSON.stringify({'type':'button', 'class': 'btn btn-xs btn-danger', 'cmd': 'coverAll', 'name': 'Close All'}) + '`') }pythonimport 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"}) + '`')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() + "`"); } -
Support for designing button controls in the status bar (legacy button structure):
javascriptfunction 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) + "`") }pythonimport 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) + "`")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() + "`"); } -
Set status bar button disable and description features (legacy button structure):
javascriptfunction 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) + "`") }pythonimport 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) + "`")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() + "`"); } -
Combined with the
GetCommand()function to construct status bar button interaction functionality (legacy button structure):javascriptfunction 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) } }pythonimport 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)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); } } -
When constructing status bar buttons for interaction, data input is also supported. The interaction command is ultimately captured by the
GetCommand()function.Add an
inputfield to the data structure of the button control in the status bar (legacy button structure). For example, add"input": {"name": "Quantity", "type": "number", "defValue": 1}to{"type": "button", "cmd": "open", "name": "Open"}. This allows the button to pop up a dialog with an input box control when clicked (the default value in the input box is 1, as set bydefValue). You can enter data and send it along with the button command. For example, when running the following test code, clicking the "Open" button will pop up a dialog with an input box. After entering 111 in the input box and clicking "OK", theGetCommand()function will capture the message:open:111.javascriptfunction 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) } }pythonimport 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)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); } } -
Supports group button controls (legacy button structure), with functionality identical to status bar buttons with input data support (configured using the "input" field). Interactive commands are ultimately captured by the
GetCommand()function. The difference is that it uses the"group"field for configuration. When clicking the button triggers an interaction, the dialog box that pops up on the page contains a group of configured input controls, allowing you to input a set of data at once.Regarding the
"group"field in status bar button controls and group button control structures, note the following:- The
typeattribute in group only supports the following 4 types, and thedefValueattribute is the default value.
"selected": Dropdown control, use the|symbol to separate each option in the dropdown.
"number": Numeric input box control.
"string": String input box control.
"boolean": Checkbox control, checked is boolean true, unchecked is boolean false. - Interactive input controls support dependency settings:
For example, in the following example:"name": "tradePrice@orderType==1"setting, this makes the trade price (tradePrice) input control only available when the order type (orderType) dropdown control is set to limit order. - Interactive input control names support bilingual settings:
For example, in the following example: "description": "下单方式|order type" setting, use the|symbol to separate Chinese and English descriptions. - Although the
nameanddescriptionfields in group have the same field names as those in the button structure, their definitions are different.
Thenamein group also has a different definition from thenamein input. - After a group button control is triggered, the format of the sent interactive content is: the button's cmd field value, and group field related data. For example, when testing the following example, the content output by the
Log("cmd:", cmd)statement:
cmd: open:{"orderType":1,"tradePrice":99,"orderAmount":"99","boolean":true}, which is the content returned by theGetCommand()function when an interactive operation occurs:open:{"orderType":1,"tradePrice":99,"orderAmount":"99","boolean":true}. - The
typeattribute of button controls only supports:"button":
Button controls that support input data, i.e., controls with theinputattribute set, thetypeattribute in theinputfield configuration supports multiple control types.
Refer to the following example:
javascriptfunction main() { var tbl = { type: "table", title: "Group Button Control Demo", cols: ["Operation"], rows: [] } // Create group button control structure 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} ] } // Test button 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}} // Add groupBtn to tbl tbl.rows.push([groupBtn]) // Supports setting multiple buttons in a single cell of the status bar table, i.e., the data in a cell is an array of button structures: [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) } }pythonimport 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)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); } } - The
-
When the status bar group button control (implemented by setting the
groupfield) and the status bar button control (implemented by setting theinputfield) are clicked to trigger interaction (legacy button structure), the dropdown control in the dialog box that pops up on the page also supports multi-select. The following example demonstrates how to design a dropdown control with multi-select options:javascriptfunction main() { // The dropdown control in the page triggered by the status bar button control (implemented by setting the input field) testBtn1 button uses the options field to set options and the defValue field to set the default option. This differs from other examples in this chapter that directly use defValue to set options. var testBtn1 = { type: "button", name: "testBtn1", cmd: "cmdTestBtn1", input: {name: "testBtn1ComboBox", type: "selected", options: ["A", "B"], defValue: 1} } /* The dropdown control in the page triggered by the status bar button control (implemented by setting the input field) testBtn2 button uses the options field to set options. The options in the options field not only support strings, but also support using the ```{text: "description", value: "value"}``` structure. Use the defValue field to set the default option, which can be multi-select (implemented through an array structure). Multi-select requires setting the additional field multiple to a truthy value (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 } } // The dropdown control in the page triggered by the status bar group button control (implemented by setting the group field) testBtn3 button uses the options field to set options, and also supports directly using defValue to set options. 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: "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.stringify(testBtn1) + "`\n", "`" + JSON.stringify(testBtn2) + "`\n", "`" + JSON.stringify(testBtn3) + "`\n") var cmd = GetCommand() if (cmd) { Log(cmd) } Sleep(5000) } }pythonimport 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)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); } } -
Using the latest button structure, construct buttons in the status bar table. When clicking a button triggers an interaction, a multi-control popup will appear.
For detailed information, please refer to: User Guide - Interactive Controls in Status Bar.
javascriptvar 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) // Parse interaction message: open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111} // Determine which button template triggered the message based on the command before the first colon 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) } }pythonimport 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) # Parse interaction message: open:{"symbol":"LTC_USDT.swap","tradeType":0,"direction":"buy","amount":111} # Determine which button template triggered the message based on the command before the first colon 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"]) # Output status bar information LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`") Sleep(1000)c++// Omitted... -
Horizontally merge cells in the table drawn by the
LogStatus()function:javascriptfunction 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() // Add a row of data, merge the first and second cells, and output the ticker variable in the merged cell table.rows.push([{body : JSON.stringify(ticker), colspan : 2}, "abc"]) LogStatus('`' + JSON.stringify(table) + '`') }pythonimport 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) + "`")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() + "`"); } -
Vertically merge cells in the table drawn by the
LogStatus()function:javascriptfunction 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 is merged by the first cell of the previous row table.rows.push(["B4", "C4"]) // A4 is merged by the first cell of the previous row table.rows.push(["B5", "C5"]) table.rows.push(["A6", "B6", "C6"]) LogStatus('`' + JSON.stringify(table) + '`') }pythonimport 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) + "`")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; // For testing purposes, to make the code short and readable, constructed data is used here 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() + "`"); } -
Status bar table pagination display:
javascriptfunction 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]) + '`') }pythonimport 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]) + "`")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() + "`"); } -
In addition to paginated table display, you can also arrange multiple tables vertically from top to bottom:
javascriptfunction 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") }pythonimport 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) + "`")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() + "`"); } -
Supports setting horizontal and vertical scroll modes for the status bar table. When the
scrollproperty is set to"auto", the content will automatically scroll when the status bar table has more than 20 rows vertically; when the horizontal columns exceed the page display range, horizontal scrolling will be enabled. Using thescrollproperty can effectively alleviate the lag issues caused by writing large amounts of data to the status bar during live trading.Refer to the following test example:
javascriptfunction 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) + "`") }pythonimport 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) + "`")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() + "`"); }
Arguments
| Name | Type | Required | Description |
msg | string / number / bool / object / array / any (any type supported by the platform) | No | The parameter |
See Also
Remarks
During live trading, the information output by the LogStatus() function is not saved to the live trading database; it only updates the status bar content of the current live trading.
The LogStatus() function supports printing base64 encoded images, starting with ` and ending with `. For example: LogStatus("`data:image/png;base64,AAAA`").
The LogStatus() function supports directly passing Python's matplotlib.pyplot object. As long as the object contains the savefig method, it can be passed as a parameter to the LogStatus() function. For example:
python
import matplotlib.pyplot as plt
def main():
plt.plot([3,6,2,4,7,1])
LogStatus(plt)
When the strategy is running live, if you browse historical records on the live trading page, the status bar will enter sleep mode and stop updating. The status bar data will only refresh when the log is on the first page. It supports outputting base64 encoded images in the status bar, and also supports outputting base64 encoded images in tables displayed in the status bar. Since the encoded image string data is usually very long, example code is not shown here.
EnableLog
Enable or disable the logging function for order information.
EnableLog(enable)Examples
javascript
function main() {
EnableLog(false)
}
python
def main():
EnableLog(False)
c++
void main() {
EnableLog(false);
}Arguments
| Name | Type | Required | Description |
enable | bool | Yes | When the |
See Also
Chart
Custom chart binding function.
Chart(options)Examples
-
Multi-chart binding configuration:
extension.layoutproperty
If this property is set with value "single", charts will not be stacked (will not display as tabbed pages), but will be displayed individually (tiled display).extension.heightproperty
This property is used to set the chart height, value can be numeric type, or set in "300px" format.extension.colproperty
This property is used to set the chart width, page width is divided into 12 units total, setting to 8 means this chart occupies 8 units of width.
javascriptfunction main() { var cfgA = { extension: { layout: 'single', // Do not participate in grouping, display individually, default is grouping 'group' height: 300, // Specify height }, 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], ] // After specifying initial data, no need to use add function to update, directly modify chart configuration to update series }] }; var cfgD = { extension: { layout: 'single', col: 8, // Specify width unit value occupied, total value is 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 a data point to pie chart, add can only update data points added through add method, built-in data points cannot be updated later 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]]); // Equivalent to updating the first data series of the second chart 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 actually equals resetting the chart configuration chart.update([cfgA, cfgB, cfgC, cfgD, cfgE]); } }pythonimport 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() * 100c++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"] = format("Bid 1 %f, Ask 1 %f", ticker.Buy, ticker.Sell); cfgA["subtitle"] = cfgASubTitle; json cfgBSubTitle = R"({"text" : ""})"_json; cfgBSubTitle["text"] = format("Spread %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}); } } -
Simple charting example:
javascript// This chart is an object in JavaScript language. Before using the Chart function, we need to declare an object variable chart to configure the chart var chart = { // This field marks whether the chart is a general chart. Interested users can change it to false and run to see the effect __isStock: true, // Zoom tool tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'}, // Title title : { text : 'Spread Analysis Chart'}, // Range selector 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-axis of coordinate system, currently set type is: time xAxis: { type: 'datetime'}, // Y-axis of coordinate system, default values adjust according to data size yAxis : { // Title title: {text: 'Spread'}, // Whether to enable right Y-axis opposite: false }, // Data series, this property stores various data series (lines, candlestick charts, labels, etc.) series : [ // Index 0, data array stores data for this index series {name : "line1", id : "Line 1,buy1Price", data : []}, // Index 1, set dashStyle:'shortdash' to create dashed line {name : "line2", id : "Line 2,lastPrice", dashStyle : 'shortdash', data : []} ] } function main(){ // Call Chart function to initialize chart var ObjChart = Chart(chart) // Clear ObjChart.reset() while(true){ // Get timestamp for current polling, i.e., a millisecond-level timestamp to determine X-axis position in chart var nowTime = new Date().getTime() // Get market data var ticker = _C(exchange.GetTicker) // Get bid price from market data return value var buy1Price = ticker.Buy // Get last traded price, add 1 to prevent line overlap var lastPrice = ticker.Last + 1 // Use timestamp as X value, bid price as Y value, pass to data series at index 0 ObjChart.add(0, [nowTime, buy1Price]) // Same as above ObjChart.add(1, [nowTime, lastPrice]) Sleep(2000) } }pythonimport 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)c++void main() { // When writing strategies in C++, try to avoid declaring non-basic type global variables, so chart configuration object is declared inside main function 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); } } -
Trigonometric curve plotting example:
javascript// Object for initializing the chart var chart = { // Chart title title: {text: "Line value triggers plotLines value"}, // Y-axis related settings yAxis: { // Horizontal lines perpendicular to Y-axis, used as trigger lines, is a structure array that can set multiple trigger lines plotLines: [{ // Trigger line value, this line will be displayed at the corresponding numerical position value: 0, // Set trigger line color color: 'red', // Width width: 2, // Display label label: { // Label text text: 'Trigger Value', // Label position centered align: 'center' } }] }, // X-axis related settings, here set type as time axis xAxis: {type: "datetime"}, series: [ {name: "sin", type: "spline", data: []}, // This is an important data series, multiple data series can be set, controlled by array index {name: "cos", type: "spline", data: []} ] } function main(){ // Pi var pi = 3.1415926535897 // Variable for recording timestamp var time = 0 // Angle var angle = 0 // Coordinate y value, used to receive sine and cosine values var y = 0 // Call API interface, use chart object to initialize chart var objChart = Chart(chart) // Clear chart on initialization objChart.reset() // Set trigger line value to 1 chart.yAxis.plotLines[0].value = 1 // Loop while(true){ // Get current timestamp time = new Date().getTime() // Every 500ms angle increases by 5 degrees, calculate sine value y = Math.sin(angle * 2 * pi / 360) // Write calculated y value to corresponding index data series in chart, first parameter of add function is specified data series index objChart.add(0, [time, y]) // Calculate cosine value y = Math.cos(angle * 2 * pi / 360) objChart.add(1, [time, y]) // Increase by 5 degrees angle += 5 // Pause for 5 seconds to avoid too frequent plotting and rapid data growth Sleep(5000) } }pythonimport 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)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); } } -
Complex example using mixed charts:
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 = tc++/*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; } } -
Charts of
pietype in the chart are charts without a time axis. When updating data, the chart configuration needs to be updated directly. For example, in the code of the above example, after updating the data, usec.update(chartCfg)to update the chart, as shown below: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)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);
Returns
| Type | Description |
object | Chart object. |
Arguments
| Name | Type | Required | Description |
options | object / object array | Yes | The |
See Also
Remarks
The Chart() function returns a chart object with 4 methods: add(), reset(), update(), del().
-
update()method:
Theupdate()method can update chart configuration information. The parameter of this method is a Chart configuration object (JSON).
-
del()method:
Thedel()method can delete the data series at the specified index based on the passed series parameter.
-
add()method:
Theadd()method can write data to the chart. The parameters are:
series: Used to set the data series index, integer type.data: Used to set the specific data to write, array type.index(optional): Used to set the data index, integer type. Specifies the specific index position of the data to modify, supports negative numbers. Setting it to-1represents the last data in the dataset.
For example, when drawing a line, to modify the data on the last point of the line:chart.add(0, [1574993606000, 13.5], -1), which changes the data of the last point inseries[0].dataof the chart. Not setting theindexparameter means adding data to the end of the current data series.
-
reset()method:
Thereset()method is used to clear chart data. Thereset()method can take a parameterremainto specify the number of data entries to retain. Not passing theremainparameter means clearing all data.
KLineChart
This function is used for custom charting during strategy runtime, using a drawing method similar to the Pine language.
KLineChart(options)Examples
-
To draw on the strategy's custom charting area, you must have a chart control object, created using the
KLineChart()function. The parameter of theKLineChart()function is a chart configuration structure. The chart structure used in the reference code is very simple:{overlay: true}.This chart configuration structure only sets the drawing content to be output on the main chart. If
overlayis set to a false value, such asfalse, all content on the chart will be output on the sub-chart. If you need to specify a particular drawing function to bindbindoutput on the main chart, you can also specify theoverlayparameter as a true value in the specific function call, such astrue.javascriptfunction main() { // Call KLineChart function to create chart control object c let c = KLineChart({ overlay: true }) // Use spot exchange object for testing, get K-line data. If using futures exchange object for testing, you need to set the contract first let bars = exchange.GetRecords() if (!bars) { return } // Iterate over K-line data to perform drawing operations. Drawing operations must start with ```c.begin(bar)``` function call and end with ```c.close(bar)``` function call. 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) }) }pythondef main(): # Call KLineChart function to create chart control object c c = KLineChart({ "overlay": True }) # Use spot exchange object for testing, get K-line data. If using futures exchange object for testing, you need to set the contract first 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)c++// Not supported currently -
Use the
pricePrecisionandvolumePrecisionparameters to control chart data precision. You can set the display precision for price and volume according to actual needs. For example, for instruments with large price fluctuations, you can set it to 0 to display integers, while for instruments requiring precise prices, you can set it to 2 or higher precision.javascriptfunction main() { // Create chart control object, set price precision to 0 (integer), volume precision to 0 (integer) let c = KLineChart({ overlay: true, pricePrecision: 0, // Price data precision, set to 2 to keep 2 decimal places volumePrecision: 0 // Volume data precision }) // Select appropriate trading pair based on exchange type let symbol = exchange.GetName().includes("Futures_") ? "ETH_USDT.swap" : "ETH_USDT" Log("Test symbol:", symbol) // Get K-line data let bars = exchange.GetRecords(symbol) if (!bars) { return } // Iterate through K-line data and draw chart 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) }) }pythondef main(): # Create chart control object, set price precision to 0 (integer), volume precision to 0 (integer) c = KLineChart({ "overlay": True, "pricePrecision": 0, # Price data precision, set to 2 to keep 2 decimal places "volumePrecision": 0 # Volume data precision }) # Select appropriate trading pair based on exchange type exName = exchange.GetName() symbol = "ETH_USDT.swap" if "Futures_" in exName else "ETH_USDT" Log("Test symbol:", symbol) # Get K-line data bars = exchange.GetRecords(symbol) if not bars: return # Iterate through K-line data and draw chart 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)c++// Not supported currently -
The
Pinelanguage charting interface functions supported in bindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindbindcharting operations include:barcolor, used to set K-line color.barcolor(color, offset, editable, show_last, title, display)
Optional values for display parameter: "none", "all"
javascriptc.barcolor(bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.2)') // Using the example from the reference code in this example, no further elaboration neededpythonc.barcolor('rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(0, 0, 0, 0.2)')c++// Not supported currently -
bgcolor, used to fill the K-line background with a specified color.bgcolor(color, offset, editable, show_last, title, display, overlay)
Optional values for display parameter: "none", "all"
javascriptc.bgcolor('rgba(0, 255, 0, 0.5)')pythonc.bgcolor('rgba(0, 255, 0, 0.5)')c++// Not supported currently -
plot, plots a series of data on the chart.plot(series, title, color, linewidth, style, trackprice, histbase, offset, join, editable, show_last, display)
style parameter options: "stepline_diamond", "stepline", "cross", "areabr", "area", "circles", "columns", "histogram", "linebr", "line"
display parameter options: "none", "all"javascriptc.plot(bar.High, 'high') c.plot(bar.Open < bar.Close ? NaN : bar.Close, "Close", {style: "linebr"}) // Supports plotting discontinuous data linespythonh = c.plot(bar.High, 'high') h = c.plot(None if bar.Open < bar.Close else bar.Close, "Close", style = "linebr") # Supports plotting discontinuous data linesc++// Not supported yet -
fill, fills the background area between two plots orhlinewith the specified color.fill(hline1, hline2, color, title, editable, fillgaps, display)
display parameter options: "none", "all"Since
JavaScriptdoes not support specifying parameters by function parameter names, to solve this problem, you can use the{key: value}structure to specify parameters for specific parameter names. For example, in the reference code,{color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)'}is used to specify thecolorparameter of thefillfunction.If you need to specify multiple parameters by parameter names consecutively, you can use the
{key1: value1, key2: value2, key3: value3}format.For example, to add a
titleparameter in this example:{color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)', title: 'fill'}.Color values can be set using the
'rgba(255, 0, 0, 0.2)'format or the'#FF0000'format.javascriptlet 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)'})pythonh = 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)')c++// Not supported yet -
hline, draws a horizontal line at the specified fixed price level.hline(price, title, color, linestyle, linewidth, editable, display)
linestyle parameter options: "dashed", "dotted", "solid"
display parameter options: "none", "all"javascriptc.hline(bar.High)pythonc.hline(bar.High)c++// Not supported yet -
plotarrow, plots up and down arrows on the chart.plotarrow(series, title, colorup, colordown, offset, minheight, maxheight, editable, show_last, display)
display parameter options: "none", "all"javascriptc.plotarrow(bar.Close - bar.Open)pythonc.plotarrow(bar.Close - bar.Open)c++// Not supported yet -
plotshape, draws visual shapes on the chart.plotshape(series, title, style, location, color, offset, text, textcolor, editable, size, show_last, display)
style parameter options: "diamond", "square", "label_down", "label_up", "arrow_down", "arrow_up", "circle", "flag", "triangle_down", "triangle_up", "cross", "xcross"
location parameter options: "abovebar", "belowbar", "top", "bottom", "absolute"
size parameter options: "10px", "14px", "20px", "40px", "80px", corresponding to size.tiny, size.small, size.normal, size.large, size.huge in Pine language
size.auto is equivalent to size.small.
display parameter options: "none", "all"javascriptc.plotshape(bar.Low, {style: 'diamond'})pythonc.plotshape(bar.Low, style = 'diamond')c++// Not supported yet -
plotchar, draws visual shapes on the chart using any Unicode character.plotchar(series, title, char, location, color, offset, text, textcolor, editable, size, show_last, display)
location parameter options: "abovebar", "belowbar", "top", "bottom", "absolute"
size parameter options: "10px", "14px", "20px", "40px", "80px", corresponding to size.tiny, size.small, size.normal, size.large, size.huge in Pine language
size.auto is equivalent to size.small.
display parameter options: "none", "all"javascriptc.plotchar(bar.Close, {char: 'X'})pythonc.plotchar(bar.Close, char = 'X')c++// Not supported yet -
plotcandle, draws candlestick chart on the chart.plotcandle(open, high, low, close, title, color, wickcolor, editable, show_last, bordercolor, display)
display parameter options: "none", "all"javascriptc.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)pythonc.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)c++// Not supported yet -
signal, this function does not exist in Pine language, used to draw buy/sell signals.signal(direction, price, qty, id)
The parameter "long" indicates the trade direction, with options: "long", "closelong", "short", "closeshort". The parameter
bar.Highspecifies the Y-axis position of the signal marker.
The parameter 1.5 represents the trade quantity of the signal. A fourth parameter can be passed to replace the default displayed text content. The default text of the signal marker is the trade direction, for example: "closelong".javascriptc.signal("long", bar.High, 1.5)pythonc.signal("long", bar.High, 1.5)c++// Not supported yet -
reset, this function does not exist in Pine language, used to clear chart data.reset(remain)
The
reset()method accepts a parameterremainto specify the number of data bars to retain. Not passing theremainparameter means clearing all data.javascriptc.reset()pythonc.reset()c++// Not supported yet
Returns
| Type | Description |
object | Chart object. The chart object returned by the |
Arguments
| Name | Type | Required | Description |
options | object / object array | Yes | The
|
See Also
Remarks
Strategy custom charting can only use one of the KLineChart() function or Chart() function methods. For color, style, and other settings used when calling the KLineChart() function, please refer to the Special Topic Article on Drawing with KLineChart Function
The pricePrecision and volumePrecision parameters are used to control the display precision of data in the chart. When these parameters are not set, the chart uses default precision to display data. After setting the precision parameters, the price and volume data in the chart will be rounded and displayed according to the specified number of decimal places, which helps simplify the chart display and improve readability.
LogReset
Clear log records.
LogReset(remain)Examples
javascript
function main() {
// 保留最近10条日志,清除其余日志
LogReset(10)
}
python
def main():
LogReset(10)
c++
void main() {
LogReset(10);
}Arguments
| Name | Type | Required | Description |
remain | number | No | The |
See Also
Remarks
The startup log generated each time a live strategy starts is counted as one record. Therefore, if no parameter is passed and there is no log output at strategy startup, no logs will be displayed at all. You need to wait for the bot's log callback (this is normal, not an exception).
LogVacuum
Used to reclaim storage space occupied by SQLite when deleting data after calling the LogReset() function to clear logs.
LogVacuum()Examples
javascript
function main() {
LogReset()
LogVacuum()
}
python
def main():
LogReset()
LogVacuum()
c++
void main() {
LogReset()
LogVacuum()
}See Also
Remarks
Since SQLite does not reclaim space when deleting data, a VACUUM operation needs to be performed to clean up tables and free up space. This function call involves file movement operations with significant latency, so it is recommended to call it at appropriate intervals.
console.log
Used to output debug information in the "Debug Info" section of the live trading page. For example, when the live trading ID is 123456, the console.log function outputs debug information on the live trading page while creating a log file with .log extension in the docker directory /logs/storage/123456/ and writing debug information to it. The file name prefix is stdout_.
console.log(...msgs)Examples
javascript
function main() {
console.log("test console.log")
}
python
# 不支持
c++
// 不支持Arguments
| Name | Type | Required | Description |
msg | string / number / bool / object / array / any (any type supported by the platform) | No | The parameter |
See Also
Remarks
Notes:
-
Only
JavaScriptlanguage supports this function. -
Only live trading environment supports this function, neither "Debug Tool" nor "Backtesting System" supports it.
-
When outputting objects, they will be converted to the string
[object Object], so it is recommended to output readable information.
console.error
Used to output error messages in the "Debug Information" section of the live trading page. For example, when the live trading ID is 123456, the console.error function outputs error messages on the live trading page while creating a log file with the prefix stderr_ and extension .log in the docker's directory /logs/storage/123456/ where the live trading belongs, and writes the error messages to this file.
console.error(...msgs)Examples
javascript
function main() {
console.error("test console.error")
}
python
# Not supported
c++
// Not supportedArguments
| Name | Type | Required | Description |
msg | string / number / bool / object / array / any (any type supported by the platform) | No | The parameter |
See Also
Remarks
Notes:
- Only
JavaScriptlanguage supports this function. - Only live trading environment supports this function, "Debug Tool" and "Backtesting System" do not support it.
- When outputting objects, they will be converted to the string
[object Object], it is recommended to output human-readable information.