Loading ...

发明者量化国际站API文档

Author: 小小梦, Created: 2021-10-14 14:39:45, Updated: 2021-10-14 15:07:03

[TOC]

基础说明

入门

FMZ量化交易平台能够做什么?

FMZ(发明者)量化交易平台是量化交易领域最专业的量化社区,在这里你可以学习、编写、分享、买卖量化策略;在线回测和使用模拟盘进行模拟交易;运行、公开、围观实盘。支持几乎所有的主流数字货币交易所。

完整的系列教程

图文教程:

视频教程:

  • 如果倾向于视频教程,FMZ量化交易平台官方也提供了网易云课堂教程数字货币量化交易课程。 包含了JavaScript教程、平台使用以及策略编写等完整内容。如果你有编程和交易基础,大约两天即可入门编写出简单的策略,没有编程基础的也只需要一两周时间学习基础的编程知识。
  • bilibili也有视频教程

如果遇到问题可以随时到论坛发帖提问、讨论,在平台提出工单,在电报(Telegram)群@管理员,问题一般都会很快解答。

可用哪些编程语言实现我的策略呢?

FMZ量化交易平台支持使用JavaScriptPythonC++麦语言Blockly可视化编写设计策略。

这些策略设计语言中掌握其中一种就足够了。除了支持编写代码的方式设计策略,还可以使用可视化模块创建策略(Blockly)。可视化模块拼接搭建策略采用了更加直观的方式设计策略,无需编码。非常适合培养策略设计兴趣,以便快速入门程序化、量化交易。

Blockly可视化教程:

设置Python策略程序使用的Python解释器

使用Python编写的策略,回测或实盘时如果托管者所在系统环境同时安装了Python2Python3,可以在策略开始第一行设置策略运行时启动的Python版本。例如:#!python3#!python2,这样系统就会自动查找解释器。也可以指定绝对路径,例如:#!/usr/bin/python3

什么是托管者?

托管者可以理解为您的交易策略的执行者,负责复杂的数据请求、数据接收、网络链接、日志回传等等工作。托管者运行在您的服务器上,即使FMZ量化交易平台网站出现网络故障也不影响您的托管者运行。托管者可运行在LinuxWindowsMac OSandroid树莓派 ARM Linux等系统上。托管者页面Linux托管者安装步骤及托管者更新步骤。托管者管理的实盘日志均保存在托管者程序所在目录./logs/storage内,文件为db3Sqlite数据库文件中。可以用Sqlite管理软件直接编辑,对于这些扩展名为db3的实盘数据库文件来说文件名即为实盘的ID

支持的协议

  • 区块链资产:现已支持50多家主流区块链资产(数字货币)交易所。
  • 通用协议接入:通用协议

策略安全性

在FMZ量化交易平台上开发策略,策略仅FMZ量化账户持有者可见。并且在FMZ量化交易平台上可以实现策略代码完全本地化,例如把策略封装成一个Python包在策略代码中加载,这样就实现了策略本地化。

Python代码的安全性: 因为Python是开源且极易被反编译的语言,如果策略非自用而是出租,如果担心策略泄漏可让策略运行于自己布署的托管者上并以子账号或全托管管理这种形式出租。

Python策略代码加密: 默认情况下,Python策略代码作者自用时不加密,租出给他人使用时加密。在Python策略开头编辑如下代码,可以指定自用或者租出Python策略运行时是否加密策略代码。支持策略代码加密的Python版本为:Python 2.7版本、Python 3.5版本、Python 3.6版本。

  • 策略作者自己运行、通过注册码给他人使用均加密策略代码: #!python为指定Python解释器版本,之后使用逗号,间隔,输入加密指令encrypt。如果不指定Python版本直接添加#!,encrypt

    #!python,encrypt
    

    或者

    #!,encrypt
    
  • 策略作者自己运行、通过注册码给他人使用均不加密策略代码:

    #!python,unencrypt
    

    或者

    #,unencrypt
    

判断Python策略代码加密是否生效使用代码os.getenv('__FMZ_ENV__'),返回字符串"encrypt"说明已经生效。仅实盘有效,回测不会加密Python策略代码。

#!,encrypt
def main():
    ret = os.getenv('__FMZ_ENV__')
    # 打印变量ret为字符串encrypt或者ret == "encrypt"为真,即代表加密生效
    Log(ret, ret == "encrypt")

密钥安全性

在FMZ量化交易平台上配置的账户信息、策略参数中的加密字符串等敏感数据均在浏览器端加密。这些在FMZ量化交易平台上储存的信息均为加密信息(非明文数据)。只有用户的私有设备可以解密使用,从而极大提高了敏感数据的安全性。

回测系统

什么是回测系统,有什么用?

当您完成了一个量化交易策略的设计工作后,怎么才能知道您这个策略的逻辑、策略收益方向等基本情况?当然我们不能直接拿真金白银去交易市场上跑策略,我们可以用历史数据来测试您的策略。看看您的策略在历史数据中盈利如何。

回测系统的数据准确么,回测结果的准确度如何?

FMZ量化交易平台将回测模式分为实盘级回测模拟级回测。实盘级回测完全按照完整的历史数据回测;模拟级回测则根据真实K线数据生成tick数据来进行回测。两者都是根据真实历史数据回测的,但实盘级回测的数据更精准,结果更加可信。FMZ回测机制说明。但是回测仅仅是策略在历史数据下的表现,历史数据并不能完全代表将来的行情。历史行情可能重演,也可能飞出黑天鹅。所以对待回测结果要理性、客观看待。

