@azteam/huobi-api
Version:
N/A
505 lines (442 loc) • 16.4 kB
JavaScript
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;