638 lines
26 KiB
Markdown
638 lines
26 KiB
Markdown
|
|||
|
|
> 策略名称
|
||
|
|
|
||
|
|
多品种期现对冲 (手动版)
|
||
|
|
|
||
|
|
> 策略作者
|
||
|
|
|
||
|
|
小小梦
|
||
|
|
|
||
|
|
> 策略描述
|
||
|
|
|
||
|
|
## 多品种期现对冲 (手动版)
|
||
|
|
|
||
|
|
相关文章:https://www.fmz.com/bbs-topic/7932
|
||
|
|
|
||
|
|
目前为测试版,策略交流学习为主,实盘自负盈亏。
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
> 策略参数
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
|参数|默认值|描述|
|
||
|
|
|----|----|----|
|
||
|
|
|isReset|false|是否重置|
|
||
|
|
|interval|8000|轮询间隔|
|
||
|
|
|symbolPairs|LTC-USDT-211231|LTC_USDT,BTC-USDT-211231|BTC_USDT|对冲控制表|
|
||
|
|
|cmdHedgeAmount|0.1|交互控件对冲的对冲量|
|
||
|
|
|fuMarginLevel|5|期货杠杆|
|
||
|
|
|fuMarginReservedRatio|0.5|期货保证金预留比例|
|
||
|
|
|isOkexV5|true|是否使用OKEX V5接口|
|
||
|
|
|isOKEX_V5_Simulate|false|切换OKEX交易所V5模拟盘|
|
||
|
|
|
||
|
|
|
||
|
|
> 源码 (javascript)
|
||
|
|
|
||
|
|
``` javascript
|
||
|
|
function createManager(fuEx, spEx, symbolPairs, cmdHedgeAmount, fuMarginLevel, fuMarginReservedRatio) {
|
||
|
|
var self = {}
|
||
|
|
self.fuEx = fuEx
|
||
|
|
self.spEx = spEx
|
||
|
|
self.symbolPairs = symbolPairs
|
||
|
|
self.pairs = []
|
||
|
|
self.fuExTickers = null
|
||
|
|
self.spExTickers = null
|
||
|
|
self.tickerUpdateTS = 0
|
||
|
|
self.fuMarginLevel = fuMarginLevel
|
||
|
|
self.fuMarginReservedRatio = fuMarginReservedRatio
|
||
|
|
self.cmdHedgeAmount = cmdHedgeAmount
|
||
|
|
self.preUpdateAccTS = 0
|
||
|
|
self.accAndPosUpdateCount = 0
|
||
|
|
self.profit = []
|
||
|
|
self.allPairs = []
|
||
|
|
|
||
|
|
self.PLUS = 0
|
||
|
|
self.MINUS = 1
|
||
|
|
self.COVER_PLUS = 2
|
||
|
|
self.COVER_MINUS = 3
|
||
|
|
self.arrTradeTypeDesc = ["正套", "反套", "平正套", "平反套"]
|
||
|
|
|
||
|
|
self.updateTickers = function() {
|
||
|
|
self.fuEx.goGetTickers()
|
||
|
|
self.spEx.goGetTickers()
|
||
|
|
var fuExTickers = self.fuEx.getTickers()
|
||
|
|
var spExTickers = self.spEx.getTickers()
|
||
|
|
|
||
|
|
if (!fuExTickers || !spExTickers) {
|
||
|
|
return null
|
||
|
|
}
|
||
|
|
self.fuExTickers = fuExTickers
|
||
|
|
self.spExTickers = spExTickers
|
||
|
|
self.tickerUpdateTS = new Date().getTime()
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
|
||
|
|
self.hedge = function(index, fuSymbol, spSymbol, tradeType, amount) {
|
||
|
|
var fe = self.fuEx
|
||
|
|
var se = self.spEx
|
||
|
|
var pair = self.pairs[index]
|
||
|
|
var timeStamp = new Date().getTime()
|
||
|
|
|
||
|
|
var fuDirection = null
|
||
|
|
var spDirection = null
|
||
|
|
var fuPrice = null
|
||
|
|
var spPrice = null
|
||
|
|
|
||
|
|
if (tradeType == self.PLUS) {
|
||
|
|
fuDirection = fe.OPEN_SHORT
|
||
|
|
spDirection = se.OPEN_LONG
|
||
|
|
fuPrice = pair.fuTicker.bid1
|
||
|
|
spPrice = pair.spTicker.ask1
|
||
|
|
} else if (tradeType == self.MINUS) {
|
||
|
|
fuDirection = fe.OPEN_LONG
|
||
|
|
spDirection = se.OPEN_SHORT
|
||
|
|
fuPrice = pair.fuTicker.ask1
|
||
|
|
spPrice = pair.spTicker.bid1
|
||
|
|
} else if (tradeType == self.COVER_PLUS) {
|
||
|
|
fuDirection = fe.COVER_SHORT
|
||
|
|
spDirection = se.COVER_LONG
|
||
|
|
fuPrice = pair.fuTicker.ask1
|
||
|
|
spPrice = pair.spTicker.bid1
|
||
|
|
} else if (tradeType == self.COVER_MINUS) {
|
||
|
|
fuDirection = fe.COVER_LONG
|
||
|
|
spDirection = se.COVER_SHORT
|
||
|
|
fuPrice = pair.fuTicker.bid1
|
||
|
|
spPrice = pair.spTicker.ask1
|
||
|
|
} else {
|
||
|
|
throw "unknow tradeType!"
|
||
|
|
}
|
||
|
|
|
||
|
|
fe.goGetAcc(fuSymbol, timeStamp)
|
||
|
|
se.goGetAcc(spSymbol, timeStamp)
|
||
|
|
var nowFuAcc = fe.getAcc(fuSymbol, timeStamp)
|
||
|
|
var nowSpAcc = se.getAcc(spSymbol, timeStamp)
|
||
|
|
if (!nowFuAcc || !nowSpAcc) {
|
||
|
|
Log(fuSymbol, spSymbol, ",获取账户数据失败")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
pair.nowFuAcc = nowFuAcc
|
||
|
|
pair.nowSpAcc = nowSpAcc
|
||
|
|
|
||
|
|
var nowFuPos = fe.getFuPos(fuSymbol, timeStamp)
|
||
|
|
var nowSpPos = se.getSpPos(spSymbol, spPrice, pair.initSpAcc, pair.nowSpAcc)
|
||
|
|
if (!nowFuPos || !nowSpPos) {
|
||
|
|
Log(fuSymbol, spSymbol, ",获取持仓数据失败")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
pair.nowFuPos = nowFuPos
|
||
|
|
pair.nowSpPos = nowSpPos
|
||
|
|
|
||
|
|
var fuAmount = amount
|
||
|
|
var spAmount = amount
|
||
|
|
if (tradeType == self.PLUS || tradeType == self.MINUS) {
|
||
|
|
if (nowFuAcc.Balance < (pair.initFuAcc.Balance + pair.initFuAcc.FrozenBalance) * self.fuMarginReservedRatio + (fuAmount * fuPrice / self.fuMarginLevel)) {
|
||
|
|
Log(pair.fuSymbol, "保证金不足!", "本次计划使用", (fuAmount * fuPrice / self.fuMarginLevel), "当前可用:", nowFuAcc.Balance,
|
||
|
|
"计划预留:", (pair.initFuAcc.Balance + pair.initFuAcc.FrozenBalance) * self.fuMarginReservedRatio)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if ((tradeType == self.PLUS && nowSpAcc.Balance < spAmount * spPrice)) {
|
||
|
|
Log(pair.spSymbol, "资金不足!", "本次买入计划使用", spAmount * spPrice, "当前可用:", nowSpAcc.Balance)
|
||
|
|
return
|
||
|
|
} else if (tradeType == self.MINUS && nowSpAcc.Stocks < spAmount) {
|
||
|
|
Log(pair.spSymbol, "资金不足!", "本次卖出计划使用", spAmount, "当前可用:", nowSpAcc.Stocks)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
var fuLongPos = self.getLongPos(nowFuPos)
|
||
|
|
var fuShortPos = self.getShortPos(nowFuPos)
|
||
|
|
var spLongPos = self.getLongPos(nowSpPos)
|
||
|
|
var spShortPos = self.getShortPos(nowSpPos)
|
||
|
|
if ((tradeType == self.COVER_PLUS && !fuShortPos) || (tradeType == self.COVER_MINUS && !fuLongPos)) {
|
||
|
|
Log(fuSymbol, spSymbol, ",期货没有对应持仓!")
|
||
|
|
return
|
||
|
|
} else if (tradeType == self.COVER_PLUS && Math.abs(fuShortPos.amount) < fuAmount) {
|
||
|
|
fuAmount = Math.abs(fuShortPos.amount)
|
||
|
|
} else if (tradeType == self.COVER_MINUS && Math.abs(fuLongPos.amount) < fuAmount) {
|
||
|
|
fuAmount = Math.abs(fuLongPos.amount)
|
||
|
|
}
|
||
|
|
if ((tradeType == self.COVER_PLUS && !spLongPos) || (tradeType == self.COVER_MINUS && !spShortPos)) {
|
||
|
|
Log(fuSymbol, spSymbol, ",现货没有对应持仓!")
|
||
|
|
return
|
||
|
|
} else if (tradeType == self.COVER_PLUS && Math.min(Math.abs(spLongPos.amount), nowSpAcc.Stocks) < spAmount) {
|
||
|
|
spAmount = Math.min(Math.abs(spLongPos.amount), nowSpAcc.Stocks)
|
||
|
|
} else if (tradeType == self.COVER_MINUS && Math.min(Math.abs(spShortPos.amount), nowSpAcc.Balance / spPrice) < spAmount) {
|
||
|
|
spAmount = Math.min(Math.abs(spShortPos.amount), nowSpAcc.Balance / spPrice)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
fuAmount = fe.calcAmount(fuSymbol, fuDirection, fuPrice, fuAmount)
|
||
|
|
spAmount = se.calcAmount(spSymbol, spDirection, spPrice, spAmount)
|
||
|
|
if (!fuAmount || !spAmount) {
|
||
|
|
Log(fuSymbol, spSymbol, "下单量计算错误:", fuAmount, spAmount)
|
||
|
|
return
|
||
|
|
} else {
|
||
|
|
fuAmount = fe.calcAmount(fuSymbol, fuDirection, fuPrice, fuAmount[1])
|
||
|
|
spAmount = se.calcAmount(spSymbol, spDirection, spPrice, Math.min(fuAmount[1], spAmount[1]))
|
||
|
|
if (!fuAmount || !spAmount) {
|
||
|
|
Log(fuSymbol, spSymbol, "下单量计算错误:", fuAmount, spAmount)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Log("合约代码:", fuSymbol + "/" + spSymbol, "方向:", self.arrTradeTypeDesc[tradeType], "差价:", fuPrice - spPrice, "期货数量:", fuAmount, "现货数量:", spAmount, "@")
|
||
|
|
|
||
|
|
fe.goGetTrade(fuSymbol, fuDirection, fuPrice, fuAmount[0])
|
||
|
|
se.goGetTrade(spSymbol, spDirection, spPrice, spAmount[0])
|
||
|
|
|
||
|
|
var feIdMsg = fe.getTrade()
|
||
|
|
var seIdMsg = se.getTrade()
|
||
|
|
return [feIdMsg, seIdMsg]
|
||
|
|
}
|
||
|
|
|
||
|
|
self.process = function() {
|
||
|
|
var nowTS = new Date().getTime()
|
||
|
|
if(!self.updateTickers()) {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
_.each(self.pairs, function(pair, index) {
|
||
|
|
var fuTicker = null
|
||
|
|
var spTicker = null
|
||
|
|
_.each(self.fuExTickers, function(ticker) {
|
||
|
|
if (ticker.originalSymbol == pair.fuSymbol) {
|
||
|
|
fuTicker = ticker
|
||
|
|
}
|
||
|
|
})
|
||
|
|
_.each(self.spExTickers, function(ticker) {
|
||
|
|
if (ticker.originalSymbol == pair.spSymbol) {
|
||
|
|
spTicker = ticker
|
||
|
|
}
|
||
|
|
})
|
||
|
|
if (fuTicker && spTicker) {
|
||
|
|
pair.canTrade = true
|
||
|
|
} else {
|
||
|
|
pair.canTrade = false
|
||
|
|
}
|
||
|
|
fuTicker = fuTicker ? fuTicker : {}
|
||
|
|
spTicker = spTicker ? spTicker : {}
|
||
|
|
pair.fuTicker = fuTicker
|
||
|
|
pair.spTicker = spTicker
|
||
|
|
pair.plusDiff = fuTicker.bid1 - spTicker.ask1
|
||
|
|
pair.minusDiff = fuTicker.ask1 - spTicker.bid1
|
||
|
|
if (pair.plusDiff && pair.minusDiff) {
|
||
|
|
pair.plusDiff = _N(pair.plusDiff, Math.max(self.fuEx.judgePrecision(fuTicker.bid1), self.spEx.judgePrecision(spTicker.ask1)))
|
||
|
|
pair.minusDiff = _N(pair.minusDiff, Math.max(self.fuEx.judgePrecision(fuTicker.ask1), self.spEx.judgePrecision(spTicker.bid1)))
|
||
|
|
}
|
||
|
|
|
||
|
|
if (nowTS - self.preUpdateAccTS > 1000 * 60 * 5) {
|
||
|
|
self.fuEx.goGetAcc(pair.fuSymbol, nowTS)
|
||
|
|
self.spEx.goGetAcc(pair.spSymbol, nowTS)
|
||
|
|
var fuAcc = self.fuEx.getAcc(pair.fuSymbol, nowTS)
|
||
|
|
var spAcc = self.spEx.getAcc(pair.spSymbol, nowTS)
|
||
|
|
if (fuAcc) {
|
||
|
|
pair.nowFuAcc = fuAcc
|
||
|
|
}
|
||
|
|
if (spAcc) {
|
||
|
|
pair.nowSpAcc = spAcc
|
||
|
|
}
|
||
|
|
var nowFuPos = self.fuEx.getFuPos(pair.fuSymbol, nowTS)
|
||
|
|
var nowSpPos = self.spEx.getSpPos(pair.spSymbol, (pair.spTicker.ask1 + pair.spTicker.bid1) / 2, pair.initSpAcc, pair.nowSpAcc)
|
||
|
|
|
||
|
|
if (nowFuPos && nowSpPos) {
|
||
|
|
pair.nowFuPos = nowFuPos
|
||
|
|
pair.nowSpPos = nowSpPos
|
||
|
|
self.keepBalance(pair)
|
||
|
|
} else {
|
||
|
|
Log(pair.fuSymbol, pair.spSymbol, "组合仓位更新失败,nowFuPos:", nowFuPos, " nowSpPos:", nowSpPos)
|
||
|
|
}
|
||
|
|
self.accAndPosUpdateCount++
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if (nowTS - self.preUpdateAccTS > 1000 * 60 * 5) {
|
||
|
|
self.preUpdateAccTS = nowTS
|
||
|
|
self.profit = self.calcProfit()
|
||
|
|
LogProfit(self.profit[0], "期货:", self.profit[1], "现货:", self.profit[2], "&") // 打印总收益曲线,使用&字符不打印收益日志
|
||
|
|
}
|
||
|
|
|
||
|
|
var cmd = GetCommand()
|
||
|
|
if(cmd) {
|
||
|
|
Log("交互命令:", cmd)
|
||
|
|
var arr = cmd.split(":")
|
||
|
|
if(arr[0] == "plus") {
|
||
|
|
var pair = self.pairs[parseFloat(arr[1])]
|
||
|
|
self.hedge(parseFloat(arr[1]), pair.fuSymbol, pair.spSymbol, self.PLUS, self.cmdHedgeAmount)
|
||
|
|
} else if (arr[0] == "cover_plus") {
|
||
|
|
var pair = self.pairs[parseFloat(arr[1])]
|
||
|
|
self.hedge(parseFloat(arr[1]), pair.fuSymbol, pair.spSymbol, self.COVER_PLUS, self.cmdHedgeAmount)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
LogStatus("当前时间:", _D(), " 数据更新时间:", _D(self.tickerUpdateTS), "持仓账户更新计数:", self.accAndPosUpdateCount, "\n", "盈亏:", self.profit[0], " 期货盈亏:", self.profit[1],
|
||
|
|
" 现货盈亏:", self.profit[2], "\n`" + JSON.stringify(self.returnTbl()) + "`", "\n`" + JSON.stringify(self.returnPosTbl()) + "`")
|
||
|
|
}
|
||
|
|
|
||
|
|
self.keepBalance = function (pair) {
|
||
|
|
var nowFuPos = pair.nowFuPos
|
||
|
|
var nowSpPos = pair.nowSpPos
|
||
|
|
var fuLongPos = self.getLongPos(nowFuPos)
|
||
|
|
var fuShortPos = self.getShortPos(nowFuPos)
|
||
|
|
var spLongPos = self.getLongPos(nowSpPos)
|
||
|
|
var spShortPos = self.getShortPos(nowSpPos)
|
||
|
|
|
||
|
|
if (fuLongPos || spShortPos) {
|
||
|
|
Log("不支持反套")
|
||
|
|
}
|
||
|
|
if (fuShortPos || spLongPos) {
|
||
|
|
var fuHoldAmount = fuShortPos ? fuShortPos.amount : 0
|
||
|
|
var spHoldAmount = spLongPos ? spLongPos.amount : 0
|
||
|
|
var sum = fuHoldAmount + spHoldAmount
|
||
|
|
if (sum > 0) {
|
||
|
|
var spAmount = self.spEx.calcAmount(pair.spSymbol, self.spEx.COVER_LONG, pair.spTicker.bid1, Math.abs(sum), true)
|
||
|
|
if (spAmount) {
|
||
|
|
Log(pair.fuSymbol, pair.spSymbol, "现货头寸多出", Math.abs(sum), "fuShortPos:", fuShortPos, "spLongPos:", spLongPos)
|
||
|
|
self.spEx.goGetTrade(pair.spSymbol, self.spEx.COVER_LONG, pair.spTicker.bid1, spAmount[0])
|
||
|
|
var seIdMsg = self.spEx.getTrade()
|
||
|
|
}
|
||
|
|
} else if (sum < 0) {
|
||
|
|
var fuAmount = self.fuEx.calcAmount(pair.fuSymbol, self.fuEx.COVER_SHORT, pair.fuTicker.ask1, Math.abs(sum), true)
|
||
|
|
if (fuAmount) {
|
||
|
|
Log(pair.fuSymbol, pair.spSymbol, "期货头寸多出", Math.abs(sum), "fuShortPos:", fuShortPos, "spLongPos:", spLongPos)
|
||
|
|
self.fuEx.goGetTrade(pair.fuSymbol, self.fuEx.COVER_SHORT, pair.fuTicker.ask1, fuAmount[0])
|
||
|
|
var feIdMsg = self.fuEx.getTrade()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
self.getLongPos = function (positions) {
|
||
|
|
return self.getPosByDirection(positions, PD_LONG)
|
||
|
|
}
|
||
|
|
|
||
|
|
self.getShortPos = function (positions) {
|
||
|
|
return self.getPosByDirection(positions, PD_SHORT)
|
||
|
|
}
|
||
|
|
|
||
|
|
self.getPosByDirection = function (positions, direction) {
|
||
|
|
var ret = null
|
||
|
|
if (positions.length > 2) {
|
||
|
|
Log("持仓错误,检测到三个持仓:", JSON.stringify(positions))
|
||
|
|
return ret
|
||
|
|
}
|
||
|
|
_.each(positions, function(pos) {
|
||
|
|
if ((direction == PD_LONG && pos.amount > 0) || (direction == PD_SHORT && pos.amount < 0)) {
|
||
|
|
ret = pos
|
||
|
|
}
|
||
|
|
})
|
||
|
|
return ret
|
||
|
|
}
|
||
|
|
|
||
|
|
self.calcProfit = function() {
|
||
|
|
var arrInitFuAcc = []
|
||
|
|
var arrNowFuAcc = []
|
||
|
|
_.each(self.pairs, function(pair) {
|
||
|
|
arrInitFuAcc.push(pair.initFuAcc)
|
||
|
|
arrNowFuAcc.push(pair.nowFuAcc)
|
||
|
|
})
|
||
|
|
var fuProfit = self.fuEx.calcProfit(arrInitFuAcc, arrNowFuAcc)
|
||
|
|
var spProfit = 0
|
||
|
|
var deltaBalance = 0
|
||
|
|
_.each(self.pairs, function(pair) {
|
||
|
|
var nowSpAcc = pair.nowSpAcc
|
||
|
|
var initSpAcc = pair.initSpAcc
|
||
|
|
var stocksDiff = nowSpAcc.Stocks + nowSpAcc.FrozenStocks - (initSpAcc.Stocks + initSpAcc.FrozenStocks)
|
||
|
|
var price = stocksDiff > 0 ? pair.spTicker.bid1 : pair.spTicker.ask1
|
||
|
|
spProfit += stocksDiff * price
|
||
|
|
deltaBalance = nowSpAcc.Balance + nowSpAcc.FrozenBalance - (initSpAcc.Balance + initSpAcc.FrozenBalance)
|
||
|
|
})
|
||
|
|
spProfit += deltaBalance
|
||
|
|
return [fuProfit + spProfit, fuProfit, spProfit]
|
||
|
|
}
|
||
|
|
|
||
|
|
self.returnPosTbl = function() {
|
||
|
|
var posTbl = {
|
||
|
|
type : "table",
|
||
|
|
title : "positions",
|
||
|
|
cols : ["索引", "期货", "期货杠杆", "数量", "现货", "数量"],
|
||
|
|
rows : []
|
||
|
|
}
|
||
|
|
_.each(self.pairs, function(pair, index) {
|
||
|
|
var nowFuPos = pair.nowFuPos
|
||
|
|
var nowSpPos = pair.nowSpPos
|
||
|
|
for (var i = 0 ; i < nowFuPos.length ; i++) {
|
||
|
|
if (nowSpPos.length > 0) {
|
||
|
|
posTbl.rows.push([index, nowFuPos[i].symbol, nowFuPos[i].marginLevel, nowFuPos[i].amount, nowSpPos[0].symbol, nowSpPos[0].amount])
|
||
|
|
} else {
|
||
|
|
posTbl.rows.push([index, nowFuPos[i].symbol, nowFuPos[i].marginLevel, nowFuPos[i].amount, "--", "--"])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
return posTbl
|
||
|
|
}
|
||
|
|
|
||
|
|
self.returnTbl = function() {
|
||
|
|
var fuExName = "[" + self.fuEx.getExName() + "]"
|
||
|
|
var spExName = "[" + self.spEx.getExName() + "]"
|
||
|
|
var combiTickersTbl = {
|
||
|
|
type : "table",
|
||
|
|
title : "combiTickersTbl",
|
||
|
|
cols : ["期货", "代码" + fuExName, "卖一", "买一", "现货", "代码" + spExName, "卖一", "买一", "正对冲差价", "反对冲差价", "正对冲", "正对冲平仓"],
|
||
|
|
rows : []
|
||
|
|
}
|
||
|
|
_.each(self.pairs, function(pair, index) {
|
||
|
|
var spSymbolInfo = self.spEx.getSymbolInfo(pair.spTicker.originalSymbol)
|
||
|
|
combiTickersTbl.rows.push([
|
||
|
|
pair.fuTicker.symbol,
|
||
|
|
pair.fuTicker.originalSymbol,
|
||
|
|
pair.fuTicker.ask1,
|
||
|
|
pair.fuTicker.bid1,
|
||
|
|
pair.spTicker.symbol,
|
||
|
|
pair.spTicker.originalSymbol,
|
||
|
|
pair.spTicker.ask1,
|
||
|
|
pair.spTicker.bid1,
|
||
|
|
pair.plusDiff,
|
||
|
|
pair.minusDiff,
|
||
|
|
{'type':'button', 'cmd': 'plus:' + String(index), 'name': '正套'},
|
||
|
|
{'type':'button', 'cmd': 'cover_plus:' + String(index), 'name': '平正套'}
|
||
|
|
])
|
||
|
|
})
|
||
|
|
|
||
|
|
var accsTbl = {
|
||
|
|
type : "table",
|
||
|
|
title : "accs",
|
||
|
|
cols : ["代码" + fuExName, "初币", "初冻币", "初钱", "初冻钱", "币", "冻币", "钱", "冻钱",
|
||
|
|
"代码" + spExName, "初币", "初冻币", "初钱", "初冻钱", "币", "冻币", "钱", "冻钱"],
|
||
|
|
rows : []
|
||
|
|
}
|
||
|
|
_.each(self.pairs, function(pair) {
|
||
|
|
var arr = [pair.fuTicker.originalSymbol, pair.initFuAcc.Stocks, pair.initFuAcc.FrozenStocks, pair.initFuAcc.Balance, pair.initFuAcc.FrozenBalance, pair.nowFuAcc.Stocks, pair.nowFuAcc.FrozenStocks, pair.nowFuAcc.Balance, pair.nowFuAcc.FrozenBalance,
|
||
|
|
pair.spTicker.originalSymbol, pair.initSpAcc.Stocks, pair.initSpAcc.FrozenStocks, pair.initSpAcc.Balance, pair.initSpAcc.FrozenBalance, pair.nowSpAcc.Stocks, pair.nowSpAcc.FrozenStocks, pair.nowSpAcc.Balance, pair.nowSpAcc.FrozenBalance]
|
||
|
|
for (var i = 0 ; i < arr.length ; i++) {
|
||
|
|
if (typeof(arr[i]) == "number") {
|
||
|
|
arr[i] = _N(arr[i], 6)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
accsTbl.rows.push(arr)
|
||
|
|
})
|
||
|
|
|
||
|
|
var symbolInfoTbl = {
|
||
|
|
type : "table",
|
||
|
|
title : "symbolInfos",
|
||
|
|
cols : ["合约代码" + fuExName, "量精度", "价格精度", "乘数", "最小下单量", "现货代码" + spExName, "量精度", "价格精度", "乘数", "最小下单量"],
|
||
|
|
rows : []
|
||
|
|
}
|
||
|
|
_.each(self.pairs, function(pair) {
|
||
|
|
var fuSymbolInfo = self.fuEx.getSymbolInfo(pair.fuTicker.originalSymbol)
|
||
|
|
var spSymbolInfo = self.spEx.getSymbolInfo(pair.spTicker.originalSymbol)
|
||
|
|
symbolInfoTbl.rows.push([fuSymbolInfo.symbol, fuSymbolInfo.amountPrecision, fuSymbolInfo.pricePrecision, fuSymbolInfo.multiplier, fuSymbolInfo.min,
|
||
|
|
spSymbolInfo.symbol, spSymbolInfo.amountPrecision, spSymbolInfo.pricePrecision, spSymbolInfo.multiplier, spSymbolInfo.min])
|
||
|
|
})
|
||
|
|
|
||
|
|
var allPairs = []
|
||
|
|
_.each(self.fuExTickers, function(fuTicker) {
|
||
|
|
_.each(self.spExTickers, function(spTicker) {
|
||
|
|
if (fuTicker.symbol == spTicker.symbol) {
|
||
|
|
allPairs.push({symbol: fuTicker.symbol, fuSymbol: fuTicker.originalSymbol, spSymbol: spTicker.originalSymbol, plus: fuTicker.bid1 - spTicker.ask1})
|
||
|
|
}
|
||
|
|
})
|
||
|
|
})
|
||
|
|
_.each(allPairs, function(pair) {
|
||
|
|
var findPair = null
|
||
|
|
_.each(self.allPairs, function(selfPair) {
|
||
|
|
if (pair.fuSymbol == selfPair.fuSymbol && pair.spSymbol == selfPair.spSymbol) {
|
||
|
|
findPair = selfPair
|
||
|
|
}
|
||
|
|
})
|
||
|
|
if (findPair) {
|
||
|
|
findPair.minPlus = pair.plus < findPair.minPlus ? pair.plus : findPair.minPlus
|
||
|
|
findPair.maxPlus = pair.plus > findPair.maxPlus ? pair.plus : findPair.maxPlus
|
||
|
|
pair.minPlus = findPair.minPlus
|
||
|
|
pair.maxPlus = findPair.maxPlus
|
||
|
|
} else {
|
||
|
|
self.allPairs.push({symbol: pair.symbol, fuSymbol: pair.fuSymbol, spSymbol: pair.spSymbol, plus: pair.plus, minPlus: pair.plus, maxPlus: pair.plus})
|
||
|
|
pair.minPlus = pair.plus
|
||
|
|
pair.maxPlus = pair.plus
|
||
|
|
}
|
||
|
|
})
|
||
|
|
return [combiTickersTbl, accsTbl, symbolInfoTbl]
|
||
|
|
}
|
||
|
|
|
||
|
|
self.onexit = function() {
|
||
|
|
_G("pairs", self.pairs)
|
||
|
|
_G("allPairs", self.allPairs)
|
||
|
|
Log("执行扫尾处理,数据保存", "#FF0000")
|
||
|
|
}
|
||
|
|
|
||
|
|
self.init = function() {
|
||
|
|
var fuExName = self.fuEx.getExName()
|
||
|
|
var spExName = self.spEx.getExName()
|
||
|
|
var gFuExName = _G("fuExName")
|
||
|
|
var gSpExName = _G("spExName")
|
||
|
|
if ((gFuExName && gFuExName != fuExName) || (gSpExName && gSpExName != spExName)) {
|
||
|
|
throw "交易所对象发生变化,需要重置数据"
|
||
|
|
}
|
||
|
|
if (!gFuExName) {
|
||
|
|
_G("fuExName", fuExName)
|
||
|
|
}
|
||
|
|
if (!gSpExName) {
|
||
|
|
_G("spExName", spExName)
|
||
|
|
}
|
||
|
|
|
||
|
|
self.allPairs = _G("allPairs")
|
||
|
|
if (!self.allPairs) {
|
||
|
|
self.allPairs = []
|
||
|
|
}
|
||
|
|
|
||
|
|
var arrPair = _G("pairs")
|
||
|
|
if (!arrPair) {
|
||
|
|
arrPair = []
|
||
|
|
}
|
||
|
|
var arrStrPair = self.symbolPairs.split(",")
|
||
|
|
var timeStamp = new Date().getTime()
|
||
|
|
_.each(arrStrPair, function(strPair) {
|
||
|
|
var arrSymbol = strPair.split("|")
|
||
|
|
var recoveryPair = null
|
||
|
|
_.each(arrPair, function(pair) {
|
||
|
|
if (pair.fuSymbol == arrSymbol[0] && pair.spSymbol == arrSymbol[1]) {
|
||
|
|
recoveryPair = pair
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if (!recoveryPair) {
|
||
|
|
var pair = {
|
||
|
|
fuSymbol : arrSymbol[0],
|
||
|
|
spSymbol : arrSymbol[1],
|
||
|
|
fuTicker : {},
|
||
|
|
spTicker : {},
|
||
|
|
plusDiff : null,
|
||
|
|
minusDiff : null,
|
||
|
|
canTrade : false,
|
||
|
|
initFuAcc : null,
|
||
|
|
initSpAcc : null,
|
||
|
|
nowFuAcc : null,
|
||
|
|
nowSpAcc : null,
|
||
|
|
nowFuPos : null,
|
||
|
|
nowSpPos : null,
|
||
|
|
fuMarginLevel : null
|
||
|
|
}
|
||
|
|
self.pairs.push(pair)
|
||
|
|
Log("初始化:", pair)
|
||
|
|
} else {
|
||
|
|
self.pairs.push(recoveryPair)
|
||
|
|
Log("恢复:", recoveryPair)
|
||
|
|
}
|
||
|
|
self.fuEx.pushSubscribeSymbol(arrSymbol[0])
|
||
|
|
self.spEx.pushSubscribeSymbol(arrSymbol[1])
|
||
|
|
if (!self.pairs[self.pairs.length - 1].initFuAcc) {
|
||
|
|
self.fuEx.goGetAcc(arrSymbol[0], timeStamp)
|
||
|
|
var nowFuAcc = self.fuEx.getAcc(arrSymbol[0], timeStamp)
|
||
|
|
self.pairs[self.pairs.length - 1].initFuAcc = nowFuAcc
|
||
|
|
self.pairs[self.pairs.length - 1].nowFuAcc = nowFuAcc
|
||
|
|
}
|
||
|
|
if (!self.pairs[self.pairs.length - 1].initSpAcc) {
|
||
|
|
self.spEx.goGetAcc(arrSymbol[1], timeStamp)
|
||
|
|
var nowSpAcc = self.spEx.getAcc(arrSymbol[1], timeStamp)
|
||
|
|
self.pairs[self.pairs.length - 1].initSpAcc = nowSpAcc
|
||
|
|
self.pairs[self.pairs.length - 1].nowSpAcc = nowSpAcc
|
||
|
|
}
|
||
|
|
Sleep(300)
|
||
|
|
})
|
||
|
|
Log("self.pairs:", self.pairs)
|
||
|
|
_.each(self.pairs, function(pair) {
|
||
|
|
var fuSymbolInfo = self.fuEx.getSymbolInfo(pair.fuSymbol)
|
||
|
|
if (!fuSymbolInfo) {
|
||
|
|
throw pair.fuSymbol + ",品种信息获取失败!"
|
||
|
|
} else {
|
||
|
|
Log(pair.fuSymbol, fuSymbolInfo)
|
||
|
|
}
|
||
|
|
var spSymbolInfo = self.spEx.getSymbolInfo(pair.spSymbol)
|
||
|
|
if (!spSymbolInfo) {
|
||
|
|
throw pair.spSymbol + ",品种信息获取失败!"
|
||
|
|
} else {
|
||
|
|
Log(pair.spSymbol, spSymbolInfo)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
_.each(self.pairs, function(pair) {
|
||
|
|
pair.fuMarginLevel = self.fuMarginLevel
|
||
|
|
var ret = self.fuEx.setMarginLevel(pair.fuSymbol, self.fuMarginLevel)
|
||
|
|
Log(pair.fuSymbol, "杠杆设置:", ret)
|
||
|
|
if (!ret) {
|
||
|
|
throw "初始设置杠杆失败!"
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
self.init()
|
||
|
|
return self
|
||
|
|
}
|
||
|
|
|
||
|
|
var manager = null
|
||
|
|
function main() {
|
||
|
|
if(isReset) {
|
||
|
|
_G(null)
|
||
|
|
LogReset(1)
|
||
|
|
LogProfitReset()
|
||
|
|
LogVacuum()
|
||
|
|
Log("重置所有数据", "#FF0000")
|
||
|
|
}
|
||
|
|
|
||
|
|
if (isOKEX_V5_Simulate) {
|
||
|
|
for (var i = 0 ; i < exchanges.length ; i++) {
|
||
|
|
if (exchanges[i].GetName() == "Futures_OKCoin" || exchanges[i].GetName() == "OKEX") {
|
||
|
|
var ret = exchanges[i].IO("simulate", true)
|
||
|
|
Log(exchanges[i].GetName(), "切换模拟盘")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var fuConfigureFunc = null
|
||
|
|
var spConfigureFunc = null
|
||
|
|
if (exchanges.length != 2) {
|
||
|
|
throw "需要添加两个交易所对象!"
|
||
|
|
} else {
|
||
|
|
var fuName = exchanges[0].GetName()
|
||
|
|
if (fuName == "Futures_OKCoin" && isOkexV5) {
|
||
|
|
fuName += "_V5"
|
||
|
|
Log("使用OKEX V5接口")
|
||
|
|
}
|
||
|
|
var spName = exchanges[1].GetName()
|
||
|
|
fuConfigureFunc = $.getConfigureFunc()[fuName]
|
||
|
|
spConfigureFunc = $.getConfigureFunc()[spName]
|
||
|
|
if (!fuConfigureFunc || !spConfigureFunc) {
|
||
|
|
throw (fuConfigureFunc ? "" : fuName) + " " + (spConfigureFunc ? "" : spName) + " not support!"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
var fuEx = $.createBaseEx(exchanges[0], fuConfigureFunc)
|
||
|
|
var spEx = $.createBaseEx(exchanges[1], spConfigureFunc)
|
||
|
|
manager = createManager(fuEx, spEx, symbolPairs, cmdHedgeAmount, fuMarginLevel, fuMarginReservedRatio)
|
||
|
|
|
||
|
|
while(true) {
|
||
|
|
manager.process()
|
||
|
|
Sleep(interval)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function onerror() {
|
||
|
|
if (manager) {
|
||
|
|
manager.onexit()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function onexit() {
|
||
|
|
if (manager) {
|
||
|
|
manager.onexit()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
> 策略出处
|
||
|
|
|
||
|
|
https://www.fmz.com/strategy/314352
|
||
|
|
|
||
|
|
> 更新时间
|
||
|
|
|
||
|
|
2021-09-08 15:17:16
|