Initial commit
This commit is contained in:
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
!.gitkeep
|
||||
.DS_STORE
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
node_modules/
|
||||
.npm
|
||||
.eslintcache
|
||||
.node_repl_history
|
||||
*.tgz
|
||||
.yarn-integrity
|
||||
.env
|
||||
.env.test
|
||||
.cache
|
||||
7
LICENSE.md
Normal file
7
LICENSE.md
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright 2019 Stefan Aebischer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
83
README.md
Normal file
83
README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# @pxtrn/bybit-api
|
||||
|
||||
An unofficial node.js lowlevel wrapper for the Bybit Cryptocurrency Derivative
|
||||
exchange API.
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
`npm install --save @pxtrn/bybit-api`
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Create API credentials at bybit (obviously you need to be logged in):
|
||||
- [Livenet](https://bybit.com/app/user/api-management)
|
||||
- [Testnet](https://testnet.bybit.com/app/user/api-management)
|
||||
|
||||
|
||||
### Rest client
|
||||
|
||||
```js
|
||||
const {RestClient} = require('@pxtrn/bybit-api');
|
||||
|
||||
const API_KEY = 'xxx';
|
||||
const PRIVATE_KEY = 'yyy';
|
||||
|
||||
const client = new RestClient(API_KEY, PRIVATE_KEY);
|
||||
|
||||
client.changeUserLeverage({leverage: 4, symbol: 'ETHUSD'})
|
||||
.then(result => {
|
||||
console.log(result);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(error);
|
||||
});
|
||||
```
|
||||
|
||||
See rest client [api docs](./doc/rest-client.md) for further information.
|
||||
|
||||
|
||||
### Websocket client
|
||||
|
||||
```js
|
||||
const {WebsocketClient} = require('@pxtrn/bybit-api');
|
||||
|
||||
const API_KEY = 'xxx';
|
||||
const PRIVATE_KEY = 'yyy';
|
||||
|
||||
const ws = new WebsocketClient({key: API_KEY, secret: PRIVATE_KEY});
|
||||
|
||||
ws.on('open', function() {
|
||||
ws.subscribe(['position', 'execution', 'trade']);
|
||||
ws.subscribe('kline.BTCUSD.1m');
|
||||
});
|
||||
|
||||
ws.on('update', function(message) {
|
||||
console.log('update', message);
|
||||
});
|
||||
|
||||
ws.on('response', function(response) {
|
||||
console.log('response', response);
|
||||
});
|
||||
|
||||
ws.on('close', function() {
|
||||
console.log('connection closed');
|
||||
});
|
||||
|
||||
ws.on('error', function(err) {
|
||||
console.error('ERR', err);
|
||||
});
|
||||
```
|
||||
|
||||
See websocket client [api docs](./doc/websocket-client.md) for further information.
|
||||
|
||||
|
||||
## Donations
|
||||
|
||||
If this library helps you to trade better on bybit, feel free to donate a coffee,
|
||||
or create a bybit account using my [ref link](https://www.bybit.com/app/register?ref=j8q5l).
|
||||
|
||||
BTC `1Fh1158pXXudfM6ZrPJJMR7Y5SgZUz4EdF`
|
||||
ETH `0x21aEdeC53ab7593b77C9558942f0c9E78131e8d7`
|
||||
LTC `LNdHSVtG6UWsriMYLJR3qLdfVNKwJ6GSLF`
|
||||
85
doc/rest-client.md
Normal file
85
doc/rest-client.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Rest API
|
||||
|
||||
|
||||
## Class: RestClient
|
||||
|
||||
|
||||
### new RestClient([key][, secret])
|
||||
- `key` {String} Bybit API Key
|
||||
- `secret` {String} Bybit private key
|
||||
|
||||
If you only use the [public endpoints](#public-endpoints) you can ommit key and secret.
|
||||
|
||||
|
||||
### Private enpoints
|
||||
|
||||
#### async placeActiveOrder(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#place-active-order)
|
||||
|
||||
#### async getActiveOrder(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#get-active-order)
|
||||
|
||||
#### async cancelActiveOrder(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#cancel-active-order)
|
||||
|
||||
#### async placeConditionalOrder(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#place-conditional-order)
|
||||
|
||||
#### async getConditioanlOrder(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#get-conditional-order)
|
||||
|
||||
#### async cancelConditionalOrder(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#cancel-conditional-order-)
|
||||
|
||||
#### async getUserLeverage()
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#user-leverage)
|
||||
|
||||
#### async changeUserLeverage(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#-change-user-leverage)
|
||||
|
||||
#### async getPosition()
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#-my-position)
|
||||
|
||||
#### async changePositionMargin(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#-change-position-margin)
|
||||
|
||||
#### async getLastFundingRate(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#-get-the-last-funding-rate)
|
||||
|
||||
#### async getMyLastFundingFee(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#-get-my-last-funding-fee)
|
||||
|
||||
#### async getPredictedFunding(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#get-predicted-funding-rate-and-funding-fee)
|
||||
|
||||
#### async getOrderTradeRecords(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#get-the-trade-records-of-a-order)
|
||||
|
||||
|
||||
### Public enpoints
|
||||
|
||||
#### async getOrderBook(params)
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#get-orderbook)
|
||||
|
||||
#### async getLatestInformation()
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/rest_api.md#latest-information-for-symbol)
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const {RestClient} = require('@pxtrn/bybit-api');
|
||||
|
||||
const API_KEY = 'xxx';
|
||||
const PRIVATE_KEY = 'yyy';
|
||||
|
||||
const client = new RestClient(API_KEY, PRIVATE_KEY);
|
||||
|
||||
client.changeUserLeverage({leverage: 4, symbol: 'ETHUSD'})
|
||||
.then(result => {
|
||||
console.log(result);
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(error);
|
||||
});
|
||||
```
|
||||
183
doc/websocket-client.md
Normal file
183
doc/websocket-client.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Websocket API
|
||||
|
||||
|
||||
## Class: WebsocketClient
|
||||
|
||||
The `WebsocketClient` inherits from `EventEmitter`. After establishing a
|
||||
connection, the client sends heartbeats in regular intervalls, and reconnects
|
||||
to the server once connection has been lost.
|
||||
|
||||
|
||||
### new WebsocketClient([options][, logger])
|
||||
- `options` {Object} Configuration options
|
||||
- `key` {String} Bybit API Key. Only needed if private topics are subscribed
|
||||
- `secret` {String} Bybit private Key. Only needed if private topics are
|
||||
subscribed
|
||||
- `livenet` {Bool} Weather to connect to livenet (`true`). Default `false`.
|
||||
- `pingInterval` {Integer} Interval in ms for heartbeat ping. Default: `10000`,
|
||||
- `pongTimeout` {Integer} Timeout in ms waiting for heartbeat pong response
|
||||
from server. Default: `1000`,
|
||||
- `reconnectTimeout` {Integer} Timeout in ms the client waits before trying
|
||||
to reconnect after a lost connection. Default: 500
|
||||
- `logger` {Object} Optional custom logger
|
||||
|
||||
Custom logger must contain the following methods:
|
||||
```js
|
||||
const logger = {
|
||||
debug: function(message, data) {}
|
||||
notice: function(message, data) {}
|
||||
info: function(message, data) {}
|
||||
warning: function(message, data) {}
|
||||
error: function(message, data) {},
|
||||
}
|
||||
```
|
||||
|
||||
### ws.subscribe(topics)
|
||||
|
||||
- `topics` {String|Array} Single topic as string or multiple topics as array of strings.
|
||||
Subscribe to one or multiple topics. See [available topics](#available-topics)
|
||||
|
||||
|
||||
### ws.close()
|
||||
|
||||
Close the connection to the server.
|
||||
|
||||
|
||||
### Event: 'open'
|
||||
|
||||
Emmited when the connection has been opened.
|
||||
|
||||
|
||||
### Event: 'update'
|
||||
|
||||
- `message` {Object}
|
||||
- `topic` {String} the topic for which the update occured
|
||||
- `data` {Array|Object} updated data (see docs for each [topic](#available-topics)).
|
||||
- `type` {String} Some topics might have different update types (see docs for each [topic](#available-topics)).
|
||||
|
||||
Emmited whenever an update to a subscribed topic occurs.
|
||||
|
||||
|
||||
### Event: 'response'
|
||||
|
||||
- `response` {Object}
|
||||
- `success` {Bool}
|
||||
- `ret_msg` {String} empty if operation was successfull, otherwise error message.
|
||||
- `conn_id` {String} connection id
|
||||
- `request` {Object} Original request, to which the response belongs
|
||||
- `op` {String} operation
|
||||
- `args` {Array} Request Arguments
|
||||
|
||||
Emited when the server responds to an operation sent by the client (usually after subscribing to a topic).
|
||||
|
||||
|
||||
### Event: 'close'
|
||||
|
||||
Emitted when the connection has been closed.
|
||||
|
||||
|
||||
### Event: 'error'
|
||||
|
||||
- `error` {Error}
|
||||
|
||||
Emitted when an error occurs.
|
||||
|
||||
|
||||
## Available Topics
|
||||
|
||||
Generaly all topics as described in the
|
||||
[official bybit api documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md)
|
||||
are available.
|
||||
|
||||
### Private topics
|
||||
|
||||
#### Positions of your account
|
||||
|
||||
All positions of your account.
|
||||
Topic: `position`
|
||||
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md#positions-of-your-account)
|
||||
|
||||
#### Execution message
|
||||
|
||||
Execution message, whenever an order has been (partially) filled.
|
||||
Topic: `execution`
|
||||
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md#execution-message)
|
||||
|
||||
#### Update for your orders
|
||||
|
||||
Updates for your active orders
|
||||
Topic: `order`
|
||||
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md#update-for-your-orders)
|
||||
|
||||
|
||||
### Public topics
|
||||
|
||||
#### Candlestick chart
|
||||
|
||||
Candlestick OHLC "candles" for selected symbol and interval.
|
||||
Example topic: `kline.BTCUSD.1m`
|
||||
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md#kline)
|
||||
|
||||
#### Real-time trading information
|
||||
|
||||
All trades as they occur.
|
||||
Topic: `trade`
|
||||
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md#trade)
|
||||
|
||||
#### Daily insurance fund update
|
||||
|
||||
Topic: `insurance`
|
||||
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md#daily-insurance-fund-update)
|
||||
|
||||
#### OrderBook of 25 depth per side
|
||||
|
||||
OrderBook for selected symbol
|
||||
Example topic: `orderBookL2_25.BTCUSD`
|
||||
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md#orderBook25_v2)
|
||||
|
||||
#### Latest information for symbol
|
||||
|
||||
LAtest information for selected symbol
|
||||
Example topic: `instrument_info.100ms.BTCUSD`
|
||||
|
||||
[See bybit documentation](https://github.com/bybit-exchange/bybit-official-api-docs/blob/master/en/websocket.md#instrument_info)
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
```js
|
||||
const {WebsocketClient} = require('@pxtrn/bybit-api');
|
||||
|
||||
const API_KEY = 'xxx';
|
||||
const PRIVATE_KEY = 'yyy';
|
||||
|
||||
const ws = new WebsocketClient({key: API_KEY, secret: PRIVATE_KEY});
|
||||
|
||||
ws.on('open', function() {
|
||||
ws.subscribe(['position', 'execution', 'trade']);
|
||||
ws.subscribe('kline.BTCUSD.1m');
|
||||
});
|
||||
|
||||
ws.on('update', function(message) {
|
||||
console.log('update', message);
|
||||
});
|
||||
|
||||
ws.on('response', function(response) {
|
||||
console.log('response', response);
|
||||
});
|
||||
|
||||
ws.on('close', function() {
|
||||
console.log('connection closed');
|
||||
});
|
||||
|
||||
ws.on('error', function(err) {
|
||||
console.error('ERR', err);
|
||||
});
|
||||
```
|
||||
7
index.js
Normal file
7
index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const RestClient = require('./lib/rest-client.js');
|
||||
const WebsocketClient = require('./lib/websocket-client.js');
|
||||
|
||||
module.exports = {
|
||||
RestClient,
|
||||
WebsocketClient,
|
||||
}
|
||||
8
lib/logger.js
Normal file
8
lib/logger.js
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
module.exports = {
|
||||
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)},
|
||||
}
|
||||
92
lib/request.js
Normal file
92
lib/request.js
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const request = require('request');
|
||||
|
||||
const {signMessage} = require('./utility.js');
|
||||
|
||||
const baseUrls = {
|
||||
livenet: 'https://api.bybit.com',
|
||||
testnet: 'https://api-testnet.bybit.com'
|
||||
}
|
||||
|
||||
module.exports = class Request {
|
||||
|
||||
constructor(key, secret, livenet=false) {
|
||||
this.baseUrl = baseUrls[livenet === true ? 'livenet' : 'testnet'];
|
||||
|
||||
if(key) assert(secret, 'Secret is required for private enpoints');
|
||||
|
||||
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 _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');
|
||||
|
||||
params = this._signRequest(params);
|
||||
}
|
||||
|
||||
const options = {
|
||||
url: [this.baseUrl, endpoint].join('/'),
|
||||
method: method,
|
||||
json: true
|
||||
};
|
||||
|
||||
switch(method) {
|
||||
case 'GET':
|
||||
options.qs = params
|
||||
break;
|
||||
case 'POST':
|
||||
options.body = params
|
||||
break;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
request(options, function callback(error, response, body) {
|
||||
if(!error && response.statusCode == 200) {
|
||||
resolve(body);
|
||||
} else if(error) {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
_signRequest(data) {
|
||||
const params = {
|
||||
...data,
|
||||
api_key: this.key,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
|
||||
if(this.key && this.secret) {
|
||||
params.sign = signMessage(this._serializeParams(params), this.secret);
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
_serializeParams(params) {
|
||||
return Object.keys(params)
|
||||
.sort()
|
||||
.map(key => `${key}=${params[key]}`)
|
||||
.join('&');
|
||||
}
|
||||
}
|
||||
124
lib/rest-client.js
Normal file
124
lib/rest-client.js
Normal file
@@ -0,0 +1,124 @@
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
const Request = require('./request.js');
|
||||
|
||||
module.exports = class RestClient {
|
||||
|
||||
constructor(key, secret, livenet=false) {
|
||||
this.request = new Request(...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('/open-api/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.order_id, 'Parameter order_id is required');
|
||||
|
||||
return await this.request.post('/open-api/order/cancel', 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.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 getConditioanlOrder(params) {
|
||||
return await this.request.get('/open-api/stop-order/list', params);
|
||||
}
|
||||
|
||||
async cancelConditionalOrder(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 getUserLeverage() {
|
||||
return await this.request.get('/user/leverage');
|
||||
}
|
||||
|
||||
async changeUserLeverage(params) {
|
||||
assert(params, 'No params passed');
|
||||
assert(params.leverage, 'Parameter leverage is required');
|
||||
assert(params.symbol, 'Parameter symbol is required');
|
||||
|
||||
return await this.request.post('/user/leverage/save', params);
|
||||
}
|
||||
|
||||
async getPosition() {
|
||||
return await this.request.get('/position/list');
|
||||
}
|
||||
|
||||
async changePositionMargin(params) {
|
||||
assert(params, 'No params passed');
|
||||
assert(params.margin, 'Parameter margin is required');
|
||||
assert(params.symbol, 'Parameter symbol is required');
|
||||
|
||||
return await this.request.post('/position/change-position-margin', params);
|
||||
}
|
||||
|
||||
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 getOrderTradeRecords(params) {
|
||||
assert(params, 'No params passed');
|
||||
assert(params.order_id, 'Parameter order_id 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 getLatestInformation() {
|
||||
return await this.request.get('/v2/public/tickers');
|
||||
}
|
||||
}
|
||||
9
lib/utility.js
Normal file
9
lib/utility.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const {createHmac} = require('crypto');
|
||||
|
||||
module.exports = {
|
||||
signMessage(message, secret) {
|
||||
return createHmac('sha256', secret)
|
||||
.update(message)
|
||||
.digest('hex');
|
||||
}
|
||||
}
|
||||
177
lib/websocket-client.js
Normal file
177
lib/websocket-client.js
Normal file
@@ -0,0 +1,177 @@
|
||||
const {EventEmitter} = require('events');
|
||||
|
||||
const WebSocket = require('ws');
|
||||
|
||||
const defaultLogger = require('./logger.js');
|
||||
const {signMessage} = require('./utility.js');
|
||||
|
||||
const wsUrls = {
|
||||
livenet: 'wss://stream.bybit.com/realtime',
|
||||
testnet: 'wss://stream-testnet.bybit.com/realtime'
|
||||
};
|
||||
|
||||
const READY_STATE_INITIAL = 0;
|
||||
const READY_STATE_CONNECTING = 1;
|
||||
const READY_STATE_CONNECTED = 2;
|
||||
const READY_STATE_CLOSING = 3;
|
||||
|
||||
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._connect();
|
||||
}
|
||||
|
||||
subscribe(topics) {
|
||||
if(!Array.isArray(topics)) topics = [topics];
|
||||
|
||||
const msgStr = JSON.stringify({
|
||||
op: 'subscribe',
|
||||
'args': topics
|
||||
});
|
||||
|
||||
this.ws.send(msgStr);
|
||||
}
|
||||
|
||||
close() {
|
||||
this.logger.info('Closing connection', {category: 'bybit-ws'});
|
||||
this.readyState = READY_STATE_CLOSING;
|
||||
this._teardown();
|
||||
this.ws.close();
|
||||
}
|
||||
|
||||
_connect() {
|
||||
if(this.readyState === READY_STATE_INITIAL) this.readyState = READY_STATE_CONNECTING;
|
||||
|
||||
const url = wsUrls[this.options.livenet ? 'livenet' : 'testnet'] + this._authenticate();
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
_authenticate() {
|
||||
if(this.options.key && this.options.secret) {
|
||||
this.logger.debug('Starting authenticated websocket client.', {category: 'bybit-ws'});
|
||||
const params = {
|
||||
api_key: this.options.key,
|
||||
expires: (Date.now() + 10000)
|
||||
};
|
||||
|
||||
params.signature = signMessage('GET/realtime' + params.expires, this.options.secret);
|
||||
|
||||
return '?' + Object.keys(params)
|
||||
.sort()
|
||||
.map(key => `${key}=${params[key]}`)
|
||||
.join('&');
|
||||
} 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 '';
|
||||
}
|
||||
|
||||
_reconnect(timeout) {
|
||||
this._teardown();
|
||||
|
||||
setTimeout(() => {
|
||||
this.logger.info('Reconnecting to server', {category: 'bybit-ws'});
|
||||
|
||||
this._connect();
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
_ping() {
|
||||
clearTimeout(this.pongTimeout);
|
||||
this.pongTimeout = null;
|
||||
|
||||
this.logger.debug('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');
|
||||
}
|
||||
|
||||
this.readyState = READY_STATE_CONNECTED;
|
||||
this.pingInterval = setInterval(this._ping.bind(this), this.options.pingInterval);
|
||||
}
|
||||
|
||||
_wsMessageHandler(message) {
|
||||
let 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);
|
||||
}
|
||||
}
|
||||
|
||||
_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);
|
||||
} else {
|
||||
this.readyState = READY_STATE_INITIAL;
|
||||
this.emit('close');
|
||||
}
|
||||
}
|
||||
|
||||
_handleResponse(response) {
|
||||
if(response.request && response.request.op === 'ping' && response.ret_msg === 'pong') {
|
||||
if(response.success === true) {
|
||||
this.logger.debug('pong recieved', {category: 'bybit-ws'});
|
||||
clearTimeout(this.pongTimeout);
|
||||
}
|
||||
} else {
|
||||
this.emit('response', response);
|
||||
}
|
||||
}
|
||||
|
||||
_handleUpdate(message) {
|
||||
this.emit('update', message);
|
||||
}
|
||||
}
|
||||
359
package-lock.json
generated
Normal file
359
package-lock.json
generated
Normal file
@@ -0,0 +1,359 @@
|
||||
{
|
||||
"name": "@pxtrn/bybit-api",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
|
||||
"requires": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||
},
|
||||
"async-limiter": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
|
||||
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||
"requires": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
|
||||
"requires": {
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
|
||||
},
|
||||
"fast-json-stable-stringify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
"mime-types": "^2.1.12"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
||||
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
||||
"requires": {
|
||||
"ajv": "^6.5.5",
|
||||
"har-schema": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
"sshpk": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
"json-schema": "0.2.3",
|
||||
"verror": "1.10.0"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.40.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
|
||||
"integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.24",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
|
||||
"integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
|
||||
"requires": {
|
||||
"mime-db": "1.40.0"
|
||||
}
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.3.1.tgz",
|
||||
"integrity": "sha512-2KLd5fKOdAfShtY2d/8XDWVRnmp3zp40Qt6ge2zBPFARLXOGUf2fHD5eg+TV/5oxBtQKVhjUaKFsAaE4HnwfSA=="
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||
},
|
||||
"request": {
|
||||
"version": "2.88.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
|
||||
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
|
||||
"requires": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.8.0",
|
||||
"caseless": "~0.12.0",
|
||||
"combined-stream": "~1.0.6",
|
||||
"extend": "~3.0.2",
|
||||
"forever-agent": "~0.6.1",
|
||||
"form-data": "~2.3.2",
|
||||
"har-validator": "~5.1.0",
|
||||
"http-signature": "~1.2.0",
|
||||
"is-typedarray": "~1.0.0",
|
||||
"isstream": "~0.1.2",
|
||||
"json-stringify-safe": "~5.0.1",
|
||||
"mime-types": "~2.1.19",
|
||||
"oauth-sign": "~0.9.0",
|
||||
"performance-now": "^2.1.0",
|
||||
"qs": "~6.5.2",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"tough-cookie": "~2.4.3",
|
||||
"tunnel-agent": "^0.6.0",
|
||||
"uuid": "^3.3.2"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
||||
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
|
||||
"requires": {
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
"bcrypt-pbkdf": "^1.0.0",
|
||||
"dashdash": "^1.12.0",
|
||||
"ecc-jsbn": "~0.1.1",
|
||||
"getpass": "^0.1.1",
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.0.2",
|
||||
"tweetnacl": "~0.14.0"
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
||||
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
|
||||
"requires": {
|
||||
"psl": "^1.1.24",
|
||||
"punycode": "^1.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||
}
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||
},
|
||||
"uri-js": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
|
||||
"integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ=="
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"extsprintf": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"ws": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz",
|
||||
"integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==",
|
||||
"requires": {
|
||||
"async-limiter": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
package.json
Normal file
29
package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@pxtrn/bybit-api",
|
||||
"version": "1.0.0",
|
||||
"description": "An unofficial node.js lowlevel wrapper for the Bybit Cryptocurrency Derivative exchange API",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/pixtron/bybit-api.git"
|
||||
},
|
||||
"keywords": [
|
||||
"bybit",
|
||||
"api",
|
||||
"websocket",
|
||||
"rest"
|
||||
],
|
||||
"author": "Stefan Aebischer <os@pixtron.ch> (https://pixtron.ch)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pixtron/bybit-api/issues"
|
||||
},
|
||||
"homepage": "https://github.com/pixtron/bybit-api#readme",
|
||||
"dependencies": {
|
||||
"request": "^2.88.0",
|
||||
"ws": "^7.1.2"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user