不同语言策略回测时应注意的问题:

JavaScriptC++ 策略回测是在浏览器端进行,实盘或者WexApp仿真交易所实盘(即FMZ量化交易平台的WexApp仿真交易所)运行不用安装任何其它软件、库或模块。 Python回测是在托管者上进行,可以在FMZ量化的公共服务器上回测,也可以在用户自己的托管者上回测。实盘和回测都依赖托管者所在系统上安装的Python,如果需要使用一些库,需要自行安装(公共服务器上只支持常用的库)。

回测系统中的数据

FMZ量化交易平台回测分模拟级回测和实盘级回测两种,模拟级回测根据底层K线周期生成模拟的tick,每个底层K线周期上将生成14个回测时间点,而实盘级则是真实收集的tick,大约几秒就有一次,数据量很大,回测速度慢,因此不能回测特别长的时间。FMZ的回测机制可以使策略在一根K线上交易多次,避免了只能收盘价成交的情况,更加精准又兼顾了回测速度。具体的说明可参考,链接

回测系统中支持的交易所

  • 加密货币(数字货币)

    名称 类型 说明
    Bitfinex 现货交易所对象 支持有限的交易对例如:BTC_USD,ETH_USD,LTC_USD等,注意交易对计价币为USD是美元计价
    币安 现货交易所对象 支持有限的交易对例如:BTC_USDT,ETH_USDT,ETH_BTC,LTC_BTC
    OKEX 现货交易所对象 支持有限的交易对例如:BTC_USDT,ETH_USDT,ETH_BTC,LTC_BTC
    火币 现货交易所对象 支持有限的交易对例如:BTC_USDT,ETH_USDT,ETH_BTC,LTC_BTC
    OKEX期货 期货交易所对象 支持有限的交易对例如:BTC_USD,ETH_USD等,交易对计价币为USD,设置具体合约代码(参看exchange.SetContractType函数)后,合约为币本位合约。支持的合约代码有:this_weeknext_weekquarterswap
    HuobiDM 期货交易所对象 HuobiDM即为火币期货(火币合约),支持有限的交易对例如:BTC_USD,ETH_USD等,交易对计价币为USD,设置具体合约代码(参看exchange.SetContractType函数)后,合约为币本位合约。支持的合约代码有:this_weeknext_weekquarterswap
    BitMEX 期货交易所对象 交易对为:XBT_USD,设置具体合约代码(参看exchange.SetContractType函数)后,合约为币本位合约。支持的合约代码有:XBTUSD
    币安期货 期货交易所对象 支持有限的交易对例如:BTC_USDT,ETH_USDT等,交易对计价币为USDT,设置具体合约代码(参看exchange.SetContractType函数)后,合约为USDT本位合约。支持的合约代码有:swap
    Deribit期权 期权交易所对象 交易对为:BTC_USD,ETH_USD,设置具体合约代码(参看exchange.SetContractType函数)后,合约为币本位合约。需要设置具体期权合约代码。

    回测系统期货交易所对象暂时不支持在策略代码中切换交易对。

模拟级别

模拟级别回测是根据回测系统的底层K线数据,按照一定算法在给定的底层K线Bar的最高价、最低价、开盘价、收盘价的数值构成的框架内模拟出tick数据,作为实时tick数据在请求接口时返回。具体可以参考:发明者量化模拟级别回测机制说明

实盘级别

实盘级别回测是真实的tick级别数据在Bar的时间序列中。对于基于tick级别数据的策略来说,使用实盘级别回测更贴近真实。实盘级别回测tick是真实记录的数据,并非模拟生成。支持深度数据、市场成交记录数据回放,支持自定义深度,支持分笔数据。实盘级别回测数据最大支持50MB,在数据上限内不限制回测时间范围,如果需要尽可能增大回测时间范围,可以降低深度档位数值设置,不使用分笔数据以增加回测时间范围。调用GetDepthGetTrades函数获取回放行情数据。在时间轴上某个行情数据时刻,调用 GetTickerGetTradesGetDepthGetRecords,不会多次推动时间在回测时间轴上移动(不会触发跳到下一个行情数据时刻)。对于以上某个函数重复调用,将推动回测时间在回测时间轴上移动(跳到下一个行情数据时刻)。回测时使用实盘级别回测不宜选择过早时间,可能过早时间段没有实盘级别数据。

实盘级别回测目前支持

  • 币安
  • OKEX(OKEX现货)
  • HuobiDM(火币期货)

回测系统参数调优

FMZ量化交易平台回测系统参数调优功能是在回测时根据各个参数的调优选项设置调优,如下:

  • 最小值:限定参数的起始值。
  • 最大值:限定参数递增变动后的最大值。
  • 步长:参数递增变动量。

生成参数组合,遍历这些参数组合进行回测(即每种参数组合都回测一遍)。策略参数只有类型为**数字型(number)**的参数才可以在回测系统中进行参数调优。

例如,在回测页面设置参数调优选项:

img

参数调优模式回测:

img

保存回测设置

在策略编辑页面,「模拟回测」分页中(即:回测系统)可以设置回测配置、策略参数等选项进行策略回测。回测配置是用来设置回测时间范围、回测的交易所、回测时滑点、手续费等条件;策略参数则是设置策略的参数选项。当设置好这些参数配置时便可按照设定回测策略,那么如何保存这些设置好的配置信息呢?方便下一次回测时使用(页面刷新回测时设置的选项会重置)。可以使用策略编辑页面的「保存回测设置到源文件」按钮将所有回测配置信息(包含回测设置、策略参数设置)以代码形式记录在策略源码中。再次打开策略编辑页面切换到回测系统时策略代码中记录的回测配置信息会自动配置在回测页面。

