Rust
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 kind | Rust type |
|---|---|
| Number | f64 |
| Boolean | bool |
| 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
staticof typeDecryptedand decrypted on first use. It implementsDisplay, so it can be used directly withformat!; when passing it toLog!or anywhere a&stris needed, write&*ParamName(this also works for&strparameters):
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
rustlsimplementation (for exampletokio-tungstenitewith therustls-tls-webpki-rootsfeature) and avoidnative-tls/openssl-sys. For WebSocket connections prefer the built-inDialfunction, 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.