diff --git a/README.md b/README.md
index ff9f3c00..943ba198 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
[1]: https://www.npmjs.com/package/bybit-api
-A production-ready Node.js connector for the Bybit APIs and WebSockets, with TypeScript & browser support.
+Node.js connector for the Bybit APIs and WebSockets, with TypeScript & browser support.
## Installation
`npm install --save bybit-api`
@@ -13,11 +13,11 @@ A production-ready Node.js connector for the Bybit APIs and WebSockets, with Typ
## Issues & Discussion
- Issues? Check the [issues tab](https://github.com/tiagosiebler/bybit-api/issues).
- Discuss & collaborate with other node devs? Join our [Node.js Algo Traders](https://t.me/nodetraders) engineering community on telegram.
-- `'bybit-api' has no exported member 'RestClient'`: use `InverseClient` instead of `RestClient`
## Documentation
Most methods accept JS objects. These can be populated using parameters specified by Bybit's API documentation.
- [Bybit API Inverse Documentation](https://bybit-exchange.github.io/docs/inverse/#t-introduction).
+- [Bybit API Inverse Futures Documentation](https://bybit-exchange.github.io/docs/inverse_futures/#t-introduction).
- [Bybit API Linear Documentation](https://bybit-exchange.github.io/docs/linear/#t-introduction)
## Structure
@@ -26,21 +26,22 @@ This project uses typescript. Resources are stored in 3 key structures:
- [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.
-## Usage
+---
+
+# Usage
Create API credentials at Bybit
- [Livenet](https://bybit.com/app/user/api-management?affiliate_id=9410&language=en-US&group_id=0&group_type=1)
- [Testnet](https://testnet.bybit.com/app/user/api-management)
-### Browser Usage
-Build a bundle using webpack:
-- `npm install`
-- `npm build`
-- `npm pack`
+## REST API Clients
-The bundle can be found in `dist/`. Altough usage should be largely consistent, smaller differences will exist. Documentation is still TODO.
+There are three REST API modules as there are some differences in each contract type.
+1. `InverseClient` for inverse perpetual
+2. `InverseFuturesClient` for inverse futures
+3. `LinearClient` for linear perpetual
-### Inverse Contracts
-Since inverse and linear (USDT) contracts don't use the exact same APIs, the REST abstractions are split into two modules. To use the inverse REST APIs, import the `InverseClient`:
+### REST Inverse
+To use the inverse REST APIs, import the `InverseClient`. Click here to expand and see full sample:
```javascript
const { InverseClient } = require('bybit-api');
@@ -98,34 +99,59 @@ client.getOrderBook({ symbol: 'BTCUSD' })
});
```
-See inverse [inverse-client.ts](./src/inverse-client.ts) for further information.
+
+
+See [inverse-client.ts](./src/inverse-client.ts) for further information.
-### Linear Contracts
-To use the Linear (USDT) REST APIs, import the `LinearClient`:
+### REST Inverse Futures
+To use the inverse futures REST APIs, import the `InverseFuturesClient`. Click here to expand and see full sample:
```javascript
-const { LinearClient } = require('bybit-api');
+const { InverseFuturesClient } = require('bybit-api');
-const restClientOptions = {
- // override the max size of the request window (in ms)
- recv_window?: number;
+const API_KEY = 'xxx';
+const PRIVATE_KEY = 'yyy';
+const useLivenet = false;
- // how often to sync time drift with bybit servers
- sync_interval_ms?: number | string;
+const client = new InverseFuturesClient(
+ API_KEY,
+ PRIVATE_KEY,
- // Default: false. Disable above sync mechanism if true.
- disable_time_sync?: boolean;
+ // optional, uses testnet by default. Set to 'true' to use livenet.
+ useLivenet,
- // Default: false. If true, we'll throw errors if any params are undefined
- strict_param_validation?: boolean;
+ // restClientOptions,
+ // requestLibraryOptions
+);
- // Optionally override API protocol + domain
- // e.g 'https://api.bytick.com'
- baseUrl?: string;
+client.getApiKeyInfo()
+ .then(result => {
+ console.log("apiKey result: ", result);
+ })
+ .catch(err => {
+ console.error("apiKey error: ", err);
+ });
- // Default: true. whether to try and post-process request exceptions.
- parse_exceptions?: boolean;
-};
+client.getOrderBook({ symbol: 'BTCUSDH21' })
+ .then(result => {
+ console.log("getOrderBook inverse futures result: ", result);
+ })
+ .catch(err => {
+ console.error("getOrderBook inverse futures error: ", err);
+ });
+```
+
+
+
+See [inverse-futures-client.ts](./src/inverse-futures-client.ts) for further information.
+
+**Note**: as of 6th March 2021 this is currently only for testnet. See the [Bybit API documentation](https://bybit-exchange.github.io/docs/inverse_futures/#t-introduction) for official updates.
+
+### REST Linear
+To use the Linear (USDT) REST APIs, import the `LinearClient`. Click here to expand and see full sample:
+
+```javascript
+const { LinearClient } = require('bybit-api');
const API_KEY = 'xxx';
const PRIVATE_KEY = 'yyy';
@@ -159,13 +185,10 @@ client.getOrderBook({ symbol: 'BTCUSDT' })
});
```
-### WebSockets
-
-Inverse & linear WebSockets can be used via a shared `WebsocketClient`.
+
-Note: to use the linear websockets, pass "linear: true" in the constructor options when instancing the `WebsocketClient`.
-
-To connect to both linear and inverse websockets, make two instances of the WebsocketClient:
+## WebSockets
+Inverse & linear WebSockets can be used via a shared `WebsocketClient`. Click here to expand and see full sample:
```javascript
const { WebsocketClient } = require('bybit-api');
@@ -240,12 +263,21 @@ ws.on('error', err => {
console.error('ERR', err);
});
```
+
+
+
See [websocket-client.ts](./src/websocket-client.ts) for further information.
-### 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:
+Note: for linear websockets, pass `linear: true` in the constructor options when instancing the `WebsocketClient`. To connect to both linear and inverse websockets, make two instances of the WebsocketClient.
+
+---
+
+## 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.
-```js
+Click here to expand and see full sample:
+
+```javascript
const { WebsocketClient, DefaultLogger } = require('bybit-api');
// Disable all logging on the silly level
@@ -257,6 +289,20 @@ const ws = new WebsocketClient(
);
```
+
+
+## Browser Usage
+Build a bundle using webpack:
+- `npm install`
+- `npm build`
+- `npm pack`
+
+The bundle can be found in `dist/`. Altough usage should be largely consistent, smaller differences will exist. Documentation is still TODO.
+
+However, note that browser usage will lead to CORS errors due Bybit. See [issue #79](#79) for more information & alternative suggestions.
+
+---
+
## Contributions & Thanks
### Donations
#### tiagosiebler
diff --git a/package.json b/package.json
index 6568d7c1..ed63cc9d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "bybit-api",
- "version": "2.0.2",
+ "version": "2.0.3",
"description": "Node.js connector for Bybit's Inverse & Linear REST APIs and WebSockets",
"main": "lib/index.js",
"types": "lib/index.d.ts",
diff --git a/src/index.ts b/src/index.ts
index bc82e2e4..bdd98508 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,5 @@
export * from './inverse-client';
+export * from './inverse-futures-client';
export * from './linear-client';
export * from './websocket-client';
export * from './logger';
diff --git a/src/inverse-client.ts b/src/inverse-client.ts
index 2a1779e8..80520bdc 100644
--- a/src/inverse-client.ts
+++ b/src/inverse-client.ts
@@ -18,7 +18,7 @@ export class InverseClient extends SharedEndpoints {
constructor(
key?: string | undefined,
secret?: string | undefined,
- useLivenet?: boolean,
+ useLivenet: boolean = false,
restClientOptions: RestClientOptions = {},
requestOptions: AxiosRequestConfig = {}
) {
@@ -282,6 +282,7 @@ export class InverseClient extends SharedEndpoints {
symbol: string;
take_profit?: number;
stop_loss?: number;
+ trailing_stop?: number;
tp_trigger_by?: string;
sl_trigger_by?: string;
new_trailing_active?: number;
diff --git a/src/inverse-futures-client.ts b/src/inverse-futures-client.ts
new file mode 100644
index 00000000..fcd4cd09
--- /dev/null
+++ b/src/inverse-futures-client.ts
@@ -0,0 +1,354 @@
+import { AxiosRequestConfig } from 'axios';
+import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils';
+import RequestWrapper from './util/requestWrapper';
+import SharedEndpoints from './shared-endpoints';
+
+export class InverseFuturesClient extends SharedEndpoints {
+ protected requestWrapper: RequestWrapper;
+
+ /**
+ * @public Creates an instance of the inverse futures 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()
+ this.requestWrapper = new RequestWrapper(
+ key,
+ secret,
+ getRestBaseUrl(useLivenet, restClientOptions),
+ restClientOptions,
+ requestOptions
+ );
+ return this;
+ }
+
+ /**
+ *
+ * Market Data Endpoints
+ * Note: These are currently the same as the inverse client
+ */
+
+ getKline(params: {
+ symbol: string;
+ interval: string;
+ from: number;
+ limit?: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/public/kline/list', params);
+ }
+
+ /**
+ * Public trading records
+ */
+ getTrades(params: {
+ symbol: string;
+ from?: number;
+ limit?: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/public/trading-records', params);
+ }
+
+ getMarkPriceKline(params: {
+ symbol: string;
+ interval: string;
+ from: number;
+ limit?: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/public/mark-price-kline', params);
+ }
+
+ getIndexPriceKline(params: {
+ symbol: string;
+ interval: string;
+ from: number;
+ limit?: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/public/index-price-kline', params);
+ }
+
+ getPremiumIndexKline(params: {
+ symbol: string;
+ interval: string;
+ from: number;
+ limit?: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/public/premium-index-kline', params);
+ }
+
+ /**
+ *
+ * Account Data Endpoints
+ *
+ */
+
+ /**
+ * Active orders
+ */
+
+ placeActiveOrder(orderRequest: {
+ side: string;
+ symbol: string;
+ order_type: string;
+ qty: number;
+ price?: number;
+ time_in_force: string;
+ take_profit?: number;
+ stop_loss?: number;
+ reduce_only?: boolean;
+ close_on_trigger?: boolean;
+ order_link_id?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/order/create', orderRequest);
+ }
+
+ getActiveOrderList(params: {
+ symbol: string;
+ order_status?: string;
+ direction?: string;
+ limit?: number;
+ cursor?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('futures/private/order/list', params);
+ }
+
+ cancelActiveOrder(params: {
+ symbol: string;
+ order_id?: string;
+ order_link_id?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/order/cancel', params);
+ }
+
+ cancelAllActiveOrders(params: {
+ symbol: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/order/cancelAll', params);
+ }
+
+ replaceActiveOrder(params: {
+ order_id?: string;
+ order_link_id?: string;
+ symbol: string;
+ p_r_qty?: string;
+ p_r_price?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/order/replace', params);
+ }
+
+ queryActiveOrder(params: {
+ order_id?: string;
+ order_link_id?: string;
+ symbol: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('futures/private/order', params);
+ }
+
+ /**
+ * Conditional orders
+ */
+
+ placeConditionalOrder(params: {
+ side: string;
+ symbol: string;
+ order_type: string;
+ qty: string;
+ price?: string;
+ base_price: string;
+ stop_px: string;
+ time_in_force: string;
+ trigger_by?: string;
+ close_on_trigger?: boolean;
+ order_link_id?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/stop-order/create', params);
+ }
+
+ getConditionalOrder(params: {
+ symbol: string;
+ stop_order_status?: string;
+ direction?: string;
+ limit?: number;
+ cursor?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('futures/private/stop-order/list', params);
+ }
+
+ cancelConditionalOrder(params: {
+ symbol: string;
+ stop_order_id?: string;
+ order_link_id?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/stop-order/cancel', params);
+ }
+
+ cancelAllConditionalOrders(params: {
+ symbol: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/stop-order/cancelAll', params);
+ }
+
+ replaceConditionalOrder(params: {
+ stop_order_id?: string;
+ order_link_id?: string;
+ symbol: string;
+ p_r_qty?: number;
+ p_r_price?: string;
+ p_r_trigger_price?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/stop-order/replace', params);
+ }
+
+ queryConditionalOrder(params: {
+ symbol: string;
+ stop_order_id?: string;
+ order_link_id?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('futures/private/stop-order', params);
+ }
+
+ /**
+ * Position
+ */
+
+
+ /**
+ * Get position list
+ */
+ getPosition(params?: {
+ symbol?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('futures/private/position/list', params);
+ }
+
+ changePositionMargin(params: {
+ symbol: string;
+ margin: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/position/change-position-margin', params);
+ }
+
+ setTradingStop(params: {
+ symbol: string;
+ take_profit?: number;
+ stop_loss?: number;
+ trailing_stop?: number;
+ tp_trigger_by?: string;
+ sl_trigger_by?: string;
+ new_trailing_active?: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/position/trading-stop', params);
+ }
+
+ setUserLeverage(params: {
+ symbol: string;
+ buy_leverage: number;
+ sell_leverage: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/position/leverage/save', params);
+ }
+
+ /**
+ * Position mode switch
+ */
+ setPositionMode(params: {
+ symbol: string;
+ mode: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/position/switch-mode', params);
+ }
+
+ /**
+ * Cross/Isolated margin switch. Must set leverage value when switching.
+ */
+ setMarginType(params: {
+ symbol: string;
+ is_isolated: boolean;
+ buy_leverage: number;
+ sell_leverage: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('futures/private/position/switch-isolated', params);
+ }
+
+ getTradeRecords(params: {
+ order_id?: string;
+ symbol: string;
+ start_time?: number;
+ page?: number;
+ limit?: number;
+ order?: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('futures/private/execution/list', params);
+ }
+
+ getClosedPnl(params: {
+ symbol: string;
+ start_time?: number;
+ end_time?: number;
+ exec_type?: string;
+ page?: number;
+ limit?: number;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('futures/private/trade/closed-pnl/list', params);
+ }
+
+ /**
+ **** The following are all the same as the inverse client ****
+ */
+
+ /**
+ * Risk Limit
+ */
+ getRiskLimitList(): GenericAPIResponse {
+ return this.requestWrapper.get('open-api/wallet/risk-limit/list');
+ }
+
+ setRiskLimit(params: {
+ symbol: string;
+ risk_id: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.post('open-api/wallet/risk-limit', params);
+ }
+
+ /**
+ * Funding
+ */
+
+ getLastFundingRate(params: {
+ symbol: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/public/funding/prev-funding-rate', params);
+ }
+
+ getMyLastFundingFee(params: {
+ symbol: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/private/funding/prev-funding', params);
+ }
+
+ getPredictedFunding(params: {
+ symbol: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/private/funding/predicted-funding', params);
+ }
+
+ /**
+ * LCP Info
+ */
+
+ getLcpInfo(params: {
+ symbol: string;
+ }): GenericAPIResponse {
+ return this.requestWrapper.get('v2/private/account/lcp', params);
+ }
+};
diff --git a/src/linear-client.ts b/src/linear-client.ts
index 4c114116..9efc57ce 100644
--- a/src/linear-client.ts
+++ b/src/linear-client.ts
@@ -18,7 +18,7 @@ export class LinearClient extends SharedEndpoints {
constructor(
key?: string | undefined,
secret?: string | undefined,
- useLivenet?: boolean,
+ useLivenet: boolean = false,
restClientOptions: RestClientOptions = {},
requestOptions: AxiosRequestConfig = {}
) {
diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts
index 1f3a9b2e..730a3207 100644
--- a/src/shared-endpoints.ts
+++ b/src/shared-endpoints.ts
@@ -17,6 +17,9 @@ export default class SharedEndpoints {
return this.requestWrapper.get('v2/public/orderBook/L2', params);
}
+ /**
+ * Get latest information for symbol
+ */
getTickers(params?: {
symbol?: string;
}): GenericAPIResponse {
@@ -27,6 +30,9 @@ export default class SharedEndpoints {
return this.requestWrapper.get('v2/public/symbols');
}
+ /**
+ * Get liquidated orders
+ */
getLiquidations(params: {
symbol: string;
from?: number;
diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts
index 903a7a23..a54086cd 100644
--- a/src/util/requestUtils.ts
+++ b/src/util/requestUtils.ts
@@ -42,13 +42,13 @@ export function serializeParams(params: object = {}, strict_validation = false):
.join('&');
};
-export function getRestBaseUrl(useLivenet?: boolean, restInverseOptions?: RestClientOptions) {
+export function getRestBaseUrl(useLivenet: boolean, restInverseOptions: RestClientOptions) {
const baseUrlsInverse = {
livenet: 'https://api.bybit.com',
testnet: 'https://api-testnet.bybit.com'
};
- if (restInverseOptions?.baseUrl) {
+ if (restInverseOptions.baseUrl) {
return restInverseOptions.baseUrl;
}
diff --git a/src/util/requestWrapper.ts b/src/util/requestWrapper.ts
index e19f8f30..e618bc67 100644
--- a/src/util/requestWrapper.ts
+++ b/src/util/requestWrapper.ts
@@ -55,7 +55,6 @@ export default class RequestUtil {
this.secret = secret;
}
- // TODO: type check that endpoint never starts with forward slash??
get(endpoint: string, params?: any): GenericAPIResponse {
return this._call('GET', endpoint, params);
}