基础说明
入门
优宽量化交易平台能够做什么?
优宽(优宽)量化交易平台是量化交易领域最专业的量化社区,在这里你可以学习、编写、分享、买卖量化策略;在线回测和使用模拟盘进行模拟交易;运行、公开、围观实盘。支持商品期货CTP/易盛API/中泰证券XTP/腾讯富途证券(美股港股)。
系列教程
电子书PDF:
| 书名 | 优宽量化交易入门 | 商品期货量化交易实战 | 纸版书籍 |
|---|---|---|---|
| 封面 | ![]() | ![]() | ![]() |
| 链接 | youquantquantbook.pdf | youquantcfquant.pdf | 京东自营购买 |
视频教程:
- 平台相关的动画简介:
优宽量化交易平台--托管者概念
优宽量化交易平台--网站概览
优宽量化交易平台--托管者部署简介
优宽量化交易平台--创建实盘
优宽量化交易平台--策略注册、复制码讲解
优宽量化交易平台--完整策略、参数配置导入导出简介
优宽量化交易平台--调试工具和远程编辑功能
如果遇到问题可以随时到论坛发帖提问、讨论,在平台提出工单。问题一般都会很快解答。
可用哪些编程语言实现我的策略呢?
优宽量化交易平台支持使用JavaScript、TypeScript、Python、C++、PINE、麦语言、Blockly可视化编写设计策略。
支持TypeScript语言,在策略创建时依然设置为JavaScript策略,然后在策略代码开头写入// @ts-check或者点击策略编辑区域右上角的按钮「TypeScript」,即可切换到TypeScript。平台将自动识别代码为TypeScript,并为您提供相应的编译和类型检查支持:
- 类型安全:
TypeScript的静态类型检查功能可以帮助您在编写代码时发现潜在的错误,提高代码质量。 - 代码自动补全:
TypeScript的类型系统使得您在编写代码时可以更快地找到所需的属性和方法,提高开发效率。 - 更清晰的代码结构:使用
TypeScript,您可以更好地组织和维护您的代码,使其易于阅读和理解。 - 强大的面向对象编程特性:
TypeScript提供了诸如接口、类、泛型等强大的面向对象编程特性,帮助您编写更加健壮、可重用的策略代码。
这些策略设计语言中掌握其中一种就足够了。除了支持编写代码的方式设计策略,还可以使用可视化模块创建策略(Blockly)。可视化模块拼接搭建策略采用了更加直观的方式设计策略,无需编码。非常适合培养策略设计兴趣,以便快速入门程序化、量化交易。
设置
Python策略程序使用的Python解释器
使用Python编写的策略,回测或实盘时如果托管者所在系统环境同时安装了Python2和Python3,可以在策略开始第一行设置策略运行时启动的Python版本。例如:#!python3,#!python2,这样系统就会自动查找解释器。也可以指定绝对路径,例如:#!/usr/bin/python3。
什么是托管者?
托管者可以理解为您的交易策略的执行者,负责复杂的数据请求、数据接收、网络链接、日志回传等等工作。托管者运行在您的服务器上,即使优宽量化交易平台网站出现网络故障也不影响您的托管者运行。托管者可运行在Linux,Windows,Mac OS,android,树莓派 ARM Linux等系统上。托管者页面,Linux托管者安装步骤及托管者更新步骤。托管者管理的实盘日志均保存在托管者程序所在目录./logs/storage内,文件为db3的Sqlite数据库文件中。可以用Sqlite管理软件直接编辑,对于这些扩展名为db3的实盘数据库文件来说文件名即为实盘的ID。
支持的协议
策略安全性
在优宽量化交易平台上开发策略,策略仅优宽量化账户持有者可见。并且在优宽量化交易平台上可以实现策略代码完全本地化,例如把策略封装成一个Python包在策略代码中加载,这样就实现了策略本地化。
Python代码的安全性:
因为Python是开源且极易被反编译的语言,如果策略非自用而是出租,如果担心策略泄漏可让策略运行于自己布署的托管者上并以子账号或全托管管理这种形式出租。
Python策略代码加密:
默认情况下,Python策略代码作者自用时不加密,租出给他人使用时加密。在Python策略开头编辑如下代码,可以指定自用或者租出Python策略运行时是否加密策略代码。支持策略代码加密的Python版本为:Python 2.7版本、Python 3.5版本、Python 3.6版本。
-
策略作者自己运行、通过注册码给他人使用均加密策略代码:
#!python为指定Python解释器版本,之后使用逗号,间隔,输入加密指令encrypt。如果不指定Python版本直接添加#!encrypt。python#!python,encrypt或者
python#!encrypt -
策略作者自己运行、通过注册码给他人使用均不加密策略代码:
python#!python,not encrypted或者
python#!not encrypted
判断Python策略代码加密是否生效使用代码os.getenv('__优宽_ENV__'),返回字符串"encrypt"说明已经生效。仅实盘有效,回测不会加密Python策略代码。
python
#!encrypt
def main():
ret = os.getenv('__优宽_ENV__')
# 打印变量ret为字符串encrypt或者ret == "encrypt"为真,即代表加密生效
Log(ret, ret == "encrypt")
密钥安全性
在优宽量化交易平台上配置的账户信息、策略参数中的加密字符串等敏感数据均在浏览器端加密。这些在优宽量化交易平台上储存的信息均为加密信息(非明文数据)。只有用户的私有设备可以解密使用,从而极大提高了敏感数据的安全性。如果在策略代码、参数设置、策略描述等信息中包含了其它敏感信息,请不要公开或者出售该策略。
回测系统
什么是回测系统,有什么用?
当您完成了一个量化交易策略的设计工作后,怎么才能知道您这个策略的逻辑、策略收益方向等基本情况?当然我们不能直接拿真金白银去交易市场上跑策略,我们可以用历史数据来测试您的策略。看看您的策略在历史数据中盈利如何。
回测系统的数据准确么,回测结果的准确度如何?
优宽量化交易平台将回测模式分为实盘级回测和模拟级回测。实盘级回测完全按照完整的历史数据回测;模拟级回测则根据真实K线数据生成tick数据来进行回测。两者都是根据真实历史数据回测的,但实盘级回测的数据更精准,结果更加可信。优宽回测机制说明。但是回测仅仅是策略在历史数据下的表现,历史数据并不能完全代表将来的行情。历史行情可能重演,也可能飞出黑天鹅。所以对待回测结果要理性、客观看待。
不同语言策略回测时应注意的问题:
JavaScript和C++策略回测是在浏览器端进行。期货公司实盘账户、simnow仿真账户配置的实盘,运行不用安装任何其它软件、库或模块。
Python回测是在托管者上进行,可以在优宽量化的公共服务器上回测,也可以在用户自己的托管者上回测。实盘和回测都依赖托管者所在系统上安装的Python,如果需要使用一些库,需要自行安装(公共服务器上只支持常用的库)。
回测系统中的数据
优宽量化交易平台回测分模拟级回测和实盘级回测两种,模拟级回测根据底层K线周期生成模拟的tick,每个底层K线周期上将生成12个回测时间点,而实盘级则是真实收集的tick,大约几秒就有一次,数据量很大,回测速度慢,因此不能回测特别长的时间。优宽的回测机制可以使策略在一根K线上交易多次,避免了只能收盘价成交的情况,更加精准又兼顾了回测速度。具体的说明可参考,链接。
回测系统中支持的交易所
- 期货证券
商品期货:支持大商所、郑商所、上期所、中金所期货数据,暂时不支持商品期权数据。
股票证券:暂时仅支持有限的数据,股票目前仅支持K线的日线级别的回测数据。
模拟级别
模拟级别回测是根据回测系统的底层K线数据,按照一定算法在给定的底层K线Bar的最高价、最低价、开盘价、收盘价的数值构成的框架内模拟出tick数据,作为实时tick数据在请求接口时返回。具体可以参考:优宽量化模拟级别回测机制说明。
实盘级别
实盘级别回测是真实的tick级别数据在Bar的时间序列中。对于基于tick级别数据的策略来说,使用实盘级别回测更贴近真实。实盘级别回测tick是真实记录的数据,并非模拟生成。支持深度数据、市场成交记录数据回放,支持自定义深度,支持分笔数据。实盘级别回测数据最大支持50MB,在数据上限内不限制回测时间范围,如果需要尽可能增大回测时间范围,可以降低深度档位数值设置,不使用分笔数据以增加回测时间范围。调用GetDepth、GetTrades函数获取回放行情数据。在时间轴上某个行情数据时刻,调用GetTicker,GetTrades,GetDepth,GetRecords,不会多次推动时间在回测时间轴上移动(不会触发跳到下一个行情数据时刻)。对于以上某个函数重复调用,将推动回测时间在回测时间轴上移动(跳到下一个行情数据时刻)。回测时使用实盘级别回测不宜选择过早时间,可能过早时间段没有实盘级别数据。
实盘级别回测目前支持:
- 商品期货
回测系统参数调优
优宽量化交易平台回测系统参数调优功能是在回测时根据各个参数的调优选项设置调优,如下:
-
最小值:限定参数的起始值。
-
最大值:限定参数递增变动后的最大值。
-
步长:参数递增变动量。
生成参数组合,遍历这些参数组合进行回测(即每种参数组合都回测一遍)。策略参数只有类型为 数字型(number) 的参数才可以在回测系统中进行参数调优。
例如,在回测页面设置参数调优选项:
参数调优模式回测:
保存回测设置
在策略编辑页面,「模拟回测」分页中(即:回测系统)可以设置回测配置、策略参数等选项进行策略回测。回测配置是用来设置回测时间范围、回测的交易所、回测时滑点、手续费等条件;策略参数则是设置策略的参数选项。当设置好这些参数配置时便可按照设定回测策略,那么如何保存这些设置好的配置信息呢?方便下一次回测时使用(页面刷新回测时设置的选项会重置)。可以使用策略编辑页面的「保存回测设置到源文件」按钮将所有回测配置信息(包含回测设置、策略参数设置)以代码形式记录在策略源码中。再次打开策略编辑页面切换到回测系统时策略代码中记录的回测配置信息会自动配置在回测页面。
以JavaScript策略为例,点击「保存回测设置到源文件」:
JavaScript/Python/C++/麦语言保存回测设置到源文件格式略有差别:
javascript
/*backtest
start: 2021-09-25 09:00:00
end: 2021-10-24 15:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
python
'''backtest
start: 2021-09-25 09:00:00
end: 2021-10-24 15:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''
c++
/*backtest
start: 2021-09-25 09:00:00
end: 2021-10-24 15:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
麦语言:
javascript
(*backtest
start: 2021-09-25 09:00:00
end: 2021-10-24 15:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*)
自定义数据源
系统用GET方法请求自定义的URL(可公开可访问的网址)来获取外部数据源进行回测,附加的请求参数如下:
| 参数 | 意义 | 说明 |
|---|---|---|
| symbol | 品种名 | 如: futures.MA888 |
| eid | 交易所 | 如: Futures_CTP |
| round | 价格精度 | 如3 那么返回的数据里的价格都要乘于1000取整 |
| vround | 数量精度 | 如2 那么返回的数据里的数量都要乘于100取整 |
| period | bar周期(毫秒) | 比如60000为请求分钟bar |
| depth | 深度档数 | 1-20 |
| trades | 是否需要分笔数据 | true/false |
| from | 开始时间 | unix时间戳 |
| to | 结束时间 | unix时间戳 |
注意:
text
round与vround是为了避免网络传输过程中浮点数的精度丢失设计的两个参数,价格数据和成交量、订单量数据都采用整型传输。
一个拼接后的数据的例子:
text
http://customserver:80/data?symbol=futures.MA888&eid=Futures_CTP&round=3&vround=3&period=900000&from=1564315200&to=1567267200
返回的格式必须为以下两种格式(可返回任意两种格式,系统自动识别):
普通的Bar级别回测
javascript
{
"schema":["time","open","high","low","close","vol"],
"data":[[1564315200000,9531300,9531300,9497060,9497060,787],[1564316100000,9495160,9495160,9474260,9489460,338]]
}
Tick级回测的数据(包含盘口深度信息, 深度格式为[价格,量]的数组, 可有多级深度, asks为价格升序, bids为价格倒序)
javascript
{
"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指一列保存数据的数组 |
| detail | 商品期货的品种需要提供的属性 |
detail字段
| 字段 | 说明 | 例子 |
|---|---|---|
| PriceTick | 一跳的值 | 0.1 |
| VolumeMultiple | 一手多少个单位 | 100 |
| ExchangeID | 交易所ID | CZCE |
| LongMarginRatio | 做多保证金比率 | 0.2 |
| ShortMarginRatio | 做空保证金比率 | 0.2 |
| InstrumentID | 合约真实代码 | rb1906 |
数据格式
| 字段 | 说明 |
|---|---|
| asks/bids | [[价格,数量],...] |
| trades | [[时间,方向(0:买,1:卖),价格,数量],...] |
自定义数据源范例:
指定数据源,网址:http://xxx.xx.x.xx:9090/data
自定义数据服务端,使用golang编写:
mylang
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=Futures_CTP&from=1566820800&period=900000&round=3&symbol=futures.MA888&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范例:
javascript
/*backtest
start: 2019-07-28 00:00:00
end: 2019-07-29 00:00:00
period: 1m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES","feeder":"http://120.24.2.20:9090/data"}]
*/
function main() {
// 对于测试代码,我们通过判断exchange.IO("status")函数,连接成功之后执行测试代码,区别于商品期货策略一般架构
while(!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约为MA888
exchange.SetContractType("MA888")
// 获取ticker数据
var ticker = exchange.GetTicker()
// 获取records数据,即K线数据
var records = exchange.GetRecords()
// 打印数据
Log(ticker)
Log(records)
}

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

策略打印信息:

本地回测引擎
优宽量化交易平台开源了JavaScript语言和Python语言的本地回测引擎,支持回测时设置底层K线周期。
回测页面快捷键
-
策略编辑页面和策略回测页面切换的快捷键
使用
Ctrl + ,键,切换回测页面和策略编辑页面,按住Ctrl键后,单按,键。 -
策略保存的快捷键

使用
Ctrl + s键,保存策略。 -
启动回测的快捷键

使用
Ctrl + b键,启动回测。
代码说明
入口函数
| 函数名 | 说明 |
|---|---|
main() | 为入口函数。 |
onexit() | 为正常退出时的扫尾函数,最长执行时间为5分钟,可以不声明,如果超时会报错interrupt错误。 |
onerror() | 为异常退出触发执行的函数,最长执行时间为5分钟,可以不声明,Python语言、C++语言编写的策略不支持该函数。 |
init() | 为初始化函数,策略程序会在开始运行时首先自动调用,可不声明。 |
- 说明:
- 回测系统不支持
onerror()函数。 - 在实盘时先触发了
onerror()函数,就不会再触发onexit()函数。
- 回测系统不支持
onexit()
onexit(),处理扫尾工作,最长执行5分钟,由用户实现。
javascript
function main(){
Log("开始运行, 5秒后停止,并执行扫尾函数!")
Sleep(1000 * 5)
}
// 扫尾函数实现
function onexit(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
var beginTime = new Date().getTime()
while(true){
var nowTime = new Date().getTime()
Log("程序停止倒计时..扫尾开始,已经过去:", (nowTime - beginTime) / 1000, "秒!")
Sleep(1000)
}
}
python
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)
c++
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()函数,完成策略中设计的初始化任务。
javascript
function main(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
Log("程序第一行代码执行!", "#FF0000")
Log("退出!")
}
// 初始化函数
function init(){
Log("初始化!")
}
python
def main():
Log("程序第一行代码执行!", "#FF0000")
Log("退出!")
def init():
Log("初始化!")
c++
void main() {
Log("程序第一行代码执行!", "#FF0000");
Log("退出!");
}
void init() {
Log("初始化!");
}

onerror()
onerror(),发生异常时会触发onerror()函数执行,该函数不支持Python、C++语言的策略。
javascript
function main() {
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
var arr = []
Log(arr[6].Close)
}
function onerror() {
Log("错误")
}
python
# python不支持
c++
// C++不支持
经典策略框架
在JavaScript、Python、C++语言编写的策略中需要在策略主循环中调用Sleep()函数。回测时用于控制回溯的速度,实盘时用于控制策略轮询的时间间隔,从而控制访问交易所API接口的请求频率。
-
经典的商品期货策略架构:
javascriptfunction main(){ while(true){ // 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数 if(exchange.IO("status")){ exchange.SetContractType("MA000") var ticker = exchange.GetTicker() Log("MA000 ticker:", ticker) LogStatus(_D(), "已经连接CTP !") } else { LogStatus(_D(), "未连接CTP !") } } }pythondef main(): while True: if exchange.IO("status"): exchange.SetContractType("MA000") ticker = exchange.GetTicker() Log("MA000 ticker:", ticker) LogStatus(_D(), "已经连接CTP !") else: LogStatus(_D(), "未连接CTP !")c++void main() { while(true) { if(exchange.IO("status") == 1) { exchange.SetContractType("MA000"); auto ticker = exchange.GetTicker(); Log("MA000 ticker:", ticker); LogStatus(_D(), "已经连接CTP !"); } else { LogStatus(_D(), "未连接CTP !"); } } }
模板类库
模板类库是优宽量化交易平台中可复用的代码模块,是策略代码的一种类别。创建策略时如果类别设置为模板类库,则创建一个模板类库在优宽量化交易平台当前登录的账号策略库中,创建后无法再修改类别为普通策略。
JavaScript语言模板类库:
Python语言模板类库:
C++语言模板类库:
-
模板类库的导出函数
导出函数为模板类库的接口函数,可以被引用该模板类库的策略调用。导出函数在模板类库中声明以及实现的例子代码如下:
javascript/* -- 策略引用该模板以后直接用 $.Test() 调用此方法 -- main 函数在策略中不会触发, 只做为模板调试的入口 */ $.Test = function() { Log('Test') } function main() { $.Test() }pythondef Test(): Log("template call") # 导出Test函数, 主策略可以通过ext.Test()调用 ext.Test = Testc++// 策略引用该模板以后直接用 ext::Test() 调用此方法 void Test() { Log("template call"); } -
模板类库的参数
模板类库也可以设置自己的界面参数,模板类库的参数在模板类库代码中是以全局变量的形式使用的。模板类库设置参数:
模板类库代码:
javascript$.SetParam1 = function(p1) { param1 = p1 } $.GetParam1 = function() { Log("param1:", param1) return param1 }pythondef SetParam1(p1): global param1 param1 = p1 def GetParam1(): Log("param1:", param1) return param1 ext.SetParam1 = SetParam1 ext.GetParam1 = GetParam1c++void SetParam1(float p1) { param1 = p1; } float GetParam1() { Log("param1:", param1); return param1; }引用以上模板类库例子的策略代码:
javascriptfunction main () { Log("调用$.GetParam1:", $.GetParam1()) Log("调用$.SetParam1:", "#FF0000") $.SetParam1(20) Log("调用$.GetParam1:", $.GetParam1()) }pythondef main(): Log("调用ext.GetParam1:", ext.GetParam1()) Log("调用ext.SetParam1:", "#FF0000") ext.SetParam1(20) Log("调用ext.GetParam1:", ext.GetParam1())c++void main() { Log("调用ext::GetParam1:", ext::GetParam1()); Log("调用ext::SetParam1:", "#FF0000"); ext::SetParam1(20); Log("调用ext::GetParam1:", ext::GetParam1()); } -
引用模板类库
在策略编辑页面模板栏中勾选引用后,保存策略即可。
内置结构
全局变量
exchange
exchange可视为一个交易所对象,默认为策略参数中添加的第一个交易所对象。所有与交易所的交互都通过这个对象里面的函数实现。一个交易所对象绑定一个期货账户。
添加的交易所对象就对应代码中的exchange对象:
javascript
function main() {
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
Log("实盘页面或者回测页面上,添加的第一个交易所对象名字:", exchange.GetName(), ",标签:", exchange.GetLabel())
}
python
def main():
Log("实盘页面或者回测页面上,添加的第一个交易所对象名字:", exchange.GetName(), ",标签:", exchange.GetLabel())
c++
void main() {
Log("实盘页面或者回测页面上,添加的第一个交易所对象名字:", exchange.GetName(), ",标签:", exchange.GetLabel());
}
exchanges
可以理解为储存如同exchange交易所对象的所有交易所对象的数组,可能包含多个交易所对象,exchanges[0]即是exchange。
添加的交易所对象对应策略代码中的exchanges[0]、exchanges[1]、exchanges[2]、... ,以此类推。
javascript
function main() {
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
for(var i = 0; i < exchanges.length; i++) {
Log("添加的交易所对象索引(第一个为0以此类推):", i, "名称:", exchanges[i].GetName(), "标签:", exchanges[i].GetLabel())
}
}
python
def main():
for i in range(len(exchanges)):
Log("添加的交易所对象索引(第一个为0以此类推):", i, "名称:", exchanges[i].GetName(), "标签:", exchanges[i].GetLabel())
c++
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 | 多头仓位 | CTP用exchange.SetDirection("closebuy_today")设置平仓方向 | 商品期货 | 0 |
| PD_SHORT | 空头仓位 | CTP用exchange.SetDirection("closesell_today")设置平仓方向 | 商品期货 | 1 |
| PD_LONG_YD | 昨日多头仓位 | CTP用exchange.SetDirection("closebuy")设置平仓方向 | 商品期货 | 2 |
| PD_SHORT_YD | 昨日空头仓位 | CTP用exchange.SetDirection("closesell")设置平仓方向 | 商品期货 | 3 |
期货开平仓方向
Order结构中的Offset属性。
| 常量名 | 定义 | 值 |
|---|---|---|
| ORDER_OFFSET_OPEN | 开仓的订单 | 0 |
| ORDER_OFFSET_CLOSE | 平仓的订单 | 1 |
策略参数
在策略代码中策略界面上设置的策略参数,是以全局变量形式体现的。JavaScript语言中可以直接访问策略界面上设置的参数数值或者修改,Python策略的函数中修改全局变量时需要使用global关键字。
参数种类:
| 变量 | 描述 | 备注 | 类型 | 默认值 | 说明 |
|---|---|---|---|---|---|
| number | 数值类型 | 备注 | 数字型(number) | 1 | C++策略为浮点型。 |
| string | 字符串 | 备注 | 字符串(string) | Hello 优宽 | 默认值输入时不需要加引号,输入均作为字符串处理。 |
| combox | 下拉框 | 备注 | 下拉框(selected) | 1|2|3 | combox变量本身是数值,代表下拉框控件选择的栏目的索引,第一个下拉框栏目内容是1,其索引值是0,依次类推。 |
| bool | 勾选项 | 备注 | 布尔型(true/false) | true | 勾选上,变量bool为true,不勾选,变量bool为false。 |
| secretString | 加密字符串 | 备注 | 加密串(string) | passWord | 使用和字符串相同,加密字符串会被加密发送,不会明文传输。 |
- 界面参数,在策略编辑页面代码编辑区下方策略参数区设置。
- 界面参数在策略代码中是以全局变量形式存在的,也就是说可以在代码中修改界面参数。
- 界面参数在策略代码中的变量名:即上图中的
number、string、combox、bool、secretString。 - 描述选项:界面参数在策略界面上的名字。
- 备注选项:界面参数的详细描述,该描述会在鼠标停留在界面参数上时相应的显示出。
- 类型选项:该界面参数的类型。
- 默认值选项:该界面参数的默认值。
参数依赖设置:
可以设置一个参数,让另一个参数基于该参数的选择实现显示与隐藏。比如我们设置参数numberA,是一个数值类型。我们让numberA基于一个参数:isShowA(布尔类型)的真假决定numberA显示与隐藏。需要把numberA变量在界面参数上设置为:numberA@isShowA。
这样不勾选isShowA参数,numberA参数就隐藏了。对于下拉框控件类型的参数,参数依赖部分为判断是否等于下拉框某个选项的索引值。同样以isShowA参数为例,在参数设置变量时写为:numberA@combox==2。numberA参数就基于combox参数是否选择为第三个选项进行显示或隐藏(索引0对应第一个选项,索引1对应第二个选项,索引2对应第三个选项)。
策略界面参数、交互控件、模板上的参数分组功能:
只用在开始分组的参数的描述开头加上 (?第一组)即可,例如下图。
在策略使用时会分组显示参数:
参数默认值保存:
策略参数如图,在回测时如果希望将策略参数默认值保存,可以在策略参数修改后点击保存回测设置按钮。
即可将设置后的策略参数以代码形式保存在策略中:
javascript
/*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]]
*/
python
'''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]]
'''
c++
/*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()函数返回。商品期货、股票证券不支持该函数。
javascript
{
Id : 9585306, // 交易记录ID
Time : 1567736576000, // 时间(Unix timestamp 毫秒)
Price : 1000, // 价格
Amount : 1, // 数量
Type : 0 // 订单类型,参考常量里的订单类型,0即为ORDER_TYPE_BUY,ORDER_TYPE_BUY的值为0
}
Ticker
市场行情由exchange.GetTicker()函数返回。
javascript
{
Info : {...}, // 请求交易所接口后,交易所接口应答的原始数据,回测时无此属性
High : 1000, // 最高价
Low : 500, // 最低价
Sell : 900, // 卖一价
Buy : 899, // 买一价
Last : 900, // 最后成交价
Volume : 10000000, // 最近成交量
Time : 1567736576000 // 毫秒级别时间戳
}
Record
标准的OHLC结构,用来画K线和指标计算分析。由exchange.GetRecords()函数返回此结构的数组。每一个Record结构代表一个K线柱,即一根K线BAR。Record其中的Time为这根K线柱周期的起始时间。
javascript
{
Time : 1567736576000, // 一个时间戳,精确到毫秒,与Javascript的new Date().getTime()得到的结果格式一样
Open : 1000, // 开盘价
High : 1500, // 最高价
Low : 900, // 最低价
Close : 1200, // 收盘价
Volume : 1000000 // 交易量
}
Order
订单结构,可由exchange.GetOrder()、exchange.GetOrders()函数返回。exchange.GetOrders()返回的是该结构的数组或者空数组(如果没有当前未完成的订单,返回[],即空数组)。
javascript
{
Info : {...}, // 请求交易所接口后,交易所接口应答的原始数据,回测时无此属性
Id : 123456, // 交易单唯一标识
Price : 1000, // 下单价格
Amount : 10, // 下单数量
DealAmount : 10, // 成交数量
AvgPrice : 1000, // 成交均价
Status : 1, // 订单状态,参考常量里的订单状态ORDER_STATE_CLOSED
Type : 0, // 订单类型,参考常量里的订单类型ORDER_TYPE_BUY
Offset : 0 // 商品期货的订单数据中,订单的开平仓方向,ORDER_OFFSET_OPEN为开仓,ORDER_OFFSET_CLOSE为平仓方向
ContractType : "" // 该属性为具体的合约代码
}
MarketOrder
市场深度单,即exchange.GetDepth()函数返回数据中Bids、Asks数组中的元素的数据结构。
javascript
{
Price : 1000, // 价格
Amount : 1 // 数量
}
Depth
市场深度,由exchange.GetDepth()函数返回。
javascript
{
Asks : [...], // 卖单数组,MarketOrder数组,按价格从低向高排序
Bids : [...], // 买单数组,MarketOrder数组,按价格从高向低排序
Time : 1567736576000 // 毫秒级别时间戳
}
Account
账户信息,由exchange.GetAccount()函数返回。
javascript
{
Info : {...}, // 请求交易所接口后,交易所接口应答的原始数据,回测时无此属性
Balance : 1000, // 可用余额
FrozenBalance : 0, // 挂单冻结的余额
Stocks : 1, // 传统期货、股票证券此属性固定为0
FrozenStocks : 0 // 传统期货、股票证券此属性固定为0
}
Position
期货交易中持有的仓位信息,由exchange.GetPosition()函数返回此Position结构的数组。
javascript
{
Info : {...}, // 请求交易所接口后,交易所接口应答的原始数据,回测时无此属性
MarginLevel : 10, // 杆杠大小,商品期货无法修改杠杆值
Amount : 100, // 持仓量
FrozenAmount : 0, // 挂单平仓时,仓位冻结量
Price : 10000, // 持仓均价
Profit : 0, // 股票不支持此字段,商品期货为盯市盈亏
// CTP中用closebuy_today平今多头仓位,对应的Type属性为PD_LONG;用closesell_today平今空头仓位,对应的Type属性为PD_SHORT;用closebuy平昨多头仓位,对应的Type属性为PD_LONG_YD;用closesell平昨空头仓位,对应的Type属性为PD_SHORT_YD
Type : 0,
ContractType : "rb2201", // 合约代码、股票代码
Margin : 1 // 仓位占用的保证金
}
全局函数
Version()
Version(),返回系统当前版本号。返回值:字符串类型。
Sleep(Millisecond)
Sleep(Millisecond),休眠函数,使程序暂停一段时间。参数值:Millisecond为数值类型。参数为毫秒数,例如:Sleep(1000)为休眠1秒。
支持休眠时间小于1毫秒的操作,例如设置Sleep(0.1)。支持最小参数为0.000001,纳秒级休眠。1纳秒等于1e-6毫秒。
注意:
在使用Python语言编写策略时,对于轮询间隔、时间等待的操作应当使用Sleep(Millisecond)函数。不建议使用Python的time库的time.sleep(second)函数。因为策略中使用time.sleep(second)函数在回测时会让策略程序实际等待一定秒数(second参数为设置暂停的秒数),导致策略回测非常慢。
IsVirtual()
IsVirtual(),判断当前策略运行是否为模拟回测。返回值:布尔类型。
模拟回测状态返回true,实盘返回false。
Mail(...)
Mail(smtpServer, smtpUsername, smtpPassword, mailTo, title, body),发送邮件函数。参数值:参数全部为字符串类型。返回值:布尔类型,发送成功返回true。
smtpServer为发送邮箱smtp服务,smtpUsername为邮箱账号,smtpPassword为邮箱的SMTP密码(不是邮箱登录密码),mailTo为接受邮件的邮箱账号,title为发送的邮件标题,body为发送的邮件内容,例如:
javascript
function main(){
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
}
python
def main():
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body")
c++
void main() {
Mail("smtp.163.com", "asdf@163.com", "password", "111@163.com", "title", "body");
}
-
Mail函数的异步版本Mail_Go函数:
使用方式和exchange.Go函数类似。javascriptfunction 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) }python# 不支持c++// 不支持
注意:
阿里云服务器可能会封一些端口,导致邮件无法发出。如需更改端口,可以直接在第一个参数中加入端口号,例如: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的数据库文件,防止频繁报错导致数据库文件膨胀)。
javascript
function main() {
SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
}
python
def main():
SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused")
c++
void main() {
SetErrorFilter("502:|503:|tcp|character|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF|reused");
}
过滤某个接口错误信息:
javascript
function main() {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 随便查询一个不存在的订单,id为123,故意让接口报错
var order = exchange.GetOrder("123")
Log(order)
// 过滤http502错误、GetOrder接口错误,设置错误过滤之后,第二次调用GetOrder不再报错
SetErrorFilter("502:|GetOrder")
order = exchange.GetOrder("123")
Log(order)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
order = exchange.GetOrder("123")
Log(order)
SetErrorFilter("502:|GetOrder")
order = exchange.GetOrder("123")
Log(order)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
TId orderId;
Order order = exchange.GetOrder(orderId);
Log(order);
SetErrorFilter("502:|GetOrder");
order = exchange.GetOrder(orderId);
Log(order);
}
GetPid()
GetPid(),返回实盘进程ID。返回值:字符串类型。
javascript
function main(){
var id = GetPid()
Log(id)
}
python
def main():
id = GetPid()
Log(id)
c++
void main() {
auto id = GetPid();
Log(id);
}
GetLastError()
GetLastError(),获取最近一次出错信息。一般无需使用,因为程序会把出错信息自动上传到日志系统。返回值:字符串类型。调用GetLastError()函数后会清除这个错误缓存,再次调用时不会再返回上次记录的错误信息。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
// 因为不存在编号为123的订单,所以会出错
exchange.GetOrder("123")
var error = GetLastError()
Log(error)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.GetOrder("123")
error = GetLastError()
Log(error)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
// 订单ID类型:TId,所以不能传入字符串,我们下一个不符合交易所规范的订单来触发
exchange.GetOrder(exchange.Buy(1, 1));
auto error = GetLastError();
Log(error);
}
GetCommand()
GetCommand(),获取交互命令字符串(utf-8)。获取策略交互界面发来的命令并清空缓存,没有命令则返回空字符串。返回的命令格式为按钮名称:参数,如果交互控件没有参数(例如不带输入框的按钮控件)则命令就是按钮名称。
javascript
function main(){
while(true) {
var cmd = GetCommand()
if (cmd) {
Log(cmd)
}
Sleep(1000)
}
}
python
def main():
while True:
cmd = GetCommand()
if cmd:
Log(cmd)
Sleep(1000)
c++
void main() {
while(true) {
auto cmd = GetCommand();
if(cmd != "") {
Log(cmd);
}
Sleep(1000);
}
}
底层系统有一个队列结构记录交互命令,当GetCommand()函数被调用时,会取出队列中最先进入的交互命令(如果没有交互命令时返回空字符串)。
交互控件的使用例子,策略编辑界面设置交互控件。
策略中设计交互代码:
javascript
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)
}
}
Sleep(1000)
}
}
python
def main():
while True:
LogStatus(_D())
cmd = GetCommand()
if cmd:
Log("cmd:", cmd)
arr = cmd.split(":")
if arr[0] == "buy":
Log("买入,该控件不带数量")
elif arr[0] == "sell":
Log("卖出,该控件带数量:", arr[1])
else:
Log("其它控件触发:", arr)
Sleep(1000)
c++
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
void split(const string& s,vector<string>& sv,const char flag = ' ') {
sv.clear();
istringstream iss(s);
string temp;
while (getline(iss, temp, flag)) {
sv.push_back(temp);
}
return;
}
void main() {
while(true) {
LogStatus(_D());
auto cmd = GetCommand();
if (cmd != "") {
vector<string> arr;
split(cmd, arr, ':');
if(arr[0] == "buy") {
Log("买入,该控件不带数量");
} else if (arr[0] == "sell") {
Log("卖出,该控件带数量:", arr[1]);
} else {
Log("其它控件触发:", arr);
}
}
Sleep(1000);
}
}
GetMeta()
GetMeta()函数返回生成策略注册码时写入的Meta的值,该函数返回值为字符串类型。
应用场景,例如策略需要对不同租户做资金限制。注意:生成注册码时Meta的长度不能超过190字符,该函数仅适用于实盘,需要使用最新的托管者。如果生成策略注册码时没有设置元数据GetMeta()返回空值。
Dial(...)
Dial(Address, Timeout),原始的Socket访问,支持tcp,udp,tls,unix协议。参数值:Address为字符串类型,TimeOut为数值类型,数值单位为秒,如果超时Dial(...)函数返回空值。
Address参数详细说明:
| -- | 参数说明 |
|---|---|
设置Dial功能的参数 | 在正常的地址:wss://xxx.xxx.xxx:10441/websocket?compress后以"|"符号分隔,如果参数字符串中有|字符,则以"||"作为分隔符号。各个参数之间用"&"连接,例如ss5代理和压缩参数一起设置:Dial("wss://baidu.com/stream|proxy=socks5://xxx:9999&compress=gzip_raw&mode=recv") |
| 用于ws协议时,数据压缩相关的参数:compress=参数值 | compress为压缩方式,compress参数,可选gzip_raw,gzip等。非标准gzip。需要使用扩展的方式:gzip_raw,即在分隔符"|"后添加设置compress=gzip_raw,用"&"符号和下一个mode参数分隔。 |
| 用于ws协议时,数据压缩相关的参数:mode=参数值 | mode为模式,可选dual,send,recv三种。dual为双向,发送压缩数据,接收压缩数据。send为发送压缩数据。recv为接收压缩数据,本地解压缩。 |
| 用于设置socks5代理的相关参数:proxy=参数值 | proxy为ss5代理设置,参数值格式:socks5://name:pwd@192.168.0.1:1080,name为ss5服务端用户名,pwd为ss5服务端登录密码,1080为ss5服务的端口 |
| 用于ws协议时,设置底层自动重连相关的参数:reconnect=参数值 | reconnect为是否设置重连,reconnect=true为启用重连,不设置默认不重连 |
| 用于ws协议时,设置底层自动重连相关的参数:interval=参数值 | interval为重试时间间隔,单位毫秒,interval=10000为重试间隔10秒,不设置默认1秒,即interval=1000 |
| 用于ws协议时,设置底层自动重连相关的参数:payload=参数值 | payload为ws重连时需要发送的订阅消息,例如:payload=okok |
javascript
function main(){
// Dial支持tcp://,udp://,tls://,unix://协议,可加一个参数指定超时的秒数
var client = Dial("tls://www.baidu.com:443")
if (client) {
// write可再跟一个数字参数指定超时,write返回成功发送的字节数
client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
while (true) {
// read可再跟一个数字参数指定超时,单位:毫秒。返回null指出错或者超时或者socket已经关闭
var buf = client.read()
if (!buf) {
break
}
Log(buf)
}
client.close()
}
}
python
def main():
client = Dial("tls://www.baidu.com:443")
if client:
client.write("GET / HTTP/1.1\nConnection: Closed\n\n")
while True:
buf = client.read()
if not buf:
break
Log(buf)
client.close()
c++
void main() {
auto client = Dial("tls://www.baidu.com:443");
if(client.Valid) {
client.write("GET / HTTP/1.1\nConnection: Closed\n\n");
while(true) {
auto buf = client.read();
if(buf == "") {
break;
}
Log(buf);
}
client.close();
}
}
read函数支持以下参数:
- 不传参数时,阻塞到有消息时就返回。例如:
ws.read()。 - 传入参数时,单位为毫秒,指定消息等待超时时间。例如:
ws.read(2000)指定超时时间为两秒(2000毫秒)。 - 以下两个参数只对
websocket有效:
传入参数-1指不管有无消息,函数立即返回,例如:ws.read(-1)。
传入参数-2指不管有无消息,函数立即返回,但只返回最新的消息,缓冲区的消息会被丢弃。例如ws.read(-2)。
read()函数缓冲区说明:
ws协议推送的来的数据,如果在策略read()函数调用之间时间间隔过长,就可能造成数据累积。这些数据储存在缓冲区,缓冲区数据结构为队列,上限2000个。超出2000后,最新的数据进入缓冲区,最旧的数据清除掉。
read函数参数 | 无参数 | 参数:-1 | 参数:-2 | 参数:2000,单位是毫秒 |
|---|---|---|---|---|
| 缓冲区已有数据 | 立即返回最旧数据 | 立即返回最旧数据 | 立即返回最新数据 | 立即返回最旧数据 |
| 缓冲区无数据 | 阻塞到有数据时返回 | 立即返回空值 | 立即返回空值 | 等待2000毫秒,无数据返回空值,有数据则返回 |
| ws连接断开,底层重连时 | read()函数返回空字符串,即:"",write()函数返回0,检测到该情况。可以使用close()函数关闭连接,如果设置了自动重连则不用关闭,系统底层会自动重连。 | |||
HttpQuery(...)
HttpQuery(Url, PostData, Cookies, Headers, IsReturnHeader),网络URL访问。参数值:全部为字符串类型。
返回值为JSON字符串,JavaScript语言的策略中可以用JSON.parse()函数解析。
注意:
HttpQuery(...)函数只支持JavaScript语言。Python语言可以使用urllib库,直接发送http请求。
获取一个Url的返回内容,如果第二个参数PostData为字符串a=1&b=2&c=abc形式,就以POST方式提交。其它例如PUT等方式提交,PostData参数为{method:'PUT', data:'a=1&b=2&c=abc'}。
PostData参数也可以是JSON字符串。
Cookies这个参数形式为:a=10; b=20,各参数用分号;间隔。
Headers这个参数形式为:User-Agent: Mobile\nContent-Type: text/html各参数用换行符\n间隔。
第二个参数PostData可以自定义方法比如:
HttpQuery("http://www.abc.com", {method:'PUT', data:'a=1&b=2&c=abc'}),注意:如果需要给HttpQuery函数设置超时时间,可以在{method:'PUT', data:'a=1&b=2&c=abc'}加入timeout属性(默认60秒)。
设置1秒钟超时:
HttpQuery("http://www.abc.com", {method:'PUT', data:'a=1&b=2&c=abc', timeout:1000})
传递Cookie字符串需要第三个参数,但不需要POST请将第二个参数置为空值。模拟测试的时候因为无法模拟访问URL,函数就返回固定字符串Dummy Data。可以用此接口发送短信,或者与其它API接口进行交互。
GET方法调用例子:HttpQuery("http://www.baidu.com")。
POST方法调用例子:HttpQuery("http://www.163.com", "a=1&b=2&c=abc")。
返回Header的调用例子:
javascript
HttpQuery("http://www.baidu.com", null, "a=10; b=20", "User-Agent: Mobile\nContent-Type: text/html", true) // will return {Header: HTTP Header, Body: HTML}
-
HttpQuery函数使用代理设置:javascriptfunction main() { // 本次设置代理并发送http请求,无用户名,无密码,此次http请求会通过代理发送 HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/") // 本次设置代理并发送http请求,输入用户名和密码,仅HttpQuery当前调用生效,之后再次调用HttpQuery("http://www.baidu.com")这样不会使用代理 HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/") }python# HttpQuery不支持Python,可以使用Python的urllib2库c++void main() { HttpQuery("socks5://127.0.0.1:8889/http://www.baidu.com/"); HttpQuery("socks5://username:password@127.0.0.1:8889/http://www.baidu.com/"); } -
HttpQuery函数的异步版本HttpQuery_Go:
使用方式和exchange.Go函数类似。javascriptfunction main() { // 创建第一个异步线程 var r1 = HttpQuery_Go("https://xxx.xxx.xxx") // https://xxx.xxx.xxx 仅为演示地址 // 创建第二个异步线程 var r2 = HttpQuery_Go("https://xxx.xxx.xxx") // 获取第一个异步线程调用的返回值 var tickers1 = r1.wait() // 获取第二个异步线程调用的返回值 var tickers2 = r2.wait() // 打印结果 Log("tickers1:", tickers1) Log("tickers2:", tickers2) }python# 不支持c++// 不支持 -
回测系统中使用
HttpQuery(...)函数:
回测系统中可以使用HttpQuery(...)发送请求(只支持GET请求)获取数据。回测时限制使用20次访问不同的URL,并且HttpQuery(...)访问会缓存数据,相同的URL第二次访问时HttpQuery(...)函数返回缓存数据(不再发生实际网络请求)。我们可以在某个服务器或者设备上运行一个服务程序,用来响应策略程序中
HttpQuery(...)发送的请求,测试用的Go语言服务程序如下:golangpackage main import ( "fmt" "net/http" "encoding/json" ) func Handle (w http.ResponseWriter, r *http.Request) { defer func() { fmt.Println("req:", *r) 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) }() }策略回测时使用
HttpQuery(...)函数发送请求:javascriptfunction main() { // 可以写自己运行服务程序所在设备的IP地址 Log(HttpQuery("http://xxx.xx.x.xxx:9090/data?msg=hello")); Log(exchange.GetAccount()); }python# HttpQuery不支持Python,可以使用Python的urllib2库c++void main() { // 可以写自己运行服务程序所在设备的IP地址 Log(HttpQuery("http://xxx.xx.x.xxx:9090/data?msg=hello")); Log(exchange.GetAccount()); } -
支持对请求的应答数据进行转码,支持常用编码。
指定PostData参数:{method: "GET",charset:"GB18030"},即可实现应答的数据转码(GB18030)。
Hash(...)
Hash(Algo, OutputAlgo, Data),支持md5/sha256/sha512/sha1的哈希计算,只支持实盘。参数值:全部为字符串类型。
第二个参数可设置为raw/hex/base64,分别指输出加密后的原始内容/hex编码过的数据/base64编码过的数据。
javascript
function main(){
Log(Hash("md5", "hex", "hello"))
Log(Hash("sha512", "base64", "hello"))
}
python
def main():
Log(Hash("md5", "hex", "hello"))
Log(Hash("sha512", "base64", "hello"))
c++
void main() {
Log(Hash("md5", "hex", "hello"));
Log(Hash("sha512", "base64", "hello"));
}
HMAC(...)
HMAC(Algo, OutputAlgo, Data, Key),支持md5/sha256/sha512/sha1的HMAC加密计算,只支持实盘。参数值:全部为字符串类型。
第二个参数可设置为raw/hex/base64,分别指输出加密后的原始内容/hex编码过的数据/base64编码过的数据。
javascript
function main(){
Log(HMAC("md5", "hex", "hello", "pass"))
Log(HMAC("sha512", "base64", "hello", "pass"))
}
python
def main():
Log(HMAC("md5", "hex", "hello", "pass"))
Log(HMAC("sha512", "base64", "hello", "pass"))
c++
void main() {
Log(HMAC("md5", "hex", "hello", "pass"));
Log(HMAC("sha512", "base64", "hello", "pass"));
}
UnixNano()
UnixNano(),返回纳秒级时间戳,如果需要获取毫秒级时间戳,可以使用如下代码:
javascript
function main() {
var time = UnixNano() / 1000000
Log(_N(time, 0))
}
python
def main():
time = UnixNano()
Log(time)
c++
void main() {
auto time = UnixNano();
Log(time);
}
Unix()
Unix(),返回秒级别时间戳。
javascript
function main() {
var t = Unix()
Log(t)
}
python
def main():
t = Unix()
Log(t)
c++
void main() {
auto t = Unix();
Log(t);
}
GetOS()
GetOS(),返回托管者所在系统的信息。
javascript
function main() {
Log("GetOS:", GetOS())
}
python
def main():
Log("GetOS:", GetOS())
c++
void main() {
Log("GetOS:", GetOS());
}
在苹果电脑Mac OS操作系统下运行的托管者日志输出:
GetOS:darwin/amd64
darwin即Mac OS系统的名称。
MD5(String)
MD5(String),参数值:字符串类型。
javascript
function main() {
Log("MD5", MD5("hello world"))
}
python
def main():
Log("MD5", MD5("hello world"))
c++
void main() {
Log("MD5", MD5("hello world"));
}
日志输出:
MD5 5eb63bbbe01eeed093cb22bb8f5acdc3
DBExec(...)
DBExec(),参数值:可以是字符串、数值、布尔值、空值等类型。返回值:包含SQLite语句执行结果的对象。
数据库接口函数DBExec()通过传入参数,可以操作实盘数据库(SQLite数据库)。实现对实盘数据库中数据的增、删、查、改等操作,支持SQLite语法。实盘数据库中系统保留表:kvdb、cfg、log、profit、chart,切勿对这些表进行操作。注意:DBExec()函数只支持实盘。
-
支持内存数据库
对于DBExec函数的参数,如果sql语句是以:开头,则在内存数据库中操作,不写文件,速度更快。适合不需要持久化保存的数据库操作,例如:javascriptfunction main() { var strSql = [ ":CREATE TABLE TEST_TABLE(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ].join("") var ret = DBExec(strSql) Log(ret) // 增加一条数据 Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) // 查询数据 Log(DBExec(":SELECT * FROM TEST_TABLE;")) }pythondef main(): arr = [ ":CREATE TABLE TEST_TABLE(", "TS INT PRIMARY KEY NOT NULL,", "HIGH REAL NOT NULL,", "OPEN REAL NOT NULL,", "LOW REAL NOT NULL,", "CLOSE REAL NOT NULL,", "VOLUME REAL NOT NULL)" ] strSql = "" for i in range(len(arr)): strSql += arr[i] ret = DBExec(strSql) Log(ret) # 增加一条数据 Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")) # 查询数据 Log(DBExec(":SELECT * FROM TEST_TABLE;"))c++void main() { string strSql = ":CREATE TABLE TEST_TABLE(\ TS INT PRIMARY KEY NOT NULL,\ HIGH REAL NOT NULL,\ OPEN REAL NOT NULL,\ LOW REAL NOT NULL,\ CLOSE REAL NOT NULL,\ VOLUME REAL NOT NULL)"; auto ret = DBExec(strSql); Log(ret); // 增加一条数据 Log(DBExec(":INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);")); // 查询数据 Log(DBExec(":SELECT * FROM TEST_TABLE;")); } -
创建表
javascript
function main() {
var strSql = [
"CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
].join("")
var ret = DBExec(strSql)
Log(ret)
}
python
def main():
arr = [
"CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
]
strSql = ""
for i in range(len(arr)):
strSql += arr[i]
ret = DBExec(strSql)
Log(ret)
c++
void main() {
string strSql = "CREATE TABLE TEST_TABLE(\
TS INT PRIMARY KEY NOT NULL,\
HIGH REAL NOT NULL,\
OPEN REAL NOT NULL,\
LOW REAL NOT NULL,\
CLOSE REAL NOT NULL,\
VOLUME REAL NOT NULL)";
auto ret = DBExec(strSql);
Log(ret);
}
- 表中记录的增删查改操作
javascript
function main() {
var strSql = [
"CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
].join("")
Log(DBExec(strSql))
// 增加一条数据
Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
// 查询数据
Log(DBExec("SELECT * FROM TEST_TABLE;"))
// 修改数据
Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))
// 删除数据
Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))
}
python
def main():
arr = [
"CREATE TABLE TEST_TABLE(",
"TS INT PRIMARY KEY NOT NULL,",
"HIGH REAL NOT NULL,",
"OPEN REAL NOT NULL,",
"LOW REAL NOT NULL,",
"CLOSE REAL NOT NULL,",
"VOLUME REAL NOT NULL)"
]
strSql = ""
for i in range(len(arr)):
strSql += arr[i]
Log(DBExec(strSql))
# 增加一条数据
Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"))
# 查询数据
Log(DBExec("SELECT * FROM TEST_TABLE;"))
# 修改数据
Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000))
# 删除数据
Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110))
c++
void main() {
string strSql = "CREATE TABLE TEST_TABLE(\
TS INT PRIMARY KEY NOT NULL,\
HIGH REAL NOT NULL,\
OPEN REAL NOT NULL,\
LOW REAL NOT NULL,\
CLOSE REAL NOT NULL,\
VOLUME REAL NOT NULL)";
Log(DBExec(strSql));
// 增加一条数据
Log(DBExec("INSERT INTO TEST_TABLE (TS, HIGH, OPEN, LOW, CLOSE, VOLUME) VALUES (1518970320000, 100, 99.1, 90, 100, 12345.6);"));
// 查询数据
Log(DBExec("SELECT * FROM TEST_TABLE;"));
// 修改数据
Log(DBExec("UPDATE TEST_TABLE SET HIGH=? WHERE TS=?", 110, 1518970320000));
// 删除数据
Log(DBExec("DELETE FROM TEST_TABLE WHERE HIGH=?", 110));
}
内置函数
_G(K, V)
_G(K, V),该函数实现了一个可保存的全局字典功能,回测和实盘均支持。回测结束后,保存的数据会被清除。
数据结构为KV表,永久保存在本地文件,每个实盘单独一个数据库,重启或者托管者退出后一直存在。K必须为字符串,不区分大小写,V可以为任何可以JSON序列化的内容。在实盘运行中当调用_G()函数并且不传任何参数时,_G()函数返回当前实盘的ID。
javascript
function main(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
// 设置一个全局变量num,值为1
_G("num", 1)
// 更改一个全局变量num,值为字符串ok
_G("num", "ok")
// 删除全局变量num
_G("num", null)
// 返回全局变量num的值
Log(_G("num"))
// 删除所有全局变量
_G(null)
// 返回实盘ID
var robotId = _G()
}
python
def main():
_G("num", 1)
_G("num", "ok")
_G("num", None)
Log(_G("num"))
_G(None)
robotId = _G()
c++
void main() {
_G("num", 1);
_G("num", "ok");
_G("num", NULL);
Log(_G("num"));
_G(NULL);
// 不支持 auto robotId = _G();
}
注意:
使用_G函数持久化储存数据时,应当根据硬件设备的内存、硬盘空间合理使用,不可滥用。否则可能造成内存溢出的问题。
_D(Timestamp, Fmt)
_D(Timestamp, Fmt),返回指定时间戳对应的时间字符串。参数值:Timestamp为数值类型,值为毫秒数。Fmt为字符串类型,Fmt默认为:yyyy-MM-dd hh:mm:ss,返回值:字符串类型。
返回指定时间戳(毫秒)对应的字符串,不传任何参数就返回当前时间。例如:_D()或者_D(1478570053241),默认格式为yyyy-MM-dd hh:mm:ss。
javascript
function main(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
var time = _D()
Log(time)
}
python
def main():
strTime = _D()
Log(strTime)
c++
void main() {
auto strTime = _D();
Log(strTime);
}
注意:
Python策略中使用_D()时,需要注意传入的参数为秒级别时间戳(JavaScript、C++策略中为毫秒级别时间戳,1秒等于1000毫秒)。
在实盘时使用_D()函数解析一个时间戳为可读的时间字符串时,需要注意托管者程序所在的操作系统的时区、时间设置。_D()函数解析一个时间戳为可读时间字符串是根据托管者系统的时间而定的。
例如一个时间戳为1574993606000使用代码解析:
javascript
function main() {
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
Log(_D(1574993606000))
}
python
def main():
# 北京时间的服务器上运行:2019-11-29 10:13:26 ,另一台其它地区的服务器上的托管者运行此代码结果则为:2019-11-29 02:13:26
Log(_D(1574993606))
c++
void main() {
Log(_D(1574993606000));
}
_N(Num, Precision)
_N(Num, Precision),格式化一个浮点数。参数值:Num为数值类型,Precision为整型。返回值:数值类型。
例如_N(3.1415, 2)将删除3.1415小数点两位以后的值,函数返回3.14。
javascript
function main(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
var i = 3.1415
Log(i)
var ii = _N(i, 2)
Log(ii)
}
python
def main():
i = 3.1415
Log(i)
ii = _N(i, 2)
Log(ii)
c++
void main() {
auto i = 3.1415;
Log(i);
auto ii = _N(i, 2);
Log(ii);
}
如果需要将小数点左边的N个位数都变为0,可以这么写:
javascript
function main(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
var i = 1300
Log(i)
var ii = _N(i, -3)
// 查看日志得知为1000
Log(ii)
}
python
def main():
i = 1300
Log(i)
ii = _N(i, -3)
Log(ii)
c++
void main() {
auto i = 1300;
Log(i);
auto ii = _N(i, -3);
Log(ii);
}
_C(...)
_C(function, args...),该函数为重试函数,用于获取行情、获取未完成订单等接口的容错。
该接口会一直调用指定函数直到成功返回(参数function引用的函数调用时返回空值或者false会重试调用)。例如_C(exchange.GetTicker),默认重试间隔为3秒,可以调用_CDelay(...)函数来设置重试间隔,例如_CDelay(1000),指改变_C函数重试间隔为1秒。
对于以下函数:
exchange.GetTicker()exchange.GetDepth()exchange.GetTrades()exchange.GetRecords()exchange.GetAccount()exchange.GetOrders()exchange.GetOrder()exchange.GetPosition()
都可以通过_C(...)函数来调用进行容错。_C(function, args...)函数不局限于以上列出的函数容错,参数function是函数引用并非函数调用,注意是_C(exchange.GetTicker)并不是_C(exchange.GetTicker())。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 测试代码部分
// 设置合约为rb888即螺纹钢主力连续合约,或者设置股票代码
exchange.SetContractType("rb888")
var ticker = _C(exchange.GetTicker)
// 调整_C()函数重试时间间隔为2秒
_CDelay(2000)
var depth = _C(exchange.GetDepth)
Log(ticker)
Log(depth)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
ticker = _C(exchange.GetTicker)
_CDelay(2000)
depth = _C(exchange.GetDepth)
Log(ticker)
Log(depth)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto ticker = _C(exchange.GetTicker);
_CDelay(2000);
auto depth = _C(exchange.GetDepth);
Log(ticker);
Log(depth);
}
对于有参数的函数使用_C(...)容错时:
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 测试代码部分
// 设置合约为rb888即螺纹钢主力连续合约,或者设置股票代码
exchange.SetContractType("rb888")
var records = _C(exchange.GetRecords, PERIOD_D1)
Log(records)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
records = _C(exchange.GetRecords, PERIOD_D1)
Log(records)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto records = _C(exchange.GetRecords, PERIOD_D1);
Log(records);
}
也可以用于自定义函数的容错处理:
javascript
var test = function(a, b){
var time = new Date().getTime() / 1000
if(time % b == 3){
Log("符合条件!", "#FF0000")
return true
}
Log("重试!", "#FF0000")
return false
}
function main(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试自定义的函数
var ret = _C(test, 1, 5)
Log(ret)
}
python
import time
def test(a, b):
ts = time.time()
if ts % b == 3:
Log("符合条件!", "#FF0000")
return True
Log("重试!", "#FF0000")
return False
def main():
ret = _C(test, 1, 5)
Log(ret)
c++
// C++ 不支持这种方式对于自定义函数容错
_Cross(Arr1, Arr2)
_Cross(Arr1, Arr2),返回数组arr1与arr2的交叉周期数。正数为上穿周期,负数表示下穿的周期,0指当前价格一样。参数值:数值类型数组。
可以模拟一组数据测试_Cross(Arr1, Arr2)函数:
javascript
// 快线指标
var arr1 = [1,2,3,4,5,6,8,8,9]
// 慢线指标
var arr2 = [2,3,4,5,6,7,7,7,7]
function main(){
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
}
python
arr1 = [1,2,3,4,5,6,8,8,9]
arr2 = [2,3,4,5,6,7,7,7,7]
def main():
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2))
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1))
c++
void main() {
vector<double> arr1 = {1,2,3,4,5,6,8,8,9};
vector<double> arr2 = {2,3,4,5,6,7,7,7,7};
Log("_Cross(arr1, arr2) : ", _Cross(arr1, arr2));
Log("_Cross(arr2, arr1) : ", _Cross(arr2, arr1));
}
把模拟的数据可视化进行观察
具体使用说明:内置函数_Cross分析及使用说明
JSONParse(strJson)
JSONParse(strJson),该函数用于解析JSON字符串。可以正确解析包含有较大数值的JSON字符串,会将较大的数值解析为字符串类型。回测系统不支持该函数。
javascript
function main() {
let s1 = '{"num": 8754613216564987646512354656874651651358}'
Log("JSON.parse:", JSON.parse(s1)) // JSON.parse: {"num":8.754613216564988e+39}
Log("JSONParse:", JSONParse(s1)) // JSONParse: {"num":"8754613216564987646512354656874651651358"}
let s2 = '{"num": 123}'
Log("JSON.parse:", JSON.parse(s2)) // JSON.parse: {"num":123}
Log("JSONParse:", JSONParse(s2)) // JSONParse: {"num":123}
}
python
import json
def main():
s1 = '{"num": 8754613216564987646512354656874651651358}'
Log("json.loads:", json.loads(s1)) # json.loads: map[num:8.754613216564987e+39]
Log("JSONParse:", JSONParse(s1)) # JSONParse: map[num:8754613216564987646512354656874651651358]
s2 = '{"num": 123}'
Log("json.loads:", json.loads(s2)) # json.loads: map[num:123]
Log("JSONParse:", JSONParse(s2)) # JSONParse: map[num:123]
c++
void main() {
auto s1 = "{\"num\":8754613216564987646512354656874651651358}";
Log("json::parse:", json::parse(s1));
// Log("JSONParse:", JSONParse(s1)); // 不支持该函数
auto s2 = "{\"num\":123}";
Log("json::parse:", json::parse(s2));
// Log("JSONParse:", JSONParse(s2)); // 不支持该函数
}
自定义颜色
每个消息字符串都可以用#ff0000这样的RGB值结尾,代表需要显示的前景色。如果为#ff0000112233这样的格式,则后六位代表背景色。
颜色十六进制图:

javascript
function main() {
// 不使用接口获取数据的测试,就无需使用exchange.IO("status")函数判断连接状态,也不用设置合约代码,因为这里仅仅是测试
Log("红色", "#FF0000")
}
python
def main():
Log("红色", "#FF0000")
c++
void main() {
Log("红色", "#FF0000");
}
日志信息
实盘运行时日志信息记录在实盘的数据库中,实盘的数据库采用sqlite3数据库,实盘的数据库文件在托管者程序所在设备,数据库文件在托管者程序(robot可执行程序)目录下。例如:ID为130350的实盘数据库文件在../logs/storage/130350这个目录内(..即为robot托管者程序所在目录),数据库文件名为130350.db3。
回测系统中的日志可以在回测结束后,点击回测页面底部右下角的[下载日志]按钮进行下载。
当需要迁移实盘到其它服务器上的托管者时,可以移动实盘的数据库文件(扩展名为db3的数据库文件)到迁移目标服务器上,把文件名设置为平台上对应的实盘ID。这样之前实盘的所有日志信息就不会因为迁移到新设备而丢失。
Log(...)
Log(message),保存一条信息到日志列表。参数值:message可以是任意类型。
如果在字符串后面加上@字符则消息会进入推送队列,推送到当前优宽量化交易平台账号推送设置中配置的邮箱、WebHook等(依次打开控制中心页面、右上角账号设置页面、推送设置页面设置绑定)。
注意:
- 「调试工具」中不支持推送。
- 「回测系统」中不支持推送。
推送限制为50条/小时,每条消息推送间隔至少10秒。推送内容重复会被过滤掉。
javascript
function main() {
Log("优宽量化你好 !@")
Sleep(1000 * 5)
// 字符串内加入#ff0000,打印日志显示为红色,注意:回测系统不支持推送!
Log("你好, #ff0000@")
}
python
def main():
Log("优宽量化你好 !@")
Sleep(1000 * 5)
Log("你好, #ff0000@")
c++
void main() {
Log("优宽量化你好 !@");
Sleep(1000 * 5);
Log("你好, #ff0000@");
}
WebHook推送:
使用Golang编写的服务程序DEMO:
golang
package main
import (
"fmt"
"net/http"
)
func Handle (w http.ResponseWriter, r *http.Request) {
defer func() {
fmt.Println("req:", *r)
}()
}
func main () {
fmt.Println("listen http://localhost:9090")
http.HandleFunc("/data", Handle)
http.ListenAndServe(":9090", nil)
}
设置WebHook:http://XXX.XX.XXX.XX:9090/data?data=Hello_优宽
运行服务程序后,执行策略推送信息:
javascript
function main() {
Log("msg", "@")
}
python
def main():
Log("msg", "@")
c++
void main() {
Log("msg", "@");
}
接收到推送,服务程序打印信息:
mylang
listen http://localhost:9090
req: {GET /data?data=Hello_优宽 HTTP/1.1 1 1 map[User-Agent:[Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/xx.x.xxxx.xxx Safari/537.36] Accept-Encoding:[gzip]] {} <nil> 0 [] false 1XX.XX.X.XX:9090 map[] map[] <nil> map[] XXX.XX.XXX.XX:4xxx2 /data?data=Hello_优宽 <nil> <nil> <nil> 0xc420056300}
打印base64编码后的图片
Log函数支持打印base64编码后的图片,以`开头,以 `结尾,例如:
javascript
function main() {
Log("`data:image/png;base64,AAAA`")
}
python
def main():
Log("`data:image/png;base64,AAAA`")
c++
void main() {
Log("`data:image/png;base64,AAAA`");
}
Log支持直接打印Python的matplotlib.pyplot对象,只要对象包含savefig方法就可以直接Log打印,例如:
python
import matplotlib.pyplot as plt
def main():
plt.plot([3,6,2,4,7,1])
Log(plt)
打印的日志自动语言切换
Log函数支持语言切换,Log函数输出文本,会根据平台页面上语言设置自动切换为对应的语言,例如:
javascript
function main() {
Log("中文")
}
python
def main():
Log("中文")
c++
void main() {
Log("中文");
}
LogProfit(Profit)
LogProfit(Profit),记录盈亏值,打印盈亏数值并根据盈亏数值绘制收益曲线,参数值:Profit为数值类型。
该函数如果以字符&结尾,只绘制收益图表,不打印收益日志,例如:LogProfit(10, '&')。
LogProfitReset()
LogProfitReset(),清空所有收益日志,可以带一个整数数值参数,指定保留的条数。
javascript
function main() {
// 在收益图表上打印30个点,然后重置,只保留最后10个点
for(var i = 0; i < 30; i++) {
LogProfit(i)
Sleep(500)
}
LogProfitReset(10)
}
python
def main():
for i in range(30):
LogProfit(i)
Sleep(500)
LogProfitReset(10)
c++
void main() {
for(int i = 0; i < 30; i++) {
LogProfit(i);
Sleep(500);
}
LogProfitReset(10);
}
LogStatus(Msg)
LogStatus(Msg),此信息不保存到日志列表里,只更新当前实盘的状态信息,在实盘页面日志区域上方的状态栏显示,可多次调用更新状态。参数值:Msg可以为任意类型。
javascript
function main() {
LogStatus('这是一个普通的状态提示')
LogStatus('这是一个红色字体的状态提示#ff0000')
LogStatus('这是一个多行的状态信息\n我是第二行')
}
python
def main():
LogStatus('这是一个普通的状态提示')
LogStatus('这是一个红色字体的状态提示#ff0000')
LogStatus('这是一个多行的状态信息\n我是第二行')
c++
void main() {
LogStatus("这是一个普通的状态提示");
LogStatus("这是一个红色字体的状态提示#ff0000");
LogStatus("这是一个多行的状态信息\n我是第二行");
}
LogStatus(Msg)支持打印base64编码后的图片,以`开头,以`结尾,例如:LogStatus("`data:image/png;base64,AAAA`")。
LogStatus(Msg)支持直接传入Python的matplotlib.pyplot对象,只要对象包含savefig方法就可以传入LogStatus(Msg)函数,例如:
python
import matplotlib.pyplot as plt
def main():
plt.plot([3,6,2,4,7,1])
LogStatus(plt)
状态栏中数据输出示例:
javascript
function main() {
var table = {type: 'table', title: '持仓信息', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
// JSON序列化后两边加上`字符,视为一个复杂消息格式(当前支持表格)
LogStatus('`' + JSON.stringify(table) + '`')
// 表格信息也可以在多行中出现
LogStatus('第一行消息\n`' + JSON.stringify(table) + '`\n第三行消息')
// 支持多个表格同时显示,将以TAB显示到一组里
LogStatus('`' + JSON.stringify([table, table]) + '`')
// 也可以构造一个按钮在表格中,策略用GetCommand接收cmd属性的内容
var table = {
type: 'table',
title: '持仓操作',
cols: ['列1', '列2', 'Action'],
rows: [
['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': '平仓'}]
]
}
LogStatus('`' + JSON.stringify(table) + '`')
// 或者构造一单独的按钮
LogStatus('`' + JSON.stringify({'type':'button', 'cmd': 'coverAll', 'name': '平仓'}) + '`')
// 可以自定义按钮风格(bootstrap的按钮属性)
LogStatus('`' + JSON.stringify({'type':'button', 'class': 'btn btn-xs btn-danger', 'cmd': 'coverAll', 'name': '平仓'}) + '`')
}
python
import json
def main():
table = {"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]}
LogStatus('`' + json.dumps(table) + '`')
LogStatus('第一行消息\n`' + json.dumps(table) + '`\n第三行消息')
LogStatus('`' + json.dumps([table, table]) + '`')
table = {
"type" : "table",
"title" : "持仓操作",
"cols" : ["列1", "列2", "Action"],
"rows" : [
["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
]
}
LogStatus('`' + json.dumps(table) + '`')
LogStatus('`' + json.dumps({"type": "button", "cmd": "coverAll", "name": "平仓"}) + '`')
LogStatus('`' + json.dumps({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "平仓"}) + '`')
c++
void main() {
json table = R"({"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
LogStatus("`" + table.dump() + "`");
LogStatus("第一行消息\n`" + table.dump() + "`\n第三行消息");
json arr = R"([])"_json;
arr.push_back(table);
arr.push_back(table);
LogStatus("`" + arr.dump() + "`");
table = R"({
"type" : "table",
"title" : "持仓操作",
"cols" : ["列1", "列2", "Action"],
"rows" : [
["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}]
]
})"_json;
LogStatus("`" + table.dump() + "`");
LogStatus("`" + R"({"type": "button", "cmd": "coverAll", "name": "平仓"})"_json.dump() + "`");
LogStatus("`" + R"({"type": "button", "class": "btn btn-xs btn-danger", "cmd": "coverAll", "name": "平仓"})"_json.dump() + "`");
}
设置状态栏按钮的禁用、描述功能:
javascript
function main() {
var table = {
type: "table",
title: "状态栏按钮的禁用、描述功能测试",
cols: ["列1", "列2", "列3"],
rows: []
}
var button1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
var button2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": true}
var button3 = {"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": false}
table.rows.push([button1, button2, button3])
LogStatus("`" + JSON.stringify(table) + "`")
}
python
import json
def main():
table = {
"type": "table",
"title": "状态栏按钮的禁用、描述功能测试",
"cols": ["列1", "列2", "列3"],
"rows": []
}
button1 = {"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"}
button2 = {"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": True}
button3 = {"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": False}
table["rows"].append([button1, button2, button3])
LogStatus("`" + json.dumps(table) + "`")
c++
void main() {
json table = R"({
"type": "table",
"title": "状态栏按钮的禁用、描述功能测试",
"cols": ["列1", "列2", "列3"],
"rows": []
})"_json;
json button1 = R"({"type": "button", "name": "按钮1", "cmd": "button1", "description": "这是第一个按钮"})"_json;
json button2 = R"({"type": "button", "name": "按钮2", "cmd": "button2", "description": "这是第二个按钮,设置为禁用", "disabled": true})"_json;
json button3 = R"({"type": "button", "name": "按钮3", "cmd": "button3", "description": "这是第三个按钮,设置为启用", "disabled": false})"_json;
json arr = R"([])"_json;
arr.push_back(button1);
arr.push_back(button2);
arr.push_back(button3);
table["rows"].push_back(arr);
LogStatus("`" + table.dump() + "`");
}
状态栏按钮样式设置:
javascript
function main() {
var table = {
type: "table",
title: "状态栏按钮样式",
cols: ["默认", "原始", "成功", "信息", "警告", "危险"],
rows: [
[
{"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
{"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
{"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
{"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
{"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
{"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
]
]
}
LogStatus("`" + JSON.stringify(table) + "`")
}
python
import json
def main():
table = {
"type": "table",
"title": "状态栏按钮样式",
"cols": ["默认", "原始", "成功", "信息", "警告", "危险"],
"rows": [
[
{"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
{"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
{"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
{"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
{"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
{"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
]
]
}
LogStatus("`" + json.dumps(table) + "`")
c++
void main() {
json table = R"({
"type": "table",
"title": "状态栏按钮样式",
"cols": ["默认", "原始", "成功", "信息", "警告", "危险"],
"rows": [
[
{"type":"button", "class": "btn btn-xs btn-default", "name": "默认"},
{"type":"button", "class": "btn btn-xs btn-primary", "name": "原始"},
{"type":"button", "class": "btn btn-xs btn-success", "name": "成功"},
{"type":"button", "class": "btn btn-xs btn-info", "name": "信息"},
{"type":"button", "class": "btn btn-xs btn-warning", "name": "告警"},
{"type":"button", "class": "btn btn-xs btn-danger", "name": "危险"}
]
]
})"_json;
LogStatus("`" + table.dump() + "`");
}
结合GetCommand()函数,构造状态栏按钮交互功能:
javascript
function test1() {
Log("调用自定义函数")
}
function main() {
while (true) {
var table = {
type: 'table',
title: '操作',
cols: ['列1', '列2', 'Action'],
rows: [
['a', '1', {
'type': 'button',
'cmd': "CoverAll",
'name': '平仓'
}],
['b', '1', {
'type': 'button',
'cmd': 10,
'name': '发送数值'
}],
['c', '1', {
'type': 'button',
'cmd': _D(),
'name': '调用函数'
}],
['d', '1', {
'type': 'button',
'cmd': 'test1',
'name': '调用自定义函数'
}]
]
}
LogStatus(_D(), "\n", '`' + JSON.stringify(table) + '`')
var str_cmd = GetCommand()
if (str_cmd) {
Log("接收到的交互数据 str_cmd:", "类型:", typeof(str_cmd), "值:", str_cmd)
if(str_cmd == "test1") {
test1()
}
}
Sleep(500)
}
}
python
import json
def test1():
Log("调用自定义函数")
def main():
while True:
table = {
"type": "table",
"title": "操作",
"cols": ["列1", "列2", "Action"],
"rows": [
["a", "1", {
"type": "button",
"cmd": "CoverAll",
"name": "平仓"
}],
["b", "1", {
"type": "button",
"cmd": 10,
"name": "发送数值"
}],
["c", "1", {
"type": "button",
"cmd": _D(),
"name": "调用函数"
}],
["d", "1", {
"type": "button",
"cmd": "test1",
"name": "调用自定义函数"
}]
]
}
LogStatus(_D(), "\n", "`" + json.dumps(table) + "`")
str_cmd = GetCommand()
if str_cmd:
Log("接收到的交互数据 str_cmd", "类型:", type(str_cmd), "值:", str_cmd)
if str_cmd == "test1":
test1()
Sleep(500)
c++
void test1() {
Log("调用自定义函数");
}
void main() {
while(true) {
json table = R"({
"type": "table",
"title": "操作",
"cols": ["列1", "列2", "Action"],
"rows": [
["a", "1", {
"type": "button",
"cmd": "CoverAll",
"name": "平仓"
}],
["b", "1", {
"type": "button",
"cmd": 10,
"name": "发送数值"
}],
["c", "1", {
"type": "button",
"cmd": "",
"name": "调用函数"
}],
["d", "1", {
"type": "button",
"cmd": "test1",
"name": "调用自定义函数"
}]
]
})"_json;
table["rows"][2][2]["cmd"] = _D();
LogStatus(_D(), "\n", "`" + table.dump() + "`");
auto str_cmd = GetCommand();
if(str_cmd != "") {
Log("接收到的交互数据 str_cmd", "类型:", typeid(str_cmd).name(), "值:", str_cmd);
if(str_cmd == "test1") {
test1();
}
}
Sleep(500);
}
}
在构造状态栏按钮进行交互时也支持输入数据,交互指令最终由GetCommand()函数捕获。
给状态栏中的按钮控件的数据结构中增加input项,例如给{"type": "button", "cmd": "open", "name": "开仓"}增加"input": {"name": "开仓数量", "type": "number", "defValue": 1},就可以使按钮在被点击时弹出一个带输入框控件的弹窗(输入框中默认值为1,即defValue设置的数据),可以输入一个数据和按钮命令一起发送。例如以下测试代码运行时,在点击「开仓」按钮后,弹出一个带输入框的弹窗,在输入框中输入111后点击「确定」。GetCommand函数就会捕获消息:open:111。
javascript
function main() {
var tbl = {
type: "table",
title: "操作",
cols: ["列1", "列2"],
rows: [
["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
]
}
LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")
while (true) {
var cmd = GetCommand()
if (cmd) {
Log("cmd:", cmd)
}
Sleep(1000)
}
}
python
import json
def main():
tbl = {
"type": "table",
"title": "操作",
"cols": ["列1", "列2"],
"rows": [
["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
]
}
LogStatus(_D(), "\n", "`" + json.dumps(tbl) + "`")
while True:
cmd = GetCommand()
if cmd:
Log("cmd:", cmd)
Sleep(1000)
c++
void main() {
json tbl = R"({
"type": "table",
"title": "操作",
"cols": ["列1", "列2"],
"rows": [
["开仓操作", {"type": "button", "cmd": "open", "name": "开仓", "input": {"name": "开仓数量", "type": "number", "defValue": 1}}],
["平仓操作", {"type": "button", "cmd": "coverAll", "name": "全部平仓"}]
]
})"_json;
LogStatus(_D(), "\n", "`" + tbl.dump() + "`");
while(true) {
auto cmd = GetCommand();
if(cmd != "") {
Log("cmd:", cmd);
}
Sleep(1000);
}
}
合并LogStatus(Msg)函数画出的表格内的单元格:
-
横向合并
javascriptfunction main() { var table = { type: 'table', title: '持仓操作', cols: ['列1', '列2', 'Action'], rows: [ ['abc', 'def', {'type':'button', 'cmd': 'coverAll', 'name': '平仓'}] ] } var ticker = exchange.GetTicker() // 添加一行数据,第一个和第二个单元格合并,并且输出ticker变量在合并后的单元格内 table.rows.push([{body : JSON.stringify(ticker), colspan : 2}, "abc"]) LogStatus('`' + JSON.stringify(table) + '`') }pythonimport json def main(): table = { "type" : "table", "title" : "持仓操作", "cols" : ["列1", "列2", "Action"], "rows" : [ ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}] ] } ticker = exchange.GetTicker() table["rows"].append([{"body": json.dumps(ticker), "colspan": 2}, "abc"]) LogStatus("`" + json.dumps(table) + "`")c++void main() { json table = R"({ "type" : "table", "title" : "持仓操作", "cols" : ["列1", "列2", "Action"], "rows" : [ ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}] ] })"_json; auto ticker = exchange.GetTicker(); json jsonTicker = R"({"Buy": 0, "Sell": 0, "High": 0, "Low": 0, "Volume": 0, "Last": 0, "Time": 0})"_json; jsonTicker["Buy"] = ticker.Buy; jsonTicker["Sell"] = ticker.Sell; jsonTicker["Last"] = ticker.Last; jsonTicker["Volume"] = ticker.Volume; jsonTicker["Time"] = ticker.Time; jsonTicker["High"] = ticker.High; jsonTicker["Low"] = ticker.Low; json arr = R"([{"body": {}, "colspan": 2}, "abc"])"_json; arr[0]["body"] = jsonTicker; table["rows"].push_back(arr); LogStatus("`" + table.dump() + "`"); }
-
纵向合并
javascriptfunction main() { var table = { type: 'table', title: '表格演示', cols: ['列A', '列B', '列C'], rows: [ ['A1', 'B1', {'type':'button', 'cmd': 'coverAll', 'name': 'C1'}] ] } var ticker = exchange.GetTicker() var name = exchange.GetName() table.rows.push([{body : "A2 + B2:" + JSON.stringify(ticker), colspan : 2}, "C2"]) table.rows.push([{body : "A3 + A4 + A5:" + name, rowspan : 3}, "B3", "C3"]) // A3被上一行第一个单元格合并 table.rows.push(["B4", "C4"]) // A2被上一行第一个单元格合并 table.rows.push(["B5", "C5"]) table.rows.push(["A6", "B6", "C6"]) LogStatus('`' + JSON.stringify(table) + '`') }pythonimport json def main(): table = { "type" : "table", "title" : "表格演示", "cols" : ["列A", "列B", "列C"], "rows" : [ ["A1", "B1", {"type": "button", "cmd": "coverAll", "name": "C1"}] ] } ticker = exchange.GetTicker() name = exchange.GetName() table["rows"].append([{"body": "A2 + B2:" + json.dumps(ticker), "colspan": 2}, "C2"]) table["rows"].append([{"body": "A3 + A4 + A5:" + name, "rowspan": 3}, "B3", "C3"]) table["rows"].append(["B4", "C4"]) table["rows"].append(["B5", "C5"]) table["rows"].append(["A6", "B6", "C6"]) LogStatus("`" + json.dumps(table) + "`")c++void main() { json table = R"({ "type" : "table", "title" : "表格演示", "cols" : ["列A", "列B", "列C"], "rows" : [ ["A1", "B1", {"type": "button", "cmd": "coverAll", "name": "C1"}] ] })"_json; // 为了测试,代码简短易读,这里使用构造的数据 json jsonTicker = R"({"High": 0, "Low": 0, "Buy": 0, "Sell": 0, "Last": 0, "Time": 0, "Volume": 0})"_json; auto name = exchange.GetName(); json arr1 = R"([{"body": "", "colspan": 2}, "C2"])"_json; arr1[0]["body"] = "A2 + B2:" + jsonTicker.dump(); json arr2 = R"([{"body": "", "rowspan": 3}, "B3", "C3"])"_json; arr2[0]["body"] = "A3 + A4 + A5:" + name; table["rows"].push_back(arr1); table["rows"].push_back(arr2); table["rows"].push_back(R"(["B4", "C4"])"_json); table["rows"].push_back(R"(["B5", "C5"])"_json); table["rows"].push_back(R"(["A6", "B6", "C6"])"_json); LogStatus("`" + table.dump() + "`"); }
状态栏表格分页显示:
javascript
function main() {
var table1 = {type: 'table', title: 'table1', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
var table2 = {type: 'table', title: 'table2', cols: ['列1', '列2'], rows: [ ['abc', 'def'], ['ABC', 'support color #ff0000']]}
LogStatus('`' + JSON.stringify([table1, table2]) + '`')
}
python
import json
def main():
table1 = {"type": "table", "title": "table1", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]}
table2 = {"type": "table", "title": "table2", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]}
LogStatus("`" + json.dumps([table1, table2]) + "`")
c++
void main() {
json table1 = R"({"type": "table", "title": "table1", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
json table2 = R"({"type": "table", "title": "table2", "cols": ["列1", "列2"], "rows": [ ["abc", "def"], ["ABC", "support color #ff0000"]]})"_json;
json arr = R"([])"_json;
arr.push_back(table1);
arr.push_back(table2);
LogStatus("`" + arr.dump() + "`");
}
除了可以分页显示表格,也可以多个表格自上而下排列显示。
javascript
function main(){
var tab1 = {
type : "table",
title : "表格1",
cols : ["1", "2"],
rows : []
}
var tab2 = {
type : "table",
title : "表格2",
cols : ["1", "2", "3"],
rows : []
}
var tab3 = {
type : "table",
title : "表格3",
cols : ["A", "B", "C"],
rows : []
}
tab1.rows.push(["jack", "lucy"])
tab2.rows.push(["A", "B", "C"])
tab3.rows.push(["A", "B", "C"])
LogStatus('`' + JSON.stringify(tab1) + '`\n' +
'`' + JSON.stringify(tab2) + '`\n' +
'`' + JSON.stringify(tab3) + '`')
Log("exit")
}
python
import json
def main():
tab1 = {
"type": "table",
"title": "表格1",
"cols": ["1", "2"],
"rows": []
}
tab2 = {
"type": "table",
"title": "表格2",
"cols": ["1", "2", "3"],
"rows": []
}
tab3 = {
"type": "table",
"title": "表格3",
"cols": ["A", "B", "C"],
"rows": []
}
tab1["rows"].append(["jack", "lucy"])
tab2["rows"].append(["A", "B", "C"])
tab3["rows"].append(["A", "B", "C"])
LogStatus("`" + json.dumps(tab1) + "`\n" +
"`" + json.dumps(tab2) + "`\n" +
"`" + json.dumps(tab3) + "`")
c++
void main() {
json tab1 = R"({
"type": "table",
"title": "表格1",
"cols": ["1", "2"],
"rows": []
})"_json;
json tab2 = R"({
"type": "table",
"title": "表格2",
"cols": ["1", "2", "3"],
"rows": []
})"_json;
json tab3 = R"({
"type": "table",
"title": "表格3",
"cols": ["A", "B", "C"],
"rows": []
})"_json;
tab1["rows"].push_back(R"(["jack", "lucy"])"_json);
tab2["rows"].push_back(R"(["A", "B", "C"])"_json);
tab3["rows"].push_back(R"(["A", "B", "C"])"_json);
LogStatus("`" + tab1.dump() + "`\n" +
"`" + tab2.dump() + "`\n" +
"`" + tab3.dump() + "`");
}
运行效果:

注意:
策略实盘运行时,在实盘页面如果翻看历史记录,状态栏会进入休眠状态,停止更新。只有日志在第一页的时候状态栏数据才会刷新。
支持在状态栏输出base64编码后的图片,也支持在状态栏显示的表格中输出base64编码后的图片。由于编码后的图片的字符串数据一般很长,所以不再展示范例代码。
EnableLog()
EnableLog(IsEnable),打开或者关闭订单信息的日志记录。参数值:isEnable为布尔类型。IsEnable设置为false则不打印订单日志,不写入实盘数据库。
Chart(...)
Chart(...),自定义图表画图函数。
Chart({...}),参数为可以JSON序列化的HighStocks的Highcharts.StockChart参数。比原生的参数增加一个__isStock属性,如果指定__isStock:false,则显示为普通图表。
注意:
如果设置__isStock属性为false,即使用的图表为:Highcharts,如图所示:

如果设置__isStock属性为true,即使用的图表为:Highstocks(默认__isStock为true),如图所示:

返回对像可以调用add(n, data)(n为series索引(如0),data为写入图表的数据)向指定索引的series添加数据,调用reset()清空图表数据,reset可以带一个数字参数,指定保留的条数。
可以调用add(n, data, i)(i为此数据在series中的索引)来更改对应的series中的数据。
可以为负数,-1指最后一个,-2是倒数第二个,例如画线时,修改线的最后一个点上的数据:
chart.add(0, [1574993606000, 13.5], -1),更改series[0].data的倒数第一个点的数据。
支持显示多个图表,配置时只需传入数组参数即可如:var chart = Chart([{...}, {...}, {...}]),比如图表一有两个series,图表二有一个series,图表三有一个series,那么add时指定0与1序列ID代表更新图表1的两个序列的数据,add时指定序列ID为2指图表2的第一个series的数据, 指定序列3指的是图表3的第一个series的数据。
HighStocks:http://api.highcharts.com/highstock
多图表显示相关的一些属性设置:范例
例如图表配置对象:
javascript
var cfgA = {
extension: {
// 不参于分组,单独显示,默认为分组 'group'
layout: 'single',
// 指定高度,可以设置为字符串,"300px",设置数值300会自动替换为"300px"
height: 300,
// 指定宽度占的单元值,总值为12
col: 8
},
title: {
text: '盘口图表'
},
xAxis: {
type: 'datetime'
},
series: [{
name: '买一',
data: []
}, {
name: '卖一',
data: []
}]
}
var cfgB = {
title: {
text: '差价图'
},
xAxis: {
type: 'datetime'
},
series: [{
name: '差价',
type: 'column',
data: []
}]
}
var cfgC = {
__isStock: false,
title: {
text: '饼图'
},
series: [{
type: 'pie',
name: 'one',
// 指定初始数据后不需要用add函数更新,直接更改图表配置就可以更新序列
data: [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
}
var cfgD = {
extension: {
layout: 'single',
// 指定宽度占的单元值,总值为12
col: 8,
height: '300px'
},
title: {
text: '盘口图表'
},
xAxis: {
type: 'datetime'
},
series: [{
name: '买一',
data: []
}, {
name: '卖一',
data: []
}]
}
var cfgE = {
__isStock: false,
extension: {
layout: 'single',
col: 4,
height: '300px'
},
title: {
text: '饼图2'
},
series: [{
type: 'pie',
name: 'one',
data: [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
}
python
cfgA = {
"extension" : {
"layout" : "single",
"height" : 300,
"col" : 8
},
"title" : {
"text" : "盘口图表"
},
"xAxis" : {
"type" : "datetime"
},
"series" : [{
"name" : "买一",
"data" : []
}, {
"name" : "卖一",
"data" : []
}]
}
cfgB = {
"title" : {
"text" : "差价图"
},
"xAxis" : {
"type" : "datetime"
},
"series" : [{
"name" : "差价",
"type" : "column",
"data" : []
}]
}
cfgC = {
"__isStock" : False,
"title" : {
"text" : "饼图"
},
"series" : [{
"type" : "pie",
"name" : "one",
"data" : [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
}
cfgD = {
"extension" : {
"layout" : "single",
"col" : 8,
"height" : "300px"
},
"title" : {
"text" : "盘口图表"
},
"series" : [{
"name" : "买一",
"data" : []
}, {
"name" : "卖一",
"data" : []
}]
}
cfgE = {
"__isStock" : False,
"extension" : {
"layout" : "single",
"col" : 4,
"height" : "300px"
},
"title" : {
"text" : "饼图2"
},
"series" : [{
"type" : "pie",
"name" : "one",
"data" : [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
}
c++
json cfgA = R"({
"extension" : {
"layout" : "single",
"height" : 300,
"col" : 8
},
"title" : {
"text" : "盘口图表"
},
"xAxis" : {
"type" : "datetime"
},
"series" : [{
"name" : "买一",
"data" : []
}, {
"name" : "卖一",
"data" : []
}]
})"_json;
json cfgB = R"({
"title" : {
"text" : "差价图"
},
"xAxis" : {
"type" : "datetime"
},
"series" : [{
"name" : "差价",
"type" : "column",
"data" : []
}]
})"_json;
json cfgC = R"({
"__isStock" : false,
"title" : {
"text" : "饼图"
},
"series" : [{
"type" : "pie",
"name" : "one",
"data" : [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
})"_json;
json cfgD = R"({
"extension" : {
"layout" : "single",
"col" : 8,
"height" : "300px"
},
"title" : {
"text" : "盘口图表"
},
"series" : [{
"name" : "买一",
"data" : []
}, {
"name" : "卖一",
"data" : []
}]
})"_json;
json cfgE = R"({
"__isStock" : false,
"extension" : {
"layout" : "single",
"col" : 4,
"height" : "300px"
},
"title" : {
"text" : "饼图2"
},
"series" : [{
"type" : "pie",
"name" : "one",
"data" : [
["A", 25],
["B", 25],
["C", 25],
["D", 25]
]
}]
})"_json;
-
cfgA.extension.layout属性如果设置此属性,值为"single",则图表不会叠加(不会以分页标签方式显示),会单独显示(平铺显示)。
-
cfgA.extension.height属性此属性用于设置图表的高度,值可以为数值类型,或以"300px"方式设置。
-
cfgA.extension.col属性此属性用于设置图表的宽度,页面宽度一共划分为12个单元,设置8即该图表占用8个单元宽度。
运行完整范例策略:
以上范例中图表配置对象显示效果:


-
在图表配置对象上的数据,直接修改图表配置,然后更新图表即可实现数据更新:
例如范例中,
JavaScript范例部分代码段(完整范例):javascriptcfgC.series[0].data[0][1] = Math.random() * 100 cfgE.series[0].data[0][1] = Math.random() * 100 // update实际上等于重置了图表的配置 chart.update([cfgA, cfgB, cfgC, cfgD, cfgE])通过
add方法更新数据,例如给饼图增加一个项目,JavaScript范例部分代码段(完整范例):javascript// 为饼图清加一个数点,add只能更新通过add方式添加的数据点,内置的数据点无法后期更新 chart.add(3, { name: "ZZ", y: Math.random() * 100 }) -
附带使用
Chart函数的例子简单的画图例子:
javascript// 这个chart在JavaScript语言中是对象,在使用Chart函数之前我们需要声明一个配置图表的对象变量chart var chart = { // 标记是否为一般图表,有兴趣的可以改成false运行看看 __isStock: true, // 缩放工具 tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'}, // 标题 title : { text : '差价分析图'}, // 选择范围 rangeSelector: { buttons: [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}], selected: 0, inputEnabled: false }, // 坐标轴横轴即:x轴,当前设置的类型是:时间 xAxis: { type: 'datetime'}, // 坐标轴纵轴即:y轴,默认数值随数据大小调整 yAxis : { // 标题 title: {text: '差价'}, // 是否启用右边纵轴 opposite: false }, // 数据系列,该属性保存的是各个数据系列(线,K线图,标签等...) series : [ // 索引为0,data数组内存放的是该索引系列的数据 {name : "line1", id : "线1,buy1Price", data : []}, // 索引为1,设置了dashStyle:'shortdash'即:设置虚线 {name : "line2", id : "线2,lastPrice", dashStyle : 'shortdash', data : []} ] } function main(){ // 调用Chart函数,初始化图表 var ObjChart = Chart(chart) // 清空 ObjChart.reset() while(true){ // 判断连接期货公司前置机是否成功。股票证券无需使用exchange.IO("status")判断连接状态 if (!exchange.IO("status")) { Sleep(1000) continue } exchange.SetContractType("rb888") // 获取本次轮询的时间戳,即一个毫秒的时间戳。用来确定写入到图表的X轴的位置 var nowTime = new Date().getTime() // 获取行情数据 var ticker = _C(exchange.GetTicker) // 从行情数据的返回值取得买一价 var buy1Price = ticker.Buy // 取得最后成交价,为了2条线不重合在一起,我们加1 var lastPrice = ticker.Last + 1 // 用时间戳作为X值,买一价作为Y值传入索引0的数据序列 ObjChart.add(0, [nowTime, buy1Price]) // 同上 ObjChart.add(1, [nowTime, lastPrice]) Sleep(2000) } }pythonimport time chart = { "__isStock" : True, "tooltip" : {"xDateFormat" : "%Y-%m-%d %H:%M:%S, %A"}, "title" : {"text" : "差价分析图"}, "rangeSelector" : { "buttons" : [{"type": "count", "count": 1, "text": "1h"}, {"type": "hour", "count": 3, "text": "3h"}, {"type": "hour", "count": 8, "text": "8h"}, {"type": "all", "text": "All"}], "selected": 0, "inputEnabled": False }, "xAxis": {"type": "datetime"}, "yAxis": { "title": {"text": "差价"}, "opposite": False }, "series": [{ "name": "line1", "id": "线1,buy1Price", "data": [] }, { "name": "line2", "id": "线2,lastPrice", "dashStyle": "shortdash", "data": [] }] } def main(): ObjChart = Chart(chart) ObjChart.reset() while True: if not exchange.IO("status"): Sleep(1000) continue exchange.SetContractType("rb888") nowTime = time.time() * 1000 ticker = exchange.GetTicker() buy1Price = ticker["Buy"] lastPrice = ticker["Last"] + 1 ObjChart.add(0, [nowTime, buy1Price]) ObjChart.add(1, [nowTime, lastPrice]) Sleep(2000)c++void main() { // C++编写策略时,尽量不要声明非基础类型的全局变量,所以图表配置对象声明在main函数内 json chart = R"({ "__isStock" : true, "tooltip" : {"xDateFormat" : "%Y-%m-%d %H:%M:%S, %A"}, "title" : {"text" : "差价分析图"}, "rangeSelector" : { "buttons" : [{"type": "count", "count": 1, "text": "1h"}, {"type": "hour", "count": 3, "text": "3h"}, {"type": "hour", "count": 8, "text": "8h"}, {"type": "all", "text": "All"}], "selected": 0, "inputEnabled": false }, "xAxis": {"type": "datetime"}, "yAxis": { "title": {"text": "差价"}, "opposite": false }, "series": [{ "name": "line1", "id": "线1,buy1Price", "data": [] }, { "name": "line2", "id": "线2,lastPrice", "dashStyle": "shortdash", "data": [] }] })"_json; auto ObjChart = Chart(chart); ObjChart.reset(); while(true) { if (exchange.IO("status") == 0) { Sleep(1000); continue; } exchange.SetContractType("rb888"); auto nowTime = Unix() * 1000; auto ticker = exchange.GetTicker(); auto buy1Price = ticker.Buy; auto lastPrice = ticker.Last + 1.0; ObjChart.add(0, {nowTime, buy1Price}); ObjChart.add(1, {nowTime, lastPrice}); Sleep(2000); } }三角函数曲线画图例子:
javascript// 用于初始化图表的对象 var chart = { // 图表标题 title: {text: "line数值触发 plotLines 值"}, // Y轴相关设置 yAxis: { // 垂直于Y轴的水平线,用作触发线,是一个结构数组,可以设置多条触发线 plotLines: [{ // 触发线的值,设置多少这条线就在相应的数值位置显示 value: 0, // 设置触发线的颜色 color: 'red', // 宽度 width: 2, // 显示的标签 label: { // 标签文本 text: '触发值', // 标签位置居中 align: 'center' } }] }, // X轴相关设置,这里设置类型是时间轴 xAxis: {type: "datetime"}, series: [ {name: "sin", type: "spline", data: []}, // 这个是比较重要的数据系列,可以设置多个数据系列,根据数组索引控制 {name: "cos", type: "spline", data: []} ] } function main(){ // 圆周率 var pi = 3.1415926535897 // 用于记录时间戳的变量 var time = 0 // 角度 var angle = 0 // 坐标y值,用于接收正弦值、余弦值 var y = 0 // 调用API接口用chart对象初始化图表 var objChart = Chart(chart) // 初始清空图表 objChart.reset() // 设置触发线的值为1 chart.yAxis.plotLines[0].value = 1 // 循环 while(true){ // 获取当前时刻的时间戳 time = new Date().getTime() // 每500ms角度angle增加5度,计算正弦值 y = Math.sin(angle * 2 * pi / 360) // 把计算出来的y值写入图表相应索引的数据系列add函数第一个参数为指定的索引 objChart.add(0, [time, y]) // 计算余弦值 y = Math.cos(angle * 2 * pi / 360) objChart.add(1, [time, y]) // 增加5度 angle += 5 // 暂停5秒,以免画图太频繁,数据增长过快 Sleep(5000) } }pythonimport math import time chart = { "title": {"text": "line数值触发 plotLines 值"}, "yAxis": { "plotLines": [{ "value": 0, "color": "red", "width": 2, "label": { "text": "触发值", "align": "center" } }] }, "xAxis": {"type": "datetime"}, "series": [{"name": "sin", "type": "spline", "data": []}, {"name": "cos", "type": "spline", "data": []}] } def main(): pi = 3.1415926535897 ts = 0 angle = 0 y = 0 objChart = Chart(chart) objChart.reset() chart["yAxis"]["plotLines"][0]["value"] = 1 while True: ts = time.time() * 1000 y = math.sin(angle * 2 * pi / 360) objChart.add(0, [ts, y]) y = math.cos(angle * 2 * pi / 360) objChart.add(1, [ts, y]) angle += 5 Sleep(5000)c++void main() { json chart = R"({ "title": {"text": "line数值触发 plotLines 值"}, "yAxis": { "plotLines": [{ "value": 0, "color": "red", "width": 2, "label": { "text": "触发值", "align": "center" } }] }, "xAxis": {"type": "datetime"}, "series": [{"name": "sin", "type": "spline", "data": []}, {"name": "cos", "type": "spline", "data": []}] })"_json; auto pi = 3.1415926535897; auto ts = 0; auto angle = 0.0; auto y = 0.0; auto objChart = Chart(chart); objChart.reset(); chart["yAxis"]["plotLines"][0]["value"] = 1; while(true) { ts = Unix() * 1000; y = sin(angle * 2 * pi / 360); objChart.add(0, {ts, y}); y = cos(angle * 2 * pi / 360); objChart.add(1, {ts, y}); angle += 5; Sleep(5000); } }C++策略中Chart函数也可以使用编码过的字符串配置图表:c++void main () { Chart c = Chart(R"EOF({"chart":{"type":"line"},"title":{"text":"简单图表"},"xAxis":{"title":{"text":"Date"}},"yAxis":{"title":{"text":"Number"}},"series":[{"name":"number","data":[]}]})EOF"); c.reset(); for (size_t i = 0; i < 10; i++) { // sprintf比如int64在32位与64位下有不同的参数, 所以最好跟平台有关的类型用toString变成字符串再传进去 c.add(0, format("[%s, %d]", toString(Unix() + i).c_str(), rand() % 100)); } }
-
使用混合图表的复杂例子
JavaScript策略地址
javascript
/*backtest
start: 2020-03-11 00:00:00
end: 2020-04-09 23:59:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
var chartCfg = {
subtitle: {
text: "subtitle",
},
yAxis: [{
height: "40%",
lineWidth: 2,
title: {
text: 'PnL',
},
tickPixelInterval: 20,
minorGridLineWidth: 1,
minorTickWidth: 0,
opposite: true,
labels: {
align: "right",
x: -3,
}
}, {
title: {
text: 'Profit',
},
top: "42%",
height: "18%",
offset: 0,
lineWidth: 2
}, {
title: {
text: 'Vol',
},
top: '62%',
height: '18%',
offset: 0,
lineWidth: 2
}, {
title: {
text: 'Asset',
},
top: '82%',
height: '18%',
offset: 0,
lineWidth: 2
}],
series: [{
name: 'PnL',
data: [],
id: 'primary',
tooltip: {
xDateFormat: '%Y-%m-%d %H:%M:%S'
},
yAxis: 0
}, {
type: 'column',
lineWidth: 2,
name: 'Profit',
data: [],
yAxis: 1,
}, {
type: 'column',
name: 'Trade',
data: [],
yAxis: 2
}, {
type: 'area',
step: true,
lineWidth: 0,
name: 'Long',
data: [],
yAxis: 2
}, {
type: 'area',
step: true,
lineWidth: 0,
name: 'Short',
data: [],
yAxis: 2
}, {
type: 'line',
step: true,
color: '#5b4b00',
name: 'Asset',
data: [],
yAxis: 3
}, {
type: 'pie',
innerSize: '70%',
name: 'Random',
data: [],
center: ['3%', '6%'],
size: '15%',
dataLabels: {
enabled: false
},
startAngle: -90,
endAngle: 90,
}],
};
function main() {
let c = Chart(chartCfg);
let preTicker = null;
while (true) {
if (!exchange.IO("status")) {
Sleep(1000)
continue
}
exchange.SetContractType("rb888")
let t = exchange.GetTicker();
c.add(0, [t.Time, t.Last]); // PnL
c.add(1, [t.Time, preTicker ? t.Last - preTicker.Last : 0]); // profit
let r = Math.random();
var pos = parseInt(t.Time/86400);
c.add(2, [t.Time, pos/2]); // Vol
c.add(3, [t.Time, r > 0.8 ? pos : null]); // Long
c.add(4, [t.Time, r < 0.8 ? -pos : null]); // Short
c.add(5, [t.Time, Math.random() * 100]); // Asset
// update pie
chartCfg.series[chartCfg.series.length-1].data = [
["A", Math.random()*100],
["B", Math.random()*100],
];
c.update(chartCfg)
preTicker = t;
}
}
python
'''backtest
start: 2020-03-11 00:00:00
end: 2020-04-09 23:59:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''
import random
chartCfg = {
"subtitle": {
"text": "subtitle"
},
"yAxis": [{
"height": "40%",
"lineWidth": 2,
"title": {
"text": 'PnL'
},
"tickPixelInterval": 20,
"minorGridLineWidth": 1,
"minorTickWidth": 0,
"opposite": True,
"labels": {
"align": "right",
"x": -3
}
}, {
"title": {
"text": 'Profit'
},
"top": "42%",
"height": "18%",
"offset": 0,
"lineWidth": 2
}, {
"title": {
"text": 'Vol'
},
"top": '62%',
"height": '18%',
"offset": 0,
"lineWidth": 2
}, {
"title": {
"text": 'Asset'
},
"top": '82%',
"height": '18%',
"offset": 0,
"lineWidth": 2
}],
"series": [{
"name": 'PnL',
"data": [],
"id": 'primary',
"tooltip": {
"xDateFormat": '%Y-%m-%d %H:%M:%S'
},
"yAxis": 0
}, {
"type": 'column',
"lineWidth": 2,
"name": 'Profit',
"data": [],
"yAxis": 1
}, {
"type": 'column',
"name": 'Trade',
"data": [],
"yAxis": 2
}, {
"type": 'area',
"step": True,
"lineWidth": 0,
"name": 'Long',
"data": [],
"yAxis": 2
}, {
"type": 'area',
"step": True,
"lineWidth": 0,
"name": 'Short',
"data": [],
"yAxis": 2
}, {
"type": 'line',
"step": True,
"color": '#5b4b00',
"name": 'Asset',
"data": [],
"yAxis": 3
}, {
"type": 'pie',
"innerSize": '70%',
"name": 'Random',
"data": [],
"center": ['3%', '6%'],
"size": '15%',
"dataLabels": {
"enabled": False
},
"startAngle": -90,
"endAngle": 90
}]
}
def main():
c = Chart(chartCfg)
preTicker = None
while True:
if not exchange.IO("status"):
Sleep(1000)
continue
exchange.SetContractType("rb888")
t = exchange.GetTicker()
c.add(0, [t["Time"], t["Last"]])
profit = t["Last"] - preTicker["Last"] if preTicker else 0
c.add(1, [t["Time"], profit])
r = random.random()
pos = t["Time"] / 86400
c.add(2, [t["Time"], pos / 2])
long = pos if r > 0.8 else None
c.add(3, [t["Time"], long])
short = -pos if r < 0.8 else None
c.add(4, [t["Time"], short])
c.add(5, [t["Time"], random.random() * 100])
# update pie
chartCfg["series"][len(chartCfg["series"]) - 1]["data"] = [
["A", random.random() * 100],
["B", random.random() * 100]
]
c.update(chartCfg)
preTicker = t
c++
/*backtest
start: 2020-03-11 00:00:00
end: 2020-04-09 23:59:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
void main() {
json chartCfg = R"({
"subtitle": {
"text": "subtitle"
},
"yAxis": [{
"height": "40%",
"lineWidth": 2,
"title": {
"text": "PnL"
},
"tickPixelInterval": 20,
"minorGridLineWidth": 1,
"minorTickWidth": 0,
"opposite": true,
"labels": {
"align": "right",
"x": -3
}
}, {
"title": {
"text": "Profit"
},
"top": "42%",
"height": "18%",
"offset": 0,
"lineWidth": 2
}, {
"title": {
"text": "Vol"
},
"top": "62%",
"height": "18%",
"offset": 0,
"lineWidth": 2
}, {
"title": {
"text": "Asset"
},
"top": "82%",
"height": "18%",
"offset": 0,
"lineWidth": 2
}],
"series": [{
"name": "PnL",
"data": [],
"id": "primary",
"tooltip": {
"xDateFormat": "%Y-%m-%d %H:%M:%S"
},
"yAxis": 0
}, {
"type": "column",
"lineWidth": 2,
"name": "Profit",
"data": [],
"yAxis": 1
}, {
"type": "column",
"name": "Trade",
"data": [],
"yAxis": 2
}, {
"type": "area",
"step": true,
"lineWidth": 0,
"name": "Long",
"data": [],
"yAxis": 2
}, {
"type": "area",
"step": true,
"lineWidth": 0,
"name": "Short",
"data": [],
"yAxis": 2
}, {
"type": "line",
"step": true,
"color": "#5b4b00",
"name": "Asset",
"data": [],
"yAxis": 3
}, {
"type": "pie",
"innerSize": "70%",
"name": "Random",
"data": [],
"center": ["3%", "6%"],
"size": "15%",
"dataLabels": {
"enabled": false
},
"startAngle": -90,
"endAngle": 90
}]
})"_json;
Chart c = Chart(chartCfg);
Ticker preTicker;
while(true) {
if (exchange.IO("status") != 0) {
Sleep(1000);
continue;
}
exchange.SetContractType("rb888");
auto t = exchange.GetTicker();
c.add(0, {t.Time, t.Last});
auto profit = preTicker.Valid ? t.Last - preTicker.Last : 0;
c.add(1, {t.Time, profit});
auto r = rand() % 100;
auto pos = t.Time / 86400.0;
c.add(2, {t.Time, pos / 2.0});
auto longPos = r > 0.8 ? pos : NULL;
c.add(3, {t.Time, longPos});
auto shortPos = r < 0.8 ? -pos : NULL;
c.add(4, {t.Time, shortPos});
c.add(5, {t.Time, rand() % 100});
// update pie
json pie = R"([["A", 0], ["B", 0]])"_json;
pie[0][1] = rand() % 100;
pie[1][1] = rand() % 100;
chartCfg["series"][chartCfg["series"].size() - 1]["data"] = pie;
c.update(chartCfg);
preTicker = t;
}
}
图表中pie类型的图是没有时间轴的图表,在更新数据时需要直接更新图表配置。例如以上范例中的代码,更新数据后使用c.update(chartCfg)更新图表,如下所示:
javascript
// update pie
chartCfg.series[chartCfg.series.length-1].data = [
["A", Math.random()*100],
["B", Math.random()*100],
];
c.update(chartCfg)
python
# update pie
chartCfg["series"][len(chartCfg["series"]) - 1]["data"] = [
["A", random.random() * 100],
["B", random.random() * 100]
]
c.update(chartCfg)
c++
// update pie
json pie = R"([["A", 0], ["B", 0]])"_json;
pie[0][1] = rand() % 100;
pie[1][1] = rand() % 100;
chartCfg["series"][chartCfg["series"].size() - 1]["data"] = pie;
c.update(chartCfg);
运行结果:
KLineChart(...)
KLineChart(chartCfg),该函数用于使用类似Pine语言的画图方式进行策略运行时的自定义画图,策略自定义画图只能使用KLineChart()方式或者Chart()方式中的一种。
参考代码:
javascript
function main() {
// 调用KLineChart函数创建图表控制对象c
let c = KLineChart({
overlay: true
})
while(true) {
if (exchange.IO("status")) {
break
}
Sleep(1000)
}
exchange.SetContractType("rb888")
// 获取K线
let bars = exchange.GetRecords()
if (!bars) {
return
}
bars.forEach(function(bar, index) {
c.begin(bar)
c.barcolor(bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.2)')
if (bar.Close > bar.Open) {
c.bgcolor('rgba(0, 255, 0, 0.5)')
}
let h = c.plot(bar.High, 'high')
let l = c.plot(bar.Low, 'low')
c.fill(h, l, {
color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)'
})
c.hline(bar.High)
c.plotarrow(bar.Close - bar.Open)
c.plotshape(bar.Low, {
style: 'diamond'
})
c.plotchar(bar.Close, {
char: 'X'
})
c.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)
if (bar.Close > bar.Open) {
// long/short/closelong/closeshort
c.signal("long", bar.High, 1.5)
} else if (bar.Close < bar.Open) {
c.signal("closelong", bar.Low, 1.5)
}
c.close()
})
}
python
def main():
# 调用KLineChart函数创建图表控制对象c
c = KLineChart({
"overlay": True
})
while True:
if exchange.IO("status"):
break
Sleep(1000)
exchange.SetContractType("rb888")
# 获取K线
bars = exchange.GetRecords()
if not bars:
return
for bar in bars:
c.begin(bar)
c.barcolor('rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(0, 0, 0, 0.2)')
if bar.Close > bar.Open:
c.bgcolor('rgba(0, 255, 0, 0.5)')
h = c.plot(bar.High, 'high')
l = c.plot(bar.Low, 'low')
c.fill(h, l, 'rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(255, 0, 0, 0.2)')
c.hline(bar.High)
c.plotarrow(bar.Close - bar.Open)
c.plotshape(bar.Low, style = 'diamond')
c.plotchar(bar.Close, char = 'X')
c.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)
if bar.Close > bar.Open:
# long/short/closelong/closeshort
c.signal("long", bar.High, 1.5)
elif bar.Close < bar.Open:
c.signal("closelong", bar.Low, 1.5)
c.close()
c++
// 暂不支持
如果在策略自定义画图区域画图必须有图表控制对象,使用函数KLineChart创建该对象。KLineChart函数的参数为一个图表配置结构,在参考代码中使用的图表结构很简单{overlay: true})。这个图表配置结构仅仅设置了画图内容在图表主图上输出,如果overlay设置为false则图表上的内容都输出在副图上,如果需要指定某个画图函数在主图上画出也可以在具体函数调用中指定参数overlay为true。
在K线数据上遍历执行画图操作,画图操作中必须以c.begin(bar)函数调用作为起始,以c.close()函数调用作为结束。画图操作中支持的Pine语言的画图接口函数有:
-
barcolor:设置K线颜色
barcolor(color, offset, editable, show_last, title, display)
javascriptc.barcolor(bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(0, 0, 0, 0.2)') // 使用本例中参考代码中的例子说明,不再赘述pythonc.barcolor('rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(0, 0, 0, 0.2)')display参数可选:"none", "all"
-
bgcolor:用指定颜色填充K线的背景
bgcolor(color, offset, editable, show_last, title, display, overlay)
javascriptc.bgcolor('rgba(0, 255, 0, 0.5)')pythonc.bgcolor('rgba(0, 255, 0, 0.5)')display参数可选:"none", "all"
-
plot:在图表上绘制一系列数据
plot(series, title, color, linewidth, style, trackprice, histbase, offset, join, editable, show_last, display)
javascriptc.plot(bar.High, 'high')pythonh = c.plot(bar.High, 'high')style参数可选:"stepline_diamond", "stepline", "cross", "areabr", "area", "circles", "columns", "histogram", "linebr", "line"display参数可选:"none", "all"
-
fill:使用提供的颜色填充两个绘图或hline之间的背景
fill(hline1, hline2, color, title, editable, fillgaps, display)
javascriptlet h = c.plot(bar.High, 'high') let l = c.plot(bar.Low, 'low') c.fill(h, l, {color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)'})pythonh = c.plot(bar.High, 'high') l = c.plot(bar.Low, 'low') c.fill(h, l, {"color": 'rgba(255, 0, 0, 0.2)' if bar.Close > bar.Open else 'rgba(255, 0, 0, 0.2)'})display参数可选:"none", "all"
由于
JavaScript语言不能根据函数形参名称指定传入参数,为了解决这个问题可以使用一个{key: value}结构指定传入某个形参名称的参数,例如参考代码中使用{color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)'}指定fill函数的color参数。如果需要连续指定多个形参名称的参数,可以使用{key1: value1, key2: value2, key3: value3}。例如本例子中增加指定一个title参数:{color: bar.Close > bar.Open ? 'rgba(255, 0, 0, 0.2)' : 'rgba(255, 0, 0, 0.2)', title: 'fill'}。对于颜色值可以使用
'rgba(255, 0, 0, 0.2)'方式设置,也可以使用'#FF0000'方式设置。 -
hline:在给定的固定价格水平上呈现水平线
hline(price, title, color, linestyle, linewidth, editable, display)
javascriptc.hline(bar.High)pythonc.hline(bar.High)linestyle参数可选:"dashed", "dotted", "solid"display参数可选:"none", "all"
-
plotarrow:在图表上绘制向上和向下箭头
plotarrow(series, title, colorup, colordown, offset, minheight, maxheight, editable, show_last, display)
javascriptc.plotarrow(bar.Close - bar.Open)pythonc.plotarrow(bar.Close - bar.Open)display参数可选:"none", "all"
-
plotshape:在图表上绘制可视形状
plotshape(series, title, style, location, color, offset, text, textcolor, editable, size, show_last, display)
javascriptc.plotshape(bar.Low, {style: 'diamond'})pythonc.plotshape(bar.Low, style = 'diamond')-
style参数可选:"diamond", "square", "label_down", "label_up", "arrow_down", "arrow_up", "circle", "flag", "triangle_down", "triangle_up", "cross", "xcross" -
location参数可选:"abovebar", "belowbar", "top", "bottom", "absolute" -
size参数可选:"10px", "14px", "20px", "40px", "80px",对比Pine语言中的size.tiny、size.small、size.normal、size.large、size.huge
size.auto即size.small。 -
display参数可选:"none", "all"
-
-
plotchar:在图表上使用任何给定的Unicode字符绘制可视形状
plotchar(series, title, char, location, color, offset, text, textcolor, editable, size, show_last, display)
javascriptc.plotchar(bar.Close, {char: 'X'})pythonc.plotchar(bar.Close, char = 'X')-
location参数可选:"abovebar", "belowbar", "top", "bottom", "absolute" -
size参数可选:"10px", "14px", "20px", "40px", "80px",对比Pine语言中的size.tiny、size.small、size.normal、size.large、size.huge
size.auto即size.small。 -
display参数可选:"none", "all"
-
-
plotcandle:在图表上绘制K线图
plotcandle(open, high, low, close, title, color, wickcolor, editable, show_last, bordercolor, display)
javascriptc.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)pythonc.plotcandle(bar.Open*0.9, bar.High*0.9, bar.Low*0.9, bar.Close*0.9)display参数可选:"none", "all"
-
signal:Pine语言上没有的函数,这里用来画买卖信号
signal(direction, price, qty, id)
javascriptc.signal("long", bar.High, 1.5)pythonc.signal("long", bar.High, 1.5)传入的参数
"long"表示交易方向,可选"long"、"closelong"、"short"、"closeshort"。传入的参数bar.High为标记信号的Y轴位置。传入的参数1.5表示信号的交易数量。可以传入第四个参数用来替换默认画出的文本内容,画出的信号标记默认文本为交易方向,例如:"closelong"。
以上函数调用时用到的一些颜色、样式等设置,参看使用KLineChart函数画图的专题文章
LogReset()
LogReset(),该函数用于清除日志。可以传入一个整数数值参数,指定保留最近日志条数,清除其余日志。每次启动的启动日志算一条,所以如果不传入参数并且策略起始时没有日志输出,就会完全不显示日志,等待托管者日志回传(并非异常情况)。该函数无返回值。
javascript
function main() {
// 保留最近10条日志,清除其余日志
LogReset(10)
}
python
def main():
LogReset(10)
c++
void main() {
LogReset(10);
}
LogVacuum()
LogVacuum(),在调用LogReset()函数清除日志后,回收SQLite删除数据时占用的储存空间。该函数无返回值。
原因是SQLite删除数据时不回收占用的空间,需要执行VACUUM清理表,释放空间。该函数调用时会发生文件移动操作,延迟较大,建议在合适的时间间隔调用。
行情API
主要的行情接口函数:
| 函数名 | 说明 |
|---|---|
| GetTicker | 获取tick行情数据 |
| GetRecords | 获取K线数据 |
| GetDepth | 获取订单薄数据(订单深度数据) |
| GetTrades | 获取市场上最近交易记录 |
以下函数均可通过exchange或exchanges[0]对象调用,例如:exchange.GetTicker()或exchanges[0].GetTicker(),函数返回当前交易对、设置合约的市场行情数据。
带有网络访问的API函数调用的重要提示:
在调用任何访问交易所接口的API函数时(如exchange.GetTicker()、exchange.Buy(Price, Amount)、exchange.CancelOrder(Id)等)都有可能由于各种原因导致访问失败。所以要对这些函数的调用做容错处理,举例:exchange.GetTicker()获取行情数据函数可能由于交易所服务器问题、网络通信问题等原因导致exchange.GetTicker()函数返回的值为null。这时就要对exchange.GetTicker()返回的值做容错处理。
如下代码exchange.GetTicker()函数返回的数据赋值给ticker变量,我们在使用ticker这个变量之前就需要对其容错处理。
javascript
function main() {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
Log(exchange.SetContractType("rb888"))
var ticker = exchange.GetTicker()
if(!ticker){
// 重新调用一次,或者其它处理逻辑
ticker = exchange.GetTicker()
}
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
Log(exchange.SetContractType("rb888"))
ticker = exchange.GetTicker()
if not ticker:
ticker = exchange.GetTicker()
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
Log(exchange.SetContractType("rb888"));
auto ticker = exchange.GetTicker();
if(!ticker.Valid) {
ticker = exchange.GetTicker();
Log("测试");
}
}
另外对于策略的容错性能测试,优宽专门在回测中增加了独有的容错模式回测。回测系统可以根据设置的参数随机给一些实盘时会发生网络访问的API接口返回一些失败调用时的返回值,可以快速的检测程序在实盘中的健壮性。
在策略编辑页面切换到回测系统页面,点击「开始回测」按钮右侧的倒三角下拉控件,会弹出「容错模式回测」按钮。
exchange.GetTicker()
exchange.GetTicker(),获取当前交易对、合约对应的市场当前行情,返回值:Ticker结构体。
回测系统中exchange.GetTicker()函数返回的Ticker数据,其中High、Low为模拟值,取自当时盘口的卖一、买一。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
Log(exchange.SetContractType("rb888"))
var ticker = exchange.GetTicker()
Log("High:", ticker.High, "Low:", ticker.Low, "Sell:", ticker.Sell, "Buy:", ticker.Buy, "Last:", ticker.Last, "Volume:", ticker.Volume)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
Log(exchange.SetContractType("rb888"))
ticker = exchange.GetTicker()
Log("High:", ticker["High"], "Low:", ticker["Low"], "Sell:", ticker["Sell"], "Buy:", ticker["Buy"], "Last:", ticker["Last"], "Volume:", ticker["Volume"])
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
Log(exchange.SetContractType("rb888"));
auto ticker = exchange.GetTicker();
Log("High:", ticker.High, "Low:", ticker.Low, "Sell:", ticker.Sell, "Buy:", ticker.Buy, "Last:", ticker.Last, "Volume:", ticker.Volume);
}
商品期货策略实盘中如果没有行情推送过来时,exchange.GetTicker()函数会阻塞,等待行情推送。
exchange.GetDepth()、exchange.GetTrades()、exchange.GetRecords()同理。如果不希望阻塞可以使用切换行情模式:
-
exchange.IO("mode", 0)
立即返回模式,如果当前还没有接收到交易所最新的行情数据推送,就立即返回旧的行情数据,如果有新的数据就返回新的数据。 -
exchange.IO("mode", 1)
缓存模式(默认模式),如果当前还没有收到交易所最新的行情数据(同上一次接口获取的数据比较),就等待接收然后再返回,如果调用该函数之前收到了最新的行情数据,就立即返回最新的数据。 -
exchange.IO("mode", 2)
强制更新模式,进入等待一直到接收到交易所下一次的最新推送数据后返回。
实盘时(非回测)exchange.GetTicker()函数的返回值中Info属性储存接口调用时返回的原始数据。
-
商品期货
返回商品期货CTP协议/易盛协议接口应答数据。以
CTP协议为例:javascript{ "BidPrice4": 1.7976931348623157e+308, "AskVolume4": 0, "AskVolume5": 0, "Turnover": 26229625880, "OpenInterest": 1364847, // 持仓量 "ClosePrice": 1.7976931348623157e+308, "LowerLimitPrice": 3473, "BidPrice3": 1.7976931348623157e+308, "ExchangeID": "", "BidPrice2": 1.7976931348623157e+308, "BidPrice5": 1.7976931348623157e+308, "AveragePrice": 37323.89130239898, "BidVolume4": 0, "BidVolume5": 0, "ExchangeInstID": "", "LowestPrice": 3715, "Volume": 702757, "BidVolume3": 0, "AskPrice3": 1.7976931348623157e+308, "AskVolume3": 0, "ActionDay": "20200714", "PreClosePrice": 3739, "SettlementPrice": 1.7976931348623157e+308, "UpdateTime": "13:40:01", "BidPrice1": 3727, "AskPrice2": 1.7976931348623157e+308, "UpperLimitPrice": 3996, "CurrDelta": 1.7976931348623157e+308, "UpdateMillisec": 500, "AskVolume1": 154, "BidVolume2": 0, "PreOpenInterest": 1372843, "PreDelta": 0, "AskPrice1": 3728, "AskVolume2": 0, "TradingDay": "20200714", "InstrumentID": "rb2010", "LastPrice": 3727, "HighestPrice": 3749, "BidVolume1": 444, "PreSettlementPrice": 3735, "OpenPrice": 3740, "AskPrice4": 1.7976931348623157e+308, "AskPrice5": 1.7976931348623157e+308 }
exchange.GetDepth()
exchange.GetDepth(),获取当前交易对、合约对应的市场的订单薄数据,返回值:Depth结构体。
Depth结构体包含两个结构体数组,分别是Asks[]和Bids[],Asks和Bids包含以下结构体变量:
| 数据类型 | 变量名 | 说明 |
|---|---|---|
| number | Price | 价格 |
| number | Amount | 数量 |
例如我想获取当前卖一价,可以这么写代码:
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
Log(exchange.SetContractType("rb888"))
var depth = exchange.GetDepth()
var price = depth.Asks[0].Price
Log("卖一价为:", price)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
Log(exchange.SetContractType("rb888"))
depth = exchange.GetDepth()
price = depth["Asks"][0]["Price"]
Log("卖一价为:", price)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
Log(exchange.SetContractType("rb888"));
auto depth = exchange.GetDepth();
auto price = depth.Asks[0].Price;
Log("卖一价为:", price);
}
商品期货实盘时需要注意:
涨停时卖单卖一的价格是涨停价格,订单量是0。跌停时买单买一的价格是跌停价格,订单量是0。通过判断买一、卖一订单数据中的订单量,可以判断出是否涨跌停。
exchange.GetTrades()
商品期货、股票证券不支持该函数。商品期货可以通过算法根据行情数据推算出市场成交记录。
策略例子:
exchange.GetRecords()
exchange.GetRecords(Period),返回K线数据。K线周期在创建实盘时指定,如果在调用exchange.GetRecords()函数时指定了参数,获取的就是该参数周期对应的K线数据。如果函数调用时没有指定参数,按照实盘参数上设置的K线周期或者回测页面设置的K线周期返回对应的K线数据。
参数Period:
- PERIOD_M1:指1分钟
- PERIOD_M5:指5分钟
- PERIOD_M15:指15分钟
- PERIOD_M30:指30分钟
- PERIOD_H1:指1小时
- PERIOD_D1:指一天
参数Period的值除了可以传以上定义的标准周期,还可以传入数值,单位为秒。K线周期设置1分钟、15分钟、1天时为直接获取K线数据,其它周期则使用次一级别周期的K线数据合成。
exchange.GetRecords(Period)函数的返回值:
返回值为Record结构体数组,返回的K线数据会随时间累积,累积K线柱数量的上限受到exchange.SetMaxBarLen函数设置的影响,没有设置过时默认上限为5000个K线柱。当K线数据到达K线柱累积上限,之后会更新加入一根K线柱的同时删除一根最早时间的K线柱(如队列进出)。
初始调用GetRecords函数时获取的K线柱数量:
- 回测系统中会预先取回测周期起始时刻前1000根K线柱,作为初始K线数据。
对于exchange.GetRecords(Period)函数来说商品期货的实盘、回测均支持自定义周期,参数Period为秒数,例如:
javascript
function main() {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
Log(exchange.SetContractType("rb888"))
// 打印K线周期为120秒(2分钟)的K线数据
Log(exchange.GetRecords(60 * 2))
// 打印K线周期为5分钟的K线数据
Log(exchange.GetRecords(PERIOD_M5))
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
Log(exchange.SetContractType("rb888"))
Log(exchange.GetRecords(60 * 2))
Log(exchange.GetRecords(PERIOD_M5))
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
Log(exchange.SetContractType("rb888"));
Log(exchange.GetRecords(60 * 2)[0]);
Log(exchange.GetRecords(PERIOD_M5)[0]);
}
Period参数设置为5,即为请求获取5秒为周期的K线数据。
如果Period参数不能被60整除(即代表的周期是不可用分钟为单位的周期),系统底层则使用tick数据合成所需的K线数据。
如果Period参数能被60整除,则最小使用1分钟K线数据(尽可能使用较大的周期合成所需K线数据),合成所需的K线数据。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
Log(exchange.SetContractType("rb888"))
var records = exchange.GetRecords(PERIOD_H1)
Log("第一根k线数据为,Time:", records[0].Time, "Open:", records[0].Open, "High:", records[0].High)
Log("第二根k线数据为,Time:", records[1].Time ,"Close:", records[1].Close)
Log("当前K线(最新)", records[records.length-1], "上一根K线", records[records.length-2])
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
Log(exchange.SetContractType("rb888"))
records = exchange.GetRecords(PERIOD_H1)
Log("第一根k线数据为,Time:", records[0]["Time"], "Open:", records[0]["Open"], "High:", records[0]["High"])
Log("第二根k线数据为,Time:", records[1]["Time"], "Close:", records[1]["Close"])
Log("当前K线(最新)", records[-1], "上一根K线", records[-2])
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
Log(exchange.SetContractType("rb888"));
auto records = exchange.GetRecords(PERIOD_H1);
Log("第一根k线数据为,Time:", records[0].Time, "Open:", records[0].Open, "High:", records[0].High);
Log("第二根k线数据为,Time:", records[1].Time, "Close:", records[1].Close);
Log("当前K线(最新)", records[records.size() - 1], "上一根K线", records[records.size() - 2]);
}
股票证券:
- 富途证券
在K线周期为日线周期以下时,调用GetRecords()函数返回的数据中数组的每个元素为一个K线柱数据(即Record结构体),每个K线柱数据结构的Time属性是该周期的结束时间(毫秒级时间戳)并非起始时间(毫秒级时间戳)。
在K线周期为日线周期时,Record结构体的Time属性是该周期的起始时间(毫秒级时间戳)。
注意:
回测系统中模拟级别回测由于需要设置底层K线周期(回测系统模拟级别回测时,根据设置的底层K线周期使用对应的K线数据生成tick数据),需要注意在策略中获取的K线数据的周期不能小于底层K线周期。因为在模拟级别回测中,各个周期的K线数据在回测系统中都是通过底层K线周期对应的K线数据合成的。
C++语言中如果需要自己构造K线数据有以下代码范例:
c++
#include <sstream>
void main() {
Records r;
r.Valid = true;
for (auto i = 0; i < 10; i++) {
Record ele;
ele.Time = i * 100000;
ele.High = i * 10000;
ele.Low = i * 1000;
ele.Close = i * 100;
ele.Open = i * 10;
ele.Volume = i * 1;
r.push_back(ele);
}
// 输出显示:Records[10]
Log(r);
auto ma = TA.MA(r,10);
// 输出显示:[nan,nan,nan,nan,nan,nan,nan,nan,nan,450]
Log(ma);
}
exchange.GetPeriod()
exchange.GetPeriod()函数返回回测、实盘运行策略时在优宽量化交易平台网站页面上设置的K线周期,返回值为整数,单位为秒。返回值:数值类型。
javascript
function main() {
// 例如回测、实盘时优宽量化交易平台网站页面上设置的K线周期为1小时
var period = exchange.GetPeriod()
Log("K线周期:", period / (60 * 60), "小时")
}
python
def main():
period = exchange.GetPeriod()
Log("K线周期:", period / (60 * 60), "小时")
c++
void main() {
auto period = exchange.GetPeriod();
Log("K线周期:", period / (60 * 60.0), "小时");
}
exchange.SetMaxBarLen(Len)
exchange.SetMaxBarLen(Len)函数对于商品期货策略来说,运行时影响K线线柱(BAR)的上限数量。exchange.SetMaxBarLen函数需要在exchange.SetContractType函数调用之前执行,因为当exchange.SetContractType函数调用时,系统底层已经开始处理K线数据。
javascript
function main() {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetMaxBarLen(50)
Log(exchange.SetContractType("rb888"))
var records = exchange.GetRecords()
Log(records.length, records)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetMaxBarLen(50)
Log(exchange.SetContractType("rb888"))
r = exchange.GetRecords()
Log(len(r), r)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetMaxBarLen(50);
Log(exchange.SetContractType("rb888"));
auto r = exchange.GetRecords();
Log(r.size(), r[0]);
}
exchange.GetRawJSON()
exchange.GetRawJSON(),返回最后一次请求时获取的原始内容(字符串),可以用来自行解析数据。返回值:字符串类型。
C++语言策略不支持该函数。
javascript
function main(){
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.GetAccount();
var obj = JSON.parse(exchange.GetRawJSON());
Log(obj);
}
python
import json
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.GetAccount()
obj = json.loads(exchange.GetRawJSON())
Log(obj)
c++
void main() {
auto obj = exchange.GetAccount();
// C++ 不支持GetRawJSON函数
Log(obj);
}
回测系统中商品期货策略只有JavaScript语言策略支持。
javascript
function main(){
while(true){
if(exchange.IO("status")){
exchange.SetContractType("rb888")
var ticker = exchange.GetTicker()
exchange.SetDirection("buy")
exchange.Buy(ticker.Sell + 2, 1)
Log("exchange.GetAccount()函数返回的数据中的Balance:", exchange.GetAccount().Balance)
var ret = JSON.parse(exchange.GetRawJSON())
Log("exchange.GetRawJSON()函数返回的数据中的Balance:", ret.Balance)
break
LogStatus(_D(), "已经连接CTP !")
} else {
LogStatus(_D(), "未连接CTP !")
}
}
}
exchange.GetRate()
exchange.GetRate(),返回交易所使用的流通货币与当前显示的计价货币的汇率,返回1表示禁用汇率转换。返回值:数值类型。
注意:
- 如果没有调用
exchange.SetRate()设置过转换汇率,exchange.GetRate()默认返回的汇率值是1,即当前显示的计价货币没有发生过汇率转换。 - 如果使用
exchange.SetRate()设置过一个汇率值,例如exchange.SetRate(7),那么当前exchange这个交易所对象代表的交易所流通货币的行情、深度、下单价格等等所有价格信息,都会被乘以之前设置的汇率7进行转换。 - 如果
exchange对应的是以美元为计价货币的交易所,调用exchange.SetRate(7)后,实盘所有价格都会被乘7转换成接近CNY的价格。此时使用exchange.GetRate()获取的汇率值就是7。
exchange.GetUSDCNY()
exchange.GetUSDCNY(),返回美元最新汇率(yahoo提供的数据源)。返回值:数值类型。
exchange.SetData(Key, Value)
exchange.SetData(Key, Value)函数用于设置策略运行时加载的数据,可以是任何经济指标、行业数据、相关指数等。用于策略量化考核所有可量化的信息,支持回测系统中使用。
exchange.SetData(Key, Value)函数的调用方式:
-
在策略中直接写入数据
要求数据格式如同以下例子中的data变量。javascript/*backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] */ function main() { var data = [ [1579536000000, "abc"], [1579622400000, 123], [1579708800000, {"price": 123}], [1579795200000, ["abc", 123, {"price": 123}]] ] exchange.SetData("test", data) while(true) { Log(exchange.GetData("test")) Sleep(1000) } }python'''backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] ''' def main(): data = [ [1579536000000, "abc"], [1579622400000, 123], [1579708800000, {"price": 123}], [1579795200000, ["abc", 123, {"price": 123}]] ] exchange.SetData("test", data) while True: Log(exchange.GetData("test")) Sleep(1000)c++/*backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] */ void main() { json data = R"([ [1579536000000, "abc"], [1579622400000, 123], [1579708800000, {"price": 123}], [1579795200000, ["abc", 123, {"price": 123}]] ])"_json; exchange.SetData("test", data); while(true) { Log(exchange.GetData("test")); Sleep(1000); } }以上测试代码运行时,会在对应的时间获取对应的数据,如图:
可以看到时间戳
1579622400000对应的时间为2020-01-22 00:00:00,当策略程序运行在这个时间之后,在下一条数据时间戳1579708800000即时间2020-01-23 00:00:00之前,调用exchange.GetData(Source)函数获取数据。获取的都是[1579622400000, 123]该条数据的内容,随着程序继续运行,时间变化,以此类推获取逐条数据。 -
通过外部链接请求数据
请求到的数据格式
javascript{ "schema":["time","data"], "data":[ [1579536000000, "abc"], [1579622400000, 123], [1579708800000, {"price": 123}], [1579795200000, ["abc", 123, {"price": 123}]] ] }其中
schema为加载数据的主体中的每一条记录的数据格式,该格式固定为["time","data"]对应data属性中的逐条数据的格式。data属性中储存的为数据主体,每条数据由毫秒级别时间戳和数据内容构成(数据内容可以是任何可JSON编码的数据)。测试用的服务程序,使用
Go语言编写:golangpackage main import ( "fmt" "net/http" "encoding/json" ) func Handle (w http.ResponseWriter, r *http.Request) { defer func() { fmt.Println("req:", *r) ret := map[string]interface{}{ "schema": []string{"time","data"}, "data": []interface{}{ []interface{}{1579536000000, "abc"}, []interface{}{1579622400000, 123}, []interface{}{1579708800000, map[string]interface{}{"price":123}}, []interface{}{1579795200000, []interface{}{"abc", 123, map[string]interface{}{"price":123}}}, }, } b, _ := json.Marshal(ret) w.Write(b) }() } func main () { fmt.Println("listen http://localhost:9090") http.HandleFunc("/data", Handle) http.ListenAndServe(":9090", nil) }程序在接收到请求后的应答数据:
javascript{ "schema":["time","data"], "data":[ [1579536000000, "abc"], [1579622400000, 123], [1579708800000, {"price": 123}], [1579795200000, ["abc", 123, {"price": 123}]] ] }测试策略代码:
javascript/*backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] */ function main() { while(true) { Log(exchange.GetData("http://xxx.xx.x.xx:9090/data")) Sleep(1000) } }python'''backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] ''' def main(): while True: Log(exchange.GetData("http://xxx.xx.x.xx:9090/data")) Sleep(1000)c++/*backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] */ void main() { while(true) { Log(exchange.GetData("http://xxx.xx.x.xx:9090/data")); Sleep(1000); } }
exchange.GetData(Source)
exchange.GetData(Source)函数用于获取exchange.SetData(Key, Value)函数加载的数据或外部链接提供的数据,支持回测系统中使用。回测时一次性获取数据,实盘时缓存一分钟的数据。
-
获取直接写入的数据的调用方式
javascript/*backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] */ function main() { exchange.SetData("test", [[1579536000000, _D(1579536000000)], [1579622400000, _D(1579622400000)], [1579708800000, _D(1579708800000)]]) while(true) { Log(exchange.GetData("test")) Sleep(1000 * 60 * 60 * 24) } }python'''backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] ''' def main(): exchange.SetData("test", [[1579536000000, _D(1579536000000/1000)], [1579622400000, _D(1579622400000/1000)], [1579708800000, _D(1579708800000/1000)]]) while True: Log(exchange.GetData("test")) Sleep(1000 * 60 * 60 * 24)c++/*backtest start: 2020-01-21 00:00:00 end: 2020-02-12 00:00:00 period: 1d basePeriod: 1d exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}] */ void main() { json arr = R"([[1579536000000, ""], [1579622400000, ""], [1579708800000, ""]])"_json; arr[0][1] = _D(1579536000000); arr[1][1] = _D(1579622400000); arr[2][1] = _D(1579708800000); exchange.SetData("test", arr); while(true) { Log(exchange.GetData("test")); Sleep(1000 * 60 * 60 * 24); } } -
获取外部链接的数据的调用方式
javascriptfunction main() { Log(exchange.GetData("http://xxx.xx.x.xx:9090/data")) Log(exchange.GetData("https://www.youquant.com/upload/asset/32bf73a69fc12d36e76.json")) }pythondef main(): Log(exchange.GetData("http://xxx.xx.x.xx:9090/data")) Log(exchange.GetData("https://www.youquant.com/upload/asset/32bf73a69fc12d36e76.json"))c++void main() { Log(exchange.GetData("http://xxx.xx.x.xx:9090/data")); Log(exchange.GetData("https://www.youquant.com/upload/asset/32bf73a69fc12d36e76.json")); } -
使用平台数据中心的基本面数据
使用exchange.GetData(Source)函数获取基本面数据。
在调用exchange.GetData(Source)函数时,可以传入第二个参数,用于设置缓存超时,单位为毫秒。实盘时默认为一分钟缓存超时,回测系统中,当使用访问接口请求数据的方式时,回测系统会自动给请求增加from(时间戳秒),to(时间戳秒),period(底层K线周期,时间戳毫秒)等参数,用于确定要获取数据的时间范围。
交易API
以下函数均可通过exchange或exchanges[0]对象调用。例如:exchange.Sell(100, 1);,即在交易所下一个订单价格为100,数量为1的卖单。
exchange.Buy(Price, Amount)
exchange.Buy(Price, Amount),该函数用于下买单,该函数返回一个订单ID。参数值:Price为订单价格,数值类型。Amount为订单数量,数值类型。exchange.Buy(Price, Amount)函数的返回值:字符串类型。
返回的订单编号,可用于查询订单信息和取消订单。
期货下单时必须注意交易方向是否设置正确,如果交易方向和交易函数不匹配会报错。参看:exchange.SetDirection
javascript
function main() {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("buy")
// 注意,这里的下单价格100为举例,具体测试的时候可以自行设置、改动
var id = exchange.Buy(100, 1)
Log("id:", id)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.SetDirection("buy")
id = exchange.Buy(100, 1)
Log("id:", id)
c++
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
exchange.SetDirection("buy");
auto id = exchange.Buy(100, 1);
Log("id:", id);
}
-
股票证券下单
下单量为股票股数,并非股票手数(在股票证券交易所对象中如无特殊说明,相关的量均为股票股数)。下单量需要符合股票信息中的每手股数要求。
例如,02333.HK港股长城汽车,每手500股。下单量必须是500的整倍数。601633.SH为A股长城汽车,每手100股。下单量必须是100的整倍数。
每手股数可以从SetContractType函数返回的数据结构中获取(VolumeMultiple字段)。也可以从GetTicker()函数返回的数据结构中的Info属性中获取(LotSize字段)。 -
市价单
价格参数传-1即可。回测系统不支持商品期货、股票证券市价单,实盘支持。
javascriptfunction main() { // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态 while (!exchange.IO("status")) { Sleep(1000) } // 设置合约代码 exchange.SetContractType("rb888") // 设置下单方向 exchange.SetDirection("buy") // 下单价格参数传-1即为市价单 var id = exchange.Buy(-1, 1) Log("id:", id) }pythondef main(): while not exchange.IO("status"): Sleep(1000) exchange.SetContractType("rb888") exchange.SetDirection("buy") id = exchange.Buy(-1, 1) Log("id:", id)c++void main() { while (exchange.IO("status") == 0) { Sleep(1000); } exchange.SetContractType("rb888"); exchange.SetDirection("buy"); auto id = exchange.Buy(-1, 1); Log("id:", id); }
商品期货、股票证券除了使用市价单(价格参数传-1,实际是系统使用了一个较高的买入价或者较低的卖出价来确保成交),还可以用限价单方式下单。可以使用一个较大的滑价,确保和对手盘成交,参看exchange.Sell中的范例。
exchange.Sell(Price, Amount)
exchange.Sell(Price, Amount),该函数用于下卖单,该函数返回一个订单ID。参数值:Price为订单价格,数值类型。Amount为订单数量,数值类型。exchange.Sell(Price, Amount)函数的返回值:字符串类型。
返回的订单编号,可用于查询订单信息和取消订单。
期货下单时必须注意交易方向是否设置正确,如果交易方向和交易函数不匹配会报错。参看:exchange.SetDirection
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("sell")
// 注意,这里的下单价格100为举例,具体测试的时候可以自行设置、改动
var id = exchange.Sell(100, 1)
Log("id:", id)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.SetDirection("sell")
id = exchange.Sell(100, 1)
Log("id:", id)
c++
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
exchange.SetDirection("sell");
auto id = exchange.Sell(100, 1);
Log("id:", id);
}
-
股票证券下单
下单量为股票股数,并非股票手数(在股票证券交易所对象中如无特殊说明,相关的量均为股票股数)。下单量需要符合股票信息中的每手股数要求。参看
-
市价单
价格参数传-1即可。回测系统不支持商品期货、股票证券市价单,实盘支持。
商品期货除了使用市价单还可以用限价单方式下单,可以使用一个较大的滑价确保和对手盘成交。
javascript
function main() {
while(true) {
if (exchange.IO("status")) {
// 设置当前合约为 rb2001 , 测试的时候按回测时间设置合适的合约
exchange.SetContractType("rb2001")
exchange.SetDirection("buy")
// 获取当前行情
var ticker = exchange.GetTicker()
// 拿到当前卖一价格
var currSell1Price = ticker.Sell
// 加50元滑价,即为比出价卖出的挂单高50,要求买入1手
var id = exchange.Buy(currSell1Price + 50, 1)
Log(exchange.GetOrder(id))
break
} else {
Log("未连接")
}
}
}
python
def main():
while True:
if exchange.IO("status"):
exchange.SetContractType("rb2001")
exchange.SetDirection("buy")
ticker = exchange.GetTicker()
currSell1Price = ticker["Sell"]
id = exchange.Buy(currSell1Price + 50, 1)
Log(exchange.GetOrder(id))
break
else :
Log("未连接")
c++
void main() {
while(true) {
if(exchange.IO("status") == 1) {
exchange.SetContractType("rb2001");
exchange.SetDirection("buy");
auto ticker = exchange.GetTicker();
auto currSell1Price = ticker.Sell;
auto id = exchange.Buy(currSell1Price + 50, 1);
Log(exchange.GetOrder(id));
break;
} else {
Log("未连接");
}
}
}
exchange.CancelOrder(Id)
exchange.CancelOrder(Id),该函数的用途为取消某个Id的订单。参数值:Id为订单编号,参数Id的类型为字符串类型。返回值:布尔类型。
返回操作结果,true表示取消订单请求发送成功,false表示取消订单请求发送失败(返回值只是代表发送请求成功或失败,交易所是否取消订单可以调用exchange.GetOrders()判断)。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("sell")
// 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
var id = exchange.Sell(99999, 1)
exchange.CancelOrder(id)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.SetDirection("sell")
id = exchange.Sell(99999, 1)
exchange.CancelOrder(id)
c++
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
exchange.SetDirection("sell");
auto id = exchange.Sell(99999, 1);
exchange.CancelOrder(id);
}
优宽的API函数中可以产生日志输出的函数例如:Log(...)、exchange.Buy(Price, Amount)、exchange.CancelOrder(Id)等都可以在必要参数后跟一些附带输出参数。
例如:exchange.CancelOrder(orders[j].Id, orders[j]),这样就是在取消Id为orders[j].Id的这个订单时附带输出这个订单的信息,即orders[j]这个Order结构。
javascript
function main() {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("sell")
Log("数据1", "数据2", "数据3", "...")
var data2 = 200
// 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
var id = exchange.Sell(100000, 0.1, "附带数据1", data2, "...")
exchange.CancelOrder(id, "附带数据1", data2, "...")
LogProfit(100, "附带数据1", data2, "...")
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.SetDirection("sell")
Log("数据1", "数据2", "数据3", "...")
data2 = 200
id = exchange.Sell(100000, 0.1, "附带数据1", data2, "...")
exchange.CancelOrder(id, "附带数据1", data2, "...")
LogProfit(100, "附带数据1", data2, "...")
c++
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
exchange.SetDirection("sell");
Log("数据1", "数据2", "数据3", "...");
int data2 = 200;
auto id = exchange.Sell(100000, 0.1, "附带数据1", data2, "...");
exchange.CancelOrder(id, "附带数据1", data2, "...");
LogProfit(100, "附带数据1", data2, "...");
}
exchange.GetOrder(Id)
exchange.GetOrder(Id),根据订单号获取订单详情。参数值:Id为需要获取的订单号,参数Id为字符串类型。返回值:Order结构体。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("sell")
// 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
var id = exchange.Sell(99999, 1)
// 参数id为订单号码,需填入你想要查询的订单的号码
var order = exchange.GetOrder(id)
Log("Id:", order.Id, "Price:", order.Price, "Amount:", order.Amount, "DealAmount:",
order.DealAmount, "Status:", order.Status, "Type:", order.Type)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.SetDirection("sell")
id = exchange.Sell(99999, 1)
order = exchange.GetOrder(id)
Log("Id:", order["Id"], "Price:", order["Price"], "Amount:", order["Amount"], "DealAmount:",
order["DealAmount"], "Status:", order["Status"], "Type:", order["Type"])
c++
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
exchange.SetDirection("sell");
auto id = exchange.Sell(99999, 1);
auto order = exchange.GetOrder(id);
Log("Id:", order.Id, "Price:", order.Price, "Amount:", order.Amount, "DealAmount:",
order.DealAmount, "Status:", order.Status, "Type:", order.Type);
}
exchange.GetOrders()
exchange.GetOrders(),获取所有未完成的订单。返回值:Order结构体数组。
Order结构体可参考exchange.GetOrder()函数说明。当交易所对象exchange代表的账户当前交易对没有挂单时,调用exchange.GetOrders()返回空数组,即:[]。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("sell")
// 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
exchange.Sell(99999, 1)
exchange.Sell(88888, 1)
var orders = exchange.GetOrders()
Log("未完成订单一的信息,ID:", orders[0].Id, "Price:", orders[0].Price, "Amount:", orders[0].Amount,
"DealAmount:", orders[0].DealAmount, "type:", orders[0].Type)
Log("未完成订单二的信息,ID:", orders[1].Id, "Price:", orders[1].Price, "Amount:", orders[1].Amount,
"DealAmount:", orders[1].DealAmount, "type:", orders[1].Type)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.SetDirection("sell")
exchange.Sell(99999, 1)
exchange.Sell(88888, 1)
orders = exchange.GetOrders()
Log("未完成订单一的信息,ID:", orders[0]["Id"], "Price:", orders[0]["Price"], "Amount:", orders[0]["Amount"],
"DealAmount:", orders[0]["DealAmount"], "type:", orders[0]["Type"])
Log("未完成订单二的信息,ID:", orders[1]["Id"], "Price:", orders[1]["Price"], "Amount:", orders[1]["Amount"],
"DealAmount:", orders[1]["DealAmount"], "type:", orders[1]["Type"])
c++
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
exchange.SetDirection("sell");
exchange.Sell(99999, 1);
exchange.Sell(88888, 1);
auto orders = exchange.GetOrders();
Log("未完成订单一的信息,ID:", orders[0].Id, "Price:", orders[0].Price, "Amount:", orders[0].Amount,
"DealAmount:", orders[0].DealAmount, "type:", orders[0].Type);
Log("未完成订单二的信息,ID:", orders[1].Id, "Price:", orders[1].Price, "Amount:", orders[1].Amount,
"DealAmount:", orders[1].DealAmount, "type:", orders[1].Type);
}
exchange.GetOrders()函数在商品期货、股票证券中获取的是所有未完成订单。在商品期货中exchange.GetOrders()函数获取的订单与当前设置的合约无关。可以使用以下例子进行回测、模拟盘、实盘测试。
javascript
/*backtest
start: 2020-06-17 10:00:00
end: 2020-06-18 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
function main() {
var contractTypeList = ["MA009", "rb2010", "i2009"]
while (true) {
if (exchange.IO("status")) {
for (var i = 0; i < contractTypeList.length; i++) {
var ret = exchange.SetContractType(contractTypeList[i])
var ticker = exchange.GetTicker()
exchange.SetDirection("sell")
var id = exchange.Sell(ticker.Sell + 5, 1)
Log(contractTypeList[i], "开空仓订单ID:", id)
}
var orders = exchange.GetOrders()
for (var j = 0; j < orders.length; j++) {
Log(orders[j])
}
break
} else {
LogStatus(_D(), "未连接")
}
}
}
python
'''backtest
start: 2020-06-17 10:00:00
end: 2020-06-18 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''
def main():
contractTypeList = ["MA009", "rb2010", "i2009"]
while True:
if exchange.IO("status"):
for i in range(len(contractTypeList)):
ret = exchange.SetContractType(contractTypeList[i])
ticker = exchange.GetTicker()
exchange.SetDirection("sell")
id = exchange.Sell(ticker["Sell"] + 5, 1)
Log(contractTypeList[i], "开空仓订单ID:", id)
orders = exchange.GetOrders()
for i in range(len(orders)):
Log(orders[i])
break
else:
LogStatus(_D(), "未连接")
c++
/*backtest
start: 2020-06-17 10:00:00
end: 2020-06-18 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
void main() {
vector<string> contractTypeList = {"MA009", "rb2010", "i2009"};
while (true) {
if (exchange.IO("status") == 1) {
for (int i = 0 ; i < contractTypeList.size(); i++) {
auto ret = exchange.SetContractType(contractTypeList[i]);
auto ticker = exchange.GetTicker();
exchange.SetDirection("sell");
auto id = exchange.Sell(ticker.Sell + 5.0, 1);
Log(contractTypeList[i], "开空仓订单ID:", id);
}
auto orders = exchange.GetOrders();
for (int j = 0; j < orders.size(); j++) {
Log(orders[j]);
}
break;
} else {
LogStatus(_D(), "未完成");
}
}
}
exchange.SetPrecision(...)
exchange.SetPrecision(PricePrecision, AmountPrecision),设置价格与品种下单量的小数位精度,设置后会自动截断数据多余的小数位。参数值:PricePrecision为数值类型,用来控制价格数据的小数点位数。AmountPrecision为数值类型,用来控制下单量数据的小数点位数。PricePrecision和AmountPrecision都必须是整型的数值。
javascript
function main(){
// 设置价格小数位精度为2位,品种下单量小数位精度为3位
exchange.SetPrecision(2, 3)
}
python
def main():
exchange.SetPrecision(2, 3)
c++
void main() {
exchange.SetPrecision(2, 3);
}
注意:
回测不支持该函数,回测的数值精度会自动处理。
exchange.SetRate(Rate)
exchange.SetRate(Rate),设置交易所的流通货币的汇率。参数值:Rate为数值类型。返回值:为数值类型。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置汇率之前打印行情数据
Log(exchange.GetTicker())
// 设置汇率转换
exchange.SetRate(7)
Log(exchange.GetTicker())
// 设置为1,不转换
exchange.SetRate(1)
Log(exchange.GetTicker())
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
Log(exchange.GetTicker())
exchange.SetRate(7)
Log(exchange.GetTicker())
exchange.SetRate(1)
Log(exchange.GetTicker())
c++
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
Log(exchange.GetTicker());
exchange.SetRate(7);
Log(exchange.GetTicker());
exchange.SetRate(1);
Log(exchange.GetTicker());
}
注意:
- 如果使用
exchange.SetRate(Rate)设置过一个汇率值,例如设置为7。那么当前exchange这个交易所对象代表的交易所的流通货币的行情、深度、下单价格等等所有价格信息,都会被乘以设置的汇率7进行转换。 - 例如
exchange是以美元为计价货币的交易所。exchange.SetRate(7)调用后,实盘所有价格都会被乘7转换成接近CNY的价格。
exchange.IO(...)
exchange.IO(...),调用协议、交易所等其它功能接口,参数有多种模式。
使用mode参数,切换行情模式:
-
exchange.IO("mode", 0)
立即返回模式,如果当前还没有接收到交易所最新的行情数据推送,就立即返回旧的行情数据,如果有新的数据就返回新的数据。
商品期货中的应用,可以参考:exchange.IO函数的wait参数的使用例子。 -
exchange.IO("mode", 1)
缓存模式(默认模式),如果当前还没有收到交易所最新的行情数据(同上一次接口获取的数据比较),就等待接收然后再返回,如果调用该函数之前收到了最新的行情数据,就立即返回最新的数据。 -
exchange.IO("mode", 2)
强制更新模式,进入等待一直到接收到交易所下一次的最新推送数据后返回。
使用status参数,判断与期货公司前置机连接状态:
exchange.IO("status"),返回true证明与CTP服务器行情与数据的两台服务器都连接正常。
javascript
function main() {
while (!exchange.IO("status")) {
LogStatus("正在等待与交易服务器连接, " + new Date())
}
}
python
def main():
while not exchange.IO("status"):
LogStatus("正在等待与交易服务器连接, " + _D())
c++
void main() {
while(exchange.IO("status") == 0) {
LogStatus("正在等待与交易服务器连接, " + _D());
}
}
使用wait参数,设置阻塞:
exchange.IO("wait", Timeout),当前交易所有任何品种更新行情信息或订单成交时才返回,可带第二个参数(毫秒数)指定超时,超时返回空值,正常返回EventTick/OrderEvent结构,结合exchange.IO("mode", 0)函数使用,这样配合使用就可以使程序在有最新行情时进行响应,执行程序逻辑(使用exchange.IO("mode", 0)并不影响exchange.IO("wait"),目的是为了在程序中使用exchange.GetTicker()等函数调用时不阻塞)。如果Timeout参数设置-1该函数设置为立即返回,在没有新事件时返回空值,Timeout参数设置0为阻塞等待最新事件,如同不设置Timeout参数。需要注意的是在使用exchange.IO("wait")时,必须至少已经订阅了一个当前处于交易状态的合约(已经交割的过期合约,不会再有行情数据),否则会阻塞在该函数(由于没有任何行情、订单更新)。只支持商品期货实盘。
EventTick:{Event:"tick", Index:交易所索引, Nano:事件纳秒级时间, Symbol:合约名称, Ticker:行情数据}。
OrderTick:{Event:"order", Index:交易所索引, Nano:事件纳秒级时间, Order:订单信息}。
简单实现回调机制:
javascript
function on_tick(symbol, ticker) {
Log("symbol:", symbol, "update")
Log("ticker:", ticker)
}
function on_order(order) {
Log("order update", order)
}
function main() {
while(!exchange.IO("status")) {
Sleep(10)
}
// 如果在程序中不使用诸如exchange.GetTicker()之类的获取行情的函数,可以不设置exchange.IO("mode", 0)
exchange.IO("mode", 0)
_C(exchange.SetContractType, "MA005")
while(true) {
var e = exchange.IO("wait")
if(e) {
if(e.Event == "tick") {
on_tick(e.Symbol, e.Ticker)
} else if(e.Event == "order") {
on_order(e.Order)
}
}
}
}
python
def on_tick(symbol, ticker):
Log("symbol:", symbol, "update")
# 数据结构:https://www.youquant.com/api#ticker
Log("ticker:", ticker)
def on_order(order):
Log("order update", order)
def main():
# wait connect trade server
while not exchange.IO("status"):
Sleep(10)
# switch push mode
exchange.IO("mode", 0)
# subscribe instrument
_C(exchange.SetContractType, "MA001")
while True:
e = exchange.IO("wait")
if e:
if e.Event == "tick":
on_tick(e['Symbol'], e['Ticker'])
elif e.Event == "order":
on_order(e['Order'])
c++
void on_tick(const string &symbol, json &ticker) {
Log("symbol:", symbol, "update");
Log("ticker:", ticker);
}
void on_order(json &order) {
Log("order update", order);
}
void main() {
while(exchange.IO("status") == 0) {
Sleep(10);
}
exchange.IO("mode", 0);
_C(exchange.SetContractType, "rb2005");
while(true) {
auto e = exchange.IO("wait");
if(e != false) {
if(e["Event"] == "tick") {
on_tick(e["Symbol"], e["Ticker"]);
}
else if(e["Event"] == "order") {
on_order(e["Order"]);
}
}
}
}
多品种回调例子:
javascript
function on_tick(symbol, ticker) {
Log("symbol:", symbol, "update", "ticker:", ticker)
}
function main() {
while(!exchange.IO("status")) {
Sleep(10)
}
_C(exchange.SetContractType, "MA101")
_C(exchange.SetContractType, "rb2101")
_C(exchange.SetContractType, "i2101")
while(true) {
var e = exchange.IO("wait", -1)
if(e) {
if(e.Event == "tick") {
on_tick(e.Symbol, e.Ticker)
}
}
Sleep(10)
}
}
python
def on_tick(symbol, ticker):
Log("symbol:", symbol, "update", "ticker:", ticker)
def main():
while not exchange.IO("status"):
Sleep(10)
_C(exchange.SetContractType, "MA101")
_C(exchange.SetContractType, "rb2101")
_C(exchange.SetContractType, "i2101")
while True:
e = exchange.IO("wait", -1)
if e:
if e.Event == "tick":
on_tick(e['Symbol'], e['Ticker'])
Sleep(10)
c++
void on_tick(const string &symbol, json &ticker) {
Log("symbol:", symbol, "update", "ticker:", ticker);
}
void main() {
while(exchange.IO("status") == 0) {
Sleep(10);
}
_C(exchange.SetContractType, "MA101");
_C(exchange.SetContractType, "rb2101");
_C(exchange.SetContractType, "i2101");
while(true) {
auto e = exchange.IO("wait", -1);
if(e != false) {
if(e["Event"] == "tick") {
on_tick(e["Symbol"], e["Ticker"]);
}
}
}
}
使用wait_any参数,设置阻塞:
exchange.IO("wait_any"),作用同上exchange.IO("wait", Timeout)。但是指的是有任何一个交易所对象收到行情信息时就返回,EventTick里的Index指交易所索引。用于多交易所对象(多个期货公司前置服务器)收集行情数据的场景,只支持商品期货实盘。
使用instruments参数,获取所有合约的列表数据:
exchange.IO("instruments"),返回交易所所有合约的列表,只支持实盘。
javascript
function main() {
while (!exchange.IO("status")) {
LogStatus("正在等待与交易服务器连接, " + new Date())
}
Log("开始获取所有合约")
var instruments = _C(exchange.IO, "instruments")
Log("合约列表获取成功")
var len = 0
for (var instrumentId in instruments) {
len++
}
Log("合约列表长度为:",len)
}
python
def main():
while not exchange.IO("status"):
LogStatus("正在等待与交易服务器连接, " + _D())
Log("开始获取所有合约")
instruments = _C(exchange.IO, "instruments")
Log("合约列表获取成功")
length = 0
for i in range(len(instruments)):
length += 1
Log("合约列表长度为:", length)
c++
void main() {
while(exchange.IO("status") == 0) {
LogStatus("正在等待与交易服务器连接, " + _D());
}
Log("开始获取所有合约");
auto instruments = _C(exchange.IO, "instruments");
Log("合约列表获取成功");
int length = 0;
for(int i = 0; i < instruments.size(); i++) {
length++;
}
Log("合约列表长度为:", length);
}
使用products参数,获取所有产品的列表数据:
exchange.IO("products"),返回交易所所有产品的列表,只支持实盘。
使用subscribed参数,获取已经订阅的合约数据:
exchange.IO("subscribed"),返回已订阅行情的合约,只支持实盘。
使用settlement参数,获取结算单数据:
exchange.IO("settlement"),结算单查询,不加第二个参数默认返回之前一个交易日的数据,加参数如20170317指返回日期为2017-03-17的结算单,只支持实盘。
使用api参数,调用底层接口:
优宽量化的CTP(商品期货)终端提供了完整的全API实现,当优宽平台的API满足不了你需要的功能时可以用exchange.IO函数进行更深层的系统调用,完全兼容官方的Api名称。CTP的IO直接扩展函数调用请求,将会在收到第一个isLast标记为true的响应包后返回。
CTP协议接口:CTP协议接口相关资料
以几个简单的例子做为说明:
-
查询投资者信息
javascriptfunction main() { while (!exchange.IO("status")) { LogStatus("正在等待与交易服务器连接, " + new Date()) } Log(exchange.IO("api", "ReqQryInvestor")) }pythondef main(): while not exchange.IO("status"): LogStatus("正在等待与交易服务器连接, " + _D()) Log(exchange.IO("api", "ReqQryInvestor"))c++void main() { while(exchange.IO("status") == 0) { LogStatus("正在等待与交易服务器连接, " + _D()); } Log(exchange.IO("api", "ReqQryInvestor")); } -
修改密码
javascriptfunction main() { // CTP协议建立连接时需要时间 Sleep(6000) exchange.IO("api", "ReqUserPasswordUpdate", {BrokerID: "9999", UserID: "11111", OldPassword: "oldpass", NewPassword: "newpass"}) }pythondef main(): Sleep(6000) exchange.IO("api", "ReqUserPasswordUpdate", {"BrokerID": "9999", "UserID": "11111", "OldPassword": "oldpass", "NewPassword": "newpass"})c++void main() { Sleep(6000); exchange.IO("api", "ReqUserPasswordUpdate", R"({"BrokerID": "9999", "UserID": "11111", "OldPassword": "oldpass", "NewPassword": "newpass"})"_json); } -
复杂的例子
javascriptfunction main() { // CTP协议建立连接时需要时间 Sleep(6000) // 如果再加一个参数值为false表示不等待返回值,只发送请求,第三个参数只需要填充需要的字段,也可省略此参数,如果类型为char,传长度为1的字符串即可 var r = exchange.IO("api", "ReqQryProduct", {ProductID: "MA"}) // CTP未登陆的时候会失败 if (!r) { return } _.each(r, function(item) { // IO请求可能返回多个数据包,所以以数组的形式返回。便历数据包的所有数据类型,一个数据包可能包含多个具体数据,具体数据类型的名称,请参看CTP官方文档http://www.sfit.com.cn/5_2_DocumentDown.htm _.each(item, function(f) { // 取出来需要的数据,Name为此数据的类型,Value为此数据的值 if (f.Name == 'CThostFtdcProductField') { // 打印查询的的甲醇的信息 Log(f.Value) } }) }); }pythondef main(): Sleep(6000) r = exchange.IO("api", "ReqQryProduct", {"ProductID": "MA"}) if not r: return for r_index in range(len(r)): for f_index in range(len(r[r_index])): if r[r_index][f_index]["Name"] == 'CThostFtdcProductField': Log(r[r_index][f_index]["Value"])c++void main() { Sleep(6000); auto r = exchange.IO("api", "ReqQryProduct", R"({"ProductID": "MA"})"_json); if(r == false) { return; } for(auto& ele1 : r.items()) { for(auto& ele2 : ele1.value().items()) { if(ele2.value()["Name"] == "CThostFtdcProductField") { Log(ele2.value()["Value"]); } } } } -
查询结算单
javascriptfunction main() { while (!exchange.IO("status")) { LogStatus("正在等待与交易服务器连接, " + new Date()) } // 也可不指定日期 var r = exchange.IO("api", "ReqQrySettlementInfo", {TradingDay: "20190506"}) var s = '' _.each(r, function(item) { _.each(item, function(f) { if (f.Name == 'CThostFtdcSettlementInfoField') { s += f.Value.Content } }) }) Log(s) }pythondef main(): while not exchange.IO("status"): LogStatus("正在等待与交易服务器连接, " + _D()) r = exchange.IO("api", "ReqQrySettlementInfo", {"TradingDay": "20190506"}) s = '' for i in range(len(r)): for ii in range(len(r[i])): if r[i][ii]["Name"] == "CThostFtdcSettlementInfoField": s += r[i][ii]["Value"]["Content"] Log(s)c++void main() { while(exchange.IO("status") == 0) { LogStatus("正在等待与交易服务器连接, " + _D()); } auto r = exchange.IO("api", "ReqQrySettlementInfo", R"({"TradingDay": "20200311"})"_json); string s = ""; for(auto& ele1 : r.items()) { for(auto& ele2 : ele1.value().items()) { if(ele2.value()["Name"] == "CThostFtdcSettlementInfoField") { s += std::string(ele2.value()["Value"]["Content"]); } } } Log(s); }
exchange.Log(...)
exchange.Log(LogType, Price, Amount),该函数调用时不下单, 只记录交易信息,用于输出交易日志信息。
注意:
- 此函数是
exchange交易所对象的成员函数,区别于全局函数Log()。 - 参数值:
LogType可为LOG_TYPE_BUY,LOG_TYPE_SELL,LOG_TYPE_CANCEL,Price为价格,Amount为数量,当LogType为LOG_TYPE_CANCEL的时候Price参数为订单ID。
使用exchange.Log(LogType, Price, Amount)可以进行实盘跟单测试、模拟下单、可以辅助记录下单。
javascript
var id = 123
function main() {
// 下单类型买入,价格999,数量 0.1
exchange.Log(LOG_TYPE_BUY, 999, 0.1)
// 取消订单
exchange.Log(LOG_TYPE_CANCEL, id)
}
python
id = 123
def main():
exchange.Log(LOG_TYPE_BUY, 999, 0.1)
exchange.Log(LOG_TYPE_CANCEL, id)
c++
void main() {
auto id = 123;
exchange.Log(LOG_TYPE_BUY, 999, 0.1);
exchange.Log(LOG_TYPE_CANCEL, id);
}
账户信息
exchange.GetAccount()
exchange.GetAccount(),将返回交易所账户信息。返回值:Account结构结构体。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
// 获取账户资产信息,可以不用设置合约
var account = exchange.GetAccount()
Log("账户信息,Balance:", account.Balance, "FrozenBalance:", account.FrozenBalance, "Stocks:",
account.Stocks, "FrozenStocks:", account.FrozenStocks)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
account = exchange.GetAccount()
Log("账户信息,Balance", account["Balance"], "FrozenBalance:", account["FrozenBalance"], "Stocks:",
account["Stocks"], "FrozenStocks:", account["FrozenStocks"])
c++
void main() {
while (exchange.IO("status") == 0) {
Sleep(1000);
}
auto account = exchange.GetAccount();
Log("账户信息,Balance", account.Balance, "FrozenBalance:", account.FrozenBalance, "Stocks:",
account.Stocks, "FrozenStocks:", account.FrozenStocks);
}
exchange.GetName()
exchange.GetName(),返回交易所名称。返回值:字符串类型。
命令行版本的托管者,可以使用-l命令打印交易所名称列表。
-
股票证券
实盘:富途证券交易所对象时exchange.GetName()函数返回Futures_Futu。中泰XTP交易所对象时exchange.GetName()函数返回Futures_XTP。
回测系统:回测系统并不区分协议,所以exchange.GetName()函数返回Futures_XTP。 -
商品期货
实盘:CTP协议的交易所对象时exchange.GetName()函数返回Futures_CTP,易盛协议的交易所对象时exchange.GetName()函数返回Futures_Esunny。
回测系统:回测系统并不区分协议,所以exchange.GetName()函数返回Futures_CTP。
exchange.GetLabel()
exchange.GetLabel(),返回交易所自定义的标签(在优宽上配置账户信息的页面设置)。返回值:字符串类型。
exchange.GetCurrency()
exchange.GetCurrency(),商品期货、股票证券等返回固定的字符串。返回值:字符串类型。
-
股票证券
实盘:富途证券交易所对象时exchange.GetCurrency()函数返回STOCK。
回测系统:exchange.GetCurrency()函数返回STOCK_CNY。 -
商品期货
实盘:exchange.GetCurrency()函数返回FUTURES。
回测系统:exchange.GetCurrency()函数返回FUTURES_CNY。
exchange.GetQuoteCurrency()
exchange.GetQuoteCurrency(),返回交易所操作的基础货币名称。返回值:字符串类型。例如国内商品期货的交易所对象返回CNY。
期货交易
期货支持传统商品期货CTP协议,易盛协议。所有操作前需要先使用exchange.SetContractType函数设置合约代码。
-
对于商品期货合约代码不太熟悉的用户可以使用如下
JavaScript语言的代码查询:javascriptfunction main(){ while(true){ if(exchange.IO("status")){ var products_CZCE_Tbl = { "type" : "table", "title" : "郑商所 CZCE", "cols" : ["商品名称(ProductName)", "合约代码短名(ProductID)" , "一跳价格(PriceTick)", "一手合约乘数(VolumeMultiple)", "交易所代码(ExchangeID)"], "rows" : [] } var products_DCE_Tbl = { "type" : "table", "title" : "大商所 DCE", "cols" : ["商品名称(ProductName)", "合约代码短名(ProductID)" , "一跳价格(PriceTick)", "一手合约乘数(VolumeMultiple)", "交易所代码(ExchangeID)"], "rows" : [] } var products_SHFE_Tbl = { "type" : "table", "title" : "上期所 SHFE", "cols" : ["商品名称(ProductName)", "合约代码短名(ProductID)" , "一跳价格(PriceTick)", "一手合约乘数(VolumeMultiple)", "交易所代码(ExchangeID)"], "rows" : [] } var products_other_Tbl = { "type" : "table", "title" : "其它", "cols" : ["商品名称(ProductName)", "合约代码短名(ProductID)" , "一跳价格(PriceTick)", "一手合约乘数(VolumeMultiple)", "交易所代码(ExchangeID)"], "rows" : [] } exchange.IO("products").forEach(function(product) { if (product.ExchangeID == "CZCE") { products_CZCE_Tbl.rows.push([product.ProductName, product.ProductID, product.PriceTick, product.VolumeMultiple, product.ExchangeID]) } else if (product.ExchangeID == "DCE") { products_DCE_Tbl.rows.push([product.ProductName, product.ProductID, product.PriceTick, product.VolumeMultiple, product.ExchangeID]) } else if (product.ExchangeID == "SHFE") { products_SHFE_Tbl.rows.push([product.ProductName, product.ProductID, product.PriceTick, product.VolumeMultiple, product.ExchangeID]) } else { products_other_Tbl.rows.push([product.ProductName, product.ProductID, product.PriceTick, product.VolumeMultiple, product.ExchangeID]) } }) LogStatus(_D(), "已经连接CTP", "\n`" + JSON.stringify([products_CZCE_Tbl, products_DCE_Tbl, products_SHFE_Tbl, products_other_Tbl]) + "`") Sleep(1000 * 60 * 5) } else { LogStatus(_D(), "未连接CTP !") } Sleep(1000) } }查询结果显示各个交易所的合约信息
-
商品期货合约命名规则如下:
前面字母代表品种名(合约代码短名),后面数字代表合约到期日。上期 / 能源所:小写 + 4个数字
大商所:小写 + 4个数字
中金所:大写 + 4个数字
郑商所:大写 + 3个数字
exchange.GetPosition()
exchange.GetPosition(),获取当前持仓信息。返回值:position结构体数组。没有持仓则返回空数组,即[]。
javascript
/*
注意:GetPosition函数获取的是所有持仓品种的持仓信息,如果没有持仓则返回空数组,所以使用该接口返回的数据前要先判断返回的数据是否为空数组
*/
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
var info = exchange.SetContractType("rb888")
var ticker = exchange.GetTicker()
exchange.SetDirection("buy")
exchange.Buy(ticker.Last + info.PriceTick * 20, 2)
var position = exchange.GetPosition()
if(position.length>0){
Log("Amount:", position[0].Amount, "FrozenAmount:", position[0].FrozenAmount, "Price:",
position[0].Price, "Profit:", position[0].Profit, "Type:", position[0].Type,
"ContractType:", position[0].ContractType)
}
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
info = exchange.SetContractType("rb888")
ticker = exchange.GetTicker()
exchange.SetDirection("buy")
exchange.Buy(ticker["Last"] + info["PriceTick"] * 20, 2)
position = exchange.GetPosition()
if len(position) > 0:
Log("Amount:", position[0]["Amount"], "FrozenAmount:", position[0]["FrozenAmount"], "Price:",
position[0]["Price"], "Profit:", position[0]["Profit"], "Type:", position[0]["Type"],
"ContractType:", position[0]["ContractType"])
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
auto info = exchange.SetContractType("rb888");
auto ticker = exchange.GetTicker();
exchange.SetDirection("buy");
exchange.Buy(ticker.Last + info["PriceTick"].get<double>() * 20, 2);
auto position = exchange.GetPosition();
if(position.size() > 0) {
Log("Amount:", position[0].Amount, "FrozenAmount:", position[0].FrozenAmount, "Price:",
position[0].Price, "Profit:", position[0].Profit, "Type:", position[0].Type,
"ContractType:", position[0].ContractType);
}
}
商品期货的持仓需要注意:
-
回测系统
回测系统不区分今仓、昨仓。GetPosition函数返回的持仓数据Position结构数组中,Position结构的Type属性仅为PD_LONG或者PD_SHORT。 -
实盘
有交易所区分今仓、昨仓,例如上期所。
有些交易所虽然也区分,但是不能指定平今还是平昨,有今仓优先平今仓。例如IF等一些品种只能先平今仓,所以今仓、昨仓仓位信息合并为一个并且不予区分。
exchange.SetMarginLevel(...)
exchange.SetMarginLevel(MarginLevel),设置杆杠大小。参数值:数值类型。商品期货、股票证券不支持。
exchange.SetDirection(...)
exchange.SetDirection(Direction),设置exchange.Buy或者exchange.Sell函数进行期货下单的方向。参数值:字符串类型。
SetDirection函数设置期货交易的方向和下单函数之间的对应关系:
| 下单函数 | SetDirection函数的参数设置的方向 | 备注 |
|---|---|---|
| exchange.Buy | "buy" | 买入开多仓 |
| exchange.Buy | "closesell" | 买入平空仓 |
| exchange.Buy | "closesell_today" | 买入平空仓(今仓) |
| exchange.Sell | "sell" | 卖出开空仓 |
| exchange.Sell | "closebuy" | 卖出平多仓 |
| exchange.Sell | "closebuy_today" | 卖出平多仓(今仓) |
参数Direction可以取buy,closebuy,sell,closesell,closebuy_today,closesell_today。其中closebuy_today,closesell_today指平今仓。closebuy/closesell为平昨仓。对于CTP协议传统期货,可以设置第二个参数"1"或者"2"或者"3",分别指"投机","套利","套保",不设置默认为投机。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
// 举例设置为螺纹钢主力合约
exchange.SetContractType("rb888")
// 设置下单类型为做多
exchange.SetDirection("buy")
// 以10000的价格,合约数量为2张下单。注意,这里的下单价格为举例,具体测试的时候可以自行设置、改动
exchange.Buy(10000, 2)
// 设置下单类型为平多
exchange.SetDirection("closebuy")
exchange.Sell(1000, 2)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
exchange.SetDirection("buy")
exchange.Buy(10000, 2)
exchange.SetDirection("closebuy")
exchange.Sell(1000, 2)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
exchange.SetDirection("buy");
exchange.Buy(10000, 2);
exchange.SetDirection("closebuy");
exchange.Sell(1000, 2);
}
exchange.SetContractType(...)
exchange.SetContractType(ContractType),设置合约类型。参数值:字符串类型。
商品期货的ContractType就是指的合约代码。例如exchange.SetContractType("au1506"),返回合约的详细信息,如最少一次买多少、手续费、交割时间等数据。主力连续合约的代码为888,例如MA888,连续指数合约为000,例如MA000。888与000虚拟合约的交易只支持回测,实盘只支持获取行情。
在商品期货策略中调用exchange.SetContractType(ContractType)函数时,实盘或者simnow模拟盘中是可能订阅失败的,例如连接期货公司前置机失败时或者设置了不存在的合约代码时。订阅虚拟合约成功以后,返回的字段里面的InstrumentID是主力合约(会在订阅同时获取),方便策略实盘下单交易时做映射使用。
使用exchange.SetContractType(ContractType)函数订阅合约以后,返回商品期货合约的详细信息:
javascript
function main(){
while(true) {
if (exchange.IO("status")) {
var ret = exchange.SetContractType("MA888")
Log("订阅的合约的详细信息:", ret)
break
} else {
LogStatus(_D(), "未连接")
}
}
}
python
def main():
while True:
if exchange.IO("status"):
ret = exchange.SetContractType("MA888")
Log("订阅的合约的详细信息:", ret)
break
else:
LogStatus(_D(), "未连接")
c++
void main() {
while(true) {
if(exchange.IO("status") == 1) {
auto ret = exchange.SetContractType("MA888");
Log("订阅的合约的详细信息:", ret);
break;
} else {
LogStatus(_D(), "未连接");
}
}
}
JavaScript语言的策略打印ret变量,即合约详细信息如下:
javascript
{
"InstrumentName": "甲醇连续",
"MinLimitOrderVolume": 1,
"OpenDate": "20190116",
"PositionType": 50,
"LongMarginRatio": 0.06999999999999999,
"DeliveryYear": 2020,
"MaxMarketOrderVolume": 1000,
"ExpireDate": "20200114",
"PositionDateType": 50,
"InstLifePhase": 49,
"UnderlyingMultiple": 1,
"CombinationType": 48,
"InstrumentID": "MA001",
"ExchangeInstID": "MA001",
"ProductClass": 49,
"MinMarketOrderVolume": 1,
"VolumeMultiple": 10,
"CreateDate": "20190116",
"ShortMarginRatio": 0.06999999999999999,
"UnderlyingInstrID": "",
"ProductID": "MA",
"PriceTick": 1,
"StartDelivDate": "20200114",
"EndDelivDate": "20200114",
"ExchangeID": "CZCE",
"MaxLimitOrderVolume": 1000,
"MaxMarginSideAlgorithm": 48,
"DeliveryMonth": 1,
"IsTrading": 1,
"StrikePrice": 0,
"OptionsType": 0
}
商品期货只有回测支持虚拟合约交易,实盘时虚拟合约只支持获取行情,实盘时我们可以用虚拟合约映射的真实合约下单,例如以下代码:
javascript
function main(){
var n = 0
while(true){
// 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数
if(exchange.IO("status")){
// 设置合约为虚拟合约,MA888,即甲醇主力合约
var ret = _C(exchange.SetContractType, "MA888")
var ticker = exchange.GetTicker()
// 当到达交易条件时
if(n == 100) {
exchange.SetContractType(ret.InstrumentID)
Log("设置映射的实际合约:", ret.InstrumentID)
exchange.SetDirection("buy")
var id = exchange.Buy(ticker.Buy - 10, 1)
Log("id:", id)
Sleep(1000)
Log(exchange.GetOrder(id))
Sleep(1000)
Log(exchange.GetPosition())
Sleep(1000)
exchange.CancelOrder(id)
Sleep(1000)
Log(exchange.GetOrders())
}
n++
LogStatus(_D(), "已经连接CTP !")
} else {
LogStatus(_D(), "未连接CTP !")
}
}
}
python
def main():
n = 0
while True:
if exchange.IO("status"):
ret = _C(exchange.SetContractType, "MA888")
ticker = exchange.GetTicker()
if n == 100:
exchange.SetContractType(ret["InstrumentID"])
Log("设置映射的实际合约:", ret["InstrumentID"])
exchange.SetDirection("buy")
id = exchange.Buy(ticker["Buy"] - 10, 1)
Log("id:", id)
Sleep(1000)
Log(exchange.GetOrder(id))
Sleep(1000)
Log(exchange.GetPosition())
Sleep(1000)
exchange.CancelOrder(id)
Sleep(1000)
Log(exchange.GetOrders())
n += 1
LogStatus(_D(), "已经连接CTP !")
else:
LogStatus(_D(), "未连接CTP !")
c++
void main() {
int n = 0;
while(true) {
if(exchange.IO("status") == 1) {
auto ret = exchange.SetContractType("MA888");
auto ticker = exchange.GetTicker();
if(n == 100) {
exchange.SetContractType(ret["InstrumentID"]);
Log("设置映射的实际合约:", ret["InstrumentID"]);
exchange.SetDirection("buy");
auto id = exchange.Buy(ticker.Buy - 10, 1);
Log("id:", id);
Sleep(1000);
Log(exchange.GetOrder(id));
Sleep(1000);
Log(exchange.GetPosition());
Sleep(1000);
exchange.CancelOrder(id);
Sleep(1000);
Log(exchange.GetOrders());
}
n++;
LogStatus(_D(), "已经连接CTP !");
} else {
LogStatus(_D(), "未连接CTP !");
}
}
}
exchange.GetContractType()
exchange.GetContractType(),返回交易所对象(exchange)当前设置的合约代码,返回值:字符串。
javascript
function main () {
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
Log(exchange.SetContractType("rb888"))
Log(exchange.GetContractType())
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
Log(exchange.SetContractType("rb888"))
Log(exchange.GetContractType())
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
Log(exchange.SetContractType("rb888"));
Log(exchange.GetContractType());
}
StrDecode()
StrDecode(),商品期货CTP协议中的汉字是GBK编码可用此函数解码。
传统商品期货
传统商品期货API获取更多信息,需要注意策略程序启动运行时需要确保和期货公司前置机服务器连接成功后(账号登录验证成功与否通过exchange.IO("status")函数调用时的返回值判断),才可以调用行情、交易等接口与期货公司前置机服务器通信。参考:exchange.IO(...)函数
javascript
function main() {
while (!exchange.IO("status")) {
LogStatus("正在等待与交易服务器连接, " + new Date())
Sleep(1000)
}
exchange.GetAccount()
// 在GetAccount成功后调用,获取更详细的账户信息,可以用JSON.parse解析
Log(exchange.GetRawJSON())
}
python
def main():
while not exchange.IO("status"):
LogStatus("正在等待与交易服务器连接, " + _D())
Sleep(1000)
exchange.GetAccount()
Log(exchange.GetRawJSON())
c++
void main() {
while(exchange.IO("status") == 0) {
LogStatus("正在等待与交易服务器连接, " + _D());
Sleep(1000);
}
exchange.GetAccount();
// C++ 不支持exchange.GetRawJSON()函数
}
同样支持exchange.GetTicker(),exchange.GetDepth(),exchange.GetPosition(),exchange.GetOrders(),exchange.GetOrder(Id)调用后,调用exchange.GetRawJSON()。
-
期货交易中
exchange.Buy(Price, Amount),exchange.Sell(Price, Amount),exchange.CancelOrder(Id)和现货交易的区别:exchange.Buy(Price, Amount)或exchange.Sell(Price, Amount)调用之前需要调用exchange.SetDirection(Direction)明确下单方向。javascriptfunction main() { while (!exchange.IO("status")) { LogStatus("正在等待与交易服务器连接, " + new Date()) Sleep(1000) } // 设置合约 exchange.SetContractType("MA888") // 设置为开空头仓位,调用exchange.Sell(...)函数下单开空头仓位 exchange.SetDirection("sell") var id = exchange.Sell(1000, 2) exchange.CancelOrder(id) }pythondef main(): while not exchange.IO("status"): LogStatus("正在等待与交易服务器连接, " + _D()) Sleep(1000) exchange.SetContractType("MA888") exchange.SetDirection("sell") id = exchange.Sell(1000, 2) exchange.CancelOrder(id)c++void main() { while(exchange.IO("status") == 0) { LogStatus("正在等待与交易服务器连接, " + _D()); Sleep(1000); } exchange.SetContractType("MA888"); exchange.SetDirection("sell"); auto id = exchange.Sell(1000, 2); exchange.CancelOrder(id); } -
商品期货支持自定义订单类型(支持实盘,回测暂不支持)。
以后缀方式指定,附加在_后面例如:CTP协议:
exchange.SetDirection("buy_ioc")
交易所暂不支持这种类型:exchange.SetDirection("sell_gtd-20170111")。CTP协议:
后缀 意义 对应CTP原始值 ioc 立即完成,否则撤销 THOST_FTDC_TC_IOC gfs 本节有效 THOST_FTDC_TC_GFS gfd 当日有效 THOST_FTDC_TC_GFD gtd 指定日期前有效 THOST_FTDC_TC_GTD gtc 撤销前有效 THOST_FTDC_TC_GTC gfa 集合竞价有效 THOST_FTDC_TC_GFA fak 部分成交,撤销剩余部分 THOST_FTDC_TC_IOC fok 未能完全成交,全部撤销 THOST_FTDC_TC_IOC 易盛协议:
后缀 意义 对应易盛协议原始值 gfd 当日有效 TAPI_ORDER_TIMEINFORCE_GFD gtc 撤销前有效 TAPI_ORDER_TIMEINFORCE_GTC gtd 指定日期前有效 TAPI_ORDER_TIMEINFORCE_GTD fak 部分成交,撤销剩余部分 TAPI_ORDER_TIMEINFORCE_FAK ioc 立即完成,否则撤销 TAPI_ORDER_TIMEINFORCE_FAK fok 未能完全成交,全部撤销 TAPI_ORDER_TIMEINFORCE_FOK -
易盛协议
- 部分接口调用时立即返回
执行exchange.GetOrder(id)、exchange.GetOrders()、exchange.GetPosition()、exchange.GetAccount()接口时立即返回。这些接口在交易所对象配置为易盛协议时,是立即返回的,本地维护数据,由期货公司前置机推送数据更新,不会阻塞。
- 部分接口调用时立即返回
Futures_OP报错
期货交易所对象相关的报错信息说明如下表:
| 值 | 错误函数显示 | 触发函数 | 描述 |
|---|---|---|---|
| 0 | Futures_OP 0 | exchange.SetMarginLevel | 调用杠杆函数时报错,商品期货不支持SetMarginLevel函数,所以不会报Futures_OP 0错误 |
| 1 | Futures_OP 1 | exchange.SetDirection | 设置期货交易方向函数报错 |
| 2 | Futures_OP 2 | exchange.SetContractType | 设置合约函数报错 |
| 3 | Futures_OP 3 | exchange.GetPosition | 获取持仓函数报错 |
| 4 | Futures_OP 4 | exchange.IO | 调用IO函数报错 |
期权交易
商品期货期权
需要期货公司开通相关权限,期权合约的代码形式,例如铁矿石期权:i2106-P-760。
商品期货期权合约基本格式:
标的期货合约 + 看涨 / 看跌 + 行权价
由于交易所的标准合约命名规则并不相同,且对大小写敏感,各交易所合约格式可能有差别,以下是具体交易所对应的合约格式。
上期所:小写 + 四个数字 +
C(或者P) + 行权价
郑商所:大写 + 三个数字 +C(或者P) + 行权价
中金所:大写 + 四个数字 +-C-(或者-P-) + 行权价
大商所:小写 + 四个数字 +-C-(或者-P-) + 行权价
交易所期权合约代码示例:
| 交易所 | 合约名称 | 看涨期权 | 看跌期权 | 看涨期权示例 | 看跌期权示例 |
|---|---|---|---|---|---|
| 中金所 | 沪深300 | IO合约月份-C-行权价格 | IO合约月份-P-行权价格 | IO2105-C-6400 | IO2105-P-6400 |
| 上期所 | 铜 | cu合约月份C行权价格 | cu合约月份P行权价格 | cu2106C69000 | cu2106P69000 |
| 上期所 | 黄金 | au合约月份C行权价格 | au合约月份P行权价格 | au2106C400 | au2106P400 |
| 上期所 | 铝 | al合约月份C行权价格 | al合约月份P行权价格 | al2106C20000 | al2106P20000 |
| 上期所 | 锌 | zn合约月份C行权价格 | zn合约月份P行权价格 | zn2106C23600 | zn2106P23600 |
| 上期所 | 橡胶 | ru合约月份C行权价格 | ru合约月份P行权价格 | ru2109C16000 | ru2109P16000 |
| 大商所 | 豆粕 | m合约月份-C-行权价格 | m合约月份-P-行权价格 | m2109-C-4000 | m2109-P-4000 |
| 大商所 | 玉米 | c合约月份-C-行权价格 | c合约月份-P-行权价格 | c2109-C-3000 | c2109-P-3000 |
| 大商所 | 铁矿 | i合约月份-C-行权价格 | i合约月份-P-行权价格 | i2109-C-1000 | i2109-P-1000 |
| 大商所 | 液化石油气 | pg合约月份-C-行权价格 | pg合约月份-P-行权价格 | pg2106-C-4000 | pg2106-P-4000 |
| 大商所 | PP | pp合约月份-C-行权价格 | pp合约月份-P-行权价格 | pp2109-C-9000 | pp2109-P-9000 |
| 大商所 | PVC | v合约月份-C-行权价格 | v合约月份-P-行权价格 | v2109-C-9000 | v2109-P-9000 |
| 大商所 | 塑料 | l合约月份-C-行权价格 | l合约月份-P-行权价格 | l2109-C-9000 | l2109-P-9000 |
| 郑商所 | 白糖 | SR合约月份C行权价格 | SR合约月份P行权价格 | SR109C5000 | SR109P5000 |
| 郑商所 | 棉花 | CF合约月份C行权价格 | CF合约月份P行权价格 | CF109C15000 | CF109P15000 |
| 郑商所 | PTA | TA合约月份C行权价格 | TA合约月份P行权价格 | TA109C5000 | TA109P5000 |
| 郑商所 | 甲醇 | MA合约月份C行权价格 | MA合约月份P行权价格 | MA109C2500 | MA109P2500 |
| 郑商所 | 菜粕 | RM合约月份C行权价格 | RM合约月份P行权价格 | RM109C3000 | RM109P3000 |
| 郑商所 | 动力煤 | ZC合约月份C行权价格 | ZC合约月份P行权价格 | ZC109C800 | ZC109P800 |
查询期权合约代码的例子:
javascript
function main(){
var productsForFind = null
while(true){
if(exchange.IO("status")){
LogStatus(_D(), "已经连接CTP !")
var ret = exchange.IO("instruments")
ret.forEach(function(product) {
// Log(product)
// 这里设置要查的名字,i铁矿石合约
if (product.InstrumentName.indexOf("i") != -1 && (product.InstrumentName.indexOf("P") != -1 || product.InstrumentName.indexOf("C") != -1)) {
Log(product, "#FF0000")
productsForFind = product
}
})
break
} else {
LogStatus(_D(), "未连接CTP !")
}
Sleep(1000)
}
Log(productsForFind, "#FF0000")
}
股票证券
中泰证券
支持中泰证券(XTP)。
富途证券
支持富途牛牛实盘交易、模拟盘交易,需要下载FutuOpenD软件。当使用FutuOpenD接入模拟交易时有些股票代码不支持所以无法交易,但是富途牛牛手机APP是可以模拟交易的。在优宽量化上配置交易所对象、运行FutuOpenD软件等操作参看富途证券配置说明文档。
-
接口调用频率
对于GetOrder、GetOrders、GetPosition、GetAccount函数默认使用缓存数据,所以不限制调用频率。当有新数据时FutuOpenD会自动更新数据,缓存数据也会同步更新。
调用exchange.IO("refresh", true)函数可以禁用缓存,如果禁用缓存则调用频率为每30秒内最多请求10次查询,超过频率限制会报错。 -
股票代码
例如:600519.SH- HK 港股
- US 美股
- SH 沪股
- SZ 深股
策略代码中使用
exchange.SetContractType()函数设置股票代码,例如:javascriptfunction main() { var info = exchange.SetContractType("600519.SH") // 设置为股票600519.SH即贵州茅台,账户即切换到大陆市场 Log(info) Log(exchange.GetAccount()) // 当前设置的股票为贵州茅台,此时调用GetAccount函数获取账户资产,获取的为大陆市场账户资产 Log(exchange.GetTicker()) // 获取股票贵州茅台当前的价格信息 }pythondef main(): info = exchange.SetContractType("600519.SH") Log(info) Log(exchange.GetAccount()) Log(exchange.GetTicker())c++void main() { auto info = exchange.SetContractType("600519.SH"); Log(info); Log(exchange.GetAccount()); Log(exchange.GetTicker()); }设置交易方向的函数
exchange.SetDirection、下单函数exchange.Buy/exchange.Sell、撤单函数exchange.CancelOrder、查询订单函数exchange.GetOrder等使用方法均和商品期货市场相同。 -
账户信息数据格式:
使用TrdMarket定义市场,用以区分香港市场,美国市场,大陆市场。摘录自
Futu API文档:javascriptconst ( TrdMarket_TrdMarket_Unknown TrdMarket = 0 //未知市场 TrdMarket_TrdMarket_HK TrdMarket = 1 //香港市场 TrdMarket_TrdMarket_US TrdMarket = 2 //美国市场 TrdMarket_TrdMarket_CN TrdMarket = 3 //大陆市场 TrdMarket_TrdMarket_HKCC TrdMarket = 4 //香港A股通市场 TrdMarket_TrdMarket_Futures TrdMarket = 5 //期货市场 )获取账户信息数据,
exchange.GetAccount()函数返回:javascript{ "Info": [{ "Header": { ... // 省略 "TrdMarket": 1 // Info原始信息中,市场ID,表示该账户资产用于香港市场交易 }, "Funds": { // 账户在该市场的资产信息 ... } }, ...], "Stocks": 0, "FrozenStocks": 0, "Balance": 1000000, // 当前市场的资产数值 "FrozenBalance": 0 } -
FutuOpenD根据登录的IP地址作为地区区分
对于非大陆IP地址登录的账户在获取行情数据时有所限制,具体可以查阅FutuOpenD(富途)官方文档。
网络设置
exchange.SetProxy(...)
exchange.SetProxy(...),切换为代理服务器访问交易所,该函数无返回值(用变量获取,获取的是undefined)。如果代理设置失败,在调用接口时会返回空值。仅限rest协议。每个交易所对象exchanges[n]可以设置一个代理,设置代理后访问交易所接口都会通过代理去访问。
以第一个添加的交易所对象exchange即:exchanges[0]为例:
- 设置代理,无用户名,无密码:
exchange.SetProxy("socks5://127.0.0.1:8889") - 设置代理,输入用户名和密码:
exchange.SetProxy("socks5://username:password@127.0.0.1:8889")。username为用户名,password为密码。 - 切换为正常模式,不使用代理:
exchange.SetProxy("")
支持设置交易所对象发出请求的IP地址
-
全局指定
windows系统的界面版托管者可以直接设置,如图:其它命令行环境运行的托管者使用
-I参数指定IP地址。 -
基于exchange指定
javascriptfunction main(){ exchange.SetProxy("ip://10.0.3.15") exchange.GetTicker() // 发出的请求IP地址为10.0.3.15 }
C++策略编写特殊事项
C++编写策略和JavaScript编写策略区别主要为优宽 API接口返回数据的区别。例如exchange.GetTicker()函数。
-
JavaScript
exchange.GetTicker()调用成功时返回一个对象,如果调用失败(交易所服务器问题、网络问题等等)返回null。javascriptfunction main() { // 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态 while(!exchange.IO("status")) { Sleep(1000) } // 设置合约为rb888即螺纹钢主力连续合约,或者设置股票代码 exchange.SetContractType("rb888") var ticker = exchange.GetTicker() // 判断exchange.GetTicker函数是否调用失败,返回null if (ticker){ Log(ticker) } } -
C++
exchange.GetTicker()调用成功时返回一个对象,如果调用失败返回的还是一个对象,和正常返回的对象是通过一个属性Valid来区别的。c++void main() { while(exchange.IO("status") == 0) { Sleep(1000); } exchange.SetContractType("rb888"); auto ticker = exchange.GetTicker(); // 判断exchange.GetTicker()函数是否调用失败,返回的对象中Valid属性是否是false if (ticker.Valid) { Log(ticker); } }
C++策略中的main()函数与标准C11中main()函数的区别:
C11中的C++程序入口函数main()返回值为int类型,在优宽的C++策略中,策略的启动函数也是main()函数。不过这两者并非是同一个函数,只是同名而已。并且优宽的C++策略中main()函数的返回值是void类型。
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
// 使用Test函数测试
if (!Test("c++")) {
// 抛出异常,让程序停止
Panic("请下载最新版本托管者");
}
// 所有的对像返回用Valid来判断是否有效
LogProfitReset();
LogReset();
Log(_N(9.12345, 2));
Log("use _C", _C(exchange.GetTicker), _C(exchange.GetAccount));
}
JavaScript多线程
从系统底层真正支持JavaScript语言策略的多线程功能。包括:并发执行自定义的执行函数;支持并发线程之间的通信、支持并发线程与主线程之间的通信;储存、共享线程环境中的变量等功能。目前仅支持实盘环境中使用,参考帖子:https://www.youquant.com/digest-topic/9451。
__Thread
__Thread(function, arguments...)函数创建一个并发运行的线程,不支持引用线程执行函数以外的变量(做为隔离环境运行),引用外部变量将会编译失败。也不支持对其它闭包函数引用,线程内部可调用平台所有API,但不能调用用户自定义的其它函数。参数function可以为函数引用或者匿名函数。参数arguments为function函数的参数(实际传入的参数),arguments...表示可以传多个。返回值:线程Id。
javascript
function testFunc(n) {
Log("执行函数testFunc,参数n:", n)
}
function main() {
var testThread1 = __Thread(function () {
Log("执行匿名函数,没有参数。")
})
var testThread2 = __Thread(testFunc, 10) // 参数n : 10
__threadJoin(testThread1) // 可以使用__threadJoin函数等待并发的线程执行完毕
__threadJoin(testThread2) // 如果没有等待testThread1、testThread2执行完成,主线程先执行完毕后会自动释放并发的线程,终止并发的线程的执行函数
}
支持__Thread([function, arguments...], [function, arguments...], ...)的调用方式,即创建的线程中顺序执行多个线程执行函数。
javascript
function threadTestFuncA(a) {
Log(a)
threadTestFuncC(4)
// 可以调用threadTestFuncC函数,但是不能调用threadTestFuncB函数
// this.d
Log(d)
}
function threadTestFuncB(b) {
Log(b)
threadTestFuncC(2)
this.d = 5
}
function main() {
// 先执行 threadTestFuncB 函数,再执行 threadTestFuncA 函数
// threadTestFuncC 不会自动执行,但是可以被其它线程执行函数调用
var threadId = __Thread([threadTestFuncA, 3], [threadTestFuncB, 1], ["function threadTestFuncC(c) {Log(c)}"])
__threadJoin(threadId)
}
传入__Thread函数的并发执行函数会按照倒序依次执行,以上例子会依次使用Log函数打印1 ~ 5。支持不同的线程执行函数共享变量,例如上例中的this.d变量,可以在threadTestFuncB函数中赋值,在threadTestFuncA函数中使用。支持传入函数字符串,例如上例中的"function threadTestFuncC(c) {Log(c)}",可以让线程执行函数调用通过这种方法“导入”的外部函数、库。
对于导入外部库来说,一个具体的使用例子如下:
javascript
function ml(input) {
const net = new brain.NeuralNetwork();
net.train([
{ input: [0, 0], output: [0] },
{ input: [0, 1], output: [1] },
{ input: [1, 0], output: [1] },
{ input: [1, 1], output: [0] },
]);
return net.run(input);
}
function main() {
Log(__threadJoin(__Thread([ml, [1, 0]], [HttpQuery("https://unpkg.com/brain.js")])))
}
__threadPeekMessage
__threadPeekMessage(threadId, timeout)函数从线程通信的通道中读取数据,参数threadId为__Thread()函数返回的Id,设置threadId参数表示接收该threadId代表的线程发送来的数据,设置为0时表示接收主线程即当前主函数发送来的数据(参数threadId设置为0仅支持在并发的线程执行函数中使用)。参数timeout为超时设置,会按照该参数设置的毫秒数阻塞等待。如果timeout设置为-1则表示一直阻塞等待,直到接收到通道中的数据。当通道的发送端线程执行结束,并且没有数据,__threadPeekMessage函数会立即返回空值。返回值:接收到的数据。
在编写程序时需要注意线程死锁问题。以下例子为创建的并发线程的执行函数testFunc和主线程的main函数通信,线程执行函数testFunc会先执行完成。
javascript
function testFunc() {
for(var i = 0 ; i < 5 ; i++) { // 0 ~ 5 ,向主线程发送5次之后该线程函数执行完成,main函数中__threadPeekMessage函数把所有数据取完,不会再阻塞,立即返回空值
__threadPostMessage(0, i) // 发送数据到主线程
var msg = __threadPeekMessage(0, -1) // 监听来自主线程的数据
Log("from main msg:", msg)
Sleep(500)
}
Log("testFunc执行完毕")
}
function main() {
var testThread = __Thread(testFunc) // 创建线程,Id为1
for (var i = 0 ; i < 10 ; i++) {
__threadPostMessage(1, i) // 发送数据到Id为1的线程,即本例中执行testFunc函数的线程
var msg = __threadPeekMessage(1, -1) // 监听Id为1的线程发送来的数据,即本例中执行testFunc函数的线程发送来的数据
Log("from testFunc msg:", msg)
Sleep(500)
}
}
__threadPostMessage
__threadPostMessage(threadId, data)函数向线程通信的通道中写入数据,参数threadId为__Thread()函数返回的Id,设置threadId参数表示向该threadId代表的线程发送数据,设置为0时表示向主线程即当前主函数发送数据(参数threadId设置为0仅支持在并发的线程执行函数中使用)。参数data可以传数值、字符串、布尔值、对象、数组等类型的数据。该函数没有返回值。
当一个线程的执行函数中调用__threadPostMessage函数发出信号、数据时,也会产生消息事件。可以用EventLoop()函数收到消息通知。
javascript
function testFunc() {
for(var i = 0 ; i < 10 ; i++) {
Log("post msg, i:", i)
__threadPostMessage(0, {msg: "testFunc", i: i})
Sleep(100)
}
}
function main() {
var testThread = __Thread(testFunc)
for (var i = 0 ; i < 10 ; i++) {
var e = EventLoop()
Log("e:", e)
// e: {"Seq":1,"Event":"thread","Index":1,"Nano":1677745512064773600,"Deleted":0,"Symbol":"","Ticker":{"Info":null,"High":0,"Low":0,"Sell":0,"Buy":0,"Last":0,"Volume":0,"OpenInterest":0,"Time":0}}
if (e.Event == "thread") {
var msg = __threadPeekMessage(testThread, -1)
Log("msg:", msg, "#FF0000")
}
Sleep(500)
}
var retThreadJoin = __threadJoin(testThread)
Log("retThreadJoin:", retThreadJoin)
}
__threadJoin
__threadJoin(threadId, timeout)函数用于等待指定Id的线程退出,并回收系统资源。参数threadId为__Thread()函数返回的Id,参数timeout为等待线程结束的超时设置,单位为毫秒,不设置timeout即为等待到线程执行结束。返回值:类型为对象,表示执行结果,如果超时返回undefined。
返回值结构例如:
javascript
{
"id":1, // 线程Id
"terminated":false, // 线程是否被强制结束
"elapsed":2504742813, // 线程的运行时间(纳秒)
"ret": 123 // 线程函数的返回值
}
__threadTerminate
__threadTerminate函数用于强制结束线程,释放创建线程使用的硬件资源(无法再使用__threadJoin等待结束)。参数threadId为__Thread()函数返回的Id。返回值:布尔值,表示执行结果。
javascript
function testFunc() {
for(var i = 0 ; i < 10 ; i++) {
Log("i:", i)
Sleep(500)
}
}
function main() {
var testThread = __Thread(testFunc)
var retThreadTerminate = null
for (var i = 0 ; i < 10 ; i++) {
Log("main i:", i)
if (i == 5) {
retThreadTerminate = __threadTerminate(testThread)
}
Sleep(500)
}
Log("retThreadTerminate:", retThreadTerminate)
}
__threadGetData
__threadGetData(threadId, key),该函数用于访问线程间共享的变量,数据在线程没有被执行__threadJoin函数(等待退出成功)并且没有被执行__threadTerminate函数(强制终止线程)的情况下有效。参数threadId为线程Id,参数key为储存的键-值对的键名。返回值:返回键-值对中key对应的键值,
threadId是0表示主线程(即main函数所在的线程),可以使用__threadId()函数获取当前线程的Id,设置参数threadId为当前线程Id,用于在线程执行函数中读取当前线程储存的变量。也可以读取指定Id的线程环境中的变量。
javascript
function main() {
var t1 = __Thread(function() {
Sleep(2000)
var id = __threadId() // 获取当前线程的Id
Log("id:", id, ", in testThread1 print:", __threadGetData(id, "msg")) // 读取当前线程中的键名为msg对应的键值,即"testThread2"
Log("id:", 2, ", in testThread1 print:", __threadGetData(2, "msg")) // 读取线程Id为2的线程中的键名为msg对应的键值,即99
})
var t2 = __Thread(function(t) {
__threadSetData(t, "msg", "testThread2") // 向Id为t1(Id为1)的线程设置键值对,键名msg,键值为字符串"testThread2"
__threadSetData(__threadId(), "msg", 99) // 在当前线程(Id为2)设置键值对,键名msg,键值为数值99
__threadSetData(0, "msg", 100) // 在主线程设置键值对,键名msg,键值为数值100
}, t1)
__threadJoin(t1) // 可以查看__threadJoin(threadId, timeout)函数,用于等待线程执行结束
Log("in main, get msg:", __threadGetData(0, "msg"))
}
__threadSetData
__threadSetData(threadId, key, value),该函数用于在线程环境储存变量。参数threadId为线程Id,参数key为储存的键-值对的键名,参数value为键值。该函数无返回值。
threadId是0表示主线程(即main函数所在的线程),可以使用__threadId()函数获取当前线程的Id。value不指定表示删除key。支持线程之间相互访问共享变量,数据在线程没有被执行__threadJoin函数(等待退出成功)并且没有被执行__threadTerminate函数(强制终止线程)的情况下有效。参数value的值必须是可序列化的变量。
javascript
function testFunc() {
var id = __threadId() // 获取当前线程Id
__threadSetData(id, "testFunc", 100) // 储存在当前线程环境
__threadSetData(0, "testFunc", 99) // 储存在主线程环境
Log("testFunc执行完毕")
}
function main() {
// threadId为1 ,创建的threadId为1的线程会先执行完,只要线程资源没有被回收,线程本地储存的变量就有效
var testThread = __Thread(testFunc)
Sleep(1000)
// 输出 in main, get testFunc: 100
Log("in main, get testFunc:", __threadGetData(testThread, "testFunc"))
// 输出 in main, get testFunc: 99
Log("in main, get testFunc:", __threadGetData(0, "testFunc"))
// 删除Id为testThread的线程环境中的testFunc键值对
__threadSetData(testThread, "testFunc")
// 删除之后,再次读取,__threadGetData函数返回undefined
Log("in main, get testFunc:", __threadGetData(testThread, "testFunc"))
}
__threadId
__threadId(),该函数用于获取当前线程的Id,无参数。返回值:当前线程的threadId。
javascript
function testFunc() {
Log("in testFunc, __threadId():", __threadId())
}
function main() {
__Thread(testFunc)
// 如果主线程执行完成,创建的子线程会停止执行,所以这里Sleep(1000),等待1秒
Sleep(1000)
Log("in main, __threadId():", __threadId())
}
支持wasm编码
在JavaScript语言的策略中,可以载入wasm文件的hex编码,并且实例化,执行其中的代码。相较于执行JavaScript代码具有一定速度优势。
wasm.parseModule
wasm.parseModule(data),该函数解析一个hex字符串模型。data参数是已经转换成hex字符串的wasm编码。返回值:返回一个wasm模型对象,可以参考策略例子。
例如可以将以下c++函数代码编译为wasm代码,再转换为hex字符串,用作wasm.parseModule(data)函数的data参数。
c++
// 斐波纳契数字的递归算法
int fib(int f) {
if (f < 2) return f;
return fib(f - 1) + fib(f - 2);
}
wasm.buildInstance
wasm.buildInstance(module, opt),该函数创建一个wasm模型实例。module参数为wasm模型,opt参数为配置信息,用于设置分配给wasm实例程序的堆栈空间。返回值:返回一个wasm模型实例。
opt参数设置例子:
{
stack_size: 65*1024*1024,
}
callFunction
callFunction(funcName, param1, ...),该函数是wasm模型实例的一个方法,用于执行wasm模型实例中的函数。funcName参数为需要执行的函数名,param1参数为执行函数(由参数funcName指定)时传入的参数。
指标函数
调用指标函数时需要加上TA.或者talib.前缀
talib库、TA库指标函数调用例子:
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(true) {
if (exchange.IO("status")) {
exchange.SetContractType("rb888")
var records = exchange.GetRecords()
// K线需要有足够长度才可以计算指标数据
if (records && records.length > 100) {
break
}
}
Sleep(1000)
}
var r = exchange.GetRecords()
var macd = TA.MACD(r)
var atr = TA.ATR(r, 14)
// 打印出最后一组指标值
Log(macd[0][r.length-1], macd[1][r.length-1], macd[2][r.length-1])
Log(atr[atr.length-1])
// 打印所有指标数据,在优宽量化交易平台上,JavaScript语言的策略已经集成了talib库
Log(talib.MACD(r))
Log(talib.MACD(r, 12, 26, 9))
Log(talib.OBV(r))
// talib库的指标函数也可以传入数值数组,依次传入即可。如: OBV(Records[Close], Records[Volume]),需要Close,Volume两个数组参数
Log(talib.OBV([1,2,3], [7.1, 6.2, 3, 3]))
// 也可以直接传入包含Close,Volume属性的records数组
Log(talib.OBV(r))
Log(TA.Highest(r, 30, 'High'))
Log(TA.Highest([1,2,3,4], 0))
}
python
# Python需要单独安装talib库
import talib
def main():
while True:
if exchange.IO("status"):
exchange.SetContractType("rb888")
records = exchange.GetRecords()
# records 为 None、空数组[] 判断均为假
if records and len(records) > 100:
break
Sleep(1000)
r = exchange.GetRecords()
macd = TA.MACD(r)
atr = TA.ATR(r, 14)
Log(macd[0][-1], macd[1][-1], macd[2][-1])
Log(atr[-1])
# 针对Python,系统扩展了GetRecords返回的数组的属性,增加了Open,High,Low,Close,Volume方便talib库函数调用
Log(talib.MACD(r.Close))
Log(talib.MACD(r.Close, 12, 26, 9))
Log(talib.OBV(r.Close, r.Volume))
Log(TA.Highest(r, 30, "High"))
Log(TA.Highest([1, 2, 3, 4], 0))
c++
void main() {
while (true) {
if (exchange.IO("status") != 0) {
exchange.SetContractType("rb888");
auto records = exchange.GetRecords();
if (records.Valid && records.size() > 100) {
break;
}
}
Sleep(1000);
}
auto r = exchange.GetRecords();
auto macd = TA.MACD(r);
auto atr = TA.ATR(r, 14);
Log(macd[0][macd[0].size() - 1], macd[1][macd[1].size() - 1], macd[2][macd[2].size() - 1]);
Log(atr[atr.size() - 1]);
Log(talib.MACD(r));
Log(talib.MACD(r, 12, 26, 9));
Log(talib.OBV(r));
Log(TA.Highest(r.Close(), 30));
}
以下参数中的数据均为exchange.GetRecords(Period)函数获取的数据。
注意records的长度,当数据长度不足指标函数的参数计算要求时将返回无效值。
TA-常用指标库
优宽量化的TA指标库,优化了常用指标算法。支持JavaScript、Python、C++语言的策略调用,开源TA库代码。
以下测试例子除特殊例子以外,均不考虑K线长度是否足够,默认为K线数据有足够的线柱数量。设计策略中,计算指标时需要考虑K线长度是否满足指标参数。
MACD,指数平滑异同平均线
TA.MACD(数据, 快周期, 慢周期, 信号周期),默认周期参数为(12, 26, 9),返回二维数组,分别是[DIF, DEA, MACD]。
javascript
function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
// 可以填入不同k线周期,比如PERIOD_M1,PERIOD_M30,PERIOD_H1......
var records = exchange.GetRecords(PERIOD_M15)
var macd = TA.MACD(records, 12, 26, 9)
// 观看日志可得知返回三个数组,分别对应DIF,DEA,MACD
Log("DIF:", macd[0], "DEA:", macd[1], "MACD:", macd[2])
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
r = exchange.GetRecords(PERIOD_M15)
macd = TA.MACD(r, 12, 26, 9)
Log("DIF:", macd[0], "DEA:", macd[1], "MACD:", macd[2])
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto r = exchange.GetRecords(PERIOD_M15);
auto macd = TA.MACD(r, 12, 26, 9);
Log("DIF:", macd[0], "DEA:", macd[1], "MACD:", macd[2]);
}
KDJ,随机指标
TA.KDJ(数据, 周期1, 周期2, 周期3),默认周期参数为(9, 3, 3),返回二维数组,分别是[K, D, J]。
javascript
function main(){
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var records = exchange.GetRecords(PERIOD_M15)
var kdj = TA.KDJ(records, 9, 3, 3)
Log("k:", kdj[0], "d:", kdj[1], "j:", kdj[2])
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
r = exchange.GetRecords(PERIOD_M15)
kdj = TA.KDJ(r, 9, 3, 3)
Log("k:", kdj[0], "d:", kdj[1], "j:", kdj[2])
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto r = exchange.GetRecords();
auto kdj = TA.KDJ(r, 9, 3, 3);
Log("k:", kdj[0], "d:", kdj[1], "j:", kdj[2]);
}
RSI,强弱指标
TA.RSI(数据, 周期),默认周期参数为14,返回一个一维数组。
javascript
function main(){
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var records = exchange.GetRecords(PERIOD_M30)
var rsi = TA.RSI(records, 14)
Log(rsi)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
r = exchange.GetRecords(PERIOD_M30)
rsi = TA.RSI(r, 14)
Log(rsi)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto r = exchange.GetRecords(PERIOD_M30);
auto rsi = TA.RSI(r, 14);
Log(rsi);
}
ATR,平均真实波幅
TA.ATR(数据, 周期),ATR(数据, 周期),默认周期参数为14,返回一个一维数组。
javascript
function main(){
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var records = exchange.GetRecords(PERIOD_M30)
var atr = TA.ATR(records, 14)
Log(atr)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
r = exchange.GetRecords(PERIOD_M30)
atr = TA.ATR(r, 14)
Log(atr)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto r = exchange.GetRecords(PERIOD_M30);
auto atr = TA.ATR(r, 14);
Log(atr);
}
OBV,能量潮
TA.OBV(数据),返回一个一维数组。
javascript
function main(){
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var records = exchange.GetRecords(PERIOD_M30)
var obv = TA.OBV(records)
Log(obv)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
r = exchange.GetRecords(PERIOD_M30)
obv = TA.OBV(r)
Log(obv)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto r = exchange.GetRecords(PERIOD_M30);
auto obv = TA.OBV(r);
Log(obv);
}
MA,移动平均线
TA.MA(数据, 周期),MA(数据, 周期),默认周期参数为9,返回一个一维数组。
javascript
function main(){
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var records = exchange.GetRecords(PERIOD_M30)
var ma = TA.MA(records, 14)
Log(ma)
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
r = exchange.GetRecords(PERIOD_M30)
ma = TA.MA(r, 14)
Log(ma)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto r = exchange.GetRecords(PERIOD_M30);
auto ma = TA.MA(r, 14);
Log(ma);
}
EMA,指数平均数指标
TA.EMA(数据, 周期),指数平均数指标。默认周期参数为9,返回一个一维数组。
javascript
function main(){
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var records = exchange.GetRecords()
// 判断K线bar数量是否满足指标计算周期
if (records && records.length > 9) {
var ema = TA.EMA(records, 9)
Log(ema)
}
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
r = exchange.GetRecords()
if r and len(r) > 9:
ema = TA.EMA(r, 9)
Log(ema)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto r = exchange.GetRecords();
if(r.Valid && r.size() > 9) {
auto ema = TA.EMA(r, 9);
Log(ema);
}
}
BOLL,布林带
TA.BOLL(数据, 周期, 乘数),BOLL(数据, 周期, 乘数),布林线指标,默认周期参数为(20, 2),返回一个二维数组[上线, 中线, 下线]。
javascript
function main() {
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var records = exchange.GetRecords()
if(records && records.length > 20) {
var boll = TA.BOLL(records, 20, 2)
var upLine = boll[0]
var midLine = boll[1]
var downLine = boll[2]
Log(upLine)
Log(midLine)
Log(downLine)
}
}
python
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
r = exchange.GetRecords()
if r and len(r) > 20:
boll = TA.BOLL(r, 20, 2)
upLine = boll[0]
midLine = boll[1]
downLine = boll[2]
Log(upLine)
Log(midLine)
Log(downLine)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto r = exchange.GetRecords();
if(r.Valid && r.size() > 20) {
auto boll = TA.BOLL(r, 20, 2);
auto upLine = boll[0];
auto midLine = boll[1];
auto downLine = boll[2];
Log(upLine);
Log(midLine);
Log(downLine);
}
}
Alligator,Alligator Indicator
TA.Alligator(数据, 下颚周期,牙齿周期,上唇周期),Alligator(数据, 下颚周期,牙齿周期,上唇周期),鳄鱼线指标,默认周期参数为(13,8,5),返回一个二维数组[下颚,牙齿,上唇]。
CMF,Chaikin Money Flow
TA.CMF(数据, 周期),蔡金货币流量指标,CMF(数据, 周期),默认周期参数为20,返回一个一维数组。
Highest,周期最高价
TA.Highest(数据, 周期, 属性),返回最近周期内的最大值(不包含当前Bar),如TA.Highest(records, 30, 'High'),如果周期为0指所有Bar,如属性不指定则视数据为普通数组,返回一个价格(数值类型)。
Lowest,周期最低价
TA.Lowest(数据, 周期, 属性),返回最近周期内的最小值(不包含当前Bar),如TA.Lowest(records, 30, 'Low'),如果周期为0指所有Bar,如属性不指定则视数据为普通数组,返回一个价格(数值类型)。
C++策略中TA.Highest(...)、TA.Lowest(...)的使用需注意,Highest、Lowest函数各自只有2个参数,并且第一个参数传入的不是auto r = exchange.GetRecords()函数返回值。需要调用r的方法传入具体属性数据,例如:传入r.Close()收盘价数据。Close、High、Low、Open、Volume如同r.Close()调用方式。
C++范例:
c++
void main() {
// 构造的K线数据,并非接口返回的数据
Records r;
r.Valid = true;
for (auto i = 0; i < 10; i++) {
Record ele;
ele.Time = i * 100000;
ele.High = i * 10000;
ele.Low = i * 1000;
ele.Close = i * 100;
ele.Open = i * 10;
ele.Volume = i * 1;
r.push_back(ele);
}
for(int j = 0; j < r.size(); j++){
Log(r[j]);
}
// 注意:第一个参数传入的不是r,需要调用r.Close()
auto highest = TA.Highest(r.Close(), 8);
Log(highest);
}
附带的第三方库
JavaScript
C++
例子
-
JavaScript库
http://mathjs.org/javascriptfunction main() { Log(math.round(math.e, 3)) // 2.718 Log(math.atan2(3, -3) / math.pi) // 0.75 Log(math.log(10000, 10)) // 4 Log(math.sqrt(-4)) // {"mathjs":"Complex","re":0,"im":2} }http://mikemcl.github.io/decimal.js/
javascriptfunction main() { var x = -1.2 var a = Decimal.abs(x) var b = new Decimal(x).abs() Log(a.equals(b)) // true var y = 2.2 var sum = Decimal.add(x, y) Log(sum.equals(new Decimal(x).plus(y))) // true }javascriptfunction main() { var sum = _.reduce([1, 2, 3], function(memo, num){return memo + num}, 0) Log(sum) } -
C++库
https://nlohmann.github.io/json/c++void main() { json table = R"({"type": "table", "title": "持仓信息", "cols": ["列1", "列2"], "rows": [["abc", "def"], ["ABC", "support color #ff0000"]]})"_json; LogStatus("`" + table.dump() + "`"); LogStatus("第一行消息\n`" + table.dump() + "`\n第三行消息"); json arr = R"([])"_json; arr.push_back(table); arr.push_back(table); LogStatus("`" + arr.dump() + "`"); table = R"({ "type" : "table", "title" : "持仓操作", "cols" : ["列1", "列2", "Action"], "rows" : [ ["abc", "def", {"type": "button", "cmd": "coverAll", "name": "平仓"}] ] })"_json; LogStatus("`" + table.dump() + "`", "\n`" + R"({"type": "button", "cmd": "coverAll", "name": "平仓"})"_json.dump() + "`"); }
talib库里面的函数指标
以下函数的参数中Records[Close]代表传入的k线数据中的收盘价,Array()代表数组,Array(outInteger)代表返回的是整型数据数组。
CCI指标调用范例代码:
javascript
function main() {
while(!exchange.IO("status")) {
Sleep(1000)
}
exchange.SetContractType("rb888")
var records = exchange.GetRecords()
var cci = talib.CCI(records, 14)
Log(cci)
}
python
import talib
def main():
while not exchange.IO("status"):
Sleep(1000)
exchange.SetContractType("rb888")
records = exchange.GetRecords()
# 14这个参数可以缺省
cci = talib.CCI(records.High, records.Low, records.Close, 14)
Log(cci)
c++
void main() {
while(exchange.IO("status") == 0) {
Sleep(1000);
}
exchange.SetContractType("rb888");
auto records = exchange.GetRecords();
auto cci = talib.CCI(records, 14);
Log(cci);
}
-
Pattern Recognition模式识别:指标 描述 CDL2CROWS Two Crows (K线图--两只乌鸦) CDL2CROWS(Records[Open,High,Low,Close]) = Array(outInteger) CDL3BLACKCROWS Three Black Crows (K线图--3只黑乌鸦) CDL3BLACKCROWS(Records[Open,High,Low,Close]) = Array(outInteger) CDL3INSIDE Three Inside Up/Down (K线图:3内上下震荡) CDL3INSIDE(Records[Open,High,Low,Close]) = Array(outInteger) CDL3LINESTRIKE Three-Line Strike (K线图:3线震荡) CDL3LINESTRIKE(Records[Open,High,Low,Close]) = Array(outInteger) CDL3OUTSIDE Three Outside Up/Down (K线图:3外下震荡) CDL3OUTSIDE(Records[Open,High,Low,Close]) = Array(outInteger) CDL3STARSINSOUTH Three Stars In The South (K线图:南方三星) CDL3STARSINSOUTH(Records[Open,High,Low,Close]) = Array(outInteger) CDL3WHITESOLDIERS Three Advancing White Soldiers (K线图:三白兵) CDL3WHITESOLDIERS(Records[Open,High,Low,Close]) = Array(outInteger) CDLABANDONEDBABY Abandoned Baby (K线图:弃婴) CDLABANDONEDBABY(Records[Open,High,Low,Close],Penetration = 0.3) = Array(outInteger) CDLADVANCEBLOCK Advance Block (K线图:推进) CDLADVANCEBLOCK(Records[Open,High,Low,Close]) = Array(outInteger) CDLBELTHOLD Belt-hold (K线图:带住) CDLBELTHOLD(Records[Open,High,Low,Close]) = Array(outInteger) CDLBREAKAWAY Breakaway (K线图:分离) CDLBREAKAWAY(Records[Open,High,Low,Close]) = Array(outInteger) CDLCLOSINGMARUBOZU Closing Marubozu (K线图:收盘光头光脚) CDLCLOSINGMARUBOZU(Records[Open,High,Low,Close]) = Array(outInteger) CDLCONCEALBABYSWALL Concealing Baby Swallow (K线图:藏婴吞没形态) CDLCONCEALBABYSWALL(Records[Open,High,Low,Close]) = Array(outInteger) CDLCOUNTERATTACK Counterattack (K线图:反击) CDLCOUNTERATTACK(Records[Open,High,Low,Close]) = Array(outInteger) CDLDARKCLOUDCOVER Dark Cloud Cover (K线图:乌云盖) CDLDARKCLOUDCOVER(Records[Open,High,Low,Close],Penetration = 0.5) = Array(outInteger) CDLDOJI Doji (K线图:十字星 ) CDLDOJI(Records[Open,High,Low,Close]) = Array(outInteger) CDLDOJISTAR Doji Star (K线图:十字星) CDLDOJISTAR(Records[Open,High,Low,Close]) = Array(outInteger) CDLDRAGONFLYDOJI Dragonfly Doji (K线图:蜻蜓十字星) CDLDRAGONFLYDOJI(Records[Open,High,Low,Close]) = Array(outInteger) CDLENGULFING Engulfing Pattern (K线图:吞没) CDLENGULFING(Records[Open,High,Low,Close]) = Array(outInteger) CDLEVENINGDOJISTAR Evening Doji Star (K线图:黄昏十字星) CDLEVENINGDOJISTAR(Records[Open,High,Low,Close],Penetration = 0.3) = Array(outInteger) CDLEVENINGSTAR Evening Star (K线图:黄昏之星) CDLEVENINGSTAR(Records[Open,High,Low,Close],Penetration = 0.3) = Array(outInteger) CDLGAPSIDESIDEWHITE Up/Down-gap side-by-side white lines (K线图:上/下间隙并排的白色线条) CDLGAPSIDESIDEWHITE(Records[Open,High,Low,Close]) = Array(outInteger) CDLGRAVESTONEDOJI Gravestone Doji (K线图:墓碑十字线) CDLGRAVESTONEDOJI(Records[Open,High,Low,Close]) = Array(outInteger) CDLHAMMER Hammer (K线图:锤) CDLHAMMER(Records[Open,High,Low,Close]) = Array(outInteger) CDLHANGINGMAN Hanging Man (K线图:吊人) CDLHANGINGMAN(Records[Open,High,Low,Close]) = Array(outInteger) CDLHARAMI Harami Pattern (K线图:阴阳线) CDLHARAMI(Records[Open,High,Low,Close]) = Array(outInteger) CDLHARAMICROSS Harami Cross Pattern (K线图:交叉阴阳线) CDLHARAMICROSS(Records[Open,High,Low,Close]) = Array(outInteger) CDLHIGHWAVE High-Wave Candle (K线图:长脚十字线 ) CDLHIGHWAVE(Records[Open,High,Low,Close]) = Array(outInteger) CDLHIKKAKE Hikkake Pattern (K线图:陷阱) CDLHIKKAKE(Records[Open,High,Low,Close]) = Array(outInteger) CDLHIKKAKEMOD Modified Hikkake Pattern (K线图:改良的陷阱) CDLHIKKAKEMOD(Records[Open,High,Low,Close]) = Array(outInteger) CDLHOMINGPIGEON Homing Pigeon (K线图:信鸽) CDLHOMINGPIGEON(Records[Open,High,Low,Close]) = Array(outInteger) CDLIDENTICAL3CROWS Identical Three Crows (K线图:相同的三只乌鸦) CDLIDENTICAL3CROWS(Records[Open,High,Low,Close]) = Array(outInteger) CDLINNECK In-Neck Pattern (K线图:颈纹) CDLINNECK(Records[Open,High,Low,Close]) = Array(outInteger) CDLINVERTEDHAMMER Inverted Hammer (K线图:倒锤) CDLINVERTEDHAMMER(Records[Open,High,Low,Close]) = Array(outInteger) CDLKICKING Kicking (K线图:踢) CDLKICKING(Records[Open,High,Low,Close]) = Array(outInteger) CDLKICKINGBYLENGTH Kicking - bull/bear determined by the longer marubozu (K线图:踢牛/踢熊) CDLKICKINGBYLENGTH(Records[Open,High,Low,Close]) = Array(outInteger) CDLLADDERBOTTOM Ladder Bottom (K线图:梯底) CDLLADDERBOTTOM(Records[Open,High,Low,Close]) = Array(outInteger) CDLLONGLEGGEDDOJI Long Legged Doji (K线图:长腿十字线) CDLLONGLEGGEDDOJI(Records[Open,High,Low,Close]) = Array(outInteger) CDLLONGLINE Long Line Candle (K线图:长线) CDLLONGLINE(Records[Open,High,Low,Close]) = Array(outInteger) CDLMARUBOZU Marubozu (K线图:光头光脚 ) CDLMARUBOZU(Records[Open,High,Low,Close]) = Array(outInteger) CDLMATCHINGLOW Matching Low (K线图:匹配低) CDLMATCHINGLOW(Records[Open,High,Low,Close]) = Array(outInteger) CDLMATHOLD Mat Hold (K线图:垫住) CDLMATHOLD(Records[Open,High,Low,Close],Penetration = 0.5) = Array(outInteger) CDLMORNINGDOJISTAR Morning Doji Star (K线图:早晨十字星) CDLMORNINGDOJISTAR(Records[Open,High,Low,Close],Penetration = 0.3) = Array(outInteger) CDLMORNINGSTAR Morning Star (K线图:晨星) CDLMORNINGSTAR(Records[Open,High,Low,Close],Penetration = 0.3) = Array(outInteger) CDLONNECK On-Neck Pattern (K线图:颈型) CDLONNECK(Records[Open,High,Low,Close]) = Array(outInteger) CDLPIERCING Piercing Pattern (K线图:穿孔模式) CDLPIERCING(Records[Open,High,Low,Close]) = Array(outInteger) CDLRICKSHAWMAN Rickshaw Man (K线图:车夫) CDLRICKSHAWMAN(Records[Open,High,Low,Close]) = Array(outInteger) CDLRISEFALL3METHODS Rising/Falling Three Methods (K线图:上升/下降三法) CDLRISEFALL3METHODS(Records[Open,High,Low,Close]) = Array(outInteger) CDLSEPARATINGLINES Separating Lines (K线图:分割线) CDLSEPARATINGLINES(Records[Open,High,Low,Close]) = Array(outInteger) CDLSHOOTINGSTAR Shooting Star (K线图:流星) CDLSHOOTINGSTAR(Records[Open,High,Low,Close]) = Array(outInteger) CDLSHORTLINE Short Line Candle (K线图:短线) CDLSHORTLINE(Records[Open,High,Low,Close]) = Array(outInteger) CDLSPINNINGTOP Spinning Top (K线图:陀螺) CDLSPINNINGTOP(Records[Open,High,Low,Close]) = Array(outInteger) CDLSTALLEDPATTERN Stalled Pattern (K线图:停滞模式) CDLSTALLEDPATTERN(Records[Open,High,Low,Close]) = Array(outInteger) CDLSTICKSANDWICH Stick Sandwich (K线图:棍子三明治) CDLSTICKSANDWICH(Records[Open,High,Low,Close]) = Array(outInteger) CDLTAKURI Takuri (Dragonfly Doji with very long lower shadow) (K线图:托里) CDLTAKURI(Records[Open,High,Low,Close]) = Array(outInteger) CDLTASUKIGAP Tasuki Gap (K线图:翼隙) CDLTASUKIGAP(Records[Open,High,Low,Close]) = Array(outInteger) CDLTHRUSTING Thrusting Pattern (K线图:推模式) CDLTHRUSTING(Records[Open,High,Low,Close]) = Array(outInteger) CDLTRISTAR Tristar Pattern (K线图:三星模式) CDLTRISTAR(Records[Open,High,Low,Close]) = Array(outInteger) CDLUNIQUE3RIVER Unique 3 River (K线图:独特的3河) CDLUNIQUE3RIVER(Records[Open,High,Low,Close]) = Array(outInteger) CDLUPSIDEGAP2CROWS Upside Gap Two Crows (K线图:双飞乌鸦) CDLUPSIDEGAP2CROWS(Records[Open,High,Low,Close]) = Array(outInteger) CDLXSIDEGAP3METHODS Upside/Downside Gap Three Methods (K线图:上行/下行缺口三方法) CDLXSIDEGAP3METHODS(Records[Open,High,Low,Close]) = Array(outInteger) -
Volume Indicators量指标:指标 描述 AD Chaikin A/D Line (线随机指标) AD(Records[High,Low,Close,Volume]) = Array(outReal) ADOSC Chaikin A/D Oscillator (佳庆指标) ADOSC(Records[High,Low,Close,Volume],Fast Period = 3,Slow Period = 10) = Array(outReal) OBV On Balance Volume (能量潮) OBV(Records[Close],Records[Volume]) = Array(outReal) -
Math Transform数学变换:指标 描述 ACOS Vector Trigonometric ACos (反余弦函数) ACOS(Records[Close]) = Array(outReal) ASIN Vector Trigonometric ASin (反正弦函数) ASIN(Records[Close]) = Array(outReal) ATAN Vector Trigonometric ATan (反正切函数) ATAN(Records[Close]) = Array(outReal) CEIL Vector Ceil (取整函数) CEIL(Records[Close]) = Array(outReal) COS Vector Trigonometric Cos (余弦函数) COS(Records[Close]) = Array(outReal) COSH Vector Trigonometric Cosh (双曲余弦值) COSH(Records[Close]) = Array(outReal) EXP Vector Arithmetic Exp (指数函数) EXP(Records[Close]) = Array(outReal) FLOOR Vector Floor (向下取整) FLOOR(Records[Close]) = Array(outReal) LN Vector Log Natural (自然对数) LN(Records[Close]) = Array(outReal) LOG10 Vector Log10 (对数函数) LOG10(Records[Close]) = Array(outReal) SIN Vector Trigonometric Sin (正弦值) SIN(Records[Close]) = Array(outReal) SINH Vector Trigonometric Sinh (双曲正弦函数) SINH(Records[Close]) = Array(outReal) SQRT Vector Square Root (平方根) SQRT(Records[Close]) = Array(outReal) TAN Vector Trigonometric Tan (正切) TAN(Records[Close]) = Array(outReal) TANH Vector Trigonometric Tanh (双曲正切函数) TANH(Records[Close]) = Array(outReal) -
Math Operators数学运算符:指标 描述 MAX Highest value over a specified period (最大值) MAX(Records[Close],Time Period = 30) = Array(outReal) MAXINDEX Index of highest value over a specified period (最大值索引) MAXINDEX(Records[Close],Time Period = 30) = Array(outInteger) MIN Lowest value over a specified period (最小值) MIN(Records[Close],Time Period = 30) = Array(outReal) MININDEX Index of lowest value over a specified period (最小值索引) MININDEX(Records[Close],Time Period = 30) = Array(outInteger) MINMAX Lowest and highest values over a specified period (最小最大值) MINMAX(Records[Close],Time Period = 30) = [Array(outMin),Array(outMax)] MINMAXINDEX Indexes of lowest and highest values over a specified period (最小最大值索引) MINMAXINDEX(Records[Close],Time Period = 30) = [Array(outMinIdx),Array(outMaxIdx)] SUM Summation (求和) SUM(Records[Close],Time Period = 30) = Array(outReal) -
Cycle Indicators循环指标:指标 描述 HT_DCPERIOD Hilbert Transform - Dominant Cycle Period (希尔伯特变换, 主周期) HT_DCPERIOD(Records[Close]) = Array(outReal) HT_DCPHASE Hilbert Transform - Dominant Cycle Phase (希尔伯特变换,主阶段) HT_DCPHASE(Records[Close]) = Array(outReal) HT_PHASOR Hilbert Transform - Phasor Components (希尔伯特变换,相成分) HT_PHASOR(Records[Close]) = [Array(outInPhase),Array(outQuadrature)] HT_SINE Hilbert Transform - SineWave (希尔伯特变换,正弦波) HT_SINE(Records[Close]) = [Array(outSine),Array(outLeadSine)] HT_TRENDMODE Hilbert Transform - Trend vs Cycle Mode (希尔伯特变换-趋势与周期模式) HT_TRENDMODE(Records[Close]) = Array(outInteger) -
Volatility Indicators波动性指标:指标 描述 ATR Average True Range (平均真实波幅) ATR(Records[High,Low,Close],Time Period = 14) = Array(outReal) NATR Normalized Average True Range (归一化平均值范围) NATR(Records[High,Low,Close],Time Period = 14) = Array(outReal) TRANGE True Range (真实范围) TRANGE(Records[High,Low,Close]) = Array(outReal) -
Overlap Studies重叠的研究:指标 描述 BBANDS Bollinger Bands (布林带) BBANDS(Records[Close],Time Period = 5,Deviations up = 2,Deviations down = 2,MA Type = 0) = [Array(outRealUpperBand),Array(outRealMiddleBand),Array(outRealLowerBand)] DEMA Double Exponential Moving Average (双指数移动平均线) DEMA(Records[Close],Time Period = 30) = Array(outReal) EMA Exponential Moving Average (指数移动平均线) EMA(Records[Close],Time Period = 30) = Array(outReal) HT_TRENDLINE Hilbert Transform - Instantaneous Trendline (希尔伯特变换,瞬时趋势) HT_TRENDLINE(Records[Close]) = Array(outReal) KAMA Kaufman Adaptive Moving Average (适应性移动平均线) KAMA(Records[Close],Time Period = 30) = Array(outReal) MA Moving average (移动平均线) MA(Records[Close],Time Period = 30,MA Type = 0) = Array(outReal) MAMA MESA Adaptive Moving Average (MESA 移动平均线) MAMA(Records[Close],Fast Limit = 0.5,Slow Limit = 0.05) = [Array(outMAMA),Array(outFAMA)] MIDPOINT MidPoint over period (中点) MIDPOINT(Records[Close],Time Period = 14) = Array(outReal) MIDPRICE Midpoint Price over period (中点价格) MIDPRICE(Records[High,Low],Time Period = 14) = Array(outReal) SAR Parabolic SAR (抛物线转向) SAR(Records[High,Low],Acceleration Factor = 0.02,AF Maximum = 0.2) = Array(outReal) SAREXT Parabolic SAR - Extended (增强型抛物线转向) SAREXT(Records[High,Low],Start Value = 0,Offset on Reverse = 0,AF Init Long = 0.02,AF Long = 0.02,AF Max Long = 0.2,AF Init Short = 0.02,AF Short = 0.02,AF Max Short = 0.2) = Array(outReal) SMA Simple Moving Average (简单移动平均) SMA(Records[Close],Time Period = 30) = Array(outReal) T3 Triple Exponential Moving Average (T3) (三指数移动平均) T3(Records[Close],Time Period = 5,Volume Factor = 0.7) = Array(outReal) TEMA Triple Exponential Moving Average (三指数移动平均) TEMA(Records[Close],Time Period = 30) = Array(outReal) TRIMA Triangular Moving Average (三指数移动平均) TRIMA(Records[Close],Time Period = 30) = Array(outReal) WMA Weighted Moving Average (加权移动平均) WMA(Records[Close],Time Period = 30) = Array(outReal) -
Statistic Functions统计功能:指标 描述 LINEARREG Linear Regression (线性回归) LINEARREG(Records[Close],Time Period = 14) = Array(outReal) LINEARREG_ANGLE Linear Regression Angle (线性回归的角度) LINEARREG_ANGLE(Records[Close],Time Period = 14) = Array(outReal) LINEARREG_INTERCEPT Linear Regression Intercept (线性回归截距) LINEARREG_INTERCEPT(Records[Close],Time Period = 14) = Array(outReal) LINEARREG_SLOPE Linear Regression Slope (线性回归斜率) LINEARREG_SLOPE(Records[Close],Time Period = 14) = Array(outReal) STDDEV Standard Deviation (标准偏差) STDDEV(Records[Close],Time Period = 5,Deviations = 1) = Array(outReal) TSF Time Series Forecast (时间序列预测) TSF(Records[Close],Time Period = 14) = Array(outReal) VAR Variance (方差) VAR(Records[Close],Time Period = 5,Deviations = 1) = Array(outReal) -
Momentum Indicators动量指标:指标 描述 ADX Average Directional Movement Index (平均趋向指数) ADX(Records[High,Low,Close],Time Period = 14) = Array(outReal) ADXR Average Directional Movement Index Rating (评估指数) ADXR(Records[High,Low,Close],Time Period = 14) = Array(outReal) APO Absolute Price Oscillator (绝对价格振荡指数) APO(Records[Close],Fast Period = 12,Slow Period = 26,MA Type = 0) = Array(outReal) AROON Aroon (阿隆指标) AROON(Records[High,Low],Time Period = 14) = [Array(outAroonDown),Array(outAroonUp)] AROONOSC Aroon Oscillator (阿隆震荡线) AROONOSC(Records[High,Low],Time Period = 14) = Array(outReal) BOP Balance Of Power (均势指标) BOP(Records[Open,High,Low,Close]) = Array(outReal) CCI Commodity Channel Index (顺势指标) CCI(Records[High,Low,Close],Time Period = 14) = Array(outReal) CMO Chande Momentum Oscillator (钱德动量摆动指标) CMO(Records[Close],Time Period = 14) = Array(outReal) DX Directional Movement Index (动向指数) DX(Records[High,Low,Close],Time Period = 14) = Array(outReal) MACD Moving Average Convergence/Divergence (指数平滑移动平均线) MACD(Records[Close],Fast Period = 12,Slow Period = 26,Signal Period = 9) = [Array(outMACD),Array(outMACDSignal),Array(outMACDHist)] MACDEXT MACD with controllable MA type (MA型可控 MACD) MACDEXT(Records[Close],Fast Period = 12,Fast MA = 0,Slow Period = 26,Slow MA = 0,Signal Period = 9,Signal MA = 0) = [Array(outMACD),Array(outMACDSignal),Array(outMACDHist)] MACDFIX Moving Average Convergence/Divergence Fix 12/26 (移动平均收敛/发散修复12/26) MACDFIX(Records[Close],Signal Period = 9) = [Array(outMACD),Array(outMACDSignal),Array(outMACDHist)] MFI Money Flow Index (货币流量指数) MFI(Records[High,Low,Close,Volume],Time Period = 14) = Array(outReal) MINUS_DI Minus Directional Indicator (负向指标) MINUS_DI(Records[High,Low,Close],Time Period = 14) = Array(outReal) MINUS_DM Minus Directional Movement (负向运动) MINUS_DM(Records[High,Low],Time Period = 14) = Array(outReal) MOM Momentum (动量) MOM(Records[Close],Time Period = 10) = Array(outReal) PLUS_DI Plus Directional Indicator (更向指示器) PLUS_DI(Records[High,Low,Close],Time Period = 14) = Array(outReal) PLUS_DM Plus Directional Movement (定向运动) PLUS_DM(Records[High,Low],Time Period = 14) = Array(outReal) PPO Percentage Price Oscillator (价格振荡百分比) PPO(Records[Close],Fast Period = 12,Slow Period = 26,MA Type = 0) = Array(outReal) ROC Rate of change : ((price/prevPrice)-1)*100 (变动率指标) ROC(Records[Close],Time Period = 10) = Array(outReal) ROCP Rate of change Percentage: (price-prevPrice)/prevPrice (价格变化率) ROCP(Records[Close],Time Period = 10) = Array(outReal) ROCR Rate of change ratio: (price/prevPrice) (价格变化率) ROCR(Records[Close],Time Period = 10) = Array(outReal) ROCR100 Rate of change ratio 100 scale: (price/prevPrice)*100 (价格变化率) ROCR100(Records[Close],Time Period = 10) = Array(outReal) RSI Relative Strength Index (相对强弱指标) RSI(Records[Close],Time Period = 14) = Array(outReal) STOCH Stochastic (STOCH指标) STOCH(Records[High,Low,Close],Fast-K Period = 5,Slow-K Period = 3,Slow-K MA = 0,Slow-D Period = 3,Slow-D MA = 0) = [Array(outSlowK),Array(outSlowD)] STOCHF Stochastic Fast (快速STOCH指标) STOCHF(Records[High,Low,Close],Fast-K Period = 5,Fast-D Period = 3,Fast-D MA = 0) = [Array(outFastK),Array(outFastD)] STOCHRSI Stochastic Relative Strength Index (随机强弱指数) STOCHRSI(Records[Close],Time Period = 14,Fast-K Period = 5,Fast-D Period = 3,Fast-D MA = 0) = [Array(outFastK),Array(outFastD)] TRIX 1-day Rate-Of-Change (ROC) of a Triple Smooth EMA (三重指数平滑平均线) TRIX(Records[Close],Time Period = 30) = Array(outReal) ULTOSC Ultimate Oscillator (极限振子) ULTOSC(Records[High,Low,Close],First Period = 7,Second Period = 14,Third Period = 28) = Array(outReal) WILLR Williams' %R (威廉指标) WILLR(Records[High,Low,Close],Time Period = 14) = Array(outReal) -
Price Transform价格指标:指标 描述 AVGPRICE Average Price (平均价格) AVGPRICE(Records[Open,High,Low,Close]) = Array(outReal) MEDPRICE Median Price (中位数价格) MEDPRICE(Records[High,Low]) = Array(outReal) TYPPRICE Typical Price (典型价格) TYPPRICE(Records[High,Low,Close]) = Array(outReal) WCLPRICE Weighted Close Price (加权收盘价) WCLPRICE(Records[High,Low,Close]) = Array(outReal)
基本面数据
优宽量化交易平台支持回测、实盘时查询各项基本面数据。基本面数据由优宽量化交易平台数据中心实时提供,优宽量化交易平台数据中心会持续收集、汇总各项基本面数据。
数据调用
使用exchange.GetData(Source)函数获取基本面数据。
回测系统测试数据调用,以BCEEI为例:
javascript
/*backtest
start: 2020-02-23 00:00:00
end: 2020-05-22 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
function main() {
while(true) {
var ret = exchange.GetData("BCEEI")
Log(ret)
Sleep(1000 * 60 * 60 * 24 * 30)
}
}
python
'''backtest
start: 2020-02-23 00:00:00
end: 2020-05-22 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
'''
def main():
while True:
ret = exchange.GetData("BCEEI")
Log(ret)
Sleep(1000 * 60 * 60 * 24 * 30)
c++
/*backtest
start: 2020-02-23 00:00:00
end: 2020-05-22 00:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
*/
void main() {
while (true) {
auto ret = exchange.GetData("BCEEI");
Log(ret);
Sleep(1000 * 60 * 60 * 24 * 30);
break;
}
}
数据格式
| 名称 | 代码 | 名称 | 代码 | 名称 | 代码 | 名称 | 代码 | 名称 | 代码 |
|---|---|---|---|---|---|---|---|---|---|
| 基差 | BASIS | 企业景气及企业家信心指数 | BCEEI | 交易结算资金(银证转账) | BTSF | 存款准备金率 | CKZBJ | 居民消费价格指数 | CPI |
| 外商直接投资数据 | FDI | 外汇和黄金储备 | FEGR | 国内生产总值 | GDP | 全国股票交易统计表 | GPJYTJ | 股票账户统计详细数据 | GPZHSJ |
| 货币供应量 | HBGYL | 海关进出口增减情况一览表 | HGJCK | 消费者信心指数 | ICS | 贷款市场报价利率 | LRP | 新房价指数 | NREPI |
| 旧房价指数 | OREPI | 采购经理人指数 | PMI | 工业品出厂价格指数 | PPI | 全国税收收入 | QGSSSR | 企业商品价格指数 | QYSPJG |
| 社会消费品零售总额 | TRSCG | 城镇固定资产投资 | UIFA | 期货标准仓单 | WHR | 本外币存款 | WBCK | 外汇贷款数据 | WHXD |
| 银行利率调整 | YHLL | 交易所会员成交量及持仓明细 | DTP | 交易所会员持仓盈亏 | FCPP | 财政收入 | CZSR | 工业增加值增长 | GYZJZ |
| 汽柴油价格 | OILPRICE | 现货价格 | SPOTPRICE | 新增信贷数据 | XZXD |
基差
代码BASIS
描述:
基差是某一特定商品于某一特定的时间和地点的现货价格与期货价格之差。它的计算方法是现货价格减去期货价格。若现货价格低于期货价格,基差为负值;现货价格高于期货价格,基差为正值。
格式:
javascript
{
"日期": "2016-01-11",
"螺纹钢": 29,
"菜籽粕": 54
}
企业景气及企业家信心指数
代码BCEEI
描述:
企业家信心指数也称"宏观经济景气指数",是根据企业家对企业外部市场经济环境与宏观政策的认识、看法、判断与预期而编制的指数,用以综合反映企业家对宏观经济环境的感受与信心。
格式:
javascript
{
"季度": "2005第1季度",
"企业家信心指数": {
"指数": 135.9,
"同比": 0.3585,
"环比": 0.0504
},
"企业景气指数": {
"指数": 132.5,
"同比": 0.3246,
"环比": -0.0227
}
}
交易结算资金(银证转账)
代码BTSF
描述:
银证转账是指将股民在银行开立的个人结算存款账户与证券公司的资金账户建立对应关系,通过银行的电话银行、网上银行、网点自助设备和证券公司的电话、网上交易系统及证券公司营业部的自助设备将资金在银行和证券公司之间划转。
格式:
javascript
{
"日期": "2014年06月20日",
"上证指数收盘": 2026.67,
"上证指数涨跌幅": -0.0213,
"交易结算资金日均余额(亿)": 5783,
"交易结算资金期末余额(亿)": 7265,
"银证转账减少额(亿)": 1686,
"银证转账变动净额(亿)": 1801,
"银证转账增加额(亿)": 3487
}
存款准备金率
代码CKZBJ
描述:
存款准备金是指金融机构为保证客户提取存款和资金清算需要而准备的,是缴存在中央银行的存款,中央银行要求的存款准备金占其存款总额的比例就是存款准备金率。
格式:
javascript
{
"日期": "2007年01月15日",
"中小金融机构": {
"调整前": 0.09,
"调整后": 0.095,
"调整幅度": 0.005
},
"大型金融机构": {
"调整前": 0.09,
"调整后": 0.095,
"调整幅度": 0.005
},
"消息公布次日指数涨跌": {
"上证": 0.0249,
"深证": 0.0245
}
}
居民消费价格指数
代码CPI
描述:
居民消费价格指数是反映与居民生活有关的产品及劳务价格统计出来的物价变动指标,以百分比变化为表达形式。它是衡量通货膨胀的主要指标之一。一般定义超过 3%为通货膨胀,超过 5%就是比较严重的通货膨胀。
格式:
javascript
{
"月份": "2008年01月份",
"全国": 107.1,
"农村": 107.7,
"城市": 106.8
}
财政收入
代码CZSR
描述:
财政收入是指政府为履行其职能、实施公共政策和提供公共物品与服务需要而筹集的一切资金的总和。它是衡量一国政府财力的重要指标,政府在社会经济活动中提供公共物品和服务的范围和数量,在很大程度上决定于财政收入的充裕状况。
格式:
javascript
{
"月份": "2008年01月份",
"当月(亿美元)": 7396.64,
"当月同比增长": 0.4235,
"环比增长": 1.352,
"累计(亿美元)": 7396.64,
"累计同比增长": 0.424
}
外商直接投资数据
代码FDI
描述:
外商直接投资指外国企业和经济组织或个人(包括华侨、港澳台胞以及我国在境外注册的企业)按我国有关政策、法规,用现汇、实物、技术等在我国境内开办外商独资企业、与我国境内的企业或经济组织共同举办中外合资经营企业、合作经营企业或合作开发资源的投资(包括外商投资收益的再投资),以及经政府有关部门批准的项目投资总额内企业从境外借入的资金。
格式:
javascript
{
"月份": "2008年01月份",
"当月(亿美元)": 112,
"当月同比增长": 1.1642,
"环比增长": -0.1446,
"累计(亿美元)": 114.38,
"累计同比增长": 1.0523
}
外汇和黄金储备
代码FEGR
描述:
外汇又称为外汇存底,是指一国政府所持有的国际储备资产中的外汇部分,即一国政府保有的以外币表示的债权。是一个国家货币当局持有并可以随时兑换外国货币的资产。黄金储备:指一国货币当局持有的,用以平衡国际收支,维持或影响汇率水平,作为金融资产持有的黄金。
格式:
javascript
{
"月份": "2008年01月份",
"国家外汇储备(亿美元)": {
"金额": 15898.1,
"同比增长": 0.4391,
"环比增长": 0.0403
},
"黄金储备(万盎司)": {
"金额": 1929,
"同比增长": 0,
"环比增长": 0
}
}
国内生产总值
代码GDP
描述:
国内生产总值,Gross Domestic Product,简称 GDP,是指在一定时期内(一个季度或一年),一个国家或地区的经济中所生产出的全部最终产品和劳务的价值,常被公认为衡量国家经济状况的最佳指标。
格式:
javascript
{
"季度": "2006年第1季度",
"国内生产总值": {
"绝对值(亿元)": 47078.9,
"同比增长": 0.125
},
"第一产业": {
"绝对值(亿元)": 3012.7,
"同比增长": 0.044
},
"第三产业": {
"绝对值(亿元)": 22647.4,
"同比增长": 0.131
},
"第二产业": {
"绝对值(亿元)": 21418.7,
"同比增长": 0.131
}
}
全国股票交易统计表
代码GPJYTJ
描述:
股票账户统计详细数据(旧版2008年至今)
格式:
javascript
{
"日期": "2008年01月",
"A股最低综合股价指数": {
"上海": 4330.7,
"深圳": 1424.35
},
"A股最高综合股价指数": {
"上海": 5522.78,
"深圳": 1667.91
},
"发行总股本": {
"上海": 14198.16,
"深圳": 2818.71
},
"市价总值": {
"上海": 225354.97,
"深圳": 52499.85
},
"成交量": {
"上海": 1771.83,
"深圳": 874.06
},
"成交金额": {
"上海": 30759.73,
"深圳": 15770.72
}
}
股票账户统计详细数据
代码GPZHSJ
描述:
股票账户统计详细数据(新版2015年至今)
格式:
javascript
{
"日期": "2015年04月",
"上证指数": {
"收盘": 4441.655,
"涨跌幅": 18.51053083
},
"新增投资者": {
"数量(万户)": 497.53,
"同比增长": 0,
"环比增长": 0
},
"期末投资者(万户)": {
"总量": 8184.79,
"同比增长": 8108.3,
"环比增长": 235.92
},
"沪深总市值(亿)": 563491.335037778,
"沪深户均市值(万)": 69.4956
}
工业增加值增长
代码GYZJZ
描述:
工业增加值增长是指工业企业在报告期内以货币形式表现的工业生产活动的最终成果,是指工业企业在一定时期内工业生产活动创造的价值,是国内生产总值的组成部分。公式: 工业增加值=固定资产折旧+劳动者报酬+生产税净值+营业盈余。
格式:
javascript
{
"月份": "2008年02月份",
"同比增长": 15.4,
"累计增长": 15.4
}
货币供应量
代码HBGYL
描述:
货币供应量亦称货币存量、货币供应,指某一时点流通中的现金量和存款量之和。货币供应量是各国中央银行编制和公布的主要经济统计指标之一。它由包括中央银行在内的金融机构供应的存款货币和现金货币两部分构成。世界各国中央银行货币估计口径不完全一致,但划分的基本依据是一致的,即流动性大小。
格式:
javascript
{
"月份": "2008年01月份",
"流通中的现金(M0)": {
"数量(亿元)": 36673.15,
"同比增长": 0.3121,
"环比增长": 0.209
},
"货币和准货币(M2)": {
"数量(亿元)": 417846.17,
"同比增长": 0.1888,
"环比增长": 0.0358
},
"货币(M1)": {
"数量(亿元)": 154872.59,
"同比增长": 0.2054,
"环比增长": 0.0154
}
}
海关进出口增减情况一览表
代码HGJCK
描述:
海关进出口增减指实际进出我国国境的货物总金额。进出口总额用以观察一个国家在对外贸易方面的总规模。我国规定出口货物按离岸价格统计,进口货物按到岸价格统计。
格式:
javascript
{
"月份": "2008年01月份",
"当月出口额": {
"金额(亿美元)": 1096.4,
"同比增长": 0.266,
"环比增长": -0.0417
},
"当月进口额": {
"金额(亿美元)": 901.74,
"同比增长": 0.276,
"环比增长": -0.0169
},
"累计出口额": {
"金额(亿美元)": 1096.4,
"同比增长": 0.266
},
"累计进口额": {
"金额(亿美元)": 901.74,
"同比增长": 0.276
}
}
消费者信心指数
代码ICS
描述:
消费者信心指数是反映消费者信心强弱的指标,是综合反映并量化消费者对当前经济形势评价和对经济前景、收入水平、收入预期以及消费心理状态的主观感受,是预测经济走势和消费趋向的一个先行指标,是监测经济周期变化不可缺少的依据。
格式:
javascript
{
"月份": "2007年01月份",
"消费者信心指数": {
"指数值": 112.4,
"同比增长": 0.019,
"环比增长": -0.0062
},
"消费者满意指数": {
"指数值": 109.5,
"同比增长": -0.0054,
"环比增长": -0.0073
},
"消费者预期指数": {
"指数值": 114.2,
"同比增长": 0.0344,
"环比增长": -0.0061
}
}
贷款市场报价利率
代码LRP
描述:
贷款市场报价利率是由具有代表性的报价行,根据本行对最优质客户的贷款利率,以公开市场操作利率加点形成的方式报价,由中国人民银行授权全国银行间同业拆借中心计算并公布的基础性的贷款参考利率,各金融机构应主要参考 LPR 进行贷款定价。
格式:
javascript
{
"日期": "2015-08-31",
"LPR_1Y利率(%)": 4.55,
"LPR_5Y利率(%)": 0,
"中长期贷款利率:5年以上(%)": 5.15,
"短期贷款利率:6个月至1年(含)(%)": 4.6
}
新房价指数
代码NREPI
描述:
房屋销售价格指数是反映一定时期房屋销售价格变动程度和趋势的相对数,它通过百分数的形式来反映房价在不同时期的涨跌幅度。包括商品房、公有房屋和私有房屋各大类房屋的销售价格的变动情况。
格式:
javascript
{
"日期": "2011年01月01日",
"上海": {
"二手住宅价格指数": {
"环比": 100.5,
"同比": 101.7,
"定基": 100.6
},
"新建住宅价格指数": {
"环比": 100.9,
"同比": 101.5,
"定基": 100.8
},
"新建商品住宅价格指数": {
"环比": 101.1,
"同比": 101.8,
"定基": 101
}
},
"北京": {
"二手住宅价格指数": {
"环比": 100.3,
"同比": 102.6,
"定基": 101.2
},
"新建住宅价格指数": {
"环比": 100.8,
"同比": 106.8,
"定基": 102.4
},
"新建商品住宅价格指数": {
"环比": 101,
"同比": 109.1,
"定基": 103
}
}
}
汽柴油价格
代码OILPRICE
描述:
汽柴油历史价格信息
格式:
javascript
{
"日期": "2000-6-6",
"柴油(元/吨)": 2430,
"汽油(元/吨)": 2935
}
旧房价指数
代码OREPI
描述:
08年--10年房地产价格指数
格式:
javascript
{
"月份": "2008年02月份",
"国房景气指数": {
"指数值": 105.55,
"同比增长": 0.037
},
"土地开发面积指数": {
"指数值": 99.34,
"同比增长": 0.0671
},
"销售价格指数": {
"指数值": 111.52,
"同比增长": 0.0656
}
}
采购经理人指数
代码PMI
描述:
采购经理人指数是统计制造业在生产、新订单、商品价格、存货、雇员、订单交货、新出口订单和进口等八个方面状况。具有其高度的时效性,是经济先行指标中一项非常重要的附属指标。
格式:
javascript
{
"月份": "2008年01月份",
"制造业同比增长": -0.0381,
"制造业指数": 53,
"非制造业同比增长": -0.0033,
"非制造业指数": 60.2
}
工业品出厂价格指数
代码PPI
描述:
工业品出厂价格指数是衡量工业企业产品出厂价格变动趋势和变动程度的指数,是反映全部工业产品出厂价格总水平的变动趋势和程度的相对数,也是制定有关经济政策和国民经济核算的重要依据。
格式:
javascript
{
"月份": "2006年01月份",
"当月": 103.1,
"当月同比增长": 0.0305,
"累计": 103.05
}
全国税收收入
代码QGSSSR
描述:
税收是指国家为了向社会提供公共产品、满足社会共同需要、按照法律的规定,参与社会产品的分配、强制、无偿取得财政收入的一种规范形式。税收是一种非常重要的政策工具。
格式:
javascript
{
"季度": "2005年第1季度",
"季度环比(%)": 0.0097,
"税收收入合计(亿元)": 7249.16,
"较上年同期(%)": 0.107
}
企业商品价格指数
代码QYSPJG
描述:
CGPI 的前身是国内批发物价指数(Wholesale Price Index,简称 WPI),指数编制始于 1994 年 1 月。是反映国内企业之间物质商品集中交易价格变动的统计指标,是比较全面的测度通货膨胀水平和反映经济波动的综合价格指数。CGPI 调查是经国家统计局批准、由中国人民银行建立并组织实施的一项调查统计制度。
格式:
javascript
{
"月份": "2005年01月份",
"农产品": {
"同比增长": -0.0386,
"指数值": 106.22,
"环比增长": -0.0002
},
"总指数": {
"同比增长": -0.0196,
"指数值": 104.67,
"环比增长": -0.0063
},
"煤油电": {
"同比增长": 0.089,
"指数值": 115.83,
"环比增长": -0.0181
},
"矿产品": {
"同比增长": 0.0623,
"指数值": 115.83,
"环比增长": 0.0117
}
}
现货价格
代码SPOTPRICE
描述:
现货价格是指商品在现货交易中的成交价格。现货交易是一经成交立即交换的买卖行为,一般是买主即时付款,但也可以采取分期付款和延期交付的方式。由于付款方式的不同,同一现货在同一时期往往可能出现不同的价格。
格式:
javascript
{
"日期": "2017-08-30",
"棉花": 15895.4,
"棕榈油": 5715,
"油菜籽": 5210,
"焦炭": 2043.33
}
社会消费品零售总额
代码TRSCG
描述:
社会消费品零售总额是指批发和零售业、住宿和餐饮业以及其他行业直接售给城乡居民和社会集团的消费品零售额。
格式:
javascript
{
"月份": "2008年01月份",
"同比增长": 0.212,
"当月(亿元)": 9077.3,
"环比增长": 0.0069,
"累计(亿元)": 9077.3,
"累计同比增长": 0.212
}
城镇固定资产投资
代码UIFA
描述:
固定资产投资是建造和购置固定资产的经济活动,即固定资产再生产活动。固定资产再生产过程包括固定资产更新(局部和全部更新)、改建、扩建、新建等活动。新的企业财务会计制度规定:固定资产局部更新的大修理作为日常生产活动的一部分,发生的大修理费用直接在成本中列支。
格式:
javascript
{
"月份": "2008年02月份",
"同比增长": 0,
"当月(亿元)": 0,
"环比增长": 0,
"自年初累计(亿元)": 8121
}
期货标准仓单
代码WHR
描述:
期货标准仓单是由期货交易所指定交割仓库按照交易所规定的程序签发的符合合约规定质量的实物提货凭证。由于期货标准仓单可以作为一种流通工具,因此它可以用作借款的质押品或用于期货合约的交割。
格式:
javascript
{
"日期": "2017-08-31",
"一号棉": {
"仓库数量": 161,
"总仓单": 1245,
"平均仓单": 7
},
"动力煤": {
"仓库数量": 0,
"总仓单": 0,
"平均仓单": 0
},
"天然橡胶": {
"仓库数量": 21,
"总仓单": 358160,
"平均仓单": 17055
},
"普麦": {
"仓库数量": 0,
"总仓单": 0,
"平均仓单": 0
},
"燃料油": {
"仓库数量": 8,
"总仓单": 0,
"平均仓单": 0
},
"玉米淀粉": {
"仓库数量": 1,
"总仓单": 500,
"平均仓单": 500
},
"聚氯乙烯": {
"仓库数量": 1,
"总仓单": 920,
"平均仓单": 920
}
}
本外币存款
代码WBCK
描述:
人民币是本位币,其他国家货币的都是外币。本外币存款就是人民币与外币的存款总和。它反映一个城市对资金的吸附能力,是1653衡量金融机构发展的重要指标。
格式:
javascript
{
"月份": "2008年02月份",
"同比增长": 1.5267,
"当月(亿元)": 13370.18,
"环比增长": -0.9668,
"累计(亿元)": 415859.25
}
外汇贷款数据
代码WHXD
描述:
外汇贷款是银行以外币为计算单位向企业发放的贷款。外汇贷款有广义和狭义之分,狭义的外汇贷款,仅指我国银行运用从境内企业、个人吸收的外汇资金,贷放于境内企业的贷款;广义的外汇贷款,还包括国际融资转贷款,即包括我国从国外借人,通过国内外汇指定银行转贷于境内企业的贷款。
格式:
javascript
{
"月份": "2008年02月份",
"同比增长": 7.5475,
"当月(亿元)": 217.02,
"环比增长": -0.9083,
"累计(亿元)": 2584.33
}
新增信贷数据
代码XZXD
描述:
借款企业第一次向这家银行贷款叫新增贷款。假如贷款到期了,企业正常偿还了这笔贷款后,再次向银行申请贷款,这样还叫新增贷款。假如贷款到期了,企业不能正常偿还这笔贷款,先银行提出再借第二笔贷款用以偿还第一笔贷款就叫借新还旧。
格式:
javascript
{
"月份": "2008年01月份",
"当月(亿元)": 8058,
"当月同比增长": 0.4229,
"环比增长": 15.6144,
"累计(亿元)": 8058,
"累计同比增长": 0.4229
}
银行利率调整
代码YHLL
描述:
表示一定时期内利息量与本金的比率,通常用百分比表示,按年计算则称为年利率。
其计算公式是:利息率= 利息量 ÷ 本金 ÷ 时间 ×100%
格式:
javascript
{
"生效时间": "2006-04-28",
"存款基准利率": {
"调整前": 0.025,
"调整后": 0.0225,
"调整幅度": -0.0025
},
"消息公布次日指数涨跌": {
"上证": 0.0166,
"深证": 0.0016
},
"贷款基准利率": {
"调整前": 0.0556,
"调整后": 0.0585,
"调整幅度": 0.0029
}
}
交易所会员成交量及持仓明细
代码DTP
描述:
当期货合约持仓量达到规定条件时,交易所将公布该月份合约前20名期货公司会员的成交量、买持仓量和卖持仓量,以及该合约所属期货品种期货公司会员、非期货公司会员的总成交量、总买持仓量和总卖持仓量。
格式:
javascript
{
"日期": "2020-05-28",
"cu2006": {
"五矿经易": {
"成交量": 10763,
"成交量增减": 2440,
"持卖单量": 5935,
"持卖单量增减": 29,
"持买单量": 1823,
"持买单量增减": -1326
},
"金瑞期货": {
"持买单量": 15581,
"持买单量增减": -1529,
"持卖单量": 4827,
"持卖单量增减": -980,
"成交量": 5265,
"成交量增减": 664
}
}
}
交易所会员持仓盈亏
代码FCPP
描述:
根据交易所公布的持买单量、持卖单量、结算价、开盘价等,分别计算出交易所会员每个合约的盈亏状态。
格式:
javascript
{
"日期": "2020-05-28",
"cu2006": {
"五矿经易": {
"持买单盈亏": 729200,
"持卖单盈亏": -2374000,
"净持仓盈亏": -1644800
},
"金瑞期货": {
"持买单盈亏": 6232400,
"持卖单盈亏": -1930800,
"净持仓盈亏": 4301600
}
}
}
优宽平台扩展API
优宽量化交易平台支持程序化调用优宽量化交易平台的各项功能,开放了平台的扩展API接口。
支持扩展API接口的权限管理,如图:
允许新创建的API KEY拥有全部权限,可以在API权限输入框中输入*符号开启所有接口权限。指定具体接口权限,需要输入对应的扩展API函数名,使用逗号间隔,例如:GetRobotDetail,DeleteRobot。即给予了这个API KEY调用获取实盘详细信息接口、删除实盘接口的权限。
创建ApiKey
API返回码(code)
| 描述 | 代码 |
|---|---|
| 执行成功 | 0 |
| 错误的API KEY | 1 |
| 错误的签名 | 2 |
| Nonce错误 | 3 |
| 方法不正确 | 4 |
| 参数不正确 | 5 |
| 内部未知错误 | 6 |
实盘状态
(正常启动)
| 状态 | 代码 |
|---|---|
| 空闲中 | 0 |
| 运行中 | 1 |
| 停止中 | 2 |
| 已退出 | 3 |
| 被停止 | 4 |
| 策略有错误 | 5 |
(异常)
| 状态 | 代码 |
|---|---|
| 策略已过期, 请联系作者重新购买 | -1 |
| 没有找到托管者 | -2 |
| 策略编译错误 | -3 |
| 实盘已经是运行状态 | -4 |
| 余额不足 | -5 |
| 策略并发数超限 | -6 |
验证方式
token验证
使用md5加密方式验证。
Python、Golang语言调用例子:
python
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import json
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
try:
import md5
import urllib2
from urllib import urlencode
except:
import hashlib as md5
import urllib.request as urllib2
from urllib.parse import urlencode
accessKey = 'f27bfcXXXXXXXX013c62e98XXXXX817a'
secretKey = 'ffeXXXXXXXX085ff7269XXXXXXXX6f82'
def api(method, *args):
d = {
'version': '1.0',
'access_key': accessKey,
'method': method,
'args': json.dumps(list(args)),
'nonce': int(time.time() * 1000),
}
d['sign'] = md5.md5(('%s|%s|%s|%d|%s' % (d['version'], d['method'], d['args'], d['nonce'], secretKey)).encode('utf-8')).hexdigest()
# 注意: urllib2.urlopen 函数,超时问题,可以设置超时时间,urllib2.urlopen('https://www.youquant.com/api/v1', urlencode(d).encode('utf-8'), timeout=10) 设置超时 10秒
return json.loads(urllib2.urlopen('https://www.youquant.com/api/v1', urlencode(d).encode('utf-8')).read().decode('utf-8'))
# 返回托管者列表
print(api('GetNodeList'))
# 返回交易所列表
print(api('GetPlatformList'))
# GetRobotList(offset, length, robotStatus, label),传-1代表获取全部
print(api('GetRobotList', 0, 5, -1, 'member2'))
# CommandRobot(robotId, cmd)向实盘发送命令
print(api('CommandRobot', 123, 'ok'))
# StopRobot(robotId)返回实盘状态代码
print(api('StopRobot', 123))
# RestartRobot(robotId)返回实盘状态代码
print(api('RestartRobot', 123))
# GetRobotDetail(robotId)返回实盘详细信息
print(api('GetRobotDetail', 123))
mylang
package main
import (
"fmt"
"time"
"encoding/json"
"crypto/md5"
"encoding/hex"
"net/http"
"io/ioutil"
"strconv"
"net/url"
)
// 填写自己的优宽平台api key
var apiKey string = ""
// 填写自己的优宽平台secret key
var secretKey string = ""
var baseApi string = "https://www.youquant.com/api/v1"
func api(method string, args ... interface{}) (ret interface{}) {
// 处理args
jsonStr, err := json.Marshal(args)
if err != nil {
panic(err)
}
params := map[string]string{
"version" : "1.0",
"access_key" : apiKey,
"method" : method,
"args" : string(jsonStr),
"nonce" : strconv.FormatInt(time.Now().UnixNano() / 1e6, 10),
}
data := fmt.Sprintf("%s|%s|%s|%v|%s", params["version"], params["method"], params["args"], params["nonce"], secretKey)
h := md5.New()
h.Write([]byte(data))
sign := h.Sum(nil)
params["sign"] = hex.EncodeToString(sign)
// http request
client := &http.Client{}
// request
urlValue := url.Values{}
for k, v := range params {
urlValue.Add(k, v)
}
urlStr := urlValue.Encode()
request, err := http.NewRequest("GET", baseApi + "?" + urlStr, nil)
if err != nil {
panic(err)
}
resp, err := client.Do(request)
if err != nil {
panic(err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
ret = string(b)
return
}
func main() {
settings := map[string]interface{}{
"name": "hedge test",
"strategy": 1234,
// K线周期参数,60即为60秒
"period": 60,
"node" : 1234,
"appid": "member2",
"exchanges": []interface{}{
map[string]interface{}{
"pid": "1234",
"pair": "FUTURES",
},
},
}
method := "NewRobot"
fmt.Println("调用接口:", method)
ret := api(method, settings)
fmt.Println("main ret:", ret)
}
直接验证
支持不使用token验证(直接传secret_key验证),可以生成一个用来直接访问的URL。例如直接给实盘发交互指令的URL,可以用于TradingView或其它场景的WebHook回调。
对于CommandRobot(RobotId, Cmd)函数,不进行nonce校验,不限制该接口的访问频率、访问次数。
javascript
https://www.youquant.com/api/v1?access_key=xxx&secret_key=yyyy&method=CommandRobot&args=[186515,"ok12345"]
支持直接验证方式下,获取请求中的Body数据,仅支持CommandRobot接口。例如在TradingView的WebHook URL中设置:
javascript
https://www.youquant.com/api/v1?access_key=fd3be82e9e6e6ed4439f2793a5e9ca&secret_key=520b9f10f3768e6ad1af59ff25184&method=CommandRobot&args=[130350,+""]
注意需要按照此格式设置:args=[130350,+""],130350是优宽量化交易平台的实盘ID。
消息框中设置(要发送的请求中的Body数据):
-
JSON格式:
pine{"close": {{close}}, "name": "aaa"}ID为186515的实盘即可收到交互命令:{"close": 39773.75, "name": "aaa"}。 -
文本格式:
pinexxx 穿过(Crossing) 39700.00 close: {{close}}ID为186515的实盘即可收到交互命令:xxx 穿过(Crossing) 39700.00 close: 39739.4。
xxx为上图打码内容。
Python、Golang语言调用例子:
python
#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
try:
import urllib2
except:
import urllib.request as urllib2
accessKey = 'your accessKey'
secretKey = 'your secretKey'
def api(method, *args):
return json.loads(urllib2.urlopen(('https://www.youquant.com/api/v1?access_key=%s&secret_key=%s&method=%s&args=%s' % (accessKey, secretKey, method, json.dumps(list(args)))).replace(' ', '')).read().decode('utf-8'))
# 如果APIKEY 没有该接口权限,调用 print(api('RestartRobot', 130350)) 会失败,返回数据:{'code': 4, 'data': None}
# print(api('RestartRobot', 130350))
# 打印ID为:130350的实盘详细信息
print(api('GetRobotDetail', 130350))
mylang
package main
import (
"fmt"
"encoding/json"
"net/http"
"io/ioutil"
"net/url"
)
// 填写自己的优宽平台api key
var apiKey string = "your access_key"
// 填写自己的优宽平台secret key
var secretKey string = "your secret_key"
var baseApi string = "https://www.youquant.com/api/v1"
func api(method string, args ... interface{}) (ret interface{}) {
jsonStr, err := json.Marshal(args)
if err != nil {
panic(err)
}
params := map[string]string{
"access_key" : apiKey,
"secret_key" : secretKey,
"method" : method,
"args" : string(jsonStr),
}
// http request
client := &http.Client{}
// request
urlValue := url.Values{}
for k, v := range params {
urlValue.Add(k, v)
}
urlStr := urlValue.Encode()
request, err := http.NewRequest("GET", baseApi + "?" + urlStr, nil)
if err != nil {
panic(err)
}
resp, err := client.Do(request)
if err != nil {
panic(err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
ret = string(b)
return
}
func main() {
method := "GetRobotDetail"
fmt.Println("调用接口:", method)
ret := api(method, 130350)
fmt.Println("main ret:", ret)
}
使用优宽量化交易平台扩展API实现TradingView报警信号交易
使用优宽量化交易平台扩展API实现TradingView报警信号交易,B站视频链接
扩展API接口详解
-
优宽量化交易平台扩展API接口
https://www.youquant.com/api/v1?以上基地址其中的
?符号后跟请求参数。Python语言描述请求参数:python{ 'version' : '1.0', # access key,在账户管理页面申请 'access_key': '8a1f6c3785fd78a1848320e0b19js99f', # 具体调用的方法 'method' : 'GetNodeList', # 具体method算法的参数列表 'args' : [], # 时间戳,单位毫秒,允许和标准时间时间戳前后误差1小时,nonce必须比上一次访问时的nonce数值大 'nonce' : 1516292399361, # 签名 'sign' : '085b63456c93hfb243a757366600f9c2' }各个参数以字符
&分隔,参数名和参数值用符号=连接,完整的请求URL(method=GetNodeList为例):javascripthttps://www.youquant.com/api/v1? access_key=8a1f6c3785fd78a1848320e0b19js99f& nonce=1516292399361& args=%5B%5D& sign=085b63456c93hfb243a757366600f9c2& version=1.0& method=GetNodeList注意请求参数中没有
secret_key这个参数。 -
签名方式
请求参数中,sign参数是一个加密字符串,加密方式如下。按照格式:
javascriptversion + "|" + method + "|" + args + "|" + nonce + "|" + secretKey拼接字符串后,使用
MD5加密算法加密字符串,并转换为十六进制数据字符串,该值作为参数sign的值。签名部分参考
Python代码:python# 参数 d = { 'version': '1.0', 'access_key': accessKey, 'method': method, 'args': json.dumps(list(args)), 'nonce': int(time.time() * 1000), } # 计算sign签名 d['sign'] = md5.md5(('%s|%s|%s|%d|%s' % (d['version'], d['method'], d['args'], d['nonce'], secretKey)).encode('utf-8')).hexdigest()
GetNodeList
GetNodeList,返回请求中的API KEY对应的优宽量化交易平台账号下的托管者列表。
-
参数
无 -
返回值
javascript{ 'code': 0, 'data': { 'result': { 'nodes': [ { ... , // ... 省略 'name': 'xxx', // 托管者节点名称 ... , }, { ... , 'name': 'Public', // 名称为Public的是公共托管者 ... } ] }, 'error': None } }返回的JSON数据中包含当前账号下的所有托管者详细信息,例如IP地址、创建时间、托管者版本、状态等信息。
GetRobotGroupList
GetRobotGroupList,返回请求中的API KEY对应的优宽量化交易平台账号下的实盘分组列表。
-
参数
无 -
返回值
python{ 'code': 0, 'data': { 'result': { 'items': [] }, 'error': None } }
GetPlatformList
GetPlatformList,返回请求中的API KEY对应的优宽量化交易平台账号下的已添加的交易所列表。
-
参数
无 -
返回值
javascript{ 'code': 0, 'data': { 'result': { 'platforms': [...] // ... 省略 }, 'error': None } }返回的JSON数据中包含当前账号下配置的所有交易所对象信息,例如交易所ID、配置时间、名称、标签等信息。
GetRobotList
GetRobotList,参数:offset, length, robotStatus, label。返回请求中的API KEY对应的优宽量化交易平台账号下的实盘列表。
-
参数
offset为整型,length为整型,robotStatus为整型,label为字符串类型。offsetlengthrobotStatus,传-1代表获取全部实盘。label,自定义标签,可以筛选出这个标签的所有实盘。
范例
Python代码,参看以上验证方式-token验证,以下是Python调用范例:
print api('GetRobotList', 'member2')把自定义标签为member2的实盘信息全部打印出来。
print api('GetRobotList', 0, 5, -1, 'member2')分页从0到5列出来最多5个标签为member2的实盘。 -
返回值
javascript{ 'code': 0, 'data': { 'result': { 'all': 1, 'concurrent': 1, 'robots': [...] // 实盘相关信息, ... 省略 }, 'error': None } }返回的JSON数据中包含本次调用筛选出的实盘信息,
robots字段中的实盘信息包括:实盘ID、实盘所属托管者节点、实盘盈亏、启动时间等。
CommandRobot
CommandRobot,参数:RobotId, Cmd。该接口向请求中的API KEY对应的优宽量化交易平台账号下的实盘ID为RobotId的实盘发送交互命令(交互命令由策略中调用的GetCommand()函数捕获返回)。
-
参数
RobotId为整型,是实盘ID。Cmd为字符串类型,是向实盘发送的交互命令。RobotId,实盘ID,可以用GetRobotList(...)接口获取。Cmd,该参数是发送给实盘的交互指令,在实盘策略中的GetCommand()函数会捕获到该交互命令,触发策略的交互逻辑(在策略里具体实现交互逻辑,可以参看文档中的GetCommand()函数)。
-
返回值
python{ 'code': 0, 'data': {'result': True, 'error': None} }
StopRobot
StopRobot,参数:RobotId。对于请求中的API KEY对应的优宽量化交易平台账号下的实盘,停止运行实盘ID为RobotId的实盘。
-
参数
RobotId为整型,RobotId即实盘ID,可以用GetRobotList(...)接口获取,StopRobot(RobotId)接口返回所停止的实盘的状态代码。 -
返回值
python{ 'code': 0, 'data': {'result': 2, 'error': None} }result字段表示实盘状态,2表示实盘已经停止。
RestartRobot
RestartRobot,参数RobotId, Settings。重启请求中的API KEY对应的优宽量化交易平台账号下的实盘ID为RobotId的实盘。
如果是使用扩展API接口创建出的实盘,重启必须使用扩展API接口RestartRobot(RobotId, Settings)进行重启,并且必须传入Settings参数。在平台页面上创建的实盘,可以通过扩展API接口重启或者点击实盘页面上的按钮重启。
可以传Settings参数或者不传Settings参数只传RobotId这个参数,如果只传RobotId参数,则按照当前实盘的设置启动实盘运行。
-
参数
-
不配置实盘策略参数
Settings:
RobotId为整型,实盘ID可以用GetRobotList(...)接口获取。 -
配置实盘、策略参数
Settings:
RobotId为整型,实盘ID可以用GetRobotList(...)接口获取。
Settings为JSON对象类型。Settings参数格式如下:pythonSettings = { "name": "商品期货跨期", // 策略参数 "args": [["symbolA", "rb2205"], ["symbolB", "rb2201"]], // 策略ID,可以用GetStrategyList方法获取到 "strategy": 12345, // K线周期参数,60即为60秒 "period": 60, // 指定在哪个托管者上运行,不写该属性就是自动分配运行 "node" : 12345, // 自定义字段 "appid": "member2", "exchanges": [ // pid可以由GetPlatformList方法获取到 {"pid": 12344, "pair": "FUTURES"}, // 向实盘添加第二个交易所对象 {"pid": 12345, "pair": "FUTURES"}, // 除了优宽控制中心配置好的交易所(pid识别),还可以设置没有配置过的交易所配置信息,用来给实盘操作 /* {"eid": "Futures_CTP", "pair": "FUTURES", "meta" :{...}}, {"eid": "Futures_CTP", "pair": "FUTURES", "meta" :{...}} */ ] }注意:
使用eid的配置中"meta":{...}这些敏感信息,优宽量化交易平台是不储存的。这些数据直接转发给托管者程序,所以每次创建或者重启实盘时必须配置该信息。meta具体格式参看:GetExchangeList接口返回的数据中的meta字段内容。以CTP协议为例,配置华安期货的信息,
meta字段结构如下:javascript"meta": { "MDFront":"180.166.37.178:41215", "TDFront":"180.166.37.178:41207", "BrokerId":"6020", "Name":"华安期货(看穿式监管)", "ClientVer":"BT_T_V001", "AppID":"client_youquant_1.0", "AuthCode":"", "V2":true, "Username":"123456", // 资金账号 "Password":"123456", // 资金账号的密码 }GetExchangeList接口返回的数据中required为真的配置项,在配置mata时不可为空。required为假的配置项在配置mata时可为空。例如上例中的AuthCode、Name。如果重启使用通用协议插件支持的交易所的实盘,需要在配置
Settings这个参数时对于exchanges属性使用如下设置:javascript{"eid": "Exchange", "label" : "test", "pair": "xxx", "meta" :{ ... , "Front" : "http://127.0.0.1:6666/XXX"}}label属性是给当前通用协议接入的交易所对象设置一个标签,在策略中可以使用exchange.GetLabel()函数获取。Front是配置通用协议交易所对象时设置的通用协议通信地址。
-
-
返回值
python{ 'code': 0, 'data': {'result': 1, 'error': None} }
GetRobotDetail
GetRobotDetail,参数:RobotId。获取请求中的API KEY对应的优宽量化交易平台账号下的实盘ID为RobotId的实盘详细信息。
-
参数
RobotId为整型,实盘ID可以用GetRobotList(...)接口获取。 -
返回值
javascript{ 'code': 0, 'data': { 'result': { 'robot': { // 下次扣费时间,即当前扣费后有效截止的时间 'charge_time': 1635491159, // 已经消耗的时间 'charged': 118800, // 已经消耗的金额(0.125 CNY = 12500000 / 1e8) 'consumed': 412500000, 'date': '2021-10-26 09:25:17', // 实盘运行时指派的托管者ID,如果是自动,该值为-1 'fixed_id': 123456, 'hits': 0, 'id': 123456, 'is_deleted': 0, // 是否有权限管理该实盘 'is_manager': True, // 是否是模拟盘 'is_sandbox': 0, // 实盘名称 'name': '商品期货跨期', // 托管者ID 'node_id': 123456, // 实盘配置的交易所对象 'pexchanges': {'123456': 'Futures_CTP'}, 'phash': {'123456': '4789fda123456e879baaa1860d9123456f'}, // 实盘配置的交易所对象的标签信息 'plabels': {'123456': '宏源期货主席(看穿式监管)'}, 'priority': 0, 'profit': 0, // 是否公开 'public': 0, // 最近活跃时间 'refresh': 1635404408000, 'robot_args': '[["symbolA","rb2205"],["symbolB","rb2201"],["Interval",500,12961],["SlideTick",1,12961],["RiskControl",false,12961],["MaxTrade",100,12961],["MaxTradeAmount",1000,12961],["CTAShowPosition",true,12961],["SyncInterval",5,12961]]', 'start_time': '2021-10-29 11:58:50', // 实盘状态 'status': 1, 'strategy_args': '[["symbolA","远月合约","远月合约","rb2005"],["symbolB","近月合约","近月合约","rb2001"],["$$$__cmd__$$$plusHedge","正套","正套","__button__"],["$$$__cmd__$$$minusHedge","反套","反套","__button__"],["$$$__cmd__$$$coverPlus","平正套仓位","平多仓","__button__"],["$$$__cmd__$$$coverMinus","平反套仓位","平空仓","__button__"]]', // 配置的交易所对象,设置的交易对信息 'strategy_exchange_pairs': '[86400,[123456],["FUTURES"]]', // 策略ID 'strategy_id': 123456, // 策略最后修改时间 'strategy_last_modified': '2021-10-27 08:42:07', // 策略名称 'strategy_name': '快速实现一个半自动量化交易工具(教学)', 'strategy_public': '0', 'summary': '2021-10-29 14:11:36 已经连接CTP ! -245 \n 81601.62', 'uid': '03dd491223455629f7a02ae075c65ef936', 'username': 'xx', // 是否开启离线报警 'wd': 0 } }, 'error': None } }返回数据中的
summary属性(实盘状态栏上的信息,缓存10秒,并非最新数据)目前有数据量限制(缓存数据)。数据量限制为200字节,超出的数据会被截断。如果需要更多的状态栏信息数据可以使用GetRobotLogs接口获取(GetRobotLogs接口获取的状态栏信息,summary字段为最新数据)。strategy_exchange_pairs属性说明,用以下数据为例:javascript[86400,[123456],["FUTURES"]]其中第一个数据
86400,代表实盘设置的默认K线周期为1天,即86400秒。[123456]为实盘配置的交易所对象的eid列表(按添加顺序)。
["FUTURES"]为实盘配置的交易所对象设置的交易对(按添加顺序与eid一一对应)。即:
[86400,[123456],["FUTURES"]]对应[K线周期,交易所对象id列表,交易对列表]。
GetAccount
GetAccount,返回请求中的API KEY对应的优宽量化交易平台账号的账户信息。
-
参数
无 -
返回值
python{ 'code': 0, 'data': { 'result': { ... }, 'error': None } }返回的JSON数据中包含当前优宽账户余额、用户名称、绑定邮箱等账户信息。
GetExchangeList
GetExchangeList,返回支持的交易所列表以及需要的配置信息。
-
参数
无 -
返回值
javascript{ 'code': 0, 'data': { 'result': { 'exchanges': [{ 'eid': 'Futures_CTP', 'id': 22, 'logo': 'images/logo/middle/ctp.png', 'meta': '...', // 数据太多 ... 为省略 'name': '商品期货', 'priority': 10, 'stocks': 'FUTURES', 'website': 'http://www.sfit.com.cn/' }, { 'eid': 'Futures_Esunny', 'id': 45, 'logo': 'images/logo/middle/esunny.png', 'meta': '...', 'name': '易盛期货', 'priority': 9, 'stocks': 'FUTURES', 'website': 'http://www.esunny.com.cn/' }, { 'eid': 'Futures_Futu', 'help': '<div class="alert alert-success outline"><a target="_blank" href="https://www.youquant.com/api#%E5%AF%8C%E9%80%94%E8%AF%81%E5%88%B8">富途证券配置帮助文档链接</a></div>', 'id': 90, 'logo': 'images/logo/middle/futu.png', 'meta': '...', 'name': '富途证券', 'priority': 9, 'stocks': 'STOCK', 'website': 'https://www.futunn.com/' }, { 'eid': 'Futures_XTP', 'id': 23, 'logo': '/upload/asset/31b810d98d49b4c81dc.png', 'meta': '...', 'name': '中泰XTP', 'stocks': 'STOCK', 'website': 'https://www.zts.com.cn' }] }, 'error': None } }返回的JSON数据中包含当前平台支持的交易所、协议、券商系统的具体配置信息。
DeleteNode
DeleteNode,参数:Nid。删除请求中的API KEY对应的优宽量化交易平台账号下的ID为Nid的托管者节点。
-
参数
Nid为整型,即托管者ID。 -
返回值
python{ 'code': 0, 'data': {'result': True, 'error': None} }
DeleteRobot
DeleteRobot,参数:RobotId, DeleteLogs。删除请求中的API KEY对应的优宽量化交易平台账号下的ID为RobotId的实盘。
-
参数
RobotId为整型,RobotId即要删除的实盘的ID。
DeleteLogs为布尔类型,DeleteLogs设置是否要删除日志,如果传入true即删除日志。 -
返回值
javascript// 实盘删除成功返回值 { 'code': 0, 'data': {'result': 0, 'error': None} }如果尝试删除一个运行中的实盘,返回的数据中
result为-1。
GetStrategyList
GetStrategyList,获取请求中的API KEY对应的优宽量化交易平台账号下的策略信息。
-
参数
无 -
返回值
javascript{ 'code': 0, 'data': { 'result': { 'all': 33, 'strategies': [...] // ... 省略 }, 'error': None } }返回的JSON数据中包含当前优宽账户下所有策略的信息。
NewRobot
NewRobot,参数:Settings。根据参数设置创建一个请求中的API KEY对应的优宽量化交易平台账号下的实盘。
-
参数
Settings,为JSON对象类型。Settings是一个实盘配置JSON对象。Settings描述解释如下:pythonSettings = { "name": "hedge test", /* args为策略参数,不一定要和策略参数顺序一致,不过参数名必须一致 注意:参数数组中第二个元素["MAType", 0, 75882]是一个包含三个元素的数组 其中第一个元素MAType是该实盘绑定策略引用的模板上的参数 第二个元素0是参数MAType的设置的具体值 第三个元素75882是MAType这个参数所属模板的ID,用来标识确定这个参数是哪个模板的参数 */ "args": [["Interval", 500], ["MAType", 0, 75882]], // 策略ID,可以用GetStrategyList方法获取到 "strategy": 25189, // K线周期参数,60即为60秒 "period": 60, // 可以指定在哪个托管者上运行,不写该属性就是自动分配 "node" : 52924, // 自定义字段 "appid": "member2", // 指定实盘分组 "group": 1122, "exchanges": [ // pid可以由GetPlatformList方法获取到 {"pid": 15445, "pair": "FUTURES"}, {"pid": 13802, "pair": "FUTURES"}, // 除了优宽控制中心配置好的交易所(pid识别),还可以设置没有配置过的交易所配置信息,用来给实盘操作 /* {"eid": "Futures_CTP", "pair": "FUTURES", "meta" :{...}}, {"eid": "Futures_CTP", "pair": "FUTURES", "meta" :{...}} */ ] }注意:
eid的配置中"meta":{...}这些敏感信息优宽量化交易平台是不储存的。这些数据直接转发给托管者程序,所以每次创建或者重启实盘时必须配置该信息。meta具体格式参看:GetExchangeList接口返回的数据中的meta字段内容。以CTP协议为例,配置华安期货的信息,
meta字段结构如下:javascript"meta": { "MDFront":"180.166.37.178:41215", "TDFront":"180.166.37.178:41207", "BrokerId":"6020", "Name":"华安期货(看穿式监管)", "ClientVer":"BT_T_V001", "AppID":"client_youquant_1.0", "AuthCode":"", "V2":true, "Username":"123456", // 资金账号 "Password":"123456", // 资金账号的密码 }GetExchangeList接口返回的数据中required为真的配置项,在配置mata时不可为空。required为假的配置项在配置mata时可为空。例如上例中的AuthCode、Name。如果创建使用通用协议插件支持交易所的实盘,在配置
Settings这个参数时,对于exchanges属性可以使用如下设置:javascript{"eid": "Exchange", "label" : "test", "pair": "xxx", "meta" :{ ... , "Front" : "http://127.0.0.1:6666/XXX"}}label属性是给当前通用协议接入的交易所对象设置一个标签,在策略中可以使用exchange.GetLabel()函数获取。Front是配置通用协议交易所对象时设置的通用协议通信地址。 -
测试用策略:
-
策略参数
Interval -
JavaScript策略代码javascriptfunction main(){ Sleep(6000) Log(exchange.GetAccount()) Log("Interval:", Interval) }
-
-
返回值:
javascript// 成功创建实盘 { "code": 0, "data": { "result": 74260, "error": null } }
PluginRun
PluginRun,参数:Settings。使用扩展API接口调用调试工具功能。
调试工具页面地址:https://www.youquant.com/m/debug
-
参数
Settings为JSON对象类型,即调试工具中的设置(Settings配置中包括了测试代码写在source属性中)。 -
测试代码
Python范例:python#!/usr/bin/python # -*- coding: utf-8 -*- import time import json import ssl ssl._create_default_https_context = ssl._create_unverified_context try: import md5 import urllib2 from urllib import urlencode except: import hashlib as md5 import urllib.request as urllib2 from urllib.parse import urlencode # API KEY已经打码,可用自己的API KEY测试 accessKey = 'f77XXXXXXXXXXXXXXX757' # API KEY已经打码,可用自己的API KEY测试 secretKey = 'd8XXXXXXXXXXXXXXXX41ca97ea15' def api(method, *args): d = { 'version': '1.0', 'access_key': accessKey, 'method': method, 'args': json.dumps(list(args)), 'nonce': int(time.time() * 1000), } d['sign'] = md5.md5(('%s|%s|%s|%d|%s' % (d['version'], d['method'], d['args'], d['nonce'], secretKey)).encode('utf-8')).hexdigest() # 注意: urllib2.urlopen 函数,超时问题,可以设置超时时间,urllib2.urlopen('https://www.youquant.com/api/v1', urlencode(d).encode('utf-8'), timeout=10) 设置超时 10秒 return json.loads(urllib2.urlopen('https://www.youquant.com/api/v1', urlencode(d).encode('utf-8')).read().decode('utf-8')) # 返回托管者列表 code = ''' function main() { Sleep(6000) Log(exchange.GetAccount()) return exchanges[0].GetPosition() } ''' settings = { # K线周期参数,60即为60秒 "period": 60, "source": code, # 托管者ID,可以指定在哪个托管者上运行实盘,如果该值为-1,代表自动分配 "node" : 12345, "exchanges": [ {"pid": 1234, "pair": "FUTURES"}, {"pid": 1223, "pair": "FUTURES"} ] } print(api('PluginRun', settings))注意:
{"pid": 1234, "pair": "FUTURES"}
{"pid": 1223, "pair": "FUTURES"}
对于settings中的exchanges属性来说,在调用PluginRun接口时只用设置一个(在调试工具页面使用时也只支持一个交易所对象)。在settings里设置2个交易所对象不会引起报错,但是在代码中如果访问第二个交易所对象就会报错。 -
返回值
api("PluginRun", settings)返回结果:{ 'code': 0, 'data': { 'result': ' { "logs":[ {...} // Log(exchange.GetAccount()) ], "result":"[]" // return exchanges[0].GetPosition() }', 'error': None } }
GetRobotLogs
GetRobotLogs,参数:robotId, logMinId, logMaxId, logOffset, logLimit, profitMinId, profitMaxId, profitOffset, profitLimit, chartMinId, chartMaxId, chartOffset, chartLimit, chartUpdateBaseId, chartUpdateDate, summaryLimit。获取请求中的API KEY对应的优宽量化交易平台账号下的ID为robotId的实盘的日志信息。
-
参数
参数名 类型 备注 robotId 为int类型 实盘ID table Log,查询数据库表Log的数据:
参数名 类型 备注 logMinId 为int类型 Log日志的最小ID logMaxId 为int类型 Log日志的最大ID logOffset 为int类型 由logMinId和logMaxId确定范围后,根据logOffset偏移(跳过多少条记录),开始作为获取数据的起始位置。 logLimit 为int类型 确定起始位置后,选取的数据记录条数。 table Profit,查询数据库表Profit的数据:
参数名 类型 备注 profitMinId 为int类型 记录最小ID profitMaxId 为int类型 记录最大ID profitOffset 为int类型 偏移(跳过多少条记录),作为起始位置 profitLimit 为int类型 确定起始位置后,选取的数据记录条数。 table Chart,查询数据表Chart的数据:
参数名 类型 备注 chartMinId 为int类型 记录的最小ID chartMaxId 为int类型 记录的最大ID chartOffset 为int类型 偏移 chartLimit 为int类型 需要获取的记录条数 chartUpdateBaseId 为int类型 查询的更新后的基础ID chartUpdateDate 为int类型 数据记录更新时间戳,会筛选出比这个时间戳大的记录 summaryLimit,查询状态栏数据:
查询实盘的状态栏数据,该参数类型为整型,设置0表示不需要查询状态栏信息,设置为非0表示需要查询的状态栏信息字节数(该接口不限制数据量,可以指定一个较大的
summaryLimit参数来获取所有状态栏信息)。状态栏数据储存在返回的数据的summary中。Python范例:pythonapi('GetRobotLogs', 63024, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) # 具体代码,参看以上内容:4、简单的例子,此处不再赘述,只写GetRobotLogs的调用和参数传入 -
返回值
返回数据:python{ 'code': 0, 'data': { 'result': { 'chart': '', 'chartTime': 0, 'logs': [ { 'Total': 4, 'Max': 12, 'Min': 9, 'Arr': [ [12, 5, '', '', 0, 0, '[]', 1635495362912, '', ''], [11, 3, 'Futures_Futu', '', 0, 0, 'Buy(1826.08, 1000): TrdMarket_CN: 缺少必要的参数: secMarket', 1635495362904, '', ''] ] }, {'Total': 0, 'Max': 0, 'Min': 0, 'Arr': []}, {'Total': 0, 'Max': 0, 'Min': 0, 'Arr': []} ], 'node_id': 507885, 'online': True, 'refresh': 0, 'status': 3, 'updateTime': 1635495362919, 'wd': 0 }, 'error': None } } -
数据库中的策略日志表
查询的日志截图:

以上返回结果数据中
Arr属性值描述:javascript'Arr': [ [12, 5, '', '', 0, 0, '[]', 1635495362912, '', ''], [11, 3, 'Futures_Futu', '', 0, 0, 'Buy(1826.08, 1000): TrdMarket_CN: 缺少必要的参数: secMarket', 1635495362904, '', ''] ]id logType eid orderId price amount extra date contractType direction 12 5 '' '' 0 0 '[]' 1635495362912 '' '' 11 3 'Futures_Futu' '' 0 0 'Buy(1826.08, 1000): TrdMarket_CN: 缺少必要的参数: secMarket' 1635495362904 '' '' extra为打印的日志的附加消息。logType值具体代表的日志类型:logType: 0 1 2 3 4 5 6 logType意义: BUY SALE RETRACT ERROR PROFIT MESSAGE RESTART 中文意义 买单类型日志 卖单类型日志 撤销 错误 收益 日志 重启 -
数据库中的收益图表日志表
该图表日志表数据与策略日志表中的收益日志一致。javascript"Arr": [ [202, 2515.44, 1575896700315], [201, 1415.44, 1575896341568] ]以其中一条日志数据为例:
[202, 2515.44, 1575896700315]202为日志ID,2515.44为收益数值,1575896700315为时间戳。 -
数据库中的图表日志表
javascript"Arr": [ [23637, 0, "{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}"], [23636, 5, "{\"x\":1575960300000,\"y\":3.0735}"] ]以其中一条日志数据为例:
javascript[23637, 0, "{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}"],23637为日志ID,0为图表数据系列索引,最后的数据"{\"close\":648,\"high\":650.5,\"low\":647,\"open\":650,\"x\":1575960300000}"为日志数据,这条数据为图表上的K线数据。
交易插件
简介
为了完善交易终端功能、更好的方便手动交易,推出了交易插件功能,位置如下:
插件原理
原理和调试工具相同,发送一段代码到交易终端页面的托管者执行,并且支持返回图表和表格(调试工具目前也升级支持)。和调试工具功能相同只能执行3分钟,不收取费用。可实现一些简单的小功能,复杂的策略还是需要运行实盘。
插件编写
在新建策略页面,设置策略类型为:交易插件,交易插件支持JavaScript、Python、C++、My语言。
插件用途
插件可以执行代码一段时间,可执行一些简单的操作。例如冰山委托、挂单、撤单、计算等任务,和调试工具一样使用return返回结果,也可以直接返回图表和表格。下面举几个例子,其他功能可以自行探索。
-
返回深度的快照
javascript// 返回深度的快照 function main() { var tbl = { type: 'table', title: '深度快照 @ ' + _D(), cols: ['#', 'Amount', 'Ask', 'Bid', 'Amount'], rows: [] } var d = exchange.GetDepth() for (var i = 0; i < Math.min(Math.min(d.Asks.length, d.Bids.length), 15); i++) { tbl.rows.push([i, d.Asks[i].Amount, d.Asks[i].Price+'#ff0000', d.Bids[i].Price+'#0000ff', d.Bids[i].Amount]) } return tbl }pythondef main(): tbl = { "type": "table", "title": "深度快照 @ " + _D(), "cols": ["#", "Amount", "Ask", "Bid", "Amount"], "rows": [] } d = exchange.GetDepth() for i in range(min(min(len(d["Asks"]), len(d["Bids"])), 15)): tbl["rows"].append([i, d["Asks"][i]["Amount"], str(d["Asks"][i]["Price"]) + "#FF0000", str(d["Bids"][i]["Price"]) + "#0000FF", d["Bids"][i]["Amount"]]) return tblc++void main() { json tbl = R"({ "type": "table", "title": "abc", "cols": ["#", "Amount", "Ask", "Bid", "Amount"], "rows": [] })"_json; tbl["title"] = "深度快照 @" + _D(); auto d = exchange.GetDepth(); for(int i = 0; i < 5; i++) { tbl["rows"].push_back({format("%d", i), format("%f", d.Asks[i].Amount), format("%f #FF0000", d.Asks[i].Price), format("%f #0000FF", d.Bids[i].Price), format("%f", d.Bids[i].Amount)}); } LogStatus("`" + tbl.dump() + "`"); // C++ 不支持return json 显示表格,可以创建实盘显示状态栏表格 } -
画跨期差价
javascript// 画跨期差价 var chart = { __isStock: true, title : { text : '差价分析图'}, xAxis: { type: 'datetime'}, yAxis : { title: {text: '差价'}, opposite: false }, series : [ {name : "diff", data : []} ] } function main() { exchange.SetContractType('rb2205') // 实际使用时,需要自行修改需要的合约代码 var recordsA = exchange.GetRecords(PERIOD_M5) exchange.SetContractType('rb2201') // 实际使用时,需要自行修改需要的合约代码 var recordsB = exchange.GetRecords(PERIOD_M5) for(var i = 0; i < Math.min(recordsA.length, recordsB.length); i++){ var diff = recordsA[recordsA.length - Math.min(recordsA.length, recordsB.length) + i].Close - recordsB[recordsB.length - Math.min(recordsA.length, recordsB.length) + i].Close chart.series[0].data.push([recordsA[recordsA.length - Math.min(recordsA.length, recordsB.length) + i].Time, diff]) } return chart }pythonchart = { "__isStock": True, "title": {"text": "差价分析图"}, "xAxis": {"type": "datetime"}, "yAxis": { "title": {"text": "差价"}, "opposite": False }, "series": [ {"name": "diff", "data": []} ] } def main(): exchange.SetContractType("rb2205") recordsA = exchange.GetRecords(PERIOD_M5) exchange.SetContractType("rb2201") recordsB = exchange.GetRecords(PERIOD_M5) for i in range(min(len(recordsA), len(recordsB))): diff = recordsA[len(recordsA) - min(len(recordsA), len(recordsB)) + i].Close - recordsB[len(recordsB) - min(len(recordsA), len(recordsB)) + i].Close chart["series"][0]["data"].append([recordsA[len(recordsA) - min(len(recordsA), len(recordsB)) + i]["Time"], diff]) return chartc++// C++ 不支持 return json 结构画图策略广场中还有其它范例。
使用方式
-
添加交易终端插件模块
如图所示,在交易终端页面打开模块添加菜单,当前优宽账号策略库中的交易终端插件会自动在列表中显示,找到需要添加的插件点击添加。 -
运行插件
点击「执行」,交易终端插件就开始运行。插件不会显示出日志,但是可以返回显示表格。 -
插件运行时间
交易终端插件运行时长最长为3分钟,超过3分钟自动停止运行。
Alpha因子分析工具
简介
分析公式参考了worldquant公开的alpha101:101_Formulaic_Alphas.pdf 中行情计算的方法,基本兼容了其语法(未实现的有说明),并进行了增强。
用于快速对时间序列进行运算,验证想法,使用地址。
页面简介:
函数和操作符
下面的“{}”代表占位符,所有表达式大小写不敏感,x代表数据时间序列
abs(x), log(x), sign(x)字面意思,分别是绝对值、对数、符号函数。
以下操作符+, -, *, /, >, <也符合其标准的含义,==:是否相等,||:逻辑或,x ? y : z:三目运算符。
rank(x):横截面的排序,返回所在百分比。需要指定侯选多个标的池,用于单个行情无法计算,将直接返回原结果。delay(x, d): 序列d周期前的值。sma(x, d): 序列d周期的简单均线。correlation(x, y, d):时间序列x和y过去d周期的相关系数。covariance(x, y, d):时间序列x和y过去d周期的协方差。scale(x, a):归一化数据,使sum(abs(x))=a(a默认为1)。delta(x, d):时间序列x的现在值减去d周期前的值。signedpower(x, a):x^a。decay_linear(x, d):时间序列x带权重的d周期移动平均值,权重为d,d-1,d-2....1(经过归一化处理)。indneutralize(x, g): 针对行业分类g进行中性处理,目前不支持。ts_{O}(x, d): 对时间序列x过去d个周期进行O操作(O可具体代表min、max等,接下来有介绍),d会转为整数。ts_min(x, d): 过去d周期最小值。ts_max(x, d): 过去d周期最大值。ts_argmax(x, d):ts_max(x, d)位置。ts_argmin(x, d):ts_min(x, d)位置。ts_rank(x, d): 过去d个周期时间序列x值的排序(百分比排序)。min(x, d):ts_min(x, d)。max(x, d):ts_max(x, d)。sum(x, d):过去d周期的和。product(x, d):过去d周期的积。stddev(x, d):过去d周期的标准差。
输入数据
输入数据的大小写不敏感,默认的数据是网页上的选择品种,也可以直接指定例如MA888.close
returns:收盘价收益率。open, close, high, low, volume:周期内开盘价、收盘价、最高价、最低价、成交量。vwap:成交量加权成交价,未实现,当前为收盘价。cap:总市值,未实现。IndClass:行业分类,未实现。
其它
支持一次输出多个结果,用列表表示。例如[sma(close, 10), sma(high, 30)]将会在图中画两条线。除了输入时间序列数据外,也可以当成一个简单的计算器。
其它
交易终端
优宽量化交易平台提供模块化、定制化的交易终端页面。可以自由添加各种数据模块、交易功能模块,甚至可以自己编写代码开发模块(交易终端插件)。凭借着高度灵活自由的使用方式也极大方便了手动交易、半程序化交易的用户。
交易终端页面上的各种模块均可以拖动、缩放,可以修改模块绑定的交易对、交易所等设置,可以添加多个同类型的模块。
调试工具
调试工具页面提供了一个快速实盘测试代码的环境,目前仅支持JavaScript语言。
远程编辑
支持本地编辑器远程同步策略代码到优宽量化交易平台,支持Sublime Text/Atom/Vim/VSCode编辑器。在策略编辑页面点击「远程编辑」展开插件下载地址按钮,显示当前策略的远程同步密钥(token)。
点击「更新密钥」可以刷新当前密钥显示,点击「删除密钥」可以删除当前策略的密钥(token)。
不同编辑器的插件安装方式略有差别,可以点击下载按钮跳转到具体的远程同步插件项目。
实盘参数导入导出
运行实盘时需要保存实盘配置的参数数据可以点击「导出参数」按钮。导出的策略参数将以json文件保存,导出的策略参数配置也可以再次导入实盘,点击「导入参数」按钮即可把保存的策略实盘参数导入到当前实盘,导入后点击「更新参数」保存。
策略导入导出文件
-
下载源码
导出策略源码,导出的文件类型基于策略的编程语言。JavaScript策略导出扩展名为js的文件;python策略导出扩展名为py的文件;c++策略导出扩展名为cpp的文件;麦语言策略导出扩展名为txt的文件。注意只导出策略源码,不包含策略参数、模板引用等信息。 -
导出策略
导出完整策略,包含策略源码、参数设计等策略所有信息,导出的文件为xml文件。 -
导入策略
使用「导出策略」功能导出的xml文件,在策略编辑页面点击「导入策略」按钮选中需要导入的xml文件即可导入完整策略。导入后需要点击「保存」按钮保存策略。
多国语言支持
策略名称,策略参数的描述,都可以用中文|英文的形式书写,让网页自动识别语言显示。
其它地方,例如:策略描述,使用说明等这些Markdown格式的文本,用中文或者中文也可以达到自动识别的效果,以上例子的效果如下图所示:
切换语言后,刷新网页后生效。
在策略代码中可以写入字符串的函数也支持语言切换,例如Log函数、LogStatus函数等。
javascript
function main() {
Log("日志")
var table = {
type: "table",
title: "操作",
cols: ["列1", "列2", "操作"],
rows: [
["螺纹钢主力合约", "甲醇主力合约", {"type": "button", "cmd": "coverAll", "name": "平仓|cover", "description": "描述|description"}] // 注意:按钮中不用加标签
]
}
LogStatus("[trans]信息|message", "\n`" + JSON.stringify(table) + "`")
throw "错误"
}
python
import json
def main():
Log("日志")
table = {
"type": "table",
"title": "操作",
"cols": ["列1", "列2", "操作"],
"rows": [
["螺纹钢主力合约", "甲醇主力合约", {"type": "button", "cmd": "coverAll", "name": "平仓|cover", "description": "描述|description"}]
]
}
LogStatus("信息", "\n`" + json.dumps(table) + "`")
raise Exception("错误")
c++
void main() {
Log("日志");
json table = R"({
"type": "table",
"title": "操作",
"cols": ["列1", "列2", "操作"],
"rows": [
["螺纹钢主力合约", "甲醇主力合约", {"type": "button", "cmd": "coverAll", "name": "平仓|cover", "description": "描述|description"}]
]
})"_json;
LogStatus("信息", "\n`" + table.dump() + "`");
Panic("错误");
}
托管者程序参数
下载托管者软件之后,解压缩后的可执行文件,文件名robot即为托管者程序,在部署托管者时可以给托管者程序指定参数。
-v:查看当前托管者程序的版本、编译时间等信息。
完整的执行命令以苹果电脑Mac系统为例:./robot -v。-s:运行托管者程序时指定的和优宽量化交易平台通信的地址。
完整的执行命令以苹果电脑Mac系统为例:./robot -s node.youquant.com/xxxxxxx,xxxxxxx部分为每个优宽量化交易平台账号唯一的识别ID,命令执行后会提示要求输入对应的优宽量化交易平台账号的密码。-p:可以直接在运行命令中指定参数输入密码,不建议这样做,因为会留下密码参数在当前系统记录中。假设地址node.youquant.com/xxxxxxx对应的账号密码为:abc123456。
完整的执行命令以苹果电脑Mac系统为例:./robot -s node.youquant.com/xxxxxxx -p abc123456。-n:给运行的托管者程序附加标签信息。
完整的执行命令以苹果电脑Mac系统为例:./robot -n macTest -s node.youquant.com/xxxxxxx。在平台托管者管理页面的托管者信息中会有macTest文本标记。-l:打印当前托管者支持的交易所列表。
完整的执行命令以苹果电脑Mac系统为例:./robot -l。即可输出所支持的交易所名称。
实盘报错、异常退出的常见原因
- 策略静态语法错误(此类错误比较明显,通常在策略编辑页面可以看到错误标记), 在回测时可以发现纠正。
- 策略运行时错误, 最常见的例如对函数返回值不做合法判断直接使用。
- 在全局变量里保存过多不能垃圾回收的内容, 导致占用内存过大。
- 使用异步
exchange.Go函数,操作时没有合理wait等待协程结束,导致协程数量过大。 - 函数递归调用层数过多导致超出协程堆栈大小。
- 接口业务错误、网络请求错误等,此类报错会显示相关的交易所对象名称、函数名称、错误相关的消息和原因等信息,此类错误不会导致实盘异常停止(此类报错通常是起因,但是并非是直接原因,直接原因通常是没有对接口返回值判断合法性直接使用引起的程序异常)。
- 平台底层报错,常见的有
Decrypt: Secret key decrypt failed错误,该错误会导致实盘无法启动。错误原因是修改了优宽量化交易平台的账号密码导致所有配置的API KEY失效,需要重新配置API KEY,重启托管者即可。 - Python策略出租时由于平台给策略加密的Python和策略运行时的Python版本不兼容导致的报错:
ValueError: bad marshal data (unknown type code),将策略运行的Python环境升级或者安装为:Python 2.7、Python 3.5、Python 3.6其中之一的策略支持的版本即可。 interrupt错误,该错误是由于程序在执行某个操作(例如访问交易所接口)时,用户点击了实盘页面上的停止实盘按钮,实盘停止中断了当前的操作打印的报错信息。该报错并没有什么影响,仅仅是一个日志记录。
实盘、策略分组
在优宽量化交易平台「实盘」页面、「策略库」页面可以点击右侧分组管理按钮,用来给策略、实盘分组管理。例如对于策略的分组管理时可以把模板类库分为一组、JavaScript语言的策略分为一组、测试用策略分为一组。
子账号、实盘围观
子账号
登录平台后,点击「控制中心」、「账号设置」跳转到优宽账户管理页面。点击「子账户组」可以看到子账户创建页面,操作权限控件内选择所创建子账号可以访问的实盘,用户信息控件内设置子账号用户名、子账号登录密码。点击「创建子账户」按钮即可创建一个子账号。创建后的子账号会在当前页面显示、并且可以「修改」、「锁定/解锁」、「删除」。
子账号只有有限权限,只能看到操作权限设置中授权的实盘。对于授权的实盘拥有修改参数、停止实盘、重启实盘的权限,但是无法修改实盘配置的交易所对象。子账号的使用场景通常为:
- A.量化团队管理多个实盘策略时方便登录、管理。
- B.策略租用时的调试。
实盘围观
在优宽平台实盘页面的实盘列表中点击「公开」按钮即可公开展示当前行的实盘。实盘围观目前有两种方式:
- 1、在优宽平台公开的实盘围观页面展示实盘。当点击「公开」按钮后选择公开分享即可。
- 2、创建围观私链。
当点击「公开」按钮后选择内部分享,设置有效期后即可生成一个私有链接用来登录该策略实盘的私有围观页面。
策略分享、出租
在策略库页面,策略右侧的「操作项」按钮点击后弹出的菜单中有分享、出租操作选项。
策略分享
-
公开分享
点击「分享」按钮后会弹出对话框,可以选择「公开分享」。策略即完整的分享在平台的策略广场,任何用户都可以复制该策略。 -
内部分享
点击「分享」按钮后会弹出对话框,可以选择「内部分享」。选择分享有效期、分享次数之后会生成该策略的复制页面地址和复制码。可以分发给指定的优宽平台用户,需求该策略的用户只需使用复制页面地址链接,登录复制页面后输入复制码即可获取该策略,获取后策略自动会在策略库中出现。
策略出租
-
公开出售
点击「出租」按钮后会弹出对话框,可以选择「公开出售」。策略即可申请上架(需要通过审核)。 -
内部出售
点击「出租」按钮后会弹出对话框,可以选择「内部出售」。选择使用天数、最多并发、注册码个数之后会生成该策略的注册页面地址和注册码。可以分发给指定的优宽平台用户,需求该策略的用户只需使用注册页面地址链接,登录注册页面后输入注册码即可获取策略的使用权。策略也会出现在策略库中,不过只有回测、实盘使用权,看不到策略源码等信息。
重要提示,在创建、分发策略注册码时请务必仔细确认是「注册码」还是「复制码」。以免误将策略分享出去。
回测系统夏普算法
javascript
function returnAnalyze(totalAssets, profits, ts, te, period, yearDays) {
// force by days
period = 86400000
if (profits.length == 0) {
return null
}
var freeProfit = 0.03 // 0.04
var yearRange = yearDays * 86400000
var totalReturns = profits[profits.length - 1][1] / totalAssets
var annualizedReturns = (totalReturns * yearRange) / (te - ts)
// MaxDrawDown
var maxDrawdown = 0
var maxAssets = totalAssets
var maxAssetsTime = 0
var maxDrawdownTime = 0
var maxDrawdownStartTime = 0
var winningRate = 0
var winningResult = 0
for (var i = 0; i < profits.length; i++) {
if (i == 0) {
if (profits[i][1] > 0) {
winningResult++
}
} else {
if (profits[i][1] > profits[i - 1][1]) {
winningResult++
}
}
if ((profits[i][1] + totalAssets) > maxAssets) {
maxAssets = profits[i][1] + totalAssets
maxAssetsTime = profits[i][0]
}
if (maxAssets > 0) {
var drawDown = 1 - (profits[i][1] + totalAssets) / maxAssets
if (drawDown > maxDrawdown) {
maxDrawdown = drawDown
maxDrawdownTime = profits[i][0]
maxDrawdownStartTime = maxAssetsTime
}
}
}
if (profits.length > 0) {
winningRate = winningResult / profits.length
}
// trim profits
var i = 0
var datas = []
var sum = 0
var preProfit = 0
var perRatio = 0
var rangeEnd = te
if ((te - ts) % period > 0) {
rangeEnd = (parseInt(te / period) + 1) * period
}
for (var n = ts; n < rangeEnd; n += period) {
var dayProfit = 0.0
var cut = n + period
while (i < profits.length && profits[i][0] < cut) {
dayProfit += (profits[i][1] - preProfit)
preProfit = profits[i][1]
i++
}
perRatio = ((dayProfit / totalAssets) * yearRange) / period
sum += perRatio
datas.push(perRatio)
}
var sharpeRatio = 0
var volatility = 0
if (datas.length > 0) {
var avg = sum / datas.length;
var std = 0;
for (i = 0; i < datas.length; i++) {
std += Math.pow(datas[i] - avg, 2);
}
volatility = Math.sqrt(std / datas.length);
if (volatility !== 0) {
sharpeRatio = (annualizedReturns - freeProfit) / volatility
}
}
return {
totalAssets: totalAssets,
yearDays: yearDays,
totalReturns: totalReturns,
annualizedReturns: annualizedReturns,
sharpeRatio: sharpeRatio,
volatility: volatility,
maxDrawdown: maxDrawdown,
maxDrawdownTime: maxDrawdownTime,
maxAssetsTime: maxAssetsTime,
maxDrawdownStartTime: maxDrawdownStartTime,
winningRate: winningRate
}
}
- 1

























































