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

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.