資源の読み込みに... 荷物...

MultiSymbolCtrlLib をインストールする

作者: リン・ハーン発明者 量化 - 微かな夢開催日:2021年4月27日11時46分
タグ:

解説

記事で使用されているテンプレート https://www.fmz.com/digest-topic/7373 このコードは,参考学習のみで,実用的な使用は注意してください.


// 交易所接口调用实现
// OKEX V3 合约
function funcConfigure_Futures_OKCoin(self) {
    // 内置函数可以根据需求自由编写
    var formatSymbol = function(originalSymbol) {
        // originalSymbol : LINK-USD-210423
        var arr = originalSymbol.split("-")
        var baseCurrency = arr[0]
        var quoteCurrency = arr[1]
        return [(baseCurrency + "_" + quoteCurrency).toUpperCase(), baseCurrency.toUpperCase(), quoteCurrency.toUpperCase()]    // 返回数据:[currency, baseCurrency, quoteCurrency]
    }

    self.interfaceGetTickers = function interfaceGetTickers() {
        // 可以识别 self.subscribeSymbols 中订阅的品种判断是否需要永续合约行情
        var url = "https://www.okex.com/api/futures/v3/instruments/ticker"
        self.routineGetTicker = HttpQuery_Go(url)
    }

    self.waitTickers = function waitTickers() {
        var ret = []
        var arr = JSON.parse(self.routineGetTicker.wait())
        _.each(arr, function(ele) {
            ret.push({
            	bid1: parseFloat(ele.best_bid), 
            	bid1Vol: parseFloat(ele.best_bid_size), 
            	ask1: parseFloat(ele.best_ask), 
            	ask1Vol: parseFloat(ele.best_ask_size), 
            	symbol: formatSymbol(ele.instrument_id)[0], 
            	type: "Futures", 
            	originalSymbol: ele.instrument_id
            })
        })
        return ret 
    }

    self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
        // 可根据symbol识别是否是永续合约
        var arr = formatSymbol(symbol)
        var url = "/api/futures/v3/accounts/" + arr[1].toLowerCase() + "-" + arr[2].toLowerCase()        
        self.routineGetAcc = self.e.Go("IO", "api", "GET", url)
    }

    self.waitAcc = function waitAcc(symbol, updateTS) {
        var acc = null 
        var ret = self.routineGetAcc.wait()
        // 判断是不是全仓
        if (ret.margin_mode != "crossed") {
            Log(self.name, "持仓模式不为全仓!")
            return 
        }
        var balance = parseFloat(ret.equity) - parseFloat(ret.margin)
        var frozenBalance = parseFloat(ret.margin_for_unfilled)
        if (ret.currency == "USDT") {
            acc = {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: balance, FrozenBalance: frozenBalance, originalInfo: ret}
        } else if (ret.currency == "USD") {
            acc = {symbol: symbol, Stocks: balance, FrozenStocks: frozenBalance, Balance: 0, FrozenBalance: 0, originalInfo: ret}
        }
        return acc
    }

    self.interfaceGetPos = function interfaceGetPos(symbol, updateTS) {
    	var symbolInfo = self.getSymbolInfo(symbol)
    	var url = "/api/futures/v3/" + symbol + "/position"
    	if (symbol.includes("SWAP")) {
    		url = "/api/swap/v3/" + symbol + "/position"
    	}
    	var ret = self.e.IO("api", "GET", url)
    	var positions = []
    	_.each(ret.holding, function(ele) {
    		if (ele.instrument_id == symbol && parseFloat(ele.short_qty) > 0) {   // 持有空头仓位
    			positions.push({symbol: symbol, amount: -parseFloat(ele.short_qty) * symbolInfo.multiplier, price: parseFloat(ele.short_avg_cost), marginLevel: parseFloat(ele.leverage), originalInfo: ele})
    		}
    		if (ele.instrument_id == symbol && parseFloat(ele.long_qty) > 0) {    // 持有多头仓位
    			positions.push({symbol: symbol, amount: parseFloat(ele.long_qty) * symbolInfo.multiplier, price: parseFloat(ele.long_avg_cost), marginLevel: parseFloat(ele.leverage), originalInfo: ele})
    		}
    	})
        return positions
    }

    self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
        // 判断合约 交割、永续
        var url = "/api/futures/v3/order"
        if (symbol.includes("SWAP")) {
            url = "/api/swap/v3/order"
        }
        var tradeType = ""
        switch(type) {
        case self.OPEN_LONG:
            tradeType = "1"
            break
        case self.OPEN_SHORT:
            tradeType = "2"
            break
        case self.COVER_LONG:
            tradeType = "3"
            break
        case self.COVER_SHORT:
            tradeType = "4"
            break
        default:
            throw "type error, type:" + type
        }
        var params = {
            "instrument_id": symbol, 
            "type": tradeType,
            "order_type": "4",
            "size": String(amount)
        }
        self.routineTrade = self.e.Go("IO", "api", "POST", url, self.encodeParams(params))
    }

    self.waitTrade = function waitTrade() {
        return self.routineTrade.wait()
    }

    self.calcAmount = function calcAmount(symbol, type, price, amount) {
        // 处理下单量
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbolInfo) {
            throw symbol + ",交易对信息查询不到"
        }
        var tradeAmount = _N(amount / symbolInfo.multiplier, 0)
        // 判断最小下单量
        if (tradeAmount < parseFloat(symbolInfo.min)) {
            Log(self.name, " tradeAmount:", tradeAmount, "小于", parseFloat(symbolInfo.min))
            return false 
        }
        return [tradeAmount, tradeAmount * symbolInfo.multiplier]
    }

    self.interfaceSetMarginLevel = function interfaceSetMarginLevel(symbol, marginLevel) {
        var arr = formatSymbol(symbol)
        var underlying = arr[1] + "-" + arr[2]
        var url = "/api/futures/v3/accounts/" + underlying + "/leverage"
        var params = {
        	"leverage" : String(marginLevel)
        }
        if (symbol.includes("SWAP")) {
        	url = "/api/futures/v3/accounts/" + symbol + "/leverage"
        	params = {
        		"instrument_id" : symbol,
        		"leverage" : String(marginLevel), 
        		"side" : "3"
        	}
        } else {
            var ret  = self.e.IO("api", "GET", "/api/futures/v3/accounts/" + underlying + "/leverage")
            // 如果不是永续合约,要先切换全仓模式,设置全仓模式前先判断是否不为全仓
            if (ret.margin_mode != "crossed") { 
                self.e.IO("api", "POST", "/api/futures/v3/accounts/margin_mode", self.encodeParams({"underlying" : underlying, "margin_mode" : "crossed"}))
            }
        }
        return self.e.IO("api", "POST", url, self.encodeParams(params))
    }

    self.interfaceCalcProfit = function interfaceCalcProfit(arrInitAcc, arrNowAcc) {
    	// 计算总权益差值,即盈亏
    	var profit = 0
        _.each(arrInitAcc, function(initAcc) {
        	_.each(arrNowAcc, function(nowAcc) {
        		if (initAcc.symbol == nowAcc.symbol) {
        			profit += nowAcc.originalInfo.equity - initAcc.originalInfo.equity
        		}
        	})
        })
        return profit
    }

    self.init = function init() {
        // 可以识别 self.subscribeSymbols 中订阅的品种判断是否需要永续合约信息
        var ret = JSON.parse(HttpQuery("https://www.okex.com/api/futures/v3/instruments"))   // 只获取了交割合约,可以增加永续合约
        _.each(ret, function(symbolInfo) {
            self.symbolsInfo.push({
                symbol: symbolInfo.instrument_id,
                amountPrecision: self.judgePrecision(parseFloat(symbolInfo.tick_size)),
                pricePrecision: self.judgePrecision(parseFloat(symbolInfo.trade_increment)),
                multiplier: parseFloat(symbolInfo.contract_val),
                min: 1,                                                                      // OKEX 合约最小下1张
                originalInfo: symbolInfo
            })
        })
    }
}

