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

JavaScript strategies can use the threading object to create threads that really run in parallel, and exchange data between them with messages, shared dictionaries, locks and similar objects. This page explains when to use threads and how to organize thread code; for the parameters and return values of each function see Threads in the syntax manual.

Pick the right tool first

NeedRecommendedLanguages
Send several API requests at once (for example tickers from several exchanges) and wait for the resultsexchange.Go, with EventLoop to wait for completion eventsAll languages
Long-running background work: separate market data collection, risk checks, heavy computationthreading.ThreadJavaScript only
Serve HTTP, WebSocket or TCP from inside the strategythreading.ServeJavaScript only

When you only need a few concurrent requests, exchange.Go() is simpler and involves no data passing between threads. The threading object on this page is for JavaScript strategies only; Python and Rust strategies use exchange.Go().

These functions can be called in the backtesting system, but the threads actually run one after another there; this only keeps the code runnable in backtests.

Threads run in isolated environments

The function passed to threading.Thread() runs in a separate JavaScript environment. This is the most important thing to keep in mind when writing thread code:

  • A thread function cannot reference outer variables or closures, nor call other functions defined in the strategy. Pass the data it needs as arguments: threading.Thread(func, arg1, arg2, ...).
  • Plain objects and arrays passed as arguments are deep-copied: changing them inside the thread does not affect other threads. When several threads need to see the same data, use a dictionary created by threading.Dict().
  • Functions can be passed as arguments too; threading.Thread() also accepts function source strings, which can be used to load external libraries in the thread.
  • Platform API functions such as exchange.GetTicker() and Log() can be called directly in a thread.
  • The return value of the thread function is retrieved with join(): t.join().ret.

Exchanging data between threads

MethodUsageNotes
Messagest.postMessage(msg) sends to thread t; inside a thread, threading.currentThread().peekMessage(timeout) reads the messages it received; a child thread sends back to the main thread with threading.mainThread().postMessage(msg)Each thread has its own inbox, read in order. peekMessage(-1) does not block and returns an empty value when there is no message
Shared dictionaryvar d = threading.Dict(), pass it to threads as an argument, then each thread uses d.get(key) and d.set(key, value)Good for holding the "latest state", such as the latest price or a running flag
Thread datat.setData(key, value), t.getData(key)Key-value pairs attached to a thread object; invalid after the thread ends (join(), terminate())
Synchronization objectsthreading.Lock(), threading.Event(), threading.Condition()Passed to threads as arguments for mutual exclusion and waiting for notifications

A message received by a thread also raises an event, so the thread object's eventLoop can wait for messages and other events in one place.

Thread lifecycle

  • t.join() waits for the thread to end and returns its result, with an optional timeout; t.terminate() ends a thread forcibly.
  • When a thread has ended and is no longer referenced, its resources are reclaimed automatically; there is no need to call join() just to free them. An error is raised when more than 2000 threads are kept referenced and cannot be reclaimed.
  • threading.pending() returns the number of running threads (main thread included).
  • All threads end when the live trading stops. Waits in peekMessage(), join(), locks and events are interrupted by the stop.

Serving from inside the strategy

threading.Serve(address, handler, ...args) starts an HTTP (WebSocket included) or TCP service inside the strategy process. Each request or connection calls the handler in its own thread. It returns a Server object (addr() gives the actual listening address, close() shuts it down). Like thread functions, handlers run in isolated environments and receive what they need as arguments; a threading.Dict() is commonly used to share state with the main thread. For the address syntax and the methods of the ctx object see Serve.

The old global function __Serve() still works but only returns the listening address string; use threading.Serve() in new code.

Examples

Examples

  • Several threads compute in parallel, the main thread collects the results

    Each thread fetches the K-lines of one symbol and computes a moving average; the result goes back to the main thread as the return value. Note that the symbol is passed as an argument and the thread function references no outer variables.

    javascript
    function main() { var symbols = ["BTC_USDT", "ETH_USDT", "SOL_USDT"] var threads = [] for (var i = 0; i < symbols.length; i++) { threads.push(threading.Thread(function(symbol, period) { // runs in the thread: only arguments and platform APIs are available var records = exchange.GetRecords(symbol, period) if (!records || records.length < 20) { return null } var ma = TA.MA(records, 20) return {symbol: symbol, close: records[records.length - 1].Close, ma20: ma[ma.length - 1]} }, symbols[i], PERIOD_H1)) } for (var i = 0; i < threads.length; i++) { var r = threads[i].join().ret if (r) { Log(r.symbol, "close:", r.close, "MA20:", r.ma20) } } }
  • A background thread collects prices, the main thread reads them and sends commands

    The background thread writes the latest price into a shared dictionary and reports errors to the main thread through messages; the main thread tells it to exit with a message.

    javascript
    function main() { var shared = threading.Dict() var worker = threading.Thread(function(dict, symbol) { while (true) { // read commands from the main thread; -1 means do not block var cmd = threading.currentThread().peekMessage(-1) if (cmd == "stop") { break } var ticker = exchange.GetTicker(symbol) if (ticker) { dict.set("last", ticker.Last) dict.set("time", ticker.Time) } else { threading.mainThread().postMessage("failed to get ticker: " + GetLastError()) } Sleep(1000) } return "worker exited" }, shared, "BTC_USDT") for (var i = 0; i < 10; i++) { // wait at most 1 second for a message from the background thread var msg = threading.currentThread().peekMessage(1000) if (msg) { Log("background thread reports:", msg) } LogStatus("last:", shared.get("last"), "time:", _D(shared.get("time"))) } worker.postMessage("stop") Log(worker.join().ret) }
  • A status endpoint with threading.Serve

    The main thread writes the state into a shared dictionary; the HTTP handler gets the same dictionary as an argument and returns it as JSON.

    javascript
    function main() { var state = threading.Dict() var server = threading.Serve("http://127.0.0.1:8088", function(ctx, st) { if (ctx.path() == "/status") { ctx.setHeader("Content-Type", "application/json") ctx.write(JSON.stringify({last: st.get("last"), updated: st.get("updated")})) } else { ctx.setStatus(404) } }, state) Log("listening on:", server.addr()) while (true) { var ticker = exchange.GetTicker("BTC_USDT") if (ticker) { state.set("last", ticker.Last) state.set("updated", _D()) } Sleep(3000) } }

See Also