En vista del hecho de que la frecuencia de cobertura de la estrategia de cobertura de futuros y puntos no es alta, en realidad es posible operar manualmente. Sin embargo, si lo hace manualmente, es muy inconveniente cambiar de página de varios intercambios, observar los precios y calcular la diferencia, y a veces puede que desee ver más variedades, y no es necesario configurar varios monitores para mostrar el mercado. ¿Es posible lograr este objetivo de operación manual con una estrategia semiautomática? Es mejor tener múltiples especies, oh! Sí, es mejor abrir y cerrar posiciones con un solo clic. Oh! Sí, también hay una posición de visualización...
¡Cuando haya una necesidad, hazlo ahora!
La escritura es bastante larga, con menos de 600 líneas de código.
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 = ["positive arbitrage", "reverse arbitrage", "close positive arbitrage", "close reverse arbitrage"]
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, ", failed to get account data")
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, ", failed to get position data")
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, "insufficient deposit!", "this plan uses", (fuAmount * fuPrice / self.fuMarginLevel), "currently available:", nowFuAcc.Balance,
"Plan to reserve:", (pair.initFuAcc.Balance + pair.initFuAcc.FrozenBalance) * self.fuMarginReservedRatio)
return
}
if ((tradeType == self.PLUS && nowSpAcc.Balance < spAmount * spPrice)) {
Log(pair.spSymbol, "insufficient funds!", "this purchase plans to use", spAmount * spPrice, "currently available:", nowSpAcc.Balance)
return
} else if (tradeType == self.MINUS && nowSpAcc.Stocks < spAmount) {
Log(pair.spSymbol, "insufficient funds!", "this selling plans to use", spAmount, "currently available:", 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, ", there is no corresponding position in futures!")
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, ", there is no corresponding position in the spot!")
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, "order quantity calculation error:", 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, "order quantity calculation error:", fuAmount, spAmount)
return
}
}
Log("contract code:", fuSymbol + "/" + spSymbol, "direction:", self.arrTradeTypeDesc[tradeType], "difference:", fuPrice - spPrice, "quantity of futures:", fuAmount, "quantity of spots:", 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, "portfolio position update failed, nowFuPos:", nowFuPos, " nowSpPos:", nowSpPos)
}
self.accAndPosUpdateCount++
}
})
if (nowTS - self.preUpdateAccTS > 1000 * 60 * 5) {
self.preUpdateAccTS = nowTS
self.profit = self.calcProfit()
LogProfit(self.profit[0], "futures:", self.profit[1], "spots:", self.profit[2], "&") // Print the total profit curve, use the & character not to print the profit log
}
var cmd = GetCommand()
if(cmd) {
Log("interactive commands:", 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("current time:", _D(), "data update time:", _D(self.tickerUpdateTS), "position account update count:", self.accAndPosUpdateCount, "\n", "Profit and loss:", self.profit[0], "futures profit and loss:", self.profit[1],
"spot profit and loss:", 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("reverse arbitrage is not supported")
}
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, "excess spot positions", 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, "long futures positions", 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("position error, three positions detected:", 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 : ["index", "future", "future leverage", "qunatity", "spot", "qunatity"],
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 : ["future", "code" + fuExName, "entrusted selling", "entrusted purchase", "spot", "code" + spExName, "entrusted selling", "entrusted purchase", "positive hedging spreads", "reverse hedging spreads", "positive hedge", "positive hedge closeout"],
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': 'positive arbitrage'},
{'type':'button', 'cmd': 'cover_plus:' + String(index), 'name': 'close positive arbitrage'}
])
})
var accsTbl = {
type : "table",
title : "accs",
cols : ["code" + fuExName, "initial coin", "initial frozen coin", "initial money", "initial frozen money", "coin", "frozen coin", "money", "frozen money",
"code" + spExName, "initial coin", "initial frozen coin", "initial money", "initial frozen money", "coin", "frozen coin", "money", "frozen money"],
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 : ["contract code" + fuExName, "quantity accuracy", "price accuracy", "multiplier", "minimum order quantity", "spot code" + spExName, "quantity accuracy", "price accuracy", "multiplier", "minimum order quantity"],
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("perform tailing processing and save data", "#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 "the exchange object has changed and the data needs to be reset"
}
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 + ", species information acquisition failure!"
} else {
Log(pair.fuSymbol, fuSymbolInfo)
}
var spSymbolInfo = self.spEx.getSymbolInfo(pair.spSymbol)
if (!spSymbolInfo) {
throw pair.spSymbol + ", species information acquisition failure!"
} 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, "leverage settings:", ret)
if (!ret) {
throw "initial setting of leverage failed!"
}
})
}
self.init()
return self
}
var manager = null
function main() {
if(isReset) {
_G(null)
LogReset(1)
LogProfitReset()
LogVacuum()
Log("reset all data", "#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(), "switch analog disk")
}
}
}
var fuConfigureFunc = null
var spConfigureFunc = null
if (exchanges.length != 2) {
throw "two exchange objects need to be added!"
} else {
var fuName = exchanges[0].GetName()
if (fuName == "Futures_OKCoin" && isOkexV5) {
fuName += "_V5"
Log("Use OKEX V5 interface")
}
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()
}
}
Dado que la estrategia de múltiples especies es más adecuada para el diseño de IO, una biblioteca de clases de plantilla llamadaMultiSymbolCtrlLib
Por lo tanto, la estrategia no puede ser probada con el bot simulado (aunque el bot real se ha ejecutado durante 2 meses, la etapa de prueba y familiarización todavía se ejecuta con el bot simulado).
Antes de comenzar la prueba, hablemos primero del diseño de parámetros.
No hay muchos parámetros estratégicos, los más importantes son:
Cuadro de control de cobertura
LTC-USDT-211231|LTC_USDT,BTC-USDT-211231|BTC_USDT
Aquí está la estrategia de configuración para monitorear esas combinaciones. Por ejemplo, la configuración anterior es monitorear el contrato Litecoin (LTC-USDT-211231) del intercambio de futuros y el Litecoin (LTC_USDT) del intercambio al contado.|
Las combinaciones diferentes se separan por,
¡Nota que los símbolos aquí están todos en el estado del método de entrada en inglés!
Estos códigos de contrato y los pares de negociación al contado están definidos por el intercambio, no por la plataforma FMZ.
Por ejemplo,LTC-USDT-211231
es un contrato del segundo trimestre actualmente, llamadonext_quarter
en FMZ, y el sistema de interfaz de OKEXLTC-USDT-211231
Para elEl valor de las acciones de las entidades de créditoel bot de simulación de WexApp está escrito comoLTC_USDT
Así que cómo rellenar aquí depende del nombre definido en el intercambio.
Valor de cobertura para la cobertura de control interactivo Haga clic en el botón de control de la barra de estado para cubrir la cantidad. La unidad es el número de monedas, y la estrategia se convertirá automáticamente en el número de contratos para realizar un pedido.
Otras funciones son configurar el disco analógico, restablecer los datos, usar la interfaz OKEX V5 (ya que también es compatible con V3) y así sucesivamente, que no son particularmente importantes.
El primer objeto de intercambio agrega el intercambio de futuros, y el segundo agrega el objeto de intercambio al contado.
Las bolsas de futuros utilizan bots de simulación de interfaz V5 de OKEX
Haga clic en el botón de conjunto positivo de la combinación BTC y abra la posición.
Haga clic para cerrar el arbitraje positivo entonces.
Perder!!! Parece que cerrar la posición no puede cubrir la tarifa de manejo cuando el diferencial de ganancias es pequeño, es necesario calcular la tarifa de manejo, el deslizamiento aproximado, y planificar el diferencial razonablemente, y luego cerrar la posición.
Código fuente de la estrategia:https://www.fmz.com/strategy/314352
Los interesados pueden usarlo y modificarlo.