// 币安合约
function funcConfigure_Futures_Binance(self) {
    var formatSymbol = function (originalSymbol) {
        // BTCUSD_200925
        originalSymbol = (originalSymbol.split("_")[0]).toLowerCase()
        var baseCurrency = originalSymbol.replace(/(btc|eth|usdt|husd|ht|trx)$/, "")
        var quoteCurrency = originalSymbol.replace(baseCurrency, "")
        return [(baseCurrency + "_" + quoteCurrency).toUpperCase(), baseCurrency.toUpperCase(), quoteCurrency.toUpperCase()]
    }

    self.interfaceGetTickers = function interfaceGetTickers() {
        self.routineGetTicker = HttpQuery_Go("https://fapi.binance.com/fapi/v1/ticker/bookTicker")
    }

    self.waitTickers = function waitTickers() {
        var ret = []
        var arr = JSON.parse(self.routineGetTicker.wait())
        _.each(arr, function(ele) {
            ret.push({
                bid1: parseFloat(ele.bidPrice), 
                bid1Vol: parseFloat(ele.bidQty), 
                ask1: parseFloat(ele.askPrice), 
                ask1Vol: parseFloat(ele.askQty), 
                symbol: formatSymbol(ele.symbol)[0],
                type: "Futures", 
                originalSymbol: ele.symbol
            })
        })
        return ret 
    }

    self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
        if (self.updateAccsTS != updateTS) {      // 如果新的一轮获取账户信息才去创建并发数据
            self.routineGetAcc = self.e.Go("IO", "api", "GET", "/fapi/v2/account")
        }
    }

    self.waitAcc = function waitAcc(symbol, updateTS) {
        var ret = null 
        if (self.updateAccsTS != updateTS) {      // 如果新的一轮获取账户信息才去获取并发数据
            ret = self.routineGetAcc.wait()            
            self.bufferGetAccRet = ret            // 由于交易所资产数据接口返回的是全部信息,避免重复调用,缓存 
        } else {                                  // 如果时间戳相等,说明是同一轮,用缓存数据
            ret = self.bufferGetAccRet
        }
        if (!ret) {
            return null 
        }
        return {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: ret.availableBalance, FrozenBalance: ret.totalOpenOrderInitialMargin, originalInfo: ret}
    }

    self.interfaceGetPos = function interfaceGetPos(symbol, updateTS) {
        // GET /fapi/v2/positionRisk
        var ret = null 
        if (self.updatePosTS != updateTS) {
            ret = self.e.IO("api", "GET", "/fapi/v2/positionRisk")
            self.bufferGetPosRet = ret 
        } else {
            ret = self.bufferGetPosRet
        }
        if (!ret) {
            return null 
        } 
        var positions = []
        _.each(ret, function(ele) {
            if (ele.symbol == symbol && parseFloat(ele.positionAmt) < 0) {   // 持有空头仓位
                positions.push({symbol: symbol, amount: parseFloat(ele.positionAmt) * symbolInfo.multiplier, price: parseFloat(ele.entryPrice), marginLevel: parseFloat(ele.leverage), originalInfo: ele})
            }
            if (ele.symbol == symbol && parseFloat(ele.positionAmt) > 0) {   // 持有多头仓位
                positions.push({symbol: symbol, amount: parseFloat(ele.positionAmt) * symbolInfo.multiplier, price: parseFloat(ele.entryPrice), marginLevel: parseFloat(ele.leverage), originalInfo: ele})
            }
        })
        return positions        
    }

    self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
        // POST /fapi/v1/order  symbol  side type  quantity  
        var params = {"symbol": symbol, "type": "MARKET", quantity: String(amount)}
        switch(type) {
        case self.OPEN_LONG:
            params["side"] = "BUY"
            params["positionSide"] = "LONG"
            break
        case self.OPEN_SHORT:
            params["side"] = "SELL"
            params["positionSide"] = "SHORT"
            break
        case self.COVER_LONG:
            params["side"] = "SELL"
            params["positionSide"] = "LONG"
            break
        case self.COVER_SHORT:
            params["side"] = "BUY"
            params["positionSide"] = "SHORT"
            break
        default: 
            throw "type error, type:" + type
        }

        if (!self.dualSidePosition) {
            params["positionSide"] = "BOTH"
        }
        self.routineTrade = self.e.Go("IO", "api", "POST", "/fapi/v1/order", self.encodeParams(params))
    }

    self.waitTrade = function waitTrade() {
        return self.routineTrade.wait()
    }

    self.calcAmount = function calcAmount(symbol, type, price, amount) {
        // 处理下单量
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbolInfo) {
            throw symbol + ",交易对信息查询不到"
        }
        var tradeAmount = _N(amount / symbolInfo.multiplier, symbolInfo.amountPrecision)
        // 判断最小下单量
        var notional = null
        _.each(symbolInfo.originalInfo.filters, function(filter) {
            if (filter.filterType == "MIN_NOTIONAL") {
                notional = parseFloat(filter.notional)
            }
        })
        if (typeof(notional) != "number") {
            Log("查找不到notional")
            return false 
        }
        if (tradeAmount < parseFloat(symbolInfo.min) || tradeAmount * price < notional) {      // 币安有额外金额限制条件
            Log(self.name, " tradeAmount:", tradeAmount, "小于", parseFloat(symbolInfo.min), "或者 ", "价值:", tradeAmount * price, "小于notional:", notional)
            return false 
        }
        return [tradeAmount, tradeAmount * symbolInfo.multiplier]
    }

    self.interfaceSetMarginLevel = function interfaceSetMarginLevel(symbol, marginLevel) {
        // POST /fapi/v1/leverage   symbol leverage
        var params = {"symbol": symbol, "leverage": String(marginLevel)}
        return self.e.IO("api", "POST", "/fapi/v1/leverage", self.encodeParams(params))
    }

    self.interfaceCalcProfit = function interfaceCalcProfit(arrInitAcc, arrNowAcc) {
        // 币安期货资产是每个交易对共有的
        var profit = 0
        var initOriginalInfo = null 
        var nowOriginalInfo = null 
        _.each(arrInitAcc, function(initAcc) {
            _.each(arrNowAcc, function(nowAcc) {
                if (!initOriginalInfo && !nowOriginalInfo && initAcc.symbol == nowAcc.symbol) {
                    initOriginalInfo = initAcc.originalInfo
                    nowOriginalInfo = nowAcc.originalInfo
                }
            })
        })
        return nowOriginalInfo.totalWalletBalance - initOriginalInfo.totalWalletBalance
    }

    self.init = function init() {
        var ret = JSON.parse(HttpQuery("https://fapi.binance.com/fapi/v1/exchangeInfo"))
        _.each(ret.symbols, function(symbolInfo) {
            var min = null 
            _.each(symbolInfo.filters, function(filter) {
                if (filter.filterType == "MARKET_LOT_SIZE") {
                    min = parseFloat(filter.minQty)
                }
            })
            self.symbolsInfo.push({
                symbol: symbolInfo.symbol,
                amountPrecision: parseFloat(symbolInfo.quantityPrecision),
                pricePrecision: parseFloat(symbolInfo.pricePrecision),
                multiplier: 1,
                min: min,
                originalInfo: symbolInfo
            })
        })
        // 查询持仓模式 GET /fapi/v1/positionSide/dual
        self.dualSidePosition = self.e.IO("api", "GET", "/fapi/v1/positionSide/dual")["dualSidePosition"]   // 给封装的交易所对象增加属性dualSidePosition,true为双向持仓
        Log("币安期货持仓模式,dualSidePosition为:", self.dualSidePosition)
    }
}