img

JavaScript策略为例,点击「保存回测设置到源文件」:

img

JavaScript/Python/C++/麦语言保存回测设置到源文件格式略有差别:

/*backtest
start: 2021-06-26 00:00:00
end: 2021-09-23 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/
'''backtest
start: 2021-06-26 00:00:00
end: 2021-09-23 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
'''
/*backtest
start: 2021-06-26 00:00:00
end: 2021-09-23 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Binance","currency":"BTC_USDT"}]
*/

麦语言:

(*backtest
start: 2021-06-26 00:00:00
end: 2021-09-23 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
*)

自定义数据源

系统用GET方法请求自定义的URL(可公开可访问的网址)来获取外部数据源进行回测,附加的请求参数如下:

参数 意义 说明
symbol 品种名 如: BTC_USD_OKCoin_EN
eid 交易所 如: OKCoin_EN
round 价格精度 如3 那么返回的数据里的价格都要乘于1000取整
vround 数量精度 如2 那么返回的数据里的数量都要乘于100取整
period bar周期(毫秒) 比如60000为请求分钟bar
depth 深度档数 1-20
trades 是否需要分笔数据 true/false
from 开始时间 unix时间戳
to 结束时间 unix时间戳

注意:

round与vround是为了避免网络传输过程中浮点数的精度丢失设计的两个参数,价格数据和成交量、订单量数据都采用整型传输。

一个拼接后的数据的例子:

http://customserver:80/data?symbol=BTC_USD_OKCoin_EN&eid=OKCoin_EN&round=3&vround=3&period=900000&from=1564315200&to=1567267200

返回的格式必须为以下两种格式(可返回任意两种格式,系统自动识别):

普通的Bar级别回测

{
    "schema":["time","open","high","low","close","vol"],
    "data":[[1564315200000,9531300,9531300,9497060,9497060,787],[1564316100000,9495160,9495160,9474260,9489460,338]]
}

Tick级回测的数据(包含盘口深度信息, 深度格式为[价格,量]的数组, 可有多级深度, asks为价格升序, bids为价格倒序)

{
    "schema":["time","asks", "bids","trades","close","vol"],
    "data":[[1564315200000,[[9531300,10]], [[9531300,10]],[[1564315200000,0,9531300,10]],9497060,787]]
}

说明

字段 说明
schema 指定data数组里列的属性,区分大小写, 仅限于 time, open, high, low, close, vol, asks, bids
data 一个按schema指一列保存数据的数组

数据格式

字段 说明
asks/bids [[价格,数量],…]
trades [[时间,方向(0:买,1:卖),价格,数量],…]

提供资金费率数据: 例如币安期货回测时,还需要额外的资金费率数据,需要自定义数据源提供。例如币安期货回测时请求的资金费率数据结构如下。

{
	"detail": {},
	"symbol": "futures_binance.eth_usdt.funding",
	"schema": ["time", "open", "high", "low", "close", "vol"],
	"data": [
		[1582876800000, 25289, 25289, 25289, 25289, 0],
		[1582905600000, 30522, 30522, 30522, 30522, 0],
		[1582934400000, 40998, 40998, 40998, 40998, 0],
        ...
		[1626652800000, 198, 198, 198, 198, 0],
		[1626681600000, 691, 691, 691, 691, 0],                  // 相邻的周期间隔8小时
		[1626710400000, 310, 310, 310, 310, 0],                  // 币安资金费率8小时更新一次,资金费率数据为什么为310?
		[1626739200000, 310, 310, 310, 310, 0],                  // 因为和K线数据一样,为了避免网络传输过程中浮点数的精度丢,数据采用整型,所以需要根据round参数处理数据,处理后用于返回给回测系统的数据就为310
		[1626768000000, -41610, -41610, -41610, -41610, 0],      // 资金费率数据也可能为负值
		[1626796800000, -5125, -5125, -5125, -5125, 0],
        ...		
		[1627977600000, 10000, 10000, 10000, 10000, 0]
	]
}

回测系统发出的数据请求举例为:

http://customserver:80/data?symbol=futures_binance.eth_usdt.funding&eid=Futures_Binance&round=8&vround=5&depth=20&trades=1&custom=0&period=3600000&from=1360771200&to=1628006400

自定义数据源范例:

指定数据源,网址:http://xxx.xx.x.xx:9090/data 自定义数据服务端,使用golang编写:

package main 
import (
    "fmt"
    "net/http"
    "encoding/json"
)

func Handle (w http.ResponseWriter, r *http.Request) {
    // e.g. set on backtest DataSourse: http://xxx.xx.x.xx:9090/data
    // r.URL: /data?depth=20&detail=true&eid=Binance&from=1566820800&period=900000&round=3&symbol=BTC_USDT_Binance&to=1569686400&trades=1&vround=5
    // response
    defer func() {
        // response data
        /* e.g. data
        {
            "schema":["time","open","high","low","close","vol"],
            "data":[
                [1564315200000,9531300,9531300,9497060,9497060,787],
                [1564316100000,9495160,9495160,9474260,9489460,338]
            ]
        }
        */
        ret := map[string]interface{}{
            "schema" : []string{"time","open","high","low","close","vol"},
            "data" : []interface{}{
                []int64{1564315200000,9531300,9531300,9497060,9497060,787},
                []int64{1564316100000,9495160,9495160,9474260,9489460,338},
            },
        }
        b, _ := json.Marshal(ret)
        w.Write(b)
    }()
}

