上一篇文章中我们一起实现了一个简单的对冲策略,接下来我们学习如何升级这个策略。 策略改动并不会很大,不过改动的细节需要注意。代码中有些地方的定义和之前有变动,需要重点理解。
A交易所->B交易所
,B交易所->A交易所
的差价线,画触发差价的水平线。我们直接使用画线类库
来处理,好处就是简单易用,这里我们也一起学习如何使用FMZ的模版类库
功能。接下来让我们逐一实现这些设计。
以币安现货实盘为例,切换为现货杠杆模式使用代码exchanges[i].IO
,传入参数trade_normal
切换为杠杆逐仓,传入trade_super_margin
切换为杠杆全仓,回测不支持。这个仅仅是在实盘时使用。
在main
函数开头的准备阶段增加:
// 切换杠杆模式
for (var i = 0 ; i < exchanges.length ; i++) { // 遍历检测所有添加的交易所对象
if (exchanges[i].GetName() == "Binance" && marginType != 0) { // 如果当前i索引代表的交易所对象是币安现货,并且策略界面参数marginType选择的不是「普通币币」选项,执行切换
if (marginType == 1) {
Log(exchanges[i].GetName(), "设置为杠杆逐仓")
exchanges[i].IO("trade_normal")
} else if (marginType == 2) {
Log(exchanges[i].GetName(), "设置为杠杆全仓")
exchanges[i].IO("trade_super_margin")
}
}
}
这里策略中仅仅增加了币安现货的切换币币杠杆模式的代码,所以策略参数上设置切换也只对币安现货有效。
使用已经封装好的画图模版就非常简单。我们使用的这个模版名称为画线类库
。可以直接在FMZ平台策略广场上搜索获取。
或者直接点击链接:https://www.fmz.com/strategy/27293 跳转到这个模版的复制页面。
点击按钮即可把这个模版类库复制到自己的策略库中。
然后在策略编辑页面中,模版栏中就可以勾选需要用的模版类库。勾选上之后保存策略,这个策略就引用这个模版了。这里只是简单说明模版类库的使用,本策略已经引用了这个模版所以不必重复操作。当你在策略广场复制了本策略之后在策略编辑页面的模版栏中就能看到画线类库
已经被引用上了。
我们主要来学习下怎么使用画线类库
的函数来画图。
我们是计划把A->B
的差价、B->A
的差价、差价触发线都画出来。需要画两条曲线(当前A到B,B到A的差价),两条水平线(触发差价线),如上图一般。
因为我们要设计上单边对冲,A->B
和B->A
的触发线就不一样了。不能用上篇文章中的设计了。
上篇文章中:
var targetDiffPrice = hedgeDiffPrice
if (diffAsPercentage) {
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
}
只有一个触发差价targetDiffPrice
。
所以这里我们要改造一下代码,首先改造参数。
然后修改代码:
var targetDiffPriceA2B = hedgeDiffPriceA2B
var targetDiffPriceB2A = hedgeDiffPriceB2A
if (diffAsPercentage) {
targetDiffPriceA2B = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageA2B
targetDiffPriceB2A = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentageB2A
}
这样差价触发线就从之前的targetDiffPrice
一条,变成了两条targetDiffPriceA2B
、targetDiffPriceB2A
。
接下来就可以使用画线类库的画线函数,在图表上画出这个数据了。
// 画图
$.PlotHLine(targetDiffPriceA2B, "A->B") // 该函数第一个参数是水平线在Y轴方向上的值,第二个参数是显示文本
$.PlotHLine(targetDiffPriceB2A, "B->A")
策略运行起来就有这样的图表显示了。
接下来画实时差价曲线,为了避免过度画线。把实时差价数据画曲线的代码放在平衡检测中。
if (ts - lastKeepBalanceTS > keepBalanceCyc * 1000) {
nowAccs = _C(updateAccs, exchanges)
var isBalance = keepBalance(initAccs, nowAccs, [depthA, depthB])
cancelAll()
if (isBalance) {
lastKeepBalanceTS = ts
if (isTrade) {
var nowBalance = _.reduce(nowAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
var initBalance = _.reduce(initAccs, function(sumBalance, acc) {return sumBalance + acc.Balance}, 0)
LogProfit(nowBalance - initBalance, nowBalance, initBalance, nowAccs)
isTrade = false
}
}
$.PlotLine("A2B", depthA.Bids[0].Price - depthB.Asks[0].Price) // 画实时差价曲线
$.PlotLine("B2A", depthB.Bids[0].Price - depthA.Asks[0].Price) // 第一个参数是曲线名称,第二个参数是曲线当前时刻的值,即当前时刻Y轴方向上的值
}
这样画图代码仅仅4行,就让策略在运行时有图表显示了。
上文中提到了差价触发线已经改造成两条,分别控制A->B
的对冲触发、B->A
的对冲触发。这样就不能使用之前的下单价格算法了,改用了盘口价格加滑价的方式。
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPriceA2B && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B 盘口条件满足
var priceSell = depthA.Bids[0].Price - slidePrice
var priceBuy = depthB.Asks[0].Price + slidePrice
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance * 0.8 / priceSell > minHedgeAmount) {
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance * 0.8 / priceSell, maxHedgeAmount)
Log("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[1].Balance * 0.8 / priceSell, nowAccs[0].Stocks) // 提示信息
hedge(exB, exA, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPriceB2A && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A 盘口条件满足
var priceBuy = depthA.Asks[0].Price + slidePrice
var priceSell = depthB.Bids[0].Price - slidePrice
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance * 0.8 / priceBuy > minHedgeAmount) {
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance * 0.8 / priceBuy, maxHedgeAmount)
Log("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, priceBuy, priceSell, amount, nowAccs[0].Balance * 0.8 / priceBuy, nowAccs[1].Stocks) // 提示信息
hedge(exA, exB, priceBuy, priceSell, amount)
cancelAll()
lastKeepBalanceTS = 0
isTrade = true
}
}
由于买卖价格分开为两个数据了,所以对于对冲函数hedge
也需要修改。
function hedge(buyEx, sellEx, priceBuy, priceSell, amount) {
var buyRoutine = buyEx.Go("Buy", priceBuy, amount)
var sellRoutine = sellEx.Go("Sell", priceSell, amount)
Sleep(500)
buyRoutine.wait()
sellRoutine.wait()
}
还有一些基于这些改动导致的细微调整,这里不在赘述,可以具体看代码。
给策略增加交互,让策略可以实时修改差价触发线。这个是一个半自动的策略的设计需求,这里也一并实现作为教学示范。 策略交互设计也非常简单,首先在策略编辑页面给策略增加交互控件。
添加了两个控件,一个叫A2B,一个叫B2A。当在控件输入框里输入数值后,点击输入框右侧的按钮。立即就会给策略发送一个指令,例如:输入框中输入数值123
,点击A2B
这个按钮,立即会向策略发送指令。
A2B:123
在策略代码中设计上交互检测、处理代码。
// 交互
var cmd = GetCommand() // 每次循环执行到这里时,都检测有没有交互指令过来,没有则返回空字符串
if (cmd) { // 检测到有交互指令,例如:A2B:123
Log("接收到命令:", cmd)
var arr = cmd.split(":") // 拆分出交互控件名称和输入框中的值,arr[0]就是A2B,arr[1]就是123
if (arr[0] == "A2B") { // 判断触发的交互控件是不是A2B
Log("修改A2B的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageB2A = parseFloat(arr[1]) // 修改触发差价线
} else {
hedgeDiffPriceA2B = parseFloat(arr[1]) // 修改触发差价线
}
} else if (arr[0] == "B2A") { // 检测到触发的控件是B2A
Log("修改B2A的参数,", diffAsPercentage ? "参数为差价百分比" : "参数为差价:", arr[1])
if (diffAsPercentage) {
hedgeDiffPercentageA2B = parseFloat(arr[1])
} else {
hedgeDiffPriceB2A = parseFloat(arr[1])
}
}
}
让状态栏数据显示的更加有条例、易于观察。
var tbl = {
"type" : "table",
"title" : "数据",
"cols" : ["交易所", "币", "冻结币", "计价币", "冻结计价币", "触发差价", "当前差价"],
"rows" : [],
}
tbl.rows.push(["A:" + exA.GetName(), nowAccs[0].Stocks, nowAccs[0].FrozenStocks, nowAccs[0].Balance, nowAccs[0].FrozenBalance, "A->B:" + targetDiffPriceA2B, "A->B:" + (depthA.Bids[0].Price - depthB.Asks[0].Price)])
tbl.rows.push(["B:" + exB.GetName(), nowAccs[1].Stocks, nowAccs[1].FrozenStocks, nowAccs[1].Balance, nowAccs[1].FrozenBalance, "B->A:" + targetDiffPriceB2A, "B->A:" + (depthB.Bids[0].Price - depthA.Asks[0].Price)])
LogStatus(_D(), "\n", "`" + JSON.stringify(tbl) + "`")
回测仅仅是测试策略,初步检测功能,很多BUG在回测阶段其实是可以测试出来的。不必要太在意回测结果,最终策略还是需要真枪实弹的在真实环境中检测的。
15570686905 这个交易策略加入 合约功能 就好了 就是把永续合约 交割合约 都加进去
轻轻的云 梦大,回测提示 main:127:9 - TypeError: Cannot read property 'SetPrecision' of undefined 【不同计价币的现货对冲策略 Ver1.1】
发明者量化-小小梦 好的,有机会出个教程。
轻轻的云 明白了,谢谢梦大。
发明者量化-小小梦 要添加两个交易所对象。