From b7781df8917396c3f51862ff0e17495223f79f79 Mon Sep 17 00:00:00 2001 From: zlq4863947 Date: Wed, 10 Feb 2021 02:12:16 +0900 Subject: [PATCH 1/2] feat: update bitmex api warper --- .../crypto/bitmex/common/authentication.ts | 32 +- .../crypto/bitmex/common/get-channel-name.ts | 35 + .../exchanges/crypto/bitmex/common/helpers.ts | 71 - .../exchanges/crypto/bitmex/common/index.ts | 3 +- .../common/test-helpers/receive-order-data.ts | 56 +- .../bitmex/common/transform-orderbook.ts | 19 + modules/exchanges/crypto/bitmex/package.json | 2 + .../rest/{types/endpoint.ts => constants.ts} | 23 +- .../bitmex/rest/internal/private/index.ts | 1 + .../internal/private/multiple-order/index.ts | 1 + .../multiple-order/multiple-order.spec.ts | 97 ++ .../private/multiple-order/multiple-order.ts | 25 + .../rest/internal/private/order/order.spec.ts | 189 +-- .../rest/internal/private/order/order.ts | 50 +- .../private/position/position.spec.ts | 152 +- .../internal/private/position/position.ts | 75 +- .../rest/internal/public/bar/bar.spec.ts | 47 +- .../bitmex/rest/internal/public/bar/bar.ts | 20 +- .../public/instrument/instrument.spec.ts | 32 +- .../internal/public/instrument/instrument.ts | 19 +- .../rest/internal/public/orderbook/helpers.ts | 24 +- .../public/orderbook/orderbook.spec.ts | 32 +- .../internal/public/orderbook/orderbook.ts | 21 +- .../bitmex/rest/internal/rest-insider.spec.ts | 20 + .../bitmex/rest/internal/rest-insider.ts | 111 +- .../get-axios-observable-request.spec.ts | 18 + .../utils/get-axios-observable-request.ts | 34 + .../rest/internal/utils/get-response-error.ts | 20 + .../rest/internal/utils/get-rest-response.ts | 11 + .../bitmex/rest/internal/utils/index.ts | 3 + .../rest/internal/utils/validate-remaining.ts | 11 + .../exchanges/crypto/bitmex/rest/rest-base.ts | 100 +- .../exchanges/crypto/bitmex/rest/rest.spec.ts | 576 ++----- modules/exchanges/crypto/bitmex/rest/rest.ts | 29 +- .../crypto/bitmex/rest/types/index.ts | 2 - .../crypto/bitmex/rest/types/request.ts | 16 + .../crypto/bitmex/rest/types/response.ts | 37 - .../exchanges/crypto/bitmex/types/response.ts | 42 +- modules/exchanges/crypto/bitmex/yarn.lock | 1342 +++++++++++++++++ 39 files changed, 2274 insertions(+), 1124 deletions(-) create mode 100644 modules/exchanges/crypto/bitmex/common/get-channel-name.ts delete mode 100644 modules/exchanges/crypto/bitmex/common/helpers.ts create mode 100644 modules/exchanges/crypto/bitmex/common/transform-orderbook.ts rename modules/exchanges/crypto/bitmex/rest/{types/endpoint.ts => constants.ts} (58%) create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/index.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/multiple-order.spec.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/multiple-order.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/rest-insider.spec.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/utils/get-axios-observable-request.spec.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/utils/get-axios-observable-request.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/utils/get-response-error.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/utils/get-rest-response.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/utils/index.ts create mode 100644 modules/exchanges/crypto/bitmex/rest/internal/utils/validate-remaining.ts delete mode 100644 modules/exchanges/crypto/bitmex/rest/types/response.ts create mode 100644 modules/exchanges/crypto/bitmex/yarn.lock diff --git a/modules/exchanges/crypto/bitmex/common/authentication.ts b/modules/exchanges/crypto/bitmex/common/authentication.ts index ac02a19..e7dbd8e 100644 --- a/modules/exchanges/crypto/bitmex/common/authentication.ts +++ b/modules/exchanges/crypto/bitmex/common/authentication.ts @@ -1,14 +1,9 @@ +import { createHmac } from 'crypto'; + import { HttpMethod } from '@dripjs/types'; import { stringify } from 'qs'; -import { apiBasePath } from '../rest/types'; -import { isNodeJs } from './helpers'; - -// tslint:disable-next-line:no-var-requires -const createHmac = require('create-hmac'); - -// tslint:disable-next-line:no-var-requires -const urljoin = require('url-join'); +import { apiBasePath } from '../rest/constants'; /** * Sign a message. hex( HMAC_SHA256(secret, verb + url + nonce + data) ) @@ -42,39 +37,24 @@ export function getWSAuthQuery(apiKey: string, apiSecret: string): string { return stringify(query); } -export function getRestAuthHeaders( - method: HttpMethod, - baseUrl: string, - endpoint: string, - apiKey: string, - apiSecret: string, - data?: any, -): AuthHeaders { +export function getRestAuthHeaders(method: HttpMethod, endpoint: string, apiKey: string, apiSecret: string, data?: any): AuthHeaders { const query = method === HttpMethod.GET && Object.keys(data).length !== 0 ? `?${stringify(data)}` : ''; - const url = urljoin(apiBasePath, endpoint, query); + const url = `${apiBasePath}${endpoint}${query}`; // 3min timeout const expires = Math.round(Date.now() / 1000) + 60 * 3; const body = method === HttpMethod.GET ? '' : data; const signature = signMessage(apiSecret, method, url, expires, body); - const authHeaders: AuthHeaders = { + return { 'Content-Type': 'application/json', accept: 'application/json', 'api-expires': String(expires), 'api-key': apiKey, 'api-signature': signature, }; - // 当前环境为node时 - if (isNodeJs()) { - // 设置Origin - authHeaders.Origin = baseUrl; - } - - return authHeaders; } export interface AuthHeaders { - Origin?: string; 'Content-Type': string; accept: string; 'api-expires': string; diff --git a/modules/exchanges/crypto/bitmex/common/get-channel-name.ts b/modules/exchanges/crypto/bitmex/common/get-channel-name.ts new file mode 100644 index 0000000..d4fbd81 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/common/get-channel-name.ts @@ -0,0 +1,35 @@ +/** + * get channel name + * + * @param params + * + * eg1: + * input: { + * pair: 'XBTUSD', + * endPoint: RestPublicEndPoints.Trade, + * } + * output: 'trade:XBTUSD' + * + * eg2: + * input: { + * pair: ['XBTUSD', 'ETHUSD'], + * endPoint: RestPublicEndPoints.Trade, + * } + * output: ['trade:XBTUSD', 'trade:ETHUSD'] + */ +export function getChannelName(params: { pair?: string | string[]; endPoint: string }): string | string[] { + let subStr = `${params.endPoint}`; + if (params.pair) { + if (params.pair instanceof Array) { + const channels: string[] = []; + for (const p of params.pair) { + channels.push(`${params.endPoint}:${p}`); + } + + return channels; + } + subStr = `${params.endPoint}:${params.pair}`; + } + + return subStr; +} diff --git a/modules/exchanges/crypto/bitmex/common/helpers.ts b/modules/exchanges/crypto/bitmex/common/helpers.ts deleted file mode 100644 index 62fb9b2..0000000 --- a/modules/exchanges/crypto/bitmex/common/helpers.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { HttpHeaders } from '@dripjs/types'; - -import { endpoints } from '../rest/types'; -import { Config, RateLimit } from '../types'; - -export function getRateLimit(headers: HttpHeaders): RateLimit { - return { - remaining: Number(headers['x-ratelimit-remaining']), - reset: Number(headers['x-ratelimit-reset']), - limit: Number(headers['x-ratelimit-limit']), - }; -} - -/** - * get channel name - * - * @param params - * - * eg1: - * input: { - * pair: 'XBTUSD', - * endPoint: PublicEndPoints.Trade, - * } - * output: 'trade:XBTUSD' - * - * eg2: - * input: { - * pair: ['XBTUSD', 'ETHUSD'], - * endPoint: PublicEndPoints.Trade, - * } - * output: ['trade:XBTUSD', 'trade:ETHUSD'] - */ -export function getChannelName(params: { pair?: string | string[]; endPoint: string }): string | string[] { - let subStr = `${params.endPoint}`; - if (params.pair) { - if (params.pair instanceof Array) { - const channels: string[] = []; - for (const p of params.pair) { - channels.push(`${params.endPoint}:${p}`); - } - - return channels; - } - subStr = `${params.endPoint}:${params.pair}`; - } - - return subStr; -} - -/** - * 获取api的url - * @param cfg - */ -export function getRestApiUrl(cfg: Config): string { - if (cfg.apiUrl) { - return cfg.apiUrl; - } - - if (cfg.testnet) { - return endpoints.testnet; - } - - return endpoints.production; -} - -/** - * 判断是否为node.js环境 - */ -export function isNodeJs(): boolean { - return typeof process === 'object' && typeof process.versions === 'object' && typeof process.versions.node !== 'undefined'; -} diff --git a/modules/exchanges/crypto/bitmex/common/index.ts b/modules/exchanges/crypto/bitmex/common/index.ts index a6242bd..7d454e9 100644 --- a/modules/exchanges/crypto/bitmex/common/index.ts +++ b/modules/exchanges/crypto/bitmex/common/index.ts @@ -1,2 +1,3 @@ export * from './authentication'; -export * from './helpers'; +export * from './get-channel-name'; +export * from './transform-orderbook'; diff --git a/modules/exchanges/crypto/bitmex/common/test-helpers/receive-order-data.ts b/modules/exchanges/crypto/bitmex/common/test-helpers/receive-order-data.ts index eb0d541..04f9c39 100644 --- a/modules/exchanges/crypto/bitmex/common/test-helpers/receive-order-data.ts +++ b/modules/exchanges/crypto/bitmex/common/test-helpers/receive-order-data.ts @@ -1,7 +1,8 @@ import { testnetConfig } from '@dripjs/testing'; +import { Observable, Subject } from 'rxjs'; +import { mergeMap, take } from 'rxjs/operators'; -import { Order } from '../../rest/internal/private/order'; -import { Orderbook } from '../../rest/internal/public/orderbook'; +import { Order, Orderbook } from '../../rest/internal'; import { OrderResponse, OrderSide, OrderType } from '../../types'; /** @@ -10,25 +11,36 @@ import { OrderResponse, OrderSide, OrderType } from '../../types'; * @param handler 执行的特定方法 * @return void */ -export const receiveOrderData = async (symbol: string, handler: (order: OrderResponse) => Promise = async () => {}): Promise => { - const order = new Order(testnetConfig); - const orderbook = new Orderbook(testnetConfig); - const obRes = await orderbook.fetch({ - symbol, - depth: 10, - }); - const price = +obRes.orderbook.bids[4][0]; - const odRes = await order.create({ - symbol, - side: OrderSide.Buy, - price, - orderQty: 25, - ordType: OrderType.Limit, - }); - const result = await handler(odRes.order); - await order.cancel({ - orderID: odRes.order.orderID, - }); +export const receiveOrderData = (symbol: string, handler: (order: OrderResponse) => Promise): Observable => { + const remaining$ = new Subject(); + const order = new Order(testnetConfig, remaining$); + const orderbook = new Orderbook(testnetConfig, remaining$); - return result; + return orderbook + .fetch({ + symbol, + depth: 10, + }) + .pipe( + take(1), + mergeMap((res) => + order.create({ + symbol, + side: OrderSide.Buy, + price: +res.data.bids[4][0], + orderQty: 25, + ordType: OrderType.Limit, + }), + ), + mergeMap(async (oRes) => { + const res = await handler(oRes.data); + await order + .cancel({ + orderID: oRes.data.orderID, + }) + .toPromise(); + + return res; + }), + ); }; diff --git a/modules/exchanges/crypto/bitmex/common/transform-orderbook.ts b/modules/exchanges/crypto/bitmex/common/transform-orderbook.ts new file mode 100644 index 0000000..e522d1f --- /dev/null +++ b/modules/exchanges/crypto/bitmex/common/transform-orderbook.ts @@ -0,0 +1,19 @@ +import { OrderbookL2Response, OrderbookResponse } from '../types'; + +export function transformOrderbook(data: OrderbookResponse[]): OrderbookL2Response { + const orderbook: OrderbookL2Response = { + bids: [], + asks: [], + }; + for (const item of data) { + const orderbookItem: [string, string] = [`${item.price}`, `${item.size}`]; + if (item.side === 'Sell') { + orderbook.asks.push(orderbookItem); + } else { + orderbook.bids.push(orderbookItem); + } + } + orderbook.asks.reverse(); + + return orderbook; +} diff --git a/modules/exchanges/crypto/bitmex/package.json b/modules/exchanges/crypto/bitmex/package.json index 95600f9..26775f1 100644 --- a/modules/exchanges/crypto/bitmex/package.json +++ b/modules/exchanges/crypto/bitmex/package.json @@ -39,8 +39,10 @@ }, "homepage": "https://github.com/drip-trader/dripjs/tree/master/modules/exchanges/crypto/bitmex#readme", "devDependencies": { + "@dripjs/testing": "^0.1.20", "@types/qs": "^6.5.3", "@types/ws": "6.0.1", + "axios-mock-adapter": "^1.19.0", "rollup": "^1.12.3", "rollup-plugin-commonjs": "^10.0.0", "rollup-plugin-json": "^4.0.0", diff --git a/modules/exchanges/crypto/bitmex/rest/types/endpoint.ts b/modules/exchanges/crypto/bitmex/rest/constants.ts similarity index 58% rename from modules/exchanges/crypto/bitmex/rest/types/endpoint.ts rename to modules/exchanges/crypto/bitmex/rest/constants.ts index 70277b2..03500d7 100644 --- a/modules/exchanges/crypto/bitmex/rest/types/endpoint.ts +++ b/modules/exchanges/crypto/bitmex/rest/constants.ts @@ -1,6 +1,16 @@ +/** + * rest请求最小剩余可用数 + */ +export const MINIMUM_REMAINING_NUM = 15; + +/** + * rest请求最大剩余可用数 + */ +export const MAX_REMAINING_NUM = 60; + export const endpoints = { - production: 'https://www.bitmex.com/', - testnet: 'https://testnet.bitmex.com/', + production: 'https://www.bitmex.com', + testnet: 'https://testnet.bitmex.com', }; export const apiBasePath = '/api/v1/'; @@ -21,9 +31,16 @@ export enum PublicEndPoints { // 要求进行身份验证的连接端点集 export enum PrivateEndPoints { - /** 邀请人状态,已邀请用户及分红比率 */ /** 你委托的更新 */ Order = 'order', + /** 复数订单 */ + OrderBulk = 'order/bulk', + /** 全部订单 */ + OrderAll = 'order/all', /** 你仓位的更新 */ Position = 'position', + /** 仓位杠杆的更新 */ + PositionLeverage = 'position/leverage', + /** 报价 */ + Quote = 'quote', } diff --git a/modules/exchanges/crypto/bitmex/rest/internal/private/index.ts b/modules/exchanges/crypto/bitmex/rest/internal/private/index.ts index 48130ad..334a3be 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/private/index.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/private/index.ts @@ -1,2 +1,3 @@ export * from './order'; +export * from './multiple-order'; export * from './position'; diff --git a/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/index.ts b/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/index.ts new file mode 100644 index 0000000..7a3f956 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/index.ts @@ -0,0 +1 @@ +export * from './multiple-order'; diff --git a/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/multiple-order.spec.ts b/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/multiple-order.spec.ts new file mode 100644 index 0000000..bcf2936 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/multiple-order.spec.ts @@ -0,0 +1,97 @@ +import { testnetConfig } from '@dripjs/testing'; +import { Subject } from 'rxjs'; +import { flatMap, take } from 'rxjs/operators'; + +import { OrderSide, OrderType } from '../../../../types'; +import { RestMultipleOrderRequest, RestOrderRequest } from '../../../types'; +import { Orderbook } from '../../public'; +import { MultipleOrder } from './multiple-order'; + +describe('Bitmex Rest Position', () => { + const pair = 'XBTUSD'; + const remaining$ = new Subject(); + const multipleOrder = new MultipleOrder(testnetConfig, remaining$); + const orderbook = new Orderbook(testnetConfig, remaining$); + let orderId: string; + let orderId2: string; + let price: number; + + it('create order', (done) => { + orderbook + .fetch({ + symbol: pair, + depth: 5, + }) + .pipe( + take(1), + flatMap((orderbookRes) => { + price = +orderbookRes.data.bids[4][0]; + const request1: Partial = { + symbol: pair, + side: OrderSide.Buy, + price, + orderQty: 25, + ordType: OrderType.Limit, + }; + const request2: Partial = { + symbol: pair, + side: OrderSide.Buy, + price: price - 1, + orderQty: 25, + ordType: OrderType.Limit, + }; + const request: RestMultipleOrderRequest = { + orders: [request1, request2], + }; + + return multipleOrder.create(request); + }), + ) + .subscribe((res) => { + orderId = res.data[0].orderID; + orderId2 = res.data[1].orderID; + expect(res.data.length).toEqual(2); + done(); + }); + }); + + it('update order', (done) => { + const request1: Partial = { + orderID: orderId, + price: price - 1, + }; + const request2: Partial = { + orderID: orderId2, + price: price - 2, + }; + const request: RestMultipleOrderRequest = { + orders: [request1, request2], + }; + multipleOrder.update(request).subscribe((res) => { + expect(res).toBeDefined(); + }); + done(); + }); + + it('cancel order', (done) => { + multipleOrder + .cancel({ + filter: { side: OrderSide.Buy }, + }) + .subscribe((res) => { + expect(res.data.length).toEqual(2); + done(); + }); + }); + + it('cancel stop order', (done) => { + multipleOrder + .cancel({ + symbol: pair, + }) + .subscribe((res) => { + console.log(res); + done(); + }); + }); +}); diff --git a/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/multiple-order.ts b/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/multiple-order.ts new file mode 100644 index 0000000..b7d0dab --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/private/multiple-order/multiple-order.ts @@ -0,0 +1,25 @@ +import { HttpMethod } from '@dripjs/types'; +import { Observable, Subject } from 'rxjs'; + +import { Config, OrderResponse, RestResponse } from '../../../../types'; +import { PrivateEndPoints } from '../../../constants'; +import { RestCancelMultipleOrderRequest, RestMultipleOrderRequest } from '../../../types'; +import { RestInsider } from '../../rest-insider'; + +export class MultipleOrder extends RestInsider { + constructor(config: Config, remaining$: Subject) { + super(config, PrivateEndPoints.OrderBulk, remaining$); + } + + create(request: RestMultipleOrderRequest): Observable> { + return this.request(HttpMethod.POST, request); + } + + update(request: RestMultipleOrderRequest): Observable> { + return this.request(HttpMethod.PUT, request); + } + + cancel(request: Partial): Observable> { + return this.requestWithEndpoint(PrivateEndPoints.OrderAll, HttpMethod.DELETE, request); + } +} diff --git a/modules/exchanges/crypto/bitmex/rest/internal/private/order/order.spec.ts b/modules/exchanges/crypto/bitmex/rest/internal/private/order/order.spec.ts index 77b5ebc..c9f0b0a 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/private/order/order.spec.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/private/order/order.spec.ts @@ -1,157 +1,98 @@ -import { isPositive } from '@dripjs/common'; -import { - assertExisitingColumns, - isUuid, - overrideTimestampColumns, - overrideValue, - testnetConfig, - testnetReadonlyConfig, -} from '@dripjs/testing'; +import { testnetConfig } from '@dripjs/testing'; +import { Subject } from 'rxjs'; +import { flatMap } from 'rxjs/internal/operators'; +import { take } from 'rxjs/operators'; import { OrderSide, OrderStatus, OrderType } from '../../../../types'; +import { MAX_REMAINING_NUM } from '../../../constants'; import { RestFetchOrderRequest, RestOrderRequest } from '../../../types'; -import { Orderbook } from '../../public/orderbook'; +import { Orderbook } from '../../public'; import { Order } from './order'; -describe('Bitmex RestInsider Order', () => { +describe('Bitmex Rest Position', () => { const pair = 'XBTUSD'; - const order = new Order(testnetConfig); - const orderbook = new Orderbook(testnetReadonlyConfig); + const remaining$ = new Subject(); + const order = new Order(testnetConfig, remaining$); + const orderbook = new Orderbook(testnetConfig, remaining$); let orderId: string; let price: number; - it('create order', async () => { - const orderbookRes = await orderbook.fetch({ - symbol: pair, - depth: 5, - }); - price = +orderbookRes.orderbook.bids[4][0]; - const request: Partial = { + it('create order', (done) => { + orderbook + .fetch({ + symbol: pair, + depth: 5, + }) + .pipe( + take(1), + flatMap((orderbookRes) => { + price = +orderbookRes.data.bids[4][0]; + const request: Partial = { + symbol: pair, + side: OrderSide.Buy, + price, + orderQty: 25, + ordType: OrderType.Limit, + }; + + return order.create(request); + }), + ) + .subscribe((res) => { + orderId = res.data.orderID; + expect(res.data).toBeDefined(); + expect(res.rateLimit.limit).toEqual(MAX_REMAINING_NUM); + done(); + }); + }); + + it('fetch order', (done) => { + const request: Partial = { symbol: pair, - side: OrderSide.Buy, - price, - orderQty: 25, - ordType: OrderType.Limit, + filter: { + orderID: orderId, + }, }; - const res = await order.create(request); - orderId = res.order.orderID; - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - order: { - orderID: isUuid, - account: isPositive, - symbol: 'XBTUSD', - side: 'Buy', - simpleOrderQty: null, - orderQty: 25, - price: isPositive, - displayQty: null, - stopPx: null, - pegOffsetValue: null, - pegPriceType: '', - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Limit', - timeInForce: 'GoodTillCancel', - execInst: '', - contingencyType: '', - exDestination: 'XBME', - ordStatus: 'New', - triggered: '', - workingIndicator: true, - ordRejReason: '', - simpleLeavesQty: null, - leavesQty: 25, - simpleCumQty: null, - cumQty: 0, - avgPx: null, - multiLegReportingType: 'SingleSecurity', - text: 'Submitted via API.', - transactTime: overrideValue, - timestamp: overrideValue, - }, - }), - ).not.toThrow(); + order.fetch(request).subscribe((res) => { + expect(res.data[0].price).toEqual(price); + done(); + }); }); - it('fetch order', async () => { + it.skip('fetch multiple order', (done) => { + const order2 = new Order(testnetConfig, remaining$); const request: Partial = { symbol: pair, filter: { - orderID: orderId, + orderID: ['989e9b5d-6f4c-a9fd-3f68-1e4faee265e2', '01870ed8-92b4-73fd-b47e-0b1ed8017c11'], }, }; - const res = await order.fetch(request); - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [ - { - orderID: isUuid, - clOrdID: '', - clOrdLinkID: '', - account: isPositive, - symbol: 'XBTUSD', - side: 'Buy', - simpleOrderQty: null, - orderQty: 25, - price, - displayQty: null, - stopPx: null, - pegOffsetValue: null, - pegPriceType: '', - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Limit', - timeInForce: 'GoodTillCancel', - execInst: '', - contingencyType: '', - exDestination: 'XBME', - ordStatus: 'New', - triggered: '', - workingIndicator: true, - ordRejReason: '', - simpleLeavesQty: null, - leavesQty: 25, - simpleCumQty: null, - cumQty: 0, - avgPx: null, - multiLegReportingType: 'SingleSecurity', - text: 'Submitted via API.', - transactTime: overrideValue, - timestamp: overrideValue, - }, - ], - }), - ).not.toThrow(); + order2.fetch(request).subscribe((res) => { + expect(res.data[0].price).toEqual(price); + done(); + }); }); - it('update order', async () => { + it('update order', (done) => { const request: Partial = { orderID: orderId, price: price - 1, }; - - const res = await order.update(request); - expect(res.order.price).toEqual(price - 1); + order.update(request).subscribe((res) => { + expect(res.data.price).toEqual(price - 1); + done(); + }); }); - it('cancel order', async () => { + it('cancel order', (done) => { const request: Partial = { orderID: orderId, }; - const res = await order.cancel(request); - expect(res.order.ordStatus).toEqual(OrderStatus.Canceled); + order.cancel(request).subscribe((res) => { + expect(res.data[0].ordStatus).toEqual(OrderStatus.Canceled); + done(); + }); }); }); diff --git a/modules/exchanges/crypto/bitmex/rest/internal/private/order/order.ts b/modules/exchanges/crypto/bitmex/rest/internal/private/order/order.ts index af3b820..4c12d81 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/private/order/order.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/private/order/order.ts @@ -1,51 +1,29 @@ import { HttpMethod } from '@dripjs/types'; +import { Observable, Subject } from 'rxjs'; -import { Config, OrderResponse } from '../../../../types'; -import { PrivateEndPoints, RestFetchOrderRequest, RestOrderRequest, RestOrderResponse, RestOrdersResponse } from '../../../types'; +import { Config, OrderResponse, RestResponse } from '../../../../types'; +import { PrivateEndPoints } from '../../../constants'; +import { RestFetchOrderRequest, RestOrderRequest } from '../../../types'; import { RestInsider } from '../../rest-insider'; export class Order extends RestInsider { - constructor(config: Config) { - super(config, PrivateEndPoints.Order); + constructor(config: Config, remaining$: Subject) { + super(config, PrivateEndPoints.Order, remaining$); } - async create(request: Partial): Promise { - const res = await this.request(HttpMethod.POST, request); - - return { - ratelimit: res.ratelimit, - order: res.body, - error: res.error, - }; + create(request: Partial): Observable> { + return this.request(HttpMethod.POST, request); } - async fetch(request: Partial): Promise { - const res = await this.request(HttpMethod.GET, request); - - return { - ratelimit: res.ratelimit, - orders: res.body, - error: res.error, - }; + fetch(request: Partial): Observable> { + return this.request(HttpMethod.GET, request); } - async update(request: Partial): Promise { - const res = await this.request(HttpMethod.PUT, request); - - return { - ratelimit: res.ratelimit, - order: res.body, - error: res.error, - }; + update(request: Partial): Observable> { + return this.request(HttpMethod.PUT, request); } - async cancel(request: Partial): Promise { - const res = await this.request(HttpMethod.DELETE, request); - - return { - ratelimit: res.ratelimit, - order: res.body[0], - error: res.error, - }; + cancel(request: Partial): Observable> { + return this.request(HttpMethod.DELETE, request); } } diff --git a/modules/exchanges/crypto/bitmex/rest/internal/private/position/position.spec.ts b/modules/exchanges/crypto/bitmex/rest/internal/private/position/position.spec.ts index acb9dd5..baa4396 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/private/position/position.spec.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/private/position/position.spec.ts @@ -1,39 +1,29 @@ import { isPositive } from '@dripjs/common'; import { assertExisitingColumns, overrideTimestampColumns, overrideValue, testnetConfig } from '@dripjs/testing'; +import { Subject } from 'rxjs'; import { OrderSide } from '../../../../types'; import { Position } from './position'; -describe('Bitmex RestInsider Position', () => { +describe('Bitmex Rest Position', () => { const pair = 'XBTUSD'; const amount = 25; let position: Position; + const remaining$ = new Subject(); beforeAll(async () => { - position = new Position(testnetConfig); - await position.removeAll(); + position = new Position(testnetConfig, remaining$); }); - afterAll(async () => { - await position.removeAll(); - }); - - it('create position', async () => { + it('create position', (done) => { const side = OrderSide.Buy; - const res = await position.create(pair, side, amount); - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [ + position.create(pair, side, amount).subscribe((res) => { + expect(() => + assertExisitingColumns(overrideTimestampColumns(res.data), [ { account: isPositive, symbol: pair, leverage: isPositive, - crossMargin: false, currentQty: amount, isOpen: true, markPrice: isPositive, @@ -47,65 +37,67 @@ describe('Bitmex RestInsider Position', () => { openingTimestamp: overrideValue, timestamp: overrideValue, }, - ], - error: undefined, - }), - ).not.toThrow(); + ]), + ).not.toThrow(); + done(); + }); }); - it('fetch position', async () => { - const res = await position.fetch({ - filter: { - symbol: pair, - }, - }); - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, + it('fetch position', (done) => { + position + .fetch({ + filter: { + symbol: pair, }, - orders: [ - { - account: isPositive, - symbol: pair, - leverage: isPositive, - crossMargin: false, - currentQty: amount, - isOpen: true, - markPrice: isPositive, - lastPrice: isPositive, - avgCostPrice: isPositive, - avgEntryPrice: isPositive, - marginCallPrice: isPositive, - liquidationPrice: isPositive, - bankruptPrice: isPositive, - currentTimestamp: overrideValue, - openingTimestamp: overrideValue, - timestamp: overrideValue, - }, - ], - error: undefined, - }), - ).not.toThrow(); + }) + .subscribe((res) => { + expect(() => + assertExisitingColumns(overrideTimestampColumns(res.data), [ + { + account: isPositive, + symbol: pair, + leverage: isPositive, + currentQty: amount, + isOpen: true, + markPrice: isPositive, + lastPrice: isPositive, + avgCostPrice: isPositive, + avgEntryPrice: isPositive, + marginCallPrice: isPositive, + liquidationPrice: isPositive, + bankruptPrice: isPositive, + currentTimestamp: overrideValue, + openingTimestamp: overrideValue, + timestamp: overrideValue, + }, + ]), + ).not.toThrow(); + done(); + }); }); - it('remove position', async () => { - const res = await position.remove(pair); - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [ + it('update position leverage', (done) => { + position + .updateLeverage({ + symbol: pair, + leverage: 4.15, + }) + .subscribe((res) => { + console.log(res); + done(); + }); + }); + + it('remove position', (done) => { + position.remove(pair).subscribe((res) => { + expect(() => + assertExisitingColumns(overrideTimestampColumns(res.data), [ { account: isPositive, symbol: pair, leverage: isPositive, - crossMargin: false, + isOpen: false, + markPrice: null, lastPrice: null, avgCostPrice: null, avgEntryPrice: null, @@ -116,25 +108,9 @@ describe('Bitmex RestInsider Position', () => { openingTimestamp: overrideValue, timestamp: overrideValue, }, - ], - error: undefined, - }), - ).not.toThrow(); - }); - - it('remove all position', async () => { - await position.removeAll(); - const res = await position.fetch({ filter: { isOpen: true } }); - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [], - error: undefined, - }), - ).not.toThrow(); + ]), + ).not.toThrow(); + done(); + }); }); }); diff --git a/modules/exchanges/crypto/bitmex/rest/internal/private/position/position.ts b/modules/exchanges/crypto/bitmex/rest/internal/private/position/position.ts index 6bfb63b..2af0936 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/private/position/position.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/private/position/position.ts @@ -1,66 +1,71 @@ -import { sleep } from '@dripjs/common'; import { HttpMethod } from '@dripjs/types'; +import { Observable, Subject } from 'rxjs'; +import { delay, flatMap, take } from 'rxjs/operators'; -import { Config, ExecInst, OrderSide, OrderType, PositionResponse } from '../../../../types'; -import { PrivateEndPoints, RestFetchPositionRequest, RestOrderRequest, RestPositionsResponse } from '../../../types'; +import { Config, ExecInst, OrderSide, OrderType, PositionResponse, RestResponse } from '../../../../types'; +import { PrivateEndPoints } from '../../../constants'; +import { RestFetchPositionRequest, RestLeverageRequest, RestOrderRequest } from '../../../types'; import { RestInsider } from '../../rest-insider'; import { Order } from '../order'; export class Position extends RestInsider { private readonly order: Order; - constructor(config: Config) { - super(config, PrivateEndPoints.Position); - this.order = new Order(config); + constructor(config: Config, remaining$: Subject) { + super(config, PrivateEndPoints.Position, remaining$); + this.order = new Order(config, remaining$); } - async fetch(request: Partial): Promise { - const res = await this.request(HttpMethod.GET, request); - - return { - ratelimit: res.ratelimit, - orders: res.body, - error: res.error, - }; + fetch(request: Partial): Observable> { + return this.request(HttpMethod.GET, request); } - async create(pair: string, side: OrderSide, amount: number): Promise { + create(pair: string, side: OrderSide, amount: number): Observable> { const orderParams: Partial = { symbol: pair, side, ordType: OrderType.Market, orderQty: amount, }; - await this.order.create(orderParams); - await sleep(500); - return this.fetch({ - filter: { - symbol: pair, - }, - }); + return this.order.create(orderParams).pipe( + take(1), + delay(500), + flatMap(() => + this.fetch({ + filter: { + symbol: pair, + }, + }), + ), + ); } - async remove(pair: string): Promise { + /** + * 利用市场价触发后平仓 + * @param pair + */ + remove(pair: string): Observable> { const orderParams: Partial = { symbol: pair, ordType: OrderType.Market, execInst: ExecInst.Close, }; - await this.order.create(orderParams); - await sleep(500); - return this.fetch({ - filter: { - symbol: pair, - }, - }); + return this.order.create(orderParams).pipe( + take(1), + delay(500), + flatMap(() => + this.fetch({ + filter: { + symbol: pair, + }, + }), + ), + ); } - async removeAll(): Promise { - const res = await this.fetch({ filter: { isOpen: true } }); - for (const position of res.orders) { - await this.remove(position.symbol); - } + updateLeverage(request: RestLeverageRequest): Observable> { + return this.requestWithEndpoint(PrivateEndPoints.PositionLeverage, HttpMethod.POST, request); } } diff --git a/modules/exchanges/crypto/bitmex/rest/internal/public/bar/bar.spec.ts b/modules/exchanges/crypto/bitmex/rest/internal/public/bar/bar.spec.ts index 3ee7795..334e1c2 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/public/bar/bar.spec.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/public/bar/bar.spec.ts @@ -1,16 +1,49 @@ import { testnetConfig } from '@dripjs/testing'; +import { Subject } from 'rxjs'; +import { switchMap, tap } from 'rxjs/operators'; import { Resolution } from '../../../../types'; +import { MAX_REMAINING_NUM } from '../../../constants'; import { Bar } from './bar'; -describe('Bitmex RestInsider Bar', () => { - const bar = new Bar(testnetConfig); +import moment = require('moment'); + +describe('Bitmex Rest Bar', () => { + const remaining$ = new Subject(); + const bar = new Bar(testnetConfig, remaining$); it('fetch instrument', async () => { - const res = await bar.fetch({ - binSize: Resolution.day, - symbol: 'XBTUSD', - }); - expect(res.bars.length).toEqual(100); + const res = await bar + .fetch({ + binSize: Resolution.day, + symbol: 'XBTUSD', + }) + .toPromise(); + expect(res.data.length).toEqual(100); + expect(res.rateLimit.limit).toEqual(MAX_REMAINING_NUM); + }); + + it('should cancel request', (done) => { + const sub$ = new Subject(); + sub$ + .pipe( + tap((n) => console.log(`run at: ${moment()}`)), + switchMap(() => + bar.fetch({ + binSize: Resolution.day, + symbol: 'XBTUSD', + }), + ), + ) + .subscribe((res) => { + console.log(res.rateLimit); + done(); + }); + sub$.next(); + sub$.next(); + sub$.next(); + sub$.next(); + sub$.next(); + sub$.next(); }); }); diff --git a/modules/exchanges/crypto/bitmex/rest/internal/public/bar/bar.ts b/modules/exchanges/crypto/bitmex/rest/internal/public/bar/bar.ts index 980f308..78ca8e1 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/public/bar/bar.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/public/bar/bar.ts @@ -1,21 +1,17 @@ import { HttpMethod } from '@dripjs/types'; +import { Observable, Subject } from 'rxjs'; -import { BarResponse, Config } from '../../../../types'; -import { PublicEndPoints, RestBarRequest, RestBarResponse } from '../../../types'; +import { BarResponse, Config, RestResponse } from '../../../../types'; +import { PublicEndPoints } from '../../../constants'; +import { RestBarRequest } from '../../../types'; import { RestInsider } from '../../rest-insider'; export class Bar extends RestInsider { - constructor(config: Config) { - super(config, PublicEndPoints.TradeBucketed); + constructor(config: Config, remaining$: Subject) { + super(config, PublicEndPoints.TradeBucketed, remaining$); } - async fetch(request: RestBarRequest): Promise { - const res = await this.request(HttpMethod.GET, request); - - return { - ratelimit: res.ratelimit, - bars: res.body, - error: res.error, - }; + fetch(request: RestBarRequest): Observable> { + return this.request(HttpMethod.GET, request); } } diff --git a/modules/exchanges/crypto/bitmex/rest/internal/public/instrument/instrument.spec.ts b/modules/exchanges/crypto/bitmex/rest/internal/public/instrument/instrument.spec.ts index 05b4a25..7c0d02f 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/public/instrument/instrument.spec.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/public/instrument/instrument.spec.ts @@ -1,20 +1,30 @@ import { testnetConfig } from '@dripjs/testing'; +import { Subject } from 'rxjs'; +import { MAX_REMAINING_NUM, MINIMUM_REMAINING_NUM } from '../../../constants'; import { Instrument } from './instrument'; -describe('Bitmex RestInsider Instrument', () => { - const instrument = new Instrument(testnetConfig); +describe('Bitmex Rest Instrument', () => { + const remaining$ = new Subject(); + const instrument = new Instrument(testnetConfig, remaining$); - it('fetch instrument', async () => { - const res = await instrument.fetch(); - expect(res.instruments.length).toBeGreaterThan(0); + it('fetch instrument', (done) => { + instrument.fetch().subscribe((res) => { + expect(res.data.length).toBeGreaterThan(0); + expect(res.rateLimit.limit).toEqual(MAX_REMAINING_NUM); + done(); + }); }); - it('fetch instrument of remaining < 20', async () => { - (instrument).remaining = 10; - const res = await instrument.fetch(); - expect(res.error).toBeDefined(); - expect(res.error!.name).toEqual('validate failed (remaining)'); - expect(res.error!.message).toEqual('The remaining is less than 20'); + it('fetch instrument of remaining < mininum', (done) => { + const remaining = MINIMUM_REMAINING_NUM - 1; + remaining$.next(remaining); + instrument.fetch().subscribe( + (res) => {}, + (error) => { + expect(error.message).toEqual(`The remaining(${remaining}) is less than ${MINIMUM_REMAINING_NUM}`); + done(); + }, + ); }); }); diff --git a/modules/exchanges/crypto/bitmex/rest/internal/public/instrument/instrument.ts b/modules/exchanges/crypto/bitmex/rest/internal/public/instrument/instrument.ts index 8632281..f0ab10b 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/public/instrument/instrument.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/public/instrument/instrument.ts @@ -1,24 +1,19 @@ import { HttpMethod } from '@dripjs/types'; +import { Observable, Subject } from 'rxjs'; -import { Config, InstrumentResponse } from '../../../../types'; -import { PublicEndPoints, RestInstrumentResponse } from '../../../types'; +import { Config, InstrumentResponse, RestResponse } from '../../../../types'; +import { PublicEndPoints } from '../../../constants'; import { RestInsider } from '../../rest-insider'; /** * 产品更新,包括交易量以及报价 */ export class Instrument extends RestInsider { - constructor(config: Config) { - super(config, PublicEndPoints.InstrumentActive); + constructor(config: Config, remaining$: Subject) { + super(config, PublicEndPoints.InstrumentActive, remaining$); } - async fetch(): Promise { - const res = await this.request(HttpMethod.GET, {}); - - return { - ratelimit: res.ratelimit, - instruments: res.body, - error: res.error, - }; + fetch(): Observable> { + return this.request(HttpMethod.GET, {}); } } diff --git a/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/helpers.ts b/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/helpers.ts index 202aec2..76b96b9 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/helpers.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/helpers.ts @@ -1,25 +1,13 @@ -import { OrderbookL2Response, OrderbookResponse } from '../../../../types'; +import { transformOrderbook } from '../../../../common'; +import { OrderbookL2Response, OrderbookResponse, RestResponse } from '../../../../types'; /** * transform raw websocket data to OrderbookL2Response * @param source raw websocket data */ -export function transform(source: OrderbookResponse[]): OrderbookL2Response { - const orderbook: OrderbookL2Response = { - bids: [], - asks: [], +export function transform(source: RestResponse): RestResponse { + return { + rateLimit: source.rateLimit, + data: transformOrderbook(source.data), }; - - for (const item of source) { - const orderbookItem: [string, string] = [`${item.price}`, `${item.size}`]; - if (item.side === 'Sell') { - orderbook.asks.push(orderbookItem); - } else { - orderbook.bids.push(orderbookItem); - } - } - - orderbook.asks.reverse(); - - return orderbook; } diff --git a/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/orderbook.spec.ts b/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/orderbook.spec.ts index f2b190a..c797e41 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/orderbook.spec.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/orderbook.spec.ts @@ -1,20 +1,38 @@ import { testnetReadonlyConfig } from '@dripjs/testing'; +import { Subject } from 'rxjs'; +import { tap } from 'rxjs/operators'; import { RestOrderbookRequest } from '../../../types'; import { Orderbook } from './orderbook'; describe.skip('Bitmex RestInsider Orderbook', () => { + const remaining$ = new Subject(); const pair = 'XBTUSD'; - const orderbook = new Orderbook(testnetReadonlyConfig); - it('fetch orderbook', async () => { + const orderbook = new Orderbook(testnetReadonlyConfig, remaining$); + it('fetch orderbook', (done) => { const request: RestOrderbookRequest = { symbol: pair, depth: 25, }; - - const res = await orderbook.fetch(request); - expect(res.orderbook.bids.length).toEqual(25); - expect(res.orderbook.asks.length).toEqual(25); - expect(res.ratelimit.limit).toEqual(300); + console.log({ + data: request, + event: '查询订单薄', + }); + orderbook + .fetch(request) + .pipe( + tap((res) => { + console.log({ + data: JSON.stringify(res), + event: '查询订单薄-结果', + }); + }), + ) + .subscribe((res) => { + expect(res.data.bids.length).toEqual(25); + expect(res.data.asks.length).toEqual(25); + expect(res.rateLimit.limit).toEqual(60); + done(); + }); }); }); diff --git a/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/orderbook.ts b/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/orderbook.ts index 3283672..a846b99 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/orderbook.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/public/orderbook/orderbook.ts @@ -1,22 +1,19 @@ import { HttpMethod } from '@dripjs/types'; +import { Observable, Subject } from 'rxjs'; +import { map } from 'rxjs/operators'; -import { Config, OrderbookResponse } from '../../../../types'; -import { PublicEndPoints, RestOrderbookL2Response, RestOrderbookRequest } from '../../../types'; +import { Config, OrderbookL2Response, OrderbookResponse, RestResponse } from '../../../../types'; +import { PublicEndPoints } from '../../../constants'; +import { RestOrderbookRequest } from '../../../types'; import { RestInsider } from '../../rest-insider'; import { transform } from './helpers'; export class Orderbook extends RestInsider { - constructor(config: Config) { - super(config, PublicEndPoints.OrderBookL2); + constructor(config: Config, remaining$: Subject) { + super(config, PublicEndPoints.OrderBookL2, remaining$); } - async fetch(request: RestOrderbookRequest): Promise { - const res = await this.request(HttpMethod.GET, request); - - return { - ratelimit: res.ratelimit, - orderbook: transform(res.body), - error: res.error, - }; + fetch(request: RestOrderbookRequest): Observable> { + return this.request(HttpMethod.GET, request).pipe(map(transform)); } } diff --git a/modules/exchanges/crypto/bitmex/rest/internal/rest-insider.spec.ts b/modules/exchanges/crypto/bitmex/rest/internal/rest-insider.spec.ts new file mode 100644 index 0000000..68a5b25 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/rest-insider.spec.ts @@ -0,0 +1,20 @@ +import { testnetConfig } from '@dripjs/testing'; +import { HttpMethod } from '@dripjs/types'; +import { Subject } from 'rxjs'; + +import { PrivateEndPoints } from '../constants'; +import { RestInsider } from './rest-insider'; + +describe('RestInsider', () => { + let restInsider: RestInsider; + const remaining$ = new Subject(); + + beforeAll(() => { + restInsider = new RestInsider(testnetConfig, PrivateEndPoints.Quote, remaining$); + }); + + it('should request', async () => { + const res = await restInsider.request(HttpMethod.GET, { symbol: 'XBTUSD' }).toPromise(); + expect(res).toBeDefined(); + }); +}); diff --git a/modules/exchanges/crypto/bitmex/rest/internal/rest-insider.ts b/modules/exchanges/crypto/bitmex/rest/internal/rest-insider.ts index 10fbba3..6b4168f 100644 --- a/modules/exchanges/crypto/bitmex/rest/internal/rest-insider.ts +++ b/modules/exchanges/crypto/bitmex/rest/internal/rest-insider.ts @@ -1,91 +1,62 @@ -import { HttpHeaders, HttpMethod } from '@dripjs/types'; -import Axios, { AxiosRequestConfig } from 'axios'; +import { HttpMethod } from '@dripjs/types'; +import { AxiosRequestConfig } from 'axios'; import { stringify } from 'qs'; +import { Observable, Subject, throwError } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; -import { getRateLimit, getRestApiUrl, getRestAuthHeaders } from '../../common'; -import { Config, ErrorResponse, RestResponse } from '../../types'; -import { PrivateEndPoints, PublicEndPoints, apiBasePath } from '../types'; - -// tslint:disable-next-line:no-var-requires -const urljoin = require('url-join'); +import { getRestAuthHeaders } from '../../common'; +import { Config } from '../../types'; +import { MAX_REMAINING_NUM, PrivateEndPoints, PublicEndPoints, apiBasePath, endpoints } from '../constants'; +import { RestResponse } from '../types'; +import { getAxiosObservableRequest, getRestResponse, validateRemaining } from './utils'; export class RestInsider { /**可用请求数 */ - private remaining = 300; + private remaining = MAX_REMAINING_NUM; + private readonly remaining$: Subject; - constructor(private readonly config: Config, private readonly endpoint: PrivateEndPoints | PublicEndPoints) { + constructor(private readonly config: Config, private readonly endpoint: PrivateEndPoints | PublicEndPoints, remaining$: Subject) { if (!this.config.apiKey || this.config.apiKey === 'undefined') { this.config.apiKey = ''; } if (!this.config.apiSecret || this.config.apiSecret === 'undefined') { this.config.apiSecret = ''; } - // 每30分钟重置remaining - setTimeout(() => (this.remaining = 300), 30 * 60 * 1000); + remaining$.subscribe((amount) => (this.remaining = amount)); + this.remaining$ = remaining$; } - protected async request(method: HttpMethod, data: any): Promise { - const validate = this.validate(); - if (validate.error) { - return { - ratelimit: { - remaining: this.remaining, - reset: 0, - limit: 300, - }, - body: {}, - error: validate.error, - }; - } - - const baseUrl = getRestApiUrl(this.config); - const authHeaders = getRestAuthHeaders(method, baseUrl, this.endpoint, this.config.apiKey, this.config.apiSecret, data); - const request: AxiosRequestConfig = { - method, - headers: { - ...authHeaders, - }, - }; - let query = ''; - if (method !== HttpMethod.GET) { - request.data = data; - } else { - query = Object.keys(data).length !== 0 ? `?${stringify(data)}` : ''; - } - - let url = urljoin(baseUrl, apiBasePath, this.endpoint, query); - if (this.config.corsProxy) { - url = urljoin(this.config.corsProxy, url); - } + request(method: HttpMethod, data: any): Observable> { + return this.requestWithEndpoint(this.endpoint, method, data); + } + protected requestWithEndpoint( + endpoint: PrivateEndPoints | PublicEndPoints, + method: HttpMethod, + data: any, + ): Observable> { try { - const response = await Axios(url, request); - - const ratelimit = getRateLimit(response.headers); - this.remaining = ratelimit.remaining; - - return { - ratelimit, - body: response.data, + validateRemaining(this.remaining); + const authHeaders = getRestAuthHeaders(method, endpoint, this.config.apiKey, this.config.apiSecret, data); + const request: AxiosRequestConfig = { + method, + headers: { ...authHeaders }, }; + let query = ''; + if (method !== HttpMethod.GET) { + request.data = data; + } else { + query = Object.keys(data).length !== 0 ? `?${stringify(data)}` : ''; + } + const baseUrl = this.config.testnet ? endpoints.testnet : endpoints.production; + const url = `${baseUrl}${apiBasePath}${endpoint}${query}`; + + return getAxiosObservableRequest(url, this.remaining$, request).pipe( + filter((o) => !!o), + map(getRestResponse), + ); } catch (error) { - return { - ratelimit: getRateLimit(error.response.headers), - body: {}, - error: error.response.data.error, - }; - } - } - - private validate(): ErrorResponse { - const errorRes: ErrorResponse = {}; - if (this.remaining < 20) { - errorRes.error = { - name: 'validate failed (remaining)', - message: 'The remaining is less than 20', - }; + return throwError(error); } - - return errorRes; } } diff --git a/modules/exchanges/crypto/bitmex/rest/internal/utils/get-axios-observable-request.spec.ts b/modules/exchanges/crypto/bitmex/rest/internal/utils/get-axios-observable-request.spec.ts new file mode 100644 index 0000000..93d98e1 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/utils/get-axios-observable-request.spec.ts @@ -0,0 +1,18 @@ +import { HttpMethod } from '@dripjs/types'; +import Axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import { Subject } from 'rxjs'; + +import { getAxiosObservableRequest } from './get-axios-observable-request'; + +const mock = new MockAdapter(Axios); + +describe('getAxiosObservableRequest', () => { + const remaining$ = new Subject(); + it('should request timeout', async () => { + mock.onPost().timeout(); + await expect(getAxiosObservableRequest('wwwsdfdsf', remaining$, { method: HttpMethod.POST }).toPromise()).rejects.toEqual( + new Error('timeout of 5000ms exceeded'), + ); + }); +}); diff --git a/modules/exchanges/crypto/bitmex/rest/internal/utils/get-axios-observable-request.ts b/modules/exchanges/crypto/bitmex/rest/internal/utils/get-axios-observable-request.ts new file mode 100644 index 0000000..bf958a2 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/utils/get-axios-observable-request.ts @@ -0,0 +1,34 @@ +import Axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; +import { Observable, Observer, Subject } from 'rxjs'; + +import { getRateLimit } from '../../../common'; +import { getResponseError } from './get-response-error'; + +/** + * axios转rx方法 + * 目的:检知超时请求,处理为5秒超时 + * + * @param url 请求url + * @param remaining$ 余额可观察对象 + * @param config 其他配置 + */ +export function getAxiosObservableRequest( + url: string, + remaining$: Subject, + config?: AxiosRequestConfig, +): Observable> { + return new Observable((observer: Observer>) => { + const cancelToken = Axios.CancelToken.source(); + const cancelTokenConfig = { cancelToken: cancelToken.token, timeout: 5000 }; + Axios(url, config ? { ...cancelTokenConfig, ...config } : cancelTokenConfig).then( + (result) => { + const amount = getRateLimit(result.headers).remaining; + // 更新可用余额 + remaining$.next(amount); + observer.next(result); + observer.complete(); + }, + (error) => (Axios.isCancel(error) ? observer.complete() : observer.error(getResponseError(error))), + ); + }); +} diff --git a/modules/exchanges/crypto/bitmex/rest/internal/utils/get-response-error.ts b/modules/exchanges/crypto/bitmex/rest/internal/utils/get-response-error.ts new file mode 100644 index 0000000..9f665a7 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/utils/get-response-error.ts @@ -0,0 +1,20 @@ +/** + * 获取bitmex的rest请求错误消息 + * @param err + */ +export function getResponseError(err: { [attr: string]: any }): Error | undefined { + if (err) { + if (err.response) { + if (err.response.data && err.response.data.error) { + const resError = err.response.data.error as Error; + + // @ts-ignore + return new Error(resError ? resError.message : resError); + } else { + return new Error(err.message); + } + } + + return err as Error; + } +} diff --git a/modules/exchanges/crypto/bitmex/rest/internal/utils/get-rest-response.ts b/modules/exchanges/crypto/bitmex/rest/internal/utils/get-rest-response.ts new file mode 100644 index 0000000..adbd01b --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/utils/get-rest-response.ts @@ -0,0 +1,11 @@ +import { AxiosResponse } from 'axios'; + +import { getRateLimit } from '../../../common'; +import { RestResponse } from '../../types'; + +export function getRestResponse(source: AxiosResponse): RestResponse { + return { + rateLimit: getRateLimit(source.headers), + data: source.data, + }; +} diff --git a/modules/exchanges/crypto/bitmex/rest/internal/utils/index.ts b/modules/exchanges/crypto/bitmex/rest/internal/utils/index.ts new file mode 100644 index 0000000..c6b95d6 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/utils/index.ts @@ -0,0 +1,3 @@ +export * from './validate-remaining'; +export * from './get-rest-response'; +export * from './get-axios-observable-request'; diff --git a/modules/exchanges/crypto/bitmex/rest/internal/utils/validate-remaining.ts b/modules/exchanges/crypto/bitmex/rest/internal/utils/validate-remaining.ts new file mode 100644 index 0000000..44b1034 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/rest/internal/utils/validate-remaining.ts @@ -0,0 +1,11 @@ +import { MINIMUM_REMAINING_NUM } from '../../constants'; + +/** + * 验证可用余额 + * @param remaining + */ +export function validateRemaining(remaining: number): void { + if (remaining < MINIMUM_REMAINING_NUM) { + throw new Error(`The remaining(${remaining}) is less than ${MINIMUM_REMAINING_NUM}`); + } +} diff --git a/modules/exchanges/crypto/bitmex/rest/rest-base.ts b/modules/exchanges/crypto/bitmex/rest/rest-base.ts index 91c7754..5c4616d 100644 --- a/modules/exchanges/crypto/bitmex/rest/rest-base.ts +++ b/modules/exchanges/crypto/bitmex/rest/rest-base.ts @@ -1,77 +1,115 @@ -import { Config, OrderSide } from '../types'; -import { Bar, Instrument, Order, Orderbook, Position } from './internal'; +import { Observable, Subject, interval } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +import { + BarResponse, + Config, + InstrumentResponse, + OrderResponse, + OrderSide, + OrderbookL2Response, + PositionResponse, + RestResponse, +} from '../types'; +import { Bar, Instrument, MultipleOrder, Order, Orderbook, Position } from './internal'; import { RestBarRequest, - RestBarResponse, + RestCancelMultipleOrderRequest, RestFetchOrderRequest, RestFetchPositionRequest, - RestInstrumentResponse, + RestLeverageRequest, + RestMultipleOrderRequest, RestOrderRequest, - RestOrderResponse, - RestOrderbookL2Response, RestOrderbookRequest, - RestOrdersResponse, - RestPositionsResponse, } from './types'; +/** + * rest请求处理基础类 + * + * 用于处理请求基础方法 + */ export class RestBase { protected readonly instrument: Instrument; protected readonly order: Order; + protected readonly multipleOrder: MultipleOrder; protected readonly orderbook: Orderbook; protected readonly bar: Bar; protected readonly position: Position; + private readonly remaining$ = new Subject(); + private readonly disposer$ = new Subject(); + constructor(config: Config) { - const cfg = { - apiKey: '', - apiSecret: '', - testnet: false, - ...config, - }; - this.instrument = new Instrument(cfg); - this.order = new Order(cfg); - this.orderbook = new Orderbook(cfg); - this.bar = new Bar(cfg); - this.position = new Position(cfg); - } - - async createOrder(request: Partial): Promise { + this.instrument = new Instrument(config, this.remaining$); + this.order = new Order(config, this.remaining$); + this.multipleOrder = new MultipleOrder(config, this.remaining$); + this.orderbook = new Orderbook(config, this.remaining$); + this.bar = new Bar(config, this.remaining$); + this.position = new Position(config, this.remaining$); + // 每分钟重置可用余额为60 + interval(60 * 1000) + .pipe(takeUntil(this.disposer$)) + .subscribe(() => this.remaining$.next(60)); + } + + createOrder(request: Partial): Observable> { return this.order.create(request); } - async fetchOrder(request: Partial): Promise { + createMultipleOrder(request: RestMultipleOrderRequest): Observable> { + return this.multipleOrder.create(request); + } + + fetchOrder(request: Partial): Observable> { return this.order.fetch(request); } - async updateOrder(request: Partial): Promise { + updateOrder(request: Partial): Observable> { return this.order.update(request); } - async cancelOrder(request: Partial): Promise { + updateMultipleOrder(request: RestMultipleOrderRequest): Observable> { + return this.multipleOrder.update(request); + } + + cancelOrder(request: Partial): Observable> { return this.order.cancel(request); } - async fetchOrderbook(request: RestOrderbookRequest): Promise { + cancelMultipleOrder(request: Partial): Observable> { + return this.multipleOrder.cancel(request); + } + + fetchOrderbook(request: RestOrderbookRequest): Observable> { return this.orderbook.fetch(request); } - async fetchInstrument(): Promise { + fetchInstrument(): Observable> { return this.instrument.fetch(); } - async fetchBar(request: RestBarRequest): Promise { + fetchBar(request: RestBarRequest): Observable> { return this.bar.fetch(request); } - async fetchPosition(request: Partial): Promise { + fetchPosition(request: Partial): Observable> { return this.position.fetch(request); } - async createPosition(symbol: string, side: OrderSide, amount: number): Promise { + createPosition(symbol: string, side: OrderSide, amount: number): Observable> { return this.position.create(symbol, side, amount); } - async removePosition(symbol: string): Promise { + removePosition(symbol: string): Observable> { return this.position.remove(symbol); } + + updateLeverage(request: RestLeverageRequest): Observable> { + return this.position.updateLeverage(request); + } + + destroy(): void { + this.disposer$.next(); + this.disposer$.complete(); + } } diff --git a/modules/exchanges/crypto/bitmex/rest/rest.spec.ts b/modules/exchanges/crypto/bitmex/rest/rest.spec.ts index 2867aac..77cb2b4 100644 --- a/modules/exchanges/crypto/bitmex/rest/rest.spec.ts +++ b/modules/exchanges/crypto/bitmex/rest/rest.spec.ts @@ -1,530 +1,140 @@ -import { isPositive, sleep } from '@dripjs/common'; -import { assertExisitingColumns, corsProxy, isUuid, overrideTimestampColumns, overrideValue, testnetConfig } from '@dripjs/testing'; +import { testnetConfig } from '@dripjs/testing'; import * as moment from 'moment'; -import { OrderResponse, OrderSide, OrderStatus, OrderType, Resolution } from '../types'; +import { receiveOrderData } from '../common/test-helpers'; +import { OrderSide, OrderStatus, OrderType, Resolution } from '../types'; +import { MAX_REMAINING_NUM } from './constants'; import { Rest } from './rest'; -import { RestFetchOrderRequest, RestOrderRequest, RestOrderbookRequest } from './types'; +import { RestCancelMultipleOrderRequest, RestFetchOrderRequest, RestOrderRequest, RestOrderbookRequest } from './types'; describe('Bitmex Rest', () => { const pair = 'XBTUSD'; const rest = new Rest(testnetConfig); - afterAll(async () => { - await rest.removePosition(pair); - }); + let orderId: string; + let price: number; - it('fetch orderbook', async () => { + it('fetch orderbook', (done) => { const request: RestOrderbookRequest = { symbol: pair, depth: 25, }; - - const res = await rest.fetchOrderbook(request); - expect(res.orderbook.bids.length).toEqual(25); - expect(res.orderbook.asks.length).toEqual(25); + rest.fetchOrderbook(request).subscribe((res) => { + price = +res.data.bids[4][0]; + expect(res.data.bids.length).toEqual(25); + expect(res.data.asks.length).toEqual(25); + done(); + }); }); - it('fetch orderbook for proxy', async () => { - const restProxy = new Rest({ ...testnetConfig, corsProxy }); - const request: RestOrderbookRequest = { + it('create order', (done) => { + const request: Partial = { symbol: pair, - depth: 25, + side: OrderSide.Buy, + price, + orderQty: 25, + ordType: OrderType.Limit, }; - const res = await restProxy.fetchOrderbook(request); - expect(res.orderbook.bids.length).toEqual(25); - expect(res.orderbook.asks.length).toEqual(25); - }); - - describe('create order', () => { - let price: number; - let orderID: string; - beforeAll(async () => { - const request: RestOrderbookRequest = { - symbol: pair, - depth: 25, - }; - const res = await rest.fetchOrderbook(request); - price = +res.orderbook.bids[4][0]; - }); - - afterEach(async () => { - if (orderID) { - await rest.cancelOrder({ orderID }); - } - }); - - it('should be create a limit order', async () => { - const request: Partial = { - symbol: pair, - side: OrderSide.Buy, - price, - orderQty: 25, - }; - const res = await rest.createLimitOrder(request); - orderID = res.order.orderID; - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - order: { - orderID: isUuid, - account: isPositive, - symbol: 'XBTUSD', - side: 'Buy', - orderQty: 25, - price: isPositive, - pegPriceType: '', - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Limit', - timeInForce: 'GoodTillCancel', - execInst: '', - contingencyType: '', - exDestination: 'XBME', - ordStatus: 'New', - triggered: '', - workingIndicator: true, - ordRejReason: '', - leavesQty: 25, - cumQty: 0, - multiLegReportingType: 'SingleSecurity', - text: 'Submitted via API.', - transactTime: overrideValue, - timestamp: overrideValue, - }, - }), - ).not.toThrow(); - }); - - it('should be create a limit ParticipateDoNotInitiate order', async () => { - const request: Partial = { - symbol: pair, - side: OrderSide.Buy, - price, - orderQty: 25, - }; - const res = await rest.createLimitOrderParticipateDoNotInitiate(request); - orderID = res.order.orderID; - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - order: { - orderID: isUuid, - account: isPositive, - symbol: 'XBTUSD', - side: 'Buy', - orderQty: 25, - price: isPositive, - pegPriceType: '', - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Limit', - timeInForce: 'GoodTillCancel', - execInst: 'ParticipateDoNotInitiate', - contingencyType: '', - exDestination: 'XBME', - ordStatus: 'New', - triggered: '', - workingIndicator: true, - ordRejReason: '', - leavesQty: 25, - cumQty: 0, - multiLegReportingType: 'SingleSecurity', - text: 'Submitted via API.', - transactTime: overrideValue, - timestamp: overrideValue, - }, - }), - ).not.toThrow(); - }); - - it('should be create a stop order', async () => { - const request: Partial = { - symbol: pair, - side: OrderSide.Sell, - stopPx: price - 50, - orderQty: 25, - }; - const res = await rest.createStopOrder(request); - orderID = res.order.orderID; - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - order: { - orderID: isUuid, - account: isPositive, - symbol: 'XBTUSD', - side: 'Sell', - orderQty: 25, - price: null, - stopPx: isPositive, - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Stop', - timeInForce: 'ImmediateOrCancel', - exDestination: 'XBME', - ordStatus: 'New', - workingIndicator: false, - leavesQty: 25, - cumQty: 0, - multiLegReportingType: 'SingleSecurity', - text: 'Submitted via API.', - transactTime: overrideValue, - timestamp: overrideValue, - }, - }), - ).not.toThrow(); - }); - - it('should be create a stop order with LastPrice and ReduceOnly', async () => { - const request: Partial = { - symbol: pair, - side: OrderSide.Sell, - stopPx: price - 50, - orderQty: 25, - }; - const res = await rest.createStopOrderLastPriceReduceOnly(request); - orderID = res.order.orderID; - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - order: { - orderID: isUuid, - account: isPositive, - symbol: 'XBTUSD', - side: 'Sell', - orderQty: 25, - price: null, - stopPx: isPositive, - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Stop', - timeInForce: 'ImmediateOrCancel', - execInst: 'LastPrice,ReduceOnly', - exDestination: 'XBME', - ordStatus: 'New', - workingIndicator: false, - leavesQty: 25, - cumQty: 0, - multiLegReportingType: 'SingleSecurity', - text: 'Submitted via API.', - transactTime: overrideValue, - timestamp: overrideValue, - }, - }), - ).not.toThrow(); + rest.createOrder(request).subscribe((res) => { + orderId = res.data.orderID; + expect(res.data).toBeDefined(); + expect(res.rateLimit.limit).toEqual(MAX_REMAINING_NUM); + done(); }); + }); - it('should be get a stop order', async () => { - const request: Partial = { - symbol: pair, - side: OrderSide.Sell, - stopPx: price - 50, - orderQty: 25, - }; - const res = await rest.createStopOrder(request); - orderID = res.order.orderID; - const resStopOrder = await rest.getStopOrder(pair); - expect(() => - assertExisitingColumns(overrideTimestampColumns(resStopOrder), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [ - { - orderID: isUuid, - account: isPositive, - symbol: 'XBTUSD', - side: 'Sell', - orderQty: 25, - price: null, - displayQty: null, - stopPx: isPositive, - pegOffsetValue: null, - pegPriceType: '', - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Stop', - timeInForce: 'ImmediateOrCancel', - execInst: '', - contingencyType: '', - exDestination: 'XBME', - ordStatus: 'New', - triggered: '', - workingIndicator: false, - ordRejReason: '', - simpleLeavesQty: null, - leavesQty: 25, - simpleCumQty: null, - cumQty: 0, - avgPx: null, - multiLegReportingType: 'SingleSecurity', - text: 'Submitted via API.', - transactTime: overrideValue, - timestamp: overrideValue, - }, - ], - }), - ).not.toThrow(); + it('fetch order', (done) => { + const request: Partial = { + symbol: pair, + filter: { + orderID: orderId, + }, + }; + rest.fetchOrder(request).subscribe((res) => { + expect(res.data[0].price).toEqual(price); + done(); }); }); - describe('fetch/update/get/cancel order', () => { - let order: OrderResponse; - beforeAll(async () => { - const res = await rest.fetchOrderbook({ - symbol: pair, - depth: 25, - }); - const price = +res.orderbook.bids[4][0]; - const resOrder = await rest.createLimitOrder({ - symbol: pair, - side: OrderSide.Buy, - price, - orderQty: 25, - }); - order = resOrder.order; + it('update order', (done) => { + const request: Partial = { + orderID: orderId, + price: price - 1, + }; + rest.updateOrder(request).subscribe((res) => { + expect(res.data.price).toEqual(price - 1); + done(); }); + }); - it('fetch order', async () => { - const request: Partial = { - symbol: pair, - filter: { - orderID: order.orderID, - }, - }; - const res = await rest.fetchOrder(request); - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [ - { - orderID: isUuid, - account: isPositive, - symbol: 'XBTUSD', - side: 'Buy', - orderQty: 25, - price: isPositive, - displayQty: null, - stopPx: null, - pegOffsetValue: null, - pegPriceType: '', - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Limit', - timeInForce: 'GoodTillCancel', - execInst: '', - contingencyType: '', - exDestination: 'XBME', - ordStatus: 'New', - triggered: '', - workingIndicator: true, - ordRejReason: '', - simpleLeavesQty: null, - leavesQty: 25, - simpleCumQty: null, - cumQty: 0, - avgPx: null, - multiLegReportingType: 'SingleSecurity', - text: 'Submitted via API.', - transactTime: overrideValue, - timestamp: overrideValue, - }, - ], - }), - ).not.toThrow(); + it('cancel order', (done) => { + const request: Partial = { + orderID: orderId, + }; + rest.cancelOrder(request).subscribe((res) => { + expect(res.data[0].ordStatus).toEqual(OrderStatus.Canceled); + done(); }); + }); // cancelMultipleOrder - it('update order', async () => { - const request: Partial = { - orderID: order.orderID, - price: order.price - 1, - }; - - const res = await rest.updateOrder(request); - expect(res.order.price).toEqual(order.price - 1); + it('cancelMultipleOrder', (done) => { + const request: Partial = { + symbol: 'XBTUSD', + }; + rest.cancelMultipleOrder(request).subscribe((res) => { + expect(res.data[0].ordStatus).toEqual(OrderStatus.Canceled); + done(); }); + }); - it('get order by id', async () => { - const res = await rest.getOrderById(pair, order.orderID); - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [ - { - orderID: isUuid, - account: isPositive, - symbol: 'XBTUSD', - side: 'Buy', - orderQty: 25, - price: isPositive, - displayQty: null, - stopPx: null, - pegOffsetValue: null, - pegPriceType: '', - currency: 'USD', - settlCurrency: 'XBt', - ordType: 'Limit', - timeInForce: 'GoodTillCancel', - execInst: '', - contingencyType: '', - exDestination: 'XBME', - ordStatus: 'New', - triggered: '', - workingIndicator: true, - ordRejReason: '', - simpleLeavesQty: null, - leavesQty: 25, - simpleCumQty: null, - cumQty: 0, - avgPx: null, - multiLegReportingType: 'SingleSecurity', - transactTime: overrideValue, - timestamp: overrideValue, - }, - ], - }), - ).not.toThrow(); - }); + it('get order by id', (done) => { + receiveOrderData(pair, async (order) => { + price = order.price ? order.price : 0; - it('cancel order', async () => { - const request: Partial = { - orderID: order.orderID, - }; - const res = await rest.cancelOrder(request); - expect(res.order.ordStatus).toEqual(OrderStatus.Canceled); + return rest.getOrderById(pair, order.orderID).toPromise(); + }).subscribe((res) => { + expect(res.data[0].price).toEqual(price); + done(); }); }); - it('config is null', async () => { + it('config is null', (done) => { const rest2 = new Rest({}); const request: Partial = { symbol: pair, side: OrderSide.Buy, - price: 8000, + price, orderQty: 25, ordType: OrderType.Limit, }; - const res = await rest2.createOrder(request); - expect(res.error!.name).toEqual('HTTPError'); - expect(res.error!.message).toEqual('Missing API key.'); - }); - - it('fetch instrument', async () => { - const res = await rest.fetchInstrument(); - expect(res.instruments.length).toBeGreaterThan(0); + rest2.createOrder(request).subscribe( + () => {}, + (error) => { + expect(error).toBeDefined(); + done(); + }, + ); }); - it('fetch bar', async () => { - const time = Date.now(); - const res = await rest.fetchBar({ - symbol: pair, - binSize: Resolution.day, - startTime: moment(time - 1000 * 60 * 60 * 24 * 60).toISOString(), - endTime: moment(time).toISOString(), + it('fetch instrument', (done) => { + rest.fetchInstrument().subscribe((res) => { + expect(res.data.length).toBeGreaterThan(0); + done(); }); - expect(res.bars.length).toEqual(60); }); - it('create/fetch position ', async () => { - await sleep(2000); - const res = await rest.createPosition(pair, OrderSide.Buy, 25); - expect(() => - assertExisitingColumns(overrideTimestampColumns(res), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [ - { - account: isPositive, - symbol: 'XBTUSD', - currency: 'XBt', - underlying: 'XBT', - quoteCurrency: 'USD', - commission: isPositive, - initMarginReq: isPositive, - maintMarginReq: isPositive, - riskLimit: isPositive, - leverage: isPositive, - crossMargin: false, - rebalancedPnl: isPositive, - prevClosePrice: isPositive, - execQty: 25, - currentTimestamp: overrideValue, - currentQty: 25, - isOpen: true, - timestamp: overrideValue, - lastPrice: isPositive, - }, - ], - }), - ).not.toThrow(); - - const postions = await rest.fetchPosition({ - filter: { + it('fetch bar', (done) => { + const time = Date.now(); + rest + .fetchBar({ symbol: pair, - isOpen: true, - }, - }); - expect(() => - assertExisitingColumns(overrideTimestampColumns(postions), { - ratelimit: { - remaining: isPositive, - reset: overrideValue, - limit: isPositive, - }, - orders: [ - { - account: isPositive, - symbol: 'XBTUSD', - currency: 'XBt', - underlying: 'XBT', - quoteCurrency: 'USD', - commission: isPositive, - initMarginReq: isPositive, - maintMarginReq: isPositive, - riskLimit: isPositive, - leverage: isPositive, - crossMargin: false, - rebalancedPnl: isPositive, - prevClosePrice: isPositive, - execQty: 25, - currentTimestamp: overrideValue, - currentQty: 25, - isOpen: true, - timestamp: overrideValue, - lastPrice: isPositive, - }, - ], - }), - ).not.toThrow(); + binSize: Resolution.day, + startTime: moment(time - 1000 * 60 * 60 * 24 * 60).toISOString(), + endTime: moment(time).toISOString(), + }) + .subscribe((res) => { + expect(res.data.length).toEqual(60); + done(); + }); }); }); diff --git a/modules/exchanges/crypto/bitmex/rest/rest.ts b/modules/exchanges/crypto/bitmex/rest/rest.ts index e18a24c..7ac11dd 100644 --- a/modules/exchanges/crypto/bitmex/rest/rest.ts +++ b/modules/exchanges/crypto/bitmex/rest/rest.ts @@ -1,13 +1,15 @@ -import { Config, ExecInst, OrderStatus, OrderType } from '../types'; +import { Observable } from 'rxjs'; + +import { Config, ExecInst, OrderResponse, OrderType, RestResponse } from '../types'; import { RestBase } from './rest-base'; -import { RestOrderRequest, RestOrderResponse, RestOrdersResponse } from './types'; +import { RestOrderRequest } from './types'; export class Rest extends RestBase { constructor(config: Config) { super(config); } - async createLimitOrder(request: Partial): Promise { + createLimitOrder(request: Partial): Observable> { return this.createOrder({ ...request, ordType: OrderType.Limit, @@ -19,28 +21,28 @@ export class Rest extends RestBase { * @param request * @Return Promise */ - async createLimitOrderParticipateDoNotInitiate(request: Partial): Promise { + createLimitOrderParticipateDoNotInitiate(request: Partial): Observable> { return this.createLimitOrder({ ...request, execInst: ExecInst.ParticipateDoNotInitiate, }); } - async createStopOrder(request: Partial): Promise { + createStopOrder(request: Partial): Observable> { return this.createOrder({ ...request, ordType: OrderType.Stop, }); } - async createStopOrderLastPriceReduceOnly(request: Partial): Promise { + createStopOrderLastPriceReduceOnly(request: Partial): Observable> { return this.createStopOrder({ ...request, execInst: `${ExecInst.LastPrice},${ExecInst.ReduceOnly}`, }); } - async getOrderById(symbol: string, orderID: string): Promise { + getOrderById(symbol: string, orderID: string | string[]): Observable> { return this.fetchOrder({ symbol, filter: { @@ -49,13 +51,22 @@ export class Rest extends RestBase { }); } - async getStopOrder(symbol: string): Promise { + getStopOrder(symbol: string): Observable> { return this.fetchOrder({ symbol, filter: { - ordStatus: OrderStatus.New, ordType: OrderType.Stop, }, + } + }); + + getActiveStopLimitOrder(symbol: string): Observable> { + return this.fetchOrder({ + symbol, + filter: { + ordType: OrderType.StopLimit, + open: true, + }, }); } } diff --git a/modules/exchanges/crypto/bitmex/rest/types/index.ts b/modules/exchanges/crypto/bitmex/rest/types/index.ts index 46cae96..56e4b05 100644 --- a/modules/exchanges/crypto/bitmex/rest/types/index.ts +++ b/modules/exchanges/crypto/bitmex/rest/types/index.ts @@ -1,3 +1 @@ export * from './request'; -export * from './endpoint'; -export * from './response'; diff --git a/modules/exchanges/crypto/bitmex/rest/types/request.ts b/modules/exchanges/crypto/bitmex/rest/types/request.ts index 0de3b8b..3683c17 100644 --- a/modules/exchanges/crypto/bitmex/rest/types/request.ts +++ b/modules/exchanges/crypto/bitmex/rest/types/request.ts @@ -19,6 +19,17 @@ export interface RestOrderRequest { text: string; } +export interface RestMultipleOrderRequest { + orders: Partial[]; +} + +export interface RestCancelMultipleOrderRequest { + symbol: string; + filter: Partial; + /** cancellation annotation. e.g. 'Spread Exceeded */ + text: number; +} + export interface RestFetchOrderRequest { symbol: string; filter: { [filterKey: string]: any }; @@ -36,6 +47,11 @@ export interface RestFetchPositionRequest { count: number; } +export interface RestLeverageRequest { + symbol: string; + leverage: number; +} + export interface RestOrderbookRequest { symbol: string; depth: number; diff --git a/modules/exchanges/crypto/bitmex/rest/types/response.ts b/modules/exchanges/crypto/bitmex/rest/types/response.ts deleted file mode 100644 index c2988c5..0000000 --- a/modules/exchanges/crypto/bitmex/rest/types/response.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { - BarResponse, - ErrorResponse, - InstrumentResponse, - OrderResponse, - OrderbookL2Response, - PositionResponse, - RateLimit, -} from '../../types'; - -export interface RestRateLimitResponse extends ErrorResponse { - ratelimit: RateLimit; -} - -export interface RestInstrumentResponse extends RestRateLimitResponse { - instruments: InstrumentResponse[]; -} - -export interface RestBarResponse extends RestRateLimitResponse { - bars: BarResponse[]; -} - -export interface RestOrderResponse extends RestRateLimitResponse { - order: OrderResponse; -} - -export interface RestOrdersResponse extends RestRateLimitResponse { - orders: OrderResponse[]; -} - -export interface RestPositionsResponse extends RestRateLimitResponse { - orders: PositionResponse[]; -} - -export interface RestOrderbookL2Response extends RestRateLimitResponse { - orderbook: OrderbookL2Response; -} diff --git a/modules/exchanges/crypto/bitmex/types/response.ts b/modules/exchanges/crypto/bitmex/types/response.ts index 79b42cd..befed6f 100644 --- a/modules/exchanges/crypto/bitmex/types/response.ts +++ b/modules/exchanges/crypto/bitmex/types/response.ts @@ -1,13 +1,9 @@ import { RateLimit } from './exchange'; import { OrderSide } from './order'; -export interface ErrorResponse { - error?: Error; -} - -export interface RestResponse extends ErrorResponse { - ratelimit: RateLimit; - body: { [attr: string]: any }; +export interface RestResponse { + rateLimit: RateLimit; + data: T; } export interface TradeResponse { @@ -27,6 +23,13 @@ export interface QuoteResponse { askPrice: number; } +export interface MarginResponse { + // 钱包余额 + walletBalance: number; + // 杠杆倍数 + leverage: number; +} + export interface SettlementResponse { timestamp: number; symbol: string; @@ -161,6 +164,7 @@ export interface OrderResponse { text: string; transactTime: Date; timestamp: string; + error: string; } export interface PositionResponse { @@ -173,7 +177,13 @@ export interface PositionResponse { initMarginReq: number; maintMarginReq: number; riskLimit: number; + /** + * 杠杆倍数 + */ leverage: number; + /** + * 是否全仓 + */ crossMargin: boolean; deleveragePercentile: number | null; rebalancedPnl: number; @@ -230,6 +240,9 @@ export interface PositionResponse { varMargin: number; realisedGrossPnl: number; realisedTax: number; + /** + * 已实现盈亏 + */ realisedPnl: number; unrealisedGrossPnl: number; longBankrupt: number; @@ -238,14 +251,29 @@ export interface PositionResponse { indicativeTaxRate: number; indicativeTax: number; unrealisedTax: number; + /** + * 未实现盈亏 + */ unrealisedPnl: number; + /** + * 未实现盈亏比 + */ unrealisedPnlPcnt: number; unrealisedRoePcnt: number; + /** + * 平均成本价 + */ avgCostPrice: number; + /** + * 平均价格 + */ avgEntryPrice: number; breakEvenPrice: number; marginCallPrice: number; liquidationPrice: number; + /** + * 强平价格 + */ bankruptPrice: number; timestamp: number; lastPrice: number; diff --git a/modules/exchanges/crypto/bitmex/yarn.lock b/modules/exchanges/crypto/bitmex/yarn.lock new file mode 100644 index 0000000..7e6c757 --- /dev/null +++ b/modules/exchanges/crypto/bitmex/yarn.lock @@ -0,0 +1,1342 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/code-frame@^7.5.5": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" + integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== + dependencies: + "@babel/highlight" "^7.12.13" + +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== + +"@babel/highlight@^7.12.13": + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" + integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== + dependencies: + "@babel/helper-validator-identifier" "^7.12.11" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@dripjs/common@^0.1.20": + version "0.1.20" + resolved "https://registry.yarnpkg.com/@dripjs/common/-/common-0.1.20.tgz#6ab62d6be86681d49b6b85ca322fa1734b51b787" + integrity sha512-rVsCiHcS2G8vfjLoGmcrtijJ06aH4M/nx7OU64vnjFkvc4drG/joaPOLJGma//+rVLGsO3UQ0pRDlJazhrG8Uw== + dependencies: + bignumber.js "^8.1.1" + rxjs "^6.4.0" + socket.io "^2.2.0" + socket.io-client "^2.2.0" + ws "^7.0.0" + +"@dripjs/testing@^0.1.20": + version "0.1.20" + resolved "https://registry.yarnpkg.com/@dripjs/testing/-/testing-0.1.20.tgz#73a9b15f5b53e8c90ee8cc6bd354be5fd06461e7" + integrity sha512-7tl+hjMc/deXv38HLV12YV2W0tDmGxH+NjIBri45qRxVGbDx201QV4AaLj61BZ1g2/YoJwvxoP93gFRGYg3Twg== + dependencies: + lodash "^4.17.11" + +"@dripjs/types@^0.1.20": + version "0.1.21" + resolved "https://registry.yarnpkg.com/@dripjs/types/-/types-0.1.21.tgz#d387f814dad26c6f05eaf270136c983da1ecda08" + integrity sha512-BdGKWSfaFLz3Iz490Jzf3Xlc0g+hvoEgJgeGXxfzLVU6x1Doqbpi0qk12GPqjShuQHfhsR2A8jgfI/+le8mi7Q== + +"@types/estree@*": + version "0.0.46" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe" + integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg== + +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/node@*": + version "14.14.25" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.25.tgz#15967a7b577ff81383f9b888aa6705d43fbbae93" + integrity sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ== + +"@types/qs@^6.5.3": + version "6.9.5" + resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.5.tgz#434711bdd49eb5ee69d90c1d67c354a9a8ecb18b" + integrity sha512-/JHkVHtx/REVG0VVToGRGH2+23hsYLHdyG+GrvoUGlGAd0ErauXDyvHtRI/7H7mzLm+tBCKA7pfcpkQ1lf58iQ== + +"@types/resolve@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + +"@types/ws@6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.1.tgz#ca7a3f3756aa12f62a0a62145ed14c6db25d5a28" + integrity sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q== + dependencies: + "@types/events" "*" + "@types/node" "*" + +abstract-leveldown@~0.12.0, abstract-leveldown@~0.12.1: + version "0.12.4" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-0.12.4.tgz#29e18e632e60e4e221d5810247852a63d7b2e410" + integrity sha1-KeGOYy5g5OIh1YECR4UqY9ey5BA= + dependencies: + xtend "~3.0.0" + +accepts@~1.3.4: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + dependencies: + mime-types "~2.1.24" + negotiator "0.6.2" + +acorn@^5.7.3: + version "5.7.4" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" + integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== + +acorn@^7.1.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + +asn1.js@^5.2.0: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + +axios-mock-adapter@^1.19.0: + version "1.19.0" + resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz#9d72e321a6c5418e1eff067aa99761a86c5188a4" + integrity sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg== + dependencies: + fast-deep-equal "^3.1.3" + is-buffer "^2.0.3" + +axios@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" + integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g== + dependencies: + follow-redirects "1.5.10" + is-buffer "^2.0.2" + +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + +base64-arraybuffer@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" + integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= + +base64id@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + +bignumber.js@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.1.tgz#4b072ae5aea9c20f6730e4e5d529df1271c4d885" + integrity sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ== + +bl@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-0.8.2.tgz#c9b6bca08d1bc2ea00fc8afb4f1a5fd1e1c66e4e" + integrity sha1-yba8oI0bwuoA/Ir7Txpf0eHGbk4= + dependencies: + readable-stream "~1.0.26" + +blob@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + +bn.js@^5.0.0, bn.js@^5.1.1: + version "5.1.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" + integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== + +brorand@^1.0.1, brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-fs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browserify-fs/-/browserify-fs-1.0.0.tgz#f075aa8a729d4d1716d066620e386fcc1311a96f" + integrity sha1-8HWqinKdTRcW0GZiDjhvzBMRqW8= + dependencies: + level-filesystem "^1.0.1" + level-js "^2.1.3" + levelup "^0.18.2" + +browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" + integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== + dependencies: + bn.js "^5.0.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" + integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== + dependencies: + bn.js "^5.1.1" + browserify-rsa "^4.0.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.5.3" + inherits "^2.0.4" + parse-asn1 "^5.1.5" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +buffer-es6@^4.9.2, buffer-es6@^4.9.3: + version "4.9.3" + resolved "https://registry.yarnpkg.com/buffer-es6/-/buffer-es6-4.9.3.tgz#f26347b82df76fd37e18bcb5288c4970cfd5c404" + integrity sha1-8mNHuC33b9N+GLy1KIxJcM/VxAQ= + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +builtin-modules@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.2.0.tgz#45d5db99e7ee5e6bc4f362e008bf917ab5049887" + integrity sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA== + +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +clone@~0.1.9: + version "0.1.19" + resolved "https://registry.yarnpkg.com/clone/-/clone-0.1.19.tgz#613fb68639b26a494ac53253e15b1a6bd88ada85" + integrity sha1-YT+2hjmyaklKxTJT4Vsaa9iK2oU= + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + +component-emitter@~1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" + integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== + +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= + +concat-stream@^1.4.4: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +cookie@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +create-ecdh@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" + integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== + dependencies: + bn.js "^4.1.0" + elliptic "^6.5.3" + +create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +debug@=3.1.0, debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + +deferred-leveldown@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-0.2.0.tgz#2cef1f111e1c57870d8bbb8af2650e587cd2f5b4" + integrity sha1-LO8fER4cV4cNi7uK8mUOWHzS9bQ= + dependencies: + abstract-leveldown "~0.12.1" + +des.js@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" + integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + +engine.io-client@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.5.0.tgz#fc1b4d9616288ce4f2daf06dcf612413dec941c7" + integrity sha512-12wPRfMrugVw/DNyJk34GQ5vIVArEcVMXWugQGGuw2XxUSztFNmJggZmv8IZlLyEdnpO1QB9LkcjeWewO2vxtA== + dependencies: + component-emitter "~1.3.0" + component-inherit "0.0.3" + debug "~3.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + ws "~7.4.2" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + +engine.io-parser@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" + integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.4" + blob "0.0.5" + has-binary2 "~1.0.2" + +engine.io@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.5.0.tgz#9d6b985c8a39b1fe87cd91eb014de0552259821b" + integrity sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA== + dependencies: + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + debug "~4.1.0" + engine.io-parser "~2.2.0" + ws "~7.4.2" + +errno@^0.1.1, errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +estree-walker@^0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.5.2.tgz#d3850be7529c9580d815600b53126515e146dd39" + integrity sha512-XpCnW/AE10ws/kDAs37cngSkvgIR8aN3G0MS85m7dUpuK2EREo9VJ00uvw6Dg/hXEpfsE1I1TvJOJr+Z+TL+ig== + +estree-walker@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" + integrity sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w== + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + +foreach@~2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +fwd-stream@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/fwd-stream/-/fwd-stream-1.0.4.tgz#ed281cabed46feecf921ee32dc4c50b372ac7cfa" + integrity sha1-7Sgcq+1G/uz5Ie4y3ExQs3KsfPo= + dependencies: + readable-stream "~1.0.26-4" + +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== + dependencies: + isarray "2.0.1" + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" + integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== + dependencies: + inherits "^2.0.4" + readable-stream "^3.6.0" + safe-buffer "^5.2.0" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +idb-wrapper@^1.5.0: + version "1.7.2" + resolved "https://registry.yarnpkg.com/idb-wrapper/-/idb-wrapper-1.7.2.tgz#8251afd5e77fe95568b1c16152eb44b396767ea2" + integrity sha512-zfNREywMuf0NzDo9mVsL0yegjsirJxHpKHvWcyRozIqQy89g0a3U+oBPOCN4cc0oCiOuYgZHimzaW/R46G1Mpg== + +indexof@0.0.1, indexof@~0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + +inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-buffer@^2.0.2, is-buffer@^2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" + integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== + +is-core-module@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" + integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== + dependencies: + has "^1.0.3" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= + +is-object@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-0.1.2.tgz#00efbc08816c33cfc4ac8251d132e10dc65098d7" + integrity sha1-AO+8CIFsM8/ErIJR0TLhDcZQmNc= + +is-reference@^1.1.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + +is@~0.2.6: + version "0.2.7" + resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562" + integrity sha1-OzSixI81mXLzUEKEkZOucmS2NWI= + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + +isbuffer@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/isbuffer/-/isbuffer-0.0.0.tgz#38c146d9df528b8bf9b0701c3d43cf12df3fc39b" + integrity sha1-OMFG2d9Si4v5sHAcPUPPEt8/w5s= + +jest-worker@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" + integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== + dependencies: + merge-stream "^2.0.0" + supports-color "^6.1.0" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +level-blobs@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/level-blobs/-/level-blobs-0.1.7.tgz#9ab9b97bb99f1edbf9f78a3433e21ed56386bdaf" + integrity sha1-mrm5e7mfHtv594o0M+Ie1WOGva8= + dependencies: + level-peek "1.0.6" + once "^1.3.0" + readable-stream "^1.0.26-4" + +level-filesystem@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/level-filesystem/-/level-filesystem-1.2.0.tgz#a00aca9919c4a4dfafdca6a8108d225aadff63b3" + integrity sha1-oArKmRnEpN+v3KaoEI0iWq3/Y7M= + dependencies: + concat-stream "^1.4.4" + errno "^0.1.1" + fwd-stream "^1.0.4" + level-blobs "^0.1.7" + level-peek "^1.0.6" + level-sublevel "^5.2.0" + octal "^1.0.0" + once "^1.3.0" + xtend "^2.2.0" + +level-fix-range@2.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/level-fix-range/-/level-fix-range-2.0.0.tgz#c417d62159442151a19d9a2367868f1724c2d548" + integrity sha1-xBfWIVlEIVGhnZojZ4aPFyTC1Ug= + dependencies: + clone "~0.1.9" + +level-fix-range@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/level-fix-range/-/level-fix-range-1.0.2.tgz#bf15b915ae36d8470c821e883ddf79cd16420828" + integrity sha1-vxW5Fa422EcMgh6IPd95zRZCCCg= + +"level-hooks@>=4.4.0 <5": + version "4.5.0" + resolved "https://registry.yarnpkg.com/level-hooks/-/level-hooks-4.5.0.tgz#1b9ae61922930f3305d1a61fc4d83c8102c0dd93" + integrity sha1-G5rmGSKTDzMF0aYfxNg8gQLA3ZM= + dependencies: + string-range "~1.2" + +level-js@^2.1.3: + version "2.2.4" + resolved "https://registry.yarnpkg.com/level-js/-/level-js-2.2.4.tgz#bc055f4180635d4489b561c9486fa370e8c11697" + integrity sha1-vAVfQYBjXUSJtWHJSG+jcOjBFpc= + dependencies: + abstract-leveldown "~0.12.0" + idb-wrapper "^1.5.0" + isbuffer "~0.0.0" + ltgt "^2.1.2" + typedarray-to-buffer "~1.0.0" + xtend "~2.1.2" + +level-peek@1.0.6, level-peek@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/level-peek/-/level-peek-1.0.6.tgz#bec51c72a82ee464d336434c7c876c3fcbcce77f" + integrity sha1-vsUccqgu5GTTNkNMfIdsP8vM538= + dependencies: + level-fix-range "~1.0.2" + +level-sublevel@^5.2.0: + version "5.2.3" + resolved "https://registry.yarnpkg.com/level-sublevel/-/level-sublevel-5.2.3.tgz#744c12c72d2e72be78dde3b9b5cd84d62191413a" + integrity sha1-dEwSxy0ucr543eO5tc2E1iGRQTo= + dependencies: + level-fix-range "2.0" + level-hooks ">=4.4.0 <5" + string-range "~1.2.1" + xtend "~2.0.4" + +levelup@^0.18.2: + version "0.18.6" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-0.18.6.tgz#e6a01cb089616c8ecc0291c2a9bd3f0c44e3e5eb" + integrity sha1-5qAcsIlhbI7MApHCqb0/DETj5es= + dependencies: + bl "~0.8.1" + deferred-leveldown "~0.2.0" + errno "~0.1.1" + prr "~0.0.0" + readable-stream "~1.0.26" + semver "~2.3.1" + xtend "~3.0.0" + +lodash@^4.17.11: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +ltgt@^2.1.2: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + +magic-string@^0.22.5: + version "0.22.5" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e" + integrity sha512-oreip9rJZkzvA8Qzk9HFs8fZGF/u7H/gtrE8EN6RjKJ9kh2HlC+yQ2QezifqTZfGyiuAV0dRv5a+y/8gBb1m9w== + dependencies: + vlq "^0.2.2" + +magic-string@^0.25.2: + version "0.25.7" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" + integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + dependencies: + sourcemap-codec "^1.4.4" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mime-db@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" + integrity sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w== + +mime-types@~2.1.24: + version "2.1.28" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.28.tgz#1160c4757eab2c5363888e005273ecf79d2a0ecd" + integrity sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ== + dependencies: + mime-db "1.45.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +object-keys@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.2.0.tgz#cddec02998b091be42bf1035ae32e49f1cb6ea67" + integrity sha1-zd7AKZiwkb5CvxA1rjLknxy26mc= + dependencies: + foreach "~2.0.1" + indexof "~0.0.1" + is "~0.2.6" + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + +octal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/octal/-/octal-1.0.0.tgz#63e7162a68efbeb9e213588d58e989d1e5c4530b" + integrity sha1-Y+cWKmjvvrniE1iNWOmJ0eXEUws= + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +parse-asn1@^5.0.0, parse-asn1@^5.1.5: + version "5.1.6" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" + integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== + dependencies: + asn1.js "^5.2.0" + browserify-aes "^1.0.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parseqs@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" + integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== + +parseuri@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" + integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== + +path-parse@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" + integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + +pbkdf2@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" + integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +process-es6@^0.11.2, process-es6@^0.11.6: + version "0.11.6" + resolved "https://registry.yarnpkg.com/process-es6/-/process-es6-0.11.6.tgz#c6bb389f9a951f82bd4eb169600105bd2ff9c778" + integrity sha1-xrs4n5qVH4K9TrFpYAEFvS/5x3g= + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +prr@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a" + integrity sha1-GoS4WQgyVQFBGFPQCB7j+obikmo= + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= + +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + +qs@^6.6.0: + version "6.9.6" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" + integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +readable-stream@^1.0.26-4: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.2.2: + version "2.3.7" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" + integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-stream@~1.0.26, readable-stream@~1.0.26-4: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +resolve@^1.11.0, resolve@^1.11.1: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rollup-plugin-commonjs@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz#417af3b54503878e084d127adf4d1caf8beb86fb" + integrity sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q== + dependencies: + estree-walker "^0.6.1" + is-reference "^1.1.2" + magic-string "^0.25.2" + resolve "^1.11.0" + rollup-pluginutils "^2.8.1" + +rollup-plugin-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-json/-/rollup-plugin-json-4.0.0.tgz#a18da0a4b30bf5ca1ee76ddb1422afbb84ae2b9e" + integrity sha512-hgb8N7Cgfw5SZAkb3jf0QXii6QX/FOkiIq2M7BAQIEydjHvTyxXHQiIzZaTFgx1GK0cRCHOCBHIyEkkLdWKxow== + dependencies: + rollup-pluginutils "^2.5.0" + +rollup-plugin-node-builtins@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-builtins/-/rollup-plugin-node-builtins-2.1.2.tgz#24a1fed4a43257b6b64371d8abc6ce1ab14597e9" + integrity sha1-JKH+1KQyV7a2Q3HYq8bOGrFFl+k= + dependencies: + browserify-fs "^1.0.0" + buffer-es6 "^4.9.2" + crypto-browserify "^3.11.0" + process-es6 "^0.11.2" + +rollup-plugin-node-globals@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-globals/-/rollup-plugin-node-globals-1.4.0.tgz#5e1f24a9bb97c0ef51249f625e16c7e61b7c020b" + integrity sha512-xRkB+W/m1KLIzPUmG0ofvR+CPNcvuCuNdjVBVS7ALKSxr3EDhnzNceGkGi1m8MToSli13AzKFYH4ie9w3I5L3g== + dependencies: + acorn "^5.7.3" + buffer-es6 "^4.9.3" + estree-walker "^0.5.2" + magic-string "^0.22.5" + process-es6 "^0.11.6" + rollup-pluginutils "^2.3.1" + +rollup-plugin-node-resolve@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz#730f93d10ed202473b1fb54a5997a7db8c6d8523" + integrity sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw== + dependencies: + "@types/resolve" "0.0.8" + builtin-modules "^3.1.0" + is-module "^1.0.0" + resolve "^1.11.1" + rollup-pluginutils "^2.8.1" + +rollup-plugin-terser@^5.0.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-5.3.1.tgz#8c650062c22a8426c64268548957463bf981b413" + integrity sha512-1pkwkervMJQGFYvM9nscrUoncPwiKR/K+bHdjv6PFgRo3cgPHoRT83y2Aa3GvINj4539S15t/tpFPb775TDs6w== + dependencies: + "@babel/code-frame" "^7.5.5" + jest-worker "^24.9.0" + rollup-pluginutils "^2.8.2" + serialize-javascript "^4.0.0" + terser "^4.6.2" + +rollup-pluginutils@^2.3.1, rollup-pluginutils@^2.5.0, rollup-pluginutils@^2.8.1, rollup-pluginutils@^2.8.2: + version "2.8.2" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz#72f2af0748b592364dbd3389e600e5a9444a351e" + integrity sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ== + dependencies: + estree-walker "^0.6.1" + +rollup@^1.12.3: + version "1.32.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.32.1.tgz#4480e52d9d9e2ae4b46ba0d9ddeaf3163940f9c4" + integrity sha512-/2HA0Ec70TvQnXdzynFffkjA6XN+1e2pEv/uKS5Ulca40g2L7KuOE3riasHoNVHOsFD5KKZgDsMk1CP3Tw9s+A== + dependencies: + "@types/estree" "*" + "@types/node" "*" + acorn "^7.1.0" + +rxjs@^6.4.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safer-buffer@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-2.3.2.tgz#b9848f25d6cf36333073ec9ef8856d42f1233e52" + integrity sha1-uYSPJdbPNjMwc+ye+IVtQvEjPlI= + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +socket.io-adapter@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" + integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== + +socket.io-client@2.4.0, socket.io-client@^2.2.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.4.0.tgz#aafb5d594a3c55a34355562fc8aea22ed9119a35" + integrity sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ== + dependencies: + backo2 "1.0.2" + component-bind "1.0.0" + component-emitter "~1.3.0" + debug "~3.1.0" + engine.io-client "~3.5.0" + has-binary2 "~1.0.2" + indexof "0.0.1" + parseqs "0.0.6" + parseuri "0.0.6" + socket.io-parser "~3.3.0" + to-array "0.1.4" + +socket.io-parser@~3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6" + integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg== + dependencies: + component-emitter "~1.3.0" + debug "~3.1.0" + isarray "2.0.1" + +socket.io-parser@~3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.4.1.tgz#b06af838302975837eab2dc980037da24054d64a" + integrity sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A== + dependencies: + component-emitter "1.2.1" + debug "~4.1.0" + isarray "2.0.1" + +socket.io@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.4.1.tgz#95ad861c9a52369d7f1a68acf0d4a1b16da451d2" + integrity sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w== + dependencies: + debug "~4.1.0" + engine.io "~3.5.0" + has-binary2 "~1.0.2" + socket.io-adapter "~1.1.0" + socket.io-client "2.4.0" + socket.io-parser "~3.4.0" + +source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sourcemap-codec@^1.4.4: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + +string-range@~1.2, string-range@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd" + integrity sha1-qJPtNH5yKZvIO++78qaSqNI51d0= + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +terser@^4.6.2: + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== + dependencies: + commander "^2.20.0" + source-map "~0.6.1" + source-map-support "~0.5.12" + +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= + +tslib@^1.9.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +typedarray-to-buffer@~1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-1.0.4.tgz#9bb8ba0e841fb3f4cf1fe7c245e9f3fa8a5fe99c" + integrity sha1-m7i6DoQfs/TPH+fCRenz+opf6Zw= + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +vlq@^0.2.2: + version "0.2.3" + resolved "https://registry.yarnpkg.com/vlq/-/vlq-0.2.3.tgz#8f3e4328cf63b1540c0d67e1b2778386f8975b26" + integrity sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^7.0.0, ws@~7.4.2: + version "7.4.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" + integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== + +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= + +xtend@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.2.0.tgz#eef6b1f198c1c8deafad8b1765a04dad4a01c5a9" + integrity sha1-7vax8ZjByN6vrYsXZaBNrUoBxak= + +xtend@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.0.6.tgz#5ea657a6dba447069c2e59c58a1138cb0c5e6cee" + integrity sha1-XqZXptukRwacLlnFihE4ywxebO4= + dependencies: + is-object "~0.1.2" + object-keys "~0.2.0" + +xtend@~2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + +xtend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-3.0.0.tgz#5cce7407baf642cba7becda568111c493f59665a" + integrity sha1-XM50B7r2Qsunvs2laBEcST9ZZlo= + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= From d594c5cdc7bfa4d4914409f5deaa04111bc75735 Mon Sep 17 00:00:00 2001 From: zlq4863947 Date: Thu, 11 Feb 2021 00:56:47 +0900 Subject: [PATCH 2/2] fix rest.ts --- modules/exchanges/crypto/bitmex/rest/rest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exchanges/crypto/bitmex/rest/rest.ts b/modules/exchanges/crypto/bitmex/rest/rest.ts index 7ac11dd..dc93d5f 100644 --- a/modules/exchanges/crypto/bitmex/rest/rest.ts +++ b/modules/exchanges/crypto/bitmex/rest/rest.ts @@ -57,8 +57,8 @@ export class Rest extends RestBase { filter: { ordType: OrderType.Stop, }, - } - }); + }); + } getActiveStopLimitOrder(symbol: string): Observable> { return this.fetchOrder({