// 火币现货
function funcConfigure_Huobi(self) {
    var formatSymbol = function(originalSymbol) {
        var baseCurrency = originalSymbol.replace(/(btc|eth|usdt|husd|ht|trx)$/, "")
        var quoteCurrency = originalSymbol.replace(baseCurrency, "")
        return [(baseCurrency + "_" + quoteCurrency).toUpperCase(), baseCurrency.toUpperCase(), quoteCurrency.toUpperCase()]
    }

    self.interfaceGetTickers = function interfaceGetTickers() {
        self.routineGetTicker = HttpQuery_Go("https://api.huobi.pro/market/tickers")
    }

    self.waitTickers = function waitTickers() {
        var ret = []
        var arr = JSON.parse(self.routineGetTicker.wait()).data
        _.each(arr, function(ele) {
            ret.push({
            	bid1: parseFloat(ele.bid), 
            	bid1Vol: parseFloat(ele.bidSize),
            	ask1: parseFloat(ele.ask), 
            	ask1Vol: parseFloat(ele.askSize),
            	symbol: formatSymbol(ele.symbol)[0],
            	type: "Spot", 
            	originalSymbol: ele.symbol
            })
        })
        return ret 
    }

    self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
        if (self.updateAccsTS != updateTS) {
            self.routineGetAcc = self.e.Go("GetAccount")
        }
    }

    self.waitAcc = function waitAcc(symbol, updateTS) {
        var arr = formatSymbol(symbol)
        var ret = null 
        if (self.updateAccsTS != updateTS) {
            ret = self.routineGetAcc.wait().Info
            self.bufferGetAccRet = ret 
        } else {
            ret = self.bufferGetAccRet
        }
        if (!ret) {
            return null 
        }
        var acc = {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: 0, FrozenBalance: 0, originalInfo: ret}
        _.each(ret.data.list, function(ele) {
            if (ele.currency == arr[1].toLowerCase()) {
                // baseCurrency
                if (ele.type == "trade") {
                    acc.Stocks = parseFloat(ele.balance)
                } else if (ele.type == "frozen") {
                    acc.FrozenStocks = parseFloat(ele.balance)
                }
            } else if (ele.currency == arr[2].toLowerCase()) {
                // quoteCurrency
                if (ele.type == "trade") {
                    acc.Balance = parseFloat(ele.balance)
                } else if (ele.type == "frozen") {
                    acc.FrozenBalance = parseFloat(ele.balance)
                }
            }
        })
        return acc
    }

    self.interfaceGetPos = function interfaceGetPos(symbol, price, initSpAcc, nowSpAcc) {
        var symbolInfo = self.getSymbolInfo(symbol)
        var sumInitStocks = initSpAcc.Stocks + initSpAcc.FrozenStocks
        var sumNowStocks = nowSpAcc.Stocks + nowSpAcc.FrozenStocks
        var diffStocks = _N(sumNowStocks - sumInitStocks, symbolInfo.amountPrecision)
        if (Math.abs(diffStocks) < symbolInfo.min / price) {
        	return []
        }
        return [{symbol: symbol, amount: diffStocks, price: null, originalInfo: {}}]
    }

    self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
        if (typeof(self.account_id) == "undefined") {
            var acc = self.e.GetAccount()
            self.account_id = acc.Info.data.id
        }

        var tradeType = ""
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeType = "buy-market"
        } else {
            tradeType = "sell-market"
        }

        var params = {
            "account-id": String(self.account_id),
            "symbol": symbol,
            "type": tradeType,
            "amount": String(amount),
        }
        self.routineTrade = self.e.Go("IO", "api", "POST", "/v1/order/orders/place", self.encodeParams(params))
    }

    self.waitTrade = function waitTrade() {
        return self.routineTrade.wait()
    }

    self.calcAmount = function calcAmount(symbol, type, price, amount) {
        // 获取交易对信息
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ",交易对信息查询不到"
        }
        var tradeAmount = null 
        var equalAmount = null  // 记录币数
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.originalInfo["value-precision"]))
            // 检查最小交易量
            if (tradeAmount < symbolInfo.originalInfo["min-order-value"]) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.originalInfo["min-order-value"])
                return false 
            }
            equalAmount = tradeAmount / price
        } else {
            tradeAmount = _N(amount, parseFloat(symbolInfo.originalInfo["amount-precision"]))
            // 检查最小交易量
            if (tradeAmount < symbolInfo.originalInfo["sell-market-min-order-amt"]) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.originalInfo["sell-market-min-order-amt"])
                return false 
            }
            equalAmount = tradeAmount
        }
        return [tradeAmount, equalAmount]
    }

    self.init = function init() {
        var ret = JSON.parse(HttpQuery("https://api.huobi.pro/v1/common/symbols"))
        _.each(ret.data, function(symbolInfo) {
            self.symbolsInfo.push({
                symbol: symbolInfo.symbol,
                amountPrecision: parseFloat(symbolInfo["amount-precision"]),
                pricePrecision: parseFloat(symbolInfo["price-precision"]),
                multiplier: 1,
                min: parseFloat(symbolInfo["min-order-value"]),
                originalInfo: symbolInfo
            })
        })        
    }
}

