输入/搜索内容
内置函数
Global
Log
Log
LogProfit
LogProfitReset
LogStatus
EnableLog
Chart
KLineChart
LogReset
LogVacuum
console.log
console.error
Market
Trade
Account
Futures
Threads
TA
Talib
OS
结构体
内置变量

刷新回测或实盘页面的状态栏:输出文字、表格、按钮,或带 type 的组件对象(指标卡、图表、标签页等);每次调用替换上一次的内容。

LogStatus(...msgs)

示例

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

    javascript
    function main() { LogStatus('这是一个普通的状态提示') LogStatus('这是一个红色字体的状态提示#ff0000') LogStatus('这是一个多行的状态信息\n我是第二行') }
    python
    def main(): LogStatus('这是一个普通的状态提示') LogStatus('这是一个红色字体的状态提示#ff0000') LogStatus('这是一个多行的状态信息\n我是第二行')
    rust
    fn main() { LogStatus!("这是一个普通的状态提示"); LogStatus!("这是一个红色字体的状态提示#ff0000"); LogStatus!("这是一个多行的状态信息\n我是第二行"); }
  • 直接传组件对象:文字后面跟一张最新价指标卡。

    javascript
    function main() { while (!exchange.IO("status")) Sleep(1000) exchange.SetContractType("rb888") var ticker = exchange.GetTicker() if (!ticker) return // 组件对象直接传,不用 JSON.stringify,也不用加反引号 LogStatus("更新时间 " + _D(), {type: "kpi", title: "最新价", value: ticker.Last}) // 输出:一行「更新时间 …」文字,后面是最新价指标卡 }
    python
    def main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") ticker = exchange.GetTicker() if not ticker: return # 组件字典直接传,不用 json.dumps,也不用加反引号 LogStatus("更新时间 " + _D(), {"type": "kpi", "title": "最新价", "value": ticker["Last"]}) # 输出:一行「更新时间 …」文字,后面是最新价指标卡
    rust
    fn main() { while !exchange.IO("status").is_ok_and(|v| v.as_i64() == Some(1)) { Sleep(1000); } if exchange.SetContractType("rb888").is_err() { return; } let Ok(t) = exchange.GetTicker(None) else { return }; // 组件写成一行 JSON 文本跟在文字后面,不用加反引号 let kpi = format!(r#"{{"type": "kpi", "title": "最新价", "value": {}}}"#, t.Last); LogStatus!(format!("更新时间 {}", _D(None)), kpi); // 输出:一行「更新时间 …」文字,后面是最新价指标卡 }
  • 组合仪表盘:grid排列指标卡、权益曲线和持仓表。

    javascript
    function main() { while (!exchange.IO("status")) Sleep(1000) exchange.SetContractType("rb888") var times = [], curve = [], f2 = {precision: 2} while (true) { var account = exchange.GetAccount() var positions = exchange.GetPositions() if (account && positions) { var profit = 0 var rows = positions.map(function (p) { profit += p.Profit var side = p.Type == PD_LONG || p.Type == PD_LONG_YD ? "多" : "空" return [p.Symbol, side, p.Amount, p.Price, p.Profit] }) // 期货账户有 Equity(权益),没有时用可用 + 冻结 var equity = account.Equity || account.Balance + account.FrozenBalance times.push(Date.now()) curve.push(equity) if (curve.length > 500) { times.shift(); curve.shift() } var color = profit >= 0 ? "up" : "down" LogStatus({type: "grid", cols: 3, items: [ {type: "kpi", title: "权益", value: equity, unit: "元", format: f2}, {type: "kpi", title: "可用", value: account.Balance, format: f2}, {type: "kpi", title: "浮动盈亏", value: profit, color: color}, {type: "chart", span: 3, kind: "area", title: "权益曲线", x: times, series: [{name: "权益", data: curve}]}, {type: "table", span: 3, title: "持仓", rows: rows, cols: ["品种", "方向", "数量", "均价", "浮动盈亏"]} ]}) } Sleep(60 * 1000) } }
    python
    import time def main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") times, curve, f2 = [], [], {"precision": 2} while True: account = exchange.GetAccount() positions = exchange.GetPositions() if account and positions is not None: rows, profit = [], 0 for p in positions: profit += p["Profit"] side = "多" if p["Type"] in (PD_LONG, PD_LONG_YD) else "空" rows.append([p["Symbol"], side, p["Amount"], p["Price"], p["Profit"]]) # 期货账户有 Equity(权益),没有时用可用 + 冻结 equity = account.get("Equity") or account["Balance"] + account["FrozenBalance"] times.append(int(time.time() * 1000)) curve.append(equity) del times[:-500], curve[:-500] color = "up" if profit >= 0 else "down" LogStatus({"type": "grid", "cols": 3, "items": [ {"type": "kpi", "title": "权益", "value": equity, "unit": "元", "format": f2}, {"type": "kpi", "title": "可用", "value": account["Balance"], "format": f2}, {"type": "kpi", "title": "浮动盈亏", "value": profit, "color": color}, {"type": "chart", "span": 3, "kind": "area", "title": "权益曲线", "x": times, "series": [{"name": "权益", "data": curve}]}, {"type": "table", "span": 3, "title": "持仓", "rows": rows, "cols": ["品种", "方向", "数量", "均价", "浮动盈亏"]} ]}) Sleep(60 * 1000)
    rust
    fn join<T: ToString>(v: &[T]) -> String { v.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",") } fn main() { while !exchange.IO("status").is_ok_and(|v| v.as_i64() == Some(1)) { Sleep(1000); } if exchange.SetContractType("rb888").is_err() { return; } let (mut times, mut curve) = (Vec::new(), Vec::new()); loop { if let (Ok(a), Ok(ps)) = (exchange.GetAccount(), exchange.GetPositions(None)) { let profit: f64 = ps.iter().map(|p| p.Profit).sum(); let rows: Vec<String> = ps.iter().map(|p| { let long = p.Type == PD_LONG || p.Type == PD_LONG_YD; let side = if long { "多" } else { "空" }; format!(r#"["{}","{}",{},{},{}]"#, p.Symbol, side, p.Amount, p.Price, p.Profit) }).collect(); // 期货账户有 Equity(权益),没有时用可用 + 冻结 let equity = if a.Equity > 0.0 { a.Equity } else { a.Balance + a.FrozenBalance }; times.push(UnixNano() / 1_000_000); curve.push(equity); if curve.len() > 500 { times.remove(0); curve.remove(0); } let color = if profit >= 0.0 { "up" } else { "down" }; LogStatus!(format!(r#"{{"type": "grid", "cols": 3, "items": [ {{"type": "kpi", "title": "权益", "value": {}, "unit": "元", "format": {{"precision": 2}}}}, {{"type": "kpi", "title": "可用", "value": {}, "format": {{"precision": 2}}}}, {{"type": "kpi", "title": "浮动盈亏", "value": {}, "color": "{}"}}, {{"type": "chart", "span": 3, "kind": "area", "title": "权益曲线", "x": [{}], "series": [{{"name": "权益", "data": [{}]}}]}}, {{"type": "table", "span": 3, "title": "持仓", "rows": [{}], "cols": ["品种", "方向", "数量", "均价", "浮动盈亏"]}}]}}"#, equity, a.Balance, profit, color, join(&times), join(&curve), rows.join(","))); } Sleep(60 * 1000); } }
  • 表格增强:列设置格式、涨跌色和单位,点击表头按原始数值排序。

    javascript
    function main() { while (!exchange.IO("status")) Sleep(1000) exchange.SetContractType("rb888") var positions = exchange.GetPositions() if (!positions) return var rows = positions.map(function (p) { var ratio = p.Margin > 0 ? p.Profit / p.Margin : 0 // 单元格对象:单独指定这个格子的单位和颜色 var lever = {value: p.MarginLevel, unit: "x", color: p.MarginLevel > 10 ? "warning" : "muted"} return [p.Symbol, p.Amount, p.Price, p.Profit, ratio, lever] }) var f2 = {precision: 2}, pct = {percent: true, precision: 2} // 单独输出表格:JavaScript、Python 自动加反引号 LogStatus({type: "table", title: "持仓", rows: rows, cols: [ "品种", {title: "数量", align: "right"}, {title: "均价", format: f2, align: "right"}, {title: "浮动盈亏", format: f2, color: "sign", unit: "元", align: "right"}, {title: "收益率", format: pct, color: "sign", align: "right"}, {title: "杠杆", align: "right"} ]}) // 输出:盈亏、收益率按正负着色,排序按数值而不是文字 }
    python
    def main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") positions = exchange.GetPositions() if positions is None: return rows = [] for p in positions: ratio = p["Profit"] / p["Margin"] if p["Margin"] > 0 else 0 # 单元格对象:单独指定这个格子的单位和颜色 lever = {"value": p["MarginLevel"], "unit": "x", "color": "warning" if p["MarginLevel"] > 10 else "muted"} rows.append([p["Symbol"], p["Amount"], p["Price"], p["Profit"], ratio, lever]) f2, pct = {"precision": 2}, {"percent": True, "precision": 2} # 单独输出表格:JavaScript、Python 自动加反引号 LogStatus({"type": "table", "title": "持仓", "rows": rows, "cols": [ "品种", {"title": "数量", "align": "right"}, {"title": "均价", "format": f2, "align": "right"}, {"title": "浮动盈亏", "format": f2, "color": "sign", "unit": "元", "align": "right"}, {"title": "收益率", "format": pct, "color": "sign", "align": "right"}, {"title": "杠杆", "align": "right"} ]}) # 输出:盈亏、收益率按正负着色,排序按数值而不是文字
    rust
    fn main() { while !exchange.IO("status").is_ok_and(|v| v.as_i64() == Some(1)) { Sleep(1000); } if exchange.SetContractType("rb888").is_err() { return; } let Ok(positions) = exchange.GetPositions(None) else { return }; let rows: Vec<String> = positions.iter().map(|p| { let ratio = if p.Margin > 0.0 { p.Profit / p.Margin } else { 0.0 }; let warn = if p.MarginLevel > 10.0 { "warning" } else { "muted" }; // 单元格对象:单独指定这个格子的单位和颜色 let lever = format!(r#"{{"value": {}, "unit": "x", "color": "{}"}}"#, p.MarginLevel, warn); format!(r#"["{}", {}, {}, {}, {}, {}]"#, p.Symbol, p.Amount, p.Price, p.Profit, ratio, lever) }).collect(); let table = format!(r#"{{"type": "table", "title": "持仓", "rows": [{}], "cols": [ "品种", {{"title": "数量", "align": "right"}}, {{"title": "均价", "format": {{"precision": 2}}, "align": "right"}}, {{"title": "浮动盈亏", "format": {{"precision": 2}}, "color": "sign", "unit": "元", "align": "right"}}, {{"title": "收益率", "format": {{"percent": true, "precision": 2}}, "color": "sign", "align": "right"}}, {{"title": "杠杆", "align": "right"}}]}}"#, rows.join(",")); // 单独输出表格要自己加反引号,且反引号里的 JSON 要在一行里 LogStatus!(format!("`{}`", table.replace('\n', ""))); // 输出:盈亏、收益率按正负着色,排序按数值而不是文字 }
  • 用tabs分页显示行情、持仓和账户,刷新时保持当前选中的页。

    javascript
    function main() { while (!exchange.IO("status")) Sleep(1000) exchange.SetContractType("rb888") while (true) { var ticker = exchange.GetTicker() var account = exchange.GetAccount() var positions = exchange.GetPositions() if (ticker && account && positions) { var rows = positions.map(function (p) { return [p.Symbol, p.Amount, p.Profit] }) var table = {type: "table", cols: ["品种", "数量", "浮动盈亏"], rows: rows} LogStatus({type: "tabs", id: "main", items: [ {title: "行情", cols: 3, items: [ {type: "kpi", title: "最新价", value: ticker.Last}, {type: "kpi", title: "买一", value: ticker.Buy}, {type: "kpi", title: "卖一", value: ticker.Sell} ]}, {title: "持仓", items: [table]}, {title: "账户", items: [{type: "stat", cols: 2, items: [ {label: "可用", value: account.Balance}, {label: "冻结", value: account.FrozenBalance} ]}]} ]}) } Sleep(5000) } }
    python
    def main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") while True: ticker = exchange.GetTicker() account = exchange.GetAccount() positions = exchange.GetPositions() if ticker and account and positions is not None: rows = [[p["Symbol"], p["Amount"], p["Profit"]] for p in positions] table = {"type": "table", "cols": ["品种", "数量", "浮动盈亏"], "rows": rows} LogStatus({"type": "tabs", "id": "main", "items": [ {"title": "行情", "cols": 3, "items": [ {"type": "kpi", "title": "最新价", "value": ticker["Last"]}, {"type": "kpi", "title": "买一", "value": ticker["Buy"]}, {"type": "kpi", "title": "卖一", "value": ticker["Sell"]} ]}, {"title": "持仓", "items": [table]}, {"title": "账户", "items": [{"type": "stat", "cols": 2, "items": [ {"label": "可用", "value": account["Balance"]}, {"label": "冻结", "value": account["FrozenBalance"]} ]}]} ]}) Sleep(5000)
    rust
    fn main() { while !exchange.IO("status").is_ok_and(|v| v.as_i64() == Some(1)) { Sleep(1000); } if exchange.SetContractType("rb888").is_err() { return; } loop { let t = exchange.GetTicker(None); let (a, ps) = (exchange.GetAccount(), exchange.GetPositions(None)); if let (Ok(t), Ok(a), Ok(ps)) = (t, a, ps) { let rows: Vec<String> = ps.iter() .map(|p| format!(r#"["{}", {}, {}]"#, p.Symbol, p.Amount, p.Profit)) .collect(); LogStatus!(format!(r#"{{"type": "tabs", "id": "main", "items": [ {{"title": "行情", "cols": 3, "items": [{{"type": "kpi", "title": "最新价", "value": {}}}, {{"type": "kpi", "title": "买一", "value": {}}}, {{"type": "kpi", "title": "卖一", "value": {}}}]}}, {{"title": "持仓", "items": [{{"type": "table", "cols": ["品种", "数量", "浮动盈亏"], "rows": [{}]}}]}}, {{"title": "账户", "items": [{{"type": "stat", "cols": 2, "items": [ {{"label": "可用", "value": {}}}, {{"label": "冻结", "value": {}}}]}}]}}]}}"#, t.Last, t.Buy, t.Sell, rows.join(","), a.Balance, a.FrozenBalance)); } Sleep(5000); } }
  • 多种图表:sparkline、gauge、progress、pie、histogram和heatmap。

    javascript
    function main() { while (!exchange.IO("status")) Sleep(1000) exchange.SetContractType("rb888") var r = exchange.GetRecords(PERIOD_H1) var account = exchange.GetAccount() if (!r || r.length < 73 || !account) return r = r.slice(-73) var closes = r.map(function (b) { return b.Close }) var rets = closes.slice(1).map(function (c, i) { return c / closes[i] - 1 }) var day = r.slice(-24) var hi = Math.max.apply(null, day.map(function (b) { return b.High })) var lo = Math.min.apply(null, day.map(function (b) { return b.Low })) var pos = hi > lo ? (closes[72] - lo) / (hi - lo) : 0.5 var total = account.Balance + account.FrozenBalance var frozen = total > 0 ? account.FrozenBalance / total : 0 var pct = {percent: true, precision: 2} LogStatus({type: "grid", cols: 3, items: [ {type: "sparkline", title: "最近24小时", data: closes.slice(-24)}, {type: "gauge", title: "24小时区间位置", value: pos, format: {percent: true}, thresholds: [{value: 0.8, color: "up"}]}, {type: "progress", title: "冻结资金占比", value: frozen}, {type: "pie", title: "资金分布", items: [ {name: "可用", value: account.Balance}, {name: "冻结", value: account.FrozenBalance}]}, {type: "histogram", title: "小时涨跌分布", data: rets, format: pct}, {type: "heatmap", title: "逐小时涨跌", format: pct, x: day.map(function (b) { return _D(b.Time, "hh:00") }), y: ["前天", "昨天", "今天"], z: [rets.slice(0, 24), rets.slice(24, 48), rets.slice(48, 72)]} ]}) }
    python
    def main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") r = exchange.GetRecords(PERIOD_H1) account = exchange.GetAccount() if not r or len(r) < 73 or not account: return r = r[-73:] closes = [b["Close"] for b in r] rets = [closes[i] / closes[i - 1] - 1 for i in range(1, 73)] day = r[-24:] hi, lo = max(b["High"] for b in day), min(b["Low"] for b in day) pos = (closes[72] - lo) / (hi - lo) if hi > lo else 0.5 total = account["Balance"] + account["FrozenBalance"] frozen = account["FrozenBalance"] / total if total > 0 else 0 pct = {"percent": True, "precision": 2} LogStatus({"type": "grid", "cols": 3, "items": [ {"type": "sparkline", "title": "最近24小时", "data": closes[-24:]}, {"type": "gauge", "title": "24小时区间位置", "value": pos, "format": {"percent": True}, "thresholds": [{"value": 0.8, "color": "up"}]}, {"type": "progress", "title": "冻结资金占比", "value": frozen}, {"type": "pie", "title": "资金分布", "items": [ {"name": "可用", "value": account["Balance"]}, {"name": "冻结", "value": account["FrozenBalance"]}]}, {"type": "histogram", "title": "小时涨跌分布", "data": rets, "format": pct}, {"type": "heatmap", "title": "逐小时涨跌", "format": pct, "x": [_D(b["Time"] / 1000, "%H:00") for b in day], "y": ["前天", "昨天", "今天"], "z": [rets[0:24], rets[24:48], rets[48:72]]} ]})
    rust
    fn join<T: ToString>(v: &[T]) -> String { v.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",") } fn main() { while !exchange.IO("status").is_ok_and(|v| v.as_i64() == Some(1)) { Sleep(1000); } if exchange.SetContractType("rb888").is_err() { return; } let Ok(r) = exchange.GetRecords(None, PERIOD_H1, None) else { return }; let Ok(a) = exchange.GetAccount() else { return }; if r.len() < 73 { return; } let r = &r[r.len() - 73..]; let closes: Vec<f64> = r.iter().map(|b| b.Close).collect(); let rets: Vec<f64> = closes.windows(2).map(|w| w[1] / w[0] - 1.0).collect(); let day = &r[49..]; let hi = day.iter().map(|b| b.High).fold(f64::MIN, f64::max); let lo = day.iter().map(|b| b.Low).fold(f64::MAX, f64::min); let pos = if hi > lo { (closes[72] - lo) / (hi - lo) } else { 0.5 }; let total = a.Balance + a.FrozenBalance; let frozen = if total > 0.0 { a.FrozenBalance / total } else { 0.0 }; // Rust 的 _D() 固定输出 UTC 时间 let hours: Vec<String> = day.iter() .map(|b| format!("\"{}:00\"", &_D(b.Time)[11..13])) .collect(); let z = format!("[{}],[{}],[{}]", join(&rets[..24]), join(&rets[24..48]), join(&rets[48..])); LogStatus!(format!(r#"{{"type": "grid", "cols": 3, "items": [ {{"type": "sparkline", "title": "最近24小时", "data": [{}]}}, {{"type": "gauge", "title": "24小时区间位置", "value": {}, "format": {{"percent": true}}, "thresholds": [{{"value": 0.8, "color": "up"}}]}}, {{"type": "progress", "title": "冻结资金占比", "value": {}}}, {{"type": "pie", "title": "资金分布", "items": [{{"name": "可用", "value": {}}}, {{"name": "冻结", "value": {}}}]}}, {{"type": "histogram", "title": "小时涨跌分布", "data": [{}], "format": {{"percent": true, "precision": 2}}}}, {{"type": "heatmap", "title": "逐小时涨跌", "format": {{"percent": true, "precision": 2}}, "x": [{}], "y": ["前天", "昨天", "今天"], "z": [{}]}}]}}"#, join(&closes[49..]), pos, frozen, a.Balance, a.FrozenBalance, join(&rets), hours.join(","), z)); }
  • 标题行加badge状态标签,下面是alert风控提示、markdown说明和timeline事件。

    javascript
    function main() { while (!exchange.IO("status")) Sleep(1000) exchange.SetContractType("rb888") var events = [{time: Date.now(), text: "策略启动", color: "primary"}] while (true) { var account = exchange.GetAccount() if (account) { var low = account.Balance < 100 if (low) { events.unshift({time: Date.now(), text: "可用资金低于100", color: "down"}) } events = events.slice(0, 10) var alert = low ? {type: "alert", level: "warning", title: "可用资金不足", text: "低于100,暂停开新仓"} : {type: "alert", level: "success", title: "风控检查通过"} // 用 "\n" 分行:第一行文字加标签,第二行是组件数组(从上到下排列) LogStatus("网格策略", {type: "badge", items: [ {text: IsVirtual() ? "回测" : "实盘", color: "muted"}, {text: low ? "暂停开仓" : "运行中", color: low ? "warning" : "up"} ]}, "\n", [ alert, {type: "markdown", text: "#### 策略说明\n- 网格间距 **0.5%**\n- 每格数量 0.01"}, {type: "timeline", title: "最近事件", items: events} ]) } Sleep(10 * 1000) } }
    python
    import time def main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") now = lambda: int(time.time() * 1000) events = [{"time": now(), "text": "策略启动", "color": "primary"}] while True: account = exchange.GetAccount() if account: low = account["Balance"] < 100 if low: events.insert(0, {"time": now(), "text": "可用资金低于100", "color": "down"}) del events[10:] if low: alert = {"type": "alert", "level": "warning", "title": "可用资金不足", "text": "低于100,暂停开新仓"} else: alert = {"type": "alert", "level": "success", "title": "风控检查通过"} # 用 "\n" 分行:第一行文字加标签,第二行是组件数组(从上到下排列) LogStatus("网格策略", {"type": "badge", "items": [ {"text": "回测" if IsVirtual() else "实盘", "color": "muted"}, {"text": "暂停开仓" if low else "运行中", "color": "warning" if low else "up"} ]}, "\n", [ alert, {"type": "markdown", "text": "#### 策略说明\n- 网格间距 **0.5%**\n- 每格数量 0.01"}, {"type": "timeline", "title": "最近事件", "items": events} ]) Sleep(10 * 1000)
    rust
    fn item(text: &str, color: &str) -> String { format!(r#"{{"text": "{}", "color": "{}"}}"#, text, color) } fn ev(text: &str, color: &str) -> String { let ms = UnixNano() / 1_000_000; format!(r#"{{"time": {}, "text": "{}", "color": "{}"}}"#, ms, text, color) } fn main() { while !exchange.IO("status").is_ok_and(|v| v.as_i64() == Some(1)) { Sleep(1000); } if exchange.SetContractType("rb888").is_err() { return; } let mut events = vec![ev("策略启动", "primary")]; loop { if let Ok(a) = exchange.GetAccount() { let low = a.Balance < 100.0; if low { events.insert(0, ev("可用资金低于100", "down")); events.truncate(10); } let mode = if IsVirtual() { "回测" } else { "实盘" }; let (state, color) = if low { ("暂停开仓", "warning") } else { ("运行中", "up") }; let badge = format!(r#"{{"type": "badge", "items": [{}, {}]}}"#, item(mode, "muted"), item(state, color)); let (level, title, text) = match low { true => ("warning", "可用资金不足", "低于100,暂停开新仓"), false => ("success", "风控检查通过", ""), }; let alert = format!( r#"{{"type": "alert", "level": "{}", "title": "{}", "text": "{}"}}"#, level, title, text ); let notes = r"#### 策略说明\n- 网格间距 **0.5%**\n- 每格数量 0.01"; let md = format!(r#"{{"type": "markdown", "text": "{}"}}"#, notes); let tl = format!(r#"{{"type": "timeline", "title": "最近事件", "items": [{}]}}"#, events.join(",")); // 用 "\n" 分行:第一行文字加标签,第二行是组件数组(从上到下排列)(每段 JSON 都写成一行) LogStatus!("网格策略", badge, "\n", format!("[{},{},{}]", alert, md, tl)); } Sleep(10 * 1000); } }
  • 与旧写法混用:反引号表格、按钮和新组件分行输出。

    javascript
    function main() { while (!exchange.IO("status")) Sleep(1000) exchange.SetContractType("rb888") var account = exchange.GetAccount() if (!account) return var table = {type: "table", title: "账户", cols: ["可用", "冻结"], rows: [[account.Balance, account.FrozenBalance]]} var btn = {type: "button", cmd: "pause", name: "暂停"} LogStatus( "`" + JSON.stringify(table) + "`", // 旧写法:反引号包裹的 JSON,显示不变 "\n汇总", {type: "grid", cols: 3, items: [ // 新组件放在这一行的最后 {type: "kpi", title: "可用", value: account.Balance}, table, btn // 表格、按钮放进 grid 时不用加反引号 ]}, "\n`" + JSON.stringify(btn) + "`" ) // 输出:第一行旧表格;第二行文字加组件网格;第三行按钮,点击后用 GetCommand() 读取 }
    python
    import json def main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") account = exchange.GetAccount() if not account: return table = {"type": "table", "title": "账户", "cols": ["可用", "冻结"], "rows": [[account["Balance"], account["FrozenBalance"]]]} btn = {"type": "button", "cmd": "pause", "name": "暂停"} LogStatus( "`" + json.dumps(table) + "`", # 旧写法:反引号包裹的 JSON,显示不变 "\n汇总", {"type": "grid", "cols": 3, "items": [ # 新组件放在这一行的最后 {"type": "kpi", "title": "可用", "value": account["Balance"]}, table, btn # 表格、按钮放进 grid 时不用加反引号 ]}, "\n`" + json.dumps(btn) + "`" ) # 输出:第一行旧表格;第二行文字加组件网格;第三行按钮,点击后用 GetCommand() 读取
    rust
    fn main() { while !exchange.IO("status").is_ok_and(|v| v.as_i64() == Some(1)) { Sleep(1000); } if exchange.SetContractType("rb888").is_err() { return; } let Ok(a) = exchange.GetAccount() else { return }; // 反引号里、或和文字同一行的 JSON 要写成一行,去掉换行 let table = format!(r#"{{"type": "table", "title": "账户", "cols": ["可用", "冻结"], "rows": [[{}, {}]]}}"#, a.Balance, a.FrozenBalance).replace('\n', ""); let btn = r#"{"type": "button", "cmd": "pause", "name": "暂停"}"#; let kpi = format!(r#"{{"type": "kpi", "title": "可用", "value": {}}}"#, a.Balance); // 新组件放在这一行的最后;表格、按钮放进 grid 时不用加反引号 let grid = format!(r#"{{"type": "grid", "cols": 3, "items": [{}, {}, {}]}}"#, kpi, table, btn); // 旧写法:反引号包裹的 JSON,显示不变 LogStatus!(format!("`{}`", table), "\n汇总", grid, format!("\n`{}`", btn)); // 输出:第一行旧表格;第二行文字加组件网格;第三行按钮,点击后用 GetCommand() 读取 }
  • 状态栏中数据输出示例:

    javascript
    function main() { var table = {type: 'table', title: '持仓信息', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]} // JSON序列化后两边加上`字符,视为一个复杂消息格式(当前支持表格) LogStatus('`' + JSON.stringify(table) + '`') // 表格信息也可以在多行中出现 LogStatus('第一行消息\n`' + JSON.stringify(table) + '`\n第三行消息') // 支持多个表格同时显示,将以TAB显示到一组里 LogStatus('`' + JSON.stringify([table, table]) + '`') // 也可以构造一个按钮在表格中,策略用GetCommand接收cmd属性的内容 var table = { type: 'table', title: '持仓操作', cols: ['列1', '列2', 'Action'], rows: [ ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': '平仓'}] ] } LogStatus('`' + JSON.stringify(table) + '`') // 或者构造一单独的按钮 LogStatus('`' + JSON.stringify({'type':'button', 'cmd': 'coverAll', 'name': '平仓'}) + '`') // 可以自定义按钮风格(bootstrap的按钮属性) LogStatus('`' + JSON.stringify({'type':'button', 'class': 'btn btn-xs btn-danger', 'cmd': 'coverAll', 'name': '平仓'}) + '`') }
    python
    import json def main(): table = {"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]} LogStatus('`' + json.dumps(table) + '`') LogStatus('第一行消息\n`' + json.dumps(table) + '`\n第三行消息') LogStatus('`' + json.dumps([table, table]) + '`') table = { "type" : "table", "title" : "持仓操作", "cols" : ["列1", "列2", "Action"], "rows" : [ ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}] ] } LogStatus('`' + json.dumps(table) + '`') LogStatus('`' + json.dumps({"type": "button", "cmd": "coverAll", "name": "平仓"}) + '`') LogStatus('`' + json.dumps({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "平仓"}) + '`')
    rust
    fn main() { let table = r#"{"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}"#; // JSON字符串两边加上`字符,视为一个复杂消息格式(当前支持表格) LogStatus!(format!("`{}`", table)); // 表格信息也可以在多行中出现 LogStatus!(format!("第一行消息\n`{}`\n第三行消息", table)); // 支持多个表格同时显示,将以TAB显示到一组里 LogStatus!(format!("`[{},{}]`", table, table)); // 也可以构造一个按钮在表格中,策略用GetCommand接收cmd属性的内容 let table = r#"{"type": "table", "title": "持仓操作", "cols": ["列1", "列2", "Action"], "rows": [["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]]}"#; LogStatus!(format!("`{}`", table)); // 或者构造一单独的按钮 LogStatus!(r#"`{"type": "button", "cmd": "coverAll", "name": "平仓"}`"#); // 可以自定义按钮风格(bootstrap的按钮属性) LogStatus!(r#"`{"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "平仓"}`"#); }
  • 支持状态栏中设计按钮控件(旧版按钮结构):

    javascript
    function main() { var table = { type: "table", title: "状态栏按钮样式", cols: ["默认", "原始", "成功", "信息", "警告", "危险"], rows: [ [ {"type":"button", "class": "btn btn-xs btn-default", "name": "默认"}, {"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"}, {"type":"button", "class": "btn btn-xs btn-success", "name": "成功"}, {"type":"button", "class": "btn btn-xs btn-info", "name": "信息"}, {"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"}, {"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"} ] ] } LogStatus("`" + JSON.stringify(table) + "`") }
    python
    import json def main(): table = { "type": "table", "title": "状态栏按钮样式", "cols": ["默认", "原始", "成功", "信息", "警告", "危险"], "rows": [ [ {"type":"button", "class": "btn btn-xs btn-default", "name": "默认"}, {"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"}, {"type":"button", "class": "btn btn-xs btn-success", "name": "成功"}, {"type":"button", "class": "btn btn-xs btn-info", "name": "信息"}, {"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"}, {"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"} ] ] } LogStatus("`" + json.dumps(table) + "`")
    rust
    fn main() { let table = String::from(r#"{"type": "table", "title": "状态栏按钮样式", "cols": ["默认", "原始", "成功", "信息", "警告", "危险"], "rows": [["#) + r#"{"type": "button", "class": "btn btn-xs btn-default", "name": "默认"},"# + r#"{"type": "button", "class": "btn btn-xs btn-primary", "name": "原始"},"# + r#"{"type": "button", "class": "btn btn-xs btn-success", "name": "成功"},"# + r#"{"type": "button", "class": "btn btn-xs btn-info", "name": "信息"},"# + r#"{"type": "button", "class": "btn btn-xs btn-warning", "name": "告警"},"# + r#"{"type": "button", "class": "btn btn-xs btn-danger", "name": "危险"}"# + r#"]]}"#; LogStatus!(format!("`{}`", table)); }
  • 设置状态栏按钮的禁用、描述功能(旧版按钮结构):

    javascript
    function main() { var table = { type: "table", title: "状态栏按钮的禁用、描述功能测试", cols: ["列1", "列2", "列3"], rows: [] } var button1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"} var button2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": true} var button3 = {"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": false} table.rows.push([button1, button2, button3]) LogStatus("`" + JSON.stringify(table) + "`") }
    python
    import json def main(): table = { "type": "table", "title": "状态栏按钮的禁用、描述功能测试", "cols": ["列1", "列2", "列3"], "rows": [] } button1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"} button2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": True} button3 = {"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": False} table["rows"].append([button1, button2, button3]) LogStatus("`" + json.dumps(table) + "`")
    rust
    fn main() { let button1 = r#"{"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}"#; let button2 = r#"{"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": true}"#; let button3 = r#"{"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": false}"#; let table = format!( r#"{{"type": "table", "title": "状态栏按钮的禁用、描述功能测试", "cols": ["列1", "列2", "列3"], "rows": [[{}, {}, {}]]}}"#, button1, button2, button3 ); LogStatus!(format!("`{}`", table)); }
  • 结合GetCommand()函数,构造状态栏按钮交互功能(旧版按钮结构):

    javascript
    function test1() { Log("调用自定义函数") } function main() { while (true) { var table = { type: 'table', title: '操作', cols: ['列1', '列2', 'Action'], rows: [ ['a', '1', { 'type': 'button', 'cmd': "CoverAll", 'name': '平仓' }], ['b', '1', { 'type': 'button', 'cmd': 10, 'name': '发送数值' }], ['c', '1', { 'type': 'button', 'cmd': _D(), 'name': '调用函数' }], ['d', '1', { 'type': 'button', 'cmd': 'test1', 'name': '调用自定义函数' }] ] } LogStatus(_D(), "\n", '`' + JSON.stringify(table) + '`') var str_cmd = GetCommand() if (str_cmd) { Log("接收到的交互数据 str_cmd:", "类型:", typeof(str_cmd), "值:", str_cmd) if(str_cmd == "test1") { test1() } } Sleep(500) } }
    python
    import json def test1(): Log("调用自定义函数") def main(): while True: table = { "type": "table", "title": "操作", "cols": ["列1", "列2", "Action"], "rows": [ ["a", "1", { "type": "button", "cmd": "CoverAll", "name": "平仓" }], ["b", "1", { "type": "button", "cmd": 10, "name": "发送数值" }], ["c", "1", { "type": "button", "cmd": _D(), "name": "调用函数" }], ["d", "1", { "type": "button", "cmd": "test1", "name": "调用自定义函数" }] ] } LogStatus(_D(), "\n", "`" + json.dumps(table) + "`") str_cmd = GetCommand() if str_cmd: Log("接收到的交互数据 str_cmd", "类型:", type(str_cmd), "值:", str_cmd) if str_cmd == "test1": test1() Sleep(500)
    rust
    fn test1() { Log!("调用自定义函数"); } fn main() { loop { let table = String::from(r#"{"type": "table", "title": "操作", "cols": ["列1", "列2", "Action"], "rows": ["#) + r#"["a", "1", {"type": "button", "cmd": "CoverAll", "name": "平仓"}],"# + r#"["b", "1", {"type": "button", "cmd": 10, "name": "发送数值"}],"# + &format!(r#"["c", "1", {{"type": "button", "cmd": "{}", "name": "调用函数"}}],"#, _D(None)) + r#"["d", "1", {"type": "button", "cmd": "test1", "name": "调用自定义函数"}]"# + r#"]}"#; LogStatus!(_D(None), "\n", format!("`{}`", table)); if let Some(str_cmd) = GetCommand(0) { Log!("接收到的交互数据 str_cmd:", "类型:", "String", "值:", &str_cmd); if str_cmd == "test1" { test1(); } } Sleep(500); } }
  • 在构造状态栏按钮进行交互时也支持输入数据,交互指令最终由GetCommand()函数捕获。
    给状态栏中的按钮控件的数据结构中增加input项(旧版按钮结构),例如给{"type": "button", "cmd": "open", "name": "开仓"}增加: "input": {"name": "开仓数量", "type": "number", "defValue": 1},就可以使按钮在被点击时弹出一个带输入框控件的弹窗(输入框中默认值为1,即defValue设置的数据),可以输入一个数据和按钮命令一起发送。例如以下测试代码运行时,在点击「开仓」按钮后,弹出一个带输入框的弹窗,在输入框中输入111后点击「确定」。GetCommand()函数就会捕获消息:open:111。

    javascript
    function main() { var tbl = { type: "table", title: "操作", cols: ["列1", "列2"], rows: [ ["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}], ["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}] ] } LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`") while (true) { var cmd = GetCommand() if (cmd) { Log("cmd:", cmd) } Sleep(1000) } }
    python
    import json def main(): tbl = { "type": "table", "title": "操作", "cols": ["列1", "列2"], "rows": [ ["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}], ["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}] ] } LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`") while True: cmd = GetCommand() if cmd: Log("cmd:", cmd) Sleep(1000)
    rust
    fn main() { let tbl = String::from(r#"{"type": "table", "title": "操作", "cols": ["列1", "列2"], "rows": ["#) + r#"["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],"# + r#"["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]"# + r#"]}"#; LogStatus!(_D(None), "\n", format!("`{}`", tbl)); loop { if let Some(cmd) = GetCommand(0) { 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中的name、description与按钮结构中的name、description虽然字段名一致,但是定义并不相同:
      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: "演示分组按钮控件", cols: ["操作"], rows: [] } // 创建分组按钮控件结构 var groupBtn = { type: "button", cmd: "open", name: "开仓", group: [ {"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"}, {"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100}, {"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100}, {"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": true} ] } // 测试按钮1 var testBtn1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"} var testBtn2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}} // 在tbl中添加groupBtn tbl.rows.push([groupBtn]) // 支持状态栏表格的一个单元格内设置多个按钮,即一个单元格内的数据为一个按钮结构数组:[testBtn1, testBtn2] tbl.rows.push([[testBtn1, testBtn2]]) while (true) { LogStatus("`" + JSON.stringify(tbl) + "`", "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", "`" + JSON.stringify(groupBtn) + "`") var cmd = GetCommand() if (cmd) { Log("cmd:", cmd) } Sleep(5000) } }
    python
    import json def main(): tbl = { "type": "table", "title": "演示分组按钮控件", "cols": ["操作"], "rows": [] } groupBtn = { "type": "button", "cmd": "open", "name": "开仓", "group": [ {"name": "orderType", "description": "下单方式|order type", "type": "selected", "defValue": "市价单|挂单"}, {"name": "tradePrice@orderType==1", "description": "交易价格|trade price", "type": "number", "defValue": 100}, {"name": "orderAmount", "description": "委托数量|order amount", "type": "string", "defValue": 100}, {"name": "boolean", "description": "是/否|boolean", "type": "boolean", "defValue": True} ] } testBtn1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"} testBtn2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}} tbl["rows"].append([groupBtn]) tbl["rows"].append([[testBtn1, testBtn2]]) while True: LogStatus("`" + json.dumps(tbl) + "`", "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", "`" + json.dumps(groupBtn) + "`") cmd = GetCommand() if cmd: Log("cmd:", cmd) Sleep(5000)
    rust
    fn main() { // 创建分组按钮控件结构 let group_btn = String::from(r#"{"type": "button", "cmd": "open", "name": "开仓", "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": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}"#; let test_btn2 = r#"{"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}"#; // 在tbl中添加groupBtn;支持状态栏表格的一个单元格内设置多个按钮,即一个单元格内的数据为一个按钮结构数组:[testBtn1, testBtn2] let tbl = format!( r#"{{"type": "table", "title": "演示分组按钮控件", "cols": ["操作"], "rows": [[{}], [[{}, {}]]]}}"#, group_btn, test_btn1, test_btn2 ); loop { LogStatus!(format!("`{}`", tbl), "\n", "分组按钮控件除了设置在状态栏表格中,也可以直接设置在状态栏上:", format!("`{}`", group_btn)); if let Some(cmd) = GetCommand(0) { 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: "实现下拉框多选", options: [{text: "选项A", value: "A"}, {text: "选项B", value: "B"}, {text: "选项C", value: "C"}], defValue: ["A", "C"], multiple: true } } // 状态栏分组按钮控件(设置group字段实现)testBtn3按钮触发的页面中的下拉框控件使用options字段设置选项,也支持直接使用defValue设置选项。 var testBtn3 = { type: "button", name: "testBtn3", cmd: "cmdTestBtn3", group: [ {name: "comboBox1", label: "labelComboBox1", description: "下拉框1", type: "selected", defValue: 1, options: ["A", "B"]}, {name: "comboBox2", label: "labelComboBox2", description: "下拉框2", type: "selected", defValue: "A|B"}, {name: "comboBox3", label: "labelComboBox3", description: "下拉框3", type: "selected", defValue: [0, 2], multiple: true, options: ["A", "B", "C"]}, { name: "comboBox4", label: "labelComboBox4", description: "下拉框4", type: "selected", defValue: ["A", "C"], multiple: true, options: [{text: "选项A", value: "A"}, {text: "选项B", value: "B"}, {text: "选项C", value: "C"}, {text: "选项D", value: "D"}] } ] } while (true) { LogStatus("`" + JSON.stringify(testBtn1) + "`\n", "`" + JSON.stringify(testBtn2) + "`\n", "`" + JSON.stringify(testBtn3) + "`\n") var cmd = GetCommand() if (cmd) { Log(cmd) } Sleep(5000) } }
    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": "实现下拉框多选", "options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}], "defValue": ["A", "C"], "multiple": True } } testBtn3 = { "type": "button", "name": "testBtn3", "cmd": "cmdTestBtn3", "group": [ {"name": "comboBox1", "label": "labelComboBox1", "description": "下拉框1", "type": "selected", "defValue": 1, "options": ["A", "B"]}, {"name": "comboBox2", "label": "labelComboBox2", "description": "下拉框2", "type": "selected", "defValue": "A|B"}, {"name": "comboBox3", "label": "labelComboBox3", "description": "下拉框3", "type": "selected", "defValue": [0, 2], "multiple": True, "options": ["A", "B", "C"]}, { "name": "comboBox4", "label": "labelComboBox4", "description": "下拉框4", "type": "selected", "defValue": ["A", "C"], "multiple": True, "options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}, {"text": "选项D", "value": "D"}] } ] } while True: LogStatus("`" + json.dumps(testBtn1) + "`\n", "`" + json.dumps(testBtn2) + "`\n", "`" + json.dumps(testBtn3) + "`\n") cmd = GetCommand() if cmd: Log(cmd) Sleep(5000)
    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": "实现下拉框多选","# + r#""options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项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": "下拉框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": "下拉框3", "type": "selected", "defValue": [0, 2], "multiple": true, "options": ["A", "B", "C"]},"# + r#"{"name": "comboBox4", "label": "labelComboBox4", "description": "下拉框4", "type": "selected", "defValue": ["A", "C"], "multiple": true, "options": [{"text": "选项A", "value": "A"}, {"text": "选项B", "value": "B"}, {"text": "选项C", "value": "C"}, {"text": "选项D", "value": "D"}]}"# + 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); } }
  • 使用当前最新的按钮结构,构造状态栏表格中的按钮,点击按钮触发交互时弹出一个多控件弹窗。
    详细内容可以参考:用户指南-状态栏中的交互控件。

    javascript
    var symbols = ["rb2410", "MA501", "hc2501", "i2501", "TA888"] function createBtn(tmp, group) { var btn = JSON.parse(JSON.stringify(tmp)) Object.values(group).forEach(function(eleByGroup) { btn["group"].unshift(eleByGroup) }) return btn } function main() { var arrManager = [] Object.values(symbols).forEach(function(symbol) { arrManager.push({ "symbol": symbol, }) }) // Btn var tmpBtnOpen = { "type": "button", "cmd": "open", "name": "开仓下单", "group": [{ "type": "selected", "name": "tradeType", "label": "下单类型", "description": "市价单、限价单", "default": 0, "group": "交易设置", "settings": { "options": ["市价单", "限价单"], "required": true, } }, { "type": "selected", "name": "direction", "label": "交易方向", "description": "买入、卖出", "default": "buy", "group": "交易设置", "settings": { "render": "segment", "required": true, "options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}], } }, { "type": "number", "name": "price", "label": "价格", "description": "订单的价格", "group": "交易设置", "filter": "tradeType==1", "settings": { "required": true, } }, { "type": "number", "name": "amount", "label": "下单量", "description": "订单的下单量", "group": "交易设置", "settings": { "required": true, } }], } while (true) { var tbl = {"type": "table", "title": "dashboard", "cols": ["symbol", "actionOpen"], "rows": []} Object.values(arrManager).forEach(function(m) { var btnOpen = createBtn(tmpBtnOpen, [{"type": "string", "name": "symbol", "label": "交易品种", "default": m["symbol"], "settings": {"required": true}}]) tbl["rows"].push([m["symbol"], btnOpen]) }) var cmd = GetCommand() if (cmd) { Log("收到交互:", cmd) // 解析交互消息: open:{"symbol":"TA888","tradeType":0,"direction":"buy","amount":111} // 根据第一个冒号:之前的指令判断是哪种按钮模板触发的消息 var arrCmd = cmd.split(":", 2) if (arrCmd[0] == "open") { var msg = JSON.parse(cmd.slice(5)) Log("交易品种:", msg["symbol"], ",交易方向:", msg["direction"], ",订单类型:", msg["tradeType"] == 0 ? "市价单" : "限价单", msg["tradeType"] == 0 ? ",订单价格:当前市价" : ",订单价格:" + msg["price"], ",订单数量:", msg["amount"]) } } LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`") Sleep(1000) } }
    python
    import json symbols = ["rb2410", "MA501", "hc2501", "i2501", "TA888"] def createBtn(tmp, group): btn = json.loads(json.dumps(tmp)) for eleByGroup in group: btn["group"].insert(0, eleByGroup) return btn def main(): arrManager = [] for symbol in symbols: arrManager.append({"symbol": symbol}) # Btn tmpBtnOpen = { "type": "button", "cmd": "open", "name": "开仓下单", "group": [{ "type": "selected", "name": "tradeType", "label": "下单类型", "description": "市价单、限价单", "default": 0, "group": "交易设置", "settings": { "options": ["市价单", "限价单"], "required": True, } }, { "type": "selected", "name": "direction", "label": "交易方向", "description": "买入、卖出", "default": "buy", "group": "交易设置", "settings": { "render": "segment", "required": True, "options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}], } }, { "type": "number", "name": "price", "label": "价格", "description": "订单的价格", "group": "交易设置", "filter": "tradeType==1", "settings": { "required": True, } }, { "type": "number", "name": "amount", "label": "下单量", "description": "订单的下单量", "group": "交易设置", "settings": { "required": True, } }], } while True: tbl = {"type": "table", "title": "dashboard", "cols": ["symbol", "actionOpen"], "rows": []} for m in arrManager: btnOpen = createBtn(tmpBtnOpen, [{"type": "string", "name": "symbol", "label": "交易品种", "default": m["symbol"], "settings": {"required": True}}]) tbl["rows"].append([m["symbol"], btnOpen]) cmd = GetCommand() if cmd != "" and cmd != None: Log("收到交互:", cmd) # 解析交互消息: open:{"symbol":"TA888","tradeType":0,"direction":"buy","amount":111} # 根据第一个冒号:之前的指令判断是哪种按钮模板触发的消息 arrCmd = cmd.split(":") if arrCmd[0] == "open": msg = json.loads(cmd[5:]) Log("交易品种:", msg["symbol"], ",交易方向:", msg["direction"], ",订单类型:", "市价单" if msg["tradeType"] == 0 else "限价单", ",订单价格:当前市价" if msg["tradeType"] == 0 else ",订单价格:" + str(msg["price"]), ",订单数量:", msg["amount"]) # 输出状态栏信息 LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`") Sleep(1000)
    rust
    fn main() { let symbols = ["rb2410", "MA501", "hc2501", "i2501", "TA888"]; // Btn:按钮模板,"group"的第一个元素为交易品种控件,__SYMBOL__为占位符,构造按钮时替换 let tmp_btn_open = String::from(r#"{"type": "button", "cmd": "open", "name": "开仓下单", "group": ["#) + r#"{"type": "string", "name": "symbol", "label": "交易品种", "default": "__SYMBOL__", "settings": {"required": true}},"# + r#"{"type": "selected", "name": "tradeType", "label": "下单类型", "description": "市价单、限价单", "default": 0, "group": "交易设置", "settings": {"options": ["市价单", "限价单"], "required": true}},"# + r#"{"type": "selected", "name": "direction", "label": "交易方向", "description": "买入、卖出", "default": "buy", "group": "交易设置", "settings": {"render": "segment", "required": true, "options": [{"name": "买入", "value": "buy"}, {"name": "卖出", "value": "sell"}]}},"# + r#"{"type": "number", "name": "price", "label": "价格", "description": "订单的价格", "group": "交易设置", "filter": "tradeType==1", "settings": {"required": true}},"# + r#"{"type": "number", "name": "amount", "label": "下单量", "description": "订单的下单量", "group": "交易设置", "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!("收到交互:", &cmd); // 解析交互消息: open:{"symbol":"TA888","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!("交易品种:", msg["symbol"].as_str().unwrap_or(""), ",交易方向:", msg["direction"].as_str().unwrap_or(""), ",订单类型:", if trade_type == 0 { "市价单" } else { "限价单" }, if trade_type == 0 { ",订单价格:当前市价".to_string() } else { format!(",订单价格:{}", msg["price"].as_f64().unwrap_or(0.0)) }, ",订单数量:", msg["amount"].as_f64().unwrap_or(0.0)); } } LogStatus!(_D(None), "\n", format!("`{}`", tbl)); Sleep(1000); } }

参数

名称类型必填描述

msg

string / number / bool / object / array

否

要显示的内容:文字、数字、表格或按钮对象、状态栏组件对象,或它们组成的数组。可以传多个,用空格拼接;"\n" 换行。

参考

备注

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

输入写法

多个参数先转成文字再用空格拼接:字符串原样,数字转成文字,对象和数组编码成JSON(JavaScript、Python中 type 为 table 的表格对象另加反引号)。拼好的内容按 \n 分行,下表是每一行的显示方式。

写法显示
普通文字文字;结尾写 #ff0000 设置颜色,[trans] 标记按页面语言显示
带 type 的组件对象,或含组件的数组状态栏组件,数组里的组件从上到下排列
反引号包裹的JSON:`{...}`表格、表格数组(显示为标签页)、按钮,也可以是组件
整个状态栏是一段(可以跨多行的)组件JSON整体识别为组件
  • 一行最多一段JSON。不带反引号的组件要放在这一行的最后,前面可以有文字(如 LogStatus("更新时间", kpi));同一行要显示多个组件时放进数组或 grid,或者用 "\n" 分行。
  • 带反引号的JSON前后都可以有文字。一行里已经有反引号时,这一行不带反引号的组件不再识别。
  • 不带反引号时只识别组件:type 不是组件的对象(例如 GetAccount() 的返回值、单独的按钮对象)、只含表格的数组按JSON文字显示,与以前一致。

状态栏组件

组件对象按 type 渲染:grid、tabs 负责布局,kpi、stat、progress、gauge、sparkline、chart、heatmap、calendar、histogram、pie、timeline、markdown、alert、badge 负责展示;表格和按钮对象也可以放进 grid、tabs 的 items。各组件的字段与示例见状态栏组件。

  • grid 按容器宽度自动减少列数,窄屏上也能正常显示。
  • 写错的组件(缺少必填字段、类型不认识、数据超出上限)只在自己的位置显示一张错误卡片,写明出错的字段并附上原始JSON,其它组件照常显示。
  • 配色随页面的明暗主题切换;up、down 跟随页面的涨跌色设置(优宽默认红涨绿跌)。
  • 与旧写法完全兼容:文字、反引号表格和按钮的显示不变,可以和组件混用。表格还支持列格式、涨跌色与单位,见LogStatus-table。
  • 组件只用于展示,交互用下文的状态栏按钮;按钮可以放进表格单元格,也可以放进 grid、tabs。
  • 上限:图表每条序列5000个点,热力图40000格,grid、tabs 嵌套6层;超出的组件显示错误卡片。

按钮也可以放进grid、tabs的 items,和状态栏组件排在一起,此时不用加反引号。

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

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

Rust没有对象字面量:把组件写成JSON文本(用 format! 拼接时 {、} 写成 {{、}}),或者传 JSONParse() 得到的 JsonValue,前端同样识别。字符串参数不会自动加反引号,单独输出表格时写 format!("`{}`", table);表格放进 grid、tabs 或含组件的数组时不用加。数值要是有限数,NaN 会让整段JSON无法解析而按文字显示。

LogStatus()函数支持直接传入Python的matplotlib.pyplot对象,只要对象包含savefig方法就可以作为参数传入LogStatus()函数,例如:

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