输入/搜索内容
入门
欢迎使用发明者量化交易平台
快速开始
密钥安全性
平台基础
账号与计费
实盘计费与充值
子账号
交易所
通用协议
本地凭据文件
交易所特殊说明
证券与期货
加密货币
托管者
策略库
实盘
编写策略
开发工具
回测系统
进阶专题
数据与研究
对外接口

每个实盘都有一个频道,频道ID就是实盘ID。实盘用SetChannelData()在自己的频道上发布数据,其它实盘用GetChannelData(实盘ID)读取。数据经平台服务端转发,可以跨托管者、跨服务器传递。

频道保存的是最新状态,不是消息队列:每次发布都覆盖上一次的数据,订阅端每次读到的都是当前最新的一份。需要历史记录时由订阅端自己保存。

常见用途:

  • 主从协同:主策略分析行情并发布信号,多个从策略读取信号在各自账户上执行。
  • 状态监控:各策略发布运行状态,监控实盘汇总展示或告警。
  • 数据共享:一个实盘计算指标、发布结果,其它实盘直接使用,避免重复计算。

使用要点

  • 首次读取即订阅:对某个频道第一次调用GetChannelData()时完成订阅并返回空值(null/None),之后服务端把该频道的更新推送到本实盘,再调用就能读到最新数据。订阅端应在启动时就开始读取,并处理空值。
  • 订阅上限:每个实盘最多订阅10个不同的频道(包括下面的UUID频道)。超出时该次调用返回空值,并记录一条错误日志channel subscriber exceed limit。
  • **数据格式**:JavaScript、Python的SetChannelData()可以传入任何可以JSON序列化的数据,订阅端读到的是解析后的对象。数据不变时不会重复发送。数据大小限制见SetChannelData。
  • Rust:SetChannelData(string)只接受字符串,需要自己拼好JSON文本;GetChannelData()没有频道参数,不能指定要读取的频道,因此不能订阅其它实盘或UUID频道。Rust策略适合作为广播端,订阅端请使用JavaScript或Python。
  • **跨平台推送**:外部系统(如TradingView告警、自建程序)可以通过扩展API的method=pub向指定实盘推送一个以32位UUID标识的频道数据,实盘用GetChannelData(UUID)读取,具体方法见SetChannelData、GetChannelData。
  • 实盘功能:频道通信用于实盘之间,回测时不要依赖它。当前实盘ID可以用_G()获取。
  • 不要在频道中传递密钥等敏感信息。

基本用法