func main () {
    fmt.Println("listen http://localhost:9090")
    http.HandleFunc("/data", Handle)
    http.ListenAndServe(":9090", nil)
}

测试策略,JavaScript范例:

/*backtest
start: 2019-07-28 00:00:00
end: 2019-07-29 00:00:00
period: 1m
exchanges: [{"eid":"OKEX","currency":"BTC_USDT","feeder":"http://120.24.2.20:9090/data"}]
*/

function main() {
    var ticker = exchange.GetTicker()
    var records = exchange.GetRecords()
    Log(ticker)
    Log(records)
}

回测系统中自定义的数据画出的图表:

策略打印信息:

本地回测引擎

FMZ量化交易平台开源了JavaScript语言和Python语言的本地回测引擎,支持回测时设置底层K线周期

回测页面快捷键

  • 策略编辑页面和策略回测页面切换的快捷键

    使用Ctrl + ,键,切换回测页面和策略编辑页面,按住Ctrl键后,单按,键。

  • 策略保存的快捷键

    使用Ctrl + s键,保存策略。

  • 启动回测的快捷键

    使用Ctrl + b键,启动回测。

代码说明

入口函数

函数名 说明
main() 为入口函数。
onexit() 为正常退出时的扫尾函数,最长执行时间为5分钟,可以不声明,如果超时会报错interrupt错误。
onerror() 为异常退出触发执行的函数,最长执行时间为5分钟,可以不声明,Python语言、C++语言编写的策略不支持该函数。
init() 为初始化函数,策略程序会在开始运行时首先自动调用,可不声明。
  • 说明:
    • 回测系统不支持onerror()函数。
    • 在实盘时先触发了onerror()函数,就不会再触发onexit()函数。

onexit()

onexit(),处理扫尾工作,最长执行5分钟,由用户实现。

function main(){
    Log("开始运行, 5秒后停止,并执行扫尾函数!")
    Sleep(1000 * 5)
}

// 扫尾函数实现
function onexit(){
    var beginTime = new Date().getTime()
    while(true){
        var nowTime = new Date().getTime()
        Log("程序停止倒计时..扫尾开始,已经过去:", (nowTime - beginTime) / 1000, "秒!")
        Sleep(1000)
    }
}
import time 
def main():
    Log("开始运行, 5秒后停止,并执行扫尾函数!")
    Sleep(1000 * 5)

def onexit():
    beginTime = time.time() * 1000
    while True:
        ts = time.time() * 1000
        Log("程序停止倒计时..扫尾开始,已经过去:", (ts - beginTime) / 1000, "秒!")
        Sleep(1000)
void main() {
    Log("开始运行, 5秒后停止,并执行扫尾函数!");
    Sleep(1000 * 5);
}

void onexit() {
    auto beginTime = Unix() * 1000;
    while(true) {
        auto ts = Unix() * 1000;
        Log("程序停止倒计时..扫尾开始,已经过去:", (ts - beginTime) / 1000, "秒!");
        Sleep(1000);
    }
}

init()

init(),用户实现初始化函数init(),策略开始运行时首先自动执行init()函数,完成策略中设计的初始化任务。

function main(){
    Log("程序第一行代码执行!", "#FF0000")
    Log("退出!")
}

// 初始化函数
function init(){     
    Log("初始化!")
}
def main():
    Log("程序第一行代码执行!", "#FF0000")
    Log("退出!")

def init():
    Log("初始化!")
void main() {
    Log("程序第一行代码执行!", "#FF0000");
    Log("退出!");
}

void init() {
    Log("初始化!");
}

onerror()

onerror(),发生异常时会触发onerror()函数执行,该函数不支持PythonC++语言的策略。

function main() {
    var arr = []
    Log(arr[6].Close)
}

function onerror() {
    Log("错误")
}
# python不支持
// C++不支持

经典策略框架

JavaScriptPythonC++语言编写的策略中需要在策略主循环中调用Sleep()函数。回测时用于控制回溯的速度,实盘时用于控制策略轮询的时间间隔,从而控制访问交易所API接口的请求频率。

  • 数字货币策略基本框架范例:

    function onTick(){
        //在这里写策略逻辑,将会不断调用,例如打印行情信息
        Log(exchange.GetTicker())
    }
    
    function main(){
        while(true){
            onTick()
            // Sleep函数主要用于数字货币策略的轮询频率控制,防止访问交易所API接口过于频繁
            Sleep(60000)
        }
    }
    
    def onTick():
        Log(exchange.GetTicker())
    
    def main():
        while True:
            onTick()
            Sleep(60000)
    
    void onTick() {
        Log(exchange.GetTicker());
    }
    
    void main() {
        while(true) {
            onTick();
            Sleep(60000);
        }
    }
    

    举个最简单的例子,如果我想每隔1秒种就在交易所挂一个价格为100,数量为1的买单可以这样写:

    function onTick(){
        // 这个仅仅是例子,回测或者实盘会很快把资金全部用于下单,实盘请勿使用
        exchange.Buy(100, 1)
    }
    
    function main(){
        while(true){
            onTick()
            // 暂停多久可自定义,单位为毫秒,1秒等于1000毫秒
            Sleep(1000)
        }
    }
    
    def onTick():
        exchange.Buy(100, 1)
    
    def main():
        while True:
            onTick()
            Sleep(1000)
    
    void onTick() {
        exchange.Buy(100, 1);
    }
    
    void main() {
        while(true) {
            onTick();
            Sleep(1000);
        }
    }
    