// 币安现货
function funcConfigure_Binance(self) {
    var formatSymbol = function(originalSymbol) {
        // LTCBTC
        var baseCurrency = originalSymbol.replace(/(BTC|ETH|USDT|BNB|XRP|TRX|BUSD|EUR)$/, "")
        var quoteCurrency = originalSymbol.replace(baseCurrency, "")
        return [baseCurrency + "_" + quoteCurrency, baseCurrency, quoteCurrency]
    }

    self.interfaceGetTickers = function interfaceGetTickers() {
        self.routineGetTicker = HttpQuery_Go("https://api.binance.com/api/v3/ticker/bookTicker")
    }

    self.waitTickers = function waitTickers() {
        var ret = []
        var arr = JSON.parse(self.routineGetTicker.wait())
        _.each(arr, function(ele) {
            ret.push({
                bid1: parseFloat(ele.bidPrice), 
                bid1Vol: parseFloat(ele.bidQty),
                ask1: parseFloat(ele.askPrice), 
                ask1Vol: parseFloat(ele.askQty),
                symbol: formatSymbol(ele.symbol)[0],
                type: "Spot", 
                originalSymbol: ele.symbol
            })
        })
        return ret 
    }

    self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
        if (self.updateAccsTS != updateTS) {
            self.routineGetAcc = self.e.Go("GetAccount")
        }
    }

    self.waitAcc = function waitAcc(symbol, updateTS) {
        var arr = formatSymbol(symbol)
        var ret = null 
        if (self.updateAccsTS != updateTS) {
            ret = self.routineGetAcc.wait().Info
            self.bufferGetAccRet = ret 
        } else {
            ret = self.bufferGetAccRet
        }
        if (!ret) {
            return null 
        }
        var acc = {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: 0, FrozenBalance: 0, originalInfo: ret}
        _.each(ret.balances, function(ele) {
            if (ele.asset == arr[1]) {
                // baseCurrency
                acc.Stocks = parseFloat(ele.free)
                acc.FrozenStocks = parseFloat(ele.locked)
            } else if (ele.asset == arr[2]) {
                // quoteCurrency
                acc.Balance = parseFloat(ele.free)
                acc.FrozenBalance = parseFloat(ele.locked)
            }
        })
        return acc
    }

    self.interfaceGetPos = function interfaceGetPos(symbol, price, initSpAcc, nowSpAcc) {
        var symbolInfo = self.getSymbolInfo(symbol)
        var sumInitStocks = initSpAcc.Stocks + initSpAcc.FrozenStocks
        var sumNowStocks = nowSpAcc.Stocks + nowSpAcc.FrozenStocks
        var diffStocks = _N(sumNowStocks - sumInitStocks, symbolInfo.amountPrecision)
        if (Math.abs(diffStocks) < symbolInfo.min / price) {
            return []
        }
        return [{symbol: symbol, amount: diffStocks, price: null, originalInfo: {}}]
    }

    self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
        var tradeType = ""
        var amountKeyName = ""
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeType = "BUY"
            amountKeyName = "quoteOrderQty"
        } else {
            tradeType = "SELL"
            amountKeyName = "quantity"
        }

        var params = {
            "symbol" : symbol,
            "side" : tradeType,
            "type" : "MARKET"
        }
        params[amountKeyName] = String(amount)
        // Log("params:", params, "encodeParams:", self.encodeParams(params))
        self.routineTrade = self.e.Go("IO", "api", "POST", "/api/v3/order", self.encodeParams(params))
    }

    self.waitTrade = function waitTrade() {
        return self.routineTrade.wait()
    }

    self.calcAmount = function calcAmount(symbol, type, price, amount) {
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbolInfo) {
            throw symbol + ",交易对信息查询不到"
        }
        var tradeAmount = null 
        var equalAmount = null 
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, symbolInfo.amountPrecision)
            if (tradeAmount < symbolInfo.min) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min)
                return false 
            }
            equalAmount = tradeAmount / price
        } else {
            tradeAmount = _N(amount, symbolInfo.amountPrecision)
            if (tradeAmount * price < symbolInfo.min) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min)
                return false 
            }
            equalAmount = tradeAmount
        }
        return [tradeAmount, equalAmount]
    }

    self.init = function init() {
        // GET /api/v3/exchangeInfo
        var ret = JSON.parse(HttpQuery("https://api.binance.com/api/v3/exchangeInfo"))
        _.each(ret.symbols, function(symbolInfo) {
            var min = null 
            var amountPrecision = null 
            var pricePrecision = null 
            _.each(symbolInfo.filters, function(filter) {
                if (filter.filterType == "PRICE_FILTER") {
                    pricePrecision = self.judgePrecision(parseFloat(filter.tickSize))
                } else if (filter.filterType == "LOT_SIZE") {
                    amountPrecision = self.judgePrecision(parseFloat(filter.minQty))
                    // min = parseFloat(filter.minQty)   // 可能还要考虑名义价值即金额
                } else if (filter.filterType == "MIN_NOTIONAL") {
                    min = parseFloat(filter.minNotional)
                }
            })
            self.symbolsInfo.push({
                symbol : symbolInfo.symbol,
                amountPrecision : parseFloat(amountPrecision),
                pricePrecision : parseFloat(pricePrecision),
                multiplier : 1,
                min : min,
                originalInfo : symbolInfo
            })
        })
    }
}

