UNPKG

bybit-api-gnome

Version:

Forked for Lick Hunter, Complete & robust node.js SDK for Bybit's REST APIs and WebSockets v5, with TypeScript & integration tests.

249 lines 8.57 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const axios_1 = __importDefault(require("axios")); const node_support_1 = require("./util/node-support"); const requestUtils_1 = require("./util/requestUtils"); const logger_1 = require("./util/logger"); class RestClientV5 { constructor(restOptions = {}, networkOptions = {}) { this.timeOffset = null; this.syncTimePromise = null; this.options = { recv_window: 5000, strict_param_validation: false, enable_time_sync: false, sync_interval_ms: 3600000, ...restOptions, }; this.globalRequestOptions = { timeout: 1000 * 60 * 5, // 5 minutes timeout ...networkOptions, headers: { 'X-Referer': requestUtils_1.APIID, 'Content-Type': 'application/json', ...networkOptions.headers, }, }; this.baseUrl = this.getV5BaseUrl(); this.key = this.options.key; this.secret = this.options.secret; if (this.key && !this.secret) { throw new Error('API Key & Secret are both required for private endpoints'); } if (this.options.enable_time_sync) { this.syncTime(); setInterval(this.syncTime.bind(this), +this.options.sync_interval_ms); } } getV5BaseUrl() { if (this.options.baseUrl) { return this.options.baseUrl; } if (this.options.testnet) { return 'https://api-testnet.bybit.com'; } return 'https://api.bybit.com'; } // Public API methods get(endpoint, params) { return this._call('GET', endpoint, params, true); } // Private API methods getPrivate(endpoint, params) { return this._call('GET', endpoint, params, false); } post(endpoint, params) { return this._call('POST', endpoint, params, true); } postPrivate(endpoint, params) { return this._call('POST', endpoint, params, false); } deletePrivate(endpoint, params) { return this._call('DELETE', endpoint, params, false); } // Market Data endpoints getServerTime() { return this.get('/v5/market/time'); } getKline(params) { return this.get('/v5/market/kline', params); } getOrderbook(params) { return this.get('/v5/market/orderbook', params); } getTickers(params) { return this.get('/v5/market/tickers', params); } getInstrumentsInfo(params) { return this.get('/v5/market/instruments-info', params); } // Trading endpoints (private) placeOrder(params) { return this.postPrivate('/v5/order/create', params); } cancelOrder(params) { return this.postPrivate('/v5/order/cancel', params); } getOrders(params) { return this.getPrivate('/v5/order/realtime', params); } getOrderHistory(params) { return this.getPrivate('/v5/order/history', params); } // Position endpoints getPositionInfo(params) { return this.getPrivate('/v5/position/list', params); } setLeverage(params) { return this.postPrivate('/v5/position/set-leverage', params); } // Account endpoints getWalletBalance(params) { return this.getPrivate('/v5/account/wallet-balance', params); } getFeeRate(params) { return this.getPrivate('/v5/account/fee-rate', params); } // Asset endpoints getCoinBalance(params) { return this.getPrivate('/v5/asset/coin/query-info', params); } getAllCoinBalance(params) { return this.getPrivate('/v5/asset/account/query-coin-balance', params); } async buildRequest(method, url, params, isPublicApi) { const options = { ...this.globalRequestOptions, url: url, method: method, }; // Remove undefined params if (params) { for (const key in params) { if (typeof params[key] === 'undefined') { delete params[key]; } } } if (isPublicApi) { if (method === 'GET') { return { ...options, params: params, }; } return { ...options, data: params, }; } // Private endpoints require authentication if (!this.key || !this.secret) { throw new Error('Private endpoints require API key and secret'); } if (this.timeOffset === null) { await this.syncTime(); } const timestamp = Date.now() + (this.timeOffset || 0); const recvWindow = this.options.recv_window || 5000; if (!options.headers) { options.headers = {}; } options.headers['X-BAPI-API-KEY'] = this.key; options.headers['X-BAPI-TIMESTAMP'] = timestamp.toString(); options.headers['X-BAPI-RECV-WINDOW'] = recvWindow.toString(); // Create signature let queryString = ''; if (method === 'GET' && params) { queryString = Object.keys(params) .sort() .map(key => `${key}=${params[key]}`) .join('&'); } else if (method !== 'GET' && params) { queryString = JSON.stringify(params); } const signaturePayload = timestamp + this.key + recvWindow + queryString; const signature = await (0, node_support_1.signMessage)(signaturePayload, this.secret); options.headers['X-BAPI-SIGN'] = signature; if (method === 'GET') { return { ...options, params: params, }; } return { ...options, data: params, }; } async _call(method, endpoint, params, isPublicApi) { const requestUrl = [this.baseUrl, endpoint].join(endpoint.startsWith('/') ? '' : '/'); const options = await this.buildRequest(method, requestUrl, params, isPublicApi); return (0, axios_1.default)(options) .then((response) => { if (response.status === 200) { return response.data; } throw response; }) .catch((e) => this.parseException(e)); } parseException(e) { if (this.options.parse_exceptions === false) { throw e; } if (!e.response) { if (!e.request) { throw new Error(e.message || 'Unknown error'); } throw new Error('Request failed: no response received'); } const response = e.response; const error = { code: response.status, message: response.statusText, body: response.data, headers: response.headers, requestOptions: this.options, }; throw error; } async syncTime(force) { if (!force && !this.options.enable_time_sync) { this.timeOffset = 0; return Promise.resolve(false); } if (this.syncTimePromise !== null) { return this.syncTimePromise; } this.syncTimePromise = this.fetchTimeOffset().then((offset) => { this.timeOffset = offset; this.syncTimePromise = null; }); return this.syncTimePromise; } async fetchTimeOffset() { var _a; try { const start = Date.now(); const serverTimeResponse = await this.getServerTime(); if (!((_a = serverTimeResponse === null || serverTimeResponse === void 0 ? void 0 : serverTimeResponse.result) === null || _a === void 0 ? void 0 : _a.timeSecond)) { throw new Error('Invalid server time response'); } const serverTime = parseInt(serverTimeResponse.result.timeSecond) * 1000; const end = Date.now(); const avgDrift = (end - start) / 2; return Math.ceil(serverTime - end + avgDrift); } catch (e) { logger_1.DefaultLogger.error('Failed to fetch time offset: ', e); return 0; } } } exports.default = RestClientV5; //# sourceMappingURL=rest-client-v5.js.map