Initial commit

This commit is contained in:
Stefan Aebischer
2019-09-14 16:02:11 +02:00
commit ed28d14171
13 changed files with 1184 additions and 0 deletions

21
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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"
}
}