2022-05-20 14:42:13 +08:00
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Name
|
2022-05-20 14:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
MultiSymbolCtrlLib
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Author
|
2022-05-20 14:42:13 +08:00
|
|
|
|
|
2025-04-30 11:10:28 +08:00
|
|
|
|
发明者量化-小小梦
|
2022-05-20 14:42:13 +08:00
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Strategy Description
|
2022-05-20 14:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
## 说明
|
|
|
|
|
|
|
|
|
|
|
|
文章https://www.fmz.com/digest-topic/7373中使用的模板
|
|
|
|
|
|
代码仅供参考学习,实盘慎用。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Source (javascript)
|
2022-05-20 14:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
``` javascript
|
|
|
|
|
|
// 交易所接口调用实现
|
|
|
|
|
|
// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
*/
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Detail
|
2022-05-20 14:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
https://www.fmz.com/strategy/276298
|
|
|
|
|
|
|
2023-07-14 18:39:48 +08:00
|
|
|
|
> Last Modified
|
2022-05-20 14:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
2022-05-18 11:35:37
|