UNPKG

@azteam/huobi-api

Version:

N/A

505 lines (442 loc) 16.4 kB
import crypto from 'crypto'; import _ from 'lodash'; import {TIME_CHART, TREND} from '@azteam/candlestick-chart'; import HttpClient from '@azteam/http-client'; const SPOT_ENDPOINT = 'api-aws.huobi.pro'; const LINEAR_SWAP_ENDPOINT = 'api.btcgateway.pro'; class HuobiAPI { constructor(accessKey, secretKey) { this.client = new HttpClient({ timeout: 20000, }); this.accessKey = accessKey; this.secretKey = secretKey; } static async getLinearSwapHistory(symbol, chartTime, to = null) { // eslint-disable-next-line no-param-reassign const size = 1900; const deviation = 22; let endpoint = `https://${LINEAR_SWAP_ENDPOINT}/linear-swap-ex/market/history/kline?period=${chartTime}&size=2000&contract_code=${symbol}`; if (to) { let from = to; if (chartTime === TIME_CHART._1_MIN.name) { from -= TIME_CHART._1_MIN.period * (size + deviation); } else if (chartTime === TIME_CHART._5_MIN.name) { from -= TIME_CHART._5_MIN.period * (size + deviation); } else if (chartTime === TIME_CHART._15_MIN.name) { from -= TIME_CHART._15_MIN.period * (size + deviation); } else if (chartTime === TIME_CHART._30_MIN.name) { from -= TIME_CHART._30_MIN.period * (size + deviation); } else if (chartTime === TIME_CHART._60_MIN.name) { from -= TIME_CHART._60_MIN.period * (size + deviation); } else if (chartTime === TIME_CHART._4_HOUR.name) { from -= TIME_CHART._4_HOUR.period * (size + deviation); } else if (chartTime === TIME_CHART._1_DAY.name) { from -= TIME_CHART._1_DAY.period * (size + deviation); } endpoint = `https://${LINEAR_SWAP_ENDPOINT}/linear-swap-ex/market/history/kline?period=${chartTime}&from=${from}&to=${to}&contract_code=${symbol}`; } const client = new HttpClient({ timeout: 20000, }); const res = await client.get(endpoint); if (res.status === 'ok') { return res.data.reverse(); } throw new Error(res['err-msg']); } static async getContractInfo() { const client = new HttpClient({ timeout: 20000, }); const res = await client.get(`https://${LINEAR_SWAP_ENDPOINT}/linear-swap-api/v1/swap_contract_info`); if (res.status === 'ok') { return res.data.filter((obj) => obj.support_margin_mode === 'all'); } throw new Error(res['err-msg']); } _generateQueryString(params) { const data = { ...params, Timestamp: new Date().toISOString().replace(/\..+/, ''), SignatureMethod: 'HmacSHA256', SignatureVersion: 2, AccessKeyId: this.accessKey, }; return Object.keys(data) .sort((a, b) => (a > b ? 1 : -1)) .reduce(function (a, k) { a.push(`${k}=${encodeURIComponent(data[k])}`); return a; }, []) .join('&'); } _encodeSpotURL(method, version, uri, params = {}) { const queryString = this._generateQueryString(params); const source = `${method}\n${SPOT_ENDPOINT}\n/${version}/${uri}\n${queryString}`; const signature = encodeURIComponent(crypto.createHmac('sha256', this.secretKey).update(String(source)).digest('base64')); return `https://${SPOT_ENDPOINT}/${version}/${uri}?${queryString}&Signature=${signature}`; } _encodeLinearSwapURL(method, version, uri, params = {}) { const queryString = this._generateQueryString(params); const source = `${method}\n${LINEAR_SWAP_ENDPOINT}\n/linear-swap-api/${version}/${uri}\n${queryString}`; const signature = encodeURIComponent(crypto.createHmac('sha256', this.secretKey).update(String(source)).digest('base64')); return `https://${LINEAR_SWAP_ENDPOINT}/linear-swap-api/${version}/${uri}?${queryString}&Signature=${signature}`; } async transferCrossToSpot(amount) { const url = this._encodeSpotURL('POST', 'v2', `account/transfer`); const res = await this.client.post( url, { amount, from: 'linear-swap', to: 'spot', currency: 'USDT', 'margin-account': 'USDT', }, false ); if (res.success === true) { return true; } throw new Error(res.message); } async transferSpotToCross(amount) { const url = this._encodeSpotURL('POST', 'v2', `account/transfer`); const res = await this.client.post( url, { amount, from: 'spot', to: 'linear-swap', currency: 'USDT', 'margin-account': 'USDT', }, false ); if (res.success === true) { return true; } throw new Error(res.message); } async transferSpotToIsolated(amount, symbol) { const url = this._encodeSpotURL('POST', 'v2', `account/transfer`); const res = await this.client.post( url, { amount, from: 'spot', to: 'linear-swap', currency: 'USDT', 'margin-account': symbol, }, false ); if (res.success === true) { return true; } throw new Error(res.message); } async transferIsolatedToSpot(amount, symbol) { const url = this._encodeSpotURL('POST', 'v2', `account/transfer`); const res = await this.client.post( url, { amount, from: 'linear-swap', to: 'spot', currency: 'USDT', 'margin-account': symbol, }, false ); if (res.success === true) { return true; } throw new Error(res.message); } async getSpotAccount() { const url = this._encodeSpotURL('GET', 'v1', 'account/accounts'); const res = await this.client.get(url); if (res.status === 'ok') { return res.data.find((account) => account.type === 'spot'); } throw new Error(res['err-msg']); } async getSpotBalance(currency = 'usdt') { const spotAccount = await this.getSpotAccount(); const url = this._encodeSpotURL('GET', 'v1', `account/accounts/${spotAccount.id}/balance`); const res = await this.client.get(url); if (res.status === 'ok') { const {balance} = res.data.list.find((coin) => coin.currency === currency); return _.floor(balance, 4); } throw new Error(res.err_msg); } async getCrossBalance() { const url = this._encodeLinearSwapURL('POST', 'v1', `swap_cross_account_info`); const res = await this.client.post(url, {}, false); if (res.status === 'ok') { const balance = res.data[0].margin_balance; return _.floor(balance, 4); } throw new Error(res.err_msg); } async getIsolatedBalance(symbol) { const url = this._encodeLinearSwapURL('POST', 'v1', `swap_account_info`); const res = await this.client.post( url, { contract_code: symbol, }, false ); if (res.status === 'ok') { const balance = res.data[0].margin_static; return _.floor(balance, 4); } throw new Error(res.err_msg); } async _setLeverRate(symbol, leverRate, uri) { const url = this._encodeLinearSwapURL('POST', 'v1', uri); const res = await this.client.post( url, { contract_code: symbol, lever_rate: leverRate, }, false ); if (res.status === 'ok') { return res.data; } throw new Error(res.err_msg); } async setCrossLeverRate(symbol, leverRate) { return this._setLeverRate(symbol, leverRate, 'swap_cross_switch_lever_rate'); } async setIsolatedLeverRate(symbol, leverRate) { return this._setLeverRate(symbol, leverRate, 'swap_switch_lever_rate'); } async _setOrder(symbol, leverRate, clientOrderId, direction, volume, openPrice, takeProfitPrice, stopLossPrice, offset, uri) { const data = { offset, order_price_type: 'market', contract_code: symbol, client_order_id: clientOrderId, volume, direction, lever_rate: leverRate, }; if (openPrice) { data.order_price_type = 'limit'; data.price = openPrice; } if (takeProfitPrice) { data.tp_trigger_price = takeProfitPrice; data.tp_order_price_type = 'optimal_20'; } if (stopLossPrice) { data.sl_trigger_price = stopLossPrice; data.sl_order_price_type = 'optimal_20'; } const url = this._encodeLinearSwapURL('POST', 'v1', uri); const res = await this.client.parseBigInt().post(url, data, false); if (res.status === 'ok') { return res.data; } throw new Error(res.err_msg); } async setCrossOpenOrder(symbol, leverRate, clientOrderId, type, volume, openPrice, takeProfitPrice = null, stopLossPrice = null) { let direction = null; if (type === TREND.LONG) { direction = 'buy'; } else if (type === TREND.SHORT) { direction = 'sell'; } return this._setOrder( symbol, leverRate, clientOrderId, direction, volume, openPrice, takeProfitPrice, stopLossPrice, 'open', 'swap_cross_order' ); } async setIsolatedOpenOrder(symbol, leverRate, clientOrderId, type, volume, openPrice, takeProfitPrice = null, stopLossPrice = null) { let direction = null; if (type === TREND.LONG) { direction = 'buy'; } else if (type === TREND.SHORT) { direction = 'sell'; } return this._setOrder(symbol, leverRate, clientOrderId, direction, volume, openPrice, takeProfitPrice, stopLossPrice, 'open', 'swap_order'); } async setCrossCloseOrder(symbol, leverRate, clientOrderId, type, volume, openPrice, takeProfitPrice = null, stopLossPrice = null) { let direction = null; if (type === TREND.LONG) { direction = 'sell'; } else if (type === TREND.SHORT) { direction = 'buy'; } return this._setOrder( symbol, leverRate, clientOrderId, direction, volume, openPrice, takeProfitPrice, stopLossPrice, 'close', 'swap_cross_order' ); } async setIsolatedCloseOrder(symbol, leverRate, clientOrderId, type, volume, openPrice, takeProfitPrice = null, stopLossPrice = null) { let direction = null; if (type === TREND.LONG) { direction = 'sell'; } else if (type === TREND.SHORT) { direction = 'buy'; } return this._setOrder(symbol, leverRate, clientOrderId, direction, volume, openPrice, takeProfitPrice, stopLossPrice, 'close', 'swap_order'); } async _getOrder(symbol, clientOrderId, uri) { const url = this._encodeLinearSwapURL('POST', 'v1', uri); const res = await this.client.parseBigInt().post( url, { client_order_id: clientOrderId, contract_code: symbol, }, false ); if (res.status === 'ok') { return res.data; } throw new Error(res.err_msg); } async getCrossOrder(symbol, clientOrderId) { return this._getOrder(symbol, clientOrderId, 'swap_cross_order_info'); } async getIsolatedOrder(symbol, clientOrderId) { return this._getOrder(symbol, clientOrderId, 'swap_order_info'); } async _setCancelOrder(symbol, clientOrderId, uri) { const url = this._encodeLinearSwapURL('POST', 'v1', uri); const res = await this.client.parseBigInt().post( url, { contract_code: symbol, client_order_id: clientOrderId, }, false ); if (res.status === 'ok') { return res.data; } throw new Error(res.err_msg); } async setCrossCancelOrder(symbol, clientOrderId) { return this._setCancelOrder(symbol, clientOrderId, 'swap_cross_cancel'); } async setIsolatedCancelOrder(symbol, clientOrderId) { return this._setCancelOrder(symbol, clientOrderId, 'swap_cancel'); } async _setCancelAllOpenOrder(symbol, uri) { const url = this._encodeLinearSwapURL('POST', 'v1', uri); const res = await this.client.parseBigInt().post( url, { contract_code: symbol, offset: 'open', }, false ); if (res.status === 'ok') { return res.data; } const errorString = res.err_msg.toString(); if (errorString.includes('No cancellable orders')) { return { successes: true, }; } throw new Error(errorString); } async setCrossCancelAllOpenOrder(symbol) { return this._setCancelAllOpenOrder(symbol, 'swap_cross_cancelall'); } async setIsolatedCancelAllOpenOrder(symbol) { return this._setCancelAllOpenOrder(symbol, 'swap_cancelall'); } async _getPositionInfo(symbol, uri) { const url = this._encodeLinearSwapURL('POST', 'v1', uri); const res = await this.client.post( url, { contract_code: symbol, }, false ); if (res.status === 'ok') { return res.data; } throw new Error(res.err_msg); } async getCrossPositionInfo(symbol) { return this._getPositionInfo(symbol, 'swap_cross_position_info'); } async getIsolatedPositionInfo(symbol) { return this._getPositionInfo(symbol, 'swap_position_info'); } async _setLightningClose(symbol, direction, clientOrderId, uri) { const url = this._encodeLinearSwapURL('POST', 'v1', uri); const data = { contract_code: symbol, direction: direction === 'sell' ? 'buy' : 'sell', }; if (clientOrderId) { data.client_order_id = clientOrderId; } const res = await this.client.parseBigInt().post(url, data, false); if (res.status === 'ok') { return res.data; } throw new Error(res.err_msg); } async setCrossLightningClose(symbol, direction, clientId = null) { return this._setLightningClose(symbol, direction, clientId, 'swap_cross_lightning_close_position'); } async setIsolatedLightningClose(symbol, direction, clientId = null) { return this._setLightningClose(symbol, direction, clientId, 'swap_lightning_close_position'); } async _getHistoryOrders(symbol, uri) { const url = this._encodeLinearSwapURL('POST', 'v3', uri); const res = await this.client.parseBigInt().post( url, { contract: symbol, trade_type: 0, type: 1, status: 0, }, false ); if (res.msg === 'ok') { return res.data; } throw new Error(res.err_msg); } async getCrossHistoryOrders(symbol) { return this._getHistoryOrders(symbol, 'swap_cross_hisorders'); } async getIsolatedHistoryOrders(symbol) { return this._getHistoryOrders(symbol, 'swap_hisorders'); } } export default HuobiAPI;