模板类库

模板类库是FMZ量化交易平台中可复用的代码模块,是策略代码的一种类别。创建策略时如果类别设置为模板类库,则创建一个模板类库在发明者量化交易平台当前登录的账号策略库中,创建后无法再修改类别为普通策略。

JavaScript语言模板类库:

img

Python语言模板类库:

img

C++语言模板类库:

img

  • 模板类库的导出函数 导出函数为模板类库的接口函数,可以被引用该模板类库的策略调用。导出函数在模板类库中声明以及实现的例子代码如下:

    /*
    -- 策略引用该模板以后直接用 $.Test() 调用此方法
    -- main 函数在策略中不会触发, 只做为模板调试的入口
    */
    $.Test = function() {
        Log('Test')
    }
    
    function main() {
        $.Test()
    }
    
    def Test():
        Log("template call")
    
    # 导出Test函数, 主策略可以通过ext.Test()调用
    ext.Test = Test 
    
    // 策略引用该模板以后直接用 ext::Test() 调用此方法
    void Test() {
        Log("template call");
    }
    
  • 模板类库的参数 模板类库也可以设置自己的界面参数,模板类库的参数在模板类库代码中是以全局变量的形式使用的。

    模板类库设置参数:

    img

    模板类库代码:

    $.SetParam1 = function(p1) {
        param1 = p1
    }
    
    $.GetParam1 = function() {
        Log("param1:", param1)
        return param1
    }
    
    def SetParam1(p1):
        global param1
        param1 = p1
    
    def GetParam1():
        Log("param1:", param1)
        return param1
    
    ext.SetParam1 = SetParam1
    ext.GetParam1 = GetParam1
    
    void SetParam1(float p1) {
        param1 = p1;
    }
    
    float GetParam1() {
        Log("param1:", param1);
        return param1;
    }
    

    引用以上模板类库例子的策略代码:

    function main () {
        Log("调用$.GetParam1:", $.GetParam1())
        Log("调用$.SetParam1:", "#FF0000")
        $.SetParam1(20)
        Log("调用$.GetParam1:", $.GetParam1())
    }
    
    def main():
        Log("调用ext.GetParam1:", ext.GetParam1())
        Log("调用ext.SetParam1:", "#FF0000")
        ext.SetParam1(20)
        Log("调用ext.GetParam1:", ext.GetParam1())
    
    void main() {
        Log("调用ext::GetParam1:", ext::GetParam1());
        Log("调用ext::SetParam1:", "#FF0000");
        ext::SetParam1(20);
        Log("调用ext::GetParam1:", ext::GetParam1());
    }
    

    img

  • 引用模板类库

    在策略编辑页面模板栏中勾选引用后,保存策略即可。

    img

内置结构

全局变量

exchange

exchange可视为一个交易所对象,默认为策略参数中添加的第一个交易所对象。所有与交易所的交互都通过这个对象里面的函数实现。

  • 回测添加交易所对象

  • 实盘页面添加交易所对象

添加的交易所对象就对应代码中的exchange对象:

function main() {
    Log("实盘页面或者回测页面上,添加的第一个交易所对象名字:", exchange.GetName(), ",标签:", exchange.GetLabel())
}
def main():
    Log("实盘页面或者回测页面上,添加的第一个交易所对象名字:", exchange.GetName(), ",标签:", exchange.GetLabel())
void main() {
    Log("实盘页面或者回测页面上,添加的第一个交易所对象名字:", exchange.GetName(), ",标签:", exchange.GetLabel());
}
exchanges

可以理解为储存如同exchange交易所对象的所有交易所对象的数组,可能包含多个交易所对象,exchanges[0]即是exchange

添加的交易所对象对应策略代码中的exchanges[0]exchanges[1]exchanges[2]、… ,以此类推。

function main() {
    for(var i = 0; i < exchanges.length; i++) {
        Log("添加的交易所对象索引(第一个为0以此类推):", i, "名称:", exchanges[i].GetName(), "标签:", exchanges[i].GetLabel())
    }
}
def main():
    for i in range(len(exchanges)):
        Log("添加的交易所对象索引(第一个为0以此类推):", i, "名称:", exchanges[i].GetName(), "标签:", exchanges[i].GetLabel())
void main() {
    for(int i = 0; i < exchanges.size(); i++) {
        Log("添加的交易所对象索引(第一个为0以此类推):", i, "名称:", exchanges[i].GetName(), "标签:", exchanges[i].GetLabel());
    }
}
订单状态

Order结构中的Status属性。

常量名 定义
ORDER_STATE_PENDING 未完成 0
ORDER_STATE_CLOSED 已经完成 1
ORDER_STATE_CANCELED 已经取消 2
ORDER_STATE_UNKNOWN 未知状态(其它状态) 3

ORDER_STATE_UNKNOWN状态,可以调用exchange.GetRawJSON()获取原始订单状态信息,查询交易所文档,查看具体描述。 表格中的常量名可以直接在策略代码中用于和Order结构的Status属性比较、判断是否相等从而确定订单状态。打印这些常量名会显示这些常量名对应的,以下其它常量名同理不再赘述。

订单买卖类型

Order结构中的Type属性。

常量名 定义
ORDER_TYPE_BUY 买单 0
ORDER_TYPE_SELL 卖单 1
仓位类型

Position结构中的Type属性。

