Skip to content

Commit

Permalink
Merge pull request #108 from tiagosiebler/fixes
Browse files Browse the repository at this point in the history
fix reversed secondary endpoints(#106). Add spot websocket & REST client (#99)
  • Loading branch information
tiagosiebler committed Aug 15, 2021
2 parents 852f651 + 72c7630 commit 198cfb0
Show file tree
Hide file tree
Showing 11 changed files with 828 additions and 42 deletions.
67 changes: 52 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ 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.
- [examples](./examples) - some implementation examples & demonstrations. Contributions are welcome!

---

Expand All @@ -42,7 +43,7 @@ There are three REST API modules as there are some differences in each contract
3. `LinearClient` for linear perpetual

### REST Inverse
<details><summary>To use the inverse REST APIs, import the `InverseClient`. Click here to expand and see full sample:</summary>
To use the inverse REST APIs, import the `InverseClient`:

```javascript
const { InverseClient } = require('bybit-api');
Expand Down Expand Up @@ -100,12 +101,11 @@ client.getOrderBook({ symbol: 'BTCUSD' })
});
```

</details>

See [inverse-client.ts](./src/inverse-client.ts) for further information.

### REST Inverse Futures
<details><summary>To use the inverse futures REST APIs, import the `InverseFuturesClient`. Click here to expand and see full sample:</summary>
To use the inverse futures REST APIs, import the `InverseFuturesClient`:

```javascript
const { InverseFuturesClient } = require('bybit-api');
Expand Down Expand Up @@ -142,12 +142,10 @@ client.getOrderBook({ symbol: 'BTCUSDH21' })
});
```

</details>

See [inverse-futures-client.ts](./src/inverse-futures-client.ts) for further information.

### REST Linear
<details><summary>To use the Linear (USDT) REST APIs, import the `LinearClient`. Click here to expand and see full sample:</summary>
To use the Linear (USDT) REST APIs, import the `LinearClient`:

```javascript
const { LinearClient } = require('bybit-api');
Expand Down Expand Up @@ -184,10 +182,50 @@ client.getOrderBook({ symbol: 'BTCUSDT' })
});
```

</details>
See [linear-client.ts](./src/linear-client.ts) for further information.

### REST Spot
To use the Spot REST APIs, import the `SpotClient`:

```javascript
const { SpotClient } = require('bybit-api');

const API_KEY = 'xxx';
const PRIVATE_KEY = 'yyy';
const useLivenet = false;

const client = new javascript(
API_KEY,
PRIVATE_KEY,

// optional, uses testnet by default. Set to 'true' to use livenet.
useLivenet,

// restClientOptions,
// requestLibraryOptions
);

client.getSymbols()
.then(result => {
console.log(result);
})
.catch(err => {
console.error(err);
});

client.getBalances()
.then(result => {
console.log("getBalances result: ", result);
})
.catch(err => {
console.error("getBalances error: ", err);
});
```

See [spot-client.ts](./src/spot-client.ts) for further information.

## WebSockets
<details><summary>Inverse & linear WebSockets can be used via a shared `WebsocketClient`. Click here to expand and see full sample:</summary>
Inverse, linear & spot WebSockets can be used via a shared `WebsocketClient`. However, make sure to make one instance of WebsocketClient per market type (spot vs inverse vs linear vs linearfutures):

```javascript
const { WebsocketClient } = require('bybit-api');
Expand All @@ -206,8 +244,12 @@ const wsConfig = {
// defaults to false == testnet. Set to true for livenet.
// livenet: true

// defaults to false == inverse. Set to true for linear (USDT) trading.
// linear: true
// NOTE: to listen to multiple markets (spot vs inverse vs linear vs linearfutures) at once, make one WebsocketClient instance per market

// defaults to inverse:
// market: 'inverse'
// market: 'linear'
// market: 'spot'

// how long to wait (in ms) before deciding the connection should be terminated & reconnected
// pongTimeout: 1000,
Expand Down Expand Up @@ -263,7 +305,6 @@ ws.on('error', err => {
});
```

</details>

See [websocket-client.ts](./src/websocket-client.ts) for further information.

Expand All @@ -274,8 +315,6 @@ Note: for linear websockets, pass `linear: true` in the constructor options when
## Customise Logging
Pass a custom logger which supports the log methods `silly`, `debug`, `notice`, `info`, `warning` and `error`, or override methods from the default logger as desired.

<details><summary>Click here to expand and see full sample:</summary>

```javascript
const { WebsocketClient, DefaultLogger } = require('bybit-api');

Expand All @@ -288,8 +327,6 @@ const ws = new WebsocketClient(
);
```

</details>

## Browser Usage
Build a bundle using webpack:
- `npm install`
Expand Down
18 changes: 18 additions & 0 deletions examples/rest-spot-public.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { SpotClient } from '../src/index';

// or
// import { SpotClient } from 'bybit-api';

const client = new SpotClient();

const symbol = 'BTCUSDT';

(async () => {
try {
// console.log('getSymbols: ', await client.getSymbols());
// console.log('getOrderBook: ', await client.getOrderBook(symbol));
console.log('getOrderBook: ', await client.getOrderBook(symbol));
} catch (e) {
console.error('request failed: ', e);
}
})();
55 changes: 55 additions & 0 deletions examples/ws-public.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { DefaultLogger } from '../src';
import { WebsocketClient, wsKeySpotPublic } from '../src/websocket-client';

// or
// import { DefaultLogger, WebsocketClient } from 'bybit-api';

(async () => {
const logger = {
...DefaultLogger,
// silly: () => {},
};

const wsClient = new WebsocketClient({
// key: key,
// secret: secret,
// market: 'inverse',
// market: 'linear',
market: 'spot',
}, logger);

wsClient.on('update', (data) => {
console.log('raw message received ', JSON.stringify(data, null, 2));
});

wsClient.on('open', (data) => {
console.log('connection opened open:', data.wsKey);

if (data.wsKey === wsKeySpotPublic) {
// Spot public.
// wsClient.subscribePublicSpotTrades('BTCUSDT');
// wsClient.subscribePublicSpotTradingPair('BTCUSDT');
// wsClient.subscribePublicSpotV1Kline('BTCUSDT', '1m');
// wsClient.subscribePublicSpotOrderbook('BTCUSDT', 'full');
}
});
wsClient.on('response', (data) => {
console.log('log response: ', JSON.stringify(data, null, 2));
});
wsClient.on('reconnect', ({ wsKey }) => {
console.log('ws automatically reconnecting.... ', wsKey);
});
wsClient.on('reconnected', (data) => {
console.log('ws has reconnected ', data?.wsKey );
});

// Inverse
// wsClient.subscribe('trade');

// Linear
// wsClient.subscribe('trade.BTCUSDT');

// For spot, request public connection first then send required topics on 'open'
// wsClient.connectPublic();

})();
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bybit-api",
"version": "2.0.7",
"version": "2.1.0",
"description": "Node.js connector for Bybit's REST APIs and WebSockets, with TypeScript & integration tests.",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from './inverse-client';
export * from './inverse-futures-client';
export * from './linear-client';
export * from './spot-client';
export * from './websocket-client';
export * from './logger';
157 changes: 157 additions & 0 deletions src/spot-client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import { AxiosRequestConfig } from 'axios';
import { KlineInterval } from './types/shared';
import { NewSpotOrder, OrderSide, OrderTypeSpot, SpotOrderQueryById } from './types/spot';
import BaseRestClient from './util/BaseRestClient';
import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils';
import RequestWrapper from './util/requestWrapper';

export class SpotClient extends BaseRestClient {
protected requestWrapper: RequestWrapper;

/**
* @public Creates an instance of the Spot REST API client.
*
* @param {string} key - your API key
* @param {string} secret - your API secret
* @param {boolean} [useLivenet=false]
* @param {RestClientOptions} [restClientOptions={}] options to configure REST API connectivity
* @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios
*/
constructor(
key?: string | undefined,
secret?: string | undefined,
useLivenet: boolean = false,
restClientOptions: RestClientOptions = {},
requestOptions: AxiosRequestConfig = {}
) {
super(key, secret, getRestBaseUrl(useLivenet, restClientOptions), restClientOptions, requestOptions);

// this.requestWrapper = new RequestWrapper(
// key,
// secret,
// getRestBaseUrl(useLivenet, restClientOptions),
// restClientOptions,
// requestOptions
// );
return this;
}

async getServerTime(urlKeyOverride?: string): Promise<number> {
const result = await this.get('/spot/v1/time');
return result.serverTime;
}

/**
*
* Market Data Endpoints
*
**/

getSymbols() {
return this.get('/spot/v1/symbols');
}

getOrderBook(symbol: string, limit?: number) {
return this.get('/spot/quote/v1/depth', {
symbol, limit
});
}

getMergedOrderBook(symbol: string, scale?: number, limit?: number) {
return this.get('/spot/quote/v1/depth/merged', {
symbol,
scale,
limit,
});
}

getTrades(symbol: string, limit?: number) {
return this.get('/spot/v1/trades', {
symbol,
limit,
});
}

getCandles(symbol: string, interval: KlineInterval, limit?: number, startTime?: number, endTime?: number) {
return this.get('/spot/v1/trades', {
symbol,
interval,
limit,
startTime,
endTime,
});
}

get24hrTicker(symbol?: string) {
return this.get('/spot/quote/v1/ticker/24hr', { symbol });
}

getLastTradedPrice(symbol?: string) {
return this.get('/spot/quote/v1/ticker/price', { symbol });
}

getBestBidAskPrice(symbol?: string) {
return this.get('/spot/quote/v1/ticker/book_ticker', { symbol });
}

/**
* Account Data Endpoints
*/

submitOrder(params: NewSpotOrder) {
return this.postPrivate('/spot/v1/order', params);
}

getOrder(params: SpotOrderQueryById) {
return this.getPrivate('/spot/v1/order', params);
}

cancelOrder(params: SpotOrderQueryById) {
return this.deletePrivate('/spot/v1/order', params);
}

cancelOrderBatch(params: {
symbol: string;
side?: OrderSide;
orderTypes: OrderTypeSpot[]
}) {
const orderTypes = params.orderTypes ? params.orderTypes.join(',') : undefined;
return this.deletePrivate('/spot/order/batch-cancel', {
...params,
orderTypes,
});
}

getOpenOrders(symbol?: string, orderId?: string, limit?: number) {
return this.getPrivate('/spot/v1/open-orders', {
symbol,
orderId,
limit,
});
}

getPastOrders(symbol?: string, orderId?: string, limit?: number) {
return this.getPrivate('/spot/v1/history-orders', {
symbol,
orderId,
limit,
});
}

getMyTrades(symbol?: string, limit?: number, fromId?: number, toId?: number) {
return this.getPrivate('/spot/v1/myTrades', {
symbol,
limit,
fromId,
toId,
});
}

/**
* Wallet Data Endpoints
*/

getBalances() {
return this.getPrivate('/spot/v1/account');
}
}
Loading

0 comments on commit 198cfb0

Please sign in to comment.