function funcConfigure_WexApp(self) {
    var formatSymbol = function(originalSymbol) {
        // BTC_USDT
        var arr = originalSymbol.split("_")
        var baseCurrency = arr[0]
        var quoteCurrency = arr[1]
        return [originalSymbol, baseCurrency, quoteCurrency]
    }

    self.interfaceGetTickers = function interfaceGetTickers() {
        self.routineGetTicker = HttpQuery_Go("https://api.wex.app/api/v1/public/tickers")
    }

    self.waitTickers = function waitTickers() {
        var ret = []
        var arr = JSON.parse(self.routineGetTicker.wait()).data
        _.each(arr, function(ele) {
            ret.push({
                bid1: parseFloat(ele.buy), 
                bid1Vol: parseFloat(-1),
                ask1: parseFloat(ele.sell), 
                ask1Vol: parseFloat(-1),
                symbol: formatSymbol(ele.market)[0],
                type: "Spot", 
                originalSymbol: ele.market
            })
        })
        return ret 
    }

    self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
        if (self.updateAccsTS != updateTS) {
            self.routineGetAcc = self.e.Go("GetAccount")
        }
    }

    self.waitAcc = function waitAcc(symbol, updateTS) {
        var arr = formatSymbol(symbol)
        var ret = null 
        if (self.updateAccsTS != updateTS) {
            ret = self.routineGetAcc.wait().Info
            self.bufferGetAccRet = ret 
        } else {
            ret = self.bufferGetAccRet
        }
        if (!ret) {
            return null 
        }        
        var acc = {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: 0, FrozenBalance: 0, originalInfo: ret}
        _.each(ret.exchange, function(ele) {
            if (ele.currency == arr[1]) {
                // baseCurrency
                acc.Stocks = parseFloat(ele.free)
                acc.FrozenStocks = parseFloat(ele.frozen)
            } else if (ele.currency == arr[2]) {
                // quoteCurrency
                acc.Balance = parseFloat(ele.free)
                acc.FrozenBalance = parseFloat(ele.frozen)
            }
        })
        return acc
    }

    self.interfaceGetPos = function interfaceGetPos(symbol, price, initSpAcc, nowSpAcc) {
        var symbolInfo = self.getSymbolInfo(symbol)
        var sumInitStocks = initSpAcc.Stocks + initSpAcc.FrozenStocks
        var sumNowStocks = nowSpAcc.Stocks + nowSpAcc.FrozenStocks
        var diffStocks = _N(sumNowStocks - sumInitStocks, symbolInfo.amountPrecision)
        if (Math.abs(diffStocks) < symbolInfo.min / price) {
            return []
        }
        return [{symbol: symbol, amount: diffStocks, price: null, originalInfo: {}}]
    }

    self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
        var tradeType = ""
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeType = "bid"
        } else {
            tradeType = "ask"
        }
        var params = {
            "market": symbol,
            "side": tradeType,
            "amount": String(amount),
            "price" : String(-1),
            "type" : "market"
        }
        self.routineTrade = self.e.Go("IO", "api", "POST", "/api/v1/private/order", self.encodeParams(params))
    }

    self.waitTrade = function waitTrade() {
        return self.routineTrade.wait()
    }

    self.calcAmount = function calcAmount(symbol, type, price, amount) {
        // 获取交易对信息
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbol) {
            throw symbol + ",交易对信息查询不到"
        }
        var tradeAmount = null 
        var equalAmount = null  // 记录币数
        if (type == self.OPEN_LONG || type == self.COVER_SHORT) {
            tradeAmount = _N(amount * price, parseFloat(symbolInfo.pricePrecision))
            // 检查最小交易量
            if (tradeAmount < symbolInfo.min) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min)
                return false 
            }
            equalAmount = tradeAmount / price
        } else {
            tradeAmount = _N(amount, parseFloat(symbolInfo.amountPrecision))
            // 检查最小交易量
            if (tradeAmount < symbolInfo.min / price) {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", symbolInfo.min / price)
                return false 
            }
            equalAmount = tradeAmount
        }
        return [tradeAmount, equalAmount]
    }

    self.init = function init() {
        var ret = JSON.parse(HttpQuery("https://api.wex.app/api/v1/public/markets"))
        _.each(ret.data, function(symbolInfo) {
            self.symbolsInfo.push({
                symbol: symbolInfo.pair,
                amountPrecision: parseFloat(symbolInfo.basePrecision),
                pricePrecision: parseFloat(symbolInfo.quotePrecision),
                multiplier: 1,
                min: parseFloat(symbolInfo.minQty),
                originalInfo: symbolInfo
            })
        })        
    }
}

// OKEX期货 V5 
function funcConfigure_Futures_OKEX_V5(self) {
    var formatSymbol = function(originalSymbol) {        
        var arr = originalSymbol.split("-")               // LTC-USD-SWAP
        return [arr[0] + "_" + arr[1], arr[0], arr[1]]
    }

    var getInstType = function() {
        var isSwap = null
        for (var i = 0 ; i < self.subscribeSymbols.length ; i++) {            
            if (i == 0) {
                isSwap = self.subscribeSymbols[i].includes("-SWAP")
            } else if (isSwap != self.subscribeSymbols[i].includes("-SWAP")) {
                throw "订阅的合约中混合了交割、永续"
            }
        }
        return isSwap ? "SWAP" : "FUTURES"
    }

    self.interfaceGetTickers = function interfaceGetTickers() {
        self.routineGetTicker = HttpQuery_Go("https://www.okex.com/api/v5/market/tickers?instType=" + getInstType())
    }  

    self.waitTickers = function waitTickers() {
        var ret = []
        var arr = JSON.parse(self.routineGetTicker.wait()).data
        _.each(arr, function(ele) {
            ret.push({
                bid1: parseFloat(ele.bidPx), 
                bid1Vol: parseFloat(ele.bidSz), 
                ask1: parseFloat(ele.askPx), 
                ask1Vol: parseFloat(ele.askSz), 
                symbol: formatSymbol(ele.instId)[0], 
                type: "Futures", 
                originalSymbol: ele.instId
            })
        })
        return ret 
    }

    self.interfaceGetAcc = function interfaceGetAcc(symbol, updateTS) {
        if (self.updateAccsTS != updateTS) {      // 如果新的一轮获取账户信息才去创建并发数据
            self.routineGetAcc = self.e.Go("IO", "api", "GET", "/api/v5/account/balance")
        }
    }  

    self.waitAcc = function waitAcc(symbol, updateTS) {
        var ret = null 
        if (self.updateAccsTS != updateTS) {      // 如果新的一轮获取账户信息才去获取并发数据
            ret = self.routineGetAcc.wait()    
            self.bufferGetAccRet = ret            // 由于交易所资产数据接口返回的是全部信息,避免重复调用,缓存 
        } else {                                  // 如果时间戳相等,说明是同一轮,用缓存数据
            ret = self.bufferGetAccRet
        }
        if (!ret) {
            return null 
        }
        var arrCurrencyName = formatSymbol(symbol)
        var quoteCurrency = arrCurrencyName[2]
        var baseCurrency = arrCurrencyName[1]
        var acc = null 
        _.each(ret.data, function(obj) {
            _.each(obj.details, function(detail) {
                if (detail.availEq == "") {
                    throw "Account-level too low"
                } else if (quoteCurrency == "USDT" && detail.ccy == quoteCurrency) {
                    acc = {symbol: symbol, Stocks: 0, FrozenStocks: 0, Balance: detail.availEq, FrozenBalance: detail.ordFrozen, originalInfo: ret}
                } else if (quoteCurrency == "USD" && detail.ccy == baseCurrency) {
                    acc = {symbol: symbol, Stocks: detail.availEq, FrozenStocks: detail.ordFrozen, Balance: 0, FrozenBalance: 0, originalInfo: ret}
                }
            })
        })
        return acc
    }  

    self.interfaceGetPos = function interfaceGetPos(symbol, updateTS) {    // 期货和现货的实现不一样
        var symbolInfo = self.getSymbolInfo(symbol)
        var arrCurrencyName = formatSymbol(symbol)
        var instType = "FUTURES"
        if (symbol.includes("-SWAP")) {
            instType = "SWAP"
        }
        var ret = self.e.IO("api", "GET", "/api/v5/account/positions", "instType=" + instType + "&instId=" + symbol)
        var positions = []
        // {"code":"0","data":[],"msg":""}
        _.each(ret.data, function(ele) {
            if (Math.abs(parseFloat(ele.pos)) > 0) {
                if (ele.posSide == "long" || (ele.posSide == "net" && parseFloat(ele.pos) > 0)) {
                    positions.push({symbol: ele.instId, amount: Math.abs(parseFloat(ele.pos)) * symbolInfo.multiplier, price: parseFloat(ele.avgPx), marginLevel: parseFloat(ele.lever), originalInfo: ele})
                } else if (ele.posSide == "short" || (ele.posSide == "net" && parseFloat(ele.pos) < 0)) {
                    positions.push({symbol: ele.instId, amount: -Math.abs(parseFloat(ele.pos)) * symbolInfo.multiplier, price: parseFloat(ele.avgPx), marginLevel: parseFloat(ele.lever), originalInfo: ele})
                }
            }         
        })
        return positions
    }

    self.interfaceTrade = function interfaceTrade(symbol, type, price, amount) {
    	var params = {
    		"instId" : symbol,
    		"tdMode" : "cross",
    		"ordType" : "market",
    		"sz" : String(amount)
    	}

    	switch(type) {
        case self.OPEN_LONG:
            params["side"] = "buy"
            params["posSide"] = "long"
            break
        case self.OPEN_SHORT: 
            params["side"] = "sell"
            params["posSide"] = "short"
            break
        case self.COVER_LONG: 
            params["side"] = "sell"
            params["posSide"] = "long"
            break        
        case self.COVER_SHORT:
            params["side"] = "buy"
            params["posSide"] = "short"
            break        
        default:
            throw "type error, type:" + type
    	}
        self.routineTrade = self.e.Go("IO", "api", "POST", "/api/v5/trade/order", self.encodeParams(params))
    }  

    self.waitTrade = function waitTrade() {
        return self.routineTrade.wait()
    }  

    self.calcAmount = function calcAmount(symbol, type, price, amount, isNotLog) {
        var symbolInfo = self.getSymbolInfo(symbol)
        if (!symbolInfo) {
        	throw symbol + ",交易对信息查询不到"
        }
        var tradeAmount = _N(amount / symbolInfo.multiplier, 0)        
        if (tradeAmount < parseFloat(symbolInfo.min)) {
            if (typeof(isNotLog) == "undefined") {
                Log(self.name, " tradeAmount:", tradeAmount, "小于", parseFloat(symbolInfo.min))
            }     	
        	return false 
        }
        return [tradeAmount, tradeAmount * symbolInfo.multiplier]
    }  
    // 实现设置杠杆接口
    self.interfaceSetMarginLevel = function interfaceSetMarginLevel(symbol, marginLevel) {
        return self.e.IO("api", "POST", "/api/v5/account/set-leverage", self.encodeParams({"instId" : symbol, "ccy" : "USDT", "lever" : String(marginLevel), "mgnMode" : "cross"}))
    }

    self.interfaceCalcProfit = function interfaceCalcProfit(arrInitAcc, arrNowAcc) {
    	var profit = 0
    	if (arrInitAcc.length > 0 && arrNowAcc.length > 0) {
    		var initEq = 0
    		_.each(arrInitAcc[0].originalInfo.data, function(obj) {
    			_.each(obj.details, function(detail) {
    				if (detail.availEq == "") {
    					throw "Account-level too low"
    				} else if (detail.ccy == "USDT") {
    					initEq = detail.eq
    				}
    			})
    		})
    		var nowEq = 0
    		_.each(arrNowAcc[0].originalInfo.data, function(obj) {
    			_.each(obj.details, function(detail) {
    				if (detail.availEq == "") {
    					throw "Account-level too low"
    				} else if (detail.ccy == "USDT") {
    					nowEq = detail.eq
    				}
    			})
    		})
    		return nowEq - initEq
    	} else {
    		Log("计算收益失败", arrInitAcc, arrNowAcc)
    		return 
    	}
        return profit
    }  

    self.init = function init() {
        _.each(["SWAP", "FUTURES"], function(instType) {          // 永续、交割合约信息都获取
            var ret = JSON.parse(HttpQuery("https://www.okex.com/api/v5/public/instruments?instType=" + instType))
            _.each(ret.data, function(symbolInfo) {
                self.symbolsInfo.push({
                    symbol: symbolInfo.instId,
                    amountPrecision: self.judgePrecision(parseFloat(symbolInfo.lotSz)),
                    pricePrecision: self.judgePrecision(parseFloat(symbolInfo.tickSz)),
                    multiplier: parseFloat(symbolInfo.ctVal),
                    min: parseFloat(symbolInfo.minSz),                                                                      // OKEX 合约最小下1张
                    originalInfo: symbolInfo
                })
            })
        })
    }
}