常量名 定义 说明 适用
PD_LONG 表示多头仓位 数字货币期货用exchange.SetDirection("closebuy")设置平仓方向,平掉该类型的持仓 数字货币期货 0
PD_SHORT 表示空头仓位 数字货币期货用exchange.SetDirection("closesell")设置平仓方向,平掉该类型的持仓 数字货币期货 1
期货开平仓方向

Order结构中的Offset属性。

常量名 定义
ORDER_OFFSET_OPEN 开仓的订单 0
ORDER_OFFSET_CLOSE 平仓的订单 1
策略参数

在策略代码中策略界面上设置的策略参数,是以全局变量形式体现的。JavaScript语言中可以直接访问策略界面上设置的参数数值或者修改,Python策略的函数中修改全局变量时需要使用global关键字。

参数种类:

img

变量 描述 备注 类型 默认值 说明
number 数值类型 备注 数字型(number) 1 C++策略为浮点型。
string 字符串 备注 字符串(string) Hello FMZ 默认值输入时不需要加引号,输入均作为字符串处理。
combox 下拉框 备注 下拉框(selected) 1|2|3 combox变量本身是数值,代表下拉框控件选择的栏目的索引,第一个下拉框栏目内容是1,其索引值是0,依次类推。
bool 勾选项 备注 布尔型(true/false) true 勾选上,变量bool为true,不勾选,变量bool为false。
secretString 加密字符串 备注 加密串(string) passWord 使用和字符串相同,加密字符串会被加密发送,不会明文传输。
  • 界面参数,在策略编辑页面代码编辑区下方策略参数区设置。
  • 界面参数在策略代码中是以全局变量形式存在的,也就是说可以在代码中修改界面参数。
  • 界面参数在策略代码中的变量名:即上图中的numberstringcomboxboolsecretString
  • 描述选项:界面参数在策略界面上的名字。
  • 备注选项:界面参数的详细描述,该描述会在鼠标停留在界面参数上时相应的显示出。
  • 类型选项:该界面参数的类型。
  • 默认值选项:该界面参数的默认值。

参数依赖设置: 可以设置一个参数,让另一个参数基于该参数的选择实现显示与隐藏。比如我们设置参数numberA,是一个数值类型。我们让numberA基于一个参数:isShowA(布尔类型)的真假决定numberA显示与隐藏。需要把numberA变量在界面参数上设置为:numberA@isShowA

img

这样不勾选isShowA参数,numberA参数就隐藏了。对于下拉框控件类型的参数,参数依赖部分为判断是否等于下拉框某个选项的索引值。同样以isShowA参数为例,在参数设置变量时写为:numberA@combox==2numberA参数就基于combox参数是否选择为第三个选项进行显示或隐藏(索引0对应第一个选项,索引1对应第二个选项,索引2对应第三个选项)。

策略界面参数、交互控件、模板上的参数分组功能: 只用在开始分组的参数的描述开头加上(?第一组)即可,例如下图。

img

在策略使用时会分组显示参数:

img

参数默认值保存: 策略参数如图,在回测时如果希望将策略参数默认值保存,可以在策略参数修改后点击保存回测设置按钮。

img

img

即可将设置后的策略参数以代码形式保存在策略中:

/*backtest
start: 2020-02-29 00:00:00
end: 2020-03-29 00:00:00
period: 1d
args: [["number",10],["string","Hello 发明者"],["combox",1],["bool",false]]
*/
'''backtest
start: 2020-02-29 00:00:00
end: 2020-03-29 00:00:00
period: 1d
args: [["number",10],["string","Hello 发明者"],["combox",1],["bool",false]]
'''
/*backtest
start: 2020-02-29 00:00:00
end: 2020-03-29 00:00:00
period: 1d
args: [["number",10],["string","Hello 发明者"],["combox",1],["bool",false]]
*/

数据结构

部分函数会附带在调用时请求返回的原始JSON数据,该原始JSON数据储存在返回对象的Info属性中。回测时由于并不是访问某个交易所的接口,所以回测时返回的数据中无Info属性,以下是各个数据结构的主要属性描述。

Trade

获取所有交易历史(非自己),由exchange.GetTrades()函数返回。

{
    Id      : 9585306,          // 交易记录ID,如果交易所接口没有提供订单ID则使用时间戳填充
    Time    : 1567736576000,    // 时间(Unix timestamp 毫秒)
    Price   : 1000,             // 价格
    Amount  : 1,                // 数量
    Type    : 0                 // 订单类型,参考常量里的订单类型,0即为ORDER_TYPE_BUY,ORDER_TYPE_BUY的值为0
}
Ticker

市场行情由exchange.GetTicker()函数返回。

{
    Info    : {...},             // 请求交易所接口后,交易所接口应答的原始数据,回测时无此属性
    High    : 1000,              // 最高价,如果交易所接口没有提供24小时最高价则使用卖一价格填充
    Low     : 500,               // 最低价,如果交易所接口没有提供24小时最低价则使用买一价格填充
    Sell    : 900,               // 卖一价
    Buy     : 899,               // 买一价
    Last    : 900,               // 最后成交价
    Volume  : 10000000,          // 最近成交量,原则上现货成交量单位为交易币种(baseCurrency),期货成交量单位为合约张数。如果交易所接口没有提供此类数据则使用交易所接口现有的数据填充,例如可能为计价币(quoteCurrency)为单位的成交量
    Time    : 1567736576000      // 毫秒级别时间戳
}
Record

标准的OHLC结构,用来画K线和指标计算分析。由exchange.GetRecords()函数返回此结构的数组。每一个Record结构代表一个K线柱,即一根K线BARRecord其中的Time为这根K线柱周期的起始时间。