示例

  • 广播端:发布行情摘要

    javascript
    function main() { var robotId = _G() // 当前实盘ID,也就是本实盘的频道ID var updateId = 0 while (true) { var ticker = exchange.GetTicker("BTC_USDT") if (ticker) { // 发布最新状态,覆盖上一次的数据 SetChannelData({ robotId: robotId, updateId: ++updateId, timestamp: Date.now(), symbol: "BTC_USDT", lastPrice: ticker.Last }) LogStatus("频道", robotId, "第", updateId, "次发布,最新价:", ticker.Last) } Sleep(60000) // 每分钟发布一次 } }
    python
    import time def main(): robotId = _G() # 当前实盘ID,也就是本实盘的频道ID updateId = 0 while True: ticker = exchange.GetTicker("BTC_USDT") if ticker: updateId += 1 # 发布最新状态,覆盖上一次的数据 SetChannelData({ "robotId": robotId, "updateId": updateId, "timestamp": int(time.time() * 1000), "symbol": "BTC_USDT", "lastPrice": ticker["Last"] }) LogStatus("频道", robotId, "第", updateId, "次发布,最新价:", ticker["Last"]) Sleep(60000) # 每分钟发布一次
    rust
    fn main() { let robotId = _G!(); // 当前实盘ID,也就是本实盘的频道ID let mut updateId = 0; loop { if let Ok(ticker) = exchange.GetTicker("BTC_USDT") { updateId += 1; // Rust 的 SetChannelData 只接受字符串,自己拼 JSON 文本 let state = format!( r#"{{"robotId": "{}", "updateId": {}, "timestamp": {}, "symbol": "BTC_USDT", "lastPrice": {}}}"#, robotId, updateId, Unix() * 1000, ticker.Last ); SetChannelData(&state); LogStatus!("频道", robotId, "第", updateId, "次发布,最新价:", ticker.Last); } Sleep(60000); // 每分钟发布一次 } }
  • 订阅端:读取两个频道

    javascript
    function main() { // 要订阅的实盘ID(按实际情况修改) var channels = ["632799", "632800"] while (true) { var msg = "" for (var i = 0; i < channels.length; i++) { // 第一次调用完成订阅并返回 null,之后返回最新数据 var state = GetChannelData(channels[i]) if (state) { msg += "频道 " + channels[i] + ":#" + state.updateId + " " + _D(state.timestamp) + " 最新价 " + state.lastPrice + "\n" } else { msg += "频道 " + channels[i] + ":等待数据\n" } } LogStatus(msg) Sleep(5000) } }
    python
    def main(): # 要订阅的实盘ID(按实际情况修改) channels = ["632799", "632800"] while True: msg = "" for ch in channels: # 第一次调用完成订阅并返回 None,之后返回最新数据 state = GetChannelData(ch) if state: msg += "频道 {}:#{} {} 最新价 {}\n".format(ch, state["updateId"], _D(state["timestamp"]), state["lastPrice"]) else: msg += "频道 {}:等待数据\n".format(ch) LogStatus(msg) Sleep(5000)
    rust
    // Rust 的 GetChannelData() 没有频道参数,不能订阅其它实盘的频道
  • 场景:主从策略协同

    主策略计算均线交叉信号并发布;从策略读取信号,在信号变化时下单。

    主策略(发布信号)

    javascript
    function main() { while (true) { var records = exchange.GetRecords("BTC_USDT") if (records && records.length >= 21) { var ma5 = TA.MA(records, 5) var ma20 = TA.MA(records, 20) var n = records.length var signal = "HOLD" if (ma5[n - 1] > ma20[n - 1] && ma5[n - 2] <= ma20[n - 2]) { signal = "BUY" } else if (ma5[n - 1] < ma20[n - 1] && ma5[n - 2] >= ma20[n - 2]) { signal = "SELL" } SetChannelData({ timestamp: Date.now(), symbol: "BTC_USDT", signal: signal, price: records[n - 1].Close }) LogStatus("当前信号:", signal, "价格:", records[n - 1].Close) } Sleep(60000) } }
    python
    import time def main(): while True: records = exchange.GetRecords("BTC_USDT") if records and len(records) >= 21: ma5 = TA.MA(records, 5) ma20 = TA.MA(records, 20) signal = "HOLD" if ma5[-1] > ma20[-1] and ma5[-2] <= ma20[-2]: signal = "BUY" elif ma5[-1] < ma20[-1] and ma5[-2] >= ma20[-2]: signal = "SELL" SetChannelData({ "timestamp": int(time.time() * 1000), "symbol": "BTC_USDT", "signal": signal, "price": records[-1]["Close"] }) LogStatus("当前信号:", signal, "价格:", records[-1]["Close"]) Sleep(60000)
    rust
    fn main() { loop { if let Ok(records) = exchange.GetRecords("BTC_USDT", None, None) { let n = records.len(); if n >= 21 { let ma5 = TA.MA(&records, 5); let ma20 = TA.MA(&records, 20); let mut signal = "HOLD"; if ma5[n - 1] > ma20[n - 1] && ma5[n - 2] <= ma20[n - 2] { signal = "BUY"; } else if ma5[n - 1] < ma20[n - 1] && ma5[n - 2] >= ma20[n - 2] { signal = "SELL"; } let price = records[n - 1].Close; // Rust 的 SetChannelData 只接受字符串,自己拼 JSON 文本 let data = format!( r#"{{"timestamp": {}, "symbol": "BTC_USDT", "signal": "{}", "price": {}}}"#, Unix() * 1000, signal, price ); SetChannelData(&data); LogStatus!("当前信号:", signal, "价格:", price); } } Sleep(60000); } }
  • 从策略(读取信号并执行)

    javascript
    function main() { var masterId = "632799" // 主策略的实盘ID var lastSignal = null while (true) { var data = GetChannelData(masterId) if (!data) { LogStatus("等待主策略信号...") } else { if (data.signal !== lastSignal) { Log("收到新信号:", data.signal, "信号价格:", data.price) var ticker = exchange.GetTicker(data.symbol) if (ticker && data.signal === "BUY") { exchange.CreateOrder(data.symbol, "buy", ticker.Last, 0.01) } else if (ticker && data.signal === "SELL") { exchange.CreateOrder(data.symbol, "sell", ticker.Last, 0.01) } lastSignal = data.signal } LogStatus("当前信号:", data.signal, "信号时间:", _D(data.timestamp)) } Sleep(5000) } }
    python
    def main(): masterId = "632799" # 主策略的实盘ID lastSignal = None while True: data = GetChannelData(masterId) if not data: LogStatus("等待主策略信号...") else: if data["signal"] != lastSignal: Log("收到新信号:", data["signal"], "信号价格:", data["price"]) ticker = exchange.GetTicker(data["symbol"]) if ticker and data["signal"] == "BUY": exchange.CreateOrder(data["symbol"], "buy", ticker["Last"], 0.01) elif ticker and data["signal"] == "SELL": exchange.CreateOrder(data["symbol"], "sell", ticker["Last"], 0.01) lastSignal = data["signal"] LogStatus("当前信号:", data["signal"], "信号时间:", _D(data["timestamp"])) Sleep(5000)
    rust
    // Rust 的 GetChannelData() 没有频道参数,不能读取主策略实盘的频道
  • 场景:多策略状态监控

    各策略按上面广播端的方式发布状态,监控实盘读取所有频道,用表格展示,超过2分钟没有更新的标记为异常。

    javascript
    function main() { var monitorList = ["632799", "632800", "632801"] // 最多10个 while (true) { var table = {type: "table", title: "策略运行状态", cols: ["实盘ID", "状态", "最后更新", "交易对", "最新价"], rows: []} for (var i = 0; i < monitorList.length; i++) { var data = GetChannelData(monitorList[i]) if (data) { var status = Date.now() - data.timestamp < 120000 ? "运行中" : "异常" table.rows.push([monitorList[i], status, _D(data.timestamp), data.symbol || "-", data.lastPrice || "-"]) } else { table.rows.push([monitorList[i], "等待数据", "-", "-", "-"]) } } LogStatus("`" + JSON.stringify(table) + "`") Sleep(10000) } }
    python
    import json import time def main(): monitorList = ["632799", "632800", "632801"] # 最多10个 while True: table = {"type": "table", "title": "策略运行状态", "cols": ["实盘ID", "状态", "最后更新", "交易对", "最新价"], "rows": []} for robotId in monitorList: data = GetChannelData(robotId) if data: status = "运行中" if time.time() * 1000 - data["timestamp"] < 120000 else "异常" table["rows"].append([robotId, status, _D(data["timestamp"]), data.get("symbol", "-"), data.get("lastPrice", "-")]) else: table["rows"].append([robotId, "等待数据", "-", "-", "-"]) LogStatus("`" + json.dumps(table) + "`") Sleep(10000)
    rust
    // Rust 的 GetChannelData() 没有频道参数,不能读取其它实盘的频道

参考