Rust
平台支持用Rust编写策略。Rust 策略先编译再运行:回测时由平台服务器编译,在浏览器端的回测系统中运行;实盘时编译通过后在托管者上运行。策略编辑器为 Rust 集成了rust-analyzer,提供代码补全与实时诊断。
策略结构
策略代码只需要一个fn main()。平台 API(exchange、exchanges、TA、Log!、_C!等)已经自动导入,不需要写use或mod声明。
可选的fn init()、fn onexit()由托管者自动调用,定义即可,不需要注册:init()在main()之前执行;onexit()在main()正常返回、实盘被停止、策略panic时都会执行。Rust 不支持onerror()。详见 策略结构。
rust
fn init() {
Log!("初始化");
}
fn main() {
// 可能失败的 API 返回 Result<T>,_C! 宏在失败时重试,直到调用成功
let ticker = _C!(exchange.GetTicker(None));
Log!("Last:", ticker.Last);
}
fn onexit() {
Log!("策略退出,执行扫尾处理");
}
日志等部分功能以宏的形式提供(注意感叹号):Log!()、LogStatus!()、Panic!()、_G!()、_C!();LogProfit()、Sleep()、_D()、_N()、HttpQuery()等是普通函数。
参数类型
界面参数以同名的全局常量注入策略,只能读取,不能在代码中修改(需要变化的值请复制到局部变量)。类型由参数种类决定:
| 参数种类 | Rust 类型 |
|---|---|
| 数字型 | f64 |
| 布尔型 | bool |
| 字符串 | &str |
| 下拉框(单选) | f64(选项索引);选项绑定了字符串数据时为&str |
| 下拉框(多选) | &[i64]、&[f64]或&[&str];选项值类型混杂时为 JSON 文本&str |
| 加密串 | &str或Decrypted(可解引用为str) |
- 整数用途需要自行转换,例如
let n = Period as usize;。 - 选填参数没有填写时为该类型的零值:
0.0、""、false,多选下拉框为空。 - 加密串参数在服务端不能预先解密时(例如私有托管者),注入为
static的Decrypted类型,第一次使用时才解密。它实现了Display,可以直接用于format!;传给Log!或其它需要&str的地方写&*参数名(对&str类型的参数同样适用):
rust
fn main() {
let key: &str = &*ApiKey; // ApiKey 为加密串参数
Log!("key length:", key.len());
}
- 参数名与代码中的其它名字冲突时,可以用
args::参数名引用参数。
错误与返回值
可能失败的 API 调用返回Result<T>,用 Rust 惯用的方式处理(JavaScript 中失败返回null):
rust
fn main() {
// 方式一:模式匹配
if let Ok(ticker) = exchange.GetTicker(None) {
Log!(ticker);
}
// 方式二:_C! 宏在失败时重试,直到调用成功
let ticker = _C!(exchange.GetTicker(None));
Log!(ticker);
}
可选参数(例如GetTicker的symbol参数)不传时用None占位,传值时直接传入,例如exchange.GetTicker("BTC_USDT")。
JSON
平台 API 返回的原始 JSON 文本(例如exchange.IO()的返回值、各结构体的Info字段)用内置的JSONParse()解析,得到Option<JsonValue>;用v["key"]、v[0]取子节点,用as_f64()、as_str()、as_bool()等方法取值。JsonValue实现了Display,v.to_string()或format!("{}", v)得到紧凑的 JSON 文本。SDK 没有提供构造 JSON 的便捷接口,生成 JSON 文本可以用format!拼接,或者引入serde_json。
第三方 crate
策略源码是唯一的代码文件(没有单独的Cargo.toml)。在源码最顶部用---包裹的 frontmatter 声明依赖,构建时合并进Cargo.toml:
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());
}
- frontmatter 必须在源码开头,前面只能有空行;
/*backtest ... */回测配置块要放在 frontmatter 结束的---之后。策略中还没有回测配置块时,「保存回测设置」会把配置块插入到源码最前面,这时需要把它移到 frontmatter 之后(之后再保存会在原位置更新)。 - 策略和它引用的模板类库中,依赖块只能写在其中一处,两处都写会编译失败。
- 编译环境中没有系统 OpenSSL,需要 TLS 的 crate(HTTP/WebSocket 客户端等)请选用纯 Rust 实现的
rustls(例如tokio-tungstenite开启rustls-tls-webpki-roots特性),避免依赖native-tls/openssl-sys;WebSocket 连接优先使用内置的Dial函数,不需要第三方 crate。
内置库
Rust 策略可以使用TA指标库,不支持talib,见 内置库。