{
    Time    : 1567736576000,     // 一个时间戳,精确到毫秒,与Javascript的new Date().getTime()得到的结果格式一样
    Open    : 1000,              // 开盘价
    High    : 1500,              // 最高价
    Low     : 900,               // 最低价
    Close   : 1200,              // 收盘价
    Volume  : 1000000            // 交易量,原则上现货成交量单位为交易币(baseCurrency),期货成交量单位为合约张数,如果交易所接口没有提供此类数据则使用交易所接口现有的数据填充,例如可能为计价币(quoteCurrency)为单位的成交量
}
Order

订单结构,可由exchange.GetOrder()exchange.GetOrders()函数返回。exchange.GetOrders()返回的是该结构的数组或者空数组(如果没有当前未完成的订单,返回[],即空数组)。

{
    Info        : {...},         // 请求交易所接口后,交易所接口应答的原始数据,回测时无此属性
    Id          : 123456,        // 交易单唯一标识
    Price       : 1000,          // 下单价格,注意市价单的该属性可能为0或者-1
    Amount      : 10,            // 下单数量,注意市价单的该属性可能为金额并非币数
    DealAmount  : 10,            // 成交数量,如果交易所接口不提供该数据则可能使用0填充
    AvgPrice    : 1000,          // 成交均价,注意有些交易所不提供该数据。不提供、也无法计算得出的情况该属性设置为0
    Status      : 1,             // 订单状态,参考常量里的订单状态,例如:ORDER_STATE_CLOSED
    Type        : 0,             // 订单类型,参考常量里的订单类型,例如:ORDER_TYPE_BUY
    Offset      : 0              // 数字货币期货的订单数据中订单的开平仓方向。ORDER_OFFSET_OPEN为开仓方向,ORDER_OFFSET_CLOSE为平仓方向
    ContractType : ""            // 现货订单中该属性为""即空字符串,期货订单该属性为具体的合约代码
}
MarketOrder

市场深度单,即exchange.GetDepth()函数返回数据结构中BidsAsks数组中的元素的数据结构。

{
    Price   : 1000,              // 价格
    Amount  : 1                  // 数量
}
Depth

市场深度,由exchange.GetDepth()函数返回。

{
    Asks    : [...],             // 卖单数组,MarketOrder数组,按价格从低向高排序
    Bids    : [...],             // 买单数组,MarketOrder数组,按价格从高向低排序
    Time    : 1567736576000      // 毫秒级别时间戳
}
Account

账户信息,由exchange.GetAccount()函数返回。返回的结构中的数据和当前设置的交易对、设置的合约代码有关。

{
    Info            : {...},     // 请求交易所接口后,交易所接口应答的原始数据,回测时无此属性
    Balance         : 1000,      // 可用计价币数量,现货中如果交易对是BTC_USDT,Balance指的是当前可用USDT数量。U本位期货合约中Balance指的是可用保证金USDT的数量
    FrozenBalance   : 0,         // Balance表示的资产用于挂单的冻结数量
    Stocks          : 1,         // 可用交易币数量,现货中如果交易对是BTC_USDT,Stocks指的是当前可用BTC数量。币本位期货合约中Stocks指的是可用保证金的币(baseCurrency)的数量
    FrozenStocks    : 0          // Stocks表示的资产用于挂单的冻结数量
}
Position

期货交易中持有的仓位信息,由exchange.GetPosition()函数返回此Position结构的数组

{
    Info            : {...},     // 请求交易所接口后,交易所接口应答的原始数据,回测时无此属性
    MarginLevel     : 10,        // 持仓杆杠大小,如果交易所接口没有提供该数据则通过计算填充,可能会有误差
    Amount          : 100,       // 持仓量,持仓合约张数,通常是正整数。注意每个交易所的合约乘数、价值等合约规格可能不一样,下单规则也可能不一样,例如币安合约可以0.1张下单
    FrozenAmount    : 0,         // 仓位冻结量,用于平仓挂单时的临时冻结仓位数量
    Price           : 10000,     // 持仓均价,原则上该属性为仓位总体的平均价格(不参与结算),如果交易所接口没有提供该数据则用交易所接口现有的持仓均价填充(参与结算)
    Profit          : 0,         // 持仓浮动盈亏,原则上为持仓的未实现盈亏,如果交易所接口没有提供该数据则用交易所接口其它盈亏数据填充,盈亏数值的单位和当前合约保证金的单位相同
    Type            : 0,         // PD_LONG为多头仓位,PD_SHORT为空头仓位
    ContractType    : "quarter", // 合约代码,具体可以参看SetContractType函数描述中传入的参数
    Margin          : 1          // 仓位占用的保证金,如果交易所接口没有提供该数据则使用0填充
}

对于数字货币期货需要注意,exchange.GetPosition()函数返回的Position结构数组。对于其中持仓数据结构中的FrozenAmountProfitMargin属性,由于交易所提供数据并不统一,不同交易所对象调用exchange.GetPosition()接口时返回的数据的定义可能不同。例如,有些交易所持仓数据中无仓位冻结数据,此时FrozenAmount为0。如果需要计算某些数据可以使用Info属性中的原始数据计算分析。

全局函数

Version()

Version(),返回系统当前版本号。返回值:字符串类型。

Sleep(Millisecond)

Sleep(Millisecond),休眠函数,使程序暂停一段时间。参数值:Millisecond为数值类型。参数为毫秒数,例如:Sleep(1000)为休眠1秒。

