Type/to search
Getting Started
Welcome to FMZ Quant Trading Platform
Quick Start
Key Security
Platform Basics
Account and Billing
Live Robot Billing and Top-up
Sub-accounts
Exchange
General Protocol
Local Credential Files
Exchange-Specific Notes
Securities and Futures
Crypto
Docker
Strategy Library
Live Trading
Writing Strategies
Development Tools
Backtesting System
Advanced Topics
Data and Research
Integrations

Every live trading has a channel whose ID is the live trading ID. A live trading publishes data on its own channel with SetChannelData(), and other live tradings read it with GetChannelData(liveTradingId). The data is relayed by the platform server, so it can travel across dockers and servers.

A channel holds the latest state, not a message queue: each publish overwrites the previous data, and a subscriber always reads the current latest copy. If history is needed, the subscriber keeps it itself.

Typical uses:

  • Master/follower: a master strategy analyzes the market and publishes signals; several follower strategies read them and trade on their own accounts.
  • Status monitoring: each strategy publishes its running status; a monitoring live trading collects them for display or alerts.
  • Data sharing: one live trading computes indicators and publishes the results; others use them directly instead of computing them again.

Key points

  • The first read subscribes: the first GetChannelData() call for a channel subscribes to it and returns an empty value (null/None). From then on the server pushes the channel's updates to this live trading, and later calls return the latest data. A subscriber should start reading at startup and handle empty values.
  • Subscription limit: a live trading can subscribe to at most 10 different channels (UUID channels below included). Beyond that, the call returns an empty value and logs the error channel subscriber exceed limit.
  • **Data format**: in JavaScript and Python, SetChannelData() accepts any JSON-serializable data and the subscriber reads the parsed object. Unchanged data is not sent again. For the data size limit see SetChannelData.
  • Rust: SetChannelData(string) only accepts a string, so build the JSON text yourself; GetChannelData() takes no channel argument and cannot choose the channel to read, so it cannot subscribe to other live tradings or UUID channels. Rust strategies are suited to publishing; write subscribers in JavaScript or Python.
  • **Cross-platform push**: an external system (a TradingView alert, your own program, etc.) can push data to a given live trading on a channel identified by a 32-character UUID through the extended API's method=pub; the live trading reads it with GetChannelData(UUID). See SetChannelData and GetChannelData for details.
  • Live trading feature: channels are meant for communication between live tradings; do not rely on them in backtests. The current live trading ID is available from _G().
  • Do not pass keys or other sensitive information through channels.

Basic usage

Examples

  • Publisher: publish a market summary

    javascript
    function main() { var robotId = _G() // current live trading ID, which is also this live trading's channel ID var updateId = 0 while (true) { var ticker = exchange.GetTicker("BTC_USDT") if (ticker) { // publish the latest state, overwriting the previous data SetChannelData({ robotId: robotId, updateId: ++updateId, timestamp: Date.now(), symbol: "BTC_USDT", lastPrice: ticker.Last }) LogStatus("channel", robotId, "publish #", updateId, "last price:", ticker.Last) } Sleep(60000) // publish once a minute } }
    python
    import time def main(): robotId = _G() # current live trading ID, which is also this live trading's channel ID updateId = 0 while True: ticker = exchange.GetTicker("BTC_USDT") if ticker: updateId += 1 # publish the latest state, overwriting the previous data SetChannelData({ "robotId": robotId, "updateId": updateId, "timestamp": int(time.time() * 1000), "symbol": "BTC_USDT", "lastPrice": ticker["Last"] }) LogStatus("channel", robotId, "publish #", updateId, "last price:", ticker["Last"]) Sleep(60000) # publish once a minute
    rust
    fn main() { let robotId = _G!(); // current live trading ID, which is also this live trading's channel ID let mut updateId = 0; loop { if let Ok(ticker) = exchange.GetTicker("BTC_USDT") { updateId += 1; // Rust's SetChannelData only accepts a string; build the JSON text yourself let state = format!( r#"{{"robotId": "{}", "updateId": {}, "timestamp": {}, "symbol": "BTC_USDT", "lastPrice": {}}}"#, robotId, updateId, Unix() * 1000, ticker.Last ); SetChannelData(&state); LogStatus!("channel", robotId, "publish #", updateId, "last price:", ticker.Last); } Sleep(60000); // publish once a minute } }
  • Subscriber: read two channels

    javascript
    function main() { // live trading IDs to subscribe to (change as needed) var channels = ["632799", "632800"] while (true) { var msg = "" for (var i = 0; i < channels.length; i++) { // the first call subscribes and returns null; later calls return the latest data var state = GetChannelData(channels[i]) if (state) { msg += "channel " + channels[i] + ": #" + state.updateId + " " + _D(state.timestamp) + " last " + state.lastPrice + "\n" } else { msg += "channel " + channels[i] + ": waiting for data\n" } } LogStatus(msg) Sleep(5000) } }
    python
    def main(): # live trading IDs to subscribe to (change as needed) channels = ["632799", "632800"] while True: msg = "" for ch in channels: # the first call subscribes and returns None; later calls return the latest data state = GetChannelData(ch) if state: msg += "channel {}: #{} {} last {}\n".format(ch, state["updateId"], _D(state["timestamp"]), state["lastPrice"]) else: msg += "channel {}: waiting for data\n".format(ch) LogStatus(msg) Sleep(5000)
    rust
    // Rust's GetChannelData() takes no channel argument and cannot subscribe to other live tradings' channels
  • Scenario: master/follower strategies

    The master strategy computes a moving-average crossover signal and publishes it; the follower reads the signal and places an order when it changes.

    Master strategy (publishes signals)

    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("current signal:", signal, "price:", 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("current signal:", signal, "price:", 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's SetChannelData only accepts a string; build the JSON text yourself let data = format!( r#"{{"timestamp": {}, "symbol": "BTC_USDT", "signal": "{}", "price": {}}}"#, Unix() * 1000, signal, price ); SetChannelData(&data); LogStatus!("current signal:", signal, "price:", price); } } Sleep(60000); } }
  • Follower strategy (reads and executes signals)

    javascript
    function main() { var masterId = "632799" // live trading ID of the master strategy var lastSignal = null while (true) { var data = GetChannelData(masterId) if (!data) { LogStatus("waiting for the master strategy's signal...") } else { if (data.signal !== lastSignal) { Log("new signal:", data.signal, "signal price:", 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("current signal:", data.signal, "signal time:", _D(data.timestamp)) } Sleep(5000) } }
    python
    def main(): masterId = "632799" # live trading ID of the master strategy lastSignal = None while True: data = GetChannelData(masterId) if not data: LogStatus("waiting for the master strategy's signal...") else: if data["signal"] != lastSignal: Log("new signal:", data["signal"], "signal price:", 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("current signal:", data["signal"], "signal time:", _D(data["timestamp"])) Sleep(5000)
    rust
    // Rust's GetChannelData() takes no channel argument and cannot read the master strategy's channel
  • Scenario: monitoring several strategies

    Each strategy publishes its status as the publisher above does; the monitoring live trading reads every channel, shows them in a table and marks those without an update for over 2 minutes as abnormal.

    javascript
    function main() { var monitorList = ["632799", "632800", "632801"] // at most 10 while (true) { var table = {type: "table", title: "Strategy status", cols: ["Live trading ID", "Status", "Last update", "Symbol", "Last price"], rows: []} for (var i = 0; i < monitorList.length; i++) { var data = GetChannelData(monitorList[i]) if (data) { var status = Date.now() - data.timestamp < 120000 ? "running" : "abnormal" table.rows.push([monitorList[i], status, _D(data.timestamp), data.symbol || "-", data.lastPrice || "-"]) } else { table.rows.push([monitorList[i], "waiting for data", "-", "-", "-"]) } } LogStatus("`" + JSON.stringify(table) + "`") Sleep(10000) } }
    python
    import json import time def main(): monitorList = ["632799", "632800", "632801"] # at most 10 while True: table = {"type": "table", "title": "Strategy status", "cols": ["Live trading ID", "Status", "Last update", "Symbol", "Last price"], "rows": []} for robotId in monitorList: data = GetChannelData(robotId) if data: status = "running" if time.time() * 1000 - data["timestamp"] < 120000 else "abnormal" table["rows"].append([robotId, status, _D(data["timestamp"]), data.get("symbol", "-"), data.get("lastPrice", "-")]) else: table["rows"].append([robotId, "waiting for data", "-", "-", "-"]) LogStatus("`" + json.dumps(table) + "`") Sleep(10000)
    rust
    // Rust's GetChannelData() takes no channel argument and cannot read other live tradings' channels

See Also