初学数字货币量化交易策略设计时,经常有各种各样的策略需求,不论用那种语言,那种平台,都会遇到各种不同情况的策略设计需求。例如有时候需要多品种轮动,有时候需要多平台对冲,有时候又需要不同品种行情并发等等。下面我们就一起分享下策略需求实现时的一些设计经验。
学习平台依然使用发明者量化交易平台(https://www.fmz.com),市场选择为数字货币市场。
此类需求情况多为需要编写一个多品种趋势策略,多品种网格策略等,需要针对策略逻辑,用不同的交易对行情迭代执行。
通常这样设计:
function Process (symbol) {
exchange.IO(\"currency\", symbol)
var ticker = _C(exchange.GetTicker)
Log(\"已经切换交易对,按照策略逻辑处理交易对 :\", symbol, \"行情:\", ticker)
// ...
// ..
// .
}
function main(){
var symbols = [\"BTC_USDT\", \"LTC_USDT\", \"ETH_USDT\"]
while (true) {
for (var i = 0 ; i < symbols.length; i++) {
Process(symbols[i])
Sleep(500)
}
}
}
我们给机器人配置:



可以看到,这样就实现了在机器人上配置一个交易所对象,切换交易对,获取不同交易对的行情,进行多品种行情,在一种策略逻辑下执行。
可以看到,三种我们定义的交易对:BTC_USDT,LTC_USDT,ETH_USDT ,在循环中依次迭代获取行情,获取行情后,就可以具体对于行情检测,触发策略设计好的交易逻辑。
有的同学可能会提出:“我不喜欢切换交易对,感觉有点麻烦,策略这样设计也不清晰”。
确实也有其它的设计办法,就是下面我们介绍的另一种方式。
通过多个交易所对象来获取不同交易对的行情数据,迭代策略逻辑中执行。
例如这样配置机器人:给机器人配置三个交易所对象,交易对分别设置为 BTC_USDT , LTC_USDT , ETH_USDT 。

名字是 「OKEX现货V3测试」的交易所对象,在控制中心,交易所配置页面:

已经配置好了。
代码修改一下,因为这次我们给机器人添加了多个交易所对象,分别是交易对为 BTC_USDT 、LTC_USDT、ETH_USDT 的交易所对象。
function Process (e) {
var ticker = _C(e.GetTicker)
Log(\"交易所\", e.GetName(), \"按照策略逻辑处理交易对 :\", e.GetCurrency(), \"行情:\", ticker)
// ...
// ..
// .
}
function main(){
while (true) {
for (var i = 0 ; i < exchanges.length; i++) {
Process(exchanges[i])
Sleep(500)
}
}
}
运行机器人:

以上讲述的例子,无论是切换交易对,还是添加一个配置账户的多个不同交易对的交易所对象。
都只是使用一个交易所账号配置(使用一个配置好的交易所)。那么如何在一个策略中使用多个交易所账号呢?
某些策略例如,多交易所跨市对冲,单交易所内多账户策略等。



function main(){
Log(exchanges[0].GetAccount()) // 打印第一个 交易所对象的账户资产信息,即火必期货 这个交易所的资产信息。
Log(exchanges[1].GetAccount()) // ... 打印Bit-Z这个交易所的资产信息
}
当然我也可以,给一个交易所添加第二个、第三个账户的交易所配置。

可以看到,这样就配置了两个「火必期货」交易所的账号。


在创建策略时,机器人的 「修改配置」 选项中就又出现了一个火必期货交易所对象以供选择。


比如这样可以让两个账户一个做先卖后买网格策略(向上),一个做先买后卖网格策略(向下)。
这里讲下机器人上配置多个交易所对象 和 「同一个交易所账户给机器人配置多个交易所对象」的区别:
这个猛地一看和上面讲解的「同一个交易所账户给机器人配置多个交易所对象」例子有些类似,但是是有区别的。
区别在于上面讲的例子是一个交易所配置,即:


在机器人配置交易所对象时,始终用的都是:

这个配置。
只不过是添加交易所对象时,交易对设置不同而已。
如果调用 GetAccount 函数,始终访问的都是同一个账户的资产信息。
然而:

这样配置的两个火必期货交易所对象,虽然都是火必期货,代表的却是不同的交易所账户。
有时候在做数字货币合约对冲的策略时,为了抓住转瞬即逝的交易机会,很多场景需要并发下单。但是因为合约不同,需要在获取行情,下单操作时切换到对应的合约。在使用 exchange.Go 函数并发执行下单函数或者获取行情时,由于有同步的问题,并不是很快。并且这样切换合约的设计也让逻辑显得不是那么简洁。那有没有更好的办法呢?
当然有办法!我们可以按照上文中讲的「同一个交易所账户给机器人配置多个交易所对象」给机器人添加两个交易所对象。

然后还是使用 这个交易所配置,再添加一个交易所对象。
这时会弹出一个提示框!

一个交易所账户配置,不能添加相同币种、交易对的交易所对象。
这样怎么办?看来是不能让策略机器人用两个交易所对象,并且让交易所对象上绑定的是一个交易所账号了码?
还是有办法的!
我们在「控制中心」->「交易所」,再添加一个 OKEX 期货交易所配置。


配置好以后点击保存。




这样带来的好处是什么呢?
当然是编写策略时,设计就很简单啦!
function main(){
exchanges[0].SetContractType(\"quarter\") // 设置第一个添加的交易所对象 当前的合约为季度合约
exchanges[1].SetContractType(\"this_week\") // 设置第二个添加的交易所对象,当前的合约为当周合约
while (true) {
var beginTime = new Date().getTime() // 记录这次获取行情时起始的时间戳。
var rA = exchanges[0].Go(\"GetTicker\") // 创建并发 线程去获取 第一个交易所对象,也就是季度合约的行情数据。
var rB = exchanges[1].Go(\"GetTicker\") // 创建并发 线程去获取 第二个交易所对象,也就是当周合约的行情数据。
var tickerA = rA.wait() // 并发的两个线程各自执行自己的任务,这里等待获取数据,A 等待时,B任务也在执行。
var tickerB = rB.wait() // 所以这里看似是顺序执行,实际在底层是并发的。只不过获取的时候是顺序先获取A,在获取B。
var endTime = new Date().getTime() // 记录并发获取两个合约行情结束时的时间戳。
if (tickerA && tickerB) { // 如果获取的数据没有问题,执行以下逻辑。
var diff = tickerA.Last - tickerB.Last // 计算差价
$.PlotLine(\"diff\", diff) // 使用画线类库把差价画在图表上。
if (diff > 500) { // 如果差价大于500, 对冲套利(当然设置500 的差价是比较大的,很少见。)
// 对冲
rA = exchanges[0].Go(\"Sell\", tickerA.Buy, 1) // 并发线程创建 季度合约下卖单
rB = exchanges[1].Go(\"Buy\", tickerB.Sell, 1) // 并发线程创建 当周合约下买单
var idA = rA.wait() // 等待 返回下单结果,返回的是订单ID
var idB = rB.wait() // ...
}
// ...
}
LogStatus(_D(), \"并发获取两个合约行情耗时:\", endTime - beginTime, \"毫秒。\") // 显示在状态栏上时间,以便知道程序在执行。
Sleep(500)
}
}
这样设计策略是不是感觉简单了许多,思路清晰了许多。
实盘运行:

可以看到,每次并发获取两个合约的行情,只耗时50毫秒左右。
买好币上币库:https://www.kucoin.com/r/1f7w3