注意: 在使用Python语言编写策略时,对于轮询间隔、时间等待的操作应当使用Sleep(Millisecond)函数。不建议使用Pythontime库的time.sleep(second)函数。因为策略中使用time.sleep(second)函数在回测时会让策略程序实际等待一定秒数(second参数为设置暂停的秒数),导致策略回测非常慢。

IsVirtual()

IsVirtual(),判断当前策略运行是否为模拟回测。返回值:布尔类型。 模拟回测状态返回true,实盘返回false

Mail(…)

Mail(smtpServer, smtpUsername, smtpPassword, mailTo, title, body),发送邮件函数。参数值:参数全部为字符串类型。返回值:布尔类型,发送成功返回truesmtpServer为发送邮箱smtp服务,smtpUsername为邮箱账号,smtpPassword为邮箱的SMTP密码(不是邮箱登录密码),mailTo为接受邮件的邮箱账号,title为发送的邮件标题,body为发送的邮件内容,例如:

function main(){
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
}
def main():
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
void main() {
    Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body");
}
  • Mail函数的异步版本Mail_Go函数: 使用方式和exchange.Go函数类似。

    function main() {
        var r1 = Mail_Go("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
        var r2 = Mail_Go("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
        
        var ret1 = r1.wait()
        var ret2 = r2.wait()
        
        Log("ret1:", ret1)
        Log("ret2:", ret2)
    }
    
    # 不支持
    
    // 不支持
    

注意: 阿里云服务器可能会封一些端口,导致邮件无法发出。如需更改端口,可以直接在第一个参数中加入端口号,例如:QQ邮箱的smtp.qq.com:587,该端口测试可用。 如果出现报错:unencryped connection,需要修改Mail函数的smtpServer参数的格式为:ssl://xxx.com:xxx,举例QQ邮箱的SMTP的ssl方式:ssl://smtp.qq.com:465或者smtp://xxx.com:xxx

SetErrorFilter(…)

SetErrorFilter(RegEx),过滤错误日志。参数值:字符串类型。 被此正则表达式匹配的错误日志将不上传到日志系统,可多次调用设置多个过滤条件(被过滤的日志不写入托管者目录下对应实盘ID的数据库文件,防止频繁报错导致数据库文件膨胀)。

function main() {
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
}
def main():
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
void main() {
    SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused");
}

过滤某个接口错误信息:

function main() {
    // 随便查询一个不存在的订单,id为123,故意让接口报错
    var order = exchange.GetOrder("123")
    Log(order)
    // 过滤http502错误、GetOrder接口错误,设置错误过滤之后,第二次调用GetOrder不再报错
    SetErrorFilter("502:|GetOrder")
    order = exchange.GetOrder("123")
    Log(order)
}
def main():
    order = exchange.GetOrder("123")
    Log(order)
    SetErrorFilter("502:|GetOrder")
    order = exchange.GetOrder("123")
    Log(order)
void main() {
    TId orderId;
    Order order = exchange.GetOrder(orderId);
    Log(order);
    SetErrorFilter("502:|GetOrder");
    order = exchange.GetOrder(orderId);
    Log(order);
}

GetPid()

GetPid(),返回实盘进程ID。返回值:字符串类型。

function main(){
    var id = GetPid()
    Log(id)
}
def main():
    id = GetPid()
    Log(id)
void main() {
    auto id = GetPid();
    Log(id);
}

GetLastError()

GetLastError(),获取最近一次出错信息。一般无需使用,因为程序会把出错信息自动上传到日志系统。返回值:字符串类型。调用GetLastError()函数后会清除这个错误缓存,再次调用时不会再返回上次记录的错误信息。

function main(){
    // 因为不存在编号为123的订单,所以会出错
    exchange.GetOrder("123")
    var error = GetLastError()
    Log(error)
}
def main():
    exchange.GetOrder("123")
    error = GetLastError()
    Log(error)
void main() {
    // 订单ID类型:TId,所以不能传入字符串,我们下一个不符合交易所规范的订单来触发
    exchange.GetOrder(exchange.Buy(1, 1));
    auto error = GetLastError();
    Log(error);
}

GetCommand()

GetCommand(),获取交互命令字符串(utf-8)。获取策略交互界面发来的命令并清空缓存,没有命令则返回空字符串。返回的命令格式为按钮名称:参数,如果交互控件没有参数(例如不带输入框的按钮控件)则命令就是按钮名称。

function main(){
    while(true) { 
        var cmd = GetCommand()
        if (cmd) { 
            Log(cmd)
        }
        Sleep(1000) 
    }
}
def main():
    while True:
        cmd = GetCommand()
        if cmd:
            Log(cmd)
        Sleep(1000)
void main() {
    while(true) {
        auto cmd = GetCommand();
        if(cmd != "") {
            Log(cmd);
        }
        Sleep(1000);
    }
}

底层系统有一个队列结构记录交互命令,当GetCommand()函数被调用时,会取出队列中最先进入的交互命令(如果没有交互命令时返回空字符串)。

交互控件的使用例子,策略编辑界面设置交互控件。

img

策略中设计交互代码:

function main() {
    while (true) {
        LogStatus(_D())
        var cmd = GetCommand()
        if (cmd) {
            Log("cmd:", cmd)    
            var arr = cmd.split(":")
            if (arr[0] == "buy") {
                Log("买入,该控件不带数量")
            } else if (arr[0] == "sell") {
                Log("卖出,该控件带数量:", arr[1])
            } else {
                Log("其它控件触发:", arr)
            }
        }

More