From 4b73b3c3f4b592d0fe500228118e74211eb8fac2 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 21 Jan 2021 22:19:00 +0000 Subject: [PATCH 001/103] Add Linear Client- Test endpoints added. --- src/linear-client.ts | 47 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/linear-client.ts diff --git a/src/linear-client.ts b/src/linear-client.ts new file mode 100644 index 0000000..26dccc7 --- /dev/null +++ b/src/linear-client.ts @@ -0,0 +1,47 @@ +import { AxiosRequestConfig } from 'axios'; +import { GenericAPIResponse, getBaseRESTInverseUrl, RestClientInverseOptions } from './util/requestUtils'; +import RequestWrapper from './util/requestWrapper'; +import { SharedEndpoints } from './shared-endpoints'; + +export class LinearClient extends SharedEndpoints { + protected requestWrapper: RequestWrapper; + + /** + * @public Creates an instance of the inverse REST API client. + * + * @param {string} key - your API key + * @param {string} secret - your API secret + * @param {boolean} [livenet=false] + * @param {RestClientInverseOptions} [restInverseOptions={}] options to configure REST API connectivity + * @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios + */ + + constructor( + key?: string | undefined, + secret?: string | undefined, + livenet?: boolean, + restInverseOptions:RestClientInverseOptions = {}, // TODO: Rename this type to be more general. + requestOptions: AxiosRequestConfig = {} + ) { + super() + this.requestWrapper = new RequestWrapper( + key, + secret, + getBaseRESTInverseUrl(livenet), + restInverseOptions, + requestOptions + ); + return this; + } + + /** + * @public Get the last funding rate. + */ + + getLastFundingRate(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/funding/prev-funding-rate', params); + } + +} From df9e0c5cf751168de32d6c499c6bd5dd67bd00cb Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 21 Jan 2021 22:20:11 +0000 Subject: [PATCH 002/103] Rename to Inverse, Enable SharedEndpoints --- src/{rest-client.ts => inverse-client.ts} | 62 ++--------------------- 1 file changed, 4 insertions(+), 58 deletions(-) rename src/{rest-client.ts => inverse-client.ts} (90%) diff --git a/src/rest-client.ts b/src/inverse-client.ts similarity index 90% rename from src/rest-client.ts rename to src/inverse-client.ts index 79b0855..aa69a15 100644 --- a/src/rest-client.ts +++ b/src/inverse-client.ts @@ -1,9 +1,10 @@ import { AxiosRequestConfig } from 'axios'; import { GenericAPIResponse, getBaseRESTInverseUrl, RestClientInverseOptions } from './util/requestUtils'; import RequestWrapper from './util/requestWrapper'; +import { SharedEndpoints } from './shared-endpoints'; -export class RestClient { - private requestWrapper: RequestWrapper; +export class InverseClient extends SharedEndpoints { + protected requestWrapper: RequestWrapper; /** * @public Creates an instance of the inverse REST API client. @@ -21,6 +22,7 @@ export class RestClient { restInverseOptions: RestClientInverseOptions = {}, httpOptions: AxiosRequestConfig = {} ) { + super() this.requestWrapper = new RequestWrapper( key, secret, @@ -464,62 +466,6 @@ export class RestClient { return this.requestWrapper.get('v2/private/account/lcp', params); } - /** - * - * Wallet Data Endpoints - * - */ - - getWalletBalance(params?: { - coin?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/wallet/balance', params); - } - - getWalletFundRecords(params?: { - start_date?: string; - end_date?: string; - currency?: string; - coin?: string; - wallet_fund_type?: string; - page?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('open-api/wallet/fund/records', params); - } - - getWithdrawRecords(params: { - start_date?: string; - end_date?: string; - coin?: string; - status?: string; - page?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('open-api/wallet/withdraw/list', params); - } - - getAssetExchangeRecords(params?: { - limit?: number; - from?: number; - direction?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/exchange-order/list', params); - } - - /** - * - * API Data Endpoints - * - */ - - getServerTime(): GenericAPIResponse { - return this.requestWrapper.get('v2/public/time'); - } - - getApiAnnouncements(): GenericAPIResponse { - return this.requestWrapper.get('v2/public/announcement'); - } async getTimeOffset(): Promise { const start = Date.now(); From 1b8b44c597a858fd710fb90e72b9ff608e7a5a8b Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 21 Jan 2021 22:21:02 +0000 Subject: [PATCH 003/103] Creation of sharedendpoints. Used for linear and inverse client --- src/shared-endpoints.ts | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/shared-endpoints.ts diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts new file mode 100644 index 0000000..ca18fca --- /dev/null +++ b/src/shared-endpoints.ts @@ -0,0 +1,58 @@ +//type Constructor = new (...args: any[]) => {}; +import { GenericAPIResponse } from './util/requestUtils'; +import RequestWrapper from './util/requestWrapper'; + +export class SharedEndpoints { + protected requestWrapper: RequestWrapper; // XXX Is there a way to say that Base has to provide this? + + + + //------------Wallet Data Endpoints------------> + + getWalletBalance(params: { + coin: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/wallet/balance',params) + } + + getAssetExchangeRecords(params?: { + limit?: number; + from?: number; + direction?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/exchange-order/list', params); + } + + getWalletFundRecords(params?: { + start_date?: string; + end_date?: string; + currency?: string; + coin?: string; + wallet_fund_type?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/wallet/fund/records', params); + } + + getWithdrawRecords(params: { + start_date?: string; + end_date?: string; + coin?: string; + status?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); + } + + //-------------API Data Endpoints--------------> + + getServerTime(): GenericAPIResponse { + return this.requestWrapper.get('v2/public/time'); + } + + getApiAnnouncements(): GenericAPIResponse { + return this.requestWrapper.get('v2/public/announcement'); + } +} From fe71e256fd05240b4f6c277e61764e72b3a09fc5 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 21 Jan 2021 22:21:36 +0000 Subject: [PATCH 004/103] Name Changes and adding linear --- src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index ab8ec69..bc82e2e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ -export * from './rest-client'; +export * from './inverse-client'; +export * from './linear-client'; export * from './websocket-client'; export * from './logger'; From aca8136fe174e232393fa92135780c83e8f956b4 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 21 Jan 2021 22:22:09 +0000 Subject: [PATCH 005/103] Rest to Inverse Name Changes --- src/websocket-client.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 8b80c80..b1ed90f 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -1,6 +1,5 @@ import { EventEmitter } from 'events'; - -import { RestClient } from './rest-client'; +import { InverseClient } from './inverse-client'; import { DefaultLogger } from './logger'; import { signMessage, serializeParams } from './util/requestUtils'; // import WebSocket from 'ws'; @@ -37,7 +36,7 @@ export class WebsocketClient extends EventEmitter { private readyState: number; private pingInterval?: number | undefined; private pongTimeout?: number | undefined; - private client: RestClient; + private client: InverseClient; private _subscriptions: Set; private ws: WebSocket; private options: WebsocketClientOptions; @@ -59,7 +58,7 @@ export class WebsocketClient extends EventEmitter { ...options }; - this.client = new RestClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); + this.client = new InverseClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); this._subscriptions = new Set(); this._connect(); @@ -161,7 +160,8 @@ export class WebsocketClient extends EventEmitter { this.logger.silly('Sending ping', { category: 'bybit-ws' }); this.ws.send(JSON.stringify({op: 'ping'})); - this.pongTimeout = setTimeout(() => { + + this.pongTimeout = setTimeout(() => { this.logger.info('Pong timeout', { category: 'bybit-ws' }); this._teardown(); // this.ws.terminate(); @@ -190,7 +190,7 @@ export class WebsocketClient extends EventEmitter { this.readyState = READY_STATE_CONNECTED; this._subscribe([...this._subscriptions]); - this.pingInterval = setInterval(this._ping.bind(this), this.options.pingInterval); + this.pingInterval = setInterval(this._ping.bind(this), this.options.pingInterval); } _wsMessageHandler(message) { From 40e06cfed610b5f41de63f90aa32293d3e51806f Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 21 Jan 2021 22:24:08 +0000 Subject: [PATCH 006/103] Support For Linear and Bugfix for setTimeout --- src/util/requestWrapper.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/util/requestWrapper.ts b/src/util/requestWrapper.ts index cf5c71c..4987ef5 100644 --- a/src/util/requestWrapper.ts +++ b/src/util/requestWrapper.ts @@ -10,6 +10,7 @@ export default class RequestUtil { private globalRequestOptions: AxiosRequestConfig; private key: string | undefined; private secret: string | undefined; + //all private constructor( key: string | undefined, @@ -68,9 +69,15 @@ export default class RequestUtil { * @private Make a HTTP request to a specific endpoint. Private endpoints are automatically signed. */ async _call(method: Method, endpoint: string, params?: any): GenericAPIResponse { - const publicEndpoint = endpoint.startsWith('v2/public'); + let publicEndpoint = false; + if(endpoint.startsWith('v2/public')){ + publicEndpoint = true; + } + else if(endpoint.startsWith('public/linear/')){ + publicEndpoint = true; + } - if (!publicEndpoint) { + if (publicEndpoint == false) { if (!this.key || !this.secret) { throw new Error('Private endpoints require api and private keys set'); } @@ -101,7 +108,7 @@ export default class RequestUtil { } throw response; - }).catch(e => this.parseException(e)); + }).catch(this.parseException); } /** From 0eff0f7d8eff6504b8f9dc12b9bef62da6187b22 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 21 Jan 2021 22:42:56 +0000 Subject: [PATCH 007/103] Update linear-client.ts --- src/linear-client.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 26dccc7..a787807 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -25,10 +25,10 @@ export class LinearClient extends SharedEndpoints { ) { super() this.requestWrapper = new RequestWrapper( - key, - secret, - getBaseRESTInverseUrl(livenet), - restInverseOptions, + key, + secret, + getBaseRESTInverseUrl(livenet), + restInverseOptions, requestOptions ); return this; From 73c3d7372013b4196d7f23ea9d4f61a84caf0a0d Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:06:58 +0000 Subject: [PATCH 008/103] Update inverse-client.ts --- src/inverse-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index aa69a15..0d2ccb3 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -3,7 +3,7 @@ import { GenericAPIResponse, getBaseRESTInverseUrl, RestClientInverseOptions } f import RequestWrapper from './util/requestWrapper'; import { SharedEndpoints } from './shared-endpoints'; -export class InverseClient extends SharedEndpoints { +export default class InverseClient extends SharedEndpoints { protected requestWrapper: RequestWrapper; /** From 9eeee0e208afa654d0239e82807778cbb063074c Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:10:03 +0000 Subject: [PATCH 009/103] Update linear-client.ts --- src/linear-client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index a787807..edb99dc 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -1,9 +1,9 @@ import { AxiosRequestConfig } from 'axios'; import { GenericAPIResponse, getBaseRESTInverseUrl, RestClientInverseOptions } from './util/requestUtils'; import RequestWrapper from './util/requestWrapper'; -import { SharedEndpoints } from './shared-endpoints'; +import SharedEndpoints from './shared-endpoints'; -export class LinearClient extends SharedEndpoints { +export default class LinearClient extends SharedEndpoints { protected requestWrapper: RequestWrapper; /** From 60ce9fa2d050c53fb405acfdf0c1448f1a009258 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:10:24 +0000 Subject: [PATCH 010/103] Update linear-client.ts --- src/linear-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index edb99dc..0419700 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -3,7 +3,7 @@ import { GenericAPIResponse, getBaseRESTInverseUrl, RestClientInverseOptions } f import RequestWrapper from './util/requestWrapper'; import SharedEndpoints from './shared-endpoints'; -export default class LinearClient extends SharedEndpoints { +export class LinearClient extends SharedEndpoints { protected requestWrapper: RequestWrapper; /** From a5e88d999e0ae3263f7763e5e72261c5aa3ee081 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:10:37 +0000 Subject: [PATCH 011/103] Update shared-endpoints.ts --- src/shared-endpoints.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index ca18fca..5a07ce5 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -2,7 +2,7 @@ import { GenericAPIResponse } from './util/requestUtils'; import RequestWrapper from './util/requestWrapper'; -export class SharedEndpoints { +export default class SharedEndpoints { protected requestWrapper: RequestWrapper; // XXX Is there a way to say that Base has to provide this? From 92519ee742fc2811634ecd416ba8c28175694216 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:10:59 +0000 Subject: [PATCH 012/103] Update inverse-client.ts --- src/inverse-client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 0d2ccb3..0767feb 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -1,9 +1,9 @@ import { AxiosRequestConfig } from 'axios'; import { GenericAPIResponse, getBaseRESTInverseUrl, RestClientInverseOptions } from './util/requestUtils'; import RequestWrapper from './util/requestWrapper'; -import { SharedEndpoints } from './shared-endpoints'; +import SharedEndpoints from './shared-endpoints'; -export default class InverseClient extends SharedEndpoints { +export class InverseClient extends SharedEndpoints { protected requestWrapper: RequestWrapper; /** From 47168e4f9edd7a021e768c9d65b1986f8c098749 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:14:22 +0000 Subject: [PATCH 013/103] Made if/else for public check a function --- src/util/requestWrapper.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/util/requestWrapper.ts b/src/util/requestWrapper.ts index 4987ef5..d32395f 100644 --- a/src/util/requestWrapper.ts +++ b/src/util/requestWrapper.ts @@ -69,15 +69,17 @@ export default class RequestUtil { * @private Make a HTTP request to a specific endpoint. Private endpoints are automatically signed. */ async _call(method: Method, endpoint: string, params?: any): GenericAPIResponse { - let publicEndpoint = false; - if(endpoint.startsWith('v2/public')){ - publicEndpoint = true; - } - else if(endpoint.startsWith('public/linear/')){ - publicEndpoint = true; + const isPublicEndpoint = (endpoint: string): boolean { + if (endpoint.startsWith('v2/public')) { + return true; + } + if (endpoint.startsWith('public/linear')) { + return true; + } + return false; } - if (publicEndpoint == false) { + if (isPublicEndpoint(endpoint) === false) { if (!this.key || !this.secret) { throw new Error('Private endpoints require api and private keys set'); } From 4f7d593fe43d22ebd69f91acbc82ae4c54e71b28 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:15:21 +0000 Subject: [PATCH 014/103] error fix --- src/util/requestWrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/requestWrapper.ts b/src/util/requestWrapper.ts index d32395f..76268e6 100644 --- a/src/util/requestWrapper.ts +++ b/src/util/requestWrapper.ts @@ -110,7 +110,7 @@ export default class RequestUtil { } throw response; - }).catch(this.parseException); + }).catch(e => this.parseException(e)); } /** From be6e344c8f28164cc002b9e6c8168d8b3ec56771 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 00:16:23 +0000 Subject: [PATCH 015/103] misc --- src/websocket-client.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index b1ed90f..0fc35a6 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -45,7 +45,6 @@ export class WebsocketClient extends EventEmitter { super(); this.logger = logger || DefaultLogger; - this.readyState = READY_STATE_INITIAL; this.pingInterval = undefined; this.pongTimeout = undefined; @@ -60,7 +59,6 @@ export class WebsocketClient extends EventEmitter { this.client = new InverseClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); this._subscriptions = new Set(); - this._connect(); } @@ -101,7 +99,6 @@ export class WebsocketClient extends EventEmitter { const authParams = await this._authenticate(); const url = this._getWsUrl() + authParams; - const ws = new WebSocket(url); ws.onopen = this._wsOpenHandler.bind(this); @@ -110,7 +107,7 @@ export class WebsocketClient extends EventEmitter { ws.onclose = this._wsCloseHandler.bind(this); this.ws = ws; - + } catch (err) { this.logger.error('Connection failed: ', err); this._reconnect(this.options.reconnectTimeout); From b7ac45e63e614ce98ff83602db0bf0644612fefc Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 09:30:22 +0000 Subject: [PATCH 016/103] Market Data Endpoints Moved to Shared --- src/inverse-client.ts | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 0767feb..cd8c481 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -39,12 +39,6 @@ export class InverseClient extends SharedEndpoints { * */ - getOrderBook(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/orderBook/L2', params); - } - getKline(params: { symbol: string; interval: string; @@ -53,20 +47,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/kline/list', params); } - - /** - * @deprecated use getTickers() instead - */ - getLatestInformation(params?: { - symbol?: string; - }): GenericAPIResponse { - return this.getTickers(params); - } - - getTickers(params?: { - symbol?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/tickers', params); +rn this.requestWrapper.get('v2/public/tickers', params); } /** From b28e14cb3a493c680e2f1612d4bf72812b97522d Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 09:30:36 +0000 Subject: [PATCH 017/103] Market Data Shared Endpoints Added --- src/shared-endpoints.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 5a07ce5..99e68ac 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -4,8 +4,29 @@ import RequestWrapper from './util/requestWrapper'; export default class SharedEndpoints { protected requestWrapper: RequestWrapper; // XXX Is there a way to say that Base has to provide this? - + //------------Market Data Endpoints------------> + + getOrderBook(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/orderBook/L2', params); + } + + /** + * @deprecated use getTickers() instead + */ + getLatestInformation(params?: { + symbol?: string; + }): GenericAPIResponse { + return this.getTickers(params); + } + + getTickers(params?: { + symbol?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/tickers', params); + } //------------Wallet Data Endpoints------------> From 90ea970bd49ff03006a9cdcaa005288c6cd24762 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 09:46:53 +0000 Subject: [PATCH 018/103] Fixed Half Deletion --- src/inverse-client.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index cd8c481..2c866f1 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -47,8 +47,6 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/kline/list', params); } -rn this.requestWrapper.get('v2/public/tickers', params); - } /** * @deprecated use getTrades() instead From e99dd756a07335629a504efe343b52fd196b0aa2 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 09:55:40 +0000 Subject: [PATCH 019/103] Transferred Last Endpoints to Shared --- src/inverse-client.ts | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 2c866f1..c126654 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -67,33 +67,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/public/trading-records', params); } - getSymbols(): GenericAPIResponse { - return this.requestWrapper.get('v2/public/symbols'); - } - - /** - * @deprecated use getLiquidations() instead - */ - getPublicLiquidations(params: { - symbol: string; - from?: number; - limit?: number; - start_time?: number; - end_time?: number; - }): GenericAPIResponse { - return this.getLiquidations(params); - } - - getLiquidations(params: { - symbol: string; - from?: number; - limit?: number; - start_time?: number; - end_time?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/liq-records', params); - } - getMarkPriceKline(params: { symbol: string; interval: string; From 32625e14850926be78969696acf1cdbb12a4f4a7 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 09:56:21 +0000 Subject: [PATCH 020/103] Added getSymbol and getLiq from inverse --- src/shared-endpoints.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 99e68ac..4c280ee 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -28,6 +28,33 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/public/tickers', params); } + getSymbols(): GenericAPIResponse { + return this.requestWrapper.get('v2/public/symbols'); + } + + /** + * @deprecated use getLiquidations() instead + */ + getPublicLiquidations(params: { + symbol: string; + from?: number; + limit?: number; + start_time?: number; + end_time?: number; + }): GenericAPIResponse { + return this.getLiquidations(params); + } + + getLiquidations(params: { + symbol: string; + from?: number; + limit?: number; + start_time?: number; + end_time?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/liq-records', params); + } + //------------Wallet Data Endpoints------------> getWalletBalance(params: { From ca14d888791009d0934603e93fcbc52c7f6d8c8c Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 10:10:07 +0000 Subject: [PATCH 021/103] Added OI, Big Deal, LS Ratio --- src/shared-endpoints.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 4c280ee..474bd04 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -55,6 +55,29 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/public/liq-records', params); } + getOpenInterest(params: { + symbol: string; + period: string; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/open-interest', params); + } + + getLatestBigDeal(params: { + symbol: string; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/big-deal', params); + } + + getLongShortRatio(params: { + symbol: string; + period: string; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/account-ratio', params); + } + //------------Wallet Data Endpoints------------> getWalletBalance(params: { From 3e14c63002f852305f8fb953f7f50289b53a5351 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 10:10:31 +0000 Subject: [PATCH 022/103] Missing Endpoint --- src/inverse-client.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index c126654..3ee8ff0 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -75,6 +75,24 @@ export class InverseClient extends SharedEndpoints { }): 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); + } getOpenInterest(params: { symbol: string; From e345943327676011f3f0aa74b113ce5a149fc5bf Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 10:15:36 +0000 Subject: [PATCH 023/103] Added Market Data Endpoints For Linear --- src/linear-client.ts | 69 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 0419700..77bb017 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -33,15 +33,76 @@ export class LinearClient extends SharedEndpoints { ); return this; } - + //------------Market Data Endpoints------------> + + getKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('/public/linear/kline', params); + } + /** - * @public Get the last funding rate. - */ + * @deprecated use getTrades() instead + */ + getPublicTradingRecords(params: { + symbol: string; + from?: number; + limit?: number; + }): GenericAPIResponse { + return this.getTrades(params); + } + getTrades(params: { + symbol: string; + from?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('/public/linear/recent-trading-records', params); + } + getLastFundingRate(params: { symbol: string; }): GenericAPIResponse { return this.requestWrapper.get('public/linear/funding/prev-funding-rate', params); } - + + getMarkPriceKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('/public/linear/mark-price-kline', params); + } + + getIndexPriceKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('/public/linear/index-price-kline', params); + } + + getPremiumIndexKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('/public/linear/premium-index-kline', params); + } + + + //-----------Account Data Endpoints------------> + + //------------Wallet Data Endpoints------------> + + //-------------API Data Endpoints--------------> + + + } From 436945671927ea2ecffd2814f223d813639e409f Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 13:39:46 +0000 Subject: [PATCH 024/103] Updated Some Account Data Endpoints from open-api to v2 --- src/inverse-client.ts | 69 ++++--------------------------------------- 1 file changed, 5 insertions(+), 64 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 3ee8ff0..71e5b53 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -152,21 +152,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/order/list', params); } - /** - * @deprecated use getActiveOrderList() instead - */ - getActiveOrder(params: { - order_id?: string; - order_link_id?: string; - symbol?: string; - order?: string; - page?: number; - limit?: number; - order_status?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('open-api/order/list', params); - } - cancelActiveOrder(params: { symbol: string; order_id?: string; @@ -197,16 +182,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.post('v2/private/order/replace', params); } - /** - * @deprecated use replaceActiveOrder() - */ - replaceActiveOrderOld(params: any): GenericAPIResponse { - // if (!params.order_id && !params.order_link_id) { - // throw new Error('Parameter order_id OR order_link_id is required'); - // } - return this.requestWrapper.post('open-api/order/replace', params); - } - queryActiveOrder(params: { order_id?: string; order_link_id?: string; @@ -237,16 +212,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.post('v2/private/stop-order/create', params); } - /** - * @deprecated use placeConditionalOrder - */ - placeConditionalOrderOld(params: any): GenericAPIResponse { - // if (params.order_type === 'Limit' && !params.price) { - // throw new Error('Parameter price is required for limit orders'); - // } - return this.requestWrapper.post('open-api/stop-order/create', params); - } - getConditionalOrder(params: { symbol: string; stop_order_status?: string; @@ -257,13 +222,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/stop-order/list', params); } - /** - * @deprecated use placeConditionalOrder - */ - getConditionalOrderOld(params: any): GenericAPIResponse { - return this.requestWrapper.get('open-api/stop-order/list', params); - } - cancelConditionalOrder(params: { symbol: string; stop_order_id?: string; @@ -275,16 +233,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.post('v2/private/stop-order/cancel', params); } - /** - * @deprecated use cancelConditionalOrder - */ - cancelConditionalOrderOld(params: any): GenericAPIResponse { - // if (!params.stop_order_id && !params.order_link_id) { - // throw new Error('Parameter stop_order_id OR order_link_id is required'); - // } - return this.requestWrapper.post('open-api/stop-order/cancel', params); - } - cancelAllConditionalOrders(params: { symbol: string; }): GenericAPIResponse { @@ -305,13 +253,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.post('v2/private/stop-order/replace', params); } - /** - * @deprecated use replaceConditionalOrder - */ - replaceConditionalOrderOld(params: any): GenericAPIResponse { - return this.requestWrapper.post('open-api/stop-order/replace', params); - } - queryConditionalOrder(params: { symbol: string; stop_order_id?: string; @@ -358,7 +299,7 @@ export class InverseClient extends SharedEndpoints { sl_trigger_by?: string; new_trailing_active?: number; }): GenericAPIResponse { - return this.requestWrapper.post('open-api/position/trading-stop', params); + return this.requestWrapper.post('v2/private/position/trading-stop', params); } setUserLeverage(params: { @@ -411,23 +352,23 @@ export class InverseClient extends SharedEndpoints { getLastFundingRate(params: { symbol: string; }): GenericAPIResponse { - return this.requestWrapper.get('open-api/funding/prev-funding-rate', params); + return this.requestWrapper.get('v2/private/funding/prev-funding-rate', params); } getMyLastFundingFee(params: { symbol: string; }): GenericAPIResponse { - return this.requestWrapper.get('open-api/funding/prev-funding', params); + return this.requestWrapper.get('v2/private/funding/prev-funding', params); } getPredictedFunding(params: { symbol: string; }): GenericAPIResponse { - return this.requestWrapper.get('open-api/funding/predicted-funding', params); + return this.requestWrapper.get('v2/private/funding/predicted-funding', params); } getApiKeyInfo(): GenericAPIResponse { - return this.requestWrapper.get('open-api/api-key'); + return this.requestWrapper.get('v2/private/account/api-key'); } getLcpInfo(params: { From c4dac0d9ed02fee2ccdd22b39c623f2c926d0329 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 14:01:42 +0000 Subject: [PATCH 025/103] Added 6 Endpoints (Active Orders) --- src/linear-client.ts | 99 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 77bb017..64c2de5 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -57,10 +57,10 @@ export class LinearClient extends SharedEndpoints { getTrades(params: { symbol: string; - from?: number; + //from?: number; limit?: number; }): GenericAPIResponse { - return this.requestWrapper.get('/public/linear/recent-trading-records', params); + return this.requestWrapper.get('public/linear/recent-trading-records', params); } getLastFundingRate(params: { @@ -75,7 +75,7 @@ export class LinearClient extends SharedEndpoints { from: number; limit?: number; }): GenericAPIResponse { - return this.requestWrapper.get('/public/linear/mark-price-kline', params); + return this.requestWrapper.get('public/linear/mark-price-kline', params); } getIndexPriceKline(params: { @@ -84,7 +84,7 @@ export class LinearClient extends SharedEndpoints { from: number; limit?: number; }): GenericAPIResponse { - return this.requestWrapper.get('/public/linear/index-price-kline', params); + return this.requestWrapper.get('public/linear/index-price-kline', params); } getPremiumIndexKline(params: { @@ -93,11 +93,98 @@ export class LinearClient extends SharedEndpoints { from: number; limit?: number; }): GenericAPIResponse { - return this.requestWrapper.get('/public/linear/premium-index-kline', params); + return this.requestWrapper.get('public/linear/premium-index-kline', params); } - //-----------Account Data Endpoints------------> + //-----------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; + tp_trigger_by?: string; + sl_trigger_by?: string; + reduce_only?: boolean; + close_on_trigger?: boolean; + order_link_id?: string; + }): GenericAPIResponse { + // if (orderRequest.order_type === 'Limit' && !orderRequest.price) { + // throw new Error('Price required for limit orders'); + // } + return this.requestWrapper.post('private/linear/order/create', orderRequest); + } + + getActiveOrderList(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + order?: string; + page?: number; + limit?: number; + order_status?: string; + + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/order/list', params); + } + + cancelActiveOrder(params: { + symbol: string; + order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + // if (!params.order_id && !params.order_link_id) { + // throw new Error('Parameter order_id OR order_link_id is required'); + // } + return this.requestWrapper.post('private/linear/order/cancel', params); + } + + cancelAllActiveOrders(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/cancel-all', params); + } + + replaceActiveOrder(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + p_r_qty?: number; + p_r_price?: number; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + // if (!params.order_id && !params.order_link_id) { + // throw new Error('Parameter order_id OR order_link_id is required'); + // } + return this.requestWrapper.post('private/linear/order/replace', params); + } + + queryActiveOrder(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + }): GenericAPIResponse { + // if (!params.order_id && !params.order_link_id) { + // throw new Error('Parameter order_id OR order_link_id is required'); + // } + return this.requestWrapper.get('/private/linear/order/search', params); + } + + //Conditional Orders + //Position + //Risk Limit + //Funding + //API Key Info //------------Wallet Data Endpoints------------> From cbfeba5ce43d4526f55b3cad163a7b4291e2545b Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 14:19:11 +0000 Subject: [PATCH 026/103] Update linear-client.ts --- src/linear-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 64c2de5..af36242 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -97,7 +97,7 @@ export class LinearClient extends SharedEndpoints { } - //-----------Account Data Endpoints------------> + //-----------Account Data Endpoints------------> //Active Orders From 212dd2456422489c242cb8431bb1a4f5f3d8ba83 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 14:43:42 +0000 Subject: [PATCH 027/103] Addition of 6 Endpoints (Conditional Orders) --- src/linear-client.ts | 85 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index af36242..c272e03 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -116,9 +116,6 @@ export class LinearClient extends SharedEndpoints { close_on_trigger?: boolean; order_link_id?: string; }): GenericAPIResponse { - // if (orderRequest.order_type === 'Limit' && !orderRequest.price) { - // throw new Error('Price required for limit orders'); - // } return this.requestWrapper.post('private/linear/order/create', orderRequest); } @@ -140,9 +137,6 @@ export class LinearClient extends SharedEndpoints { order_id?: string; order_link_id?: string; }): GenericAPIResponse { - // if (!params.order_id && !params.order_link_id) { - // throw new Error('Parameter order_id OR order_link_id is required'); - // } return this.requestWrapper.post('private/linear/order/cancel', params); } @@ -163,9 +157,6 @@ export class LinearClient extends SharedEndpoints { tp_trigger_by?: string; sl_trigger_by?: string; }): GenericAPIResponse { - // if (!params.order_id && !params.order_link_id) { - // throw new Error('Parameter order_id OR order_link_id is required'); - // } return this.requestWrapper.post('private/linear/order/replace', params); } @@ -174,13 +165,81 @@ export class LinearClient extends SharedEndpoints { order_link_id?: string; symbol: string; }): GenericAPIResponse { - // if (!params.order_id && !params.order_link_id) { - // throw new Error('Parameter order_id OR order_link_id is required'); - // } - return this.requestWrapper.get('/private/linear/order/search', params); + return this.requestWrapper.get('private/linear/order/search', params); } //Conditional Orders + + placeConditionalOrder(params: { + side: string; + symbol: string; + order_type: string; + qty: number; + price?: number; + base_price: number; + stop_px: number; + time_in_force: string; + trigger_by?: string; + close_on_trigger?: boolean; + order_link_id?: string; + reduce_only: boolean; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/create', params); + } + + getConditionalOrder(params: { + stop_order_id?: string; + order_link_id?: string; + symbol: string; + stop_order_status?: string; + order?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/stop-order/list', params); + } + + cancelConditionalOrder(params: { + symbol: string; + stop_order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/cancel', params); + } + + cancelAllConditionalOrders(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/cancel-all', params); + } + + replaceConditionalOrder(params: { + stop_order_id?: string; + order_link_id?: string; + symbol: string; + p_r_qty?: number; + p_r_price?: number; + p_r_trigger_price?: number; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/replace', params); + } + + queryConditionalOrder(params: { + symbol: string; + stop_order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/stop-order/search', params); + } + //Position //Risk Limit //Funding From 3caa049d5325f5da200c99f8de030b13d9115b77 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 15:08:25 +0000 Subject: [PATCH 028/103] Added 9 Endpoints to Linear (Position) --- src/linear-client.ts | 83 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/linear-client.ts b/src/linear-client.ts index c272e03..62862fd 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -241,6 +241,89 @@ export class LinearClient extends SharedEndpoints { } //Position + + getPosition(params?: { + symbol?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/position/list', params); + } + + setAutoAddMargin(params?: { + symbol: string; + side: string; + auto_add_margin: boolean; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/set-auto-add-margin', params); + } + + setMarginSwitch(params?: { + symbol: string; + is_isolated: boolean; + buy_leverage: number; + sell_leverage: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/switch-isolated', params); + } + + setSwitchMode(params?: { + symbol: string; + tp_sl_mode: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/tpsl/switch-mode', params); + } + + setAddReduceMargin(params?: { + symbol: string; + side: string; + margin: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/add-margin', params); + } + + setUserLeverage(params: { + symbol: string; + buy_leverage: number; + sell_leverage: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/set-leverage', params); + } + + setTradingStop(params: { + symbol: string; + side: string; + take_profit?: number; + stop_loss?: number; + trailing_stop?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + sl_size?: number; + tp_size?: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/trading-stop', params); + } + + getTradeRecords(params: { + symbol: string; + start_time?: number; + end_time?: number; + exec_type?: string; + page?: integer; + limit?: intiger; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/trade/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('private/linear/tpsl/switch-mode', params); + } + //Risk Limit //Funding //API Key Info From abadbc9dfea16629b2da73778eb4d5999543c68d Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 15:38:26 +0000 Subject: [PATCH 029/103] Added Requirement for risk limit --- src/inverse-client.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 71e5b53..22949e5 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -338,7 +338,9 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/trade/closed-pnl/list', params); } - getRiskLimitList(): GenericAPIResponse { + getRiskLimitList(params: { + symbol: string; + }): GenericAPIResponse { return this.requestWrapper.get('open-api/wallet/risk-limit/list'); } From e20121f43ee3abd88bb82de56384da59dcbe7a86 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 15:40:31 +0000 Subject: [PATCH 030/103] mistake fixed --- src/inverse-client.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 22949e5..71e5b53 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -338,9 +338,7 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/trade/closed-pnl/list', params); } - getRiskLimitList(params: { - symbol: string; - }): GenericAPIResponse { + getRiskLimitList(): GenericAPIResponse { return this.requestWrapper.get('open-api/wallet/risk-limit/list'); } From 62ea702f2660b52507ca1c33e534c4b7065cd386 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 15:43:37 +0000 Subject: [PATCH 031/103] Added Risk Limit and Funding Endpoints --- src/linear-client.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 62862fd..8811a5e 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -325,13 +325,25 @@ export class LinearClient extends SharedEndpoints { } //Risk Limit + + getRiskLimitList(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/risk-limit'); + } + //Funding - //API Key Info - - //------------Wallet Data Endpoints------------> - - //-------------API Data Endpoints--------------> + getPredictedFundingFee(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/funding/predicted-funding'); + } + getLastFundingFee(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/funding/prev-funding'); + } } From 8c4e109153cc995026fd30db44a6dcc72dd9e04e Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 15:46:13 +0000 Subject: [PATCH 032/103] misc --- src/shared-endpoints.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 474bd04..7ee1e28 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -116,14 +116,14 @@ export default class SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); } + + //-------------API Data Endpoints--------------> - //-------------API Data Endpoints--------------> - - getServerTime(): GenericAPIResponse { + getServerTime(): GenericAPIResponse { return this.requestWrapper.get('v2/public/time'); - } + } - getApiAnnouncements(): GenericAPIResponse { + getApiAnnouncements(): GenericAPIResponse { return this.requestWrapper.get('v2/public/announcement'); - } + } } From 8a991a77bb8af6a3041e1ab7d53a9d8c877012a0 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 15:47:51 +0000 Subject: [PATCH 033/103] Shared ApiKeyInfo --- src/inverse-client.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 71e5b53..4055b6e 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -367,10 +367,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/funding/predicted-funding', params); } - getApiKeyInfo(): GenericAPIResponse { - return this.requestWrapper.get('v2/private/account/api-key'); - } - getLcpInfo(params: { symbol: string; }): GenericAPIResponse { From eb536d6b6853a232d17bbe97bfb796773f2bfbc7 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 15:48:02 +0000 Subject: [PATCH 034/103] Added ApiKeyInfo to shared --- src/shared-endpoints.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 7ee1e28..0178a71 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -78,6 +78,12 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/public/account-ratio', params); } + //------------Market Data Endpoints------------> + + getApiKeyInfo(): GenericAPIResponse { + return this.requestWrapper.get('v2/private/account/api-key'); + } + //------------Wallet Data Endpoints------------> getWalletBalance(params: { From 0d6532df230169bb2f40ececa646919a9d44cec7 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 15:51:58 +0000 Subject: [PATCH 035/103] Updated Comments for a clieaner look. I also removed the three line comments in some endpoints as per request. --- src/inverse-client.ts | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 4055b6e..a39ad00 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -33,11 +33,7 @@ export class InverseClient extends SharedEndpoints { return this; } - /** - * - * Market Data Endpoints - * - */ + //------------Market Data Endpoints------------> getKline(params: { symbol: string; @@ -117,11 +113,7 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/public/account-ratio', params); } - /** - * - * Account Data Endpoints - * - */ + //-----------Account Data Endpoints------------> placeActiveOrder(orderRequest: { side: string; @@ -136,9 +128,6 @@ export class InverseClient extends SharedEndpoints { close_on_trigger?: boolean; order_link_id?: string; }): GenericAPIResponse { - // if (orderRequest.order_type === 'Limit' && !orderRequest.price) { - // throw new Error('Price required for limit orders'); - // } return this.requestWrapper.post('v2/private/order/create', orderRequest); } @@ -157,9 +146,6 @@ export class InverseClient extends SharedEndpoints { order_id?: string; order_link_id?: string; }): GenericAPIResponse { - // if (!params.order_id && !params.order_link_id) { - // throw new Error('Parameter order_id OR order_link_id is required'); - // } return this.requestWrapper.post('v2/private/order/cancel', params); } @@ -176,9 +162,6 @@ export class InverseClient extends SharedEndpoints { p_r_qty?: string; p_r_price?: string; }): GenericAPIResponse { - // if (!params.order_id && !params.order_link_id) { - // throw new Error('Parameter order_id OR order_link_id is required'); - // } return this.requestWrapper.post('v2/private/order/replace', params); } @@ -187,9 +170,6 @@ export class InverseClient extends SharedEndpoints { order_link_id?: string; symbol: string; }): GenericAPIResponse { - // if (!params.order_id && !params.order_link_id) { - // throw new Error('Parameter order_id OR order_link_id is required'); - // } return this.requestWrapper.get('v2/private/order', params); } @@ -206,9 +186,6 @@ export class InverseClient extends SharedEndpoints { close_on_trigger?: boolean; order_link_id?: string; }): GenericAPIResponse { - // if (params.order_type === 'Limit' && !params.price) { - // throw new Error('Parameter price is required for limit orders'); - // } return this.requestWrapper.post('v2/private/stop-order/create', params); } @@ -227,9 +204,6 @@ export class InverseClient extends SharedEndpoints { stop_order_id?: string; order_link_id?: string; }): GenericAPIResponse { - // if (!params.stop_order_id && !params.order_link_id) { - // throw new Error('Parameter stop_order_id OR order_link_id is required'); - // } return this.requestWrapper.post('v2/private/stop-order/cancel', params); } @@ -247,9 +221,6 @@ export class InverseClient extends SharedEndpoints { p_r_price?: string; p_r_trigger_price?: string; }): GenericAPIResponse { - // if (!params.stop_order_id && !params.order_link_id) { - // throw new Error('Parameter stop_order_id OR order_link_id is required'); - // } return this.requestWrapper.post('v2/private/stop-order/replace', params); } @@ -258,9 +229,6 @@ export class InverseClient extends SharedEndpoints { stop_order_id?: string; order_link_id?: string; }): GenericAPIResponse { - // if (!params.stop_order_id && !params.order_link_id) { - // throw new Error('Parameter stop_order_id OR order_link_id is required'); - // } return this.requestWrapper.get('v2/private/stop-order', params); } @@ -373,7 +341,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/account/lcp', params); } - async getTimeOffset(): Promise { const start = Date.now(); return this.getServerTime().then(result => { From d6799b891eb823031e480fe4c6d78244d3580bbc Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 17:21:32 +0000 Subject: [PATCH 036/103] fixed typos --- src/linear-client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 8811a5e..b40246f 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -307,8 +307,8 @@ export class LinearClient extends SharedEndpoints { start_time?: number; end_time?: number; exec_type?: string; - page?: integer; - limit?: intiger; + page?: number; + limit?: number; }): GenericAPIResponse { return this.requestWrapper.get('private/linear/trade/execution/list', params); } From 0ac74140c3a9eacd831c9fd56ee3a830aaeaa245 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 22 Jan 2021 17:22:07 +0000 Subject: [PATCH 037/103] fixed public check function --- src/util/requestWrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/requestWrapper.ts b/src/util/requestWrapper.ts index 76268e6..4a66bec 100644 --- a/src/util/requestWrapper.ts +++ b/src/util/requestWrapper.ts @@ -69,7 +69,7 @@ export default class RequestUtil { * @private Make a HTTP request to a specific endpoint. Private endpoints are automatically signed. */ async _call(method: Method, endpoint: string, params?: any): GenericAPIResponse { - const isPublicEndpoint = (endpoint: string): boolean { + const isPublicEndpoint = (endpoint: string): boolean => { if (endpoint.startsWith('v2/public')) { return true; } From c59d6c1412e131e5e80832b8c12cd59641890d0b Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sat, 23 Jan 2021 21:52:54 +0000 Subject: [PATCH 038/103] Made non-required endpoint not needed for wallet balance. --- src/shared-endpoints.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 0178a71..d70d92a 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -87,7 +87,7 @@ export default class SharedEndpoints { //------------Wallet Data Endpoints------------> getWalletBalance(params: { - coin: string; + coin?: string; }): GenericAPIResponse { return this.requestWrapper.get('v2/private/wallet/balance',params) } From 0fe804413b6d3fe26c703ae1aebb7e671041717f Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 28 Jan 2021 23:39:47 +0000 Subject: [PATCH 039/103] Moved time function to shared --- src/inverse-client.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index a39ad00..ef8506a 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -340,12 +340,4 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/account/lcp', params); } - - async getTimeOffset(): Promise { - const start = Date.now(); - return this.getServerTime().then(result => { - const end = Date.now(); - return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2)); - }); - } }; From 55266b625e046e226e5a92b474b6c8e057b23630 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 28 Jan 2021 23:40:16 +0000 Subject: [PATCH 040/103] Added time function from inverse --- src/shared-endpoints.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index d70d92a..b7bae2d 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -132,4 +132,12 @@ export default class SharedEndpoints { getApiAnnouncements(): GenericAPIResponse { return this.requestWrapper.get('v2/public/announcement'); } + + async getTimeOffset(): Promise { + const start = Date.now(); + return this.getServerTime().then(result => { + const end = Date.now(); + return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2)); + }); + } } From 9d6add36bd58d1d9132c8201cdcd60ffa451cd41 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Thu, 28 Jan 2021 23:40:55 +0000 Subject: [PATCH 041/103] Added option for linear [BROKEN] --- src/websocket-client.ts | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 0fc35a6..0389e2a 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -1,15 +1,21 @@ import { EventEmitter } from 'events'; import { InverseClient } from './inverse-client'; +import { LinearClient } from './linear-client'; import { DefaultLogger } from './logger'; import { signMessage, serializeParams } from './util/requestUtils'; // import WebSocket from 'ws'; import WebSocket from 'isomorphic-ws'; -const wsUrls = { +const iwsUrls = { livenet: 'wss://stream.bybit.com/realtime', testnet: 'wss://stream-testnet.bybit.com/realtime' }; +const lwsUrls = { + livenet: 'wss://stream.bybit.com/realtime_public', + testnet: 'wss://stream-testnet.bybit.com/realtime_public' +}; + const READY_STATE_INITIAL = 0; const READY_STATE_CONNECTING = 1; const READY_STATE_CONNECTED = 2; @@ -20,7 +26,7 @@ export interface WebsocketClientOptions { key?: string; secret?: string; livenet?: boolean; - + linear?: boolean; pongTimeout?: number; pingInterval?: number; reconnectTimeout?: number; @@ -31,12 +37,14 @@ export interface WebsocketClientOptions { type Logger = typeof DefaultLogger; + + export class WebsocketClient extends EventEmitter { private logger: Logger; private readyState: number; private pingInterval?: number | undefined; private pongTimeout?: number | undefined; - private client: InverseClient; + private client: InverseClient | LinearClient; private _subscriptions: Set; private ws: WebSocket; private options: WebsocketClientOptions; @@ -51,17 +59,25 @@ export class WebsocketClient extends EventEmitter { this.options = { livenet: false, + linear: false, pongTimeout: 1000, pingInterval: 10000, reconnectTimeout: 500, ...options }; - this.client = new InverseClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); + + if (this.options.linear) { + this.client = new InverseClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); + }else{ + this.client = new LinearClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); + } + this._subscriptions = new Set(); this._connect(); } + subscribe(topics) { if (!Array.isArray(topics)) topics = [topics]; topics.forEach(topic => this._subscriptions.add(topic)); @@ -90,7 +106,10 @@ export class WebsocketClient extends EventEmitter { if (this.options.wsUrl) { return this.options.wsUrl; } - return wsUrls[this.options.livenet ? 'livenet' : 'testnet']; + if (this.options.linear){ + return lwsUrls[this.options.livenet ? 'livenet' : 'testnet']; + } + return iwsUrls[this.options.livenet ? 'livenet' : 'testnet']; } async _connect() { From e556b1721ebf4ffc95092eb585a7a0de42488219 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 11:27:29 +0000 Subject: [PATCH 042/103] Fixed bug where linear was inv --- src/websocket-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 0389e2a..e17e6eb 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -67,7 +67,7 @@ export class WebsocketClient extends EventEmitter { }; - if (this.options.linear) { + if (!this.options.linear) { this.client = new InverseClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); }else{ this.client = new LinearClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); From c18155f4455fef43a7c4f149419df81b4b15b627 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 11:30:12 +0000 Subject: [PATCH 043/103] misc --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 1c4e97c..922abff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "1.3.1", + "version": "1.3.2", "lockfileVersion": 1, "requires": true, "dependencies": { From fb16feac755a0e43d9566abc2d12566f51be1ce9 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 11:49:19 +0000 Subject: [PATCH 044/103] fixed small bugs --- src/websocket-client.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index e17e6eb..546e8d4 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -67,17 +67,16 @@ export class WebsocketClient extends EventEmitter { }; - if (!this.options.linear) { - this.client = new InverseClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); - }else{ + if (this.options.linear === true) { this.client = new LinearClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); + }else{ + this.client = new InverseClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); } this._subscriptions = new Set(); this._connect(); } - subscribe(topics) { if (!Array.isArray(topics)) topics = [topics]; topics.forEach(topic => this._subscriptions.add(topic)); @@ -196,7 +195,7 @@ export class WebsocketClient extends EventEmitter { _wsOpenHandler() { if (this.readyState === READY_STATE_CONNECTING) { - this.logger.info('Websocket connected', { category: 'bybit-ws', livenet: this.options.livenet }); + this.logger.info('Websocket connected', { category: 'bybit-ws', livenet: this.options.livenet, linear: this.options.linear }); this.emit('open'); } else if (this.readyState === READY_STATE_RECONNECTING) { this.logger.info('Websocket reconnected', { category: 'bybit-ws', livenet: this.options.livenet }); From eb21785adcbcbca244c7ca8c8b4748488805e1d9 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:10:08 +0000 Subject: [PATCH 045/103] misc Co-authored-by: Tiago --- src/inverse-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index ef8506a..72bd211 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -81,7 +81,7 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/public/index-price-kline', params); } - getPremiumIndexKline(params: { + getPremiumIndexKline(params: { symbol: string; interval: string; from: number; From ae2519f3a2bbf74587dcbe09c5fbc4e4c12d9875 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:16:23 +0000 Subject: [PATCH 046/103] misc --- src/linear-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index b40246f..b71e7d7 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -33,6 +33,7 @@ export class LinearClient extends SharedEndpoints { ); return this; } + //------------Market Data Endpoints------------> getKline(params: { @@ -96,7 +97,6 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('public/linear/premium-index-kline', params); } - //-----------Account Data Endpoints------------> //Active Orders From c41cb89b2c0e95a23aaa538fb528ede8e65a5b55 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:16:33 +0000 Subject: [PATCH 047/103] Some endpoint updates --- src/inverse-client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 72bd211..4d4fd1f 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -274,7 +274,7 @@ export class InverseClient extends SharedEndpoints { symbol: string; leverage: number; }): GenericAPIResponse { - return this.requestWrapper.post('user/leverage/save', params); + return this.requestWrapper.post('v2/private/position/leverage/save', params); } /** @@ -320,7 +320,7 @@ export class InverseClient extends SharedEndpoints { getLastFundingRate(params: { symbol: string; }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/funding/prev-funding-rate', params); + return this.requestWrapper.get('v2/public/funding/prev-funding-rate', params); } getMyLastFundingFee(params: { From 05c5e1d2135e35fcb35966433993b36bf324d50a Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:20:03 +0000 Subject: [PATCH 048/103] Moved getLatestInformation to inverse --- src/shared-endpoints.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index b7bae2d..fbcc9d7 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -12,15 +12,6 @@ export default class SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/orderBook/L2', params); } - - /** - * @deprecated use getTickers() instead - */ - getLatestInformation(params?: { - symbol?: string; - }): GenericAPIResponse { - return this.getTickers(params); - } getTickers(params?: { symbol?: string; From 0ce6c97a1f222731ae79d0f0ed9841efd27c3ffb Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:20:17 +0000 Subject: [PATCH 049/103] Added getLatestInformation depreciated endpoint --- src/inverse-client.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 4d4fd1f..e8b48ff 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -43,7 +43,16 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/kline/list', params); } - + + /** + * @deprecated use getTickers() instead + */ + getLatestInformation(params?: { + symbol?: string; + }): GenericAPIResponse { + return this.getTickers(params); + } + /** * @deprecated use getTrades() instead */ From 13a5fc70fa5da232a6d2258eec049c80acc5ed8c Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:23:22 +0000 Subject: [PATCH 050/103] Added getPublicLiquidations depreciated endpoint --- src/inverse-client.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index e8b48ff..b500f57 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -71,6 +71,19 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/trading-records', params); } + + /** + * @deprecated use getLiquidations() instead + */ + getPublicLiquidations(params: { + symbol: string; + from?: number; + limit?: number; + start_time?: number; + end_time?: number; + }): GenericAPIResponse { + return this.getLiquidations(params); + } getMarkPriceKline(params: { symbol: string; From 9f8b7d76e67b48962afa8de830f4f4095c17f118 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:23:38 +0000 Subject: [PATCH 051/103] Moved getPublicLiquidations endpoint to shared --- src/shared-endpoints.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index fbcc9d7..9840422 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -22,19 +22,6 @@ export default class SharedEndpoints { getSymbols(): GenericAPIResponse { return this.requestWrapper.get('v2/public/symbols'); } - - /** - * @deprecated use getLiquidations() instead - */ - getPublicLiquidations(params: { - symbol: string; - from?: number; - limit?: number; - start_time?: number; - end_time?: number; - }): GenericAPIResponse { - return this.getLiquidations(params); - } getLiquidations(params: { symbol: string; From 8a88e6a223714a026549282da1d2287f79049278 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:24:42 +0000 Subject: [PATCH 052/103] Removed 3 Shared Endpoints --- src/inverse-client.ts | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index b500f57..16b8471 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -112,29 +112,6 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/public/premium-index-kline', params); } - getOpenInterest(params: { - symbol: string; - period: string; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/open-interest', params); - } - - getLatestBigDeal(params: { - symbol: string; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/big-deal', params); - } - - getLongShortRatio(params: { - symbol: string; - period: string; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/account-ratio', params); - } - //-----------Account Data Endpoints------------> placeActiveOrder(orderRequest: { From bd75b6d9b425c7a0d30fdc97867a51c5fbae9961 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:27:01 +0000 Subject: [PATCH 053/103] Resolved Comments --- src/shared-endpoints.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 9840422..224295d 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -56,7 +56,7 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/public/account-ratio', params); } - //------------Market Data Endpoints------------> + //------------Account Data Endpoints------------> getApiKeyInfo(): GenericAPIResponse { return this.requestWrapper.get('v2/private/account/api-key'); From 5e4369d38522367afb0017021df04ad2969a2cb1 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:27:24 +0000 Subject: [PATCH 054/103] Update requestWrapper.ts --- src/util/requestWrapper.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/util/requestWrapper.ts b/src/util/requestWrapper.ts index 4a66bec..9dab5ad 100644 --- a/src/util/requestWrapper.ts +++ b/src/util/requestWrapper.ts @@ -10,7 +10,6 @@ export default class RequestUtil { private globalRequestOptions: AxiosRequestConfig; private key: string | undefined; private secret: string | undefined; - //all private constructor( key: string | undefined, From b3b327004bf9bdb93eadd4b73d50a44ad473e0d8 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:32:32 +0000 Subject: [PATCH 055/103] Added isPublicEndpoint Function Export --- src/util/requestUtils.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index b5f5761..3596c57 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -56,4 +56,14 @@ export function getBaseRESTInverseUrl(useLivenet?: boolean, restInverseOptions?: return baseUrlsInverse.livenet; } return baseUrlsInverse.testnet; -} \ No newline at end of file +} + +export function isPublicEndpoint = (endpoint: string): boolean => { + if (endpoint.startsWith('v2/public')) { + return true; + } + if (endpoint.startsWith('public/linear')) { + return true; + } + return false; +} From 3269e457c915f3efd0abedf0c5e57a7f70f09ea8 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:34:02 +0000 Subject: [PATCH 056/103] Moved function to util --- src/util/requestWrapper.ts | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/util/requestWrapper.ts b/src/util/requestWrapper.ts index 9dab5ad..7645061 100644 --- a/src/util/requestWrapper.ts +++ b/src/util/requestWrapper.ts @@ -1,6 +1,6 @@ import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios'; -import { signMessage, serializeParams, RestClientInverseOptions, GenericAPIResponse } from './requestUtils'; +import { signMessage, serializeParams, RestClientInverseOptions, GenericAPIResponse, isPublicEndpoint } from './requestUtils'; export default class RequestUtil { private timeOffset: number | null; @@ -68,17 +68,7 @@ export default class RequestUtil { * @private Make a HTTP request to a specific endpoint. Private endpoints are automatically signed. */ async _call(method: Method, endpoint: string, params?: any): GenericAPIResponse { - const isPublicEndpoint = (endpoint: string): boolean => { - if (endpoint.startsWith('v2/public')) { - return true; - } - if (endpoint.startsWith('public/linear')) { - return true; - } - return false; - } - - if (isPublicEndpoint(endpoint) === false) { + if (!isPublicEndpoint(endpoint)) { if (!this.key || !this.secret) { throw new Error('Private endpoints require api and private keys set'); } From 73f567c8096f5b8fc419ea4e1aead1069bf6028c Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:51:30 +0000 Subject: [PATCH 057/103] Indenting --- src/linear-client.ts | 660 +++++++++++++++++++++---------------------- 1 file changed, 330 insertions(+), 330 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index b71e7d7..a9228aa 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -4,9 +4,9 @@ import RequestWrapper from './util/requestWrapper'; import SharedEndpoints from './shared-endpoints'; export class LinearClient extends SharedEndpoints { - protected requestWrapper: RequestWrapper; + protected requestWrapper: RequestWrapper; - /** + /** * @public Creates an instance of the inverse REST API client. * * @param {string} key - your API key @@ -16,334 +16,334 @@ export class LinearClient extends SharedEndpoints { * @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios */ - constructor( - key?: string | undefined, - secret?: string | undefined, - livenet?: boolean, - restInverseOptions:RestClientInverseOptions = {}, // TODO: Rename this type to be more general. - requestOptions: AxiosRequestConfig = {} - ) { - super() - this.requestWrapper = new RequestWrapper( - key, - secret, - getBaseRESTInverseUrl(livenet), - restInverseOptions, - requestOptions - ); - return this; - } - - //------------Market Data Endpoints------------> - - getKline(params: { - symbol: string; - interval: string; - from: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('/public/linear/kline', params); - } + constructor( + key?: string | undefined, + secret?: string | undefined, + livenet?: boolean, + restInverseOptions:RestClientInverseOptions = {}, // TODO: Rename this type to be more general. + requestOptions: AxiosRequestConfig = {} + ) { + super() + this.requestWrapper = new RequestWrapper( + key, + secret, + getBaseRESTInverseUrl(livenet), + restInverseOptions, + requestOptions + ); + return this; + } + + //------------Market Data Endpoints------------> + + getKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('/public/linear/kline', params); + } - /** - * @deprecated use getTrades() instead - */ - getPublicTradingRecords(params: { - symbol: string; - from?: number; - limit?: number; - }): GenericAPIResponse { - return this.getTrades(params); - } + /** + * @deprecated use getTrades() instead + */ + getPublicTradingRecords(params: { + symbol: string; + from?: number; + limit?: number; + }): GenericAPIResponse { + return this.getTrades(params); + } - getTrades(params: { - symbol: string; - //from?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/recent-trading-records', params); - } - - getLastFundingRate(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/funding/prev-funding-rate', params); - } - - getMarkPriceKline(params: { - symbol: string; - interval: string; - from: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/mark-price-kline', params); - } - - getIndexPriceKline(params: { - symbol: string; - interval: string; - from: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/index-price-kline', params); - } - - getPremiumIndexKline(params: { - symbol: string; - interval: string; - from: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/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; - tp_trigger_by?: string; - sl_trigger_by?: string; - reduce_only?: boolean; - close_on_trigger?: boolean; - order_link_id?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/create', orderRequest); - } - - getActiveOrderList(params: { - order_id?: string; - order_link_id?: string; - symbol: string; - order?: string; - page?: number; - limit?: number; - order_status?: string; - - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/order/list', params); - } - - cancelActiveOrder(params: { - symbol: string; - order_id?: string; - order_link_id?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/cancel', params); - } - - cancelAllActiveOrders(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/cancel-all', params); - } - - replaceActiveOrder(params: { - order_id?: string; - order_link_id?: string; - symbol: string; - p_r_qty?: number; - p_r_price?: number; - take_profit?: number; - stop_loss?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/replace', params); - } - - queryActiveOrder(params: { - order_id?: string; - order_link_id?: string; - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/order/search', params); - } - - //Conditional Orders - - placeConditionalOrder(params: { - side: string; - symbol: string; - order_type: string; - qty: number; - price?: number; - base_price: number; - stop_px: number; - time_in_force: string; - trigger_by?: string; - close_on_trigger?: boolean; - order_link_id?: string; - reduce_only: boolean; - take_profit?: number; - stop_loss?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/stop-order/create', params); - } - - getConditionalOrder(params: { - stop_order_id?: string; - order_link_id?: string; - symbol: string; - stop_order_status?: string; - order?: string; - page?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/stop-order/list', params); - } - - cancelConditionalOrder(params: { - symbol: string; - stop_order_id?: string; - order_link_id?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/stop-order/cancel', params); - } - - cancelAllConditionalOrders(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/stop-order/cancel-all', params); - } - - replaceConditionalOrder(params: { - stop_order_id?: string; - order_link_id?: string; - symbol: string; - p_r_qty?: number; - p_r_price?: number; - p_r_trigger_price?: number; - take_profit?: number; - stop_loss?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/stop-order/replace', params); - } - - queryConditionalOrder(params: { - symbol: string; - stop_order_id?: string; - order_link_id?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/stop-order/search', params); - } - - //Position - - getPosition(params?: { - symbol?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/position/list', params); - } - - setAutoAddMargin(params?: { - symbol: string; - side: string; - auto_add_margin: boolean; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/set-auto-add-margin', params); - } - - setMarginSwitch(params?: { - symbol: string; - is_isolated: boolean; - buy_leverage: number; - sell_leverage: number; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/switch-isolated', params); - } - - setSwitchMode(params?: { - symbol: string; - tp_sl_mode: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/tpsl/switch-mode', params); - } - - setAddReduceMargin(params?: { - symbol: string; - side: string; - margin: number; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/add-margin', params); - } - - setUserLeverage(params: { - symbol: string; - buy_leverage: number; - sell_leverage: number; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/set-leverage', params); - } - - setTradingStop(params: { - symbol: string; - side: string; - take_profit?: number; - stop_loss?: number; - trailing_stop?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - sl_size?: number; - tp_size?: number; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/trading-stop', params); - } - - getTradeRecords(params: { - symbol: string; - start_time?: number; - end_time?: number; - exec_type?: string; - page?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/trade/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('private/linear/tpsl/switch-mode', params); - } - - //Risk Limit - - getRiskLimitList(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/risk-limit'); - } - - //Funding - - getPredictedFundingFee(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/funding/predicted-funding'); - } - - getLastFundingFee(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/funding/prev-funding'); - } - + getTrades(params: { + symbol: string; + //from?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/recent-trading-records', params); + } + + getLastFundingRate(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/funding/prev-funding-rate', params); + } + + getMarkPriceKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/mark-price-kline', params); + } + + getIndexPriceKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/index-price-kline', params); + } + + getPremiumIndexKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/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; + tp_trigger_by?: string; + sl_trigger_by?: string; + reduce_only?: boolean; + close_on_trigger?: boolean; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/create', orderRequest); + } + + getActiveOrderList(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + order?: string; + page?: number; + limit?: number; + order_status?: string; + + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/order/list', params); + } + + cancelActiveOrder(params: { + symbol: string; + order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/cancel', params); + } + + cancelAllActiveOrders(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/cancel-all', params); + } + + replaceActiveOrder(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + p_r_qty?: number; + p_r_price?: number; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/replace', params); + } + + queryActiveOrder(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/order/search', params); + } + + //Conditional Orders + + placeConditionalOrder(params: { + side: string; + symbol: string; + order_type: string; + qty: number; + price?: number; + base_price: number; + stop_px: number; + time_in_force: string; + trigger_by?: string; + close_on_trigger?: boolean; + order_link_id?: string; + reduce_only: boolean; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/create', params); + } + + getConditionalOrder(params: { + stop_order_id?: string; + order_link_id?: string; + symbol: string; + stop_order_status?: string; + order?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/stop-order/list', params); + } + + cancelConditionalOrder(params: { + symbol: string; + stop_order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/cancel', params); + } + + cancelAllConditionalOrders(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/cancel-all', params); + } + + replaceConditionalOrder(params: { + stop_order_id?: string; + order_link_id?: string; + symbol: string; + p_r_qty?: number; + p_r_price?: number; + p_r_trigger_price?: number; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/replace', params); + } + + queryConditionalOrder(params: { + symbol: string; + stop_order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/stop-order/search', params); + } + + //Position + + getPosition(params?: { + symbol?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/position/list', params); + } + + setAutoAddMargin(params?: { + symbol: string; + side: string; + auto_add_margin: boolean; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/set-auto-add-margin', params); + } + + setMarginSwitch(params?: { + symbol: string; + is_isolated: boolean; + buy_leverage: number; + sell_leverage: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/switch-isolated', params); + } + + setSwitchMode(params?: { + symbol: string; + tp_sl_mode: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/tpsl/switch-mode', params); + } + + setAddReduceMargin(params?: { + symbol: string; + side: string; + margin: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/add-margin', params); + } + + setUserLeverage(params: { + symbol: string; + buy_leverage: number; + sell_leverage: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/set-leverage', params); + } + + setTradingStop(params: { + symbol: string; + side: string; + take_profit?: number; + stop_loss?: number; + trailing_stop?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + sl_size?: number; + tp_size?: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/trading-stop', params); + } + + getTradeRecords(params: { + symbol: string; + start_time?: number; + end_time?: number; + exec_type?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/trade/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('private/linear/tpsl/switch-mode', params); + } + + //Risk Limit + + getRiskLimitList(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/risk-limit'); + } + + //Funding + + getPredictedFundingFee(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/funding/predicted-funding'); + } + + getLastFundingFee(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/funding/prev-funding'); + } + } From 6d58a65dbed5dc935b532189a1d12d8c26a36063 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:52:28 +0000 Subject: [PATCH 058/103] Indenting --- src/shared-endpoints.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 224295d..375856b 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -101,7 +101,7 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); } - //-------------API Data Endpoints--------------> + //-------------API Data Endpoints-------------> getServerTime(): GenericAPIResponse { return this.requestWrapper.get('v2/public/time'); From b1efaeb8570944a6cca5cdfb68ffabd7cca14083 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 14:54:29 +0000 Subject: [PATCH 059/103] Update linear-client.ts --- src/linear-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index a9228aa..f343ebd 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -97,7 +97,7 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('public/linear/premium-index-kline', params); } - //-----------Account Data Endpoints------------> + //------------Account Data Endpoints------------> //Active Orders From e35a6a57807b6a723a78762d54713d1d384f2e98 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Fri, 29 Jan 2021 16:31:29 +0000 Subject: [PATCH 060/103] Fixed Function --- src/util/requestUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index 3596c57..01fc785 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -58,7 +58,7 @@ export function getBaseRESTInverseUrl(useLivenet?: boolean, restInverseOptions?: return baseUrlsInverse.testnet; } -export function isPublicEndpoint = (endpoint: string): boolean => { +export function isPublicEndpoint (endpoint: string): boolean { if (endpoint.startsWith('v2/public')) { return true; } From e092b1355ad9565a4fa345a8391241e905496315 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 30 Jan 2021 18:11:55 +0000 Subject: [PATCH 061/103] fix indent --- src/linear-client.ts | 88 ++++++++--------- src/shared-endpoints.ts | 214 ++++++++++++++++++++-------------------- 2 files changed, 151 insertions(+), 151 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index f343ebd..ede59b3 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -22,7 +22,7 @@ export class LinearClient extends SharedEndpoints { livenet?: boolean, restInverseOptions:RestClientInverseOptions = {}, // TODO: Rename this type to be more general. requestOptions: AxiosRequestConfig = {} - ) { + ) { super() this.requestWrapper = new RequestWrapper( key, @@ -32,10 +32,10 @@ export class LinearClient extends SharedEndpoints { requestOptions ); return this; - } - + } + //------------Market Data Endpoints------------> - + getKline(params: { symbol: string; interval: string; @@ -44,7 +44,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('/public/linear/kline', params); } - + /** * @deprecated use getTrades() instead */ @@ -63,13 +63,13 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('public/linear/recent-trading-records', params); } - + getLastFundingRate(params: { symbol: string; }): GenericAPIResponse { return this.requestWrapper.get('public/linear/funding/prev-funding-rate', params); } - + getMarkPriceKline(params: { symbol: string; interval: string; @@ -78,7 +78,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('public/linear/mark-price-kline', params); } - + getIndexPriceKline(params: { symbol: string; interval: string; @@ -87,7 +87,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('public/linear/index-price-kline', params); } - + getPremiumIndexKline(params: { symbol: string; interval: string; @@ -96,12 +96,12 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('public/linear/premium-index-kline', params); } - + //------------Account Data Endpoints------------> - + //Active Orders - - placeActiveOrder(orderRequest: { + + placeActiveOrder(params: { side: string; symbol: string; order_type: string; @@ -116,9 +116,9 @@ export class LinearClient extends SharedEndpoints { close_on_trigger?: boolean; order_link_id?: string; }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/create', orderRequest); + return this.requestWrapper.post('private/linear/order/create', params); } - + getActiveOrderList(params: { order_id?: string; order_link_id?: string; @@ -127,11 +127,11 @@ export class LinearClient extends SharedEndpoints { page?: number; limit?: number; order_status?: string; - + }): GenericAPIResponse { return this.requestWrapper.get('private/linear/order/list', params); } - + cancelActiveOrder(params: { symbol: string; order_id?: string; @@ -139,13 +139,13 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/order/cancel', params); } - + cancelAllActiveOrders(params: { symbol: string; }): GenericAPIResponse { return this.requestWrapper.post('private/linear/order/cancel-all', params); } - + replaceActiveOrder(params: { order_id?: string; order_link_id?: string; @@ -159,7 +159,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/order/replace', params); } - + queryActiveOrder(params: { order_id?: string; order_link_id?: string; @@ -167,9 +167,9 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('private/linear/order/search', params); } - + //Conditional Orders - + placeConditionalOrder(params: { side: string; symbol: string; @@ -190,7 +190,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/stop-order/create', params); } - + getConditionalOrder(params: { stop_order_id?: string; order_link_id?: string; @@ -202,7 +202,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('private/linear/stop-order/list', params); } - + cancelConditionalOrder(params: { symbol: string; stop_order_id?: string; @@ -210,13 +210,13 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/stop-order/cancel', params); } - + cancelAllConditionalOrders(params: { symbol: string; }): GenericAPIResponse { return this.requestWrapper.post('private/linear/stop-order/cancel-all', params); } - + replaceConditionalOrder(params: { stop_order_id?: string; order_link_id?: string; @@ -231,23 +231,23 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/stop-order/replace', params); } - + queryConditionalOrder(params: { symbol: string; stop_order_id?: string; order_link_id?: string; }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/stop-order/search', params); + return this.requestWrapper.get('private/linear/stop-order/search', params); } - + //Position - + getPosition(params?: { symbol?: string; }): GenericAPIResponse { return this.requestWrapper.get('private/linear/position/list', params); } - + setAutoAddMargin(params?: { symbol: string; side: string; @@ -255,7 +255,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/position/set-auto-add-margin', params); } - + setMarginSwitch(params?: { symbol: string; is_isolated: boolean; @@ -264,14 +264,14 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/position/switch-isolated', params); } - + setSwitchMode(params?: { symbol: string; tp_sl_mode: string; }): GenericAPIResponse { return this.requestWrapper.post('private/linear/tpsl/switch-mode', params); } - + setAddReduceMargin(params?: { symbol: string; side: string; @@ -279,7 +279,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/position/add-margin', params); } - + setUserLeverage(params: { symbol: string; buy_leverage: number; @@ -287,7 +287,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/position/set-leverage', params); } - + setTradingStop(params: { symbol: string; side: string; @@ -301,7 +301,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('private/linear/position/trading-stop', params); } - + getTradeRecords(params: { symbol: string; start_time?: number; @@ -312,7 +312,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('private/linear/trade/execution/list', params); } - + getClosedPnl(params: { symbol: string; start_time?: number; @@ -323,27 +323,27 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('private/linear/tpsl/switch-mode', params); } - + //Risk Limit - + getRiskLimitList(params: { symbol: string; }): GenericAPIResponse { return this.requestWrapper.get('public/linear/risk-limit'); } - + //Funding - + getPredictedFundingFee(params: { symbol: string; }): GenericAPIResponse { return this.requestWrapper.get('private/linear/funding/predicted-funding'); } - + getLastFundingFee(params: { symbol: string; }): GenericAPIResponse { return this.requestWrapper.get('private/linear/funding/prev-funding'); } - + } diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 375856b..b77c4d3 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -1,121 +1,121 @@ -//type Constructor = new (...args: any[]) => {}; import { GenericAPIResponse } from './util/requestUtils'; import RequestWrapper from './util/requestWrapper'; export default class SharedEndpoints { - protected requestWrapper: RequestWrapper; // XXX Is there a way to say that Base has to provide this? - - //------------Market Data Endpoints------------> - - getOrderBook(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/orderBook/L2', params); - } + // TODO: Is there a way to say that Base has to provide this? + protected requestWrapper: RequestWrapper; - getTickers(params?: { - symbol?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/tickers', params); - } - - getSymbols(): GenericAPIResponse { - return this.requestWrapper.get('v2/public/symbols'); - } + //------------Market Data Endpoints------------> - getLiquidations(params: { - symbol: string; - from?: number; - limit?: number; - start_time?: number; - end_time?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/liq-records', params); - } - - getOpenInterest(params: { - symbol: string; - period: string; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/open-interest', params); - } + getOrderBook(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/orderBook/L2', params); + } - getLatestBigDeal(params: { - symbol: string; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/big-deal', params); - } + getTickers(params?: { + symbol?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/tickers', params); + } - getLongShortRatio(params: { - symbol: string; - period: string; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/public/account-ratio', params); - } - - //------------Account Data Endpoints------------> - - getApiKeyInfo(): GenericAPIResponse { - return this.requestWrapper.get('v2/private/account/api-key'); - } - - //------------Wallet Data Endpoints------------> - - getWalletBalance(params: { - coin?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/wallet/balance',params) - } + getSymbols(): GenericAPIResponse { + return this.requestWrapper.get('v2/public/symbols'); + } - getAssetExchangeRecords(params?: { - limit?: number; - from?: number; - direction?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/exchange-order/list', params); - } + getLiquidations(params: { + symbol: string; + from?: number; + limit?: number; + start_time?: number; + end_time?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/liq-records', params); + } - getWalletFundRecords(params?: { - start_date?: string; - end_date?: string; - currency?: string; - coin?: string; - wallet_fund_type?: string; - page?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/wallet/fund/records', params); - } + getOpenInterest(params: { + symbol: string; + period: string; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/open-interest', params); + } - getWithdrawRecords(params: { - start_date?: string; - end_date?: string; - coin?: string; - status?: string; - page?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); - } - - //-------------API Data Endpoints-------------> + getLatestBigDeal(params: { + symbol: string; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/big-deal', params); + } - getServerTime(): GenericAPIResponse { - return this.requestWrapper.get('v2/public/time'); - } + getLongShortRatio(params: { + symbol: string; + period: string; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/public/account-ratio', params); + } - getApiAnnouncements(): GenericAPIResponse { - return this.requestWrapper.get('v2/public/announcement'); - } + //------------Account Data Endpoints------------> - async getTimeOffset(): Promise { - const start = Date.now(); - return this.getServerTime().then(result => { - const end = Date.now(); - return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2)); - }); - } -} + getApiKeyInfo(): GenericAPIResponse { + return this.requestWrapper.get('v2/private/account/api-key'); + } + + //------------Wallet Data Endpoints------------> + + getWalletBalance(params: { + coin?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/wallet/balance', params) + } + + getAssetExchangeRecords(params?: { + limit?: number; + from?: number; + direction?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/exchange-order/list', params); + } + + getWalletFundRecords(params?: { + start_date?: string; + end_date?: string; + currency?: string; + coin?: string; + wallet_fund_type?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/wallet/fund/records', params); + } + + getWithdrawRecords(params: { + start_date?: string; + end_date?: string; + coin?: string; + status?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); + } + + //-------------API Data Endpoints-------------> + + getServerTime(): GenericAPIResponse { + return this.requestWrapper.get('v2/public/time'); + } + + getApiAnnouncements(): GenericAPIResponse { + return this.requestWrapper.get('v2/public/announcement'); + } + + async getTimeOffset(): Promise < number > { + const start = Date.now(); + return this.getServerTime().then(result => { + const end = Date.now(); + return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2)); + }); + } +} \ No newline at end of file From e292f8694b61950f710d00c55bce787b2564668a Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 30 Jan 2021 18:12:15 +0000 Subject: [PATCH 062/103] cleaning in websocket client --- src/websocket-client.ts | 194 ++++++++++++++++++++++++---------------- 1 file changed, 117 insertions(+), 77 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 546e8d4..bdcee09 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -3,15 +3,15 @@ import { InverseClient } from './inverse-client'; import { LinearClient } from './linear-client'; import { DefaultLogger } from './logger'; import { signMessage, serializeParams } from './util/requestUtils'; -// import WebSocket from 'ws'; + import WebSocket from 'isomorphic-ws'; -const iwsUrls = { +const inverseEndpoints = { livenet: 'wss://stream.bybit.com/realtime', testnet: 'wss://stream-testnet.bybit.com/realtime' }; -const lwsUrls = { +const linearEndpoints = { livenet: 'wss://stream.bybit.com/realtime_public', testnet: 'wss://stream-testnet.bybit.com/realtime_public' }; @@ -22,7 +22,15 @@ const READY_STATE_CONNECTED = 2; const READY_STATE_CLOSING = 3; const READY_STATE_RECONNECTING = 4; -export interface WebsocketClientOptions { +enum WsConnectionState { + READY_STATE_INITIAL, + READY_STATE_CONNECTING, + READY_STATE_CONNECTED, + READY_STATE_CLOSING, + READY_STATE_RECONNECTING +}; + +export interface WebsocketClientConfigurableOptions { key?: string; secret?: string; livenet?: boolean; @@ -35,21 +43,27 @@ export interface WebsocketClientOptions { wsUrl?: string; }; +export interface WebsocketClientOptions extends WebsocketClientConfigurableOptions { + livenet: boolean; + linear: boolean; + pongTimeout: number; + pingInterval: number; + reconnectTimeout: number; +}; + type Logger = typeof DefaultLogger; - - export class WebsocketClient extends EventEmitter { private logger: Logger; - private readyState: number; + private readyState: WsConnectionState; private pingInterval?: number | undefined; private pongTimeout?: number | undefined; private client: InverseClient | LinearClient; - private _subscriptions: Set; + private subcribedTopics: Set; private ws: WebSocket; private options: WebsocketClientOptions; - constructor(options: WebsocketClientOptions, logger?: Logger) { + constructor(options: WebsocketClientConfigurableOptions, logger?: Logger) { super(); this.logger = logger || DefaultLogger; @@ -68,71 +82,89 @@ export class WebsocketClient extends EventEmitter { if (this.options.linear === true) { - this.client = new LinearClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); + this.client = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); }else{ - this.client = new InverseClient(undefined, undefined, this.options.livenet, this.options.restOptions, this.options.requestOptions); + this.client = new InverseClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); } - - this._subscriptions = new Set(); - this._connect(); + + this.subcribedTopics = new Set(); + this.connect(); } - subscribe(topics) { - if (!Array.isArray(topics)) topics = [topics]; - topics.forEach(topic => this._subscriptions.add(topic)); - - // subscribe not necessary if not yet connected (will subscribe onOpen) - if (this.readyState === READY_STATE_CONNECTED) this._subscribe(topics); + isLivenet(): boolean { + return this.options.livenet === true; } - unsubscribe(topics) { - if (!Array.isArray(topics)) topics = [topics]; + /** + * Add topic/topics to WS subscription list + */ + public subscribe(wsTopics: string[] | string) { + const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; + topics.forEach(topic => this.subcribedTopics.add(topic)); - topics.forEach(topic => this._subscriptions.delete(topic)); + // subscribe not necessary if not yet connected (will automatically subscribe onOpen) + if (this.readyState === READY_STATE_CONNECTED) { + this.requestSubscribeTopics(topics); + } + } + + /** + * Remove topic/topics from WS subscription list + */ + public unsubscribe(wsTopics: string[] | string) { + const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; + topics.forEach(topic => this.subcribedTopics.delete(topic)); // unsubscribe not necessary if not yet connected - if (this.readyState === READY_STATE_CONNECTED) this._unsubscribe(topics); + if (this.readyState === READY_STATE_CONNECTED) { + this.requestUnsubscribeTopics(topics); + } } close() { this.logger.info('Closing connection', {category: 'bybit-ws'}); this.readyState = READY_STATE_CLOSING; - this._teardown(); + this.teardown(); this.ws && this.ws.close(); } - _getWsUrl() { + private getWsUrl() { if (this.options.wsUrl) { return this.options.wsUrl; } if (this.options.linear){ - return lwsUrls[this.options.livenet ? 'livenet' : 'testnet']; + return linearEndpoints[this.options.livenet ? 'livenet' : 'testnet']; } - return iwsUrls[this.options.livenet ? 'livenet' : 'testnet']; + return inverseEndpoints[this.options.livenet ? 'livenet' : 'testnet']; } - async _connect() { + private async connect() { try { - if (this.readyState === READY_STATE_INITIAL) this.readyState = READY_STATE_CONNECTING; + if (this.readyState === READY_STATE_INITIAL) { + this.readyState = READY_STATE_CONNECTING; + } - const authParams = await this._authenticate(); - const url = this._getWsUrl() + authParams; + const authParams = await this.getAuthParams(); + const url = this.getWsUrl() + authParams; const ws = new WebSocket(url); - ws.onopen = this._wsOpenHandler.bind(this); - ws.onmessage = this._wsMessageHandler.bind(this); - ws.onerror = this._wsOnErrorHandler.bind(this); - ws.onclose = this._wsCloseHandler.bind(this); + ws.onopen = this.onWsOpen.bind(this); + ws.onmessage = this.onWsMessage.bind(this); + ws.onerror = this.onWsError.bind(this); + ws.onclose = this.onWsClose.bind(this); this.ws = ws; - + } catch (err) { this.logger.error('Connection failed: ', err); - this._reconnect(this.options.reconnectTimeout); + this.reconnectWithDelay(this.options.reconnectTimeout!); } } - async _authenticate() { + /** + * Return params required to make authorized request + */ + private async getAuthParams(): Promise { if (this.options.key && this.options.secret) { this.logger.debug('Starting authenticated websocket client.', {category: 'bybit-ws'}); @@ -155,8 +187,8 @@ export class WebsocketClient extends EventEmitter { return ''; } - _reconnect(timeout) { - this._teardown(); + private reconnectWithDelay(connectionDelay: number) { + this.teardown(); if (this.readyState !== READY_STATE_CONNECTING) { this.readyState = READY_STATE_RECONNECTING; } @@ -164,28 +196,28 @@ export class WebsocketClient extends EventEmitter { setTimeout(() => { this.logger.info('Reconnecting to server', { category: 'bybit-ws' }); - this._connect(); - }, timeout); + this.connect(); + }, connectionDelay); } - _ping() { + private ping() { clearTimeout(this.pongTimeout!); delete this.pongTimeout; this.logger.silly('Sending ping', { category: 'bybit-ws' }); this.ws.send(JSON.stringify({op: 'ping'})); - + this.pongTimeout = setTimeout(() => { this.logger.info('Pong timeout', { category: 'bybit-ws' }); - this._teardown(); + this.teardown(); // this.ws.terminate(); // TODO: does this work? this.ws.close(); }, this.options.pongTimeout); } - _teardown() { + private teardown() { if (this.pingInterval) clearInterval(this.pingInterval); if (this.pongTimeout) clearTimeout(this.pongTimeout); @@ -193,7 +225,31 @@ export class WebsocketClient extends EventEmitter { this.pingInterval = undefined; } - _wsOpenHandler() { + /** + * Send WS message to subscribe to topics. + */ + private requestSubscribeTopics(topics: string[]) { + const msgStr = JSON.stringify({ + op: 'subscribe', + 'args': topics + }); + + this.ws.send(msgStr); + } + + /** + * Send WS message to unsubscribe from topics. + */ + private requestUnsubscribeTopics(topics: string[]) { + const msgStr = JSON.stringify({ + op: 'unsubscribe', + 'args': topics + }); + + this.ws.send(msgStr); + } + + private onWsOpen() { if (this.readyState === READY_STATE_CONNECTING) { this.logger.info('Websocket connected', { category: 'bybit-ws', livenet: this.options.livenet, linear: this.options.linear }); this.emit('open'); @@ -204,32 +260,34 @@ export class WebsocketClient extends EventEmitter { this.readyState = READY_STATE_CONNECTED; - this._subscribe([...this._subscriptions]); - this.pingInterval = setInterval(this._ping.bind(this), this.options.pingInterval); + this.requestSubscribeTopics([...this.subcribedTopics]); + this.pingInterval = setInterval(this.ping.bind(this), this.options.pingInterval); } - _wsMessageHandler(message) { + private onWsMessage(message) { const msg = JSON.parse(message && message.data || message); if ('success' in msg) { - this._handleResponse(msg); + this.onWsMessageResponse(msg); } else if (msg.topic) { - this._handleUpdate(msg); + this.onWsMessageUpdate(msg); } else { this.logger.warning('Got unhandled ws message', msg); } } - _wsOnErrorHandler(err) { + private onWsError(err) { this.logger.error('Websocket error', {category: 'bybit-ws', err}); - if (this.readyState === READY_STATE_CONNECTED) this.emit('error', err); + if (this.readyState === READY_STATE_CONNECTED) { + this.emit('error', err); + } } - _wsCloseHandler() { + private onWsClose() { this.logger.info('Websocket connection closed', {category: 'bybit-ws'}); if (this.readyState !== READY_STATE_CLOSING) { - this._reconnect(this.options.reconnectTimeout); + this.reconnectWithDelay(this.options.reconnectTimeout!); this.emit('reconnect'); } else { this.readyState = READY_STATE_INITIAL; @@ -237,7 +295,7 @@ export class WebsocketClient extends EventEmitter { } } - _handleResponse(response) { + private onWsMessageResponse(response) { if ( response.request && response.request.op === 'ping' && @@ -251,25 +309,7 @@ export class WebsocketClient extends EventEmitter { } } - _handleUpdate(message) { + private onWsMessageUpdate(message) { this.emit('update', message); } - - _subscribe(topics) { - const msgStr = JSON.stringify({ - op: 'subscribe', - 'args': topics - }); - - this.ws.send(msgStr); - } - - _unsubscribe(topics) { - const msgStr = JSON.stringify({ - op: 'unsubscribe', - 'args': topics - }); - - this.ws.send(msgStr); - } }; From 4822c5b6d3dbc8b7b45f1e91962ec5f6d90da525 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 30 Jan 2021 18:38:21 +0000 Subject: [PATCH 063/103] cleaning and prep for multi-connection ws client --- src/websocket-client.ts | 137 +++++++++++++++++++++++++--------------- 1 file changed, 87 insertions(+), 50 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index bdcee09..eb61350 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -53,23 +53,50 @@ export interface WebsocketClientOptions extends WebsocketClientConfigurableOptio type Logger = typeof DefaultLogger; +// class WsStore { +// private connections: { +// [key: string]: WebSocket +// }; +// private logger: Logger; + +// constructor(logger: Logger) { +// this.connections = {} +// this.logger = logger || DefaultLogger; +// } + +// getConnection(key: string) { +// return this.connections[key]; +// } + +// setConnection(key: string, wsConnection: WebSocket) { +// const existingConnection = this.getConnection(key); +// if (existingConnection) { +// this.logger.info('WsStore setConnection() overwriting existing connection: ', existingConnection); +// } +// this.connections[key] = wsConnection; +// } +// } + export class WebsocketClient extends EventEmitter { + private activePingTimer?: number | undefined; + private activePongTimer?: number | undefined; + private logger: Logger; - private readyState: WsConnectionState; - private pingInterval?: number | undefined; - private pongTimeout?: number | undefined; + private wsState: WsConnectionState; private client: InverseClient | LinearClient; private subcribedTopics: Set; - private ws: WebSocket; private options: WebsocketClientOptions; + private ws: WebSocket; + // private wsStore: WsStore; + constructor(options: WebsocketClientConfigurableOptions, logger?: Logger) { super(); this.logger = logger || DefaultLogger; - this.readyState = READY_STATE_INITIAL; - this.pingInterval = undefined; - this.pongTimeout = undefined; + this.wsState = READY_STATE_INITIAL; + this.activePingTimer = undefined; + this.activePongTimer = undefined; this.options = { livenet: false, @@ -88,6 +115,7 @@ export class WebsocketClient extends EventEmitter { } this.subcribedTopics = new Set(); + // this.wsStore = new WsStore(this.logger); this.connect(); } @@ -103,7 +131,7 @@ export class WebsocketClient extends EventEmitter { topics.forEach(topic => this.subcribedTopics.add(topic)); // subscribe not necessary if not yet connected (will automatically subscribe onOpen) - if (this.readyState === READY_STATE_CONNECTED) { + if (this.wsState === READY_STATE_CONNECTED) { this.requestSubscribeTopics(topics); } } @@ -116,14 +144,14 @@ export class WebsocketClient extends EventEmitter { topics.forEach(topic => this.subcribedTopics.delete(topic)); // unsubscribe not necessary if not yet connected - if (this.readyState === READY_STATE_CONNECTED) { + if (this.wsState === READY_STATE_CONNECTED) { this.requestUnsubscribeTopics(topics); } } close() { this.logger.info('Closing connection', {category: 'bybit-ws'}); - this.readyState = READY_STATE_CLOSING; + this.wsState = READY_STATE_CLOSING; this.teardown(); this.ws && this.ws.close(); } @@ -140,21 +168,15 @@ export class WebsocketClient extends EventEmitter { private async connect() { try { - if (this.readyState === READY_STATE_INITIAL) { - this.readyState = READY_STATE_CONNECTING; + if (this.wsState === READY_STATE_INITIAL) { + this.wsState = READY_STATE_CONNECTING; } const authParams = await this.getAuthParams(); const url = this.getWsUrl() + authParams; - const ws = new WebSocket(url); - - ws.onopen = this.onWsOpen.bind(this); - ws.onmessage = this.onWsMessage.bind(this); - ws.onerror = this.onWsError.bind(this); - ws.onclose = this.onWsClose.bind(this); - - this.ws = ws; + this.ws = this.connectToWsUrl(url, 'main'); + return this.ws; } catch (err) { this.logger.error('Connection failed: ', err); this.reconnectWithDelay(this.options.reconnectTimeout!); @@ -166,7 +188,7 @@ export class WebsocketClient extends EventEmitter { */ private async getAuthParams(): Promise { if (this.options.key && this.options.secret) { - this.logger.debug('Starting authenticated websocket client.', {category: 'bybit-ws'}); + this.logger.debug('Getting auth\'d request params', {category: 'bybit-ws'}); const timeOffset = await this.client.getTimeOffset(); @@ -179,7 +201,7 @@ export class WebsocketClient extends EventEmitter { return '?' + serializeParams(params); } else if (this.options.key || this.options.secret) { - this.logger.warning('Could not authenticate websocket, either api key or private key missing.', { category: 'bybit-ws' }); + this.logger.warning('Connot authenticate websocket, either api or private keys missing.', { category: 'bybit-ws' }); } else { this.logger.debug('Starting public only websocket client.', { category: 'bybit-ws' }); } @@ -189,8 +211,8 @@ export class WebsocketClient extends EventEmitter { private reconnectWithDelay(connectionDelay: number) { this.teardown(); - if (this.readyState !== READY_STATE_CONNECTING) { - this.readyState = READY_STATE_RECONNECTING; + if (this.wsState !== READY_STATE_CONNECTING) { + this.wsState = READY_STATE_RECONNECTING; } setTimeout(() => { @@ -201,14 +223,14 @@ export class WebsocketClient extends EventEmitter { } private ping() { - clearTimeout(this.pongTimeout!); - delete this.pongTimeout; + clearTimeout(this.activePongTimer!); + delete this.activePongTimer; this.logger.silly('Sending ping', { category: 'bybit-ws' }); this.ws.send(JSON.stringify({op: 'ping'})); - this.pongTimeout = setTimeout(() => { + this.activePongTimer = setTimeout(() => { this.logger.info('Pong timeout', { category: 'bybit-ws' }); this.teardown(); // this.ws.terminate(); @@ -218,54 +240,69 @@ export class WebsocketClient extends EventEmitter { } private teardown() { - if (this.pingInterval) clearInterval(this.pingInterval); - if (this.pongTimeout) clearTimeout(this.pongTimeout); + if (this.activePingTimer) { + clearInterval(this.activePingTimer); + } + if (this.activePongTimer) { + clearTimeout(this.activePongTimer); + } - this.pongTimeout = undefined; - this.pingInterval = undefined; + this.activePongTimer = undefined; + this.activePingTimer = undefined; } /** * Send WS message to subscribe to topics. */ private requestSubscribeTopics(topics: string[]) { - const msgStr = JSON.stringify({ + const wsMessage = JSON.stringify({ op: 'subscribe', - 'args': topics + args: topics }); - this.ws.send(msgStr); + this.ws.send(wsMessage); } /** * Send WS message to unsubscribe from topics. */ private requestUnsubscribeTopics(topics: string[]) { - const msgStr = JSON.stringify({ + const wsMessage = JSON.stringify({ op: 'unsubscribe', - 'args': topics + args: topics }); - this.ws.send(msgStr); + this.ws.send(wsMessage); } - private onWsOpen() { - if (this.readyState === READY_STATE_CONNECTING) { + private connectToWsUrl(url: string, wsKey: string): WebSocket { + const ws = new WebSocket(url); + + ws.onopen = event => this.onWsOpen(event, wsKey); + ws.onmessage = event => this.onWsMessage(event, wsKey); + ws.onerror = event => this.onWsError(event, wsKey); + ws.onclose = event => this.onWsClose(event, wsKey); + + return ws; + } + + private onWsOpen(event, wsRef?: string) { + if (this.wsState === READY_STATE_CONNECTING) { this.logger.info('Websocket connected', { category: 'bybit-ws', livenet: this.options.livenet, linear: this.options.linear }); this.emit('open'); - } else if (this.readyState === READY_STATE_RECONNECTING) { + } else if (this.wsState === READY_STATE_RECONNECTING) { this.logger.info('Websocket reconnected', { category: 'bybit-ws', livenet: this.options.livenet }); this.emit('reconnected'); } - this.readyState = READY_STATE_CONNECTED; + this.wsState = READY_STATE_CONNECTED; this.requestSubscribeTopics([...this.subcribedTopics]); - this.pingInterval = setInterval(this.ping.bind(this), this.options.pingInterval); + this.activePingTimer = setInterval(this.ping.bind(this), this.options.pingInterval); } - private onWsMessage(message) { - const msg = JSON.parse(message && message.data || message); + private onWsMessage(event, wsRef?: string) { + const msg = JSON.parse(event && event.data || event); if ('success' in msg) { this.onWsMessageResponse(msg); @@ -276,21 +313,21 @@ export class WebsocketClient extends EventEmitter { } } - private onWsError(err) { + private onWsError(err, wsRef?: string) { this.logger.error('Websocket error', {category: 'bybit-ws', err}); - if (this.readyState === READY_STATE_CONNECTED) { + if (this.wsState === READY_STATE_CONNECTED) { this.emit('error', err); } } - private onWsClose() { + private onWsClose(event, wsRef?: string) { this.logger.info('Websocket connection closed', {category: 'bybit-ws'}); - if (this.readyState !== READY_STATE_CLOSING) { + if (this.wsState !== READY_STATE_CLOSING) { this.reconnectWithDelay(this.options.reconnectTimeout!); this.emit('reconnect'); } else { - this.readyState = READY_STATE_INITIAL; + this.wsState = READY_STATE_INITIAL; this.emit('close'); } } @@ -303,7 +340,7 @@ export class WebsocketClient extends EventEmitter { response.success === true ) { this.logger.silly('pong recieved', {category: 'bybit-ws'}); - clearTimeout(this.pongTimeout); + clearTimeout(this.activePongTimer); } else { this.emit('response', response); } From 65448609df2e20be4ab4beb84a903f253605dc9a Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 31 Jan 2021 12:33:44 +0000 Subject: [PATCH 064/103] cleaning & checked with inverse --- src/websocket-client.ts | 152 ++++++++++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 44 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index eb61350..77c78bf 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -16,6 +16,8 @@ const linearEndpoints = { testnet: 'wss://stream-testnet.bybit.com/realtime_public' }; +const loggerCategory = { category: 'bybit-ws' }; + const READY_STATE_INITIAL = 0; const READY_STATE_CONNECTING = 1; const READY_STATE_CONNECTED = 2; @@ -53,29 +55,57 @@ export interface WebsocketClientOptions extends WebsocketClientConfigurableOptio type Logger = typeof DefaultLogger; -// class WsStore { -// private connections: { -// [key: string]: WebSocket -// }; -// private logger: Logger; +class WsStore { + private connections: { + [key: string]: WebSocket + }; + private connectionState: { + [key: string]: WsConnectionState + } + private logger: Logger; -// constructor(logger: Logger) { -// this.connections = {} -// this.logger = logger || DefaultLogger; -// } + constructor(logger: Logger) { + this.connections = {} + this.connectionState = {}; + this.logger = logger || DefaultLogger; + } -// getConnection(key: string) { -// return this.connections[key]; -// } + getConnection(key: string) { + return this.connections[key]; + } -// setConnection(key: string, wsConnection: WebSocket) { -// const existingConnection = this.getConnection(key); -// if (existingConnection) { -// this.logger.info('WsStore setConnection() overwriting existing connection: ', existingConnection); -// } -// this.connections[key] = wsConnection; -// } -// } + setConnection(key: string, wsConnection: WebSocket) { + const existingConnection = this.getConnection(key); + if (existingConnection) { + this.logger.info('WsStore setConnection() overwriting existing connection: ', existingConnection); + } + this.connections[key] = wsConnection; + } + + clearConnection(key: string) { + const existingConnection = this.getConnection(key); + if (existingConnection) { + delete this.connections[key]; + } + } + + getConnectionState(key: string) { + return this.connectionState[key]; + } + + setConnectionState(key: string, state: WsConnectionState) { + this.connectionState[key] = state; + } + + isConnectionState(key: string, state: WsConnectionState) { + const a = this.getConnectionState(key) === state; + const b = this.getConnectionState(key) == state; + if (a != b) { + console.error('connection state doesnt match: ', { state, storedState: this.getConnectionState(key) }); + } + return this.getConnectionState(key) === state; + } +} export class WebsocketClient extends EventEmitter { private activePingTimer?: number | undefined; @@ -88,13 +118,14 @@ export class WebsocketClient extends EventEmitter { private options: WebsocketClientOptions; private ws: WebSocket; - // private wsStore: WsStore; + private wsStore: WsStore; constructor(options: WebsocketClientConfigurableOptions, logger?: Logger) { super(); this.logger = logger || DefaultLogger; - this.wsState = READY_STATE_INITIAL; + this.subcribedTopics = new Set(); + this.wsStore = new WsStore(this.logger); this.activePingTimer = undefined; this.activePongTimer = undefined; @@ -107,6 +138,8 @@ export class WebsocketClient extends EventEmitter { ...options }; + this.wsState = READY_STATE_INITIAL; + this.setWsState('main', READY_STATE_INITIAL); if (this.options.linear === true) { this.client = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); @@ -114,9 +147,15 @@ export class WebsocketClient extends EventEmitter { this.client = new InverseClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); } - this.subcribedTopics = new Set(); - // this.wsStore = new WsStore(this.logger); - this.connect(); + this.connect('main'); + } + + getWsState(wsRefKey: string) { + return this.wsStore.getConnectionState(wsRefKey); + } + + setWsState(wsRefKey: string, state: WsConnectionState) { + this.wsStore.setConnectionState(wsRefKey, state); } isLivenet(): boolean { @@ -150,8 +189,9 @@ export class WebsocketClient extends EventEmitter { } close() { - this.logger.info('Closing connection', {category: 'bybit-ws'}); + this.logger.info('Closing connection', loggerCategory); this.wsState = READY_STATE_CLOSING; + this.setWsState('main', READY_STATE_CLOSING); this.teardown(); this.ws && this.ws.close(); } @@ -166,29 +206,49 @@ export class WebsocketClient extends EventEmitter { return inverseEndpoints[this.options.livenet ? 'livenet' : 'testnet']; } - private async connect() { + private async connect(wsRefKey: string = 'main') { try { if (this.wsState === READY_STATE_INITIAL) { this.wsState = READY_STATE_CONNECTING; + this.setWsState(wsRefKey, READY_STATE_CONNECTING); } const authParams = await this.getAuthParams(); const url = this.getWsUrl() + authParams; - this.ws = this.connectToWsUrl(url, 'main'); + this.ws = this.connectToWsUrl(url, wsRefKey); return this.ws; } catch (err) { - this.logger.error('Connection failed: ', err); + this.parseWsError('Connection failed', err); this.reconnectWithDelay(this.options.reconnectTimeout!); } } + private parseWsError(context: string, error, wsRef?: string) { + if (!error.message) { + this.logger.error(context + ': due to unexpected error: ', error); + return; + } + + switch (error.message) { + case 'Unexpected server response: 401': + this.logger.error(`${context} due to 401 authorization failure.`, loggerCategory); + break; + + default: + this.logger.error(`{context} due to unexpected response error: ${error.msg}`); + break; + } + } + /** * Return params required to make authorized request */ private async getAuthParams(): Promise { - if (this.options.key && this.options.secret) { - this.logger.debug('Getting auth\'d request params', {category: 'bybit-ws'}); + const { key, secret } = this.options; + + if (key && secret) { + this.logger.debug('Getting auth\'d request params', loggerCategory); const timeOffset = await this.client.getTimeOffset(); @@ -197,26 +257,27 @@ export class WebsocketClient extends EventEmitter { expires: (Date.now() + timeOffset + 5000) }; - params.signature = signMessage('GET/realtime' + params.expires, this.options.secret); + params.signature = signMessage('GET/realtime' + params.expires, secret); return '?' + serializeParams(params); - } else if (this.options.key || this.options.secret) { - this.logger.warning('Connot authenticate websocket, either api or private keys missing.', { category: 'bybit-ws' }); + } else if (!key || !secret) { + this.logger.warning('Connot authenticate websocket, either api or private keys missing.', loggerCategory); } else { - this.logger.debug('Starting public only websocket client.', { category: 'bybit-ws' }); + this.logger.debug('Starting public only websocket client.', loggerCategory); } - return ''; + return ''; } private reconnectWithDelay(connectionDelay: number) { this.teardown(); if (this.wsState !== READY_STATE_CONNECTING) { this.wsState = READY_STATE_RECONNECTING; + this.setWsState('main', READY_STATE_RECONNECTING); } setTimeout(() => { - this.logger.info('Reconnecting to server', { category: 'bybit-ws' }); + this.logger.info('Reconnecting to server', loggerCategory); this.connect(); }, connectionDelay); @@ -226,12 +287,12 @@ export class WebsocketClient extends EventEmitter { clearTimeout(this.activePongTimer!); delete this.activePongTimer; - this.logger.silly('Sending ping', { category: 'bybit-ws' }); + this.logger.silly('Sending ping', loggerCategory); this.ws.send(JSON.stringify({op: 'ping'})); this.activePongTimer = setTimeout(() => { - this.logger.info('Pong timeout', { category: 'bybit-ws' }); + this.logger.info('Pong timeout', loggerCategory); this.teardown(); // this.ws.terminate(); // TODO: does this work? @@ -288,14 +349,15 @@ export class WebsocketClient extends EventEmitter { private onWsOpen(event, wsRef?: string) { if (this.wsState === READY_STATE_CONNECTING) { - this.logger.info('Websocket connected', { category: 'bybit-ws', livenet: this.options.livenet, linear: this.options.linear }); + this.logger.info('Websocket connected', { ...loggerCategory, livenet: this.options.livenet, linear: this.options.linear }); this.emit('open'); } else if (this.wsState === READY_STATE_RECONNECTING) { - this.logger.info('Websocket reconnected', { category: 'bybit-ws', livenet: this.options.livenet }); + this.logger.info('Websocket reconnected', { ...loggerCategory, livenet: this.options.livenet }); this.emit('reconnected'); } this.wsState = READY_STATE_CONNECTED; + this.setWsState('main', READY_STATE_CONNECTED); this.requestSubscribeTopics([...this.subcribedTopics]); this.activePingTimer = setInterval(this.ping.bind(this), this.options.pingInterval); @@ -314,20 +376,22 @@ export class WebsocketClient extends EventEmitter { } private onWsError(err, wsRef?: string) { - this.logger.error('Websocket error', {category: 'bybit-ws', err}); + this.parseWsError('Websocket error', err, wsRef); if (this.wsState === READY_STATE_CONNECTED) { this.emit('error', err); } } private onWsClose(event, wsRef?: string) { - this.logger.info('Websocket connection closed', {category: 'bybit-ws'}); + this.logger.info('Websocket connection closed', loggerCategory); if (this.wsState !== READY_STATE_CLOSING) { this.reconnectWithDelay(this.options.reconnectTimeout!); this.emit('reconnect'); } else { this.wsState = READY_STATE_INITIAL; + this.setWsState('main', READY_STATE_INITIAL); + this.emit('close'); } } @@ -339,7 +403,7 @@ export class WebsocketClient extends EventEmitter { response.ret_msg === 'pong' && response.success === true ) { - this.logger.silly('pong recieved', {category: 'bybit-ws'}); + this.logger.silly('pong recieved', loggerCategory); clearTimeout(this.activePongTimer); } else { this.emit('response', response); From 9b62bae369b7b4a1f11abbf823f4e59af1583e42 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Mon, 1 Feb 2021 22:33:11 +0000 Subject: [PATCH 065/103] move ws state tracking to store --- src/logger.ts | 2 + src/util/WsStore.ts | 63 ++++++++++++++ src/websocket-client.ts | 182 +++++++++++++--------------------------- 3 files changed, 122 insertions(+), 125 deletions(-) create mode 100644 src/util/WsStore.ts diff --git a/src/logger.ts b/src/logger.ts index 0f5f29e..4c5e682 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,5 +1,7 @@ export type LogParams = null | any; +export type Logger = typeof DefaultLogger; + export const DefaultLogger = { silly: (...params: LogParams): void => { console.log(params); diff --git a/src/util/WsStore.ts b/src/util/WsStore.ts new file mode 100644 index 0000000..b6a9d8a --- /dev/null +++ b/src/util/WsStore.ts @@ -0,0 +1,63 @@ +import { DefaultLogger, Logger } from '../logger'; + +export enum WsConnectionState { + READY_STATE_INITIAL, + READY_STATE_CONNECTING, + READY_STATE_CONNECTED, + READY_STATE_CLOSING, + READY_STATE_RECONNECTING +}; + +export default class WsStore { + private connections: { + [key: string]: WebSocket + }; + private connectionState: { + [key: string]: WsConnectionState + } + private logger: Logger; + + constructor(logger: Logger) { + this.connections = {} + this.connectionState = {}; + this.logger = logger || DefaultLogger; + } + + getConnection(key: string) { + return this.connections[key]; + } + + setConnection(key: string, wsConnection: WebSocket) { + const existingConnection = this.getConnection(key); + if (existingConnection) { + this.logger.info('WsStore setConnection() overwriting existing connection: ', existingConnection); + } + this.connections[key] = wsConnection; + } + + clearConnection(key: string) { + const existingConnection = this.getConnection(key); + if (existingConnection) { + delete this.connections[key]; + } + } + + getConnectionState(key: string) { + return this.connectionState[key]; + } + + setConnectionState(key: string, state: WsConnectionState) { + this.connectionState[key] = state; + } + + isConnectionState(key: string, state: WsConnectionState) { + const a = this.getConnectionState(key) === state; + const b = this.getConnectionState(key) == state; + if (a != b) { + console.error('connection state doesnt match: ', { state, storedState: this.getConnectionState(key) }); + } else { + console.log('isConnectionState matches'); + } + return this.getConnectionState(key) === state; + } +} \ No newline at end of file diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 77c78bf..839d606 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -1,10 +1,11 @@ import { EventEmitter } from 'events'; import { InverseClient } from './inverse-client'; import { LinearClient } from './linear-client'; -import { DefaultLogger } from './logger'; +import { DefaultLogger, Logger } from './logger'; import { signMessage, serializeParams } from './util/requestUtils'; import WebSocket from 'isomorphic-ws'; +import WsStore, { WsConnectionState } from './util/WsStore'; const inverseEndpoints = { livenet: 'wss://stream.bybit.com/realtime', @@ -24,15 +25,7 @@ const READY_STATE_CONNECTED = 2; const READY_STATE_CLOSING = 3; const READY_STATE_RECONNECTING = 4; -enum WsConnectionState { - READY_STATE_INITIAL, - READY_STATE_CONNECTING, - READY_STATE_CONNECTED, - READY_STATE_CLOSING, - READY_STATE_RECONNECTING -}; - -export interface WebsocketClientConfigurableOptions { +export interface WSClientConfigurableOptions { key?: string; secret?: string; livenet?: boolean; @@ -45,7 +38,7 @@ export interface WebsocketClientConfigurableOptions { wsUrl?: string; }; -export interface WebsocketClientOptions extends WebsocketClientConfigurableOptions { +export interface WebsocketClientOptions extends WSClientConfigurableOptions { livenet: boolean; linear: boolean; pongTimeout: number; @@ -53,66 +46,13 @@ export interface WebsocketClientOptions extends WebsocketClientConfigurableOptio reconnectTimeout: number; }; -type Logger = typeof DefaultLogger; - -class WsStore { - private connections: { - [key: string]: WebSocket - }; - private connectionState: { - [key: string]: WsConnectionState - } - private logger: Logger; - - constructor(logger: Logger) { - this.connections = {} - this.connectionState = {}; - this.logger = logger || DefaultLogger; - } - - getConnection(key: string) { - return this.connections[key]; - } - - setConnection(key: string, wsConnection: WebSocket) { - const existingConnection = this.getConnection(key); - if (existingConnection) { - this.logger.info('WsStore setConnection() overwriting existing connection: ', existingConnection); - } - this.connections[key] = wsConnection; - } - - clearConnection(key: string) { - const existingConnection = this.getConnection(key); - if (existingConnection) { - delete this.connections[key]; - } - } - - getConnectionState(key: string) { - return this.connectionState[key]; - } - - setConnectionState(key: string, state: WsConnectionState) { - this.connectionState[key] = state; - } - - isConnectionState(key: string, state: WsConnectionState) { - const a = this.getConnectionState(key) === state; - const b = this.getConnectionState(key) == state; - if (a != b) { - console.error('connection state doesnt match: ', { state, storedState: this.getConnectionState(key) }); - } - return this.getConnectionState(key) === state; - } -} +const mainWsKey = 'main'; export class WebsocketClient extends EventEmitter { - private activePingTimer?: number | undefined; - private activePongTimer?: number | undefined; + private activePingTimer?: NodeJS.Timeout | undefined; + private activePongTimer?: NodeJS.Timeout | undefined; private logger: Logger; - private wsState: WsConnectionState; private client: InverseClient | LinearClient; private subcribedTopics: Set; private options: WebsocketClientOptions; @@ -120,7 +60,7 @@ export class WebsocketClient extends EventEmitter { private ws: WebSocket; private wsStore: WsStore; - constructor(options: WebsocketClientConfigurableOptions, logger?: Logger) { + constructor(options: WSClientConfigurableOptions, logger?: Logger) { super(); this.logger = logger || DefaultLogger; @@ -138,27 +78,17 @@ export class WebsocketClient extends EventEmitter { ...options }; - this.wsState = READY_STATE_INITIAL; - this.setWsState('main', READY_STATE_INITIAL); - if (this.options.linear === true) { this.client = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); }else{ this.client = new InverseClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); } - this.connect('main'); + this.setWsState(mainWsKey, READY_STATE_INITIAL); + this.connect(mainWsKey); } - getWsState(wsRefKey: string) { - return this.wsStore.getConnectionState(wsRefKey); - } - - setWsState(wsRefKey: string, state: WsConnectionState) { - this.wsStore.setConnectionState(wsRefKey, state); - } - - isLivenet(): boolean { + public isLivenet(): boolean { return this.options.livenet === true; } @@ -170,7 +100,7 @@ export class WebsocketClient extends EventEmitter { topics.forEach(topic => this.subcribedTopics.add(topic)); // subscribe not necessary if not yet connected (will automatically subscribe onOpen) - if (this.wsState === READY_STATE_CONNECTED) { + if (this.wsStore.isConnectionState(mainWsKey, READY_STATE_CONNECTED)) { this.requestSubscribeTopics(topics); } } @@ -183,20 +113,23 @@ export class WebsocketClient extends EventEmitter { topics.forEach(topic => this.subcribedTopics.delete(topic)); // unsubscribe not necessary if not yet connected - if (this.wsState === READY_STATE_CONNECTED) { + if (this.wsStore.isConnectionState(mainWsKey, READY_STATE_CONNECTED)) { this.requestUnsubscribeTopics(topics); } } close() { this.logger.info('Closing connection', loggerCategory); - this.wsState = READY_STATE_CLOSING; - this.setWsState('main', READY_STATE_CLOSING); - this.teardown(); + this.setWsState(mainWsKey, READY_STATE_CLOSING); + this.clearTimers(); this.ws && this.ws.close(); } - private getWsUrl() { + private setWsState(wsRefKey: string, state: WsConnectionState) { + this.wsStore.setConnectionState(wsRefKey, state); + } + + private getWsUrl(): string { if (this.options.wsUrl) { return this.options.wsUrl; } @@ -206,10 +139,9 @@ export class WebsocketClient extends EventEmitter { return inverseEndpoints[this.options.livenet ? 'livenet' : 'testnet']; } - private async connect(wsRefKey: string = 'main') { + private async connect(wsRefKey: string = mainWsKey): Promise { try { - if (this.wsState === READY_STATE_INITIAL) { - this.wsState = READY_STATE_CONNECTING; + if (this.wsStore.isConnectionState(mainWsKey, READY_STATE_INITIAL)) { this.setWsState(wsRefKey, READY_STATE_CONNECTING); } @@ -226,7 +158,7 @@ export class WebsocketClient extends EventEmitter { private parseWsError(context: string, error, wsRef?: string) { if (!error.message) { - this.logger.error(context + ': due to unexpected error: ', error); + this.logger.error(`${context} due to unexpected error: `, error); return; } @@ -270,46 +202,49 @@ export class WebsocketClient extends EventEmitter { } private reconnectWithDelay(connectionDelay: number) { - this.teardown(); - if (this.wsState !== READY_STATE_CONNECTING) { - this.wsState = READY_STATE_RECONNECTING; - this.setWsState('main', READY_STATE_RECONNECTING); + this.clearTimers(); + if (this.wsStore.getConnectionState(mainWsKey) !== READY_STATE_CONNECTING) { + this.setWsState(mainWsKey, READY_STATE_RECONNECTING); } setTimeout(() => { this.logger.info('Reconnecting to server', loggerCategory); - this.connect(); }, connectionDelay); } private ping() { - clearTimeout(this.activePongTimer!); - delete this.activePongTimer; + this.clearPongTimer(); this.logger.silly('Sending ping', loggerCategory); - this.ws.send(JSON.stringify({op: 'ping'})); + this.ws.send(JSON.stringify({ op: 'ping' })); - - this.activePongTimer = setTimeout(() => { + this.activePongTimer = setTimeout(() => { this.logger.info('Pong timeout', loggerCategory); - this.teardown(); - // this.ws.terminate(); - // TODO: does this work? + this.clearTimers(); this.ws.close(); }, this.options.pongTimeout); } - private teardown() { + private clearTimers() { + this.clearPingTimer() + this.clearPongTimer(); + } + + // Send a ping at intervals + private clearPingTimer() { if (this.activePingTimer) { clearInterval(this.activePingTimer); + this.activePingTimer = undefined; } + } + + // Expect a pong within a time limit + private clearPongTimer() { if (this.activePongTimer) { clearTimeout(this.activePongTimer); + this.activePongTimer = undefined; } - - this.activePongTimer = undefined; - this.activePingTimer = undefined; } /** @@ -347,20 +282,19 @@ export class WebsocketClient extends EventEmitter { return ws; } - private onWsOpen(event, wsRef?: string) { - if (this.wsState === READY_STATE_CONNECTING) { + private onWsOpen(event, wsRef: string = mainWsKey) { + if (this.wsStore.isConnectionState(wsRef, READY_STATE_CONNECTING)) { this.logger.info('Websocket connected', { ...loggerCategory, livenet: this.options.livenet, linear: this.options.linear }); this.emit('open'); - } else if (this.wsState === READY_STATE_RECONNECTING) { - this.logger.info('Websocket reconnected', { ...loggerCategory, livenet: this.options.livenet }); + } else if (this.wsStore.isConnectionState(wsRef, READY_STATE_RECONNECTING)) { + this.logger.info('Websocket reconnected', { ...loggerCategory }); this.emit('reconnected'); } - this.wsState = READY_STATE_CONNECTED; - this.setWsState('main', READY_STATE_CONNECTED); + this.setWsState(mainWsKey, READY_STATE_CONNECTED); this.requestSubscribeTopics([...this.subcribedTopics]); - this.activePingTimer = setInterval(this.ping.bind(this), this.options.pingInterval); + this.activePingTimer = setInterval(this.ping.bind(this), this.options.pingInterval); } private onWsMessage(event, wsRef?: string) { @@ -375,28 +309,26 @@ export class WebsocketClient extends EventEmitter { } } - private onWsError(err, wsRef?: string) { + private onWsError(err, wsRef: string = mainWsKey) { this.parseWsError('Websocket error', err, wsRef); - if (this.wsState === READY_STATE_CONNECTED) { + if (this.wsStore.isConnectionState(wsRef, READY_STATE_CONNECTED)) { this.emit('error', err); } } - private onWsClose(event, wsRef?: string) { + private onWsClose(event, wsRef: string = mainWsKey) { this.logger.info('Websocket connection closed', loggerCategory); - if (this.wsState !== READY_STATE_CLOSING) { + if (this.wsStore.getConnectionState(wsRef) !== READY_STATE_CLOSING) { this.reconnectWithDelay(this.options.reconnectTimeout!); this.emit('reconnect'); } else { - this.wsState = READY_STATE_INITIAL; - this.setWsState('main', READY_STATE_INITIAL); - + this.setWsState(wsRef, READY_STATE_INITIAL); this.emit('close'); } } - private onWsMessageResponse(response) { + private onWsMessageResponse(response: any) { if ( response.request && response.request.op === 'ping' && @@ -404,13 +336,13 @@ export class WebsocketClient extends EventEmitter { response.success === true ) { this.logger.silly('pong recieved', loggerCategory); - clearTimeout(this.activePongTimer); + this.clearPongTimer(); } else { this.emit('response', response); } } - private onWsMessageUpdate(message) { + private onWsMessageUpdate(message: any) { this.emit('update', message); } }; From 0038daf531a84e2c5ac8aeb0acf9ce1257e4651c Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Mon, 1 Feb 2021 23:25:39 +0000 Subject: [PATCH 066/103] move websocket to ws store --- src/util/WsStore.ts | 36 ++++++++-------------- src/websocket-client.ts | 67 +++++++++++++++++++++++++---------------- 2 files changed, 53 insertions(+), 50 deletions(-) diff --git a/src/util/WsStore.ts b/src/util/WsStore.ts index b6a9d8a..2f143b4 100644 --- a/src/util/WsStore.ts +++ b/src/util/WsStore.ts @@ -1,13 +1,6 @@ +import { WsConnectionState } from '../websocket-client'; import { DefaultLogger, Logger } from '../logger'; -export enum WsConnectionState { - READY_STATE_INITIAL, - READY_STATE_CONNECTING, - READY_STATE_CONNECTED, - READY_STATE_CLOSING, - READY_STATE_RECONNECTING -}; - export default class WsStore { private connections: { [key: string]: WebSocket @@ -23,26 +16,28 @@ export default class WsStore { this.logger = logger || DefaultLogger; } - getConnection(key: string) { + getWs(key: string): WebSocket | undefined { return this.connections[key]; } - setConnection(key: string, wsConnection: WebSocket) { - const existingConnection = this.getConnection(key); - if (existingConnection) { - this.logger.info('WsStore setConnection() overwriting existing connection: ', existingConnection); + setWs(key: string, wsConnection: WebSocket): WebSocket { + const existingConnection = this.getWs(key); + if (existingConnection && existingConnection.readyState === existingConnection.OPEN) { + this.logger.warning('WsStore setConnection() overwriting existing open connection: ', existingConnection); } this.connections[key] = wsConnection; + return wsConnection; } - clearConnection(key: string) { - const existingConnection = this.getConnection(key); + clearWs(key: string) { + const existingConnection = this.getWs(key); if (existingConnection) { + existingConnection.close(); delete this.connections[key]; } } - getConnectionState(key: string) { + getConnectionState(key: string): WsConnectionState { return this.connectionState[key]; } @@ -50,14 +45,7 @@ export default class WsStore { this.connectionState[key] = state; } - isConnectionState(key: string, state: WsConnectionState) { - const a = this.getConnectionState(key) === state; - const b = this.getConnectionState(key) == state; - if (a != b) { - console.error('connection state doesnt match: ', { state, storedState: this.getConnectionState(key) }); - } else { - console.log('isConnectionState matches'); - } + isConnectionState(key: string, state: WsConnectionState): boolean { return this.getConnectionState(key) === state; } } \ No newline at end of file diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 839d606..211f520 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -5,7 +5,7 @@ import { DefaultLogger, Logger } from './logger'; import { signMessage, serializeParams } from './util/requestUtils'; import WebSocket from 'isomorphic-ws'; -import WsStore, { WsConnectionState } from './util/WsStore'; +import WsStore from './util/WsStore'; const inverseEndpoints = { livenet: 'wss://stream.bybit.com/realtime', @@ -25,6 +25,23 @@ const READY_STATE_CONNECTED = 2; const READY_STATE_CLOSING = 3; const READY_STATE_RECONNECTING = 4; +export enum WsConnectionState { + READY_STATE_INITIAL, + READY_STATE_CONNECTING, + READY_STATE_CONNECTED, + READY_STATE_CLOSING, + READY_STATE_RECONNECTING +}; + +const isWsPong = (response: any) => { + return ( + response.request && + response.request.op === 'ping' && + response.ret_msg === 'pong' && + response.success === true + ); +} + export interface WSClientConfigurableOptions { key?: string; secret?: string; @@ -57,7 +74,6 @@ export class WebsocketClient extends EventEmitter { private subcribedTopics: Set; private options: WebsocketClientOptions; - private ws: WebSocket; private wsStore: WsStore; constructor(options: WSClientConfigurableOptions, logger?: Logger) { @@ -118,15 +134,12 @@ export class WebsocketClient extends EventEmitter { } } - close() { + public close(wsRefKey: string = mainWsKey) { this.logger.info('Closing connection', loggerCategory); - this.setWsState(mainWsKey, READY_STATE_CLOSING); + this.setWsState(wsRefKey, READY_STATE_CLOSING); this.clearTimers(); - this.ws && this.ws.close(); - } - private setWsState(wsRefKey: string, state: WsConnectionState) { - this.wsStore.setConnectionState(wsRefKey, state); + this.getWs(wsRefKey)?.close(); } private getWsUrl(): string { @@ -147,9 +160,9 @@ export class WebsocketClient extends EventEmitter { const authParams = await this.getAuthParams(); const url = this.getWsUrl() + authParams; + const ws = this.connectToWsUrl(url, wsRefKey); - this.ws = this.connectToWsUrl(url, wsRefKey); - return this.ws; + return this.wsStore.setWs(wsRefKey, ws); } catch (err) { this.parseWsError('Connection failed', err); this.reconnectWithDelay(this.options.reconnectTimeout!); @@ -201,7 +214,7 @@ export class WebsocketClient extends EventEmitter { return ''; } - private reconnectWithDelay(connectionDelay: number) { + private reconnectWithDelay(connectionDelayMs: number) { this.clearTimers(); if (this.wsStore.getConnectionState(mainWsKey) !== READY_STATE_CONNECTING) { this.setWsState(mainWsKey, READY_STATE_RECONNECTING); @@ -210,19 +223,18 @@ export class WebsocketClient extends EventEmitter { setTimeout(() => { this.logger.info('Reconnecting to server', loggerCategory); this.connect(); - }, connectionDelay); + }, connectionDelayMs); } private ping() { this.clearPongTimer(); this.logger.silly('Sending ping', loggerCategory); - this.ws.send(JSON.stringify({ op: 'ping' })); + this.getWs(mainWsKey)?.send(JSON.stringify({ op: 'ping' })); this.activePongTimer = setTimeout(() => { - this.logger.info('Pong timeout', loggerCategory); - this.clearTimers(); - this.ws.close(); + this.logger.info('Pong timeout - closing socket to reconnect', loggerCategory); + this.getWs(mainWsKey)?.close(); }, this.options.pongTimeout); } @@ -256,7 +268,7 @@ export class WebsocketClient extends EventEmitter { args: topics }); - this.ws.send(wsMessage); + this.getWs(mainWsKey)?.send(wsMessage); } /** @@ -268,7 +280,7 @@ export class WebsocketClient extends EventEmitter { args: topics }); - this.ws.send(wsMessage); + this.getWs(mainWsKey)?.send(wsMessage); } private connectToWsUrl(url: string, wsKey: string): WebSocket { @@ -329,14 +341,9 @@ export class WebsocketClient extends EventEmitter { } private onWsMessageResponse(response: any) { - if ( - response.request && - response.request.op === 'ping' && - response.ret_msg === 'pong' && - response.success === true - ) { - this.logger.silly('pong recieved', loggerCategory); - this.clearPongTimer(); + if (isWsPong(response)) { + this.logger.silly('pong recieved', loggerCategory); + // this.clearPongTimer(); } else { this.emit('response', response); } @@ -345,4 +352,12 @@ export class WebsocketClient extends EventEmitter { private onWsMessageUpdate(message: any) { this.emit('update', message); } + + private getWs(wsRefKey: string): WebSocket | undefined { + return this.wsStore.getWs(wsRefKey); + } + + private setWsState(wsRefKey: string, state: WsConnectionState) { + this.wsStore.setConnectionState(wsRefKey, state); + } }; From 569b0651842c5d46915c1483173f064be31a3271 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Tue, 2 Feb 2021 00:31:57 +0000 Subject: [PATCH 067/103] Cryotojs Bugfix for vuejs in browser environments --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 47ac72c..11c61f4 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,9 @@ "webpack-bundle-analyzer": "^4.1.0", "webpack-cli": "^4.2.0" }, + "browser": { + "crypto": false + }, "keywords": [ "bybit", "api", From 8afaca5668469c5e9e89a09f61e6b9be9cfc036e Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Tue, 2 Feb 2021 10:32:28 +0000 Subject: [PATCH 068/103] fix --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 11c61f4..47ac72c 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,6 @@ "webpack-bundle-analyzer": "^4.1.0", "webpack-cli": "^4.2.0" }, - "browser": { - "crypto": false - }, "keywords": [ "bybit", "api", From c233dfc79027e76e67329242f4ce0be78e26b74a Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Tue, 2 Feb 2021 11:38:14 +0000 Subject: [PATCH 069/103] Indenting and Comments --- src/linear-client.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index ede59b3..0674f82 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -34,7 +34,11 @@ export class LinearClient extends SharedEndpoints { return this; } - //------------Market Data Endpoints------------> + /** + * + * Market Data Endpoints + * + */ getKline(params: { symbol: string; @@ -97,8 +101,12 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('public/linear/premium-index-kline', params); } - //------------Account Data Endpoints------------> - + /** + * + * Account Data Endpoints + * + */ + //Active Orders placeActiveOrder(params: { From e6f21e99568de91b3ee1f27325a894971d9b785e Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Tue, 2 Feb 2021 11:51:53 +0000 Subject: [PATCH 070/103] comments --- src/inverse-client.ts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index 16b8471..d0f661f 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -33,7 +33,11 @@ export class InverseClient extends SharedEndpoints { return this; } - //------------Market Data Endpoints------------> + /** + * + * Market Data Endpoints + * + */ getKline(params: { symbol: string; @@ -112,7 +116,13 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/public/premium-index-kline', params); } - //-----------Account Data Endpoints------------> + /** + * + * Account Data Endpoints + * + */ + + //Active Orders placeActiveOrder(orderRequest: { side: string; @@ -171,6 +181,8 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/order', params); } + + //Conditional Orders placeConditionalOrder(params: { side: string; @@ -230,6 +242,8 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/stop-order', params); } + + //Position /** * @deprecated use getPosition() instead @@ -305,6 +319,8 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/trade/closed-pnl/list', params); } + //Risk Limit + getRiskLimitList(): GenericAPIResponse { return this.requestWrapper.get('open-api/wallet/risk-limit/list'); } @@ -315,6 +331,8 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('open-api/wallet/risk-limit', params); } + + //Funding getLastFundingRate(params: { symbol: string; @@ -333,6 +351,8 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/funding/predicted-funding', params); } + + //misc getLcpInfo(params: { symbol: string; From 80c37cf3647641016f278b37ab05e2807a9ac180 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Tue, 2 Feb 2021 11:51:59 +0000 Subject: [PATCH 071/103] comments --- src/linear-client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 0674f82..e2b2261 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -77,12 +77,12 @@ export class LinearClient extends SharedEndpoints { getMarkPriceKline(params: { symbol: string; interval: string; - from: number; + from: number; limit?: number; }): GenericAPIResponse { return this.requestWrapper.get('public/linear/mark-price-kline', params); } - + getIndexPriceKline(params: { symbol: string; interval: string; From 3ceec80c17f8ee7bb46bd763078949a253a9d7c3 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Tue, 2 Feb 2021 11:52:13 +0000 Subject: [PATCH 072/103] comments --- src/shared-endpoints.ts | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index b77c4d3..dbb4bc9 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -5,7 +5,11 @@ export default class SharedEndpoints { // TODO: Is there a way to say that Base has to provide this? protected requestWrapper: RequestWrapper; - //------------Market Data Endpoints------------> + /** + * + * Market Data Endpoints + * + */ getOrderBook(params: { symbol: string; @@ -56,13 +60,21 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/public/account-ratio', params); } - //------------Account Data Endpoints------------> + /** + * + * Account Data Endpoints + * + */ getApiKeyInfo(): GenericAPIResponse { return this.requestWrapper.get('v2/private/account/api-key'); } - //------------Wallet Data Endpoints------------> + /** + * + * Wallet Data Endpoints + * + */ getWalletBalance(params: { coin?: string; @@ -101,7 +113,11 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); } - //-------------API Data Endpoints-------------> + /** + * + * API Data Endpoints + * + */ getServerTime(): GenericAPIResponse { return this.requestWrapper.get('v2/public/time'); @@ -118,4 +134,4 @@ export default class SharedEndpoints { return Math.ceil((result.time_now * 1000) - end + ((end - start) / 2)); }); } -} \ No newline at end of file +} From 3881ccf605a0ed9bf387069bd6d94374bf873913 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Tue, 2 Feb 2021 11:55:42 +0000 Subject: [PATCH 073/103] FINAL indent fix --- src/linear-client.ts | 582 +++++++++++++++++++++---------------------- 1 file changed, 291 insertions(+), 291 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index e2b2261..9296f81 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -4,9 +4,9 @@ import RequestWrapper from './util/requestWrapper'; import SharedEndpoints from './shared-endpoints'; export class LinearClient extends SharedEndpoints { - protected requestWrapper: RequestWrapper; + protected requestWrapper: RequestWrapper; - /** + /** * @public Creates an instance of the inverse REST API client. * * @param {string} key - your API key @@ -16,23 +16,23 @@ export class LinearClient extends SharedEndpoints { * @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios */ - constructor( - key?: string | undefined, - secret?: string | undefined, - livenet?: boolean, - restInverseOptions:RestClientInverseOptions = {}, // TODO: Rename this type to be more general. - requestOptions: AxiosRequestConfig = {} - ) { - super() - this.requestWrapper = new RequestWrapper( - key, - secret, - getBaseRESTInverseUrl(livenet), - restInverseOptions, - requestOptions - ); - return this; - } + constructor( + key?: string | undefined, + secret?: string | undefined, + livenet?: boolean, + restInverseOptions:RestClientInverseOptions = {}, // TODO: Rename this type to be more general. + requestOptions: AxiosRequestConfig = {} + ) { + super() + this.requestWrapper = new RequestWrapper( + key, + secret, + getBaseRESTInverseUrl(livenet), + restInverseOptions, + requestOptions + ); + return this; + } /** * @@ -40,66 +40,66 @@ export class LinearClient extends SharedEndpoints { * */ - getKline(params: { - symbol: string; - interval: string; - from: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('/public/linear/kline', params); - } + getKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('/public/linear/kline', params); + } - /** - * @deprecated use getTrades() instead - */ - getPublicTradingRecords(params: { - symbol: string; - from?: number; - limit?: number; - }): GenericAPIResponse { - return this.getTrades(params); - } + /** + * @deprecated use getTrades() instead + */ + getPublicTradingRecords(params: { + symbol: string; + from?: number; + limit?: number; + }): GenericAPIResponse { + return this.getTrades(params); + } - getTrades(params: { - symbol: string; - //from?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/recent-trading-records', params); - } + getTrades(params: { + symbol: string; + //from?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/recent-trading-records', params); + } - getLastFundingRate(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/funding/prev-funding-rate', params); - } + getLastFundingRate(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/funding/prev-funding-rate', params); + } - getMarkPriceKline(params: { - symbol: string; - interval: string; - from: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/mark-price-kline', params); - } - - getIndexPriceKline(params: { - symbol: string; - interval: string; - from: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/index-price-kline', params); - } + getMarkPriceKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/mark-price-kline', params); + } + + getIndexPriceKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/index-price-kline', params); + } - getPremiumIndexKline(params: { - symbol: string; - interval: string; - from: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/premium-index-kline', params); - } + getPremiumIndexKline(params: { + symbol: string; + interval: string; + from: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/premium-index-kline', params); + } /** * @@ -107,251 +107,251 @@ export class LinearClient extends SharedEndpoints { * */ - //Active Orders + //Active Orders - placeActiveOrder(params: { - side: string; - symbol: string; - order_type: string; - qty: number; - price?: number; - time_in_force: string; - take_profit?: number; - stop_loss?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - reduce_only?: boolean; - close_on_trigger?: boolean; - order_link_id?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/create', params); - } + placeActiveOrder(params: { + side: string; + symbol: string; + order_type: string; + qty: number; + price?: number; + time_in_force: string; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + reduce_only?: boolean; + close_on_trigger?: boolean; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/create', params); + } - getActiveOrderList(params: { - order_id?: string; - order_link_id?: string; - symbol: string; - order?: string; - page?: number; - limit?: number; - order_status?: string; + getActiveOrderList(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + order?: string; + page?: number; + limit?: number; + order_status?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/order/list', params); - } + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/order/list', params); + } - cancelActiveOrder(params: { - symbol: string; - order_id?: string; - order_link_id?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/cancel', params); - } + cancelActiveOrder(params: { + symbol: string; + order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/cancel', params); + } - cancelAllActiveOrders(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/cancel-all', params); - } + cancelAllActiveOrders(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/cancel-all', params); + } - replaceActiveOrder(params: { - order_id?: string; - order_link_id?: string; - symbol: string; - p_r_qty?: number; - p_r_price?: number; - take_profit?: number; - stop_loss?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/order/replace', params); - } + replaceActiveOrder(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + p_r_qty?: number; + p_r_price?: number; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/order/replace', params); + } - queryActiveOrder(params: { - order_id?: string; - order_link_id?: string; - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/order/search', params); - } + queryActiveOrder(params: { + order_id?: string; + order_link_id?: string; + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/order/search', params); + } - //Conditional Orders + //Conditional Orders - placeConditionalOrder(params: { - side: string; - symbol: string; - order_type: string; - qty: number; - price?: number; - base_price: number; - stop_px: number; - time_in_force: string; - trigger_by?: string; - close_on_trigger?: boolean; - order_link_id?: string; - reduce_only: boolean; - take_profit?: number; - stop_loss?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/stop-order/create', params); - } + placeConditionalOrder(params: { + side: string; + symbol: string; + order_type: string; + qty: number; + price?: number; + base_price: number; + stop_px: number; + time_in_force: string; + trigger_by?: string; + close_on_trigger?: boolean; + order_link_id?: string; + reduce_only: boolean; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/create', params); + } - getConditionalOrder(params: { - stop_order_id?: string; - order_link_id?: string; - symbol: string; - stop_order_status?: string; - order?: string; - page?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/stop-order/list', params); - } + getConditionalOrder(params: { + stop_order_id?: string; + order_link_id?: string; + symbol: string; + stop_order_status?: string; + order?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/stop-order/list', params); + } - cancelConditionalOrder(params: { - symbol: string; - stop_order_id?: string; - order_link_id?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/stop-order/cancel', params); - } + cancelConditionalOrder(params: { + symbol: string; + stop_order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/cancel', params); + } - cancelAllConditionalOrders(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/stop-order/cancel-all', params); - } + cancelAllConditionalOrders(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/cancel-all', params); + } - replaceConditionalOrder(params: { - stop_order_id?: string; - order_link_id?: string; - symbol: string; - p_r_qty?: number; - p_r_price?: number; - p_r_trigger_price?: number; - take_profit?: number; - stop_loss?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/stop-order/replace', params); - } + replaceConditionalOrder(params: { + stop_order_id?: string; + order_link_id?: string; + symbol: string; + p_r_qty?: number; + p_r_price?: number; + p_r_trigger_price?: number; + take_profit?: number; + stop_loss?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/stop-order/replace', params); + } - queryConditionalOrder(params: { - symbol: string; - stop_order_id?: string; - order_link_id?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/stop-order/search', params); - } + queryConditionalOrder(params: { + symbol: string; + stop_order_id?: string; + order_link_id?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/stop-order/search', params); + } - //Position + //Position - getPosition(params?: { - symbol?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/position/list', params); - } + getPosition(params?: { + symbol?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/position/list', params); + } - setAutoAddMargin(params?: { - symbol: string; - side: string; - auto_add_margin: boolean; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/set-auto-add-margin', params); - } + setAutoAddMargin(params?: { + symbol: string; + side: string; + auto_add_margin: boolean; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/set-auto-add-margin', params); + } - setMarginSwitch(params?: { - symbol: string; - is_isolated: boolean; - buy_leverage: number; - sell_leverage: number; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/switch-isolated', params); - } + setMarginSwitch(params?: { + symbol: string; + is_isolated: boolean; + buy_leverage: number; + sell_leverage: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/switch-isolated', params); + } - setSwitchMode(params?: { - symbol: string; - tp_sl_mode: string; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/tpsl/switch-mode', params); - } + setSwitchMode(params?: { + symbol: string; + tp_sl_mode: string; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/tpsl/switch-mode', params); + } - setAddReduceMargin(params?: { - symbol: string; - side: string; - margin: number; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/add-margin', params); - } + setAddReduceMargin(params?: { + symbol: string; + side: string; + margin: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/add-margin', params); + } - setUserLeverage(params: { - symbol: string; - buy_leverage: number; - sell_leverage: number; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/set-leverage', params); - } + setUserLeverage(params: { + symbol: string; + buy_leverage: number; + sell_leverage: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/set-leverage', params); + } - setTradingStop(params: { - symbol: string; - side: string; - take_profit?: number; - stop_loss?: number; - trailing_stop?: number; - tp_trigger_by?: string; - sl_trigger_by?: string; - sl_size?: number; - tp_size?: number; - }): GenericAPIResponse { - return this.requestWrapper.post('private/linear/position/trading-stop', params); - } + setTradingStop(params: { + symbol: string; + side: string; + take_profit?: number; + stop_loss?: number; + trailing_stop?: number; + tp_trigger_by?: string; + sl_trigger_by?: string; + sl_size?: number; + tp_size?: number; + }): GenericAPIResponse { + return this.requestWrapper.post('private/linear/position/trading-stop', params); + } - getTradeRecords(params: { - symbol: string; - start_time?: number; - end_time?: number; - exec_type?: string; - page?: number; - limit?: number; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/trade/execution/list', params); - } + getTradeRecords(params: { + symbol: string; + start_time?: number; + end_time?: number; + exec_type?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/trade/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('private/linear/tpsl/switch-mode', params); - } + getClosedPnl(params: { + symbol: string; + start_time?: number; + end_time?: number; + exec_type?: string; + page?: number; + limit?: number; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/tpsl/switch-mode', params); + } - //Risk Limit + //Risk Limit - getRiskLimitList(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/risk-limit'); - } + getRiskLimitList(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('public/linear/risk-limit'); + } - //Funding + //Funding - getPredictedFundingFee(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/funding/predicted-funding'); - } + getPredictedFundingFee(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/funding/predicted-funding'); + } - getLastFundingFee(params: { - symbol: string; - }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/funding/prev-funding'); - } + getLastFundingFee(params: { + symbol: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/funding/prev-funding'); + } } From e8bc931bf17328fe2ccdb8a7be53cbcf2457d981 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Wed, 3 Feb 2021 19:53:53 +0000 Subject: [PATCH 074/103] Comment Updates --- src/inverse-client.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index d0f661f..a22e327 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -122,7 +122,9 @@ export class InverseClient extends SharedEndpoints { * */ - //Active Orders + /** + * Active orders + */ placeActiveOrder(orderRequest: { side: string; @@ -182,7 +184,9 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/order', params); } - //Conditional Orders + /** + * Conditional orders + */ placeConditionalOrder(params: { side: string; @@ -243,7 +247,9 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/stop-order', params); } - //Position + /** + * Position + */ /** * @deprecated use getPosition() instead @@ -319,7 +325,9 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/trade/closed-pnl/list', params); } - //Risk Limit + /** + * Risk Limit + */ getRiskLimitList(): GenericAPIResponse { return this.requestWrapper.get('open-api/wallet/risk-limit/list'); @@ -332,7 +340,9 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.post('open-api/wallet/risk-limit', params); } - //Funding + /** + * Funding + */ getLastFundingRate(params: { symbol: string; @@ -352,7 +362,9 @@ export class InverseClient extends SharedEndpoints { return this.requestWrapper.get('v2/private/funding/predicted-funding', params); } - //misc + /** + * LCP Info + */ getLcpInfo(params: { symbol: string; From 5a968f79a92eee7a9f617791044221a44cec6a5d Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Wed, 3 Feb 2021 19:54:01 +0000 Subject: [PATCH 075/103] Comment Updates --- src/shared-endpoints.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index dbb4bc9..fe7daed 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -113,7 +113,7 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); } - /** + /** * * API Data Endpoints * @@ -127,7 +127,7 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/public/announcement'); } - async getTimeOffset(): Promise < number > { + async getTimeOffset(): Promise { const start = Date.now(); return this.getServerTime().then(result => { const end = Date.now(); From e0839d0c8db5001b5f75b0fe51fe9620af7b4bd3 Mon Sep 17 00:00:00 2001 From: peepopoggers <72892531+peepopoggers@users.noreply.github.com> Date: Wed, 3 Feb 2021 19:54:52 +0000 Subject: [PATCH 076/103] Comment Updates --- src/linear-client.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 9296f81..e62f9e0 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -106,8 +106,10 @@ export class LinearClient extends SharedEndpoints { * Account Data Endpoints * */ - - //Active Orders + + /** + * Active orders + */ placeActiveOrder(params: { side: string; @@ -176,7 +178,9 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('private/linear/order/search', params); } - //Conditional Orders + /** + * Conditional orders + */ placeConditionalOrder(params: { side: string; @@ -248,7 +252,9 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('private/linear/stop-order/search', params); } - //Position + /** + * Position + */ getPosition(params?: { symbol?: string; @@ -332,7 +338,9 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('private/linear/tpsl/switch-mode', params); } - //Risk Limit + /** + * Risk Limit + */ getRiskLimitList(params: { symbol: string; @@ -340,7 +348,9 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('public/linear/risk-limit'); } - //Funding + /** + * Funding + */ getPredictedFundingFee(params: { symbol: string; From d81381d7ec3104f97639f6f9cb82584b62f58503 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 6 Feb 2021 16:47:38 +0000 Subject: [PATCH 077/103] move wsstate to store --- src/util/WsStore.ts | 92 ++++++++++++++++++++++------ src/websocket-client.ts | 130 +++++++++++++++++++++++++++------------- 2 files changed, 160 insertions(+), 62 deletions(-) diff --git a/src/util/WsStore.ts b/src/util/WsStore.ts index 2f143b4..3e56930 100644 --- a/src/util/WsStore.ts +++ b/src/util/WsStore.ts @@ -1,51 +1,105 @@ import { WsConnectionState } from '../websocket-client'; import { DefaultLogger, Logger } from '../logger'; +interface WsStoredState { + ws?: WebSocket; + connectionState?: WsConnectionState; + activePingTimer?: NodeJS.Timeout | undefined; + activePongTimer?: NodeJS.Timeout | undefined; + subscribedTopics: Set; +}; + export default class WsStore { - private connections: { - [key: string]: WebSocket - }; - private connectionState: { - [key: string]: WsConnectionState + private wsState: { + [key: string]: WsStoredState; } private logger: Logger; constructor(logger: Logger) { - this.connections = {} - this.connectionState = {}; this.logger = logger || DefaultLogger; + this.wsState = {}; + } + + get(key: string, createIfMissing?: boolean): WsStoredState | undefined { + if (this.wsState[key]) { + return this.wsState[key]; + } + + if (createIfMissing) { + return this.create(key); + } + + return undefined; + } + + create(key: string): WsStoredState | undefined { + if (this.hasExistingActiveConnection(key)) { + this.logger.warning('WsStore setConnection() overwriting existing open connection: ', this.getWs(key)); + } + this.wsState[key] = { + subscribedTopics: new Set(), + connectionState: WsConnectionState.READY_STATE_INITIAL + }; + return this.get(key); + } + + delete(key: string) { + if (this.hasExistingActiveConnection(key)) { + const ws = this.getWs(key); + this.logger.warning('WsStore deleting state for connection still open: ', ws); + ws?.close(); + } + delete this.wsState[key]; + } + + /* connection websocket */ + + hasExistingActiveConnection(key) { + return this.get(key) && this.isWsOpen(key); } getWs(key: string): WebSocket | undefined { - return this.connections[key]; + return this.get(key)?.ws; } setWs(key: string, wsConnection: WebSocket): WebSocket { - const existingConnection = this.getWs(key); - if (existingConnection && existingConnection.readyState === existingConnection.OPEN) { - this.logger.warning('WsStore setConnection() overwriting existing open connection: ', existingConnection); + if (this.isWsOpen(key)) { + this.logger.warning('WsStore setConnection() overwriting existing open connection: ', this.getWs(key)); } - this.connections[key] = wsConnection; + this.get(key, true)!.ws = wsConnection; return wsConnection; } - clearWs(key: string) { + /* connection state */ + + isWsOpen(key: string): boolean { const existingConnection = this.getWs(key); - if (existingConnection) { - existingConnection.close(); - delete this.connections[key]; - } + return !!existingConnection && existingConnection.readyState === existingConnection.OPEN; } getConnectionState(key: string): WsConnectionState { - return this.connectionState[key]; + return this.get(key, true)!.connectionState!; } setConnectionState(key: string, state: WsConnectionState) { - this.connectionState[key] = state; + this.get(key, true)!.connectionState = state; } isConnectionState(key: string, state: WsConnectionState): boolean { return this.getConnectionState(key) === state; } + + /* subscribed topics */ + + getTopics(key: string): Set { + return this.get(key, true)!.subscribedTopics; + } + + addTopic(key: string, topic: string) { + return this.getTopics(key).add(topic); + } + + deleteTopic(key: string, topic: string) { + return this.getTopics(key).delete(topic); + } } \ No newline at end of file diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 211f520..2585291 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -63,7 +63,17 @@ export interface WebsocketClientOptions extends WSClientConfigurableOptions { reconnectTimeout: number; }; -const mainWsKey = 'main'; +const defaultWsKey = 'inverse'; + +const getLinearWsKeyForTopic = (topic: string) => { + switch (topic) { + case '': + return 'public'; + + default: + return 'private' + } +} export class WebsocketClient extends EventEmitter { private activePingTimer?: NodeJS.Timeout | undefined; @@ -71,7 +81,6 @@ export class WebsocketClient extends EventEmitter { private logger: Logger; private client: InverseClient | LinearClient; - private subcribedTopics: Set; private options: WebsocketClientOptions; private wsStore: WsStore; @@ -80,7 +89,7 @@ export class WebsocketClient extends EventEmitter { super(); this.logger = logger || DefaultLogger; - this.subcribedTopics = new Set(); + // this.subcribedTopics = new Set(); this.wsStore = new WsStore(this.logger); this.activePingTimer = undefined; this.activePongTimer = undefined; @@ -96,50 +105,64 @@ export class WebsocketClient extends EventEmitter { if (this.options.linear === true) { this.client = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); - }else{ + } else { this.client = new InverseClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); } - this.setWsState(mainWsKey, READY_STATE_INITIAL); - this.connect(mainWsKey); + this.setWsState(defaultWsKey, READY_STATE_INITIAL); + this.connect(defaultWsKey); } public isLivenet(): boolean { return this.options.livenet === true; } + public isInverse(): boolean { + return !this.options.linear; + } + /** * Add topic/topics to WS subscription list */ public subscribe(wsTopics: string[] | string) { const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; - topics.forEach(topic => this.subcribedTopics.add(topic)); + topics.forEach(topic => this.wsStore.addTopic( + this.getWsKeyForTopic(topic), + topic + )); // subscribe not necessary if not yet connected (will automatically subscribe onOpen) - if (this.wsStore.isConnectionState(mainWsKey, READY_STATE_CONNECTED)) { + if (this.wsStore.isConnectionState(defaultWsKey, READY_STATE_CONNECTED)) { this.requestSubscribeTopics(topics); } } + private getWsKeyForTopic(topic: string) { + return this.isInverse() ? defaultWsKey : getLinearWsKeyForTopic(topic); + } + /** * Remove topic/topics from WS subscription list */ public unsubscribe(wsTopics: string[] | string) { const topics = Array.isArray(wsTopics) ? wsTopics : [wsTopics]; - topics.forEach(topic => this.subcribedTopics.delete(topic)); + topics.forEach(topic => this.wsStore.deleteTopic( + this.getWsKeyForTopic(topic), + topic + )); // unsubscribe not necessary if not yet connected - if (this.wsStore.isConnectionState(mainWsKey, READY_STATE_CONNECTED)) { + if (this.wsStore.isConnectionState(defaultWsKey, READY_STATE_CONNECTED)) { this.requestUnsubscribeTopics(topics); } } - public close(wsRefKey: string = mainWsKey) { + public close(wsKey: string = defaultWsKey) { this.logger.info('Closing connection', loggerCategory); - this.setWsState(wsRefKey, READY_STATE_CLOSING); + this.setWsState(wsKey, READY_STATE_CLOSING); this.clearTimers(); - this.getWs(wsRefKey)?.close(); + this.getWs(wsKey)?.close(); } private getWsUrl(): string { @@ -152,24 +175,37 @@ export class WebsocketClient extends EventEmitter { return inverseEndpoints[this.options.livenet ? 'livenet' : 'testnet']; } - private async connect(wsRefKey: string = mainWsKey): Promise { + private async connect(wsKey: string = defaultWsKey): Promise { try { - if (this.wsStore.isConnectionState(mainWsKey, READY_STATE_INITIAL)) { - this.setWsState(wsRefKey, READY_STATE_CONNECTING); + if (this.wsStore.isWsOpen(wsKey)) { + this.logger.error('Refused to connect to ws with existing active connection', { ...loggerCategory, wsKey }) + return this.wsStore.getWs(wsKey); + } + + if (this.wsStore.isConnectionState(defaultWsKey, READY_STATE_CONNECTING)) { + this.logger.error('Refused to connect to ws, connection attempt already active', { ...loggerCategory, wsKey }) + return; + } + + if ( + !this.wsStore.getConnectionState(defaultWsKey) || + this.wsStore.isConnectionState(defaultWsKey, READY_STATE_INITIAL) + ) { + this.setWsState(wsKey, READY_STATE_CONNECTING); } const authParams = await this.getAuthParams(); const url = this.getWsUrl() + authParams; - const ws = this.connectToWsUrl(url, wsRefKey); + const ws = this.connectToWsUrl(url, wsKey); - return this.wsStore.setWs(wsRefKey, ws); + return this.wsStore.setWs(wsKey, ws); } catch (err) { this.parseWsError('Connection failed', err); this.reconnectWithDelay(this.options.reconnectTimeout!); } } - private parseWsError(context: string, error, wsRef?: string) { + private parseWsError(context: string, error, wsKey?: string) { if (!error.message) { this.logger.error(`${context} due to unexpected error: `, error); return; @@ -216,8 +252,8 @@ export class WebsocketClient extends EventEmitter { private reconnectWithDelay(connectionDelayMs: number) { this.clearTimers(); - if (this.wsStore.getConnectionState(mainWsKey) !== READY_STATE_CONNECTING) { - this.setWsState(mainWsKey, READY_STATE_RECONNECTING); + if (this.wsStore.getConnectionState(defaultWsKey) !== READY_STATE_CONNECTING) { + this.setWsState(defaultWsKey, READY_STATE_RECONNECTING); } setTimeout(() => { @@ -230,11 +266,11 @@ export class WebsocketClient extends EventEmitter { this.clearPongTimer(); this.logger.silly('Sending ping', loggerCategory); - this.getWs(mainWsKey)?.send(JSON.stringify({ op: 'ping' })); + this.tryWsSend(defaultWsKey, JSON.stringify({ op: 'ping' })); this.activePongTimer = setTimeout(() => { this.logger.info('Pong timeout - closing socket to reconnect', loggerCategory); - this.getWs(mainWsKey)?.close(); + this.getWs(defaultWsKey)?.close(); }, this.options.pongTimeout); } @@ -268,7 +304,7 @@ export class WebsocketClient extends EventEmitter { args: topics }); - this.getWs(mainWsKey)?.send(wsMessage); + this.tryWsSend(defaultWsKey, wsMessage); } /** @@ -280,7 +316,15 @@ export class WebsocketClient extends EventEmitter { args: topics }); - this.getWs(mainWsKey)?.send(wsMessage); + this.tryWsSend(defaultWsKey, wsMessage); + } + + private tryWsSend(wsKey: string, wsMessage: string) { + try { + this.getWs(wsKey)?.send(wsMessage); + } catch (e) { + this.logger.error(`Failed to send WS message`, { ...loggerCategory, wsMessage, wsKey, exception: e }); + } } private connectToWsUrl(url: string, wsKey: string): WebSocket { @@ -294,22 +338,22 @@ export class WebsocketClient extends EventEmitter { return ws; } - private onWsOpen(event, wsRef: string = mainWsKey) { - if (this.wsStore.isConnectionState(wsRef, READY_STATE_CONNECTING)) { + private onWsOpen(event, wsKey: string = defaultWsKey) { + if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) { this.logger.info('Websocket connected', { ...loggerCategory, livenet: this.options.livenet, linear: this.options.linear }); this.emit('open'); - } else if (this.wsStore.isConnectionState(wsRef, READY_STATE_RECONNECTING)) { + } else if (this.wsStore.isConnectionState(wsKey, READY_STATE_RECONNECTING)) { this.logger.info('Websocket reconnected', { ...loggerCategory }); this.emit('reconnected'); } - this.setWsState(mainWsKey, READY_STATE_CONNECTED); + this.setWsState(defaultWsKey, READY_STATE_CONNECTED); - this.requestSubscribeTopics([...this.subcribedTopics]); + this.requestSubscribeTopics([...this.wsStore.getTopics(wsKey)]); this.activePingTimer = setInterval(this.ping.bind(this), this.options.pingInterval); } - private onWsMessage(event, wsRef?: string) { + private onWsMessage(event, wsKey?: string) { const msg = JSON.parse(event && event.data || event); if ('success' in msg) { @@ -321,29 +365,29 @@ export class WebsocketClient extends EventEmitter { } } - private onWsError(err, wsRef: string = mainWsKey) { - this.parseWsError('Websocket error', err, wsRef); - if (this.wsStore.isConnectionState(wsRef, READY_STATE_CONNECTED)) { + private onWsError(err, wsKey: string = defaultWsKey) { + this.parseWsError('Websocket error', err, wsKey); + if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { this.emit('error', err); } } - private onWsClose(event, wsRef: string = mainWsKey) { + private onWsClose(event, wsKey: string = defaultWsKey) { this.logger.info('Websocket connection closed', loggerCategory); - if (this.wsStore.getConnectionState(wsRef) !== READY_STATE_CLOSING) { + if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CLOSING) { this.reconnectWithDelay(this.options.reconnectTimeout!); this.emit('reconnect'); } else { - this.setWsState(wsRef, READY_STATE_INITIAL); + this.setWsState(wsKey, READY_STATE_INITIAL); this.emit('close'); } } private onWsMessageResponse(response: any) { if (isWsPong(response)) { - this.logger.silly('pong recieved', loggerCategory); - // this.clearPongTimer(); + this.logger.silly('Received pong', loggerCategory); + this.clearPongTimer(); } else { this.emit('response', response); } @@ -353,11 +397,11 @@ export class WebsocketClient extends EventEmitter { this.emit('update', message); } - private getWs(wsRefKey: string): WebSocket | undefined { - return this.wsStore.getWs(wsRefKey); + private getWs(wsKey: string) { + return this.wsStore.getWs(wsKey); } - private setWsState(wsRefKey: string, state: WsConnectionState) { - this.wsStore.setConnectionState(wsRefKey, state); + private setWsState(wsKey: string, state: WsConnectionState) { + this.wsStore.setConnectionState(wsKey, state); } }; From dd7a4a899baa104ee2b4c585be4943ec5457e3b8 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 6 Feb 2021 16:48:04 +0000 Subject: [PATCH 078/103] move topics to store --- src/util/WsStore.ts | 22 ++++++++++++++++++++-- src/websocket-client.ts | 38 ++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 18 deletions(-) diff --git a/src/util/WsStore.ts b/src/util/WsStore.ts index 3e56930..77f7bea 100644 --- a/src/util/WsStore.ts +++ b/src/util/WsStore.ts @@ -1,12 +1,18 @@ import { WsConnectionState } from '../websocket-client'; import { DefaultLogger, Logger } from '../logger'; + +type WsTopicList = Set; +type KeyedWsTopicLists = { + [key: string]: WsTopicList; +}; + interface WsStoredState { ws?: WebSocket; connectionState?: WsConnectionState; activePingTimer?: NodeJS.Timeout | undefined; activePongTimer?: NodeJS.Timeout | undefined; - subscribedTopics: Set; + subscribedTopics: WsTopicList; }; export default class WsStore { @@ -32,6 +38,10 @@ export default class WsStore { return undefined; } + getKeys(): string[] { + return Object.keys(this.wsState); + } + create(key: string): WsStoredState | undefined { if (this.hasExistingActiveConnection(key)) { this.logger.warning('WsStore setConnection() overwriting existing open connection: ', this.getWs(key)); @@ -91,10 +101,18 @@ export default class WsStore { /* subscribed topics */ - getTopics(key: string): Set { + getTopics(key: string): WsTopicList { return this.get(key, true)!.subscribedTopics; } + getTopicsByKey(): KeyedWsTopicLists { + const result = {}; + for (const refKey in this.wsState) { + result[refKey] = this.getTopics(refKey); + } + return result; + } + addTopic(key: string, topic: string) { return this.getTopics(key).add(topic); } diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 2585291..3ba37ed 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -89,7 +89,6 @@ export class WebsocketClient extends EventEmitter { super(); this.logger = logger || DefaultLogger; - // this.subcribedTopics = new Set(); this.wsStore = new WsStore(this.logger); this.activePingTimer = undefined; this.activePongTimer = undefined; @@ -133,7 +132,9 @@ export class WebsocketClient extends EventEmitter { // subscribe not necessary if not yet connected (will automatically subscribe onOpen) if (this.wsStore.isConnectionState(defaultWsKey, READY_STATE_CONNECTED)) { - this.requestSubscribeTopics(topics); + this.wsStore.getKeys().forEach(wsKey => + this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) + ); } } @@ -153,7 +154,9 @@ export class WebsocketClient extends EventEmitter { // unsubscribe not necessary if not yet connected if (this.wsStore.isConnectionState(defaultWsKey, READY_STATE_CONNECTED)) { - this.requestUnsubscribeTopics(topics); + this.wsStore.getKeys().forEach(wsKey => + this.requestUnsubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) + ); } } @@ -262,25 +265,25 @@ export class WebsocketClient extends EventEmitter { }, connectionDelayMs); } - private ping() { + private ping(wsKey: string = defaultWsKey) { this.clearPongTimer(); this.logger.silly('Sending ping', loggerCategory); - this.tryWsSend(defaultWsKey, JSON.stringify({ op: 'ping' })); + this.tryWsSend(wsKey, JSON.stringify({ op: 'ping' })); this.activePongTimer = setTimeout(() => { this.logger.info('Pong timeout - closing socket to reconnect', loggerCategory); - this.getWs(defaultWsKey)?.close(); + this.getWs(wsKey)?.close(); }, this.options.pongTimeout); } - private clearTimers() { - this.clearPingTimer() - this.clearPongTimer(); + private clearTimers(wsKey: string = defaultWsKey) { + this.clearPingTimer(wsKey); + this.clearPongTimer(wsKey); } // Send a ping at intervals - private clearPingTimer() { + private clearPingTimer(wsKey: string = defaultWsKey) { if (this.activePingTimer) { clearInterval(this.activePingTimer); this.activePingTimer = undefined; @@ -288,7 +291,7 @@ export class WebsocketClient extends EventEmitter { } // Expect a pong within a time limit - private clearPongTimer() { + private clearPongTimer(wsKey: string = defaultWsKey) { if (this.activePongTimer) { clearTimeout(this.activePongTimer); this.activePongTimer = undefined; @@ -298,25 +301,25 @@ export class WebsocketClient extends EventEmitter { /** * Send WS message to subscribe to topics. */ - private requestSubscribeTopics(topics: string[]) { + private requestSubscribeTopics(wsKey: string, topics: string[]) { const wsMessage = JSON.stringify({ op: 'subscribe', args: topics }); - this.tryWsSend(defaultWsKey, wsMessage); + this.tryWsSend(wsKey, wsMessage); } /** * Send WS message to unsubscribe from topics. */ - private requestUnsubscribeTopics(topics: string[]) { + private requestUnsubscribeTopics(wsKey: string, topics: string[]) { const wsMessage = JSON.stringify({ op: 'unsubscribe', args: topics }); - this.tryWsSend(defaultWsKey, wsMessage); + this.tryWsSend(wsKey, wsMessage); } private tryWsSend(wsKey: string, wsMessage: string) { @@ -349,7 +352,10 @@ export class WebsocketClient extends EventEmitter { this.setWsState(defaultWsKey, READY_STATE_CONNECTED); - this.requestSubscribeTopics([...this.wsStore.getTopics(wsKey)]); + this.wsStore.getKeys().forEach(wsKey => + this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) + ); + this.activePingTimer = setInterval(this.ping.bind(this), this.options.pingInterval); } From 39a86f1f066cb26326952a43084b01554c419173 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 6 Feb 2021 16:52:39 +0000 Subject: [PATCH 079/103] cleaning & param keys --- src/websocket-client.ts | 54 ++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 3ba37ed..5437f36 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -63,7 +63,7 @@ export interface WebsocketClientOptions extends WSClientConfigurableOptions { reconnectTimeout: number; }; -const defaultWsKey = 'inverse'; +export const defaultWsKey = 'inverse'; const getLinearWsKeyForTopic = (topic: string) => { switch (topic) { @@ -138,10 +138,6 @@ export class WebsocketClient extends EventEmitter { } } - private getWsKeyForTopic(topic: string) { - return this.isInverse() ? defaultWsKey : getLinearWsKeyForTopic(topic); - } - /** * Remove topic/topics from WS subscription list */ @@ -163,21 +159,11 @@ export class WebsocketClient extends EventEmitter { public close(wsKey: string = defaultWsKey) { this.logger.info('Closing connection', loggerCategory); this.setWsState(wsKey, READY_STATE_CLOSING); - this.clearTimers(); + this.clearTimers(wsKey); this.getWs(wsKey)?.close(); } - private getWsUrl(): string { - if (this.options.wsUrl) { - return this.options.wsUrl; - } - if (this.options.linear){ - return linearEndpoints[this.options.livenet ? 'livenet' : 'testnet']; - } - return inverseEndpoints[this.options.livenet ? 'livenet' : 'testnet']; - } - private async connect(wsKey: string = defaultWsKey): Promise { try { if (this.wsStore.isWsOpen(wsKey)) { @@ -185,14 +171,14 @@ export class WebsocketClient extends EventEmitter { return this.wsStore.getWs(wsKey); } - if (this.wsStore.isConnectionState(defaultWsKey, READY_STATE_CONNECTING)) { + if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) { this.logger.error('Refused to connect to ws, connection attempt already active', { ...loggerCategory, wsKey }) return; } if ( - !this.wsStore.getConnectionState(defaultWsKey) || - this.wsStore.isConnectionState(defaultWsKey, READY_STATE_INITIAL) + !this.wsStore.getConnectionState(wsKey) || + this.wsStore.isConnectionState(wsKey, READY_STATE_INITIAL) ) { this.setWsState(wsKey, READY_STATE_CONNECTING); } @@ -203,8 +189,8 @@ export class WebsocketClient extends EventEmitter { return this.wsStore.setWs(wsKey, ws); } catch (err) { - this.parseWsError('Connection failed', err); - this.reconnectWithDelay(this.options.reconnectTimeout!); + this.parseWsError('Connection failed', err, wsKey); + this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!); } } @@ -253,10 +239,10 @@ export class WebsocketClient extends EventEmitter { return ''; } - private reconnectWithDelay(connectionDelayMs: number) { - this.clearTimers(); - if (this.wsStore.getConnectionState(defaultWsKey) !== READY_STATE_CONNECTING) { - this.setWsState(defaultWsKey, READY_STATE_RECONNECTING); + private reconnectWithDelay(wsKey: string = defaultWsKey, connectionDelayMs: number) { + this.clearTimers(wsKey); + if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CONNECTING) { + this.setWsState(wsKey, READY_STATE_RECONNECTING); } setTimeout(() => { @@ -277,7 +263,7 @@ export class WebsocketClient extends EventEmitter { }, this.options.pongTimeout); } - private clearTimers(wsKey: string = defaultWsKey) { + private clearTimers(wsKey: string) { this.clearPingTimer(wsKey); this.clearPongTimer(wsKey); } @@ -350,7 +336,7 @@ export class WebsocketClient extends EventEmitter { this.emit('reconnected'); } - this.setWsState(defaultWsKey, READY_STATE_CONNECTED); + this.setWsState(wsKey, READY_STATE_CONNECTED); this.wsStore.getKeys().forEach(wsKey => this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) @@ -410,4 +396,18 @@ export class WebsocketClient extends EventEmitter { private setWsState(wsKey: string, state: WsConnectionState) { this.wsStore.setConnectionState(wsKey, state); } + + private getWsUrl(): string { + if (this.options.wsUrl) { + return this.options.wsUrl; + } + if (this.options.linear){ + return linearEndpoints[this.options.livenet ? 'livenet' : 'testnet']; + } + return inverseEndpoints[this.options.livenet ? 'livenet' : 'testnet']; + } + + private getWsKeyForTopic(topic: string) { + return this.isInverse() ? defaultWsKey : getLinearWsKeyForTopic(topic); + } }; From a07ea48d1aacdd786607ae47bebc6043e6f082f4 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 6 Feb 2021 17:01:30 +0000 Subject: [PATCH 080/103] move timers to keyed store --- src/websocket-client.ts | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 5437f36..08f239e 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -76,9 +76,6 @@ const getLinearWsKeyForTopic = (topic: string) => { } export class WebsocketClient extends EventEmitter { - private activePingTimer?: NodeJS.Timeout | undefined; - private activePongTimer?: NodeJS.Timeout | undefined; - private logger: Logger; private client: InverseClient | LinearClient; private options: WebsocketClientOptions; @@ -90,8 +87,6 @@ export class WebsocketClient extends EventEmitter { this.logger = logger || DefaultLogger; this.wsStore = new WsStore(this.logger); - this.activePingTimer = undefined; - this.activePongTimer = undefined; this.options = { livenet: false, @@ -252,12 +247,12 @@ export class WebsocketClient extends EventEmitter { } private ping(wsKey: string = defaultWsKey) { - this.clearPongTimer(); + this.clearPongTimer(wsKey); this.logger.silly('Sending ping', loggerCategory); this.tryWsSend(wsKey, JSON.stringify({ op: 'ping' })); - this.activePongTimer = setTimeout(() => { + this.wsStore.get(wsKey, true)!.activePongTimer = setTimeout(() => { this.logger.info('Pong timeout - closing socket to reconnect', loggerCategory); this.getWs(wsKey)?.close(); }, this.options.pongTimeout); @@ -269,18 +264,20 @@ export class WebsocketClient extends EventEmitter { } // Send a ping at intervals - private clearPingTimer(wsKey: string = defaultWsKey) { - if (this.activePingTimer) { - clearInterval(this.activePingTimer); - this.activePingTimer = undefined; + private clearPingTimer(wsKey: string) { + const wsState = this.wsStore.get(wsKey); + if (wsState?.activePingTimer) { + clearInterval(wsState.activePingTimer); + wsState.activePingTimer = undefined; } } // Expect a pong within a time limit - private clearPongTimer(wsKey: string = defaultWsKey) { - if (this.activePongTimer) { - clearTimeout(this.activePongTimer); - this.activePongTimer = undefined; + private clearPongTimer(wsKey: string) { + const wsState = this.wsStore.get(wsKey); + if (wsState?.activePongTimer) { + clearInterval(wsState.activePongTimer); + wsState.activePongTimer = undefined; } } @@ -342,14 +339,17 @@ export class WebsocketClient extends EventEmitter { this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) ); - this.activePingTimer = setInterval(this.ping.bind(this), this.options.pingInterval); + this.wsStore.get(wsKey, true)!.activePingTimer = setInterval( + this.ping.bind(this), + this.options.pingInterval + ); } - private onWsMessage(event, wsKey?: string) { + private onWsMessage(event, wsKey: string) { const msg = JSON.parse(event && event.data || event); if ('success' in msg) { - this.onWsMessageResponse(msg); + this.onWsMessageResponse(msg, wsKey); } else if (msg.topic) { this.onWsMessageUpdate(msg); } else { @@ -368,7 +368,7 @@ export class WebsocketClient extends EventEmitter { this.logger.info('Websocket connection closed', loggerCategory); if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CLOSING) { - this.reconnectWithDelay(this.options.reconnectTimeout!); + this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!); this.emit('reconnect'); } else { this.setWsState(wsKey, READY_STATE_INITIAL); @@ -376,10 +376,10 @@ export class WebsocketClient extends EventEmitter { } } - private onWsMessageResponse(response: any) { + private onWsMessageResponse(response: any, wsKey: string) { if (isWsPong(response)) { this.logger.silly('Received pong', loggerCategory); - this.clearPongTimer(); + this.clearPongTimer(wsKey); } else { this.emit('response', response); } @@ -402,7 +402,7 @@ export class WebsocketClient extends EventEmitter { return this.options.wsUrl; } if (this.options.linear){ - return linearEndpoints[this.options.livenet ? 'livenet' : 'testnet']; + return linearEndpoints[this.options.livenet ? 'livenet' : 'testnet']; } return inverseEndpoints[this.options.livenet ? 'livenet' : 'testnet']; } From eac3d6bf0646ee73f54c118105616bd5206b47c1 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 6 Feb 2021 17:05:41 +0000 Subject: [PATCH 081/103] fix pong timeout --- src/websocket-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 08f239e..dd5cf1d 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -276,7 +276,7 @@ export class WebsocketClient extends EventEmitter { private clearPongTimer(wsKey: string) { const wsState = this.wsStore.get(wsKey); if (wsState?.activePongTimer) { - clearInterval(wsState.activePongTimer); + clearTimeout(wsState.activePongTimer); wsState.activePongTimer = undefined; } } From 2b112b987942a3fcb7b093dd45e59602c131060c Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sat, 6 Feb 2021 23:54:45 +0000 Subject: [PATCH 082/103] implement linear websockets --- src/util/requestUtils.ts | 11 ++- src/websocket-client.ts | 163 +++++++++++++++++++++++---------------- 2 files changed, 108 insertions(+), 66 deletions(-) diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index 01fc785..f75f106 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -57,7 +57,7 @@ export function getBaseRESTInverseUrl(useLivenet?: boolean, restInverseOptions?: } return baseUrlsInverse.testnet; } - + export function isPublicEndpoint (endpoint: string): boolean { if (endpoint.startsWith('v2/public')) { return true; @@ -67,3 +67,12 @@ export function isPublicEndpoint (endpoint: string): boolean { } return false; } + +export function isWsPong(response: any) { + return ( + response.request && + response.request.op === 'ping' && + response.ret_msg === 'pong' && + response.success === true + ); +} \ No newline at end of file diff --git a/src/websocket-client.ts b/src/websocket-client.ts index dd5cf1d..22e266a 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -2,7 +2,7 @@ import { EventEmitter } from 'events'; import { InverseClient } from './inverse-client'; import { LinearClient } from './linear-client'; import { DefaultLogger, Logger } from './logger'; -import { signMessage, serializeParams } from './util/requestUtils'; +import { signMessage, serializeParams, isWsPong } from './util/requestUtils'; import WebSocket from 'isomorphic-ws'; import WsStore from './util/WsStore'; @@ -13,8 +13,16 @@ const inverseEndpoints = { }; const linearEndpoints = { - livenet: 'wss://stream.bybit.com/realtime_public', - testnet: 'wss://stream-testnet.bybit.com/realtime_public' + private: { + livenet: 'wss://stream.bybit.com/realtime_private', + livenet2: 'wss://stream.bytick.com/realtime_public', + testnet: 'wss://stream-testnet.bybit.com/realtime_private' + }, + public: { + livenet: 'wss://stream.bybit.com/realtime_public', + livenet2: 'wss://stream.bytick.com/realtime_private', + testnet: 'wss://stream-testnet.bybit.com/realtime_public' + } }; const loggerCategory = { category: 'bybit-ws' }; @@ -33,15 +41,6 @@ export enum WsConnectionState { READY_STATE_RECONNECTING }; -const isWsPong = (response: any) => { - return ( - response.request && - response.request.op === 'ping' && - response.ret_msg === 'pong' && - response.success === true - ); -} - export interface WSClientConfigurableOptions { key?: string; secret?: string; @@ -50,6 +49,7 @@ export interface WSClientConfigurableOptions { pongTimeout?: number; pingInterval?: number; reconnectTimeout?: number; + autoConnectWs?: boolean; restOptions?: any; requestOptions?: any; wsUrl?: string; @@ -63,23 +63,23 @@ export interface WebsocketClientOptions extends WSClientConfigurableOptions { reconnectTimeout: number; }; -export const defaultWsKey = 'inverse'; +export const wsKeyInverse = 'inverse'; +export const wsKeyLinearPrivate = 'linearPrivate'; +export const wsKeyLinearPublic = 'linearPublic'; const getLinearWsKeyForTopic = (topic: string) => { - switch (topic) { - case '': - return 'public'; - - default: - return 'private' + const privateLinearTopics = ['position', 'execution', 'order', 'stop_order', 'wallet']; + if (privateLinearTopics.includes(topic)) { + return wsKeyLinearPrivate; } + + return wsKeyLinearPublic; } export class WebsocketClient extends EventEmitter { private logger: Logger; - private client: InverseClient | LinearClient; + private restClient: InverseClient | LinearClient; private options: WebsocketClientOptions; - private wsStore: WsStore; constructor(options: WSClientConfigurableOptions, logger?: Logger) { @@ -98,13 +98,10 @@ export class WebsocketClient extends EventEmitter { }; if (this.options.linear === true) { - this.client = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); + this.restClient = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); } else { - this.client = new InverseClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); + this.restClient = new InverseClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); } - - this.setWsState(defaultWsKey, READY_STATE_INITIAL); - this.connect(defaultWsKey); } public isLivenet(): boolean { @@ -125,12 +122,22 @@ export class WebsocketClient extends EventEmitter { topic )); - // subscribe not necessary if not yet connected (will automatically subscribe onOpen) - if (this.wsStore.isConnectionState(defaultWsKey, READY_STATE_CONNECTED)) { - this.wsStore.getKeys().forEach(wsKey => - this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) - ); - } + // attempt to send subscription topic per websocket + this.wsStore.getKeys().forEach(wsKey => { + // if connected, send subscription request + if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { + console.log(`${wsKey} is supposedly connected - sending request for topics`); + return this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]); + } + + // start connection process if it hasn't yet begun. Topics are automatically subscribed to on-connect + if ( + !this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING) && + !this.wsStore.isConnectionState(wsKey, READY_STATE_RECONNECTING) + ) { + return this.connect(wsKey); + } + }); } /** @@ -144,22 +151,35 @@ export class WebsocketClient extends EventEmitter { )); // unsubscribe not necessary if not yet connected - if (this.wsStore.isConnectionState(defaultWsKey, READY_STATE_CONNECTED)) { + if (this.wsStore.isConnectionState(wsKeyInverse, READY_STATE_CONNECTED)) { this.wsStore.getKeys().forEach(wsKey => this.requestUnsubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) ); } } - public close(wsKey: string = defaultWsKey) { - this.logger.info('Closing connection', loggerCategory); + public close(wsKey: string = wsKeyInverse) { + this.logger.info('Closing connection', { ...loggerCategory, wsKey }); this.setWsState(wsKey, READY_STATE_CLOSING); this.clearTimers(wsKey); this.getWs(wsKey)?.close(); } - private async connect(wsKey: string = defaultWsKey): Promise { + /** + * Request connection of all dependent websockets, instead of waiting for automatic connection by library + */ + public connectAll(): Promise | Promise[] | undefined { + if (this.isInverse()) { + return this.connect(wsKeyInverse); + } + + if (this.options.linear === true) { + return [this.connect(wsKeyLinearPublic), this.connect(wsKeyLinearPrivate)]; + } + } + + private async connect(wsKey: string = wsKeyInverse): Promise { try { if (this.wsStore.isWsOpen(wsKey)) { this.logger.error('Refused to connect to ws with existing active connection', { ...loggerCategory, wsKey }) @@ -177,9 +197,10 @@ export class WebsocketClient extends EventEmitter { ) { this.setWsState(wsKey, READY_STATE_CONNECTING); } + // this.setWsState(wsKey, READY_STATE_CONNECTING); const authParams = await this.getAuthParams(); - const url = this.getWsUrl() + authParams; + const url = this.getWsUrl(wsKey) + authParams; const ws = this.connectToWsUrl(url, wsKey); return this.wsStore.setWs(wsKey, ws); @@ -189,7 +210,7 @@ export class WebsocketClient extends EventEmitter { } } - private parseWsError(context: string, error, wsKey?: string) { + private parseWsError(context: string, error, wsKey: string) { if (!error.message) { this.logger.error(`${context} due to unexpected error: `, error); return; @@ -197,11 +218,11 @@ export class WebsocketClient extends EventEmitter { switch (error.message) { case 'Unexpected server response: 401': - this.logger.error(`${context} due to 401 authorization failure.`, loggerCategory); + this.logger.error(`${context} due to 401 authorization failure.`, { ...loggerCategory, wsKey }); break; default: - this.logger.error(`{context} due to unexpected response error: ${error.msg}`); + this.logger.error(`{context} due to unexpected response error: ${error.msg}`, { ...loggerCategory, wsKey }); break; } } @@ -215,7 +236,7 @@ export class WebsocketClient extends EventEmitter { if (key && secret) { this.logger.debug('Getting auth\'d request params', loggerCategory); - const timeOffset = await this.client.getTimeOffset(); + const timeOffset = await this.restClient.getTimeOffset(); const params: any = { api_key: this.options.key, @@ -234,26 +255,26 @@ export class WebsocketClient extends EventEmitter { return ''; } - private reconnectWithDelay(wsKey: string = defaultWsKey, connectionDelayMs: number) { + private reconnectWithDelay(wsKey: string, connectionDelayMs: number) { this.clearTimers(wsKey); if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CONNECTING) { this.setWsState(wsKey, READY_STATE_RECONNECTING); } setTimeout(() => { - this.logger.info('Reconnecting to server', loggerCategory); - this.connect(); + this.logger.info('Reconnecting to websocket', { ...loggerCategory, wsKey }); + this.connect(wsKey); }, connectionDelayMs); } - private ping(wsKey: string = defaultWsKey) { + private ping(wsKey: string) { this.clearPongTimer(wsKey); - this.logger.silly('Sending ping', loggerCategory); + this.logger.silly('Sending ping', { ...loggerCategory, wsKey }); this.tryWsSend(wsKey, JSON.stringify({ op: 'ping' })); this.wsStore.get(wsKey, true)!.activePongTimer = setTimeout(() => { - this.logger.info('Pong timeout - closing socket to reconnect', loggerCategory); + this.logger.info('Pong timeout - closing socket to reconnect', { ...loggerCategory, wsKey }); this.getWs(wsKey)?.close(); }, this.options.pongTimeout); } @@ -307,6 +328,10 @@ export class WebsocketClient extends EventEmitter { private tryWsSend(wsKey: string, wsMessage: string) { try { + this.logger.silly(`Sending upstream ws message: `, { ...loggerCategory, wsMessage, wsKey }); + if (!wsKey) { + console.error('ws with key: ', wsKey, ' not found'); + } this.getWs(wsKey)?.send(wsMessage); } catch (e) { this.logger.error(`Failed to send WS message`, { ...loggerCategory, wsMessage, wsKey, exception: e }); @@ -314,8 +339,9 @@ export class WebsocketClient extends EventEmitter { } private connectToWsUrl(url: string, wsKey: string): WebSocket { - const ws = new WebSocket(url); + this.logger.silly(`Opening WS connection to URL: ${url}`, { ...loggerCategory, wsKey }) + const ws = new WebSocket(url); ws.onopen = event => this.onWsOpen(event, wsKey); ws.onmessage = event => this.onWsMessage(event, wsKey); ws.onerror = event => this.onWsError(event, wsKey); @@ -324,23 +350,21 @@ export class WebsocketClient extends EventEmitter { return ws; } - private onWsOpen(event, wsKey: string = defaultWsKey) { + private onWsOpen(event, wsKey: string) { if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) { - this.logger.info('Websocket connected', { ...loggerCategory, livenet: this.options.livenet, linear: this.options.linear }); + this.logger.info('Websocket connected', { ...loggerCategory, wsKey, livenet: this.options.livenet, linear: this.options.linear }); this.emit('open'); } else if (this.wsStore.isConnectionState(wsKey, READY_STATE_RECONNECTING)) { - this.logger.info('Websocket reconnected', { ...loggerCategory }); + this.logger.info('Websocket reconnected', { ...loggerCategory, wsKey }); this.emit('reconnected'); } this.setWsState(wsKey, READY_STATE_CONNECTED); - this.wsStore.getKeys().forEach(wsKey => - this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) - ); + this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]); this.wsStore.get(wsKey, true)!.activePingTimer = setInterval( - this.ping.bind(this), + () => this.ping(wsKey), this.options.pingInterval ); } @@ -353,19 +377,19 @@ export class WebsocketClient extends EventEmitter { } else if (msg.topic) { this.onWsMessageUpdate(msg); } else { - this.logger.warning('Got unhandled ws message', msg); + this.logger.warning('Got unhandled ws message', { ...loggerCategory, message: msg, event, wsKey}); } } - private onWsError(err, wsKey: string = defaultWsKey) { + private onWsError(err, wsKey: string = wsKeyInverse) { this.parseWsError('Websocket error', err, wsKey); if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { this.emit('error', err); } } - private onWsClose(event, wsKey: string = defaultWsKey) { - this.logger.info('Websocket connection closed', loggerCategory); + private onWsClose(event, wsKey: string = wsKeyInverse) { + this.logger.info('Websocket connection closed', { ...loggerCategory, wsKey}); if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CLOSING) { this.reconnectWithDelay(wsKey, this.options.reconnectTimeout!); @@ -378,7 +402,7 @@ export class WebsocketClient extends EventEmitter { private onWsMessageResponse(response: any, wsKey: string) { if (isWsPong(response)) { - this.logger.silly('Received pong', loggerCategory); + this.logger.silly('Received pong', { ...loggerCategory, wsKey }); this.clearPongTimer(wsKey); } else { this.emit('response', response); @@ -397,17 +421,26 @@ export class WebsocketClient extends EventEmitter { this.wsStore.setConnectionState(wsKey, state); } - private getWsUrl(): string { + private getWsUrl(wsKey: string): string { if (this.options.wsUrl) { return this.options.wsUrl; } - if (this.options.linear){ - return linearEndpoints[this.options.livenet ? 'livenet' : 'testnet']; + + const networkKey = this.options.livenet ? 'livenet' : 'testnet'; + if (this.options.linear || wsKey.startsWith('linear')){ + if (wsKey === wsKeyLinearPublic) { + return linearEndpoints.public[networkKey]; + } + if (wsKey === wsKeyLinearPrivate) { + return linearEndpoints.private[networkKey]; + } + this.logger.error('Unhandled linear wsKey: ', { ...loggerCategory, wsKey }); + return linearEndpoints[networkKey]; } - return inverseEndpoints[this.options.livenet ? 'livenet' : 'testnet']; + return inverseEndpoints[networkKey]; } private getWsKeyForTopic(topic: string) { - return this.isInverse() ? defaultWsKey : getLinearWsKeyForTopic(topic); + return this.isInverse() ? wsKeyInverse : getLinearWsKeyForTopic(topic); } }; From 87dc3b1c103a9d67d66f9515d93e45a4be89a104 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 7 Feb 2021 11:28:52 +0000 Subject: [PATCH 083/103] fix ws reference in store --- src/util/WsStore.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/WsStore.ts b/src/util/WsStore.ts index 77f7bea..8937191 100644 --- a/src/util/WsStore.ts +++ b/src/util/WsStore.ts @@ -1,6 +1,7 @@ import { WsConnectionState } from '../websocket-client'; import { DefaultLogger, Logger } from '../logger'; +import WebSocket from 'isomorphic-ws'; type WsTopicList = Set; type KeyedWsTopicLists = { From e76e6f3c40fb4cc91fca7692862ba5c4f86d9ce5 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 7 Feb 2021 11:32:35 +0000 Subject: [PATCH 084/103] flatten connectall --- src/websocket-client.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 22e266a..d21e411 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -169,9 +169,9 @@ export class WebsocketClient extends EventEmitter { /** * Request connection of all dependent websockets, instead of waiting for automatic connection by library */ - public connectAll(): Promise | Promise[] | undefined { + public connectAll(): Promise[] | undefined { if (this.isInverse()) { - return this.connect(wsKeyInverse); + return [this.connect(wsKeyInverse)]; } if (this.options.linear === true) { From eb5f8333c1c1b79b1e6ccc33dd67d6b7b27d0dda Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 7 Feb 2021 16:41:42 +0000 Subject: [PATCH 085/103] clean linear websocket work --- README.md | 45 ++++++++++++++++++++++++----------- src/websocket-client.ts | 52 +++++++++++++++++++++-------------------- 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 2d75d55..854b870 100644 --- a/README.md +++ b/README.md @@ -101,20 +101,22 @@ const wsConfig = { key: API_KEY, secret: PRIVATE_KEY, - // The following parameters are optional: + /* + The following parameters are optional: + */ - // defaults to false == testnet. set to true for livenet. + // defaults to false == testnet. Set to true for livenet. // livenet: true - // override which URL to use for websocket connections - // wsUrl: 'wss://stream.bytick.com/realtime' - - // how often to check (in ms) that WS connection is still alive - // pingInterval: 10000, + // defaults to fase == inverse. Set to true for linear (USDT) trading. + // linear: true // how long to wait (in ms) before deciding the connection should be terminated & reconnected // pongTimeout: 1000, + // how often to check (in ms) that WS connection is still alive + // pingInterval: 10000, + // how long to wait before attempting to reconnect (in ms) after connection is closed // reconnectTimeout: 500, @@ -123,45 +125,60 @@ const wsConfig = { // config for axios to pass to RestClient. E.g for proxy support // requestOptions: { } + + // override which URL to use for websocket connections + // wsUrl: 'wss://stream.bytick.com/realtime' }; const ws = new WebsocketClient(wsConfig); +// subscribe to multiple topics at once ws.subscribe(['position', 'execution', 'trade']); + +// and/or subscribe to individual topics on demand ws.subscribe('kline.BTCUSD.1m'); -ws.on('open', () => { - console.log('connection open'); +// Listen to events coming from websockets. This is the primary data source +ws.on('update', data => { + console.log('update', data); }); -ws.on('update', message => { - console.log('update', message); +// Optional: Listen to websocket connection open event (automatic after subscribing to one or more topics) +ws.on('open', ({ wsKey, event }) => { + console.log('connection open for websocket with ID: ' + wsKey); }); +// Optional: Listen to responses to websocket queries (e.g. the response after subscribing to a topic) ws.on('response', response => { console.log('response', response); }); +// Optional: Listen to connection close event. Unexpected connection closes are automatically reconnected. ws.on('close', () => { console.log('connection closed'); }); +// Optional: Listen to raw error events. +// Note: responses to invalid topics are currently only sent in the "response" event. ws.on('error', err => { console.error('ERR', err); }); ``` -See inverse [websocket-client.ts](./src/websocket-client.ts) for further information. +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: ```js -const { RestClient, WebsocketClient, DefaultLogger } = require('bybit-api'); +const { WebsocketClient, DefaultLogger } = require('bybit-api'); // Disable all logging on the silly level DefaultLogger.silly = () => {}; -const ws = new WebsocketClient({key: 'xxx', secret: 'yyy'}, DefaultLogger); +const ws = new WebsocketClient( + { key: 'xxx', secret: 'yyy' }, + DefaultLogger +); ``` ## Contributions & Thanks diff --git a/src/websocket-client.ts b/src/websocket-client.ts index d21e411..92038f7 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -49,7 +49,6 @@ export interface WSClientConfigurableOptions { pongTimeout?: number; pingInterval?: number; reconnectTimeout?: number; - autoConnectWs?: boolean; restOptions?: any; requestOptions?: any; wsUrl?: string; @@ -97,7 +96,7 @@ export class WebsocketClient extends EventEmitter { ...options }; - if (this.options.linear === true) { + if (this.isLinear()) { this.restClient = new LinearClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); } else { this.restClient = new InverseClient(undefined, undefined, this.isLivenet(), this.options.restOptions, this.options.requestOptions); @@ -108,8 +107,12 @@ export class WebsocketClient extends EventEmitter { return this.options.livenet === true; } + public isLinear(): boolean { + return this.options.linear === true; + } + public isInverse(): boolean { - return !this.options.linear; + return !this.isLinear(); } /** @@ -150,15 +153,15 @@ export class WebsocketClient extends EventEmitter { topic )); - // unsubscribe not necessary if not yet connected - if (this.wsStore.isConnectionState(wsKeyInverse, READY_STATE_CONNECTED)) { - this.wsStore.getKeys().forEach(wsKey => + this.wsStore.getKeys().forEach(wsKey => { + // unsubscribe request only necessary if active connection exists + if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { this.requestUnsubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]) - ); - } + } + }); } - public close(wsKey: string = wsKeyInverse) { + public close(wsKey: string) { this.logger.info('Closing connection', { ...loggerCategory, wsKey }); this.setWsState(wsKey, READY_STATE_CLOSING); this.clearTimers(wsKey); @@ -169,17 +172,17 @@ export class WebsocketClient extends EventEmitter { /** * Request connection of all dependent websockets, instead of waiting for automatic connection by library */ - public connectAll(): Promise[] | undefined { + public connectAll(): Promise[] | undefined { if (this.isInverse()) { return [this.connect(wsKeyInverse)]; } - if (this.options.linear === true) { + if (this.isLinear()) { return [this.connect(wsKeyLinearPublic), this.connect(wsKeyLinearPrivate)]; } } - private async connect(wsKey: string = wsKeyInverse): Promise { + private async connect(wsKey: string): Promise { try { if (this.wsStore.isWsOpen(wsKey)) { this.logger.error('Refused to connect to ws with existing active connection', { ...loggerCategory, wsKey }) @@ -197,9 +200,8 @@ export class WebsocketClient extends EventEmitter { ) { this.setWsState(wsKey, READY_STATE_CONNECTING); } - // this.setWsState(wsKey, READY_STATE_CONNECTING); - const authParams = await this.getAuthParams(); + const authParams = await this.getAuthParams(wsKey); const url = this.getWsUrl(wsKey) + authParams; const ws = this.connectToWsUrl(url, wsKey); @@ -230,11 +232,11 @@ export class WebsocketClient extends EventEmitter { /** * Return params required to make authorized request */ - private async getAuthParams(): Promise { + private async getAuthParams(wsKey: string): Promise { const { key, secret } = this.options; - if (key && secret) { - this.logger.debug('Getting auth\'d request params', loggerCategory); + if (key && secret && wsKey !== wsKeyLinearPublic) { + this.logger.debug('Getting auth\'d request params', { ...loggerCategory, wsKey }); const timeOffset = await this.restClient.getTimeOffset(); @@ -247,9 +249,9 @@ export class WebsocketClient extends EventEmitter { return '?' + serializeParams(params); } else if (!key || !secret) { - this.logger.warning('Connot authenticate websocket, either api or private keys missing.', loggerCategory); + this.logger.warning('Connot authenticate websocket, either api or private keys missing.', { ...loggerCategory, wsKey }); } else { - this.logger.debug('Starting public only websocket client.', loggerCategory); + this.logger.debug('Starting public only websocket client.', { ...loggerCategory, wsKey }); } return ''; @@ -352,11 +354,11 @@ export class WebsocketClient extends EventEmitter { private onWsOpen(event, wsKey: string) { if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTING)) { - this.logger.info('Websocket connected', { ...loggerCategory, wsKey, livenet: this.options.livenet, linear: this.options.linear }); - this.emit('open'); + this.logger.info('Websocket connected', { ...loggerCategory, wsKey, livenet: this.isLivenet(), linear: this.isLinear() }); + this.emit('open', { wsKey, event }); } else if (this.wsStore.isConnectionState(wsKey, READY_STATE_RECONNECTING)) { this.logger.info('Websocket reconnected', { ...loggerCategory, wsKey }); - this.emit('reconnected'); + this.emit('reconnected', { wsKey, event }); } this.setWsState(wsKey, READY_STATE_CONNECTED); @@ -381,14 +383,14 @@ export class WebsocketClient extends EventEmitter { } } - private onWsError(err, wsKey: string = wsKeyInverse) { + private onWsError(err, wsKey: string) { this.parseWsError('Websocket error', err, wsKey); if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { this.emit('error', err); } } - private onWsClose(event, wsKey: string = wsKeyInverse) { + private onWsClose(event, wsKey: string) { this.logger.info('Websocket connection closed', { ...loggerCategory, wsKey}); if (this.wsStore.getConnectionState(wsKey) !== READY_STATE_CLOSING) { @@ -427,7 +429,7 @@ export class WebsocketClient extends EventEmitter { } const networkKey = this.options.livenet ? 'livenet' : 'testnet'; - if (this.options.linear || wsKey.startsWith('linear')){ + if (this.isLinear() || wsKey.startsWith('linear')){ if (wsKey === wsKeyLinearPublic) { return linearEndpoints.public[networkKey]; } From 4ee7de37740eb704273eb1498e3ecb9c2984ce72 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 7 Feb 2021 16:53:32 +0000 Subject: [PATCH 086/103] cleaning logger types --- src/logger.ts | 2 -- src/util/WsStore.ts | 6 +++--- src/websocket-client.ts | 6 +++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/logger.ts b/src/logger.ts index 4c5e682..0f5f29e 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,7 +1,5 @@ export type LogParams = null | any; -export type Logger = typeof DefaultLogger; - export const DefaultLogger = { silly: (...params: LogParams): void => { console.log(params); diff --git a/src/util/WsStore.ts b/src/util/WsStore.ts index 8937191..37e2f96 100644 --- a/src/util/WsStore.ts +++ b/src/util/WsStore.ts @@ -1,5 +1,5 @@ import { WsConnectionState } from '../websocket-client'; -import { DefaultLogger, Logger } from '../logger'; +import { DefaultLogger } from '../logger'; import WebSocket from 'isomorphic-ws'; @@ -20,9 +20,9 @@ export default class WsStore { private wsState: { [key: string]: WsStoredState; } - private logger: Logger; + private logger: typeof DefaultLogger; - constructor(logger: Logger) { + constructor(logger: typeof DefaultLogger) { this.logger = logger || DefaultLogger; this.wsState = {}; } diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 92038f7..983e045 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -1,7 +1,7 @@ import { EventEmitter } from 'events'; import { InverseClient } from './inverse-client'; import { LinearClient } from './linear-client'; -import { DefaultLogger, Logger } from './logger'; +import { DefaultLogger } from './logger'; import { signMessage, serializeParams, isWsPong } from './util/requestUtils'; import WebSocket from 'isomorphic-ws'; @@ -76,12 +76,12 @@ const getLinearWsKeyForTopic = (topic: string) => { } export class WebsocketClient extends EventEmitter { - private logger: Logger; + private logger: typeof DefaultLogger; private restClient: InverseClient | LinearClient; private options: WebsocketClientOptions; private wsStore: WsStore; - constructor(options: WSClientConfigurableOptions, logger?: Logger) { + constructor(options: WSClientConfigurableOptions, logger?: typeof DefaultLogger) { super(); this.logger = logger || DefaultLogger; From e0ee0d3c8957555522417871dcbc7af23969d0a8 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 7 Feb 2021 17:01:05 +0000 Subject: [PATCH 087/103] cleaning --- src/websocket-client.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 983e045..b81520b 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -129,7 +129,6 @@ export class WebsocketClient extends EventEmitter { this.wsStore.getKeys().forEach(wsKey => { // if connected, send subscription request if (this.wsStore.isConnectionState(wsKey, READY_STATE_CONNECTED)) { - console.log(`${wsKey} is supposedly connected - sending request for topics`); return this.requestSubscribeTopics(wsKey, [...this.wsStore.getTopics(wsKey)]); } @@ -332,7 +331,7 @@ export class WebsocketClient extends EventEmitter { try { this.logger.silly(`Sending upstream ws message: `, { ...loggerCategory, wsMessage, wsKey }); if (!wsKey) { - console.error('ws with key: ', wsKey, ' not found'); + throw new Error('Cannot send message due to no known websocket for this wsKey'); } this.getWs(wsKey)?.send(wsMessage); } catch (e) { From 62750228c4989d51f763737ae3261acb141b0351 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 14 Feb 2021 16:13:35 +0000 Subject: [PATCH 088/103] move script commands around. Move shared endpoint down. Add event annotations to ws client. --- .github/workflows/npmpublish.yml | 2 ++ package.json | 3 ++- src/shared-endpoints.ts | 16 ++++++++-------- src/websocket-client.ts | 10 ++++++++++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index f2af8e7..b0b36fc 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -42,6 +42,8 @@ jobs: - run: npm ci if: steps.version-updated.outputs.has-updated + - run: npm run clean + if: steps.version-updated.outputs.has-updated - run: npm run build if: steps.version-updated.outputs.has-updated - run: npm publish diff --git a/package.json b/package.json index 47ac72c..b933318 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,9 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "clean": "rm -rf lib dist", - "prebuild": "npm run clean", + "buildclean": "npm run clean && npm run build", "build": "tsc", + "build:watch": "npm run clean && tsc --watch", "pack": "webpack --config webpack/webpack.config.js", "prepublish": "npm run build", "betapublish": "npm publish --tag beta" diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index fe7daed..0f868a0 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -82,14 +82,6 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/private/wallet/balance', params) } - getAssetExchangeRecords(params?: { - limit?: number; - from?: number; - direction?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/exchange-order/list', params); - } - getWalletFundRecords(params?: { start_date?: string; end_date?: string; @@ -113,6 +105,14 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); } + getAssetExchangeRecords(params?: { + limit?: number; + from?: number; + direction?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/exchange-order/list', params); + } + /** * * API Data Endpoints diff --git a/src/websocket-client.ts b/src/websocket-client.ts index b81520b..27d9825 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -75,6 +75,16 @@ const getLinearWsKeyForTopic = (topic: string) => { return wsKeyLinearPublic; } +export declare interface WebsocketClient { + on(event: 'open', listener: ({ wsKey: string, event: any }) => void): this; + on(event: 'reconnected', listener: ({ wsKey: string, event: any }) => void): this; + on(event: 'reconnect', listener: () => void): this; + on(event: 'close', listener: () => void): this; + on(event: 'response', listener: (response: any) => void): this; + on(event: 'update', listener: (response: any) => void): this; + on(event: 'error', listener: (response: any) => void): this; +} + export class WebsocketClient extends EventEmitter { private logger: typeof DefaultLogger; private restClient: InverseClient | LinearClient; From 3b2af548d0c53277ef7cc2078518cec5f88b6e76 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 14 Feb 2021 16:15:45 +0000 Subject: [PATCH 089/103] cleaning in package json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b933318..98ff433 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,11 @@ "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "clean": "rm -rf lib dist", - "buildclean": "npm run clean && npm run build", "build": "tsc", + "build:clean": "npm run clean && npm run build", "build:watch": "npm run clean && tsc --watch", "pack": "webpack --config webpack/webpack.config.js", - "prepublish": "npm run build", + "prepublish": "npm run build:clean", "betapublish": "npm publish --tag beta" }, "author": "Tiago Siebler (https://github.com/tiagosiebler)", From 26602cfa05be58475d596969568f6ca13161f099 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 14 Feb 2021 16:29:35 +0000 Subject: [PATCH 090/103] rename rest client options to be more generic --- src/inverse-client.ts | 34 +++++++++++++++++----------------- src/linear-client.ts | 17 ++++++++--------- src/util/requestUtils.ts | 4 ++-- src/util/requestWrapper.ts | 6 +++--- 4 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/inverse-client.ts b/src/inverse-client.ts index a22e327..405c4b1 100644 --- a/src/inverse-client.ts +++ b/src/inverse-client.ts @@ -1,5 +1,5 @@ import { AxiosRequestConfig } from 'axios'; -import { GenericAPIResponse, getBaseRESTInverseUrl, RestClientInverseOptions } from './util/requestUtils'; +import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils'; import RequestWrapper from './util/requestWrapper'; import SharedEndpoints from './shared-endpoints'; @@ -12,23 +12,23 @@ export class InverseClient extends SharedEndpoints { * @param {string} key - your API key * @param {string} secret - your API secret * @param {boolean} [useLivenet=false] - * @param {RestClientInverseOptions} [restInverseOptions={}] options to configure REST API connectivity + * @param {RestClientOptions} [restInverseOptions={}] options to configure REST API connectivity * @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios */ constructor( key?: string | undefined, secret?: string | undefined, useLivenet?: boolean, - restInverseOptions: RestClientInverseOptions = {}, - httpOptions: AxiosRequestConfig = {} + restInverseOptions: RestClientOptions = {}, + requestOptions: AxiosRequestConfig = {} ) { super() this.requestWrapper = new RequestWrapper( key, secret, - getBaseRESTInverseUrl(useLivenet), + getRestBaseUrl(useLivenet), restInverseOptions, - httpOptions + requestOptions ); return this; } @@ -47,7 +47,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/kline/list', params); } - + /** * @deprecated use getTickers() instead */ @@ -56,7 +56,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.getTickers(params); } - + /** * @deprecated use getTrades() instead */ @@ -75,7 +75,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/trading-records', params); } - + /** * @deprecated use getLiquidations() instead */ @@ -97,7 +97,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/mark-price-kline', params); } - + getIndexPriceKline(params: { symbol: string; interval: string; @@ -106,7 +106,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/public/index-price-kline', params); } - + getPremiumIndexKline(params: { symbol: string; interval: string; @@ -121,7 +121,7 @@ export class InverseClient extends SharedEndpoints { * Account Data Endpoints * */ - + /** * Active orders */ @@ -183,7 +183,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/order', params); } - + /** * Conditional orders */ @@ -246,7 +246,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/stop-order', params); } - + /** * Position */ @@ -328,7 +328,7 @@ export class InverseClient extends SharedEndpoints { /** * Risk Limit */ - + getRiskLimitList(): GenericAPIResponse { return this.requestWrapper.get('open-api/wallet/risk-limit/list'); } @@ -339,7 +339,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.post('open-api/wallet/risk-limit', params); } - + /** * Funding */ @@ -361,7 +361,7 @@ export class InverseClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/funding/predicted-funding', params); } - + /** * LCP Info */ diff --git a/src/linear-client.ts b/src/linear-client.ts index e62f9e0..4ef2566 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -1,5 +1,5 @@ import { AxiosRequestConfig } from 'axios'; -import { GenericAPIResponse, getBaseRESTInverseUrl, RestClientInverseOptions } from './util/requestUtils'; +import { GenericAPIResponse, getRestBaseUrl, RestClientOptions } from './util/requestUtils'; import RequestWrapper from './util/requestWrapper'; import SharedEndpoints from './shared-endpoints'; @@ -11,23 +11,22 @@ export class LinearClient extends SharedEndpoints { * * @param {string} key - your API key * @param {string} secret - your API secret - * @param {boolean} [livenet=false] - * @param {RestClientInverseOptions} [restInverseOptions={}] options to configure REST API connectivity + * @param {boolean} [useLivenet=false] + * @param {RestClientOptions} [restInverseOptions={}] options to configure REST API connectivity * @param {AxiosRequestConfig} [requestOptions={}] HTTP networking options for axios */ - constructor( key?: string | undefined, secret?: string | undefined, - livenet?: boolean, - restInverseOptions:RestClientInverseOptions = {}, // TODO: Rename this type to be more general. + useLivenet?: boolean, + restInverseOptions: RestClientOptions = {}, requestOptions: AxiosRequestConfig = {} ) { super() this.requestWrapper = new RequestWrapper( key, secret, - getBaseRESTInverseUrl(livenet), + getRestBaseUrl(useLivenet), restInverseOptions, requestOptions ); @@ -46,7 +45,7 @@ export class LinearClient extends SharedEndpoints { from: number; limit?: number; }): GenericAPIResponse { - return this.requestWrapper.get('/public/linear/kline', params); + return this.requestWrapper.get('public/linear/kline', params); } /** @@ -82,7 +81,7 @@ export class LinearClient extends SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('public/linear/mark-price-kline', params); } - + getIndexPriceKline(params: { symbol: string; interval: string; diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index f75f106..903a7a2 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -1,6 +1,6 @@ import { createHmac } from 'crypto'; -export interface RestClientInverseOptions { +export interface RestClientOptions { // override the max size of the request window (in ms) recv_window?: number; @@ -42,7 +42,7 @@ export function serializeParams(params: object = {}, strict_validation = false): .join('&'); }; -export function getBaseRESTInverseUrl(useLivenet?: boolean, restInverseOptions?: RestClientInverseOptions) { +export function getRestBaseUrl(useLivenet?: boolean, restInverseOptions?: RestClientOptions) { const baseUrlsInverse = { livenet: 'https://api.bybit.com', testnet: 'https://api-testnet.bybit.com' diff --git a/src/util/requestWrapper.ts b/src/util/requestWrapper.ts index 7645061..e19f8f3 100644 --- a/src/util/requestWrapper.ts +++ b/src/util/requestWrapper.ts @@ -1,11 +1,11 @@ import axios, { AxiosRequestConfig, AxiosResponse, Method } from 'axios'; -import { signMessage, serializeParams, RestClientInverseOptions, GenericAPIResponse, isPublicEndpoint } from './requestUtils'; +import { signMessage, serializeParams, RestClientOptions, GenericAPIResponse, isPublicEndpoint } from './requestUtils'; export default class RequestUtil { private timeOffset: number | null; private syncTimePromise: null | Promise; - private options: RestClientInverseOptions; + private options: RestClientOptions; private baseUrl: string; private globalRequestOptions: AxiosRequestConfig; private key: string | undefined; @@ -15,7 +15,7 @@ export default class RequestUtil { key: string | undefined, secret: string | undefined, baseUrl: string, - options: RestClientInverseOptions = {}, + options: RestClientOptions = {}, requestOptions: AxiosRequestConfig = {} ) { this.timeOffset = null; From 581cbe7d43208006739ca95a44c14ad89b2dd35f Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 14 Feb 2021 16:40:53 +0000 Subject: [PATCH 091/103] remove deprecated new method, fix missing parms --- src/linear-client.ts | 32 +++++++++++--------------------- src/shared-endpoints.ts | 6 ++++++ 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 4ef2566..59f7f52 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -48,20 +48,8 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('public/linear/kline', params); } - /** - * @deprecated use getTrades() instead - */ - getPublicTradingRecords(params: { - symbol: string; - from?: number; - limit?: number; - }): GenericAPIResponse { - return this.getTrades(params); - } - getTrades(params: { symbol: string; - //from?: number; limit?: number; }): GenericAPIResponse { return this.requestWrapper.get('public/linear/recent-trading-records', params); @@ -104,10 +92,6 @@ export class LinearClient extends SharedEndpoints { * * Account Data Endpoints * - */ - - /** - * Active orders */ placeActiveOrder(params: { @@ -136,7 +120,6 @@ export class LinearClient extends SharedEndpoints { page?: number; limit?: number; order_status?: string; - }): GenericAPIResponse { return this.requestWrapper.get('private/linear/order/list', params); } @@ -344,7 +327,15 @@ export class LinearClient extends SharedEndpoints { getRiskLimitList(params: { symbol: string; }): GenericAPIResponse { - return this.requestWrapper.get('public/linear/risk-limit'); + return this.requestWrapper.get('public/linear/risk-limit', params); + } + + setRiskLimit(params: { + symbol: string; + side: string; + risk_id: string; + }): GenericAPIResponse { + return this.requestWrapper.get('private/linear/position/set-risk', params); } /** @@ -354,13 +345,12 @@ export class LinearClient extends SharedEndpoints { getPredictedFundingFee(params: { symbol: string; }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/funding/predicted-funding'); + return this.requestWrapper.get('private/linear/funding/predicted-funding', params); } getLastFundingFee(params: { symbol: string; }): GenericAPIResponse { - return this.requestWrapper.get('private/linear/funding/prev-funding'); + return this.requestWrapper.get('private/linear/funding/prev-funding', params); } - } diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index 0f868a0..1f3a9b2 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -37,6 +37,12 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/public/liq-records', params); } + /** + * + * Market Data : Advanced + * + */ + getOpenInterest(params: { symbol: string; period: string; From ce83b3a6662072a8ab236988591b0deb885ccae9 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 14 Feb 2021 16:49:48 +0000 Subject: [PATCH 092/103] readme notes --- README.md | 79 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 854b870..c13d7c5 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,10 @@ Build a bundle using webpack: The bundle can be found in `dist/`. Altough usage should be largely consistent, smaller differences will exist. Documentation is still TODO. ### Inverse Contracts -#### Rest client -```javascript -const { RestClient } = require('bybit-api'); +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`: -const API_KEY = 'xxx'; -const PRIVATE_KEY = 'yyy'; -const useLivenet = false; +```javascript +const { InverseClient } = require('bybit-api'); const restInverseOptions = { // override the max size of the request window (in ms) @@ -68,7 +65,11 @@ const restInverseOptions = { parse_exceptions?: boolean; }; -const client = new RestClient( +const API_KEY = 'xxx'; +const PRIVATE_KEY = 'yyy'; +const useLivenet = false; + +const client = new InverseClient( API_KEY, PRIVATE_KEY, @@ -88,9 +89,67 @@ client.changeUserLeverage({leverage: 4, symbol: 'ETHUSD'}) }); ``` -See inverse [rest-client.ts](./src/rest-client.ts) for further information. +See inverse [inverse-client.ts](./src/inverse-client.ts) for further information. + +### Linear Contracts +To use the Linear (USDT) REST APIs, import the `LinearClient`: + +```javascript +const { LinearClient } = require('bybit-api'); + +const restInverseOptions = { + // override the max size of the request window (in ms) + recv_window?: number; + + // how often to sync time drift with bybit servers + sync_interval_ms?: number | string; + + // Default: false. Disable above sync mechanism if true. + disable_time_sync?: boolean; + + // Default: false. If true, we'll throw errors if any params are undefined + strict_param_validation?: boolean; + + // Optionally override API protocol + domain + // e.g 'https://api.bytick.com' + baseUrl?: string; + + // Default: true. whether to try and post-process request exceptions. + parse_exceptions?: boolean; +}; + +const API_KEY = 'xxx'; +const PRIVATE_KEY = 'yyy'; +const useLivenet = false; + +const client = new LinearClient( + API_KEY, + PRIVATE_KEY, + + // optional, uses testnet by default. Set to 'true' to use livenet. + useLivenet, + + // restInverseOptions, + // requestLibraryOptions +); + +client.changeUserLeverage({leverage: 4, symbol: 'ETHUSD'}) + .then(result => { + console.log(result); + }) + .catch(err => { + console.error(err); + }); +``` + +### 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: -#### Websocket client ```javascript const { WebsocketClient } = require('bybit-api'); @@ -123,7 +182,7 @@ const wsConfig = { // config options sent to RestClient (used for time sync). See RestClient docs. // restOptions: { }, - // config for axios to pass to RestClient. E.g for proxy support + // config for axios used for HTTP requests. E.g for proxy support // requestOptions: { } // override which URL to use for websocket connections From fae3935b851e2a8185facf0618d9a26911abb290 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 16:54:33 +0000 Subject: [PATCH 093/103] Order Change --- src/shared-endpoints.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index fe7daed..744c4e0 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -82,14 +82,6 @@ export default class SharedEndpoints { return this.requestWrapper.get('v2/private/wallet/balance', params) } - getAssetExchangeRecords(params?: { - limit?: number; - from?: number; - direction?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/exchange-order/list', params); - } - getWalletFundRecords(params?: { start_date?: string; end_date?: string; @@ -112,6 +104,14 @@ export default class SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); } + + getAssetExchangeRecords(params?: { + limit?: number; + from?: number; + direction?: string; + }): GenericAPIResponse { + return this.requestWrapper.get('v2/private/exchange-order/list', params); + } /** * From 0211b743ba05ff04a50f71e8f0f906f483cc3344 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 16:59:50 +0000 Subject: [PATCH 094/103] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c13d7c5..05c8b70 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ A production-ready Node.js connector for the Bybit APIs and WebSockets, with Typ ## 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 Linear Documentation (not supported yet)](https://bybit-exchange.github.io/docs/linear/#t-introduction) +- [Bybit API Linear Documentation](https://bybit-exchange.github.io/docs/linear/#t-introduction) ## Structure This project uses typescript. Resources are stored in 3 key structures: From 8ad2e0800442b6d9e46d21b51200dbcb8756a713 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:06:21 +0000 Subject: [PATCH 095/103] helper for breaking change --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 05c8b70..de14114 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ 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). +- RestClient not defined? With our recent addition of LinearClient, we renamed RestClient to InverseClient. Change all references to RestClient. - Discuss & collaborate with other node devs? Join our [Node.js Algo Traders](https://t.me/nodetraders) engineering community on telegram. ## Documentation From 2bcfafd54ecdfdbc50a508d507cc9687beca8f92 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:06:59 +0000 Subject: [PATCH 096/103] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de14114..8fbb597 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ 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). -- RestClient not defined? With our recent addition of LinearClient, we renamed RestClient to InverseClient. Change all references to RestClient. +- RestClient not defined? With the recent addition of LinearClient, we renamed RestClient to InverseClient. Change all old references. - Discuss & collaborate with other node devs? Join our [Node.js Algo Traders](https://t.me/nodetraders) engineering community on telegram. ## Documentation From 5e91c78bbff13d819f54beb588c42dbebf1524a9 Mon Sep 17 00:00:00 2001 From: tiagosiebler Date: Sun, 14 Feb 2021 17:13:38 +0000 Subject: [PATCH 097/103] address codefactor suggestion --- src/shared-endpoints.ts | 8 -------- src/websocket-client.ts | 10 +++------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/shared-endpoints.ts b/src/shared-endpoints.ts index c65a2ef..1f3a9b2 100644 --- a/src/shared-endpoints.ts +++ b/src/shared-endpoints.ts @@ -110,14 +110,6 @@ export default class SharedEndpoints { }): GenericAPIResponse { return this.requestWrapper.get('v2/private/wallet/withdraw/list', params); } - - getAssetExchangeRecords(params?: { - limit?: number; - from?: number; - direction?: string; - }): GenericAPIResponse { - return this.requestWrapper.get('v2/private/exchange-order/list', params); - } getAssetExchangeRecords(params?: { limit?: number; diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 27d9825..7e23e42 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -76,13 +76,9 @@ const getLinearWsKeyForTopic = (topic: string) => { } export declare interface WebsocketClient { - on(event: 'open', listener: ({ wsKey: string, event: any }) => void): this; - on(event: 'reconnected', listener: ({ wsKey: string, event: any }) => void): this; - on(event: 'reconnect', listener: () => void): this; - on(event: 'close', listener: () => void): this; - on(event: 'response', listener: (response: any) => void): this; - on(event: 'update', listener: (response: any) => void): this; - on(event: 'error', listener: (response: any) => void): this; + on(event: 'open' | 'reconnected', listener: ({ wsKey: string, event: any }) => void): this; + on(event: 'response' | 'update' | 'error', listener: (response: any) => void): this; + on(event: 'reconnect' | 'close', listener: () => void): this; } export class WebsocketClient extends EventEmitter { From 629ecd3d2941ed48d999801bc3830a7d87b2f33f Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:26:49 +0000 Subject: [PATCH 098/103] Update src/linear-client.ts Co-authored-by: Tiago --- src/linear-client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 59f7f52..7b2cb9c 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -110,7 +110,7 @@ export class LinearClient extends SharedEndpoints { order_link_id?: string; }): GenericAPIResponse { return this.requestWrapper.post('private/linear/order/create', params); - } + } getActiveOrderList(params: { order_id?: string; From 80bcbdb752d98a83660c30961fa57e2a121db52c Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:26:54 +0000 Subject: [PATCH 099/103] Update README.md Co-authored-by: Tiago --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8fbb597..752e2c1 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ 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). -- RestClient not defined? With the recent addition of LinearClient, we renamed RestClient to InverseClient. Change all old references. - 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. From 17157ac407ef8e09eba17c37fac99d5803759892 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:26:58 +0000 Subject: [PATCH 100/103] Update src/linear-client.ts Co-authored-by: Tiago --- src/linear-client.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index 7b2cb9c..ce2e9a0 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -338,9 +338,9 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('private/linear/position/set-risk', params); } - /** - * Funding - */ + /** + * Funding + */ getPredictedFundingFee(params: { symbol: string; From 93f5e442f5500e8357ec3d2d5e1c7aacb409785a Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:27:04 +0000 Subject: [PATCH 101/103] Update src/linear-client.ts Co-authored-by: Tiago --- src/linear-client.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index ce2e9a0..f0de1b0 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -320,9 +320,9 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('private/linear/tpsl/switch-mode', params); } - /** - * Risk Limit - */ + /** + * Risk Limit + */ getRiskLimitList(params: { symbol: string; From fffa05ffcd91d5c406bcb770667a4468a90bdc52 Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Sun, 14 Feb 2021 17:34:44 +0000 Subject: [PATCH 102/103] Update src/linear-client.ts Co-authored-by: Tiago --- src/linear-client.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/linear-client.ts b/src/linear-client.ts index f0de1b0..d44d6ad 100644 --- a/src/linear-client.ts +++ b/src/linear-client.ts @@ -160,9 +160,9 @@ export class LinearClient extends SharedEndpoints { return this.requestWrapper.get('private/linear/order/search', params); } - /** - * Conditional orders - */ + /** + * Conditional orders + */ placeConditionalOrder(params: { side: string; From b2018879e6ef1fe25409d81a5dc073b703b86eeb Mon Sep 17 00:00:00 2001 From: CryptoCompiler <72892531+peepopoggers@users.noreply.github.com> Date: Mon, 15 Feb 2021 17:12:16 +0000 Subject: [PATCH 103/103] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 752e2c1..5a4321d 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ const client = new LinearClient( // requestLibraryOptions ); -client.changeUserLeverage({leverage: 4, symbol: 'ETHUSD'}) +client.changeUserLeverage({leverage: 4, symbol: 'ETHUSDT'}) .then(result => { console.log(result); })