256 lines
9.2 KiB
Markdown
256 lines
9.2 KiB
Markdown
|
||
> Name
|
||
|
||
不同计价币的现货对冲策略教学
|
||
|
||
> Author
|
||
|
||
小小梦
|
||
|
||
> Strategy Description
|
||
|
||
## 不同计价币的现货对冲策略(教学)
|
||
|
||
相关文章:https://www.fmz.com/digest-topic/7593
|
||
|
||
> Strategy Arguments
|
||
|
||
|
||
|
||
|Argument|Default|Description|
|
||
|----|----|----|
|
||
|keepBalanceCyc|300|平衡周期|
|
||
|diffAsPercentage|true|使用差价百分比|
|
||
|hedgeDiffPrice|20|对冲差价|
|
||
|hedgeDiffPercentage|0.04|对冲差价百分比|
|
||
|minHedgeAmount|0.005|对冲最小下单量|
|
||
|maxHedgeAmount|0.2|对冲最大下单量|
|
||
|rateA|true|A交易所汇率|
|
||
|rateB|true|B交易所汇率|
|
||
|isReset|false|重置所有信息|
|
||
|pricePrecisionA|2|A价格精度|
|
||
|amountPrecisionA|3|A下单量精度|
|
||
|pricePrecisionB|2|B价格精度|
|
||
|amountPrecisionB|3|B下单量精度|
|
||
|
||
|
||
> Source (javascript)
|
||
|
||
``` javascript
|
||
// 全局变量
|
||
var lastKeepBalanceTS = 0
|
||
|
||
function hedge(buyEx, sellEx, price, amount) {
|
||
var buyRoutine = buyEx.Go("Buy", price, amount)
|
||
var sellRoutine = sellEx.Go("Sell", price, amount)
|
||
Sleep(500)
|
||
buyRoutine.wait()
|
||
sellRoutine.wait()
|
||
}
|
||
|
||
function getDepthPrice(depth, side, amount) {
|
||
var arr = depth[side]
|
||
var sum = 0
|
||
var price = null
|
||
for (var i = 0 ; i < arr.length ; i++) {
|
||
var ele = arr[i]
|
||
sum += ele.Amount
|
||
if (sum >= amount) {
|
||
price = ele.Price
|
||
break
|
||
}
|
||
}
|
||
return price
|
||
}
|
||
|
||
function keepBalance(initAccs, nowAccs, depths) {
|
||
var initSumStocks = 0
|
||
var nowSumStocks = 0
|
||
_.each(initAccs, function(acc) {
|
||
initSumStocks += acc.Stocks + acc.FrozenStocks
|
||
})
|
||
_.each(nowAccs, function(acc) {
|
||
nowSumStocks += acc.Stocks + acc.FrozenStocks
|
||
})
|
||
|
||
var diff = nowSumStocks - initSumStocks
|
||
// 计算币差
|
||
if (Math.abs(diff) > minHedgeAmount && initAccs.length == nowAccs.length && nowAccs.length == depths.length) {
|
||
Log("触发平衡操作,平衡量:", Math.abs(diff))
|
||
var index = -1
|
||
var available = []
|
||
var side = diff > 0 ? "Bids" : "Asks"
|
||
for (var i = 0 ; i < nowAccs.length ; i++) {
|
||
var price = getDepthPrice(depths[i], side, Math.abs(diff))
|
||
if (side == "Bids" && nowAccs[i].Stocks > Math.abs(diff)) {
|
||
available.push(i)
|
||
} else if (price && nowAccs[i].Balance / price > Math.abs(diff)) {
|
||
available.push(i)
|
||
}
|
||
}
|
||
for (var i = 0 ; i < available.length ; i++) {
|
||
if (index == -1) {
|
||
index = available[i]
|
||
} else {
|
||
var priceIndex = getDepthPrice(depths[index], side, Math.abs(diff))
|
||
var priceI = getDepthPrice(depths[available[i]], side, Math.abs(diff))
|
||
if (side == "Bids" && priceIndex && priceI && priceI > priceIndex) {
|
||
index = available[i]
|
||
} else if (priceIndex && priceI && priceI < priceIndex) {
|
||
index = available[i]
|
||
}
|
||
}
|
||
}
|
||
if (index == -1) {
|
||
Log("无法平衡")
|
||
} else {
|
||
// 平衡下单
|
||
var price = getDepthPrice(depths[index], side, Math.abs(diff))
|
||
if (price) {
|
||
var tradeFunc = side == "Bids" ? exchanges[index].Sell : exchanges[index].Buy
|
||
tradeFunc(price, Math.abs(diff))
|
||
} else {
|
||
Log("价格无效", price)
|
||
}
|
||
}
|
||
return false
|
||
} else if (!(initAccs.length == nowAccs.length && nowAccs.length == depths.length)) {
|
||
Log("错误:", "initAccs.length:", initAccs.length, "nowAccs.length:", nowAccs.length, "depths.length:", depths.length)
|
||
return true
|
||
} else {
|
||
return true
|
||
}
|
||
}
|
||
|
||
function cancelAll() {
|
||
_.each(exchanges, function(ex) {
|
||
while (true) {
|
||
var orders = _C(ex.GetOrders)
|
||
if (orders.length == 0) {
|
||
break
|
||
}
|
||
for (var i = 0 ; i < orders.length ; i++) {
|
||
ex.CancelOrder(orders[i].Id, orders[i])
|
||
Sleep(500)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
function updateAccs(arrEx) {
|
||
var ret = []
|
||
for (var i = 0 ; i < arrEx.length ; i++) {
|
||
var acc = arrEx[i].GetAccount()
|
||
if (!acc) {
|
||
return null
|
||
}
|
||
ret.push(acc)
|
||
}
|
||
return ret
|
||
}
|
||
|
||
function main() {
|
||
var exA = exchanges[0]
|
||
var exB = exchanges[1]
|
||
// 精度,汇率设置
|
||
if (rateA != 1) {
|
||
// 设置汇率A
|
||
exA.SetRate(rateA)
|
||
Log("交易所A设置汇率:", rateA, "#FF0000")
|
||
}
|
||
if (rateB != 1) {
|
||
// 设置汇率B
|
||
exB.SetRate(rateB)
|
||
Log("交易所B设置汇率:", rateB, "#FF0000")
|
||
}
|
||
exA.SetPrecision(pricePrecisionA, amountPrecisionA)
|
||
exB.SetPrecision(pricePrecisionB, amountPrecisionB)
|
||
|
||
if (isReset) {
|
||
_G(null)
|
||
LogReset(1)
|
||
LogProfitReset()
|
||
LogVacuum()
|
||
Log("重置所有数据", "#FF0000")
|
||
}
|
||
|
||
var nowAccs = _C(updateAccs, exchanges)
|
||
var initAccs = _G("initAccs")
|
||
if (!initAccs) {
|
||
initAccs = nowAccs
|
||
_G("initAccs", initAccs)
|
||
}
|
||
|
||
var isTrade = false
|
||
while (true) {
|
||
var ts = new Date().getTime()
|
||
var depthARoutine = exA.Go("GetDepth")
|
||
var depthBRoutine = exB.Go("GetDepth")
|
||
var depthA = depthARoutine.wait()
|
||
var depthB = depthBRoutine.wait()
|
||
if (!depthA || !depthB || depthA.Asks.length == 0 || depthA.Bids.length == 0 || depthB.Asks.length == 0 || depthB.Bids.length == 0) {
|
||
Sleep(500)
|
||
continue
|
||
}
|
||
|
||
var targetDiffPrice = hedgeDiffPrice
|
||
if (diffAsPercentage) {
|
||
targetDiffPrice = (depthA.Bids[0].Price + depthB.Asks[0].Price + depthB.Bids[0].Price + depthA.Asks[0].Price) / 4 * hedgeDiffPercentage
|
||
}
|
||
|
||
if (depthA.Bids[0].Price - depthB.Asks[0].Price > targetDiffPrice && Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount) >= minHedgeAmount) { // A -> B 盘口条件满足
|
||
var price = (depthA.Bids[0].Price + depthB.Asks[0].Price) / 2
|
||
var amount = Math.min(depthA.Bids[0].Amount, depthB.Asks[0].Amount)
|
||
if (nowAccs[0].Stocks > minHedgeAmount && nowAccs[1].Balance / price > minHedgeAmount) {
|
||
amount = Math.min(amount, nowAccs[0].Stocks, nowAccs[1].Balance / price, maxHedgeAmount)
|
||
Log("触发A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, price, amount, nowAccs[1].Balance / price, nowAccs[0].Stocks) // 提示信息
|
||
hedge(exB, exA, price, amount)
|
||
cancelAll()
|
||
lastKeepBalanceTS = 0
|
||
isTrade = true
|
||
}
|
||
} else if (depthB.Bids[0].Price - depthA.Asks[0].Price > targetDiffPrice && Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount) >= minHedgeAmount) { // B -> A 盘口条件满足
|
||
var price = (depthB.Bids[0].Price + depthA.Asks[0].Price) / 2
|
||
var amount = Math.min(depthB.Bids[0].Amount, depthA.Asks[0].Amount)
|
||
if (nowAccs[1].Stocks > minHedgeAmount && nowAccs[0].Balance / price > minHedgeAmount) {
|
||
amount = Math.min(amount, nowAccs[1].Stocks, nowAccs[0].Balance / price, maxHedgeAmount)
|
||
Log("触发B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, price, amount, nowAccs[0].Balance / price, nowAccs[1].Stocks) // 提示信息
|
||
hedge(exA, exB, price, amount)
|
||
cancelAll()
|
||
lastKeepBalanceTS = 0
|
||
isTrade = true
|
||
}
|
||
}
|
||
|
||
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
|
||
}
|
||
}
|
||
}
|
||
LogStatus(_D(), "A->B:", depthA.Bids[0].Price - depthB.Asks[0].Price, " B->A:", depthB.Bids[0].Price - depthA.Asks[0].Price, " targetDiffPrice:", targetDiffPrice, "\n",
|
||
"当前A,Stocks:", nowAccs[0].Stocks, "FrozenStocks:", nowAccs[0].FrozenStocks, "Balance:", nowAccs[0].Balance, "FrozenBalance", nowAccs[0].FrozenBalance, "\n",
|
||
"当前B,Stocks:", nowAccs[1].Stocks, "FrozenStocks:", nowAccs[1].FrozenStocks, "Balance:", nowAccs[1].Balance, "FrozenBalance", nowAccs[1].FrozenBalance, "\n",
|
||
"初始A,Stocks:", initAccs[0].Stocks, "FrozenStocks:", initAccs[0].FrozenStocks, "Balance:", initAccs[0].Balance, "FrozenBalance", initAccs[0].FrozenBalance, "\n",
|
||
"初始B,Stocks:", initAccs[1].Stocks, "FrozenStocks:", initAccs[1].FrozenStocks, "Balance:", initAccs[1].Balance, "FrozenBalance", initAccs[1].FrozenBalance)
|
||
Sleep(1000)
|
||
}
|
||
}
|
||
```
|
||
|
||
> Detail
|
||
|
||
https://www.fmz.com/strategy/298752
|
||
|
||
> Last Modified
|
||
|
||
2021-07-30 16:54:39
|