// 交易所对象构造函数
function createBaseEx(e, funcConfigure) {
    var self = {}
    self.e = e 
    self.funcConfigure = funcConfigure
    self.name = e.GetName()
    self.type = self.name.includes("Futures_") ? "Futures" : "Spot"
    self.label = e.GetLabel()
    self.fuMarginLevel = null 

    // 数据成员
    self.subscribeSymbols = []     // 订阅的标的物
    self.bufferTickers = []        // 缓存市场全部行情数据
    self.bufferAccs = []           // 缓存订阅的账户资产数据
    self.bufferPositions = []      // 缓存订阅的持仓数据
    self.symbolsInfo = []          // 记录市场所有标的物信息数组
    self.bufferGetAccRet = null    // 访问资产信息接口缓存数据
    self.bufferGetPosRet = null    // 访问持仓信息接口缓存数据

    // 更新时间戳
    self.updateAccsTS = 0          // 更新账户信息的时间戳
    self.updatePosTS = 0           // 更新持仓信息的时间戳

    // 并发线程
    self.routineGetTicker = null 
    self.routineGetAcc = null 
    self.routineTrade = null 

    // 枚举
    self.OPEN_LONG = 0
    self.OPEN_SHORT = 1
    self.COVER_LONG = 2
    self.COVER_SHORT = 3

    // 需要实现的接口列表
    var configList = ["interfaceGetTickers", "interfaceGetAcc", "interfaceGetPos", "interfaceTrade", 
        "waitTickers", "waitAcc", "waitTrade", "calcAmount", "init"]
    if (self.type == "Futures") {
        configList.push("interfaceSetMarginLevel")   // 期货还需要设置杠杆接口
        configList.push("interfaceCalcProfit")       // 期货需要计算收益接口
    }
    // 需要实现的接口
    self.interfaceGetTickers = null   // 创建异步获取聚合行情数据线程的函数
    self.interfaceGetAcc = null       // 创建异步获取账户数据线程的函数
    self.interfaceGetPos = null       // 获取持仓
    self.interfaceTrade = null        // 创建并发下单
    self.waitTickers = null           // 等待并发行情数据 
    self.waitAcc = null               // 等待账户并发数据
    self.waitTrade = null             // 等待下单并发数据
    self.calcAmount = null            // 根据交易对精度等数据计算下单量
    self.init = null                  // 初始化工作,获取精度等数据

    // 回调函数
    self.callBack_getTrade = null     // 执行getTrade 时需要的回调

    // 判断精度
    self.judgePrecision = function(p) {
        var arr = p.toString().split(".")
        if (arr.length != 2) {
            if (arr.length == 1) {
                return 0
            }
            throw "judgePrecision error, p:" + String(p)
        }
        return arr[1].length
    }

    // 获取某个交易对的信息
    self.getSymbolInfo = function(symbol) {
        var ret = null 
        _.each(self.symbolsInfo, function(info) {
            if (info.symbol == symbol) {
                ret = info
            }
        })
        return ret 
    }

    self.getExName = function() {    // 获取交易所对象的名称
        return self.name
    }

    self.pushInArr = function(arr, obj, attributeName) {
        var isFind = false 
        _.each(arr, function(ele) {
        	if (obj[attributeName] == ele[attributeName]) {
        		ele = obj
        		isFind = true 
        	}
        })
        if (!isFind) {
        	arr.push(obj)
        }
    }

    // encodeParams
    self.encodeParams = function(params) {
        var ret = ""
        var index = 0
        for (var key in params) {
            if (index == 0) {
                ret += key + "=" + params[key]
            } else {
                ret += "&" + key + "=" + params[key]
            }            
            index++
        }
        return ret 
    }

    // 成员函数        
    self.tryProcess = function(tryFunc, args) {   // 发生异常返回空值,正常返回tryFunc函数调用结果
        var ret = null 
        try {
            ret = tryFunc.apply(this, args)
        } catch (err) {
            var funcName = tryFunc.toString().match(/function\s*([^(]*)\(/)[1]   // 获取函数名称
            Log("错误:", err, " 错误信息:", typeof(err.message) == "string" ? err.message : "", " 异常函数名:", funcName, " 参数:", typeof(args) == "undefined" ? "无" : args)    // 输出引发异常的函数信息
            return null
        }
        return ret 
    }

    // 写入订阅品种到 subscribeSymbols
    self.pushSubscribeSymbol = function(symbol) {    // 只在初始化时调用,写入订阅合约,用于接口中判断订阅的合约
        self.subscribeSymbols.push(symbol)
    }

    // 创建获取行情并发
    self.goGetTickers = function() {                 
        self.interfaceGetTickers()
    }

    // 获取并发行情数据
    self.getTickers = function() {                   // 取并发结果,使用tryProcess 如果发生异常会报错,不会导致程序停止
        var ret = self.tryProcess(self.waitTickers)
        // 缓存数据
        if (ret) {
            self.bufferTickers = ret
        }
        return ret 
    }

    // 创建下单并发
    self.goGetTrade = function(symbol, type, price, amount) {      // 创建并发下单,不检测异常
        self.interfaceTrade(symbol, type, price, amount)
        // 设置回调
        self.callBack_getTrade = function(ret) {
            if (self.type == "Futures") {
                if (type == self.OPEN_LONG || type == self.OPEN_SHORT) {    
                    self.e.SetDirection(type == self.OPEN_SHORT ? "sell" : "buy")                   // 正套期货做空
                } else {
                    self.e.SetDirection(type == self.COVER_SHORT ? "closesell" : "closebuy")        // 平正套期货平空仓
                }
            }
            self.e.Log((type == self.OPEN_LONG || type == self.COVER_SHORT) ? LOG_TYPE_BUY : LOG_TYPE_SELL, price, amount, symbol, ret)
        }
    }

    // 获取下单结果
    self.getTrade = function() {
        var retTradeMsg = self.tryProcess(self.waitTrade)
        if (retTradeMsg) {
            self.callBack_getTrade(retTradeMsg)
        }
        return retTradeMsg
    }

    // 创建获取单个资产并发
    self.goGetAcc = function(symbol, updateTS) {
        self.interfaceGetAcc(symbol, updateTS)
    }

    // 获取单个标的物相关资产信息
    self.getAcc = function(symbol, updateTS) {
        var ret = self.tryProcess(self.waitAcc, [symbol, updateTS])
        // 缓存数据
        if (ret) {
            // 更新 bufferAccs 中的数据
            self.pushInArr(self.bufferAccs, ret, "symbol")
            self.updateAccsTS = updateTS    // 更新,记录时间戳,对于逐个账户信息获取的接口不影响(具体看接口实现)
        }
        return ret
    }

    self.getFuPos = function(symbol, updateTS) {                               // 获取期货持仓
        var ret = self.tryProcess(self.interfaceGetPos, [symbol, updateTS])
        if (ret) {
        	_.each(ret, function(pos) {
        		self.pushInArr(self.bufferPositions, pos, "symbol")
        	})
            self.updatePosTS = updateTS
        }
        return ret 
    }

    self.getSpPos = function(symbol, price, initSpAcc, nowSpAcc) {   // 获取现货持仓
    	var ret = self.tryProcess(self.interfaceGetPos, [symbol, price, initSpAcc, nowSpAcc])
        if (ret) {
        	// self.bufferPositions = ret        	
        	_.each(ret, function(pos) {
        		self.pushInArr(self.bufferPositions, pos, "symbol")
        	})
        }
        if (ret.length > 1) {
        	Log("现货持仓返回数据错误!", JSON.stringify(ret))
        }
        return ret
    }

    self.setMarginLevel = function(symbol, marginLevel) {    	
    	var ret = self.tryProcess(self.interfaceSetMarginLevel, [symbol, marginLevel])
    	if (ret) {
    		self.fuMarginLevel = marginLevel
    	}
    	return ret 
    }

    self.calcProfit = function(arrInitAcc, arrNowAcc) {
    	if (self.type == "Futures") {    		
			return self.tryProcess(self.interfaceCalcProfit, [arrInitAcc, arrNowAcc])    		
    	} else {
    		throw "调用错误"
    	}
    }

    // 执行配置函数,给对象配置
    funcConfigure(self)

    // 检测configList约定的接口是否都实现
    _.each(configList, function(funcName) {
        if (!self[funcName]) {
            throw "接口" + funcName + "未实现"
        }
    })

    // self.tryProcess(self.init)    // 执行接口配置中实现的init函数
    self.init()                      // 不做异常检测,发生错误程序停止,执行接口配置中实现的init函数
    return self
}

// 导出函数
$.createBaseEx = createBaseEx
$.getConfigureFunc = function(exName) {
    dicRegister = {
        "Futures_OKCoin" : funcConfigure_Futures_OKCoin,
        "Huobi" : funcConfigure_Huobi,
        "Futures_Binance" : funcConfigure_Futures_Binance,
        "Binance" : funcConfigure_Binance,
        "WexApp" : funcConfigure_WexApp,
        "Futures_OKCoin_V5" : funcConfigure_Futures_OKEX_V5,        // exchange.GetName() + "_V5"
    }
    if (typeof(exName) != "undefined" && !dicRegister[exName]) {
        throw "未找到匹配的交易所配置函数"
    }
    return dicRegister
}

// 测试函数
function main() {
    // 切换为模拟盘环境
    // exchange.IO("simulate", true)   // OKEX V5 切换模拟盘

    var fuExName = exchange.GetName() + "_V5"    // 获取交易所名称,这里设置为OKEX V5
    Log("测试的交易所名称为:", fuExName)
    var fuConfigureFunc = $.getConfigureFunc()[fuExName]
    var ex = $.createBaseEx(exchange, fuConfigureFunc)

    var arrTestSymbol = ["ETH-USDT-211231"]
    var ts = new Date().getTime()
    // 设置需要订阅的合约
    // /*
    _.each(arrTestSymbol, function(symbol) {
        ex.pushSubscribeSymbol(symbol)
    })
    // */

    _.each(arrTestSymbol, function(symbol) {
        // 测试获取行情
        /*
        ex.goGetTickers()
        var tickers = ex.getTickers()
        Log("tickers:", tickers)
        _.each(tickers, function(ticker) {
            if (symbol == ticker.originalSymbol) {
                Log(symbol, ticker)
            }
        })
        */

        // 测试获取账户信息
        // /*
        ex.goGetAcc(symbol, ts)
        var acc = ex.getAcc(symbol, ts)
        Log("acc:", acc.symbol, acc)

        var arr1 = [acc]
        var arr2 = [acc]
        Log(ex.interfaceCalcProfit(arr1, arr2))
        // */

        // 测试获取持仓信息
        /*
        var pos = ex.getFuPos(symbol)  // 期货持仓
        Log("pos:", symbol, pos)
        // 打印交易对信息
        _.each(ex.symbolsInfo, function(symbolInfo) {
            if (_.contains(arrTestSymbol, symbolInfo.symbol)) {
                Log("symbolInfo:", symbolInfo)
            }
        })
        */

        // 下单测试
        /*
        if (symbol == "LTC-USDT-SWAP") {
            var price = 170 + 170 * 0.02
            var amount = 1
            var tradeType = ex.OPEN_LONG
            var retCalc = ex.calcAmount(symbol, tradeType, price, amount)
            if (retCalc) {
                Log("retCalc:", retCalc)
                Log("symbolInfo:", ex.getSymbolInfo(symbol))
                var setMarginLevelRet = ex.setMarginLevel(symbol, 5)
                Log("setMarginLevelRet:", setMarginLevelRet)  // 打印持仓设置
                ex.goGetTrade(symbol, tradeType, price, retCalc[0])    // 测试开多
                // ex.goGetTrade(symbol, ex.COVER_LONG, -1, 0.004)   // 测试平多
                var ret = ex.getTrade()
                Log(symbol, "trade ret:", ret)
            }
        }
        */

        /* 单独测试杠杆
        var ret = ex.setMarginLevel(symbol, 35)
        Log(ret)
        */

        /*
        if (symbol == "LTCUSDT") {
            var price = 230
            var amount = 0.01
            var tradeType = ex.OPEN_SHORT
            var retCalc = ex.calcAmount(symbol, tradeType, price, amount)
            if (retCalc) {
                Log("symbolInfo:", ex.getSymbolInfo(symbol))
                // ex.setMarginLevel(symbol, 5)
                ex.goGetTrade(symbol, tradeType, price, retCalc[0])    // 测试开多
                // ex.goGetTrade(symbol, ex.COVER_LONG, -1, 0.004)   // 测试平多
                var ret = ex.getTrade()
                Log(symbol, "trade ret:", ret)
            }
        }
        */
    })
}



もっと