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

What programming languages can I use to write my strategies on the FMZ Quant trading platform?

Supported Programming Languages

The FMZ Quant trading platform supports writing and designing trading strategies using JavaScript, TypeScript, Python, Rust, PINE, My Language, Blockly visual programming, and the Workflow workflow tool.

Strategies can be written in JavaScript. The runtime is based on the QuickJS engine and supports modern syntax such as async/await, class and BigInt. In live trading the strategy runs on the docker; in backtesting it runs in the browser-side backtesting system. Adding // @ts-check to the code switches to TypeScript (see Programming Languages → TypeScript).

Structure and parameters

The entry point is function main(). The optional init(), onexit() and onerror(msg) are called automatically by the docker (see Writing Strategies → Strategy Structure). Interface parameters are global variables with the same names; they can be read directly and also modified in code (see Writing Strategies → Strategy Parameters).

Errors and return values

When an API call fails (the exchange returns an error, a network problem, etc.) it returns null and writes the error to the log. Check the return value before using it, or retry with _C:

javascript
function main() { var ticker = exchange.GetTicker() // null when the call fails if (ticker) { Log(ticker) } // retry until valid data is returned var account = _C(exchange.GetAccount) Log(account) }

For program exceptions (for example reading a property of undefined) and API business errors, the log shows the line number in the strategy code where the error occurred, which makes debugging easier.

Strings and ArrayBuffer

JavaScript strings are UTF-16. If text returned by a platform API is not a valid UTF-8 byte sequence, an ArrayBuffer (the raw bytes) is returned instead so that no data is lost. Every API parameter that accepts a string also accepts an ArrayBuffer.

javascript
function stringToHex(str) { let hex = '' for (let i = 0; i < str.length; i++) { const charCode = str.charCodeAt(i).toString(16) hex += charCode.length === 1 ? '0' + charCode : charCode } return hex } function main() { // the code point of "𠮷" exceeds 16 bits; it takes two UTF-16 code units in a JavaScript string const inputString = "abc𠮷123" // Encode outputs the UTF-8 bytes as hex const encodedHex = Encode("raw", "string", "hex", inputString) Log(encodedHex) // 616263f0a0aeb7313233 // charCodeAt returns UTF-16 code units, so "𠮷" becomes d842, dfb7 - not UTF-8 const manuallyEncodedHex = stringToHex(inputString) Log(manuallyEncodedHex) // 616263d842dfb7313233 // valid UTF-8 bytes decode back to a string const decodedString = Encode("raw", "hex", "string", encodedHex) Log(decodedString) // abc𠮷123 // bytes that are not valid UTF-8 come back as an ArrayBuffer // (with inputString = "abcG123" both encodings are identical and this is a string) const outputD = Encode("raw", "hex", "string", manuallyEncodedHex) Log(outputD instanceof ArrayBuffer) // true // inspect the raw bytes in the ArrayBuffer const bufferD = new Uint8Array(outputD) let hexBufferD = '' for (let i = 0; i < bufferD.length; i++) { hexBufferD += bufferD[i].toString(16).padStart(2, '0') } Log(hexBufferD) // 616263d842dfb7313233 }

Asynchrony and threads

  • setTimeout/clearTimeout: callbacks run while the main thread is waiting in Sleep(). When main() returns, timers that have not fired yet run first, then onexit() is called.
  • fetch(url): returns a Promise that resolves to a response object (ok, status, headers; text() and json() return the content directly). On the docker, fetch completes the request synchronously when called and returns an already settled Promise, so combining several fetch calls with Promise.all does not make them concurrent.
  • Exchange APIs (such as exchange.GetTicker()) are synchronous blocking calls; wrapping them in a Promise or an async function does not make them concurrent either.
  • For concurrency use exchange.Go, HttpQuery_Go, or create threads with Thread (see Advanced Topics → JavaScript Multithreading).
javascript
async function main() { let resp = await fetch("https://www.okx.com/api/v5/market/books?instId=BTC-USDT") if (resp.ok) { Log(resp.json()) } else { Log("status:", resp.status) } }

Libraries and dependencies

JavaScript strategies can use the built-in TA and talib indicator libraries directly; see Writing Strategies → Built-in Libraries for what each language provides. Other third-party JavaScript libraries can be downloaded at run time and loaded with eval; the same page has an example.

TypeScript is not a separate language option. Create the strategy as JavaScript and add a // @ts-check line to the code (or click the "TypeScript" button at the top right of the editor); the platform then treats it as TypeScript and compiles it to JavaScript before backtesting or live trading. When a strategy is saved through the AI/MCP tools, the language can be given as typescript: the platform saves it as a JavaScript strategy and adds //@ts-check at the top automatically (see External Interfaces → AI Access).

Static type checking catches mistakes such as wrong argument counts, property names or types while you write, and makes editor completion more accurate.

A minimal example:

ts
// @ts-check interface Signal { side: "buy" | "sell" price: number } function getSignal(ticker: ITicker, ma: number): Signal | null { if (ticker.Last > ma) { return {side: "buy", price: ticker.Last} } if (ticker.Last < ma) { return {side: "sell", price: ticker.Last} } return null } function main() { while (true) { const records = exchange.GetRecords() const ticker = exchange.GetTicker() if (records && ticker && records.length > 20) { const ma = TA.MA(records, 20) const signal = getSignal(ticker, ma[ma.length - 1]) if (signal) { Log(signal.side, signal.price) } } Sleep(60 * 1000) } }

Type declarations for the platform API are built into the strategy editor; nothing needs to be referenced in the code. They cover the global functions, the exchange object, data structure interfaces such as ITicker, IRecord, IOrder and IPosition, and TA, talib and so on. Language features, APIs and libraries at run time are the same as for JavaScript strategies (see Programming Languages → JavaScript).

Strategies can be written in Python 3; Python 2 is not supported. Live trading, and backtests that run on a docker, use the Python interpreter installed on the docker's machine.

Interpreter

The docker looks for an interpreter in this order and uses the first program that starts and is Python 3:

  1. the interpreter given by the environment variable PYTHON_BIN;
  2. python3;
  3. python.

To use a specific interpreter (for example the Python of a virtual environment), set the environment variable before starting the docker:

bash
export PYTHON_BIN=/opt/venv/bin/python3

A first line such as #!python3 or #!python2 in the strategy is no longer used to choose the interpreter.

Structure and parameters

The entry point is def main(). The optional init() and onexit() are called automatically by the docker (Python does not support onerror()); see Writing Strategies → Strategy Structure. Interface parameters are global variables with the same names; to assign a new value to one inside a function, declare it with global first.

Errors and return values

When an API call fails it returns None and writes the error to the log. Check the return value before using it, or retry with _C(). An uncaught exception ends the strategy, and the error is recorded in the log.

Output

The output of print() goes to the docker process's standard output and does not appear in the live trading log. Use Log for anything that should show up in the log.

Third-party packages

A strategy can import any package installed in the interpreter. Install packages with the same interpreter the docker uses, for example:

bash
python3 -m pip install numpy # when PYTHON_BIN is set $PYTHON_BIN -m pip install numpy

To use talib, install TA-Lib (the talib package) and numpy on the docker's machine.

Your own modules

While a strategy runs, its current directory and PYTHONPATH are a temporary directory created by the docker for that run and deleted afterwards; .py files placed under the docker's directory (for example logs/storage/<live trading ID>/) are not found automatically. There are two ways to import your own modules:

  • install the module into the interpreter's site-packages (for example package it and install it with pip install, or copy it into the site-packages directory);
  • in the strategy, append the absolute path of the module's directory to sys.path, then import it.

For example, with the module file /home/user/fmz_modules/mymath.py:

python
# mymath.py def add(a, b): return a + b

the strategy code is:

python
import sys sys.path.append("/home/user/fmz_modules") # absolute path of the module's directory import mymath def main(): Log("mymath.add(1, 2):", mymath.add(1, 2))

Keeping the core logic in a module on your own docker, with only the calling code in the strategy, is also a way to avoid uploading that logic to the platform.

Strategies can be written in Rust. Rust strategies are compiled before they run: for backtesting the platform server compiles them and they run in the browser-side backtesting system; in live trading they run on the docker once compiled. The strategy editor integrates rust-analyzer for Rust, with code completion and live diagnostics.

Structure

A strategy only needs a fn main(). The platform API (exchange, exchanges, TA, Log!, _C! and so on) is imported automatically; no use or mod declarations are needed.

The optional fn init() and fn onexit() are called automatically by the docker; just define them, no registration is needed. init() runs before main(); onexit() runs when main() returns normally, when the live trading is stopped, and when the strategy panics. Rust does not support onerror(). See Writing Strategies → Strategy Structure.

rust
fn init() { Log!("initializing"); } fn main() { // APIs that can fail return Result<T>; the _C! macro retries until the call succeeds let ticker = _C!(exchange.GetTicker(None)); Log!("Last:", ticker.Last); } fn onexit() { Log!("strategy exiting, cleaning up"); }

Some functions are macros (note the exclamation mark): Log!(), LogStatus!(), Panic!(), _G!(), _C!(). LogProfit(), Sleep(), _D(), _N(), HttpQuery() and others are ordinary functions.

Parameter types

Interface parameters are injected as global constants with the same names. They can only be read, not modified in code (copy a value into a local variable if it needs to change). The type depends on the kind of parameter:

Parameter kindRust type
Numberf64
Booleanbool
String&str
Dropdown (single choice)f64 (option index); &str when the options are bound to string data
Dropdown (multiple choice)&[i64], &[f64] or &[&str]; JSON text as &str when the option values have mixed types
Encrypted string&str or Decrypted (dereferences to str)
  • Convert explicitly where an integer is needed, for example let n = Period as usize;.
  • An optional parameter that is left empty has the zero value of its type: 0.0, "", false, or an empty list for a multiple-choice dropdown.
  • When the server cannot decrypt an encrypted-string parameter in advance (for example on a private docker), it is injected as a static of type Decrypted and decrypted on first use. It implements Display, so it can be used directly with format!; when passing it to Log! or anywhere a &str is needed, write &*ParamName (this also works for &str parameters):
rust
fn main() { let key: &str = &*ApiKey; // ApiKey is an encrypted-string parameter Log!("key length:", key.len()); }
  • If a parameter name clashes with another name in the code, refer to the parameter as args::ParamName.

Errors and return values

API calls that can fail return Result<T>; handle it the usual Rust way (in JavaScript a failed call returns null):

rust
fn main() { // option 1: pattern matching if let Ok(ticker) = exchange.GetTicker(None) { Log!(ticker); } // option 2: the _C! macro retries until the call succeeds let ticker = _C!(exchange.GetTicker(None)); Log!(ticker); }

Optional arguments (such as the symbol argument of GetTicker) are passed as None when omitted, or given directly, for example exchange.GetTicker("BTC_USDT").

JSON

Raw JSON text returned by the platform API (for example the return value of exchange.IO() or the Info field of each structure) is parsed with the built-in JSONParse(), which returns an Option<JsonValue>. Navigate with v["key"] and v[0] and read values with methods such as as_f64(), as_str() and as_bool(). JsonValue implements Display, so v.to_string() or format!("{}", v) gives compact JSON text. The SDK has no convenient API for building JSON; build JSON text with format!, or use serde_json.

Third-party crates

The strategy source is the only code file (there is no separate Cargo.toml). Declare dependencies in a frontmatter block wrapped in --- at the very top of the source; it is merged into Cargo.toml at build time:

rust
--- [dependencies] serde_json = "1" --- /*backtest start: 2024-01-01 00:00:00 end: 2024-02-01 00:00:00 period: 1h */ fn main() { let v: serde_json::Value = serde_json::from_str(r#"{"a": 1}"#).unwrap(); Log!("a:", v["a"].to_string()); }
  • The frontmatter must be at the start of the source, with only blank lines before it; the /*backtest ... */ backtest configuration block goes after the closing ---. If the strategy has no backtest configuration block yet, "Save Backtest Settings" inserts one at the very top of the source; move it below the frontmatter (later saves update it in place).
  • Between a strategy and the template libraries it references, the dependency block may appear in only one place; declaring it in both fails the build.
  • The build environment has no system OpenSSL. For crates that need TLS (HTTP/WebSocket clients and the like), choose the pure-Rust rustls implementation (for example tokio-tungstenite with the rustls-tls-webpki-roots feature) and avoid native-tls/openssl-sys. For WebSocket connections prefer the built-in Dial function, which needs no third-party crate.

Built-in libraries

Rust strategies can use the TA indicator library; talib is not supported. See Writing Strategies → Built-in Libraries.

The platform supports MyLanguage for writing and designing strategies, compatible with most syntax, instructions and functions of Wenhua MyLanguage. MyLanguage encourages modular programming, breaking down complex algorithms into function modules. Through concise syntax, dedicated data structures and powerful financial function libraries, it supports the implementation of complex financial logic. Building applications in a modular way improves development efficiency and code maintainability.

MyLanguage Strategy Example: System Based on Displaced Bollinger Bands

mylang
M := 12; // Parameter range 1, 20 N := 3; // Parameter range 1, 10 SDEV := 2; // Parameter range 1, 10 P := 16; // Parameter range 1, 20 // This strategy is a trend-following trading strategy, suitable for larger timeframes such as daily charts. // This model is only used as a model development case. Trading based on this carries your own risk. //////////////////////////////////////////////////////// // Displaced BOLL channel calculation MID:=MA(C,N); // Calculate middle band TMP:=STD(C,M)*SDEV; // Calculate standard deviation DISPTOP:=REF(MID,P)+TMP; // Displaced BOLL channel upper band DISPBOTTOM:=REF(MID,P)-TMP; // Displaced BOLL channel lower band // System entry H>=DISPTOP,BPK; L<=DISPBOTTOM,SPK; AUTOFILTER;

The platform supports and is compatible with Trading View's PINE language scripts. PINE is a lightweight yet powerful strategy programming language for creating technical indicators and strategies that can be backtested and traded live. The active community has created over 100,000 PINE scripts.
Users can easily access and apply various technical analysis tools and trading strategies; leverage community scripts to quickly implement trading ideas without writing code from scratch, significantly reducing development cycles; help both beginners and experienced traders learn and understand different technical indicators, strategies, and programming concepts.

PINE Language Strategy Example: Supertrend Strategy

pine
strategy("supertrend", overlay=true) [supertrend, direction] = ta.supertrend(input(5, "factor"), input.int(10, "atrPeriod")) plot(direction < 0 ? supertrend : na, "Up direction", color = color.green, style=plot.style_linebr) plot(direction > 0 ? supertrend : na, "Down direction", color = color.red, style=plot.style_linebr) if direction < 0 if supertrend > supertrend[2] strategy.entry("entry long", strategy.long) else if strategy.position_size < 0 strategy.close_all() else if direction > 0 if supertrend < supertrend[3] strategy.entry("entry short", strategy.short) else if strategy.position_size > 0 strategy.close_all()

The platform supports Blockly visual programming. With the Blockly editor, users can express code concepts such as variables, logical expressions, and loops by connecting graphical blocks (similar to building blocks). This approach allows the programming process to focus less on tedious syntax details and instead operate directly according to programming principles. Through the arrangement and combination of graphical blocks, users can easily understand programming logic and implement creative ideas, making it ideal for cultivating interest in strategy design and quickly getting started with programmatic and quantitative trading.

The platform supports writing strategies using the Workflow approach. Workflow is a visual strategy design method that builds trading logic through node connections and configurations, enabling strategy implementation without writing code.

Workflow Features:

  • Visual drag-and-drop design, WYSIWYG
  • Rich preset functional nodes (data retrieval, indicator calculation, conditional judgment, trade execution, etc.)
  • Lower programming barrier, suitable for rapid strategy building and validation
  • Supports backtesting functionality with visual node execution status viewing

Learning Resources: