@yuants/vendor-okx 0.22.4 → 0.22.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/quote.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Terminal } from '@yuants/protocol';
2
2
  import { writeToSQL } from '@yuants/sql';
3
- import { decodePath, encodePath, listWatch } from '@yuants/utils';
4
- import { defer, filter, from, groupBy, map, merge, mergeMap, Observable, repeat, retry, scan, share, shareReplay, tap, toArray, } from 'rxjs';
3
+ import { decodePath, encodePath, formatTime, listWatch } from '@yuants/utils';
4
+ import { catchError, defer, EMPTY, filter, from, groupBy, map, merge, mergeMap, Observable, repeat, retry, scan, share, shareReplay, tap, timeout, toArray, } from 'rxjs';
5
5
  import { client } from './api';
6
6
  import { OKXWsClient } from './websocket';
7
7
  const wsPool = [];
@@ -27,15 +27,30 @@ const getWsClient = () => {
27
27
  wsPool.push({ client: newClient, requests: 1, isFull: false });
28
28
  return newClient;
29
29
  };
30
- const fromWsChannelAndInstId = (channel, instId) => new Observable((subscriber) => {
30
+ const fromWsChannelAndInstId = (channel, instId) => defer(() => new Observable((subscriber) => {
31
31
  const client = getWsClient();
32
32
  client.subscribe(channel, instId, (data) => {
33
33
  subscriber.next(data);
34
34
  });
35
+ client.ws.addEventListener('error', (err) => {
36
+ subscriber.error(err);
37
+ });
38
+ client.ws.addEventListener('close', () => {
39
+ subscriber.error('WS Connection Closed');
40
+ });
35
41
  subscriber.add(() => {
36
42
  client.unsubscribe(channel, instId);
37
43
  });
38
- });
44
+ })).pipe(
45
+ // 防止单个连接断开导致数据流关闭
46
+ timeout(60000), tap({
47
+ error: (err) => {
48
+ console.info(formatTime(Date.now()), 'WS_SUBSCRIBE_ERROR', channel, instId, err);
49
+ },
50
+ }),
51
+ // 暂时不太确定是否能支持 retry
52
+ // retry({ delay: 1000 }),
53
+ catchError(() => EMPTY));
39
54
  const swapInstruments$ = defer(() => client.getInstruments({ instType: 'SWAP' })).pipe(repeat({ delay: 3600000 }), retry({ delay: 10000 }), map((x) => x.data), shareReplay(1));
40
55
  const spotInstruments$ = defer(() => client.getInstruments({ instType: 'SPOT' })).pipe(repeat({ delay: 3600000 }), retry({ delay: 10000 }), map((x) => x.data), shareReplay(1));
41
56
  const spotTickers$ = defer(() => client.getMarketTickers({ instType: 'SPOT' })).pipe(repeat({ delay: 5000 }), retry({ delay: 5000 }), shareReplay(1));
