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
JavaScript
"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