Manual Cryptocurrency Futures & Strategi Hedge Spot

Penulis:Ninabadass, Dibuat: 2022-04-12 15:12:36, Diperbarui: 2022-04-12 15:44:18

Manual Cryptocurrency Futures & Strategi Hedge Spot

Karena frekuensi lindung nilai strategi lindung nilai saat ini tidak terlalu tinggi, sebenarnya dapat dioperasikan secara manual. Namun, jika Anda melakukannya secara manual, beralih halaman berbagai platform, amati harga, dan hitung spread harga, yang sangat tidak nyaman. Kadang-kadang, Anda mungkin ingin melihat lebih banyak simbol, dan tidak perlu menyiapkan beberapa tampilan untuk menunjukkan kutipan pasar. Apakah mungkin untuk mencapai persyaratan operasi manual ini dengan strategi semi-otomatis? Dan lebih baik memiliki lebih banyak simbol? oh! ya. Yang terbaik adalah membuka dan menutup posisi dengan satu klik. oh, benar. Ada juga tampilan posisi...

Jika ada kebutuhan, lakukan sekarang!

Merancang Manual Cryptocurrency Futures & Spot Hedge Strategy

Kode yang ditulis di sini agak membosankan, tidak mencapai 600 baris.

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, ", fail to obtain the 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, ",fail to obtain the 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, "Inadequate margin!", "This time, plan to use", (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, "Inadequate assets!", "This time, buy and plan to use", spAmount * spPrice, "Currently available:", nowSpAcc.Balance)
                return 
            } else if (tradeType == self.MINUS && nowSpAcc.Stocks < spAmount) {       
                Log(pair.spSymbol, "Inadequate assets!", "This time, sell and plan 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, ", no corresponding position of 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, ", no corresponding position of 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 amount 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 amount calculation error:", fuAmount, spAmount)
                return 
            }
        }
        Log("Contract code:", fuSymbol + "/" + spSymbol, "Direction:", self.arrTradeTypeDesc[tradeType], "Spread:", fuPrice - spPrice, "Futures amount:", fuAmount, "Spot amount:", 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, "Fail to update combined position, 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], "Spot:", self.profit[2], "&")    // print the total equity curve, and use charater "&" to not print the equity log 
        }

        var cmd = GetCommand()
        if(cmd) {
            Log("Interactive command:", 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 date:", _D(), "  Data update date:", _D(self.tickerUpdateTS), "Update count of postion account:", 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("Do not support reverse arbitrage") 
        }
        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, "spot long position", 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, "futures long position", 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, and three positions are 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", "Futures", "Futures Leverage", "Amount", "Spot", "Amount"], 
            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 : ["Futures", "Code" + fuExName, "Sell 1", "Buy 1", "Spot", "Code" + spExName, "Sell 1", "Buy 1", "Positive Hedge Spread", "Reverse Hedge Spread", "Positive Hedge", "Positive Hedge to close Positions"], 
            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 Symbol", "Initial Frozen Symbol", "Initial Assets", "Initial Frozen Assets", "Symbol", "Frozen Symbol", "Assets", "Frozen Assets",
                "Code" + spExName, "Initial Symbol", "Initial Frozen Symbol", "Initial Assets", "Initial Frozen Assets", "Symbol", "Frozen Symbol", "Assets", "Forzen Assets"], 
            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, "Amount Precision", "Price Precision", "Multiplier", "Minimum Order Amount", "Spot Code" + spExName, "Amount Precision", "Price Precision", "Multiplier", "Minimum Order Amount"], 
            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("Execute clean-up processing, and save the 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 exchenge object is changed, so reset the data"
        }
        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("Initialize:", pair)
            } else {
                self.pairs.push(recoveryPair)
                Log("Recover:", 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 + ", fail to obtain the symbol information!"
            } else {
                Log(pair.fuSymbol, fuSymbolInfo)
            }
            var spSymbolInfo = self.spEx.getSymbolInfo(pair.spSymbol)
            if (!spSymbolInfo) {
                throw pair.spSymbol + ", fail to obtain the symbol information!"
            } 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 Setting:", ret)
            if (!ret) {
                throw "Leverage initial setting 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 to simulated bot")
            }
        }
    }

    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()
    }
}

Karena strategi multi-simbol lebih cocok untuk desain IO, perpustakaan template bernamaMultiSymbolCtrlLibStrategi ini tidak dapat diuji kembali, tetapi dapat diuji dengan bot simulasi (meskipun telah dijalankan di bot nyata selama 2 bulan, pada tahap pengujian dan familiarization, Anda lebih baik menjalankannya di bot simulasi).

Parameter

Sebelum tes, mari kita bicara tentang desain parameter.

img

Parameter strategi tidak terlalu banyak, dan yang penting adalah:

LTC-USDT-211231|LTC_USDT,BTC-USDT-211231|BTC_USDT

Di sini adalah untuk mengatur strategi untuk memantau kombinasi tersebut. Misalnya pengaturan di atas adalah untuk memantau kontrak Litecoin (LTC-USDT-211231) dari platform futures dan Litecoin (LTC_USDT) dari platform spot.|simbol untuk membentuk kombinasi. kombinasi yang berbeda dibagi oleh,Perhatikan bahwa simbol-simbol di sini semua dalam keadaan metode input bahasa Inggris! Kemudian Anda mungkin bertanya kepada saya bagaimana menemukan kode kontrak. kode kontrak ini dan pasangan perdagangan spot semuanya didefinisikan oleh platform, tidak didefinisikan pada platform FMZ. Misalnya, kontrakLTC-USDT-211231saat ini kontrak kuartal berikutnya, disebutnext_quarterdi FMZ, dan sistem antarmuka OKEX disebutLTC-USDT-211231. UntukLTC/USDTpasangan perdagangan, bot simulasi WexApp ditulis sebagaiLTC_USDTJadi bagaimana mengisi di sini tergantung pada nama yang didefinisikan dalam platform tertentu.

  • Nilai lindung nilai dari kontrol interaktif Klik tombol kontrol di bilah status, yaitu jumlah lindung nilai. unit adalah jumlah mata uang, yang akan secara otomatis dikonversi menjadi jumlah kontrak oleh strategi untuk menempatkan pesanan.

    img

Fungsi lainnya adalah mengatur bot simulasi, mengatur ulang data, menggunakan antarmuka OKEX V5 (karena juga kompatibel dengan V3) dan fungsi lain, yang tidak terlalu penting.

Tes

Objek pertukaran pertama yang ditambahkan memilih platform yang menambahkan futures, dan yang kedua memilih objek pertukaran spot.

Platform berjangka menggunakan bot simulasi antarmuka OKEX V5, dan platform spot menggunakan bot simulasi wexApp.

Saya mengklik tombol arbitrage positif dari kombinasi BTC dan membuka posisi.

img

img

img

Kemudian, klik arbitrage positif untuk menutup posisi.

img

img

kerugian!!! tampaknya bahwa ketika profit spread kecil, penutupan posisi tidak dapat menutupi biaya. perlu untuk menghitung biaya dan titik kemiringan perkiraan, dan kemudian secara wajar merencanakan spread untuk menutup posisi, dan kemudian menutup posisi.

Kode Sumber Strategi:https://www.fmz.com/strategy/314352

Siapa pun yang tertarik dapat menggunakannya dan memodifikasinya.


Lebih banyak