@@ -44,42 +59,80 @@ export const spotMarketTickers$ = defer(() => spotTickers$).pipe(mergeMap((x) =>
44
59
  const spotTicker$ = spotInstruments$.pipe(tap((x) => {
45
60
  console.info('SPOT INSTRUMENTS', x.length);
46
61
  }), listWatch((x) => x.instId, (x) => fromWsChannelAndInstId('tickers', x.instId), () => true), share());
47
- const quote1$ = swapInstruments$.pipe(listWatch((x) => x.instId, (x) => fromWsChannelAndInstId('tickers', x.instId), () => true), map((ticker) => ({
62
+ const quoteOfSwapFromRest$ = defer(() => client.getMarketTickers({ instType: 'SWAP' })).pipe(mergeMap((x) => x.data || []), map((x) => ({
48
63
  datasource_id: 'OKX',
49
- product_id: encodePath('SWAP', ticker.instId),
50
- last_price: ticker.last,
51
- ask_price: ticker.askPx,
52
- bid_price: ticker.bidPx,
53
- ask_volume: ticker.askSz,
54
- bid_volume: ticker.bidSz,
55
- })), share());
56
- const quote2$ = spotTicker$.pipe(map((ticker) => ({
64
+ product_id: encodePath(x.instType, x.instId),
65
+ last_price: x.last,
66
+ ask_price: x.askPx,
67
+ bid_price: x.bidPx,
68
+ ask_volume: x.askSz,
69
+ bid_volume: x.bidSz,
70
+ })), repeat({ delay: 1000 }), retry({ delay: 1000 }));
71
+ const quoteOfSpotAndMarginFromRest$ = defer(() => client.getMarketTickers({ instType: 'SPOT' })).pipe(mergeMap((x) => x.data || []), mergeMap((x) => [
72
+ {
73
+ datasource_id: 'OKX',
74
+ product_id: encodePath('SPOT', x.instId),
75
+ last_price: x.last,
76
+ ask_price: x.askPx,
77
+ bid_price: x.bidPx,
78
+ ask_volume: x.askSz,
79
+ bid_volume: x.bidSz,
80
+ },
81
+ {
82
+ datasource_id: 'OKX',
83
+ product_id: encodePath('MARGIN', x.instId),
84
+ last_price: x.last,
85
+ ask_price: x.askPx,
86
+ bid_price: x.bidPx,
87
+ ask_volume: x.askSz,
88
+ bid_volume: x.bidSz,
89
+ },
90
+ ]), repeat({ delay: 1000 }), retry({ delay: 1000 }));
91
+ const quoteOfSwapFromWs$ = swapInstruments$.pipe(listWatch((x) => x.instId, (x) => fromWsChannelAndInstId('tickers', x.instId), () => true), map((ticker) => ({
57
92
  datasource_id: 'OKX',
58
- product_id: encodePath('SPOT', ticker.instId),
59
- last_price: ticker.last,
60
- ask_price: ticker.askPx,
61
- bid_price: ticker.bidPx,
62
- ask_volume: ticker.askSz,
63
- bid_volume: ticker.bidSz,
64
- })), share());
65
- const quote3$ = spotTicker$.pipe(map((ticker) => ({
66
- datasource_id: 'OKX',
67
- product_id: encodePath('MARGIN', ticker.instId),
93
+ product_id: encodePath('SWAP', ticker.instId),
68
94
  last_price: ticker.last,
69
95
  ask_price: ticker.askPx,
70
96
  bid_price: ticker.bidPx,
71
97
  ask_volume: ticker.askSz,
72
98
  bid_volume: ticker.bidSz,
73
- })), share());
99
+ })));
100
+ const quoteOfSpotAndMarginFromWs$ = spotTicker$.pipe(mergeMap((ticker) => [
101
+ {
102
+ datasource_id: 'OKX',
103
+ product_id: encodePath('SPOT', ticker.instId),
104
+ last_price: ticker.last,
105
+ ask_price: ticker.askPx,
106
+ bid_price: ticker.bidPx,
107
+ ask_volume: ticker.askSz,
108
+ bid_volume: ticker.bidSz,
109
+ },
110
+ {
111
+ datasource_id: 'OKX',
112
+ product_id: encodePath('MARGIN', ticker.instId),
113
+ last_price: ticker.last,
114
+ ask_price: ticker.askPx,
115
+ bid_price: ticker.bidPx,
116
+ ask_volume: ticker.askSz,
117
+ bid_volume: ticker.bidSz,
118
+ },
119
+ ]));
74
120
  const swapOpenInterests$ = defer(() => client.getOpenInterest({ instType: 'SWAP' })).pipe(repeat({ delay: 10000 }), retry({ delay: 10000 }), shareReplay(1));
75
- const quote4$ = swapInstruments$.pipe(listWatch((x) => x.instId, (x) => fromWsChannelAndInstId('open-interest', x.instId), () => true), map((x) => ({
121
+ const interestRateOfSwapFromWS$ = swapInstruments$.pipe(listWatch((x) => x.instId, (x) => fromWsChannelAndInstId('open-interest', x.instId), () => true), map((x) => ({
76
122
  datasource_id: 'OKX',
77
123
  product_id: encodePath('SWAP', x.instId),
78
124
  open_interest: x.oi,
79
125
  })), share());
80
- const quote$ = merge(
81
- //
82
- quote1$, quote2$, quote3$, quote4$).pipe(groupBy((x) => encodePath(x.datasource_id, x.product_id)), mergeMap((group$) => {
126
+ const quoteSources$ = [
127
+ quoteOfSwapFromWs$,
128
+ interestRateOfSwapFromWS$,
129
+ quoteOfSwapFromRest$,
130
+ quoteOfSpotAndMarginFromWs$,
131
+ quoteOfSpotAndMarginFromRest$,
132
+ ];
133
+ const quote$ = defer(() => merge(...quoteSources$.map((x$) => defer(() => x$).pipe(
134
+ // 防止单个流关闭导致整体关闭
135
+ catchError(() => EMPTY))))).pipe(groupBy((x) => encodePath(x.datasource_id, x.product_id)), mergeMap((group$) => {
83
136
  return group$.pipe(
84
137
  //
85
138
  scan((acc, cur) => Object.assign(acc, cur), {}));
package/dist/quote.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath, listWatch } from '@yuants/utils';\nimport {\n defer,\n filter,\n from,\n groupBy,\n map,\n merge,\n mergeMap,\n Observable,\n repeat,\n retry,\n scan,\n share,\n shareReplay,\n tap,\n toArray,\n} from 'rxjs';\nimport { client } from './api';\nimport { OKXWsClient } from './websocket';\n\nconst wsPool: {\n client: OKXWsClient;\n requests: number;\n isFull: boolean;\n}[] = [];\n\n// ISSUE: 连接限制:3 次/秒 (基于IP)\n//\n// https://www.okx.com/docs-v5/zh/#overview-websocket-connect\n//\n// 当订阅公有频道时,使用公有服务的地址;当订阅私有频道时,使用私有服务的地址\n//\n// 请求限制:\n//\n// 每个连接 对于 订阅/取消订阅/登录 请求的总次数限制为 480 次/小时\nconst getWsClient = () => {\n const existing = wsPool.find((item) => !item.isFull);\n if (existing) {\n existing.requests++;\n if (existing.requests >= 480) {\n existing.isFull = true;\n }\n return existing.client;\n }\n const newClient = new OKXWsClient('ws/v5/public');\n wsPool.push({ client: newClient, requests: 1, isFull: false });\n return newClient;\n};\n\nconst fromWsChannelAndInstId = (channel: string, instId: string) =>\n new Observable<any>((subscriber) => {\n const client = getWsClient();\n client.subscribe(channel, instId, (data: any) => {\n subscriber.next(data);\n });\n subscriber.add(() => {\n client.unsubscribe(channel, instId);\n });\n });\n\nconst swapInstruments$ = defer(() => client.getInstruments({ instType: 'SWAP' })).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 10_000 }),\n map((x) => x.data),\n shareReplay(1),\n);\nconst spotInstruments$ = defer(() => client.getInstruments({ instType: 'SPOT' })).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 10_000 }),\n map((x) => x.data),\n shareReplay(1),\n);\n\nconst spotTickers$ = defer(() => client.getMarketTickers({ instType: 'SPOT' })).pipe(\n repeat({ delay: 5000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\n// depend by SubmitOrders\nexport const spotMarketTickers$ = defer(() => spotTickers$).pipe(\n mergeMap((x) =>\n from(x.data).pipe(\n map((x) => [x.instId, x] as const),\n toArray(),\n map((x) => Object.fromEntries(x)),\n ),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\nconst spotTicker$ = spotInstruments$.pipe(\n tap((x) => {\n console.info('SPOT INSTRUMENTS', x.length);\n }),\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('tickers', x.instId),\n () => true,\n ),\n share(),\n);\n\nconst quote1$ = swapInstruments$.pipe(\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('tickers', x.instId),\n () => true,\n ),\n map((ticker) => ({\n datasource_id: 'OKX',\n product_id: encodePath('SWAP', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n })),\n share(),\n);\n\nconst quote2$ = spotTicker$.pipe(\n map((ticker) => ({\n datasource_id: 'OKX',\n product_id: encodePath('SPOT', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n })),\n share(),\n);\n\nconst quote3$ = spotTicker$.pipe(\n map((ticker) => ({\n datasource_id: 'OKX',\n product_id: encodePath('MARGIN', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n })),\n share(),\n);\n\nconst swapOpenInterests$ = defer(() => client.getOpenInterest({ instType: 'SWAP' })).pipe(\n repeat({ delay: 10_000 }),\n retry({ delay: 10_000 }),\n shareReplay(1),\n);\n\nconst quote4$ = swapInstruments$.pipe(\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('open-interest', x.instId),\n () => true,\n ),\n map(\n (x): Partial<IQuote> => ({\n datasource_id: 'OKX',\n product_id: encodePath('SWAP', x.instId),\n open_interest: x.oi,\n }),\n ),\n share(),\n);\n\nconst quote$ = merge(\n //\n quote1$,\n quote2$,\n quote3$,\n quote4$,\n).pipe(\n groupBy((x) => encodePath(x.datasource_id, x.product_id)),\n mergeMap((group$) => {\n return group$.pipe(\n //\n scan((acc, cur) => Object.assign(acc, cur), {} as Partial<IQuote>),\n );\n }),\n share(),\n);\n\n// 合并不同来源的数据并进行合并,避免死锁\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n Terminal.fromNodeEnv().channel.publishChannel('quote', { pattern: `^OKX/` }, (channel_id) => {\n const [datasource_id, product_id] = decodePath(channel_id);\n if (!datasource_id) {\n throw 'datasource_id is required';\n }\n if (!product_id) {\n throw 'product_id is required';\n }\n return quote$.pipe(filter((x) => x.product_id === product_id));\n });\n\n quote$\n .pipe(\n writeToSQL({\n terminal: Terminal.fromNodeEnv(),\n writeInterval: 1000,\n tableName: 'quote',\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n}\n\nexport const swapOpenInterest$ = defer(() => swapOpenInterests$).pipe(\n map((x) => new Map(x.data.map((x: any) => [x.instId, +x.oi] as const))),\n shareReplay(1),\n);\n"]}
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EACL,UAAU,EACV,KAAK,EACL,KAAK,EACL,MAAM,EACN,IAAI,EACJ,OAAO,EACP,GAAG,EACH,KAAK,EACL,QAAQ,EACR,UAAU,EACV,MAAM,EACN,KAAK,EACL,IAAI,EACJ,KAAK,EACL,WAAW,EACX,GAAG,EACH,OAAO,EACP,OAAO,GACR,MAAM,MAAM,CAAC;AACd,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,MAAM,GAIN,EAAE,CAAC;AAET,2BAA2B;AAC3B,EAAE;AACF,6DAA6D;AAC7D,EAAE;AACF,wCAAwC;AACxC,EAAE;AACF,QAAQ;AACR,EAAE;AACF,wCAAwC;AACxC,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrD,IAAI,QAAQ,EAAE;QACZ,QAAQ,CAAC,QAAQ,EAAE,CAAC;QACpB,IAAI,QAAQ,CAAC,QAAQ,IAAI,GAAG,EAAE;YAC5B,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC;SACxB;QACD,OAAO,QAAQ,CAAC,MAAM,CAAC;KACxB;IACD,MAAM,SAAS,GAAG,IAAI,WAAW,CAAC,cAAc,CAAC,CAAC;IAClD,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,OAAO,SAAS,CAAC;AACnB,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAAC,OAAe,EAAE,MAAc,EAAE,EAAE,CACjE,KAAK,CACH,GAAG,EAAE,CACH,IAAI,UAAU,CAAM,CAAC,UAAU,EAAE,EAAE;IACjC,MAAM,MAAM,GAAG,WAAW,EAAE,CAAC;IAC7B,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,IAAS,EAAE,EAAE;QAC9C,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QAC1C,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;QACvC,UAAU,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IACH,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE;QAClB,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CACL,CAAC,IAAI;AACJ,kBAAkB;AAClB,OAAO,CAAC,KAAM,CAAC,EACf,GAAG,CAAC;IACF,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;QACb,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC;IACnF,CAAC;CACF,CAAC;AACF,oBAAoB;AACpB,0BAA0B;AAC1B,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CACxB,CAAC;AAEJ,MAAM,gBAAgB,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CACpF,MAAM,CAAC,EAAE,KAAK,EAAE,OAAQ,EAAE,CAAC,EAC3B,KAAK,CAAC,EAAE,KAAK,EAAE,KAAM,EAAE,CAAC,EACxB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAClB,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;AACF,MAAM,gBAAgB,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CACpF,MAAM,CAAC,EAAE,KAAK,EAAE,OAAQ,EAAE,CAAC,EAC3B,KAAK,CAAC,EAAE,KAAK,EAAE,KAAM,EAAE,CAAC,EACxB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAClB,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;AAEF,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAClF,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACvB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACtB,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;AAEF,yBAAyB;AACzB,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,IAAI,CAC9D,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CACb,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CACf,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAU,CAAC,EAClC,OAAO,EAAE,EACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAClC,CACF,EACD,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACvB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACtB,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;AAEF,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CACvC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;IACR,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;AAC7C,CAAC,CAAC,EACF,SAAS,CACP,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EACf,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,EAClD,GAAG,EAAE,CAAC,IAAI,CACX,EACD,KAAK,EAAE,CACR,CAAC;AAEF,MAAM,oBAAoB,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAC1F,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,EAC7B,GAAG,CACD,CAAC,CAAC,EAAmB,EAAE,CAAC,CAAC;IACvB,aAAa,EAAE,KAAK;IACpB,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;IAC5C,UAAU,EAAE,CAAC,CAAC,IAAI;IAClB,SAAS,EAAE,CAAC,CAAC,KAAK;IAClB,SAAS,EAAE,CAAC,CAAC,KAAK;IAClB,UAAU,EAAE,CAAC,CAAC,KAAK;IACnB,UAAU,EAAE,CAAC,CAAC,KAAK;CACpB,CAAC,CACH,EACD,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACvB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CACvB,CAAC;AAEF,MAAM,6BAA6B,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CACnG,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,EAC7B,QAAQ,CAAC,CAAC,CAAC,EAAqB,EAAE,CAAC;IACjC;QACE,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACxC,UAAU,EAAE,CAAC,CAAC,IAAI;QAClB,SAAS,EAAE,CAAC,CAAC,KAAK;QAClB,SAAS,EAAE,CAAC,CAAC,KAAK;QAClB,UAAU,EAAE,CAAC,CAAC,KAAK;QACnB,UAAU,EAAE,CAAC,CAAC,KAAK;KACpB;IACD;QACE,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;QAC1C,UAAU,EAAE,CAAC,CAAC,IAAI;QAClB,SAAS,EAAE,CAAC,CAAC,KAAK;QAClB,SAAS,EAAE,CAAC,CAAC,KAAK;QAClB,UAAU,EAAE,CAAC,CAAC,KAAK;QACnB,UAAU,EAAE,CAAC,CAAC,KAAK;KACpB;CACF,CAAC,EACF,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACvB,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CACvB,CAAC;AAEF,MAAM,kBAAkB,GAAG,gBAAgB,CAAC,IAAI,CAC9C,SAAS,CACP,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EACf,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,SAAS,EAAE,CAAC,CAAC,MAAM,CAAC,EAClD,GAAG,EAAE,CAAC,IAAI,CACX,EACD,GAAG,CACD,CAAC,MAAM,EAAmB,EAAE,CAAC,CAAC;IAC5B,aAAa,EAAE,KAAK;IACpB,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;IAC7C,UAAU,EAAE,MAAM,CAAC,IAAI;IACvB,SAAS,EAAE,MAAM,CAAC,KAAK;IACvB,SAAS,EAAE,MAAM,CAAC,KAAK;IACvB,UAAU,EAAE,MAAM,CAAC,KAAK;IACxB,UAAU,EAAE,MAAM,CAAC,KAAK;CACzB,CAAC,CACH,CACF,CAAC;AAEF,MAAM,2BAA2B,GAAG,WAAW,CAAC,IAAI,CAClD,QAAQ,CAAC,CAAC,MAAM,EAAqB,EAAE,CAAC;IACtC;QACE,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;QAC7C,UAAU,EAAE,MAAM,CAAC,IAAI;QACvB,SAAS,EAAE,MAAM,CAAC,KAAK;QACvB,SAAS,EAAE,MAAM,CAAC,KAAK;QACvB,UAAU,EAAE,MAAM,CAAC,KAAK;QACxB,UAAU,EAAE,MAAM,CAAC,KAAK;KACzB;IACD;QACE,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC;QAC/C,UAAU,EAAE,MAAM,CAAC,IAAI;QACvB,SAAS,EAAE,MAAM,CAAC,KAAK;QACvB,SAAS,EAAE,MAAM,CAAC,KAAK;QACvB,UAAU,EAAE,MAAM,CAAC,KAAK;QACxB,UAAU,EAAE,MAAM,CAAC,KAAK;KACzB;CACF,CAAC,CACH,CAAC;AAEF,MAAM,kBAAkB,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CACvF,MAAM,CAAC,EAAE,KAAK,EAAE,KAAM,EAAE,CAAC,EACzB,KAAK,CAAC,EAAE,KAAK,EAAE,KAAM,EAAE,CAAC,EACxB,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;AAEF,MAAM,yBAAyB,GAAG,gBAAgB,CAAC,IAAI,CACrD,SAAS,CACP,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,EACf,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,eAAe,EAAE,CAAC,CAAC,MAAM,CAAC,EACxD,GAAG,EAAE,CAAC,IAAI,CACX,EACD,GAAG,CACD,CAAC,CAAC,EAAmB,EAAE,CAAC,CAAC;IACvB,aAAa,EAAE,KAAK;IACpB,UAAU,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;IACxC,aAAa,EAAE,CAAC,CAAC,EAAE;CACpB,CAAC,CACH,EACD,KAAK,EAAE,CACR,CAAC;AAEF,MAAM,aAAa,GAAG;IACpB,kBAAkB;IAClB,yBAAyB;IACzB,oBAAoB;IACpB,2BAA2B;IAC3B,6BAA6B;CAC9B,CAAC;AAEF,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,EAAE,CACxB,KAAK,CACH,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAC1B,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,IAAI;AAClB,gBAAgB;AAChB,UAAU,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CACxB,CACF,CACF,CACF,CAAC,IAAI,CACJ,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,EACzD,QAAQ,CAAC,CAAC,MAAM,EAAE,EAAE;IAClB,OAAO,MAAM,CAAC,IAAI;IAChB,EAAE;IACF,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAqB,CAAC,CACnE,CAAC;AACJ,CAAC,CAAC,EACF,KAAK,EAAE,CACR,CAAC;AAEF,sBAAsB;AACtB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,MAAM,EAAE;IAC7C,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,UAAU,EAAE,EAAE;QAC1F,MAAM,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;QAC3D,IAAI,CAAC,aAAa,EAAE;YAClB,MAAM,2BAA2B,CAAC;SACnC;QACD,IAAI,CAAC,UAAU,EAAE;YACf,MAAM,wBAAwB,CAAC;SAChC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,MAAM;SACH,IAAI,CACH,UAAU,CAAC;QACT,QAAQ,EAAE,QAAQ,CAAC,WAAW,EAAE;QAChC,aAAa,EAAE,IAAI;QACnB,SAAS,EAAE,OAAO;QAClB,YAAY,EAAE,CAAC,eAAe,EAAE,YAAY,CAAC;KAC9C,CAAC,CACH;SACA,SAAS,EAAE,CAAC;CAChB;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,CAAC,IAAI,CACnE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAU,CAAC,CAAC,CAAC,EACvE,WAAW,CAAC,CAAC,CAAC,CACf,CAAC","sourcesContent":["import { IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath, formatTime, listWatch } from '@yuants/utils';\nimport {\n catchError,\n defer,\n EMPTY,\n filter,\n from,\n groupBy,\n map,\n merge,\n mergeMap,\n Observable,\n repeat,\n retry,\n scan,\n share,\n shareReplay,\n tap,\n timeout,\n toArray,\n} from 'rxjs';\nimport { client } from './api';\nimport { OKXWsClient } from './websocket';\n\nconst wsPool: {\n client: OKXWsClient;\n requests: number;\n isFull: boolean;\n}[] = [];\n\n// ISSUE: 连接限制:3 次/秒 (基于IP)\n//\n// https://www.okx.com/docs-v5/zh/#overview-websocket-connect\n//\n// 当订阅公有频道时,使用公有服务的地址;当订阅私有频道时,使用私有服务的地址\n//\n// 请求限制:\n//\n// 每个连接 对于 订阅/取消订阅/登录 请求的总次数限制为 480 次/小时\nconst getWsClient = () => {\n const existing = wsPool.find((item) => !item.isFull);\n if (existing) {\n existing.requests++;\n if (existing.requests >= 480) {\n existing.isFull = true;\n }\n return existing.client;\n }\n const newClient = new OKXWsClient('ws/v5/public');\n wsPool.push({ client: newClient, requests: 1, isFull: false });\n return newClient;\n};\n\nconst fromWsChannelAndInstId = (channel: string, instId: string) =>\n defer(\n () =>\n new Observable<any>((subscriber) => {\n const client = getWsClient();\n client.subscribe(channel, instId, (data: any) => {\n subscriber.next(data);\n });\n client.ws.addEventListener('error', (err) => {\n subscriber.error(err);\n });\n client.ws.addEventListener('close', () => {\n subscriber.error('WS Connection Closed');\n });\n subscriber.add(() => {\n client.unsubscribe(channel, instId);\n });\n }),\n ).pipe(\n // 防止单个连接断开导致数据流关闭\n timeout(60_000),\n tap({\n error: (err) => {\n console.info(formatTime(Date.now()), 'WS_SUBSCRIBE_ERROR', channel, instId, err);\n },\n }),\n // 暂时不太确定是否能支持 retry\n // retry({ delay: 1000 }),\n catchError(() => EMPTY),\n );\n\nconst swapInstruments$ = defer(() => client.getInstruments({ instType: 'SWAP' })).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 10_000 }),\n map((x) => x.data),\n shareReplay(1),\n);\nconst spotInstruments$ = defer(() => client.getInstruments({ instType: 'SPOT' })).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 10_000 }),\n map((x) => x.data),\n shareReplay(1),\n);\n\nconst spotTickers$ = defer(() => client.getMarketTickers({ instType: 'SPOT' })).pipe(\n repeat({ delay: 5000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\n// depend by SubmitOrders\nexport const spotMarketTickers$ = defer(() => spotTickers$).pipe(\n mergeMap((x) =>\n from(x.data).pipe(\n map((x) => [x.instId, x] as const),\n toArray(),\n map((x) => Object.fromEntries(x)),\n ),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\nconst spotTicker$ = spotInstruments$.pipe(\n tap((x) => {\n console.info('SPOT INSTRUMENTS', x.length);\n }),\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('tickers', x.instId),\n () => true,\n ),\n share(),\n);\n\nconst quoteOfSwapFromRest$ = defer(() => client.getMarketTickers({ instType: 'SWAP' })).pipe(\n mergeMap((x) => x.data || []),\n map(\n (x): Partial<IQuote> => ({\n datasource_id: 'OKX',\n product_id: encodePath(x.instType, x.instId),\n last_price: x.last,\n ask_price: x.askPx,\n bid_price: x.bidPx,\n ask_volume: x.askSz,\n bid_volume: x.bidSz,\n }),\n ),\n repeat({ delay: 1000 }),\n retry({ delay: 1000 }),\n);\n\nconst quoteOfSpotAndMarginFromRest$ = defer(() => client.getMarketTickers({ instType: 'SPOT' })).pipe(\n mergeMap((x) => x.data || []),\n mergeMap((x): Partial<IQuote>[] => [\n {\n datasource_id: 'OKX',\n product_id: encodePath('SPOT', x.instId),\n last_price: x.last,\n ask_price: x.askPx,\n bid_price: x.bidPx,\n ask_volume: x.askSz,\n bid_volume: x.bidSz,\n },\n {\n datasource_id: 'OKX',\n product_id: encodePath('MARGIN', x.instId),\n last_price: x.last,\n ask_price: x.askPx,\n bid_price: x.bidPx,\n ask_volume: x.askSz,\n bid_volume: x.bidSz,\n },\n ]),\n repeat({ delay: 1000 }),\n retry({ delay: 1000 }),\n);\n\nconst quoteOfSwapFromWs$ = swapInstruments$.pipe(\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('tickers', x.instId),\n () => true,\n ),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'OKX',\n product_id: encodePath('SWAP', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n }),\n ),\n);\n\nconst quoteOfSpotAndMarginFromWs$ = spotTicker$.pipe(\n mergeMap((ticker): Partial<IQuote>[] => [\n {\n datasource_id: 'OKX',\n product_id: encodePath('SPOT', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n },\n {\n datasource_id: 'OKX',\n product_id: encodePath('MARGIN', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n },\n ]),\n);\n\nconst swapOpenInterests$ = defer(() => client.getOpenInterest({ instType: 'SWAP' })).pipe(\n repeat({ delay: 10_000 }),\n retry({ delay: 10_000 }),\n shareReplay(1),\n);\n\nconst interestRateOfSwapFromWS$ = swapInstruments$.pipe(\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('open-interest', x.instId),\n () => true,\n ),\n map(\n (x): Partial<IQuote> => ({\n datasource_id: 'OKX',\n product_id: encodePath('SWAP', x.instId),\n open_interest: x.oi,\n }),\n ),\n share(),\n);\n\nconst quoteSources$ = [\n quoteOfSwapFromWs$,\n interestRateOfSwapFromWS$,\n quoteOfSwapFromRest$,\n quoteOfSpotAndMarginFromWs$,\n quoteOfSpotAndMarginFromRest$,\n];\n\nconst quote$ = defer(() =>\n merge(\n ...quoteSources$.map((x$) =>\n defer(() => x$).pipe(\n // 防止单个流关闭导致整体关闭\n catchError(() => EMPTY),\n ),\n ),\n ),\n).pipe(\n groupBy((x) => encodePath(x.datasource_id, x.product_id)),\n mergeMap((group$) => {\n return group$.pipe(\n //\n scan((acc, cur) => Object.assign(acc, cur), {} as Partial<IQuote>),\n );\n }),\n share(),\n);\n\n// 合并不同来源的数据并进行合并,避免死锁\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n Terminal.fromNodeEnv().channel.publishChannel('quote', { pattern: `^OKX/` }, (channel_id) => {\n const [datasource_id, product_id] = decodePath(channel_id);\n if (!datasource_id) {\n throw 'datasource_id is required';\n }\n if (!product_id) {\n throw 'product_id is required';\n }\n return quote$.pipe(filter((x) => x.product_id === product_id));\n });\n\n quote$\n .pipe(\n writeToSQL({\n terminal: Terminal.fromNodeEnv(),\n writeInterval: 1000,\n tableName: 'quote',\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n}\n\nexport const swapOpenInterest$ = defer(() => swapOpenInterests$).pipe(\n map((x) => new Map(x.data.map((x: any) => [x.instId, +x.oi] as const))),\n shareReplay(1),\n);\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"quote.d.ts","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"AAIA,OAAO,EAQL,UAAU,EAQX,MAAM,MAAM,CAAC;AAgEd,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;EAW9B,CAAC;AA0HF,eAAO,MAAM,iBAAiB,8BAG7B,CAAC"}
1
+ {"version":3,"file":"quote.d.ts","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"AAIA,OAAO,EAUL,UAAU,EASX,MAAM,MAAM,CAAC;AAoFd,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;EAW9B,CAAC;AA8KF,eAAO,MAAM,iBAAiB,8BAG7B,CAAC"}
package/lib/quote.js CHANGED
@@ -30,15 +30,30 @@ const getWsClient = () => {
30
30
  wsPool.push({ client: newClient, requests: 1, isFull: false });
31
31
  return newClient;
32
32
  };
33
- const fromWsChannelAndInstId = (channel, instId) => new rxjs_1.Observable((subscriber) => {
33
+ const fromWsChannelAndInstId = (channel, instId) => (0, rxjs_1.defer)(() => new rxjs_1.Observable((subscriber) => {
34
34
  const client = getWsClient();
35
35
  client.subscribe(channel, instId, (data) => {
36
36
  subscriber.next(data);
37
37
  });
38
+ client.ws.addEventListener('error', (err) => {
39
+ subscriber.error(err);
40
+ });
41
+ client.ws.addEventListener('close', () => {
42
+ subscriber.error('WS Connection Closed');
43
+ });
38
44
  subscriber.add(() => {
39
45
  client.unsubscribe(channel, instId);
40
46
  });
41
- });
47
+ })).pipe(
48
+ // 防止单个连接断开导致数据流关闭
49
+ (0, rxjs_1.timeout)(60000), (0, rxjs_1.tap)({
50
+ error: (err) => {
51
+ console.info((0, utils_1.formatTime)(Date.now()), 'WS_SUBSCRIBE_ERROR', channel, instId, err);
52
+ },
53
+ }),
54
+ // 暂时不太确定是否能支持 retry
55
+ // retry({ delay: 1000 }),
56
+ (0, rxjs_1.catchError)(() => rxjs_1.EMPTY));
42
57
  const swapInstruments$ = (0, rxjs_1.defer)(() => api_1.client.getInstruments({ instType: 'SWAP' })).pipe((0, rxjs_1.repeat)({ delay: 3600000 }), (0, rxjs_1.retry)({ delay: 10000 }), (0, rxjs_1.map)((x) => x.data), (0, rxjs_1.shareReplay)(1));
43
58
  const spotInstruments$ = (0, rxjs_1.defer)(() => api_1.client.getInstruments({ instType: 'SPOT' })).pipe((0, rxjs_1.repeat)({ delay: 3600000 }), (0, rxjs_1.retry)({ delay: 10000 }), (0, rxjs_1.map)((x) => x.data), (0, rxjs_1.shareReplay)(1));
44
59
  const spotTickers$ = (0, rxjs_1.defer)(() => api_1.client.getMarketTickers({ instType: 'SPOT' })).pipe((0, rxjs_1.repeat)({ delay: 5000 }), (0, rxjs_1.retry)({ delay: 5000 }), (0, rxjs_1.shareReplay)(1));
@@ -47,42 +62,80 @@ exports.spotMarketTickers$ = (0, rxjs_1.defer)(() => spotTickers$).pipe((0, rxjs
47
62
  const spotTicker$ = spotInstruments$.pipe((0, rxjs_1.tap)((x) => {
48
63
  console.info('SPOT INSTRUMENTS', x.length);
49
64
  }), (0, utils_1.listWatch)((x) => x.instId, (x) => fromWsChannelAndInstId('tickers', x.instId), () => true), (0, rxjs_1.share)());
50
- const quote1$ = swapInstruments$.pipe((0, utils_1.listWatch)((x) => x.instId, (x) => fromWsChannelAndInstId('tickers', x.instId), () => true), (0, rxjs_1.map)((ticker) => ({
65
+ const quoteOfSwapFromRest$ = (0, rxjs_1.defer)(() => api_1.client.getMarketTickers({ instType: 'SWAP' })).pipe((0, rxjs_1.mergeMap)((x) => x.data || []), (0, rxjs_1.map)((x) => ({
51
66
  datasource_id: 'OKX',
52
- product_id: (0, utils_1.encodePath)('SWAP', ticker.instId),
53
- last_price: ticker.last,
54
- ask_price: ticker.askPx,
55
- bid_price: ticker.bidPx,
56
- ask_volume: ticker.askSz,
57
- bid_volume: ticker.bidSz,
58
- })), (0, rxjs_1.share)());
59
- const quote2$ = spotTicker$.pipe((0, rxjs_1.map)((ticker) => ({
67
+ product_id: (0, utils_1.encodePath)(x.instType, x.instId),
68
+ last_price: x.last,
69
+ ask_price: x.askPx,
70
+ bid_price: x.bidPx,
71
+ ask_volume: x.askSz,
72
+ bid_volume: x.bidSz,
73
+ })), (0, rxjs_1.repeat)({ delay: 1000 }), (0, rxjs_1.retry)({ delay: 1000 }));
74
+ const quoteOfSpotAndMarginFromRest$ = (0, rxjs_1.defer)(() => api_1.client.getMarketTickers({ instType: 'SPOT' })).pipe((0, rxjs_1.mergeMap)((x) => x.data || []), (0, rxjs_1.mergeMap)((x) => [
75
+ {
76
+ datasource_id: 'OKX',
77
+ product_id: (0, utils_1.encodePath)('SPOT', x.instId),
78
+ last_price: x.last,
79
+ ask_price: x.askPx,
80
+ bid_price: x.bidPx,
81
+ ask_volume: x.askSz,
82
+ bid_volume: x.bidSz,
83
+ },
84
+ {
85
+ datasource_id: 'OKX',
86
+ product_id: (0, utils_1.encodePath)('MARGIN', x.instId),
87
+ last_price: x.last,
88
+ ask_price: x.askPx,
89
+ bid_price: x.bidPx,
90
+ ask_volume: x.askSz,
91
+ bid_volume: x.bidSz,
92
+ },
93
+ ]), (0, rxjs_1.repeat)({ delay: 1000 }), (0, rxjs_1.retry)({ delay: 1000 }));
94
+ const quoteOfSwapFromWs$ = swapInstruments$.pipe((0, utils_1.listWatch)((x) => x.instId, (x) => fromWsChannelAndInstId('tickers', x.instId), () => true), (0, rxjs_1.map)((ticker) => ({
60
95
  datasource_id: 'OKX',
61
- product_id: (0, utils_1.encodePath)('SPOT', ticker.instId),
62
- last_price: ticker.last,
63
- ask_price: ticker.askPx,
64
- bid_price: ticker.bidPx,
65
- ask_volume: ticker.askSz,
66
- bid_volume: ticker.bidSz,
67
- })), (0, rxjs_1.share)());
68
- const quote3$ = spotTicker$.pipe((0, rxjs_1.map)((ticker) => ({
69
- datasource_id: 'OKX',
70
- product_id: (0, utils_1.encodePath)('MARGIN', ticker.instId),
96
+ product_id: (0, utils_1.encodePath)('SWAP', ticker.instId),
71
97
  last_price: ticker.last,
72
98
  ask_price: ticker.askPx,
73
99
  bid_price: ticker.bidPx,
74
100
  ask_volume: ticker.askSz,
75
101
  bid_volume: ticker.bidSz,
76
- })), (0, rxjs_1.share)());
102
+ })));
103
+ const quoteOfSpotAndMarginFromWs$ = spotTicker$.pipe((0, rxjs_1.mergeMap)((ticker) => [
104
+ {
105
+ datasource_id: 'OKX',
106
+ product_id: (0, utils_1.encodePath)('SPOT', ticker.instId),
107
+ last_price: ticker.last,
108
+ ask_price: ticker.askPx,
109
+ bid_price: ticker.bidPx,
110
+ ask_volume: ticker.askSz,
111
+ bid_volume: ticker.bidSz,
112
+ },
113
+ {
114
+ datasource_id: 'OKX',
115
+ product_id: (0, utils_1.encodePath)('MARGIN', ticker.instId),
116
+ last_price: ticker.last,
117
+ ask_price: ticker.askPx,
118
+ bid_price: ticker.bidPx,
119
+ ask_volume: ticker.askSz,
120
+ bid_volume: ticker.bidSz,
121
+ },
122
+ ]));
77
123
  const swapOpenInterests$ = (0, rxjs_1.defer)(() => api_1.client.getOpenInterest({ instType: 'SWAP' })).pipe((0, rxjs_1.repeat)({ delay: 10000 }), (0, rxjs_1.retry)({ delay: 10000 }), (0, rxjs_1.shareReplay)(1));
78
- const quote4$ = swapInstruments$.pipe((0, utils_1.listWatch)((x) => x.instId, (x) => fromWsChannelAndInstId('open-interest', x.instId), () => true), (0, rxjs_1.map)((x) => ({
124
+ const interestRateOfSwapFromWS$ = swapInstruments$.pipe((0, utils_1.listWatch)((x) => x.instId, (x) => fromWsChannelAndInstId('open-interest', x.instId), () => true), (0, rxjs_1.map)((x) => ({
79
125
  datasource_id: 'OKX',
80
126
  product_id: (0, utils_1.encodePath)('SWAP', x.instId),
81
127
  open_interest: x.oi,
82
128
  })), (0, rxjs_1.share)());
83
- const quote$ = (0, rxjs_1.merge)(
84
- //
85
- quote1$, quote2$, quote3$, quote4$).pipe((0, rxjs_1.groupBy)((x) => (0, utils_1.encodePath)(x.datasource_id, x.product_id)), (0, rxjs_1.mergeMap)((group$) => {
129
+ const quoteSources$ = [
130
+ quoteOfSwapFromWs$,
131
+ interestRateOfSwapFromWS$,
132
+ quoteOfSwapFromRest$,
133
+ quoteOfSpotAndMarginFromWs$,
134
+ quoteOfSpotAndMarginFromRest$,
135
+ ];
136
+ const quote$ = (0, rxjs_1.defer)(() => (0, rxjs_1.merge)(...quoteSources$.map((x$) => (0, rxjs_1.defer)(() => x$).pipe(
137
+ // 防止单个流关闭导致整体关闭
138
+ (0, rxjs_1.catchError)(() => rxjs_1.EMPTY))))).pipe((0, rxjs_1.groupBy)((x) => (0, utils_1.encodePath)(x.datasource_id, x.product_id)), (0, rxjs_1.mergeMap)((group$) => {
86
139
  return group$.pipe(
87
140
  //
88
141
  (0, rxjs_1.scan)((acc, cur) => Object.assign(acc, cur), {}));
package/lib/quote.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath, listWatch } from '@yuants/utils';\nimport {\n defer,\n filter,\n from,\n groupBy,\n map,\n merge,\n mergeMap,\n Observable,\n repeat,\n retry,\n scan,\n share,\n shareReplay,\n tap,\n toArray,\n} from 'rxjs';\nimport { client } from './api';\nimport { OKXWsClient } from './websocket';\n\nconst wsPool: {\n client: OKXWsClient;\n requests: number;\n isFull: boolean;\n}[] = [];\n\n// ISSUE: 连接限制:3 次/秒 (基于IP)\n//\n// https://www.okx.com/docs-v5/zh/#overview-websocket-connect\n//\n// 当订阅公有频道时,使用公有服务的地址;当订阅私有频道时,使用私有服务的地址\n//\n// 请求限制:\n//\n// 每个连接 对于 订阅/取消订阅/登录 请求的总次数限制为 480 次/小时\nconst getWsClient = () => {\n const existing = wsPool.find((item) => !item.isFull);\n if (existing) {\n existing.requests++;\n if (existing.requests >= 480) {\n existing.isFull = true;\n }\n return existing.client;\n }\n const newClient = new OKXWsClient('ws/v5/public');\n wsPool.push({ client: newClient, requests: 1, isFull: false });\n return newClient;\n};\n\nconst fromWsChannelAndInstId = (channel: string, instId: string) =>\n new Observable<any>((subscriber) => {\n const client = getWsClient();\n client.subscribe(channel, instId, (data: any) => {\n subscriber.next(data);\n });\n subscriber.add(() => {\n client.unsubscribe(channel, instId);\n });\n });\n\nconst swapInstruments$ = defer(() => client.getInstruments({ instType: 'SWAP' })).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 10_000 }),\n map((x) => x.data),\n shareReplay(1),\n);\nconst spotInstruments$ = defer(() => client.getInstruments({ instType: 'SPOT' })).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 10_000 }),\n map((x) => x.data),\n shareReplay(1),\n);\n\nconst spotTickers$ = defer(() => client.getMarketTickers({ instType: 'SPOT' })).pipe(\n repeat({ delay: 5000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\n// depend by SubmitOrders\nexport const spotMarketTickers$ = defer(() => spotTickers$).pipe(\n mergeMap((x) =>\n from(x.data).pipe(\n map((x) => [x.instId, x] as const),\n toArray(),\n map((x) => Object.fromEntries(x)),\n ),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\nconst spotTicker$ = spotInstruments$.pipe(\n tap((x) => {\n console.info('SPOT INSTRUMENTS', x.length);\n }),\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('tickers', x.instId),\n () => true,\n ),\n share(),\n);\n\nconst quote1$ = swapInstruments$.pipe(\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('tickers', x.instId),\n () => true,\n ),\n map((ticker) => ({\n datasource_id: 'OKX',\n product_id: encodePath('SWAP', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n })),\n share(),\n);\n\nconst quote2$ = spotTicker$.pipe(\n map((ticker) => ({\n datasource_id: 'OKX',\n product_id: encodePath('SPOT', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n })),\n share(),\n);\n\nconst quote3$ = spotTicker$.pipe(\n map((ticker) => ({\n datasource_id: 'OKX',\n product_id: encodePath('MARGIN', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n })),\n share(),\n);\n\nconst swapOpenInterests$ = defer(() => client.getOpenInterest({ instType: 'SWAP' })).pipe(\n repeat({ delay: 10_000 }),\n retry({ delay: 10_000 }),\n shareReplay(1),\n);\n\nconst quote4$ = swapInstruments$.pipe(\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('open-interest', x.instId),\n () => true,\n ),\n map(\n (x): Partial<IQuote> => ({\n datasource_id: 'OKX',\n product_id: encodePath('SWAP', x.instId),\n open_interest: x.oi,\n }),\n ),\n share(),\n);\n\nconst quote$ = merge(\n //\n quote1$,\n quote2$,\n quote3$,\n quote4$,\n).pipe(\n groupBy((x) => encodePath(x.datasource_id, x.product_id)),\n mergeMap((group$) => {\n return group$.pipe(\n //\n scan((acc, cur) => Object.assign(acc, cur), {} as Partial<IQuote>),\n );\n }),\n share(),\n);\n\n// 合并不同来源的数据并进行合并,避免死锁\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n Terminal.fromNodeEnv().channel.publishChannel('quote', { pattern: `^OKX/` }, (channel_id) => {\n const [datasource_id, product_id] = decodePath(channel_id);\n if (!datasource_id) {\n throw 'datasource_id is required';\n }\n if (!product_id) {\n throw 'product_id is required';\n }\n return quote$.pipe(filter((x) => x.product_id === product_id));\n });\n\n quote$\n .pipe(\n writeToSQL({\n terminal: Terminal.fromNodeEnv(),\n writeInterval: 1000,\n tableName: 'quote',\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n}\n\nexport const swapOpenInterest$ = defer(() => swapOpenInterests$).pipe(\n map((x) => new Map(x.data.map((x: any) => [x.instId, +x.oi] as const))),\n shareReplay(1),\n);\n"]}
1
+ {"version":3,"file":"quote.js","sourceRoot":"","sources":["../src/quote.ts"],"names":[],"mappings":"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","sourcesContent":["import { IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { writeToSQL } from '@yuants/sql';\nimport { decodePath, encodePath, formatTime, listWatch } from '@yuants/utils';\nimport {\n catchError,\n defer,\n EMPTY,\n filter,\n from,\n groupBy,\n map,\n merge,\n mergeMap,\n Observable,\n repeat,\n retry,\n scan,\n share,\n shareReplay,\n tap,\n timeout,\n toArray,\n} from 'rxjs';\nimport { client } from './api';\nimport { OKXWsClient } from './websocket';\n\nconst wsPool: {\n client: OKXWsClient;\n requests: number;\n isFull: boolean;\n}[] = [];\n\n// ISSUE: 连接限制:3 次/秒 (基于IP)\n//\n// https://www.okx.com/docs-v5/zh/#overview-websocket-connect\n//\n// 当订阅公有频道时,使用公有服务的地址;当订阅私有频道时,使用私有服务的地址\n//\n// 请求限制:\n//\n// 每个连接 对于 订阅/取消订阅/登录 请求的总次数限制为 480 次/小时\nconst getWsClient = () => {\n const existing = wsPool.find((item) => !item.isFull);\n if (existing) {\n existing.requests++;\n if (existing.requests >= 480) {\n existing.isFull = true;\n }\n return existing.client;\n }\n const newClient = new OKXWsClient('ws/v5/public');\n wsPool.push({ client: newClient, requests: 1, isFull: false });\n return newClient;\n};\n\nconst fromWsChannelAndInstId = (channel: string, instId: string) =>\n defer(\n () =>\n new Observable<any>((subscriber) => {\n const client = getWsClient();\n client.subscribe(channel, instId, (data: any) => {\n subscriber.next(data);\n });\n client.ws.addEventListener('error', (err) => {\n subscriber.error(err);\n });\n client.ws.addEventListener('close', () => {\n subscriber.error('WS Connection Closed');\n });\n subscriber.add(() => {\n client.unsubscribe(channel, instId);\n });\n }),\n ).pipe(\n // 防止单个连接断开导致数据流关闭\n timeout(60_000),\n tap({\n error: (err) => {\n console.info(formatTime(Date.now()), 'WS_SUBSCRIBE_ERROR', channel, instId, err);\n },\n }),\n // 暂时不太确定是否能支持 retry\n // retry({ delay: 1000 }),\n catchError(() => EMPTY),\n );\n\nconst swapInstruments$ = defer(() => client.getInstruments({ instType: 'SWAP' })).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 10_000 }),\n map((x) => x.data),\n shareReplay(1),\n);\nconst spotInstruments$ = defer(() => client.getInstruments({ instType: 'SPOT' })).pipe(\n repeat({ delay: 3600_000 }),\n retry({ delay: 10_000 }),\n map((x) => x.data),\n shareReplay(1),\n);\n\nconst spotTickers$ = defer(() => client.getMarketTickers({ instType: 'SPOT' })).pipe(\n repeat({ delay: 5000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\n// depend by SubmitOrders\nexport const spotMarketTickers$ = defer(() => spotTickers$).pipe(\n mergeMap((x) =>\n from(x.data).pipe(\n map((x) => [x.instId, x] as const),\n toArray(),\n map((x) => Object.fromEntries(x)),\n ),\n ),\n repeat({ delay: 5000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\nconst spotTicker$ = spotInstruments$.pipe(\n tap((x) => {\n console.info('SPOT INSTRUMENTS', x.length);\n }),\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('tickers', x.instId),\n () => true,\n ),\n share(),\n);\n\nconst quoteOfSwapFromRest$ = defer(() => client.getMarketTickers({ instType: 'SWAP' })).pipe(\n mergeMap((x) => x.data || []),\n map(\n (x): Partial<IQuote> => ({\n datasource_id: 'OKX',\n product_id: encodePath(x.instType, x.instId),\n last_price: x.last,\n ask_price: x.askPx,\n bid_price: x.bidPx,\n ask_volume: x.askSz,\n bid_volume: x.bidSz,\n }),\n ),\n repeat({ delay: 1000 }),\n retry({ delay: 1000 }),\n);\n\nconst quoteOfSpotAndMarginFromRest$ = defer(() => client.getMarketTickers({ instType: 'SPOT' })).pipe(\n mergeMap((x) => x.data || []),\n mergeMap((x): Partial<IQuote>[] => [\n {\n datasource_id: 'OKX',\n product_id: encodePath('SPOT', x.instId),\n last_price: x.last,\n ask_price: x.askPx,\n bid_price: x.bidPx,\n ask_volume: x.askSz,\n bid_volume: x.bidSz,\n },\n {\n datasource_id: 'OKX',\n product_id: encodePath('MARGIN', x.instId),\n last_price: x.last,\n ask_price: x.askPx,\n bid_price: x.bidPx,\n ask_volume: x.askSz,\n bid_volume: x.bidSz,\n },\n ]),\n repeat({ delay: 1000 }),\n retry({ delay: 1000 }),\n);\n\nconst quoteOfSwapFromWs$ = swapInstruments$.pipe(\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('tickers', x.instId),\n () => true,\n ),\n map(\n (ticker): Partial<IQuote> => ({\n datasource_id: 'OKX',\n product_id: encodePath('SWAP', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n }),\n ),\n);\n\nconst quoteOfSpotAndMarginFromWs$ = spotTicker$.pipe(\n mergeMap((ticker): Partial<IQuote>[] => [\n {\n datasource_id: 'OKX',\n product_id: encodePath('SPOT', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n },\n {\n datasource_id: 'OKX',\n product_id: encodePath('MARGIN', ticker.instId),\n last_price: ticker.last,\n ask_price: ticker.askPx,\n bid_price: ticker.bidPx,\n ask_volume: ticker.askSz,\n bid_volume: ticker.bidSz,\n },\n ]),\n);\n\nconst swapOpenInterests$ = defer(() => client.getOpenInterest({ instType: 'SWAP' })).pipe(\n repeat({ delay: 10_000 }),\n retry({ delay: 10_000 }),\n shareReplay(1),\n);\n\nconst interestRateOfSwapFromWS$ = swapInstruments$.pipe(\n listWatch(\n (x) => x.instId,\n (x) => fromWsChannelAndInstId('open-interest', x.instId),\n () => true,\n ),\n map(\n (x): Partial<IQuote> => ({\n datasource_id: 'OKX',\n product_id: encodePath('SWAP', x.instId),\n open_interest: x.oi,\n }),\n ),\n share(),\n);\n\nconst quoteSources$ = [\n quoteOfSwapFromWs$,\n interestRateOfSwapFromWS$,\n quoteOfSwapFromRest$,\n quoteOfSpotAndMarginFromWs$,\n quoteOfSpotAndMarginFromRest$,\n];\n\nconst quote$ = defer(() =>\n merge(\n ...quoteSources$.map((x$) =>\n defer(() => x$).pipe(\n // 防止单个流关闭导致整体关闭\n catchError(() => EMPTY),\n ),\n ),\n ),\n).pipe(\n groupBy((x) => encodePath(x.datasource_id, x.product_id)),\n mergeMap((group$) => {\n return group$.pipe(\n //\n scan((acc, cur) => Object.assign(acc, cur), {} as Partial<IQuote>),\n );\n }),\n share(),\n);\n\n// 合并不同来源的数据并进行合并,避免死锁\nif (process.env.WRITE_QUOTE_TO_SQL === 'true') {\n Terminal.fromNodeEnv().channel.publishChannel('quote', { pattern: `^OKX/` }, (channel_id) => {\n const [datasource_id, product_id] = decodePath(channel_id);\n if (!datasource_id) {\n throw 'datasource_id is required';\n }\n if (!product_id) {\n throw 'product_id is required';\n }\n return quote$.pipe(filter((x) => x.product_id === product_id));\n });\n\n quote$\n .pipe(\n writeToSQL({\n terminal: Terminal.fromNodeEnv(),\n writeInterval: 1000,\n tableName: 'quote',\n conflictKeys: ['datasource_id', 'product_id'],\n }),\n )\n .subscribe();\n}\n\nexport const swapOpenInterest$ = defer(() => swapOpenInterests$).pipe(\n map((x) => new Map(x.data.map((x: any) => [x.instId, +x.oi] as const))),\n shareReplay(1),\n);\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuants/vendor-okx",
3
- "version": "0.22.4",
3
+ "version": "0.22.5",
4
4
  "bin": "lib/cli.js",
5
5
  "main": "lib/index.js",
6
6
  "files": [
@@ -1,13 +1,13 @@
1
1
  {
2
- "apps/vendor-okx/CHANGELOG.json": "66452fa66559340a90524ccc99eeaad74d6634c4",
3
- "apps/vendor-okx/CHANGELOG.md": "112059089c0cd7bbde6356c692819340c2029b26",
2
+ "apps/vendor-okx/CHANGELOG.json": "49841fc841cb06bc29d612aa914be57d169a5cfd",
3
+ "apps/vendor-okx/CHANGELOG.md": "2dd82c660951d312f278f2cc4c41afbba232f81a",
4
4
  "apps/vendor-okx/README.md": "ac3d1f6c4c8d73066664699b0bb1b01db02e67a2",
5
5
  "apps/vendor-okx/api-extractor.json": "62f4fd324425b9a235f0c117975967aab09ced0c",
6
6
  "apps/vendor-okx/config/jest.config.json": "4bb17bde3ee911163a3edb36a6eb71491d80b1bd",
7
7
  "apps/vendor-okx/config/rig.json": "f6c7b5537dc77a3170ba9f008bae3b6c3ee11956",
8
8
  "apps/vendor-okx/config/typescript.json": "854907e8a821f2050f6533368db160c649c25348",
9
9
  "apps/vendor-okx/etc/vendor-okx.api.md": "92c54ccd547d2ad81ca6088158ca193e85c208df",
10
- "apps/vendor-okx/package.json": "3ef111b891263888efb1518f6d10e7e4961ceaa2",
10
+ "apps/vendor-okx/package.json": "fa8789f947b058d6b81db24ad71aef3830ebd852",
11
11
  "apps/vendor-okx/src/account.ts": "27500bdf012fe19e2f73db72a4f086437235dc5b",
12
12
  "apps/vendor-okx/src/api.ts": "36fa856ff6ce83b9ccf5b5f4319850065afe3226",
13
13
  "apps/vendor-okx/src/cli.ts": "9bf6b5559a6c6f33da20e74cc6c5d702c60ec891",
@@ -20,7 +20,7 @@
20
20
  "apps/vendor-okx/src/ohlc.ts": "7a8375fc004931ba411e900195ef936372a0400f",
21
21
  "apps/vendor-okx/src/order.ts": "945865088271a194eb1c684af0f10e6b54d89f6b",
22
22
  "apps/vendor-okx/src/product.ts": "a2e7feb86bc01fe3b1af6dcb9a9ebfba6be91434",
23
- "apps/vendor-okx/src/quote.ts": "469c96cc864eec03d2c872a8359886d7f80f2d80",
23
+ "apps/vendor-okx/src/quote.ts": "38293b3781eb52ab2f82cdc8c467fc5e6c732c10",
24
24
  "apps/vendor-okx/src/services.ts": "7b7df830a6d78b6de5cee44bbcec66f282ab53ab",
25
25
  "apps/vendor-okx/src/trade.ts": "aed7272d4231567918d68661e5cbae64165d2f60",
26
26
  "apps/vendor-okx/src/websocket.ts": "bf3d19090361e96a81ecafb47b9d00bff88193c7",