bybit-api
Advanced tools
Comparing version 1.2.5 to 1.3.0-beta.0
10
index.js
@@ -1,9 +0,1 @@ | ||
const RestClient = require('./lib/rest-client'); | ||
const WebsocketClient = require('./lib/websocket-client'); | ||
const DefaultLogger = require('./lib/logger'); | ||
module.exports = { | ||
RestClient, | ||
WebsocketClient, | ||
DefaultLogger | ||
}; | ||
module.exports = require('lib/index'); |
@@ -1,9 +0,24 @@ | ||
module.exports = { | ||
silly: function() {console.log(arguments);}, | ||
debug: function() {console.log(arguments);}, | ||
notice: function() {console.log(arguments);}, | ||
info: function() {console.info(arguments);}, | ||
warning: function() {console.warn(arguments);}, | ||
error: function() {console.error(arguments);}, | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.DefaultLogger = void 0; | ||
exports.DefaultLogger = { | ||
silly: (...params) => { | ||
console.log(params); | ||
}, | ||
debug: (...params) => { | ||
console.log(params); | ||
}, | ||
notice: (...params) => { | ||
console.log(params); | ||
}, | ||
info: (...params) => { | ||
console.info(params); | ||
}, | ||
warning: (...params) => { | ||
console.error(params); | ||
}, | ||
error: (...params) => { | ||
console.error(params); | ||
} | ||
}; | ||
//# sourceMappingURL=logger.js.map |
@@ -1,346 +0,290 @@ | ||
const assert = require('assert'); | ||
const RequestWrapper = require('./util/requestWrapper'); | ||
module.exports = class RestClient { | ||
/** | ||
* @public Creates an instance of the inverse REST API client. | ||
* | ||
* @param {string} key - your API key | ||
* @param {string} secret - your API secret | ||
* @param {boolean} [livenet=false] | ||
* @param {*} [options={}] options to configure REST API connectivity | ||
* @param {*} [requestOptions={}] HTTP networking options for axios | ||
*/ | ||
constructor(key, secret, livenet=false, options={}, requestOptions={}) { | ||
this.request = new RequestWrapper(...arguments); | ||
} | ||
async placeActiveOrder(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.side, 'Parameter side is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.order_type, 'Parameter order_type is required'); | ||
assert(params.qty, 'Parameter qty is required'); | ||
assert(params.time_in_force, 'Parameter time_in_force is required'); | ||
if (params.order_type === 'Limit') assert(params.price, 'Parameter price is required for limit orders'); | ||
return await this.request.post('v2/private/order/create', params); | ||
} | ||
async getActiveOrder(params) { | ||
return await this.request.get('open-api/order/list', params); | ||
} | ||
async cancelActiveOrder(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.order_id || params.order_link_id, 'Parameter order_id OR order_link_id is required'); | ||
return await this.request.post('v2/private/order/cancel', params); | ||
} | ||
async cancelAllActiveOrders(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.post('v2/private/order/cancelAll', params); | ||
} | ||
async replaceActiveOrder(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.order_id || params.order_link_id, 'Parameter order_id OR order_link_id is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.post('v2/private/order/replace', params); | ||
} | ||
/** | ||
* @deprecated use replaceActiveOrder() | ||
*/ | ||
async replaceActiveOrderOld(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.order_id || params.order_link_id, 'Parameter order_id OR order_link_id is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.post('open-api/order/replace', params); | ||
} | ||
async queryActiveOrder(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.order_id || params.order_link_id, 'Parameter order_id OR order_link_id is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('v2/private/order', params); | ||
} | ||
async placeConditionalOrder(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.side, 'Parameter side is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.order_type, 'Parameter order_type is required'); | ||
assert(params.qty, 'Parameter qty is required'); | ||
assert(params.base_price, 'Parameter base_price is required'); | ||
assert(params.stop_px, 'Parameter stop_px is required'); | ||
assert(params.time_in_force, 'Parameter time_in_force is required'); | ||
if (params.order_type === 'Limit') assert(params.price, 'Parameter price is required for limit orders'); | ||
return await this.request.post('v2/private/stop-order/create', params); | ||
} | ||
/** | ||
* @deprecated use placeConditionalOrder | ||
*/ | ||
async placeConditionalOrderOld(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.side, 'Parameter side is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.order_type, 'Parameter order_type is required'); | ||
assert(params.qty, 'Parameter qty is required'); | ||
assert(params.time_in_force, 'Parameter time_in_force is required'); | ||
assert(params.base_price, 'Parameter base_price is required'); | ||
assert(params.stop_px, 'Parameter stop_px is required'); | ||
if (params.order_type === 'Limit') assert(params.price, 'Parameter price is required for limit orders'); | ||
return await this.request.post('open-api/stop-order/create', params); | ||
} | ||
async getConditionalOrder(params) { | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('v2/private/stop-order/list', params); | ||
} | ||
/** | ||
* @deprecated use placeConditionalOrder | ||
*/ | ||
async getConditionalOrderOld(params) { | ||
return await this.request.get('open-api/stop-order/list', params); | ||
} | ||
async cancelConditionalOrder(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.stop_order_id || params.order_link_id, 'Parameter stop_order_id OR order_link_id is required'); | ||
return await this.request.post('v2/private/stop-order/cancel', params); | ||
} | ||
/** | ||
* @deprecated use cancelConditionalOrder | ||
*/ | ||
async cancelConditionalOrderOld(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.stop_order_id, 'Parameter stop_order_id is required'); | ||
return await this.request.post('open-api/stop-order/cancel', params); | ||
} | ||
async cancelAllConditionalOrders(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.post('v2/private/stop-order/cancelAll', params); | ||
} | ||
async replaceConditionalOrder(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.stop_order_id || params.order_link_id, 'Parameter stop_order_id OR order_link_id is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.post('v2/private/stop-order/replace', params); | ||
} | ||
/** | ||
* @deprecated use replaceConditionalOrder | ||
*/ | ||
async replaceConditionalOrderOld(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.stop_order_id, 'Parameter stop_order_id is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.post('open-api/stop-order/replace', params); | ||
} | ||
async queryConditionalOrder(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.stop_order_id || params.order_link_id, 'Parameter order_id OR order_link_id is required'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('v2/private/stop-order', params); | ||
} | ||
/** | ||
* @deprecated use getPosition() instead | ||
*/ | ||
async getUserLeverage() { | ||
return await this.request.get('user/leverage'); | ||
} | ||
async getPosition(params) { | ||
return await this.request.get('v2/private/position/list', params); | ||
} | ||
async changeUserLeverage(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
if (typeof params.leverage === 'undefined') { | ||
throw new Error('Parameter leverage is required'); | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.RestClient = void 0; | ||
const requestWrapper_1 = __importDefault(require("./util/requestWrapper")); | ||
class RestClient { | ||
/** | ||
* @public Creates an instance of the inverse REST API client. | ||
* | ||
* @param {string} key - your API key | ||
* @param {string} secret - your API secret | ||
* @param {boolean} [livenet=false] | ||
* @param {RestClientInverseOptions} [restInverseOptions={}] options to configure REST API connectivity | ||
* @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios | ||
*/ | ||
constructor(key, secret, livenet, restInverseOptions = {}, requestOptions = {}) { | ||
this.requestWrapper = new requestWrapper_1.default(key, secret, livenet, restInverseOptions, requestOptions); | ||
} | ||
return await this.request.post('user/leverage/save', params); | ||
} | ||
/** | ||
* @deprecated use getPosition() instead | ||
*/ | ||
async getPositions() { | ||
return await this.request.get('position/list'); | ||
} | ||
async changePositionMargin(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.margin, 'Parameter margin is required'); | ||
return await this.request.post('position/change-position-margin', params); | ||
} | ||
async setTradingStop(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.post('open-api/position/trading-stop', params); | ||
} | ||
async getWalletFundRecords(params) { | ||
return await this.request.get('open-api/wallet/fund/records', params); | ||
} | ||
async getWithdrawRecords(params) { | ||
return await this.request.get('open-api/wallet/withdraw/list', params); | ||
} | ||
async getAssetExchangeRecords(params) { | ||
return await this.request.get('v2/private/exchange-order/list', params); | ||
} | ||
async getWalletBalance(params) { | ||
return await this.request.get('v2/private/wallet/balance', params); | ||
} | ||
async setRiskLimit(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.risk_id, 'Parameter risk_id is required'); | ||
return await this.request.post('open-api/wallet/risk-limit', params); | ||
} | ||
async getRiskLimitList() { | ||
return await this.request.get('open-api/wallet/risk-limit/list'); | ||
} | ||
async getLastFundingRate(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('open-api/funding/prev-funding-rate', params); | ||
} | ||
async getMyLastFundingFee(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('open-api/funding/prev-funding', params); | ||
} | ||
async getPredictedFunding(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('open-api/funding/predicted-funding', params); | ||
} | ||
async getTradeRecords(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.order_id || params.symbol, 'Parameter order_id OR symbol is required'); | ||
return await this.request.get('v2/private/execution/list', params); | ||
} | ||
async getOrderBook(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('v2/public/orderBook/L2', params); | ||
} | ||
async getKline(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.interval, 'Parameter interval is required'); | ||
assert(params.from, 'Parameter from is required'); | ||
return await this.request.get('v2/public/kline/list', params); | ||
} | ||
async getOpenInterest(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.period, 'Parameter period is required'); | ||
return await this.request.get('v2/public/open-interest', params); | ||
} | ||
async getLatestBigDeal(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('v2/public/big-deal', params); | ||
} | ||
async getLongShortRatio(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
assert(params.period, 'Parameter period is required'); | ||
return await this.request.get('v2/public/account-ratio', params); | ||
} | ||
async getLatestInformation() { | ||
return await this.request.get('v2/public/tickers'); | ||
} | ||
async getPublicTradingRecords(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('v2/public/trading-records', params); | ||
} | ||
async getPublicLiquidations(params) { | ||
assert(params, 'No params passed'); | ||
assert(params.symbol, 'Parameter symbol is required'); | ||
return await this.request.get('v2/public/liq-records', params); | ||
} | ||
async getServerTime() { | ||
return await this.request.get('v2/public/time'); | ||
} | ||
async getApiAnnouncements() { | ||
return await this.request.get('v2/public/announcement'); | ||
} | ||
async getSymbols() { | ||
return await this.request.get('v2/public/symbols'); | ||
} | ||
async getTimeOffset() { | ||
return await this.request.getTimeOffset(); | ||
} | ||
}; | ||
/** | ||
* | ||
* Market Data Endpoints | ||
* | ||
*/ | ||
getOrderBook(params) { | ||
return this.requestWrapper.get('v2/public/orderBook/L2', params); | ||
} | ||
getKline(params) { | ||
return this.requestWrapper.get('v2/public/kline/list', params); | ||
} | ||
/** | ||
* @deprecated use getTickers() instead | ||
*/ | ||
getLatestInformation(params) { | ||
return this.getTickers(params); | ||
} | ||
getTickers(params) { | ||
return this.requestWrapper.get('v2/public/tickers', params); | ||
} | ||
/** | ||
* @deprecated use getTrades() instead | ||
*/ | ||
getPublicTradingRecords(params) { | ||
return this.getTrades(params); | ||
} | ||
getTrades(params) { | ||
return this.requestWrapper.get('v2/public/trading-records', params); | ||
} | ||
getSymbols() { | ||
return this.requestWrapper.get('v2/public/symbols'); | ||
} | ||
/** | ||
* @deprecated use getLiquidations() instead | ||
*/ | ||
getPublicLiquidations(params) { | ||
return this.getLiquidations(params); | ||
} | ||
getLiquidations(params) { | ||
return this.requestWrapper.get('v2/public/liq-records', params); | ||
} | ||
getMarkPriceKline(params) { | ||
return this.requestWrapper.get('v2/public/mark-price-kline', params); | ||
} | ||
getOpenInterest(params) { | ||
return this.requestWrapper.get('v2/public/open-interest', params); | ||
} | ||
getLatestBigDeal(params) { | ||
return this.requestWrapper.get('v2/public/big-deal', params); | ||
} | ||
getLongShortRatio(params) { | ||
return this.requestWrapper.get('v2/public/account-ratio', params); | ||
} | ||
/** | ||
* | ||
* Account Data Endpoints | ||
* | ||
*/ | ||
placeActiveOrder(orderRequest) { | ||
if (orderRequest.order_type === 'Limit' && !orderRequest.price) { | ||
throw new Error('Price required for limit orders'); | ||
} | ||
return this.requestWrapper.post('v2/private/order/create', orderRequest); | ||
} | ||
getActiveOrderList(params) { | ||
return this.requestWrapper.get('v2/private/order/list', params); | ||
} | ||
/** | ||
* @deprecated use getActiveOrderList() instead | ||
*/ | ||
getActiveOrder(params) { | ||
return this.requestWrapper.get('open-api/order/list', params); | ||
} | ||
cancelActiveOrder(params) { | ||
if (!params.order_id && !params.order_link_id) { | ||
throw new Error('Parameter order_id OR order_link_id is required'); | ||
} | ||
return this.requestWrapper.post('v2/private/order/cancel', params); | ||
} | ||
cancelAllActiveOrders(params) { | ||
return this.requestWrapper.post('v2/private/order/cancelAll', params); | ||
} | ||
replaceActiveOrder(params) { | ||
if (!params.order_id && !params.order_link_id) { | ||
throw new Error('Parameter order_id OR order_link_id is required'); | ||
} | ||
return this.requestWrapper.post('v2/private/order/replace', params); | ||
} | ||
/** | ||
* @deprecated use replaceActiveOrder() | ||
*/ | ||
replaceActiveOrderOld(params) { | ||
if (!params.order_id && !params.order_link_id) { | ||
throw new Error('Parameter order_id OR order_link_id is required'); | ||
} | ||
return this.requestWrapper.post('open-api/order/replace', params); | ||
} | ||
queryActiveOrder(params) { | ||
if (!params.order_id && !params.order_link_id) { | ||
throw new Error('Parameter order_id OR order_link_id is required'); | ||
} | ||
return this.requestWrapper.get('v2/private/order', params); | ||
} | ||
placeConditionalOrder(params) { | ||
if (params.order_type === 'Limit' && !params.price) { | ||
throw new Error('Parameter price is required for limit orders'); | ||
} | ||
return this.requestWrapper.post('v2/private/stop-order/create', params); | ||
} | ||
/** | ||
* @deprecated use placeConditionalOrder | ||
*/ | ||
placeConditionalOrderOld(params) { | ||
if (params.order_type === 'Limit' && !params.price) { | ||
throw new Error('Parameter price is required for limit orders'); | ||
} | ||
return this.requestWrapper.post('open-api/stop-order/create', params); | ||
} | ||
getConditionalOrder(params) { | ||
return this.requestWrapper.get('v2/private/stop-order/list', params); | ||
} | ||
/** | ||
* @deprecated use placeConditionalOrder | ||
*/ | ||
getConditionalOrderOld(params) { | ||
return this.requestWrapper.get('open-api/stop-order/list', params); | ||
} | ||
cancelConditionalOrder(params) { | ||
if (!params.stop_order_id && !params.order_link_id) { | ||
throw new Error('Parameter stop_order_id OR order_link_id is required'); | ||
} | ||
return this.requestWrapper.post('v2/private/stop-order/cancel', params); | ||
} | ||
/** | ||
* @deprecated use cancelConditionalOrder | ||
*/ | ||
cancelConditionalOrderOld(params) { | ||
if (!params.stop_order_id && !params.order_link_id) { | ||
throw new Error('Parameter stop_order_id OR order_link_id is required'); | ||
} | ||
return this.requestWrapper.post('open-api/stop-order/cancel', params); | ||
} | ||
cancelAllConditionalOrders(params) { | ||
return this.requestWrapper.post('v2/private/stop-order/cancelAll', params); | ||
} | ||
replaceConditionalOrder(params) { | ||
if (!params.stop_order_id && !params.order_link_id) { | ||
throw new Error('Parameter stop_order_id OR order_link_id is required'); | ||
} | ||
return this.requestWrapper.post('v2/private/stop-order/replace', params); | ||
} | ||
/** | ||
* @deprecated use replaceConditionalOrder | ||
*/ | ||
replaceConditionalOrderOld(params) { | ||
return this.requestWrapper.post('open-api/stop-order/replace', params); | ||
} | ||
queryConditionalOrder(params) { | ||
if (!params.stop_order_id && !params.order_link_id) { | ||
throw new Error('Parameter stop_order_id OR order_link_id is required'); | ||
} | ||
return this.requestWrapper.get('v2/private/stop-order', params); | ||
} | ||
/** | ||
* @deprecated use getPosition() instead | ||
*/ | ||
getUserLeverage() { | ||
return this.requestWrapper.get('user/leverage'); | ||
} | ||
getPosition(params) { | ||
return this.requestWrapper.get('v2/private/position/list', params); | ||
} | ||
/** | ||
* @deprecated use getPosition() instead | ||
*/ | ||
getPositions() { | ||
return this.requestWrapper.get('position/list'); | ||
} | ||
changePositionMargin(params) { | ||
return this.requestWrapper.post('position/change-position-margin', params); | ||
} | ||
setTradingStop(params) { | ||
return this.requestWrapper.post('open-api/position/trading-stop', params); | ||
} | ||
setUserLeverage(params) { | ||
return this.requestWrapper.post('user/leverage/save', params); | ||
} | ||
/** | ||
* @deprecated use setUserLeverage() instead | ||
*/ | ||
changeUserLeverage(params) { | ||
return this.setUserLeverage(params); | ||
} | ||
getTradeRecords(params) { | ||
return this.requestWrapper.get('v2/private/execution/list', params); | ||
} | ||
getClosedPnl(params) { | ||
return this.requestWrapper.get('v2/private/trade/closed-pnl/list', params); | ||
} | ||
getRiskLimitList() { | ||
return this.requestWrapper.get('open-api/wallet/risk-limit/list'); | ||
} | ||
setRiskLimit(params) { | ||
return this.requestWrapper.post('open-api/wallet/risk-limit', params); | ||
} | ||
getLastFundingRate(params) { | ||
return this.requestWrapper.get('open-api/funding/prev-funding-rate', params); | ||
} | ||
getMyLastFundingFee(params) { | ||
return this.requestWrapper.get('open-api/funding/prev-funding', params); | ||
} | ||
getPredictedFunding(params) { | ||
return this.requestWrapper.get('open-api/funding/predicted-funding', params); | ||
} | ||
getApiKeyInfo() { | ||
return this.requestWrapper.get('open-api/api-key'); | ||
} | ||
getLcpInfo(params) { | ||
return this.requestWrapper.get('v2/private/account/lcp', params); | ||
} | ||
/** | ||
* | ||
* Wallet Data Endpoints | ||
* | ||
*/ | ||
getWalletBalance(params) { | ||
return this.requestWrapper.get('v2/private/wallet/balance', params); | ||
} | ||
getWalletFundRecords(params) { | ||
return this.requestWrapper.get('open-api/wallet/fund/records', params); | ||
} | ||
getWithdrawRecords(params) { | ||
return this.requestWrapper.get('open-api/wallet/withdraw/list', params); | ||
} | ||
getAssetExchangeRecords(params) { | ||
return this.requestWrapper.get('v2/private/exchange-order/list', params); | ||
} | ||
/** | ||
* | ||
* API Data Endpoints | ||
* | ||
*/ | ||
getServerTime() { | ||
return this.requestWrapper.get('v2/public/time'); | ||
} | ||
getApiAnnouncements() { | ||
return this.requestWrapper.get('v2/public/announcement'); | ||
} | ||
getTimeOffset() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const start = Date.now(); | ||
return this.getServerTime().then(result => { | ||
const end = Date.now(); | ||
return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2)); | ||
}); | ||
}); | ||
} | ||
} | ||
exports.RestClient = RestClient; | ||
; | ||
//# sourceMappingURL=rest-client.js.map |
@@ -1,21 +0,26 @@ | ||
const { createHmac } = require('crypto'); | ||
module.exports = { | ||
signMessage(message, secret) { | ||
return createHmac('sha256', secret) | ||
.update(message) | ||
.digest('hex'); | ||
}, | ||
serializeParams(params = {}, strict_validation = false) { | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.serializeParams = exports.signMessage = void 0; | ||
const crypto_1 = require("crypto"); | ||
function signMessage(message, secret) { | ||
return crypto_1.createHmac('sha256', secret) | ||
.update(message) | ||
.digest('hex'); | ||
} | ||
exports.signMessage = signMessage; | ||
; | ||
function serializeParams(params = {}, strict_validation = false) { | ||
return Object.keys(params) | ||
.sort() | ||
.map(key => { | ||
.sort() | ||
.map(key => { | ||
const value = params[key]; | ||
if (strict_validation === true && typeof value === 'undefined') { | ||
throw new Error('Failed to sign API request due to undefined parameter'); | ||
throw new Error('Failed to sign API request due to undefined parameter'); | ||
} | ||
return `${key}=${value}`; | ||
}) | ||
.join('&'); | ||
} | ||
}; | ||
}) | ||
.join('&'); | ||
} | ||
exports.serializeParams = serializeParams; | ||
; | ||
//# sourceMappingURL=requestUtils.js.map |
@@ -1,167 +0,147 @@ | ||
const assert = require('assert'); | ||
const axios = require('axios'); | ||
const { signMessage, serializeParams } = require('./requestUtils'); | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
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 requestUtils_1 = require("./requestUtils"); | ||
const baseUrls = { | ||
livenet: 'https://api.bybit.com', | ||
testnet: 'https://api-testnet.bybit.com' | ||
livenet: 'https://api.bybit.com', | ||
testnet: 'https://api-testnet.bybit.com' | ||
}; | ||
module.exports = class RequestUtil { | ||
constructor(key, secret, livenet=false, options={}, requestOptions={}) { | ||
this._timeOffset = null; | ||
this._syncTimePromise = null; | ||
this.options = { | ||
recv_window: 5000, | ||
// how often to sync time drift with bybit servers | ||
sync_interval_ms: 3600000, | ||
// if true, we'll throw errors if any params are undefined | ||
strict_param_validation: false, | ||
...options | ||
}; | ||
this.baseUrl = baseUrls[livenet === true ? 'livenet' : 'testnet']; | ||
if (options.baseUrl) { | ||
this.baseUrl = options.baseUrl; | ||
class RequestUtil { | ||
constructor(key, secret, livenet = false, options = {}, requestOptions = {}) { | ||
this.timeOffset = null; | ||
this.syncTimePromise = null; | ||
this.options = Object.assign({ recv_window: 5000, | ||
// how often to sync time drift with bybit servers | ||
sync_interval_ms: 3600000, | ||
// if true, we'll throw errors if any params are undefined | ||
strict_param_validation: false }, options); | ||
this.baseUrl = baseUrls[livenet === true ? 'livenet' : 'testnet']; | ||
if (options.baseUrl) { | ||
this.baseUrl = options.baseUrl; | ||
} | ||
this.globalRequestOptions = Object.assign(Object.assign({ | ||
// in ms == 5 minutes by default | ||
timeout: 1000 * 60 * 5 }, requestOptions), { headers: { | ||
'referer': 'bybitapinode', | ||
'x-referer': 'bybitapinode' | ||
} }); | ||
if (key && !secret) { | ||
throw new Error('API Key & Secret are both required for private enpoints'); | ||
} | ||
this._syncTime(); | ||
setInterval(this._syncTime.bind(this), +this.options.sync_interval_ms); | ||
this.key = key; | ||
this.secret = secret; | ||
} | ||
this.globalRequestOptions = { | ||
// in ms == 5 minutes by default | ||
timeout: 1000 * 60 * 5, | ||
// custom request options based on axios specs - see: https://github.com/axios/axios#request-config | ||
...requestOptions, | ||
headers: { | ||
'referer': 'bybitapinode' | ||
}, | ||
}; | ||
if (key) { | ||
assert(secret, 'Secret is required for private enpoints'); | ||
// TODO: type check that endpoint never starts with forward slash?? | ||
get(endpoint, params) { | ||
return this._call('GET', endpoint, params); | ||
} | ||
this._syncTime(); | ||
setInterval(this._syncTime.bind(this), parseInt(this.options.sync_interval_ms)); | ||
this.key = key; | ||
this.secret = secret; | ||
} | ||
async get(endpoint, params) { | ||
const result = await this._call('GET', endpoint, params); | ||
return result; | ||
} | ||
async post(endpoint, params) { | ||
const result = await this._call('POST', endpoint, params); | ||
return result; | ||
} | ||
async getTimeOffset() { | ||
const start = Date.now(); | ||
const result = await this.get('v2/public/time'); | ||
const end = Date.now(); | ||
return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2)); | ||
} | ||
async _call(method, endpoint, params) { | ||
const publicEndpoint = endpoint.startsWith('v2/public'); | ||
if (!publicEndpoint) { | ||
if (!this.key || !this.secret) { | ||
throw new Error('Private endpoints require api and private keys set'); | ||
} | ||
if (this._timeOffset === null) { | ||
await this._syncTime(); | ||
} | ||
params = this._signRequest(params); | ||
post(endpoint, params) { | ||
return this._call('POST', endpoint, params); | ||
} | ||
const options = { | ||
...this.globalRequestOptions, | ||
url: [this.baseUrl, endpoint].join('/'), | ||
method: method, | ||
json: true | ||
}; | ||
switch (method) { | ||
case 'GET': | ||
options.params = params; | ||
break; | ||
default: | ||
options.data = params; | ||
break; | ||
_call(method, endpoint, params) { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const publicEndpoint = endpoint.startsWith('v2/public'); | ||
if (!publicEndpoint) { | ||
if (!this.key || !this.secret) { | ||
throw new Error('Private endpoints require api and private keys set'); | ||
} | ||
if (this.timeOffset === null) { | ||
yield this._syncTime(); | ||
} | ||
params = this._signRequest(params); | ||
} | ||
const options = Object.assign(Object.assign({}, this.globalRequestOptions), { url: [this.baseUrl, endpoint].join('/'), method: method, json: true }); | ||
switch (method) { | ||
case 'GET': | ||
options.params = params; | ||
break; | ||
default: | ||
options.data = params; | ||
break; | ||
} | ||
return axios_1.default(options).then(response => { | ||
if (response.status == 200) { | ||
return response.data; | ||
} | ||
throw { | ||
code: response.status, | ||
message: response.statusText, | ||
body: response.data, | ||
requestOptions: options | ||
}; | ||
}) | ||
.catch(e => { | ||
if (!e.response) { | ||
// Something happened in setting up the request that triggered an Error | ||
if (!e.request) { | ||
throw e.message; | ||
} | ||
// request made but no response received | ||
throw e; | ||
} | ||
// The request was made and the server responded with a status code | ||
// that falls out of the range of 2xx | ||
throw { | ||
code: e.response.statusCode, | ||
message: e.response.message, | ||
body: e.response.body, | ||
requestOptions: options, | ||
headers: e.response.headers | ||
}; | ||
}); | ||
}); | ||
} | ||
return axios(options).then(response => { | ||
if (response.status == 200) { | ||
return response.data; | ||
} | ||
throw { | ||
code: response.status, | ||
message: response.statusText, | ||
body: response.data, | ||
requestOptions: options | ||
}; | ||
}) | ||
.catch(e => { | ||
if (!e.response) { | ||
// Something happened in setting up the request that triggered an Error | ||
if (!e.request) { | ||
throw e.message; | ||
} | ||
// request made but no response received | ||
throw e; | ||
_signRequest(data) { | ||
const params = Object.assign(Object.assign({}, data), { api_key: this.key, timestamp: Date.now() + (this.timeOffset || 0) }); | ||
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen. | ||
if (this.options.recv_window && !params.recv_window) { | ||
params.recv_window = this.options.recv_window; | ||
} | ||
// The request was made and the server responded with a status code | ||
// that falls out of the range of 2xx | ||
throw { | ||
code: e.response.statusCode, | ||
message: e.response.message, | ||
body: e.response.body, | ||
requestOptions: options, | ||
headers: e.response.headers | ||
}; | ||
}); | ||
} | ||
_signRequest(data) { | ||
const params = { | ||
...data, | ||
api_key: this.key, | ||
timestamp: Date.now() + this._timeOffset | ||
}; | ||
// Optional, set to 5000 by default. Increase if timestamp/recv_window errors are seen. | ||
if (this.options.recv_window && !params.recv_window) { | ||
params.recv_window = this.options.recv_window; | ||
if (this.key && this.secret) { | ||
const serializedParams = requestUtils_1.serializeParams(params, this.options.strict_param_validation); | ||
params.sign = requestUtils_1.signMessage(serializedParams, this.secret); | ||
} | ||
return params; | ||
} | ||
if (this.key && this.secret) { | ||
const serializedParams = serializeParams(params, this.options.strict_param_validation); | ||
params.sign = signMessage(serializedParams, this.secret); | ||
/** | ||
* @deprecated move this somewhere else, because v2/public/time shouldn't be hardcoded here | ||
* | ||
* @returns {Promise<number>} | ||
* @memberof RequestUtil | ||
*/ | ||
getTimeOffset() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
const start = Date.now(); | ||
const result = yield this.get('v2/public/time'); | ||
const end = Date.now(); | ||
return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2)); | ||
}); | ||
} | ||
return params; | ||
} | ||
_syncTime() { | ||
if (this._syncTimePromise !== null) { | ||
return this._syncTimePromise; | ||
_syncTime() { | ||
if (this.syncTimePromise !== null) { | ||
return this.syncTimePromise; | ||
} | ||
this.syncTimePromise = this.getTimeOffset().then(offset => { | ||
this.timeOffset = offset; | ||
this.syncTimePromise = null; | ||
}); | ||
return this.syncTimePromise; | ||
} | ||
this._syncTimePromise = this.getTimeOffset().then(offset => { | ||
this._timeOffset = offset; | ||
this._syncTimePromise = null; | ||
}); | ||
return this._syncTimePromise; | ||
} | ||
}; | ||
} | ||
exports.default = RequestUtil; | ||
; | ||
//# sourceMappingURL=requestWrapper.js.map |
@@ -1,13 +0,26 @@ | ||
const { EventEmitter } = require('events'); | ||
const WebSocket = require('ws'); | ||
const defaultLogger = require('./logger'); | ||
const RestClient = require('./rest-client'); | ||
const { signMessage, serializeParams } = require('./util/requestUtils'); | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.WebsocketClient = void 0; | ||
const events_1 = require("events"); | ||
const logger_1 = require("./logger"); | ||
const rest_client_1 = require("./rest-client"); | ||
const requestUtils_1 = require("./util/requestUtils"); | ||
// import WebSocket from 'ws'; | ||
const isomorphic_ws_1 = __importDefault(require("isomorphic-ws")); | ||
const wsUrls = { | ||
livenet: 'wss://stream.bybit.com/realtime', | ||
testnet: 'wss://stream-testnet.bybit.com/realtime' | ||
livenet: 'wss://stream.bybit.com/realtime', | ||
testnet: 'wss://stream-testnet.bybit.com/realtime' | ||
}; | ||
const READY_STATE_INITIAL = 0; | ||
@@ -18,214 +31,192 @@ const READY_STATE_CONNECTING = 1; | ||
const READY_STATE_RECONNECTING = 4; | ||
module.exports = class WebsocketClient extends EventEmitter { | ||
constructor(options, logger) { | ||
super(); | ||
this.logger = logger || defaultLogger; | ||
this.readyState = READY_STATE_INITIAL; | ||
this.pingInterval = null; | ||
this.pongTimeout = null; | ||
this.options = { | ||
livenet: false, | ||
pongTimeout: 1000, | ||
pingInterval: 10000, | ||
reconnectTimeout: 500, | ||
...options | ||
}; | ||
this.client = new RestClient(null, null, this.options.livenet, this.options.restOptions, this.options.requestOptions); | ||
this._subscriptions = new Set(); | ||
this._connect(); | ||
} | ||
subscribe(topics) { | ||
if (!Array.isArray(topics)) topics = [topics]; | ||
topics.forEach(topic => this._subscriptions.add(topic)); | ||
// subscribe not necessary if not yet connected (will subscribe onOpen) | ||
if (this.readyState === READY_STATE_CONNECTED) this._subscribe(topics); | ||
} | ||
unsubscribe(topics) { | ||
if (!Array.isArray(topics)) topics = [topics]; | ||
topics.forEach(topic => this._subscriptions.delete(topic)); | ||
// unsubscribe not necessary if not yet connected | ||
if (this.readyState === READY_STATE_CONNECTED) this._unsubscribe(topics); | ||
} | ||
close() { | ||
this.logger.info('Closing connection', {category: 'bybit-ws'}); | ||
this.readyState = READY_STATE_CLOSING; | ||
this._teardown(); | ||
this.ws && this.ws.close(); | ||
} | ||
_getWsUrl() { | ||
if (this.options.wsUrl) { | ||
return this.options.wsUrl; | ||
; | ||
class WebsocketClient extends events_1.EventEmitter { | ||
constructor(options, logger) { | ||
super(); | ||
this.logger = logger || logger_1.DefaultLogger; | ||
this.readyState = READY_STATE_INITIAL; | ||
this.pingInterval = undefined; | ||
this.pongTimeout = undefined; | ||
this.options = Object.assign({ livenet: false, pongTimeout: 1000, pingInterval: 10000, reconnectTimeout: 500 }, options); | ||
this.client = new rest_client_1.RestClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); | ||
this._subscriptions = new Set(); | ||
this._connect(); | ||
} | ||
return wsUrls[this.options.livenet ? 'livenet' : 'testnet']; | ||
} | ||
async _connect() { | ||
try { | ||
if (this.readyState === READY_STATE_INITIAL) this.readyState = READY_STATE_CONNECTING; | ||
const authParams = await this._authenticate(); | ||
const url = this._getWsUrl() + authParams; | ||
this.ws = new WebSocket(url); | ||
this.ws.on('open', this._wsOpenHandler.bind(this)); | ||
this.ws.on('message', this._wsMessageHandler.bind(this)); | ||
this.ws.on('error', this._wsOnErrorHandler.bind(this)); | ||
this.ws.on('close', this._wsCloseHandler.bind(this)); | ||
} catch (err) { | ||
this.logger.error('Connection failed', err); | ||
this._reconnect(this.options.reconnectTimeout); | ||
subscribe(topics) { | ||
if (!Array.isArray(topics)) | ||
topics = [topics]; | ||
topics.forEach(topic => this._subscriptions.add(topic)); | ||
// subscribe not necessary if not yet connected (will subscribe onOpen) | ||
if (this.readyState === READY_STATE_CONNECTED) | ||
this._subscribe(topics); | ||
} | ||
} | ||
async _authenticate() { | ||
if (this.options.key && this.options.secret) { | ||
this.logger.debug('Starting authenticated websocket client.', {category: 'bybit-ws'}); | ||
const timeOffset = await this.client.getTimeOffset(); | ||
const params = { | ||
api_key: this.options.key, | ||
expires: (Date.now() + timeOffset + 5000) | ||
}; | ||
params.signature = signMessage('GET/realtime' + params.expires, this.options.secret); | ||
return '?' + serializeParams(params); | ||
} else if (this.options.key || this.options.secret) { | ||
this.logger.warning('Could not authenticate websocket, either api key or private key missing.', {category: 'bybit-ws'}); | ||
} else { | ||
this.logger.debug('Starting public only websocket client.', {category: 'bybit-ws'}); | ||
unsubscribe(topics) { | ||
if (!Array.isArray(topics)) | ||
topics = [topics]; | ||
topics.forEach(topic => this._subscriptions.delete(topic)); | ||
// unsubscribe not necessary if not yet connected | ||
if (this.readyState === READY_STATE_CONNECTED) | ||
this._unsubscribe(topics); | ||
} | ||
return ''; | ||
} | ||
_reconnect(timeout) { | ||
this._teardown(); | ||
if (this.readyState !== READY_STATE_CONNECTING) { | ||
this.readyState = READY_STATE_RECONNECTING; | ||
close() { | ||
this.logger.info('Closing connection', { category: 'bybit-ws' }); | ||
this.readyState = READY_STATE_CLOSING; | ||
this._teardown(); | ||
this.ws && this.ws.close(); | ||
} | ||
setTimeout(() => { | ||
this.logger.info('Reconnecting to server', {category: 'bybit-ws'}); | ||
this._connect(); | ||
}, timeout); | ||
} | ||
_ping() { | ||
clearTimeout(this.pongTimeout); | ||
this.pongTimeout = null; | ||
this.logger.silly('Sending ping', {category: 'bybit-ws'}); | ||
this.ws.send(JSON.stringify({op: 'ping'})); | ||
this.pongTimeout = setTimeout(() => { | ||
this.logger.info('Pong timeout', {category: 'bybit-ws'}); | ||
this._teardown(); | ||
this.ws.terminate(); | ||
}, this.options.pongTimeout); | ||
} | ||
_teardown() { | ||
if (this.pingInterval) clearInterval(this.pingInterval); | ||
if (this.pongTimeout) clearTimeout(this.pongTimeout); | ||
this.pongTimeout = null; | ||
this.pingInterval = null; | ||
} | ||
_wsOpenHandler() { | ||
if (this.readyState === READY_STATE_CONNECTING) { | ||
this.logger.info('Websocket connected', {category: 'bybit-ws', livenet: this.options.livenet}); | ||
this.emit('open'); | ||
} else if (this.readyState === READY_STATE_RECONNECTING) { | ||
this.logger.info('Websocket reconnected', {category: 'bybit-ws', livenet: this.options.livenet}); | ||
this.emit('reconnected'); | ||
_getWsUrl() { | ||
if (this.options.wsUrl) { | ||
return this.options.wsUrl; | ||
} | ||
return wsUrls[this.options.livenet ? 'livenet' : 'testnet']; | ||
} | ||
this.readyState = READY_STATE_CONNECTED; | ||
this._subscribe([...this._subscriptions]); | ||
this.pingInterval = setInterval(this._ping.bind(this), this.options.pingInterval); | ||
} | ||
_wsMessageHandler(message) { | ||
const msg = JSON.parse(message); | ||
if ('success' in msg) { | ||
this._handleResponse(msg); | ||
} else if (msg.topic) { | ||
this._handleUpdate(msg); | ||
} else { | ||
this.logger.warning('Got unhandled ws message', msg); | ||
_connect() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
try { | ||
if (this.readyState === READY_STATE_INITIAL) | ||
this.readyState = READY_STATE_CONNECTING; | ||
const authParams = yield this._authenticate(); | ||
const url = this._getWsUrl() + authParams; | ||
const ws = new isomorphic_ws_1.default(url); | ||
// ws.onopen!(this._wsOpenHandler.bind(this)); | ||
// ws.onmessage!(this._wsMessageHandler.bind(this)); | ||
// ws.onerror!(this._wsOnErrorHandler.bind(this)); | ||
// ws.onclose!(this._wsCloseHandler.bind(this)); | ||
ws.onopen = this._wsOpenHandler.bind(this); | ||
ws.onmessage = this._wsMessageHandler.bind(this); | ||
ws.onerror = this._wsOnErrorHandler.bind(this); | ||
ws.onclose = this._wsCloseHandler.bind(this); | ||
this.ws = ws; | ||
} | ||
catch (err) { | ||
this.logger.error('Connection failed: ', err); | ||
this._reconnect(this.options.reconnectTimeout); | ||
} | ||
}); | ||
} | ||
} | ||
_wsOnErrorHandler(err) { | ||
this.logger.error('Websocket error', {category: 'bybit-ws', err}); | ||
if (this.readyState === READY_STATE_CONNECTED) this.emit('error', err); | ||
} | ||
_wsCloseHandler() { | ||
this.logger.info('Websocket connection closed', {category: 'bybit-ws'}); | ||
if (this.readyState !== READY_STATE_CLOSING) { | ||
this._reconnect(this.options.reconnectTimeout); | ||
this.emit('reconnect'); | ||
} else { | ||
this.readyState = READY_STATE_INITIAL; | ||
this.emit('close'); | ||
_authenticate() { | ||
return __awaiter(this, void 0, void 0, function* () { | ||
if (this.options.key && this.options.secret) { | ||
this.logger.debug('Starting authenticated websocket client.', { category: 'bybit-ws' }); | ||
const timeOffset = yield this.client.getTimeOffset(); | ||
const params = { | ||
api_key: this.options.key, | ||
expires: (Date.now() + timeOffset + 5000) | ||
}; | ||
params.signature = requestUtils_1.signMessage('GET/realtime' + params.expires, this.options.secret); | ||
return '?' + requestUtils_1.serializeParams(params); | ||
} | ||
else if (this.options.key || this.options.secret) { | ||
this.logger.warning('Could not authenticate websocket, either api key or private key missing.', { category: 'bybit-ws' }); | ||
} | ||
else { | ||
this.logger.debug('Starting public only websocket client.', { category: 'bybit-ws' }); | ||
} | ||
return ''; | ||
}); | ||
} | ||
} | ||
_handleResponse(response) { | ||
if ( | ||
response.request && | ||
response.request.op === 'ping' && | ||
response.ret_msg === 'pong' && | ||
response.success === true | ||
) { | ||
this.logger.silly('pong recieved', {category: 'bybit-ws'}); | ||
_reconnect(timeout) { | ||
this._teardown(); | ||
if (this.readyState !== READY_STATE_CONNECTING) { | ||
this.readyState = READY_STATE_RECONNECTING; | ||
} | ||
setTimeout(() => { | ||
this.logger.info('Reconnecting to server', { category: 'bybit-ws' }); | ||
this._connect(); | ||
}, timeout); | ||
} | ||
_ping() { | ||
clearTimeout(this.pongTimeout); | ||
} else { | ||
this.emit('response', response); | ||
delete this.pongTimeout; | ||
this.logger.silly('Sending ping', { category: 'bybit-ws' }); | ||
this.ws.send(JSON.stringify({ op: 'ping' })); | ||
this.pongTimeout = setTimeout(() => { | ||
this.logger.info('Pong timeout', { category: 'bybit-ws' }); | ||
this._teardown(); | ||
// this.ws.terminate(); | ||
// TODO: does this work? | ||
this.ws.close(); | ||
}, this.options.pongTimeout); | ||
} | ||
} | ||
_handleUpdate(message) { | ||
this.emit('update', message); | ||
} | ||
_subscribe(topics) { | ||
const msgStr = JSON.stringify({ | ||
op: 'subscribe', | ||
'args': topics | ||
}); | ||
this.ws.send(msgStr); | ||
} | ||
_unsubscribe(topics) { | ||
const msgStr = JSON.stringify({ | ||
op: 'unsubscribe', | ||
'args': topics | ||
}); | ||
this.ws.send(msgStr); | ||
} | ||
}; | ||
_teardown() { | ||
if (this.pingInterval) | ||
clearInterval(this.pingInterval); | ||
if (this.pongTimeout) | ||
clearTimeout(this.pongTimeout); | ||
this.pongTimeout = undefined; | ||
this.pingInterval = undefined; | ||
} | ||
_wsOpenHandler() { | ||
if (this.readyState === READY_STATE_CONNECTING) { | ||
this.logger.info('Websocket connected', { category: 'bybit-ws', livenet: this.options.livenet }); | ||
this.emit('open'); | ||
} | ||
else if (this.readyState === READY_STATE_RECONNECTING) { | ||
this.logger.info('Websocket reconnected', { category: 'bybit-ws', livenet: this.options.livenet }); | ||
this.emit('reconnected'); | ||
} | ||
this.readyState = READY_STATE_CONNECTED; | ||
this._subscribe([...this._subscriptions]); | ||
this.pingInterval = setInterval(this._ping.bind(this), this.options.pingInterval); | ||
} | ||
_wsMessageHandler(message) { | ||
const msg = JSON.parse(message && message.data || message); | ||
if ('success' in msg) { | ||
this._handleResponse(msg); | ||
} | ||
else if (msg.topic) { | ||
this._handleUpdate(msg); | ||
} | ||
else { | ||
this.logger.warning('Got unhandled ws message', msg); | ||
} | ||
} | ||
_wsOnErrorHandler(err) { | ||
this.logger.error('Websocket error', { category: 'bybit-ws', err }); | ||
if (this.readyState === READY_STATE_CONNECTED) | ||
this.emit('error', err); | ||
} | ||
_wsCloseHandler() { | ||
this.logger.info('Websocket connection closed', { category: 'bybit-ws' }); | ||
if (this.readyState !== READY_STATE_CLOSING) { | ||
this._reconnect(this.options.reconnectTimeout); | ||
this.emit('reconnect'); | ||
} | ||
else { | ||
this.readyState = READY_STATE_INITIAL; | ||
this.emit('close'); | ||
} | ||
} | ||
_handleResponse(response) { | ||
if (response.request && | ||
response.request.op === 'ping' && | ||
response.ret_msg === 'pong' && | ||
response.success === true) { | ||
this.logger.silly('pong recieved', { category: 'bybit-ws' }); | ||
clearTimeout(this.pongTimeout); | ||
} | ||
else { | ||
this.emit('response', response); | ||
} | ||
} | ||
_handleUpdate(message) { | ||
this.emit('update', message); | ||
} | ||
_subscribe(topics) { | ||
const msgStr = JSON.stringify({ | ||
op: 'subscribe', | ||
'args': topics | ||
}); | ||
this.ws.send(msgStr); | ||
} | ||
_unsubscribe(topics) { | ||
const msgStr = JSON.stringify({ | ||
op: 'unsubscribe', | ||
'args': topics | ||
}); | ||
this.ws.send(msgStr); | ||
} | ||
} | ||
exports.WebsocketClient = WebsocketClient; | ||
; | ||
//# sourceMappingURL=websocket-client.js.map |
{ | ||
"name": "bybit-api", | ||
"version": "1.2.5", | ||
"version": "1.3.0-beta.0", | ||
"description": "A production-ready Node.js connector for the Bybit APIs and WebSockets", | ||
"main": "index.js", | ||
"main": "lib/index.js", | ||
"types": "lib/index.d.ts", | ||
"files": [ | ||
"lib/*", | ||
"index.js", | ||
"dist/*" | ||
], | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "echo \"Error: no test specified\" && exit 1", | ||
"clean": "rm -rf lib dist", | ||
"prebuild": "npm run clean", | ||
"build": "tsc", | ||
"pack": "webpack --config webpack/webpack.config.js", | ||
"prepush": "npm run build", | ||
"prepublish": "npm run build" | ||
}, | ||
@@ -13,2 +25,24 @@ "repository": { | ||
}, | ||
"author": "Tiago Siebler (https://github.com/tiagosiebler)", | ||
"contributors": [ | ||
"Stefan Aebischer <os@pixtron.ch> (https://pixtron.ch)" | ||
], | ||
"dependencies": { | ||
"axios": "^0.21.0", | ||
"isomorphic-ws": "^4.0.1", | ||
"ws": "^7.4.0" | ||
}, | ||
"devDependencies": { | ||
"@types/node": "^14.14.7", | ||
"buffer": "^6.0.2", | ||
"crypto-browserify": "^3.12.0", | ||
"eslint": "^7.10.0", | ||
"source-map-loader": "^1.1.2", | ||
"stream-browserify": "^3.0.0", | ||
"ts-loader": "^8.0.11", | ||
"typescript": "^4.0.5", | ||
"webpack": "^5.4.0", | ||
"webpack-bundle-analyzer": "^4.1.0", | ||
"webpack-cli": "^4.2.0" | ||
}, | ||
"keywords": [ | ||
@@ -28,6 +62,6 @@ "bybit", | ||
], | ||
"author": "Tiago Siebler (https://github.com/tiagosiebler)", | ||
"contributors": [ | ||
"Stefan Aebischer <os@pixtron.ch> (https://pixtron.ch)" | ||
], | ||
"funding": { | ||
"type": "individual", | ||
"url": "https://github.com/sponsors/tiagosiebler" | ||
}, | ||
"license": "MIT", | ||
@@ -37,10 +71,3 @@ "bugs": { | ||
}, | ||
"homepage": "https://github.com/tiagosiebler/bybit-api#readme", | ||
"dependencies": { | ||
"axios": "^0.21.0", | ||
"ws": "^7.3.1" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^7.10.0" | ||
} | ||
"homepage": "https://github.com/tiagosiebler/bybit-api#readme" | ||
} |
@@ -27,2 +27,8 @@ # bybit-api | ||
## Structure | ||
This project uses typescript. Resources are stored in 3 key structures: | ||
- [src](./src) - the whole connector written in typescript | ||
- [lib](./lib) - the javascript version of the project (compiled from typescript). This should not be edited directly, as it will be overwritten with each release. | ||
- [dist](./dist) - the packed bundle of the project for use in browser environments. | ||
### Inverse Contracts | ||
@@ -35,5 +41,30 @@ #### Rest client | ||
const PRIVATE_KEY = 'yyy'; | ||
const useLivenet = false; | ||
const client = new RestClient(API_KEY, PRIVATE_KEY); | ||
const restInverseOptions = { | ||
// how much time to allow for valid request | ||
recv_window?: number; | ||
// how often to sync time drift with bybit servers | ||
sync_interval_ms?: number | string; | ||
// if true, we'll throw errors if any params are undefined | ||
strict_param_validation?: boolean; | ||
// Optionally override API protocol + domain | ||
// e.g 'https://api.bytick.com' | ||
baseUrl?: string; | ||
}; | ||
const client = new RestClient( | ||
API_KEY, | ||
PRIVATE_KEY, | ||
// optional, uses testnet by default. Set to 'true' to use livenet. | ||
useLivenet, | ||
// restInverseOptions, | ||
// requestLibraryOptions | ||
) | ||
client.changeUserLeverage({leverage: 4, symbol: 'ETHUSD'}) | ||
@@ -40,0 +71,0 @@ .then(result => { |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
68335
22
1117
172
3
11
2
1
+ Addedisomorphic-ws@^4.0.1
+ Addedisomorphic-ws@4.0.1(transitive)
Updatedws@^7.4.0