}
另外,也可以设置一个参数,让另一个参数基于该参数的选择,实现显示与隐藏。 比如我们设置参数`numberA`,是一个数值类型。 我们让`numberA`基于一个参数: `isShowA`(布尔类型)的真假决定`numberA`显示与隐藏。 需要把`numberA` 变量在界面参数上设置为:`numberA@isShowA`。这样,不勾选 `isShowA` 参数,`numberA`参数就隐藏了。当我们的策略使用的参数比较多的时候,策略界面参数同时也具有分组功能,只用在开始分组的参数的描述开头加上 `(?第一组)`即可。参数分组多品种或多周期等投资组合中,可以很方便的针对不同的品种使用不同的参数组,并且无需在原有的参数上再次修改。大家注意下,这里的符号要使用英文的。
当然,参数的调优是使用指标函数时非常重要的一步。在实际操作中,不同的交易市场和品种都有自己的特点,因此同样的指标函数,在不同的市场和品种中可能需要不同的参数设置,以达到最佳的效果。这也是为什么在量化交易中,参数的调优显得尤为重要。通过对历史数据进行分析和模拟回测,可以找到最适合当前市场的参数设置,提高交易策略的成功率和盈利水平。在后续的量化策略交易中,我们将为大家展示指标函数的参数调优过程。
最后,温馨提醒下,在计算技术指标时,需要选择合适的统计方法和指标参数,同时还需要考虑指标的灵敏度和有效性,以确保计算结果的准确性和可行性。此外,在使用技术场指标进行投资和交易时,还需要结合其他数据和因素进行分析和判断,包括基本面因素、市场情绪、政策变化等,以做出更加精准和稳健的投资决策。总之,技术指标的计算是量化策略的重要组成部分,它可以为投资者和交易者提供有力的决策支持和参考依据,同时也需要不断地进行优化和改进,以适应市场的变化和发展。
# 6:交易函数
在之前的课程中,我们已经学习了如何通过API获取市场数据,并使用技术分析的方法来计算市场信号,进而呢,可以去选择开平仓的操作。在交易信号确定之后,编写交易函数来自动化执行这些操作是非常必要的。交易函数可以帮助我们更方便、快捷地执行交易策略,以及实现更加精准和稳定的交易决策。
在进行交易函数的编写时,需要考虑交易平台所提供的API和功能。交易函数的主要目的是执行交易操作,包括开多、开空、平多、平空、撤销订单等。一般而言,交易过程应该包括以下几个步骤:
* 第一步,连接交易平台:通过API连接到交易平台,以便获取市场行情、下单、查询持仓等交易操作。
* 第二步,执行交易操作:在获得市场行情和交易信号后,通过API发送相应的交易请求,完成开仓、平仓等操作。
* 第三步,监控交易状态:对于正在持仓的交易,需要实时监测盈亏状态,并进行止损和止盈操作。
首先我们来看第一个步骤。
## 连接交易平台
商品期货策略需要检测与期货公司前置机连接状态,在获取行情之前需要订阅合约,这样才能获取订阅的行情。在优宽量化交易平台上,回测时模拟是和实盘一样的连接机制。在先前的课程中,我们了解、学习的行情接口因为篇幅有限,为了容易理解,并没有检测与前置机连接状态代码,其实一个完整的商品期货策略框架应该首先检查与前置机的连接状态:
```JavaScript
function main(){
while(true){
// 需要在判断exchange.IO("status")函数返回true,即为真值时才可调用行情、交易等函数
if(exchange.IO("status")){
exchange.SetContractType("MA888")
var ticker = exchange.GetTicker()
Log("MA888 tickerBuy:", ticker.Buy)
Log(_D(), "已经连接CTP !")
} else {
Log(_D(), "未连接CTP !")
}
}
}
这段代码中exchange.IO
函数用来调用协议、交易所等其它功能接口。exchange.IO("status")
函数可以判断当前是否和期货公司前置机连接,如果连接成功返回 1,如果是非连接状态就返回0。在连接成功的状况下,Log函数在日志信息显示时间和“已经连接CTP”,这里的_D()
函数会返回当前时间的字符串,连接失败会显示未连接。
交易函数都可以通过exchange对象调用。首先我们来看下买单函数exchange.Buy
。该函数返回一个订单ID。参数值:Price为订单价格。Amount为订单数量,两个参数都是数值类型。exchange.Buy(Price, Amount)
函数的返回值是订单编号,是字符串类型,可用于查询订单信息和取消订单。
exchange.Buy(Price, Amount)
同样的,卖单函数为exchange.Sell
,需要的参数和返回的结果和买单函数一样。
exchange.Sell(Price, Amount)
期货下单时必须注意交易方向。exchange.SetDirection(Direction)
用来设置exchange.Buy
或者exchange.Sell
函数进行期货下单的方向。这里面有特殊的 closebuy_today
和 closesell_today
,这是平今仓的指令,只有上海期货交易所的品种有平今仓指令,大连和郑州交易所的期货品种可以不区分。
下单函数 | SetDirection函数的参数设置的方向 | 备注 |
---|---|---|
exchange.Buy | “buy” | 买入开多仓 |
exchange.Buy | “closesell” | 买入平空仓 |
exchange.Buy | “closesell_today” | 买入平空仓(今仓) |
exchange.Sell | “sell” | 卖出开空仓 |
exchange.Sell | “closebuy” | 卖出平多仓 |
exchange.Sell | “closebuy_today” | 卖出平多仓(今仓) |
在设置好交易方向后,我们就可以进行开平仓的操作,我们使用代码示范下。
function main() {
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("buy")
var id = exchange.Buy(100, 1)
Log("id:", id)
// 设置下单方向
exchange.SetDirection("closebuy")
// 平掉仓位
var id2 = exchange.Sell(100, 1)
Log("id:2", id2)
}
在设置好交易方向后,我们就可以进行开平仓的操作,我们使用代码示范下。我们来看下交易逻辑。
设置合约代码为"rb888",即螺纹钢主力期货合约。接着设置交易方向为"buy",开多仓。使用exchange.Buy
函数下单,买入1手期货合约,价格为ticker的卖单,所以会立即成交。将返回的订单编号存储在id变量中。
然后设置交易方向为"closebuy",平多仓。使用exchange.Sell
函数下单,卖出1手期货合约。价格是ticker的买单,所以也会立即成交,将返回的订单编号存储在id2变量中。
使用Log函数输出订单编号id和id2,以便后续查看订单状态等信息。可以看到结果里,买入开多仓的id为1,卖出多仓的id为2。
这里只是给大家展示了一个开平仓的操作。需要注意的是,此代码中的交易策略比较简单,没有考虑风险控制等因素,只是演示了如何使用API函数进行期货交易的基本操作。在实际交易中,需要根据自己的交易策略和风险偏好进行相应的修改和补充。
exchange.CreateOrder()
函数用于下单。该函数最大的功能是直接在该函数的参数中指定下单的品种、方向。这样就不再依赖系统当前设置的交易对、合约代码、交易方向等设置了。在多品种交易下单场景中、并发场景中极大程度的降低了设计复杂度。exchange.CreateOrder()
函数的四个参数分别是symbol、side、price、amount。用来指定订单的合约代码、方向、价格、数量。
function main() {
while(!exchange.IO("status")) {
Sleep(1000)
}
// 调用CreateOrder函数下单
var id = exchange.CreateOrder("rb2410", "buy", 3500, 1)
Log(id)
}
如果要查询订单的状态,可以根据订单号获取订单详情。参数值:Id为需要获取的订单号,参数Id为字符串类型。它的返回值是Order结构体。
exchange.GetOrder(orderId)
根据具体订单Id查询订单详细信息。
function main(){
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("sell")
var id = exchange.Sell(99999, 1)
var order = exchange.GetOrder(id)
Log("Id:", order.Id, "Price:", order.Price, "Amount:", order.Amount, "DealAmount:",
order.DealAmount, "Status:", order.Status, "Type:", order.Type)
}
我们来看下代码,方向和下单都是sell,所以是一个开空仓的操作。这里的下单价格只是举例,较大的价格不会成交,订单会处于待成交状态,在实盘中我们可以自行调整价格。订单建立后,参数id为订单号码,在GetOrder
函数中填入我们想要查询的订单的号码。
重点看一下返回值结果:
ORDER_STATE_PENDING
),表示该订单的状态为“未成交”;如果是1(ORDER_STATE_CLOSED
),代表是成交;如果是2(ORDER_STATE_CANCELED
),代表被撤销的订单;如果是3(ORDER_STATE_UNKNOWN
),代表订单状态未知。函数exchange.GetOrders()
,当不填写参数的时候,获取所有未完成的订单。可填写参数symbol
,获取目标品种的订单信息。
返回值:Order
结构体数组。当交易所对象exchange
代表的账户当前交易对没有挂单(就是没有未完成的订单时)时,调用该函数将返回空数组([])。
以下代码设置了两个不能成交的单子,然后利用GetOrders
获取到了所有未完成的订单的信息。
function main(){
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)
// 填写参数
var rborder = exchange.GetOrders("rb888")
Log(rborder)
}
同样的,这里设置了两个不能成交的单子,然后利用GetOrders
获取到了所有未完成的订单的信息。
exchange.GetHistoryOrders()
函数用于获取当前交易日内的所有合约的历史订单,支持查询指定合约的历史订单。exchange.GetHistoryOrders()
函数有两种调用形式:
exchange.GetHistoryOrders()
当不传任何参数时,获取所有合约的历史订单。exchange.GetHistoryOrders("rb2410")
当指定具体合约代码时,获取具体合约的历史订单。function main(){
// 鉴于测试代码,不使用商品期货策略一般架构,这里仅仅判断exchange.IO("status")函数,判断连接期货公司前置机成功后立即执行测试代码。股票证券无需使用exchange.IO("status")判断连接状态
while (!exchange.IO("status")) {
Sleep(1000)
}
var orders = exchange.GetHistoryOrders()
Log(orders)
}
注:
回测系统和实盘系统该函数返回结果有所区别:
since
参数模拟本交易日生成订单;在实盘中,为了确保交易成功,价格参数可以传-1。但是在回测系统中不支持。商品期货除了使用市价单还可以用限价单方式下单,可以使用一个较大的滑价确保和对手盘成交。
function main() {
while(true) {
if (exchange.IO("status")) {
exchange.SetContractType("rb888")
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))
} else {
Log("未连接")
}
}
}
函数exchange.CancelOrder(orderId)
可以根据订单 ID 取消订单。 比如下面的代码: 我们使用Sell
函数下单了一个价格为99999不能成交的单子。 然后使用CancelOrder
函数传入 id 参数,来取消了这个订单。最后使用GetOrder
函数获取这个 id 的当前订单状态,可以看到打印出的订单信息,其中 Status属性为 2,代表被取消的订单。
function main(){
while (!exchange.IO("status")) {
Sleep(1000)
}
// 设置合约代码
exchange.SetContractType("rb888")
// 设置下单方向
exchange.SetDirection("sell")
// 下单价格只是举例,较大的价格不会成交,订单会处于订单薄中待成交状态,具体测试可以自行调整价格
var id = exchange.Sell(99999, 1)
exchange.CancelOrder(id)
Log(exchange.GetOrder(id))
}
在订单完成以后,我们来看下有关于查看交易状态的函数。
第一个exchange.GetAccount()
,它会返回交易所账户信息。具体包含以下几个字段:
需要注意的是,在实际交易中,账户资金和持仓情况会随着交易的进行而发生变化,因此需要及时查询和更新账户信息,以便进行下一步的交易操作。
function main(){
while (!exchange.IO("status")) {
Sleep(1000)
}
// 获取账户资产信息,可以不用设置合约
var account = exchange.GetAccount()
Log("账户信息,Balance:", account.Balance, "FrozenBalance:", account.FrozenBalance, "Stocks:",
account.Stocks, "FrozenStocks:", account.FrozenStocks)
}
函数exchange.GetPositions()
,获取所有合约当前持仓信息。exchange.GetPositions()
函数不依赖于当前设置的合约代码,不传symbol
参数时获取所有合约当前持仓信息。传入symbol
参数时获取指定合约的持仓信息。该函数兼容exchange.GetPosition()
调用。
// 不传参数
exchange.GetPositions()
// 传入symbol参数
exchange.GetPositions(symbol)
函数返回结果为Position
结构体:
PD_LONG
(多头仓位,如果区分今仓、昨仓,PD_LONG
表示今仓), PD_SHORT
(空头仓位,同理代表今日空仓), PD_LONG_YD
(昨日多头仓位), PD_SHORT_YD
(昨日空头仓位)。需要注意的是,GetPositions
函数获取的是所有持仓品种的持仓信息,如果没有持仓则返回空数组,所以使用该接口返回的数据前要先判断返回的数据是否为空数组。
/*
注意:GetPositions函数获取的是所有持仓品种的持仓信息,如果没有持仓则返回空数组,所以使用该接口返回的数据前要先判断返回的数据是否为空数组
*/
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.GetPositions()
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)
}
}
商品期货的持仓需要注意:
GetPositions
函数返回的持仓数据Position
结构数组中,Position
结构的Type
属性仅为PD_LONG
或者PD_SHORT
。好了,量化策略中交易函数的内容就讲到这里,在实际的工作中,使用交易函数需要综合考虑市场和策略的各种因素,注重实践和经验总结,才能够获得良好的交易效果和表现。
在前面一个阶段的课程中,我们从 JavaScript 语言的简介、基础语法、金融数据的获取和计算等方面为大家讲解实现交易策略的前提部分,本篇我们将继续前面的内容,从常用的策略模块、技术指标,一步一步帮助大家实现一个可行的日内量化交易策略。
布林带也称为布林通道,英文简称BOLL。它是最常用的技术指标之一,由约翰·包宁杰(John Bollinger)在1980年代发明。理论上,价格总是围绕着价值在一定范围内上下波动,布林带正是根据这个理论基础,引入了“价格通道” 的概念。布林带的计算方式是利用统计学原理,先计算一段时间价格的“标准差” ,再由均线加/减2倍的标准差。给大家稍微解释一下,这里假设价格的波动符合正态分布,2倍的标准差就在95%的置信区间,所以属于正常范围的波动,这样就可以求出价格的“信赖区间” ,如果波动超过2倍的标准差,可以认为是均值,也就是价格发生了实质性的改变。其基本的型态是由三条轨道线组成的带状通道(中轨、上轨、下轨)。中轨为价格的平均成本,上轨和下轨分别代表价格的压力线和支撑线。
由于采用了标准差的概念,使得布林通道的宽度会根据近期价格的波动而做出动态调整。波动小,布林通道会变窄;波动大,布林通道会变宽。当BOLL通道由宽变窄,说明价格逐渐向均值回归。当BOLL通道由窄变宽,意味着行情开始发生变化,如果价格上穿上轨,表明买力增强,如果价格下穿下轨,表明卖力增强。
在所有的技术指标中,布林带的计算方法是比较复杂的一种,其中引进了统计学中的标准差概念,涉及到中轨线(MB)、上轨线(UP)和下轨线(DN)的计算。具体参数有两个,N是时间周期,K是标准层宽度系数。三条轨道具体的计算方法如下:
但是使用JavaScript的ta
库,布林带的计算可以使用成熟的函数获取布林带的上轨,中轨和下轨。这里我们使用的是默认的参数,20个周期,标准差为2。然后使用log函数将三个轨道的数组打印出来。
在优宽量化工具中,获取布林带数组很简单,直接调用布林带的 API 就可以了,因为布林带数组是一个二维数组。二维数组其实很好理解,它就是数组中的数组,那么获取的顺序就是: 先获取数组中指定的数组,然后在从指定的数组中获取指定的元素。索引为0,1,2的元素就是布林带的上轨,中轨和下轨数组。
这里为了以防最新的k线没有走完,所以我们获取的是倒数第二根布林带轨道的值[r.length-2]
。当然这只是技术指标的计算,技术指标是为了判断策略逻辑。布林线的使用方法有很多,可以单独使用,也可以和其他指标结合在一起使用。
/*backtest
start: 2023-01-29 09:00:00
end: 2023-06-04 15:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES","depthDeep":20}]
*/
function main(){
while(true){
exchange.SetContractType('rb888')
var r = exchange.GetRecords(); //获取K线数组
var boll = TA.BOLL(r, 20, 2); //计算布林带指标
var upLine = boll[0];//获取上轨数组
var midLine = boll[1];//卖取中轨数组
var downLine = boll[2]; //获取下轨数组
Log('上轨:',upLine[r.length-2])
Log('中轨:',midLine[r.length-2])
Log('下轨:',downLine[r.length-2])
}
}
本节教程我们将采用布林线一种最简单的使用方法。就是:当价格自下而上突破上轨,即突破上方压力线时,我们认为多方力量正在走强,一波上涨行情已经形成,买入开仓信号产生;当价格自上而下跌破下轨,就是跌破支撑线时,我们认为空方力量正在走强,一波下跌趋势已经形成,卖出开仓信号产生。
有开仓必然也有平仓,我们来看下平仓的逻辑。如果买入开仓后,价格又重新跌回到了布林线中轨,我们认为多方力量正在走弱,或者空方力量正在加强,卖出平仓信号产生;如果卖出开仓后,价格又重新涨回到布林线中轨,我们认为空方力量正在走弱,或者多方力量正在加强,买入平仓信号产生。因此可以总结市场的操作信号:
/*backtest
start: 2023-01-29 09:00:00
end: 2023-06-04 15:00:00
period: 1d
basePeriod: 1h
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES","depthDeep":20}]
*/
function main(){
var mp = 0 //设置持仓信息
while(true){
exchange.SetContractType('rb888')
var r = exchange.GetRecords(); //获取K线数组
if (r.length < 20){//需要超过K线的长度20
return;}
var boll = TA.BOLL(r, period, width); //计算布林带指标
var upLine = boll[0];//获取上轨数组
var midLine = boll[1];//卖取中轨数组
var downLine = boll[2]; //获取下轨数组
var upPrice = upLine[upLine.length - 3];
//获取上上根K线上轨数值
var midPrice = midLine[midLine.length - 3];
//获取上上根K线中轨数值
var downPrice = downLine[downLine.length - 3];
//获取上上根K线下轨数值
recclose = r[r.length - 2].Close; //获取上根K线收盘价
if(mp == 0 && recclose > upPrice){//如果无持仓,并且收盘价大于上轨,开多
// 设置下单方向
exchange.SetDirection("buy");
exchange.Buy(recclose,1)
mp = 1;}
if(mp == 0 && recclose < downPrice){ //如果无持仓,并且收盘价小于下轨,开空
// 设置下单方向
exchange.SetDirection("sell");
exchange.Sell(recclose,1)
mp = -1;}
if(mp == 1 && (recclose < midPrice)){ //如果持多,并且收盘价小于中轨,平多
// 设置下单方向
exchange.SetDirection("closebuy");
exchange.Sell(recclose-5,1);//这里为了保证交易,所以设置限价单为最新的价格减5
mp = 0;}
if(mp == -1 && (recclose > midPrice )){ //如果持空,并且收盘价大于中轨,平空
// 设置下单方向
exchange.SetDirection("closesell");
exchange.Buy(recclose+5,1);//同样的,为保证交易,这里设置限价单
mp = 0;}
Sleep(1000)
}
}
这是一段使用JavaScript语言编写的期货交易策略代码。该代码实现的交易策略基于布林带指标,用于自动化期货交易。具体说明如下:
首先开始一个无限循环,是为了获取最新K线数据。检查K线的长度是否达到20根或以上,如果没有就结束程序。
使用TA.BOLL
计算20根K线周期内的布林带指标,其中2表示标准差宽度系数。
获取上轨、中轨、下轨的价格。为了防止最新的k线没有走完,所以我们使用倒数第二根k线[r.length-3]
,然后判断倒数第一根k线和布林带的关系,去形成买卖的信号。这样做是为了防止未来函数,就是在历史回测中使用还没有发生,但是已知的信号。
获取前一根K线的收盘价 recclose,作为当前价格的参考。
如果持仓 mp = 0(无持仓) 并且 recclose > upPrice,则开多仓。设置交易方向为 “buy”,然后使用exchange.Buy
函数下单,买入1手期货合约。最后将mp值设为 1,表示现在持有多仓。
如果持仓 mp = 0(无持仓)并且 recclose < downPrice,则开空仓。设置交易方向为 “sell”,然后使用exchange.Sell
函数下单,卖出1手期货合约。最后将mp值设为 -1,表示现在持有空仓。
如果持仓 mp = 1(持有多仓)并且 recclose < midPrice,则平仓。设置交易方向为 “closebuy”,然后使用exchange.Sell
函数下单,卖出1手期货合约。这里为了保证交易,所以设置限价单为最新价格减1。最后将mp值设为 0,表示现在无持仓。
如果持仓 mp = -1(持有空仓)并且 recclose > midPrice,则平仓。设置交易方向为 “closesell”,然后使用exchange.Buy
函数下单,买入1手期货合约。这里同样的,为了保证交易,设置限价单为最新价格加一。最后将mp值设为 0,表示现在无持仓。使用Sleep(1000)函数延迟1秒后再重新获取最新K线数据,继续执行上述的交易策略。
上面这些就是我们学习开发一个完整的日内量化交易策略的每个步骤,包括:策略简介、布林带指标计算方法、策略逻辑、 买卖条件、 策略代码实现等。点击开始回测,我们看到我们的策略的回测结果。
通过这个策略案例,不仅熟悉优宽量化工具的编程方法,还可以根据这个模板改编成不同的策略。策略能否改进的更好呢,上节课我们提到参数的调整,这里我们设置布林带周期和标准差宽度为外部参数,进行参数调优。在参数编辑页面设置好参数以后,在回测页面这里我们设置参数区间,然后进行调参,看一下优化的结果。可以看到,当设置周期为18和标准差宽度为1的时候,取得了最高的胜率和收益,但是使用固定品种和固定周期可能陷入过拟合的风险,大家也需要考虑下。
量化交易策略无非是主观交易经验或系统的总结,如果我们在写策略之前,把主观交易中用到的经验或系统,分别写出来,然后再一条一条翻译成代码,你会发现写策略就会容易很多。大家可以尝试一下!
在量化交易中,画图具有非常重要的作用。画图可以帮助我们更好地理解和分析市场情况,提高交易准确性,同时也可以验证和改进量化交易策略。首先,画图可以帮助我们进行行情分析。通过绘制K线图、均线、波动指标等图形,我们可以更直观地了解市场趋势、价格波动等情况,从而更好地预测市场走势和发现交易机会。其次,画图也可以帮助我们发现交易信号。例如,我们可以通过绘制支撑位、压力位、趋势线等图形来识别市场的趋势和反转点,从而产生相应的交易信号,提高交易准确性。此外,画图还可以帮助我们进行风险管理。例如,我们可以通过绘制止损线、趋势线、波动范围等图形来识别市场风险,并制定相应的止损策略,从而有效控制风险和保护资金。最后,画图还可以帮助我们验证和优化量化交易策略。通过回测和画图,我们可以确认某种策略在特定市场环境下的表现,并针对不同的市场情况进行优化和改进,提高交易效果和收益率。总之,画图在量化交易中具有极其重要的作用,可以帮助我们更好地理解市场行情、发现交易信号、进行风险管理和验证和优化量化交易策略等。因此,在进行量化交易时,合理运用画图工具可以大大提高交易准确性和效果。
在优宽量化平台,JavaScript语言有多种的画图方法,今天我们介绍chart画图函数。Chart(...)
是自定义图表画图函数,专门用于绘制各种类型的交互式图表。它支持折线图、区域图、柱状图、饼图等多种类型的图表,并提供了丰富的工具和选项,可以满足各种需求。
首先我们讲下chart函数的使用方法,在使用chart画图函数时,我们需要使用数据和选项的配置对象来控制图表的显示和交互效果。
选项配置对象用于控制图表的显示效果、交互效果等。以下是选项配置对象的常见属性:
下面我们示范一下,如果我们想画两条双均线(五日均线和十日均线),首先我们可以定义选项配置对象:
var chart = {
// 标记是否为一般图表,有兴趣的可以改成false运行看看
__isStock: true,
// 缩放工具
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
// 标题
title : { text : '均线'},
// 坐标轴横轴即:x轴,当前设置的类型是:时间
xAxis: { type: 'datetime'},
// 坐标轴纵轴即:y轴,默认数值随数据大小调整
yAxis : {
// 标题
title: {text: '均线'},
// 是否启用右边纵轴
opposite: false
},
}
该选项配置对象包含了对图表的完整配置。其中__isStock
属性表示是否为一般图表,选择true为Highstocks,Highstocks是一个专门用来创建交互式股票图表和金融图表的JavaScript库。它是Highcharts图表库的一部分,并提供了更多的功能,包括支持股票指标、数据区域缩小、鼠标拖拽和滚轮缩放等。tooltip属性定义了缩放工具的格式,title属性定义了图表的标题,xAxis属性定义了X轴的配置,yAxis属性定义了Y轴的配置。
数据配置对象用于控制图表的数据源。其中,最重要的属性是series,它是一个数组,每个元素表示一个系列(Series)的数据。
数据配置对象:
// 数据系列,该属性保存的是各个数据系列(线,K线图,标签等...)
series : [
// 索引为0,data数组内存放的是该索引系列的数据
{name : "line1", id : "线1,五日均线", data : []},
// 索引为1,设置了dashStyle:'shortdash'即:设置虚线
{name : "line2", id : "线2,十日均线", dashStyle : 'shortdash', data : []}
]
该数据配置对象包含了两条均线数据,分别被定义在一个数组上。其中每个数据系列都有name、id、data属性。name属性用于图例显示的名称,id属性为数据系列的唯一标识符,data属性则为数据系列的数据。
我们将数据配置对象和选项配置对象的属性合并到一个名为chart的对象中。这个对象包含了所有的配置选项,从而实现了用一个对象控制整个图表的效果。
var chart = {
// 标记是否为一般图表,有兴趣的可以改成false运行看看
__isStock: true,
// 缩放工具
tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
// 标题
title : { text : '均线'},
// 坐标轴横轴即:x轴,当前设置的类型是:时间
xAxis: { type: 'datetime'},
// 坐标轴纵轴即:y轴,默认数值随数据大小调整
yAxis : {
// 标题
title: {text: '均线'},
// 是否启用右边纵轴
opposite: false
},
// 数据系列,该属性保存的是各个数据系列(线,K线图,标签等...)
series : [
// 索引为0,data数组内存放的是该索引系列的数据
{name : "line1", id : "线1,五日均线", data : []},
// 索引为1,设置了dashStyle:'shortdash'即:设置虚线
{name : "line2", id : "线2,十日均线", dashStyle : 'shortdash', data : []}
]
}
在chart对象设置完成以后,接着我们需要往里面添加数据。
function main(){
// 调用Chart函数,初始化图表
var ObjChart = Chart(chart)
// 清空
ObjChart.reset()
while(true){
exchange.SetContractType("rb888")
// 获取本次轮询的时间戳,即一个毫秒的时间戳。用来确定写入到图表的X轴的位置
var nowTime = new Date().getTime()
// 获取行情数据
var r = exchange.GetRecords()
// 五日均线
var ave_5 = TA.MA(r, 5)
// 十日均线
var ave_10 = TA.MA(r, 10)
// 用时间戳作为X值,均价作为Y值传入索引0的数据序列
ObjChart.add(0, [nowTime, ave_5[r.length-1]])
// 同上
ObjChart.add(1, [nowTime, ave_10[r.length-1]])
Log(ave_5)
Log(ave_10)
}
}
首先,通过Chart(chart)
函数初始化了一个图表对象ObjChart,其中chart是一个选项配置对象,用于配置图表的各项属性。然后,通过ObjChart.reset()
函数清空了图表中已经存在的数据。接下来,通过一个无限循环while(true)
来实现不断获取并更新行情数据。在每次循环中,调用exchange.GetRecords()
函数获取当前品种的K线行情数据,并用TA.MA()
计算出五日和十日均线的值。然后,通过new Date().getTime()
获取当前时间戳作为X值,将五日和十日均线的值作为Y值,调用ObjChart.add()
函数把这些数据加入到图表中。其中,第一个参数0和1分别对应了选项配置对象中的两个数据序列,即五日均线和十日均线。第二个参数是一个包含X值(也就是时间)和Y值(就是最新时刻的均线值)的数组。总之,这段代码通过数据配置对象和选项配置对象来定义图表,然后不断获取K线数据,计算均线,将计算结果添加到图表上,最终实现了两条均线的效果。
这里呢,我们只涉及到均线的计算和绘图,下节课我们将研究下k线图的画法。Chart画图函数确实比较复杂,参数也比较多。不过,对于刚接触量化交易的人来说,掌握这些画图函数十分重要,因为它可以帮助我们更好地理解市场行情和策略效果。但是,对于刚入门量化学习的我们可能会被这些代码吓到,不知道从何下手。这时候,兴趣是最好的老师。只要抱着兴趣和学习的心态,慢慢琢磨、尝试,相信大家一定可以掌握这些画图函数的使用方法。
另外,优宽平台提供了许多丰富的讲解材料和策略案例,这些都可以帮助刚接触量化交易的人更好地入门和掌握相关知识。我们可以通过阅读官方文档、教程视频、参与社区讨论等方式获取更多的相关知识和经验。
因为交易员需要关注多个市场、多种资产的行情数据,以及交易策略的实时表现,经常可以看到职业的交易员有多个屏幕显示不同维度的数据。观察不同维度的数据可以帮助交易员更好地跟踪多个市场、多种资产的行情情况,分析市场趋势和交易信号,以及实时评估交易策略的效果。本节课程呢,我们就是用JavaScript语言实现一些复杂的画图展示。
在量化交易中,复杂图表是指包含多个技术指标的图表或者同时展示多个品种的监控图表等复杂的图表类型。这些图表可能会同时包含多条曲线、多个子图以及各种颜色和标记等元素,从而使其更加详细和全面地呈现市场行情和交易策略效果。复杂图表可以帮助我们更好地了解市场走势和交易机会,同时可以辅助我们进行交易决策,并提供更为细致的风险管理和位置管理。但是,这些图表可能也会比较难懂和使用,需要对技术指标的原理和用法有一定理解、熟练使用相应的量化工具和软件等。
黑色系铁矿石、螺纹钢和热卷是钢铁工业中的三个主要品种,它们的相关性比较强。一般来说,铁矿石作为钢铁生产的原材料,直接影响生产成本,价格上涨或下跌都会对螺纹钢产生一定的影响。而螺纹钢和热卷都是钢铁制品的重要品种,两者的价格都受到市场供需关系、宏观经济环境等多个因素的影响,所以它们之间的价格走势也具有很强的相关性。这段代码是一个基于量化交易框架的示例程序,主要用于实时监控这三个相关品种数据并绘制相应的图表。该段代码实现了对三个不同品种的均线和K线数据的获取和显示。具体来说,该段代码创建了三个图表配置对象 cfgA、cfgB 和 cfgC,分别代表要展示的三个品种 i888、rb888 和 hc888 的图表。每个图表配置对象中包含了图表的标题、x轴的类型等参数,其中每个对象中的数据系列用于展示蜡烛图。之后,使用 Chart 函数将这三个图表配置对象包装成一个 chart 对象。
var cfgA = {
__isStock: true,
title: {
text: 'A'
},
series: [{
type: 'candlestick',
name: 'A',
id: 'A',
data: []
}, {
type: 'line',
yAxis: 0,
name: "A_MA",
data: [],
}]
}
var cfgB = {
__isStock: true,
title: {
text: 'B'
},
series: [{
type: 'candlestick',
name: 'B',
id: 'B',
data: []
}, {
type: 'line',
yAxis: 0,
name: "B_MA",
data: [],
}]
}
var cfgC = {
__isStock: true,
title: {
text: 'C'
},
series: [{
type: 'candlestick',
name: 'C',
id: 'C',
data: []
}, {
type: 'line',
yAxis: 0,
name: "C_MA",
data: [],
}]
}
function main() {
var symbols = ["rb888", "MA888", "i888"]
var chart = Chart([cfgA, cfgB, cfgC])
chart.reset()
var arrLastTime = [0, 0, 0]
while (true) {
if (exchange.IO("status")) {
LogStatus("时间:", _D(), ",已经连接")
for (var i = 0; i < symbols.length; i++) {
exchange.SetContractType(symbols[i])
var r = exchange.GetRecords()
var ma = TA.MA(r, 10)
for (var j = 0; j < r.length; j++) {
if (r[j].Time > arrLastTime[i]) {
// 增加
chart.add(i * 2, [r[j].Time, r[j].Open, r[j].High, r[j].Low, r[j].Close])
chart.add(i * 2 + 1, [r[j].Time, ma[j]])
arrLastTime[i] = r[j].Time
}
else if (r[j].Time == arrLastTime[i]) {
// 更新
chart.add(i * 2, [r[j].Time, r[j].Open, r[j].High, r[j].Low, r[j].Close], -1)
chart.add(i * 2 + 1, [r[j].Time, ma[j]], -1)
}
}
}
chart.update([cfgA, cfgB, cfgC])
}
Sleep(5000)
}
}
在进入 while 循环之后,通过 exchange.SetContractType
函数循环遍历每个品种,调用 exchange.GetRecords()
获取该品种最新一根 K 线数据并存储到变量中,并使用 TA.MA(r, 10)
函数计算10日移动平均线。然后遍历获取到的 K 线数据,根据时间戳判断是需要新增还是更新,根据判断结果使用 chart.add()
方法将K线数据和对应的MA值添加或更新到对应的图表系列中去。其中,i * 2
和 i * 2 +1
表示三个合约的K线数据和对应的均线数据分别在系列数组中的索引位置,该索引位置与初始化的 cfgA、cfgB、cfgC 对象中的系列顺序是相对应的。
下面我们来研究下这段代码的细节:
extension是一个在图表开发中常用的属性,用于对图表进行进一步的自定义和细粒度控制。它可以包含一些子属性,比如在该示例中使用的layout、height、col等。
layout可以指定图表的布局方式,支持多种取值,例如示例代码中的single表示单独显示,不参与分组,正如我们图形中展示的一样,呈现纵向排列的状态。默认取值为group,表示与其他图表一起分组显示。这里我们设置为group看一下,可以看到,是分组折叠展示的。
height是一个数值型属性,指定了图表的高度。该属性只在layout为single时才有效,因为分组时采用的是自适应的方式。
col是一个数值型属性,指定了图表的宽度占据几个单元格。这个属性只在分组布局,也就是group中有意义,因为布局时每行通常包含12个单元格,可以将多个图表放在同一行实现紧凑排列的效果(比如有两个图一个宽度为8,一个为4,所有紧密的并排在一起,如果调大其中一个,那么另一个图像就会换行展示)。
cfgA、cfgB 和 cfgC 分别代表了三个不同品种(i888、rb888 和 hc888)的图表配置对象,三个配置是一样的。对于三个品种,配置对象都用来展示品种的均线走势图和蜡烛图。其中,title 表示图表标题,xAxis 表示 x 轴的类型是类别型轴,series时数据的配置对象,包含了两个系列,分别是均线线形图和蜡烛图。具体解释如下:name 是系列的名称。type 是系列的类型,这里定义了两个不同类型的系列,一个是线形图,另一个是蜡烛图,data 是系列的数据,是一个数组,用来存储该系列要展示的数据。series数据是有单独索引的,第一第二个数据索引属于铁矿石,第三第四属于螺纹钢,最后两个是热卷的。所以可以看到我们的数据添加过程是这样的。
这里我们解释下三个品种数据的添加过程,这段代码中使用了两个 for 循环。
第一个循环语句 for (var i = 0; i < symbols.length; i++)
遍历了 symbols 数组中的所有元素,该数组包含了要显示在图表上的三个品种的合约代码。通过调用 exchange.SetContractType(symbols[i])
方法设置当前合约代码为数组 symbols 中的第 i 个元素。
第二个循环语句 for (var j = 0; j < r.length; j++)
遍历了 r 数组中的所有元素,该数组包含了当前品种最新的 K 线数据。通过遍历 r 数组并将每个元素添加到当前品种对应的系列中,实现了将最新的 K 线数据添加到图表中并刷新的效果。在每次添加完数据之后,通过更新arrLastTime
数组来记录最新的时间戳,并用于判断下一次是否需要添加/更新数据。
这里的数据添加和更新也很有意思,如果上一根k线已经走完,就是新的时间戳大于上一个周期的时间戳,表示新的k线已经产生,这时候就要增加最新的数据。而如果此时的k线周期还没有完成,最新的k线数据还没有固定,这时候就要不断的更新最新的k线数据,add函数里最后使用-1进行k线数据的更新。综上,这两个for
循环共同实现了将最新的 K 线数据添加到对应的系列中,用于刷新图表并展示最新的数据。
在量化交易中,混合图表是指同时展示多种不同类型或不同时间尺度的K线图、技术指标图、成交量图等多种图表,以便更全面地呈现市场行情和交易策略效果。混合图表可以帮助我们更好地了解市场走势和交易机会,同时可以帮助我们对策略的执行效果进行监控和评估。特别是对于一些复杂的交易策略,通过混合图表可以更好地展示策略在不同时间尺度上的表现和回测结果。在优宽平台中,我们提供了丰富的图表类型和工具,包括K线图、技术指标图、成交量图,同时也可以自由组合这些图表,并支持简单的操作交互,使用户可以方便而又详尽地观察市场情况和策略效果。
我们来看一个混合图表的例子。MACD 是一种技术分析指标,全称为“Moving Average Convergence Divergence”,中文翻译为“移动平均线收敛/发散指标”。它由两条曲线和一个柱形图组成,可以帮助分析价格趋势的变化情况,以及判断价格是处于超买还是超卖状态,从而提供交易信号。
在期货中,MACD 也是相当重要的分析工具之一。期货中的价格波动非常剧烈,因此需要使用技术分析工具来帮助交易者理性判断市场走势和价位变动方向,并制定相应的交易策略。MACD 在期货交易中被广泛应用,可作为趋势跟随和逆势交易的参考依据。在期货软件上,MACD 指标通常以两条线和一个柱形图的形式展示,其中包括了 DIF、DEA 和 MACD 三个指标数据。DIF 代表短期(12周期)EMA 值减去长期(26周期)EMA 值的差值。DEA 则是 DIF 的9周期 EMA 平均值,称为离差平均值。MACD 则是 DIF 与 DEA 差值的2倍,表示市场短期动量的差异程度。这些指标通常与 K 线图一起显示,交易者可以通过观察 MACD 指标的变化情况来判断市场走势,制定相应的交易策略。使用JavaScript语言我们可以呈现和期货软件几乎一样的可视化结果。
下面这段代码是展示铁矿石 MACD 指标的变化情况。整个代码分为两个部分:首先定义了一个chartCfg
变量,其中包括了图表的标题、纵轴、数据序列等配置信息,用于初始化图表;然后在main
函数中不断循环获取 i888 合约的 K 线数据,并计算出其对应的指标数据,然后将这些指标数据实时添加到已经初始化的图表上。
var chartCfg = {
subtitle: {
text: "铁矿石MACD指标",
},
yAxis: [{
height: "60%",
lineWidth: 2,