输入/搜索内容
入门
欢迎使用发明者量化交易平台
快速开始
密钥安全性
平台基础
账号与计费
实盘计费与充值
子账号
交易所
通用协议
本地凭据文件
交易所特殊说明
证券与期货
加密货币
托管者
策略库
实盘
编写策略
开发工具
回测系统
进阶专题
数据与研究
对外接口

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

支持的编程语言

发明者量化交易平台支持使用JavaScript、TypeScript、Python、Rust、PINE、My语言、Blockly可视化以及Workflow工作流来编写和设计交易策略。

平台支持用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 不是单独的语言选项:创建策略时选择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 3编写策略,不支持 Python 2。实盘以及在托管者上进行的回测,使用托管者所在机器上安装的 Python 解释器运行策略。

解释器

托管者按以下顺序查找解释器,使用第一个能启动、并且版本为 Python 3 的程序:

  1. 环境变量PYTHON_BIN指定的解释器;
  2. python3;
  3. 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-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语言策略范例:基于平移布林通道的系统

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;

平台支持并兼容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编辑器,用户可以通过拼接图形块(类似积木)来表达代码概念,如变量、逻辑表达式、循环等。这种方式使编程过程无需过多关注繁琐的语法细节,而是可以直接按照编程原则进行操作。通过图形块的排列组合,用户能够轻松理解编程逻辑,实现创意想法,非常适合培养策略设计兴趣,快速入门程序化和量化交易。

平台支持 Workflow 工作流方式编写策略。工作流是一种可视化的策略设计方式,通过节点连接和配置来构建交易逻辑,无需编写代码即可实现策略。

工作流特点:

  • 可视化拖拽设计,所见即所得
  • 预置丰富的功能节点(数据获取、指标计算、条件判断、交易执行等)
  • 降低编程门槛,适合快速搭建和验证策略
  • 支持回测功能,可视化查看节点执行状态

学习资源: