编程语言
在发明者量化交易平台上,可以使用哪些编程语言来编写我的策略呢?

发明者量化交易平台支持使用JavaScript、TypeScript、Python、Rust、PINE、My语言、Blockly可视化以及Workflow工作流来编写和设计交易策略。
JavaScript
平台支持用JavaScript编写策略。运行时基于 QuickJS 引擎,支持async/await、class、BigInt等现代语法;实盘时策略在托管者上运行,回测时在浏览器端的回测系统中运行。在代码中加入// @ts-check即可改用 TypeScript 编写,见 TypeScript。
策略结构与参数
策略入口为function main(),可选的init()、onexit()、onerror(msg)由托管者自动调用,见 策略结构。界面参数是同名的全局变量,可以直接读取,也可以在代码中修改,见 策略参数。
错误与返回值
API 函数调用失败(交易所返回错误、网络问题等)时返回null,并在日志中输出错误信息。使用返回值之前先判断,或者用 _C 重试:
javascript
function main() {
var ticker = exchange.GetTicker()
// 调用失败时返回 null
if (ticker) {
Log(ticker)
}
// 失败时自动重试,直到返回有效数据
var account = _C(exchange.GetAccount)
Log(account)
}
程序异常(例如读取undefined的属性)和接口业务报错的日志中会显示出错位置在策略代码中的行号,便于调试和排查。
字符串与ArrayBuffer
JavaScript 的字符串是 UTF-16 编码。平台 API 返回的文本如果不是合法的 UTF-8 字节序列,为了不丢失数据,会返回ArrayBuffer(原始字节)。所有可以传入字符串的 API 参数也都接受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() {
// “𠮷”的 Unicode 码点超出 16 位,在 JavaScript 字符串中占两个 UTF-16 码元
const inputString = "abc𠮷123"
// Encode 按 UTF-8 编码后输出 hex
const encodedHex = Encode("raw", "string", "hex", inputString)
Log(encodedHex) // 616263f0a0aeb7313233
// charCodeAt 取的是 UTF-16 码元,“𠮷”被写成 d842、dfb7,结果不是 UTF-8 编码
const manuallyEncodedHex = stringToHex(inputString)
Log(manuallyEncodedHex) // 616263d842dfb7313233
// 合法的 UTF-8 字节可以还原为字符串
const decodedString = Encode("raw", "hex", "string", encodedHex)
Log(decodedString) // abc𠮷123
// 不是合法的 UTF-8 字节,返回 ArrayBuffer
// (如果 inputString 改为 "abcG123",两种编码结果相同,这里得到的是字符串)
const outputD = Encode("raw", "hex", "string", manuallyEncodedHex)
Log(outputD instanceof ArrayBuffer) // true
// 查看 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
}
异步与多线程
setTimeout/clearTimeout:回调在主线程调用Sleep()等待期间执行;main()返回时还没到期的定时器会先执行完,再调用onexit()。fetch(url):返回Promise,结果为响应对象(ok、status、headers属性,text()、json()直接返回内容)。托管者中的fetch在调用时就同步完成请求,返回的是已经完成的Promise,所以用Promise.all组合多个fetch并不会并发请求。- 交易所 API(例如
exchange.GetTicker())是同步阻塞调用,包装进Promise或async函数也不会并发执行。 - 需要并发时使用
exchange.Go、HttpQuery_Go,或者用Thread创建线程,见JavaScript多线程。
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)
}
}
库与依赖
JavaScript 策略可以直接使用内置的TA指标库和talib指标库,各语言可用的内置库见 内置库。其它第三方 JavaScript 库可以在运行时下载后用eval加载,示例见同一页。
TypeScript
TypeScript 不是单独的语言选项:创建策略时选择JavaScript,在代码中加入一行// @ts-check(或点击策略编辑区右上角的「TypeScript」按钮),平台就按 TypeScript 处理,在回测和实盘运行前先编译为 JavaScript。通过 AI/MCP 工具保存策略时,语言可以直接写typescript,平台按 JavaScript 策略保存,并在代码开头自动加上//@ts-check,见 AI接入。
静态类型检查能在编写时发现参数个数、属性名、类型用错之类的问题,编辑器的补全也更准确。
最小示例:
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)
}
}
平台 API 的类型声明由策略编辑器内置提供,不需要在代码中引用:全局函数、exchange对象、ITicker、IRecord、IOrder、IPosition等数据结构接口,以及TA、talib等。运行时的语言特性、API 和库与 JavaScript 策略相同,见 JavaScript。
Python
平台支持用Python 3编写策略,不支持 Python 2。实盘以及在托管者上进行的回测,使用托管者所在机器上安装的 Python 解释器运行策略。
解释器
托管者按以下顺序查找解释器,使用第一个能启动、并且版本为 Python 3 的程序:
- 环境变量
PYTHON_BIN指定的解释器; python3;python。
需要使用指定的解释器(例如虚拟环境中的 Python)时,在启动托管者之前设置环境变量:
bash
export PYTHON_BIN=/opt/venv/bin/python3
策略代码首行的#!python3、#!python2等写法不再用于选择解释器。
策略结构与参数
策略入口为def main(),可选的init()、onexit()由托管者自动调用(Python 不支持onerror()),见 策略结构。界面参数是同名的全局变量;在函数中给参数重新赋值时,需要先用global声明。
错误与返回值
API 函数调用失败时返回None,并在日志中输出错误信息。使用返回值之前先判断,或者用_C()重试。策略中未捕获的异常会结束运行,错误信息记录在日志中。
输出
print()的输出写到托管者进程的标准输出,不会出现在实盘日志里。需要显示在日志中的内容请用 Log。
第三方库
策略可以导入解释器中已经安装的任何库。安装时要使用托管者运行策略的那个解释器,例如:
bash
python3 -m pip install numpy
# 设置了 PYTHON_BIN 时
$PYTHON_BIN -m pip install numpy
使用talib需要在托管者所在机器上安装 TA-Lib(talib包)和numpy。
自定义模块
策略运行时的当前目录和PYTHONPATH是托管者为本次运行创建的临时目录,运行结束后会被删除;放在托管者程序目录下(例如logs/storage/<实盘ID>/)的.py文件不会被自动找到。导入自己编写的模块有两种方式:
- 把模块安装到解释器的
site-packages中(例如做成包用pip install安装,或者直接复制到site-packages目录); - 在策略中先把模块所在目录的绝对路径加入
sys.path,再导入。
例如模块文件/home/user/fmz_modules/mymath.py:
python
# mymath.py
def add(a, b):
return a + b
策略代码:
python
import sys
sys.path.append("/home/user/fmz_modules") # 模块所在目录的绝对路径
import mymath
def main():
Log("mymath.add(1, 2):", mymath.add(1, 2))
把核心代码做成模块放在自己的托管者上,策略代码中只保留调用部分,也是一种不把核心代码上传到平台的做法。
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,见 内置库。
My语言(麦语言)
平台支持My语言(麦语言)编写和设计策略,兼容文华麦语言的大部分语法、指令和函数。My语言鼓励积木式编程,将复杂算法拆解为函数模块。通过简洁的语法、专用数据结构和强大的金融函数库,支持复杂金融逻辑的实现。以模块化方式构建应用,提升开发效率和代码可维护性。
My语言策略范例:基于平移布林通道的系统
mylang
M := 12; // 参数范围 1, 20
N := 3; // 参数范围 1, 10
SDEV := 2; // 参数范围 1, 10
P := 16; // 参数范围 1, 20
// 该策略为趋势跟踪交易策略,适用较大周期,如日线。
// 该模型仅用作模型开发案例,依此入市,风险自负。
////////////////////////////////////////////////////////
// 平移BOLL通道计算
MID:=MA(C,N); // 计算中轨
TMP:=STD(C,M)*SDEV; // 计算标准差
DISPTOP:=REF(MID,P)+TMP; // 平移BOLL通道上轨
DISPBOTTOM:=REF(MID,P)-TMP; // 平移BOLL通道下轨
// 系统入场
H>=DISPTOP,BPK;
L<=DISPBOTTOM,SPK;
AUTOFILTER;
PINE语言
平台支持并兼容Trading View的PINE语言脚本。PINE语言是一种轻量级但功能强大的策略编程语言,用于创建可进行回测和实盘交易的技术指标与策略。活跃的社区已创作了超过10万个PINE脚本。
用户可以轻松获取并应用各种技术分析工具和交易策略;能够借助社区脚本快速实现交易想法,无需从零开始编写代码,从而大幅缩短开发周期;帮助新手和资深交易员学习并理解不同的技术指标、策略及编程概念。
PINE语言策略示例:超级趋势策略
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()
Blockly可视化
平台支持Blockly可视化编程方式。借助Blockly编辑器,用户可以通过拼接图形块(类似积木)来表达代码概念,如变量、逻辑表达式、循环等。这种方式使编程过程无需过多关注繁琐的语法细节,而是可以直接按照编程原则进行操作。通过图形块的排列组合,用户能够轻松理解编程逻辑,实现创意想法,非常适合培养策略设计兴趣,快速入门程序化和量化交易。