@yuants/app-virtual-exchange 0.11.1 → 0.11.3
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/position.js +7 -15
- package/dist/position.js.map +1 -1
- package/dist/quote/implementations/v1.js +7 -2
- package/dist/quote/implementations/v1.js.map +1 -1
- package/dist/quote/scheduler.js +244 -0
- package/dist/quote/scheduler.js.map +1 -0
- package/dist/quote/service.js +4 -67
- package/dist/quote/service.js.map +1 -1
- package/dist/quote/state.js +1 -0
- package/dist/quote/state.js.map +1 -1
- package/lib/position.d.ts.map +1 -1
- package/lib/position.js +7 -15
- package/lib/position.js.map +1 -1
- package/lib/quote/implementations/v1.d.ts.map +1 -1
- package/lib/quote/implementations/v1.js +7 -2
- package/lib/quote/implementations/v1.js.map +1 -1
- package/lib/quote/scheduler.d.ts +21 -0
- package/lib/quote/scheduler.d.ts.map +1 -0
- package/lib/quote/scheduler.js +248 -0
- package/lib/quote/scheduler.js.map +1 -0
- package/lib/quote/service.js +7 -70
- package/lib/quote/service.js.map +1 -1
- package/lib/quote/state.d.ts +1 -0
- package/lib/quote/state.d.ts.map +1 -1
- package/lib/quote/state.js +2 -1
- package/lib/quote/state.js.map +1 -1
- package/package.json +2 -2
- package/temp/package-deps.json +9 -13
- package/dist/quote/upstream/executor.js +0 -98
- package/dist/quote/upstream/executor.js.map +0 -1
- package/dist/quote/upstream/index.js +0 -2
- package/dist/quote/upstream/index.js.map +0 -1
- package/dist/quote/upstream/prefix-matcher.js +0 -7
- package/dist/quote/upstream/prefix-matcher.js.map +0 -1
- package/dist/quote/upstream/registry.js +0 -116
- package/dist/quote/upstream/registry.js.map +0 -1
- package/dist/quote/upstream/router.js +0 -119
- package/dist/quote/upstream/router.js.map +0 -1
- package/lib/quote/upstream/executor.d.ts +0 -8
- package/lib/quote/upstream/executor.d.ts.map +0 -1
- package/lib/quote/upstream/executor.js +0 -102
- package/lib/quote/upstream/executor.js.map +0 -1
- package/lib/quote/upstream/index.d.ts +0 -3
- package/lib/quote/upstream/index.d.ts.map +0 -1
- package/lib/quote/upstream/index.js +0 -6
- package/lib/quote/upstream/index.js.map +0 -1
- package/lib/quote/upstream/prefix-matcher.d.ts +0 -8
- package/lib/quote/upstream/prefix-matcher.d.ts.map +0 -1
- package/lib/quote/upstream/prefix-matcher.js +0 -11
- package/lib/quote/upstream/prefix-matcher.js.map +0 -1
- package/lib/quote/upstream/registry.d.ts +0 -18
- package/lib/quote/upstream/registry.d.ts.map +0 -1
- package/lib/quote/upstream/registry.js +0 -120
- package/lib/quote/upstream/registry.js.map +0 -1
- package/lib/quote/upstream/router.d.ts +0 -27
- package/lib/quote/upstream/router.d.ts.map +0 -1
- package/lib/quote/upstream/router.js +0 -124
- package/lib/quote/upstream/router.js.map +0 -1
package/dist/position.js
CHANGED
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
import { createCache } from '@yuants/cache';
|
|
2
2
|
import { createClientProductCache } from '@yuants/data-product';
|
|
3
|
-
import { queryQuotes } from '@yuants/data-quote';
|
|
4
3
|
import { Terminal } from '@yuants/protocol';
|
|
5
4
|
import { escapeSQL, requestSQL } from '@yuants/sql';
|
|
6
5
|
import { newError } from '@yuants/utils';
|
|
7
6
|
const terminal = Terminal.fromNodeEnv();
|
|
8
7
|
const productCache = createClientProductCache(terminal);
|
|
9
8
|
const quoteCache = createCache(async (product_id) => {
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
'last_price',
|
|
14
|
-
'interest_rate_long',
|
|
15
|
-
'interest_rate_short',
|
|
16
|
-
'interest_rate_prev_settled_at',
|
|
17
|
-
'interest_rate_next_settled_at',
|
|
18
|
-
], Date.now());
|
|
19
|
-
return quoteRecord[product_id];
|
|
9
|
+
const sql = `select * from quote where product_id = ${escapeSQL(product_id)}`;
|
|
10
|
+
const [quote] = await requestSQL(terminal, sql);
|
|
11
|
+
return quote;
|
|
20
12
|
}, { expire: 30000 });
|
|
21
13
|
const interestRateIntervalCache = createCache(async (product_id) => {
|
|
22
14
|
const sql = `select created_at from interest_rate where series_id = ${escapeSQL(product_id)} order by created_at desc limit 2`;
|
|
@@ -76,7 +68,7 @@ export const polyfillPosition = async (positions) => {
|
|
|
76
68
|
}
|
|
77
69
|
// 利率相关信息的追加
|
|
78
70
|
if (quote) {
|
|
79
|
-
if (quote.interest_rate_next_settled_at
|
|
71
|
+
if (quote.interest_rate_next_settled_at !== null) {
|
|
80
72
|
const nextSettledAt = new Date(quote.interest_rate_next_settled_at).getTime();
|
|
81
73
|
// 优先使用行情数据中的下一个结算时间
|
|
82
74
|
pos.settlement_scheduled_at = nextSettledAt;
|
|
@@ -86,7 +78,7 @@ export const polyfillPosition = async (positions) => {
|
|
|
86
78
|
pos.settlement_interval = interval;
|
|
87
79
|
}
|
|
88
80
|
}
|
|
89
|
-
else if (quote.interest_rate_next_settled_at
|
|
81
|
+
else if (quote.interest_rate_next_settled_at === null && interestRateInterval !== undefined) {
|
|
90
82
|
// 估算下一个结算时间
|
|
91
83
|
// 找到 prev + k * interval > now 的最小 k,则下一个结算时间为 prev + k * interval
|
|
92
84
|
const k = Math.ceil((Date.now() - interestRateInterval.prev) / interestRateInterval.interval);
|
|
@@ -97,12 +89,12 @@ export const polyfillPosition = async (positions) => {
|
|
|
97
89
|
pos.settlement_interval = interestRateInterval.interval;
|
|
98
90
|
}
|
|
99
91
|
if (pos.direction === 'LONG') {
|
|
100
|
-
if (quote.interest_rate_long
|
|
92
|
+
if (quote.interest_rate_long !== null) {
|
|
101
93
|
pos.interest_to_settle = +quote.interest_rate_long * pos.valuation;
|
|
102
94
|
}
|
|
103
95
|
}
|
|
104
96
|
if (pos.direction === 'SHORT') {
|
|
105
|
-
if (quote.interest_rate_short
|
|
97
|
+
if (quote.interest_rate_short !== null) {
|
|
106
98
|
pos.interest_to_settle = +quote.interest_rate_short * pos.valuation;
|
|
107
99
|
}
|
|
108
100
|
}
|
package/dist/position.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"position.js","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCache } from '@yuants/cache';\nimport { IPosition } from '@yuants/data-account';\nimport { IOrder } from '@yuants/data-order';\nimport { createClientProductCache } from '@yuants/data-product';\nimport { IQuote, queryQuotes } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { escapeSQL, requestSQL } from '@yuants/sql';\nimport { newError } from '@yuants/utils';\n\nconst terminal = Terminal.fromNodeEnv();\nconst productCache = createClientProductCache(terminal);\n\nconst quoteCache = createCache<Partial<IQuote> | undefined>(\n async (product_id) => {\n const quoteRecord = await queryQuotes(\n terminal,\n [product_id],\n [\n 'ask_price',\n 'bid_price',\n 'last_price',\n 'interest_rate_long',\n 'interest_rate_short',\n 'interest_rate_prev_settled_at',\n 'interest_rate_next_settled_at',\n ],\n Date.now(),\n );\n return quoteRecord[product_id];\n },\n { expire: 30_000 },\n);\n\nconst interestRateIntervalCache = createCache(\n async (product_id: string) => {\n const sql = `select created_at from interest_rate where series_id = ${escapeSQL(\n product_id,\n )} order by created_at desc limit 2`;\n const rates = await requestSQL<{ created_at: string }[]>(terminal, sql);\n if (rates.length < 2) return undefined;\n const prev = new Date(rates[0].created_at).getTime();\n const prevOfPrev = new Date(rates[1].created_at).getTime();\n const interval = prev - prevOfPrev;\n return {\n prev,\n prevOfPrev,\n interval,\n };\n },\n { swrAfter: 60_000, expire: 3600_000 },\n);\n\nexport const polyfillPosition = async (positions: IPosition[]): Promise<IPosition[]> => {\n // TODO: 使用 batch query SQL 优化 product / quote 查询性能\n for (const pos of positions) {\n const [theProduct, quote, interestRateInterval] = await Promise.all([\n productCache.query(pos.product_id),\n quoteCache.query(pos.product_id),\n interestRateIntervalCache.query(pos.product_id),\n ]);\n\n // 估值 = value_scale * volume * closable_price\n if (theProduct) {\n if (theProduct.base_currency) {\n pos.base_currency = theProduct.base_currency;\n }\n if (theProduct.quote_currency) {\n pos.quote_currency = theProduct.quote_currency;\n }\n if (pos.size === undefined && pos.volume !== undefined && pos.direction !== undefined) {\n pos.size = (pos.direction === 'LONG' ? 1 : -1) * pos.volume * (theProduct.value_scale || 1) + '';\n }\n if (pos.free_size === undefined && pos.free_volume !== undefined && pos.direction !== undefined) {\n pos.free_size =\n (pos.direction === 'LONG' ? 1 : -1) * pos.free_volume * (theProduct.value_scale || 1) + '';\n }\n pos.valuation = Math.abs((theProduct.value_scale || 1) * pos.volume * pos.closable_price);\n }\n\n if (quote && pos.size) {\n const sizeNum = +pos.size;\n if (pos.current_price === undefined) {\n if (sizeNum > 0) {\n // 多头头寸使用买一价作为可平仓价格,如果没有买一价则使用最新价\n pos.current_price = (quote.ask_price || quote.last_price) + '';\n } else {\n // 空头头寸使用卖一价作为可平仓价格,如果没有卖一价则使用最新价\n pos.current_price = (quote.bid_price || quote.last_price) + '';\n }\n }\n\n // 计算名义价值\n if (pos.notional === undefined) {\n pos.notional = sizeNum * (+pos.current_price || 0) + '';\n }\n }\n\n // 利率相关信息的追加\n if (quote) {\n if (quote.interest_rate_next_settled_at != null) {\n const nextSettledAt = new Date(quote.interest_rate_next_settled_at).getTime();\n // 优先使用行情数据中的下一个结算时间\n pos.settlement_scheduled_at = nextSettledAt;\n // 优先使用下一个结算时间推算结算间隔\n if (interestRateInterval !== undefined) {\n const interval = nextSettledAt - interestRateInterval.prev;\n pos.settlement_interval = interval;\n }\n } else if (quote.interest_rate_next_settled_at == null && interestRateInterval !== undefined) {\n // 估算下一个结算时间\n // 找到 prev + k * interval > now 的最小 k,则下一个结算时间为 prev + k * interval\n const k = Math.ceil((Date.now() - interestRateInterval.prev) / interestRateInterval.interval);\n pos.settlement_scheduled_at = interestRateInterval.prev + k * interestRateInterval.interval;\n }\n\n // 如果还没有结算间隔,则使用 interest rate 表的时间间隔作为结算间隔\n if (pos.settlement_interval === undefined && interestRateInterval) {\n pos.settlement_interval = interestRateInterval.interval;\n }\n\n if (pos.direction === 'LONG') {\n if (quote.interest_rate_long != null) {\n pos.interest_to_settle = +quote.interest_rate_long * pos.valuation;\n }\n }\n if (pos.direction === 'SHORT') {\n if (quote.interest_rate_short != null) {\n pos.interest_to_settle = +quote.interest_rate_short * pos.valuation;\n }\n }\n }\n }\n return positions;\n};\n\nexport const polyfillOrders = async (orders: IOrder[]): Promise<IOrder[]> => {\n for (const order of orders) {\n const theProduct = await productCache.query(order.product_id);\n if (theProduct) {\n if (order.size !== undefined) {\n const sizeNum = +order.size;\n const sizeStep = theProduct.volume_step * theProduct.value_scale;\n if (!(sizeStep > 0)) throw newError('INVALID_SIZE_STEP', { product: theProduct, sizeStep });\n // check size is multiple of sizeStep\n if (Math.abs(sizeNum - Math.round(sizeNum / sizeStep) * sizeStep) > 1e-16) {\n throw newError('INVALID_ORDER_SIZE_NOT_MULTIPLE_OF_SIZE_STEP', {\n order,\n sizeStep,\n sizeNum,\n product: theProduct,\n });\n }\n\n if (sizeNum >= 0) {\n order.order_direction = order.is_close ? 'CLOSE_SHORT' : 'OPEN_LONG';\n } else {\n order.order_direction = order.is_close ? 'CLOSE_LONG' : 'OPEN_SHORT';\n }\n order.volume = Math.abs(sizeNum) / theProduct.value_scale;\n }\n }\n }\n return orders;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"position.js","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCache } from '@yuants/cache';\nimport { IPosition } from '@yuants/data-account';\nimport { IOrder } from '@yuants/data-order';\nimport { createClientProductCache } from '@yuants/data-product';\nimport { IQuote } from '@yuants/data-quote';\nimport { Terminal } from '@yuants/protocol';\nimport { escapeSQL, requestSQL } from '@yuants/sql';\nimport { newError } from '@yuants/utils';\n\nconst terminal = Terminal.fromNodeEnv();\nconst productCache = createClientProductCache(terminal);\nconst quoteCache = createCache<IQuote>(\n async (product_id) => {\n const sql = `select * from quote where product_id = ${escapeSQL(product_id)}`;\n const [quote] = await requestSQL<IQuote[]>(terminal, sql);\n return quote;\n },\n { expire: 30_000 },\n);\n\nconst interestRateIntervalCache = createCache(\n async (product_id: string) => {\n const sql = `select created_at from interest_rate where series_id = ${escapeSQL(\n product_id,\n )} order by created_at desc limit 2`;\n const rates = await requestSQL<{ created_at: string }[]>(terminal, sql);\n if (rates.length < 2) return undefined;\n const prev = new Date(rates[0].created_at).getTime();\n const prevOfPrev = new Date(rates[1].created_at).getTime();\n const interval = prev - prevOfPrev;\n return {\n prev,\n prevOfPrev,\n interval,\n };\n },\n { swrAfter: 60_000, expire: 3600_000 },\n);\n\nexport const polyfillPosition = async (positions: IPosition[]): Promise<IPosition[]> => {\n // TODO: 使用 batch query SQL 优化 product / quote 查询性能\n for (const pos of positions) {\n const [theProduct, quote, interestRateInterval] = await Promise.all([\n productCache.query(pos.product_id),\n quoteCache.query(pos.product_id),\n interestRateIntervalCache.query(pos.product_id),\n ]);\n\n // 估值 = value_scale * volume * closable_price\n if (theProduct) {\n if (theProduct.base_currency) {\n pos.base_currency = theProduct.base_currency;\n }\n if (theProduct.quote_currency) {\n pos.quote_currency = theProduct.quote_currency;\n }\n if (pos.size === undefined && pos.volume !== undefined && pos.direction !== undefined) {\n pos.size = (pos.direction === 'LONG' ? 1 : -1) * pos.volume * (theProduct.value_scale || 1) + '';\n }\n if (pos.free_size === undefined && pos.free_volume !== undefined && pos.direction !== undefined) {\n pos.free_size =\n (pos.direction === 'LONG' ? 1 : -1) * pos.free_volume * (theProduct.value_scale || 1) + '';\n }\n pos.valuation = Math.abs((theProduct.value_scale || 1) * pos.volume * pos.closable_price);\n }\n\n if (quote && pos.size) {\n const sizeNum = +pos.size;\n if (pos.current_price === undefined) {\n if (sizeNum > 0) {\n // 多头头寸使用买一价作为可平仓价格,如果没有买一价则使用最新价\n pos.current_price = (quote.ask_price || quote.last_price) + '';\n } else {\n // 空头头寸使用卖一价作为可平仓价格,如果没有卖一价则使用最新价\n pos.current_price = (quote.bid_price || quote.last_price) + '';\n }\n }\n\n // 计算名义价值\n if (pos.notional === undefined) {\n pos.notional = sizeNum * (+pos.current_price || 0) + '';\n }\n }\n\n // 利率相关信息的追加\n if (quote) {\n if (quote.interest_rate_next_settled_at !== null) {\n const nextSettledAt = new Date(quote.interest_rate_next_settled_at).getTime();\n // 优先使用行情数据中的下一个结算时间\n pos.settlement_scheduled_at = nextSettledAt;\n // 优先使用下一个结算时间推算结算间隔\n if (interestRateInterval !== undefined) {\n const interval = nextSettledAt - interestRateInterval.prev;\n pos.settlement_interval = interval;\n }\n } else if (quote.interest_rate_next_settled_at === null && interestRateInterval !== undefined) {\n // 估算下一个结算时间\n // 找到 prev + k * interval > now 的最小 k,则下一个结算时间为 prev + k * interval\n const k = Math.ceil((Date.now() - interestRateInterval.prev) / interestRateInterval.interval);\n pos.settlement_scheduled_at = interestRateInterval.prev + k * interestRateInterval.interval;\n }\n\n // 如果还没有结算间隔,则使用 interest rate 表的时间间隔作为结算间隔\n if (pos.settlement_interval === undefined && interestRateInterval) {\n pos.settlement_interval = interestRateInterval.interval;\n }\n\n if (pos.direction === 'LONG') {\n if (quote.interest_rate_long !== null) {\n pos.interest_to_settle = +quote.interest_rate_long * pos.valuation;\n }\n }\n if (pos.direction === 'SHORT') {\n if (quote.interest_rate_short !== null) {\n pos.interest_to_settle = +quote.interest_rate_short * pos.valuation;\n }\n }\n }\n }\n return positions;\n};\n\nexport const polyfillOrders = async (orders: IOrder[]): Promise<IOrder[]> => {\n for (const order of orders) {\n const theProduct = await productCache.query(order.product_id);\n if (theProduct) {\n if (order.size !== undefined) {\n const sizeNum = +order.size;\n const sizeStep = theProduct.volume_step * theProduct.value_scale;\n if (!(sizeStep > 0)) throw newError('INVALID_SIZE_STEP', { product: theProduct, sizeStep });\n // check size is multiple of sizeStep\n if (Math.abs(sizeNum - Math.round(sizeNum / sizeStep) * sizeStep) > 1e-16) {\n throw newError('INVALID_ORDER_SIZE_NOT_MULTIPLE_OF_SIZE_STEP', {\n order,\n sizeStep,\n sizeNum,\n product: theProduct,\n });\n }\n\n if (sizeNum >= 0) {\n order.order_direction = order.is_close ? 'CLOSE_SHORT' : 'OPEN_LONG';\n } else {\n order.order_direction = order.is_close ? 'CLOSE_LONG' : 'OPEN_SHORT';\n }\n order.volume = Math.abs(sizeNum) / theProduct.value_scale;\n }\n }\n }\n return orders;\n};\n"]}
|
|
@@ -113,8 +113,13 @@ export const createQuoteStateV1 = () => {
|
|
|
113
113
|
for (const product_id of product_ids) {
|
|
114
114
|
result[product_id] = {};
|
|
115
115
|
for (const field of fields) {
|
|
116
|
-
const
|
|
117
|
-
|
|
116
|
+
const offset = getFieldOffset(product_id, field);
|
|
117
|
+
if (offset === undefined) {
|
|
118
|
+
result[product_id][field] = '';
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
result[product_id][field] = data[offset] || '';
|
|
122
|
+
}
|
|
118
123
|
}
|
|
119
124
|
}
|
|
120
125
|
return result;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"v1.js","sourceRoot":"","sources":["../../../src/quote/implementations/v1.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAGzC,wBAAwB;AACxB,MAAM,MAAM,GAAG,CAAC,CAAC,CAAiC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAiB,CAAC,CAAC;IAC3F,mDAAmD;IACnD,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,CAAC;IACZ,mBAAmB,EAAE,CAAC;IACtB,aAAa,EAAE,CAAC;IAChB,6BAA6B,EAAE,CAAC;IAChC,6BAA6B,EAAE,CAAC;IAChC,kBAAkB,EAAE,CAAC;CACtB,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;AAClC,MAAM,oBAAoB,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAElG;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAgB,EAAE;IAClD,+BAA+B;IAC/B,MAAM,IAAI,GAAwB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtD,2CAA2C;IAC3C,MAAM,cAAc,GAAG,CAAC,UAAkB,EAAE,KAAa,EAAsB,EAAE;QAC/E,IAAI,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,SAAS,KAAK,SAAS,EAAE;YAC3B,OAAO,SAAS,CAAC;SAClB;QACD,MAAM,WAAW,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,WAAW,KAAK,SAAS;YAAE,MAAM,QAAQ,CAAC,oBAAoB,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAC;QACzG,OAAO,SAAS,GAAG,WAAW,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,UAAkB,EAAE,KAAgB,EAAgC,EAAE;QAC3F,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACjD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAW,CAAC;QACrC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAW,CAAC;QAC9C,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,UAAkB,EAAE,KAAgB,EAAE,KAAa,EAAE,UAAkB,EAAE,EAAE;QAChG,IAAI,MAAM,GAAG,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,MAAM,KAAK,SAAS,EAAE;YACxB,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,GAAG,WAAW,GAAG,CAAC,CAAC;YAC7D,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC1B,mBAAmB,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;YAC/C,MAAM,GAAG,SAAS,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;SAClD;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC;IAChC,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,MAA0B,EAAE,EAAE;QAC5C,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE;YAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YAClC,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE;gBAC/B,MAAM,KAAK,GAAG,UAAuB,CAAC;gBACtC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,MAAM,CAAC,KAAK,CAAE,CAAC;gBAC3C,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAClD,IAAI,QAAQ,KAAK,SAAS,IAAI,UAAU,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE;oBACvD,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;iBACrD;aACF;SACF;IACH,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,GAAuB,EAAE;QAC5C,MAAM,MAAM,GAAuB,EAAE,CAAC;QACtC,KAAK,MAAM,UAAU,IAAI,QAAQ,EAAE;YACjC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;SACzB;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE;YACvC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAW,CAAC;YAChC,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAW,CAAC;YAEzC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;YACvD,MAAM,UAAU,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;YAE1C,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YAEtC,MAAM,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;SACtD;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF;;;;;;OAMG;IACH,MAAM,MAAM,GAAG,CAAC,WAAqB,EAAE,MAAmB,EAAE,UAAkB,EAAsB,EAAE;QACpG,MAAM,MAAM,GAAuB,EAAE,CAAC;QACtC,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;YACpC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;YACxB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;gBAC1B,MAAM,KAAK,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAC/C,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,EAAE;oBACnC,MAAM,CAAC,UAAU,CAAE,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;iBACpC;aACF;SACF;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CACnB,WAAqB,EACrB,MAAW,EACwB,EAAE;QACrC,MAAM,MAAM,GAAsC,EAAE,CAAC;QACrD,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;YACpC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAuB,CAAC;YAC7C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;gBAC1B,MAAM,
|
|
1
|
+
{"version":3,"file":"v1.js","sourceRoot":"","sources":["../../../src/quote/implementations/v1.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2]));\n\n/**\n * 高效的行情状态管理器\n * 内部使用扁平化数组存储数据,避免内存碎片化和过多的 Map/对象开销\n * 提供高效的读写接口,支持按需更新和查询\n */\nexport const createQuoteStateV1 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const data: (string | number)[] = [];\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 0~20 (10 fields * 2 (value, updated_at))\n const getFieldOffset = (product_id: string, field: string): number | undefined => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n return undefined;\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) return undefined;\n const value = data[offset] as string;\n if (value === undefined) return undefined;\n const updated_at = data[offset + 1] as number;\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n let offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n const baseIndex = mapProductIdToIndex.size * FIELD_COUNT * 2;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n offset = baseIndex + mapFieldNameToOffset[field];\n }\n data[offset] = value;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < data.length; i += 2) {\n const value = data[i] as string;\n if (value === undefined) continue;\n const updated_at = data[i + 1] as number;\n\n const productIndex = Math.floor(i / (FIELD_COUNT * 2));\n const product_id = products[productIndex];\n\n const fieldIndex = (i % (FIELD_COUNT * 2)) / 2;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id][field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n result[product_id][field] = '';\n } else {\n result[product_id][field] = (data[offset] as string) || '';\n }\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { parseQuoteServiceMetadataFromSchema } from '@yuants/exchange';
|
|
2
|
+
import { Terminal } from '@yuants/protocol';
|
|
3
|
+
import { encodePath, formatTime } from '@yuants/utils';
|
|
4
|
+
import { filter, from, map, mergeMap, tap, toArray } from 'rxjs';
|
|
5
|
+
import { quoteState } from './state';
|
|
6
|
+
const createFifoQueue = () => ({ items: [], head: 0 });
|
|
7
|
+
const fifoEnqueue = (queue, value) => {
|
|
8
|
+
queue.items.push(value);
|
|
9
|
+
};
|
|
10
|
+
const fifoDequeue = (queue) => {
|
|
11
|
+
if (queue.head >= queue.items.length)
|
|
12
|
+
return undefined;
|
|
13
|
+
const value = queue.items[queue.head++];
|
|
14
|
+
if (queue.head > 1024 && queue.head * 2 > queue.items.length) {
|
|
15
|
+
queue.items = queue.items.slice(queue.head);
|
|
16
|
+
queue.head = 0;
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
const fifoSize = (queue) => queue.items.length - queue.head;
|
|
21
|
+
const isTraceEnabled = process.env.VEX_QUOTE_UPSTREAM_REFINE_TRACE === '1';
|
|
22
|
+
// Query -> Make Cell Dirty -> Trigger Dirty Check
|
|
23
|
+
// Dirty Check():
|
|
24
|
+
// Acquire dirtyCheck lock
|
|
25
|
+
// Foreach Dirty cell in parallel:
|
|
26
|
+
//. Routing the dirty cell to ServiceID
|
|
27
|
+
//. Acquire Service Lock
|
|
28
|
+
//. if success:
|
|
29
|
+
// request the service and wait for response
|
|
30
|
+
//. release service lock
|
|
31
|
+
// clean cells
|
|
32
|
+
//. if failed: do nothing.
|
|
33
|
+
// Release dirtyCheck lock
|
|
34
|
+
// if any cell dirty remaining, call dirtyCheck()
|
|
35
|
+
// (service_id, service_group_id) -> sync func state (hash func)
|
|
36
|
+
const services = [];
|
|
37
|
+
const mapGroupIdToServices = new Map();
|
|
38
|
+
// (product_id, field, service_group_id, is_dirty = true, is_fetching)
|
|
39
|
+
// use-case 1: find unique product_ids (not is_fetching) by service_group_id
|
|
40
|
+
// use-case 2: mark
|
|
41
|
+
/**
|
|
42
|
+
* Use cases:
|
|
43
|
+
* - upsert items (when queryQuotes)
|
|
44
|
+
* - find unique product_ids (is_dirty = true and is_fetching = false) by service_group_id
|
|
45
|
+
* - when service fired, update is_fetching = true for some items (product_id, field)
|
|
46
|
+
* - when service failed, update is_fetching = false for some items (product_id, field)
|
|
47
|
+
* - when service success, update is_fetching = false and is_dirty = false for some items (product_id, field)
|
|
48
|
+
*/
|
|
49
|
+
export const cells = [];
|
|
50
|
+
const mapCellKeyToCell = new Map();
|
|
51
|
+
const mapGroupIdToState = new Map();
|
|
52
|
+
const getOrCreateGroupState = (group_id) => {
|
|
53
|
+
const existing = mapGroupIdToState.get(group_id);
|
|
54
|
+
if (existing)
|
|
55
|
+
return existing;
|
|
56
|
+
const next = {
|
|
57
|
+
productQueue: createFifoQueue(),
|
|
58
|
+
inQueue: new Set(),
|
|
59
|
+
fetchingProducts: new Set(),
|
|
60
|
+
dirtyFieldsByProduct: new Map(),
|
|
61
|
+
};
|
|
62
|
+
mapGroupIdToState.set(group_id, next);
|
|
63
|
+
return next;
|
|
64
|
+
};
|
|
65
|
+
const route = (product_id, field) => {
|
|
66
|
+
for (const service of services) {
|
|
67
|
+
if (!product_id.startsWith(service.meta.product_id_prefix))
|
|
68
|
+
continue;
|
|
69
|
+
if (!service.metaFieldsSet.has(field))
|
|
70
|
+
continue;
|
|
71
|
+
return service.service_group_id;
|
|
72
|
+
}
|
|
73
|
+
return '';
|
|
74
|
+
};
|
|
75
|
+
const enqueueDirty = (params) => {
|
|
76
|
+
var _a;
|
|
77
|
+
const { product_id, field, service_group_id } = params;
|
|
78
|
+
if (!service_group_id)
|
|
79
|
+
return;
|
|
80
|
+
const groupState = getOrCreateGroupState(service_group_id);
|
|
81
|
+
const dirtyFields = (_a = groupState.dirtyFieldsByProduct.get(product_id)) !== null && _a !== void 0 ? _a : (() => {
|
|
82
|
+
const next = new Set();
|
|
83
|
+
groupState.dirtyFieldsByProduct.set(product_id, next);
|
|
84
|
+
return next;
|
|
85
|
+
})();
|
|
86
|
+
dirtyFields.add(field);
|
|
87
|
+
if (groupState.fetchingProducts.has(product_id))
|
|
88
|
+
return;
|
|
89
|
+
if (groupState.inQueue.has(product_id))
|
|
90
|
+
return;
|
|
91
|
+
groupState.inQueue.add(product_id);
|
|
92
|
+
fifoEnqueue(groupState.productQueue, product_id);
|
|
93
|
+
};
|
|
94
|
+
export const markDirty = (product_id, field) => {
|
|
95
|
+
const service_group_id = route(product_id, field);
|
|
96
|
+
const cellKey = encodePath(product_id, field);
|
|
97
|
+
const existing = mapCellKeyToCell.get(cellKey);
|
|
98
|
+
if (!existing) {
|
|
99
|
+
const cell = {
|
|
100
|
+
product_id,
|
|
101
|
+
field,
|
|
102
|
+
service_group_id,
|
|
103
|
+
is_dirty: true,
|
|
104
|
+
is_fetching: false,
|
|
105
|
+
round: 0,
|
|
106
|
+
};
|
|
107
|
+
mapCellKeyToCell.set(cellKey, cell);
|
|
108
|
+
cells.push(cell);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
existing.service_group_id = service_group_id;
|
|
112
|
+
existing.is_dirty = true;
|
|
113
|
+
}
|
|
114
|
+
enqueueDirty({ product_id, field, service_group_id });
|
|
115
|
+
scheduleServiceGroupId(service_group_id);
|
|
116
|
+
};
|
|
117
|
+
const isServiceIdRunning = new Set();
|
|
118
|
+
const scheduleServiceGroupId = (serviceGroupId) => {
|
|
119
|
+
if (!serviceGroupId)
|
|
120
|
+
return;
|
|
121
|
+
const serviceList = mapGroupIdToServices.get(serviceGroupId);
|
|
122
|
+
if (!serviceList || serviceList.length === 0)
|
|
123
|
+
return;
|
|
124
|
+
for (const service of serviceList) {
|
|
125
|
+
if (isServiceIdRunning.has(service.service_id))
|
|
126
|
+
continue;
|
|
127
|
+
isServiceIdRunning.add(service.service_id);
|
|
128
|
+
handleService(service).finally(() => {
|
|
129
|
+
isServiceIdRunning.delete(service.service_id);
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const handleService = async (service) => {
|
|
134
|
+
var _a;
|
|
135
|
+
const groupState = mapGroupIdToState.get(service.service_group_id);
|
|
136
|
+
if (!groupState)
|
|
137
|
+
return;
|
|
138
|
+
while (true) {
|
|
139
|
+
const maxProducts = (_a = service.meta.max_products_per_request) !== null && _a !== void 0 ? _a : Infinity;
|
|
140
|
+
const fetchedFieldsByProduct = new Map();
|
|
141
|
+
const productsToFetch = [];
|
|
142
|
+
const cellsToFetch = [];
|
|
143
|
+
while (productsToFetch.length < maxProducts && fifoSize(groupState.productQueue) > 0) {
|
|
144
|
+
const product_id = fifoDequeue(groupState.productQueue);
|
|
145
|
+
if (!product_id)
|
|
146
|
+
break;
|
|
147
|
+
groupState.inQueue.delete(product_id);
|
|
148
|
+
if (groupState.fetchingProducts.has(product_id))
|
|
149
|
+
continue;
|
|
150
|
+
const dirtyFields = groupState.dirtyFieldsByProduct.get(product_id);
|
|
151
|
+
if (!dirtyFields || dirtyFields.size === 0)
|
|
152
|
+
continue;
|
|
153
|
+
const matchedFields = [];
|
|
154
|
+
for (const f of dirtyFields) {
|
|
155
|
+
if (!service.metaFieldsSet.has(f))
|
|
156
|
+
continue;
|
|
157
|
+
const cell = mapCellKeyToCell.get(encodePath(product_id, f));
|
|
158
|
+
if (!cell)
|
|
159
|
+
continue;
|
|
160
|
+
if (cell.service_group_id !== service.service_group_id)
|
|
161
|
+
continue;
|
|
162
|
+
if (!cell.is_dirty)
|
|
163
|
+
continue;
|
|
164
|
+
if (cell.is_fetching)
|
|
165
|
+
continue;
|
|
166
|
+
matchedFields.push(f);
|
|
167
|
+
cellsToFetch.push(cell);
|
|
168
|
+
}
|
|
169
|
+
if (matchedFields.length === 0)
|
|
170
|
+
continue;
|
|
171
|
+
productsToFetch.push(product_id);
|
|
172
|
+
groupState.fetchingProducts.add(product_id);
|
|
173
|
+
fetchedFieldsByProduct.set(product_id, new Set(matchedFields));
|
|
174
|
+
}
|
|
175
|
+
if (productsToFetch.length === 0)
|
|
176
|
+
return;
|
|
177
|
+
for (const cell of cellsToFetch) {
|
|
178
|
+
cell.round++;
|
|
179
|
+
cell.is_fetching = true;
|
|
180
|
+
}
|
|
181
|
+
const res = await makeRequest(service, productsToFetch).catch(() => ({}));
|
|
182
|
+
quoteState.update(res);
|
|
183
|
+
for (const cell of cellsToFetch) {
|
|
184
|
+
cell.is_fetching = false;
|
|
185
|
+
cell.is_dirty = false;
|
|
186
|
+
}
|
|
187
|
+
for (const product_id of productsToFetch) {
|
|
188
|
+
groupState.fetchingProducts.delete(product_id);
|
|
189
|
+
const dirtyFields = groupState.dirtyFieldsByProduct.get(product_id);
|
|
190
|
+
const fetchedFields = fetchedFieldsByProduct.get(product_id);
|
|
191
|
+
if (!dirtyFields || !fetchedFields)
|
|
192
|
+
continue;
|
|
193
|
+
for (const f of fetchedFields) {
|
|
194
|
+
dirtyFields.delete(f);
|
|
195
|
+
}
|
|
196
|
+
if (dirtyFields.size === 0) {
|
|
197
|
+
groupState.dirtyFieldsByProduct.delete(product_id);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (!groupState.inQueue.has(product_id)) {
|
|
201
|
+
groupState.inQueue.add(product_id);
|
|
202
|
+
fifoEnqueue(groupState.productQueue, product_id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const terminal = Terminal.fromNodeEnv();
|
|
208
|
+
const makeRequest = async (service, product_ids) => {
|
|
209
|
+
const res = await terminal.client.requestForResponseData('GetQuotes', {
|
|
210
|
+
product_ids,
|
|
211
|
+
fields: service.meta.fields,
|
|
212
|
+
});
|
|
213
|
+
if (isTraceEnabled) {
|
|
214
|
+
console.info(formatTime(Date.now()), `[VEX][Quote][Refine]GetQuotes`, `service_id=${service.service_id}`, `group=${service.service_group_id}`, `products=${product_ids.length}`);
|
|
215
|
+
}
|
|
216
|
+
return res;
|
|
217
|
+
};
|
|
218
|
+
terminal.terminalInfos$
|
|
219
|
+
.pipe(mergeMap((terminalInfos) => from(terminalInfos).pipe(mergeMap((terminalInfo) => from(Object.values(terminalInfo.serviceInfo || {})).pipe(filter((serviceInfo) => serviceInfo.method === 'GetQuotes'), map((serviceInfo) => {
|
|
220
|
+
var _a;
|
|
221
|
+
try {
|
|
222
|
+
const meta = parseQuoteServiceMetadataFromSchema(serviceInfo.schema);
|
|
223
|
+
const service_group_id = encodePath(meta.product_id_prefix, meta.fields.join(','), (_a = meta.max_products_per_request) !== null && _a !== void 0 ? _a : '');
|
|
224
|
+
return {
|
|
225
|
+
service_id: serviceInfo.service_id,
|
|
226
|
+
service_group_id,
|
|
227
|
+
meta,
|
|
228
|
+
metaFieldsSet: new Set(meta.fields),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (_b) { }
|
|
232
|
+
}), filter((x) => !!x))), toArray(), tap((x) => {
|
|
233
|
+
services.length = 0;
|
|
234
|
+
mapGroupIdToServices.clear();
|
|
235
|
+
x.forEach((service) => {
|
|
236
|
+
var _a;
|
|
237
|
+
services.push(service);
|
|
238
|
+
const list = (_a = mapGroupIdToServices.get(service.service_group_id)) !== null && _a !== void 0 ? _a : [];
|
|
239
|
+
list.push(service);
|
|
240
|
+
mapGroupIdToServices.set(service.service_group_id, list);
|
|
241
|
+
});
|
|
242
|
+
}))))
|
|
243
|
+
.subscribe();
|
|
244
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","sourceRoot":"","sources":["../../src/quote/scheduler.ts"],"names":[],"mappings":"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","sourcesContent":["import { IQuoteUpdateAction } from '@yuants/data-quote';\nimport { IQuoteField, IQuoteServiceMetadata, parseQuoteServiceMetadataFromSchema } from '@yuants/exchange';\nimport { Terminal } from '@yuants/protocol';\nimport { encodePath, formatTime } from '@yuants/utils';\nimport { filter, from, map, mergeMap, tap, toArray } from 'rxjs';\nimport { quoteState } from './state';\n\ninterface IQuoteService {\n service_id: string;\n service_group_id: string;\n meta: IQuoteServiceMetadata;\n metaFieldsSet: Set<IQuoteField>;\n}\n\ninterface ICellState {\n product_id: string;\n field: IQuoteField;\n service_group_id: string;\n is_dirty: boolean;\n is_fetching: boolean;\n round: number;\n}\n\ntype IFifoQueue<T> = {\n items: T[];\n head: number;\n};\n\nconst createFifoQueue = <T>(): IFifoQueue<T> => ({ items: [], head: 0 });\n\nconst fifoEnqueue = <T>(queue: IFifoQueue<T>, value: T) => {\n queue.items.push(value);\n};\n\nconst fifoDequeue = <T>(queue: IFifoQueue<T>): T | undefined => {\n if (queue.head >= queue.items.length) return undefined;\n const value = queue.items[queue.head++];\n if (queue.head > 1024 && queue.head * 2 > queue.items.length) {\n queue.items = queue.items.slice(queue.head);\n queue.head = 0;\n }\n return value;\n};\n\nconst fifoSize = <T>(queue: IFifoQueue<T>): number => queue.items.length - queue.head;\n\nconst isTraceEnabled = process.env.VEX_QUOTE_UPSTREAM_REFINE_TRACE === '1';\n\n// Query -> Make Cell Dirty -> Trigger Dirty Check\n\n// Dirty Check():\n// Acquire dirtyCheck lock\n// Foreach Dirty cell in parallel:\n//. Routing the dirty cell to ServiceID\n//. Acquire Service Lock\n//. if success:\n// request the service and wait for response\n//. release service lock\n// clean cells\n//. if failed: do nothing.\n// Release dirtyCheck lock\n// if any cell dirty remaining, call dirtyCheck()\n\n// (service_id, service_group_id) -> sync func state (hash func)\nconst services: Array<IQuoteService> = [];\nconst mapGroupIdToServices = new Map<string, IQuoteService[]>();\n\n// (product_id, field, service_group_id, is_dirty = true, is_fetching)\n// use-case 1: find unique product_ids (not is_fetching) by service_group_id\n// use-case 2: mark\n/**\n * Use cases:\n * - upsert items (when queryQuotes)\n * - find unique product_ids (is_dirty = true and is_fetching = false) by service_group_id\n * - when service fired, update is_fetching = true for some items (product_id, field)\n * - when service failed, update is_fetching = false for some items (product_id, field)\n * - when service success, update is_fetching = false and is_dirty = false for some items (product_id, field)\n */\nexport const cells: Array<ICellState> = [];\nconst mapCellKeyToCell = new Map<string, ICellState>();\n\ntype IGroupState = {\n productQueue: IFifoQueue<string>;\n inQueue: Set<string>;\n fetchingProducts: Set<string>;\n dirtyFieldsByProduct: Map<string, Set<IQuoteField>>;\n};\n\nconst mapGroupIdToState = new Map<string, IGroupState>();\n\nconst getOrCreateGroupState = (group_id: string): IGroupState => {\n const existing = mapGroupIdToState.get(group_id);\n if (existing) return existing;\n const next: IGroupState = {\n productQueue: createFifoQueue(),\n inQueue: new Set<string>(),\n fetchingProducts: new Set<string>(),\n dirtyFieldsByProduct: new Map<string, Set<IQuoteField>>(),\n };\n mapGroupIdToState.set(group_id, next);\n return next;\n};\n\nconst route = (product_id: string, field: IQuoteField) => {\n for (const service of services) {\n if (!product_id.startsWith(service.meta.product_id_prefix)) continue;\n if (!service.metaFieldsSet.has(field)) continue;\n return service.service_group_id;\n }\n return '';\n};\n\nconst enqueueDirty = (params: { product_id: string; field: IQuoteField; service_group_id: string }) => {\n const { product_id, field, service_group_id } = params;\n if (!service_group_id) return;\n const groupState = getOrCreateGroupState(service_group_id);\n const dirtyFields =\n groupState.dirtyFieldsByProduct.get(product_id) ??\n (() => {\n const next = new Set<IQuoteField>();\n groupState.dirtyFieldsByProduct.set(product_id, next);\n return next;\n })();\n dirtyFields.add(field);\n\n if (groupState.fetchingProducts.has(product_id)) return;\n if (groupState.inQueue.has(product_id)) return;\n groupState.inQueue.add(product_id);\n fifoEnqueue(groupState.productQueue, product_id);\n};\n\nexport const markDirty = (product_id: string, field: IQuoteField) => {\n const service_group_id = route(product_id, field);\n\n const cellKey = encodePath(product_id, field);\n const existing = mapCellKeyToCell.get(cellKey);\n if (!existing) {\n const cell: ICellState = {\n product_id,\n field,\n service_group_id,\n is_dirty: true,\n is_fetching: false,\n round: 0,\n };\n mapCellKeyToCell.set(cellKey, cell);\n cells.push(cell);\n } else {\n existing.service_group_id = service_group_id;\n existing.is_dirty = true;\n }\n\n enqueueDirty({ product_id, field, service_group_id });\n scheduleServiceGroupId(service_group_id);\n};\n\nconst isServiceIdRunning = new Set<string>();\n\nconst scheduleServiceGroupId = (serviceGroupId: string): void => {\n if (!serviceGroupId) return;\n const serviceList = mapGroupIdToServices.get(serviceGroupId);\n if (!serviceList || serviceList.length === 0) return;\n\n for (const service of serviceList) {\n if (isServiceIdRunning.has(service.service_id)) continue;\n isServiceIdRunning.add(service.service_id);\n handleService(service).finally(() => {\n isServiceIdRunning.delete(service.service_id);\n });\n }\n};\n\nconst handleService = async (service: IQuoteService) => {\n const groupState = mapGroupIdToState.get(service.service_group_id);\n if (!groupState) return;\n\n while (true) {\n const maxProducts = service.meta.max_products_per_request ?? Infinity;\n const fetchedFieldsByProduct = new Map<string, Set<IQuoteField>>();\n const productsToFetch: string[] = [];\n const cellsToFetch: ICellState[] = [];\n\n while (productsToFetch.length < maxProducts && fifoSize(groupState.productQueue) > 0) {\n const product_id = fifoDequeue(groupState.productQueue);\n if (!product_id) break;\n groupState.inQueue.delete(product_id);\n\n if (groupState.fetchingProducts.has(product_id)) continue;\n const dirtyFields = groupState.dirtyFieldsByProduct.get(product_id);\n if (!dirtyFields || dirtyFields.size === 0) continue;\n\n const matchedFields: IQuoteField[] = [];\n for (const f of dirtyFields) {\n if (!service.metaFieldsSet.has(f)) continue;\n const cell = mapCellKeyToCell.get(encodePath(product_id, f));\n if (!cell) continue;\n if (cell.service_group_id !== service.service_group_id) continue;\n if (!cell.is_dirty) continue;\n if (cell.is_fetching) continue;\n matchedFields.push(f);\n cellsToFetch.push(cell);\n }\n\n if (matchedFields.length === 0) continue;\n productsToFetch.push(product_id);\n groupState.fetchingProducts.add(product_id);\n fetchedFieldsByProduct.set(product_id, new Set(matchedFields));\n }\n\n if (productsToFetch.length === 0) return;\n\n for (const cell of cellsToFetch) {\n cell.round++;\n cell.is_fetching = true;\n }\n\n const res = await makeRequest(service, productsToFetch).catch(() => ({} as IQuoteUpdateAction));\n quoteState.update(res);\n\n for (const cell of cellsToFetch) {\n cell.is_fetching = false;\n cell.is_dirty = false;\n }\n\n for (const product_id of productsToFetch) {\n groupState.fetchingProducts.delete(product_id);\n const dirtyFields = groupState.dirtyFieldsByProduct.get(product_id);\n const fetchedFields = fetchedFieldsByProduct.get(product_id);\n if (!dirtyFields || !fetchedFields) continue;\n for (const f of fetchedFields) {\n dirtyFields.delete(f);\n }\n if (dirtyFields.size === 0) {\n groupState.dirtyFieldsByProduct.delete(product_id);\n continue;\n }\n if (!groupState.inQueue.has(product_id)) {\n groupState.inQueue.add(product_id);\n fifoEnqueue(groupState.productQueue, product_id);\n }\n }\n }\n};\n\nconst terminal = Terminal.fromNodeEnv();\n\nconst makeRequest = async (service: IQuoteService, product_ids: string[]): Promise<IQuoteUpdateAction> => {\n const res = await terminal.client.requestForResponseData<{}, IQuoteUpdateAction>('GetQuotes', {\n product_ids,\n fields: service.meta.fields,\n });\n if (isTraceEnabled) {\n console.info(\n formatTime(Date.now()),\n `[VEX][Quote][Refine]GetQuotes`,\n `service_id=${service.service_id}`,\n `group=${service.service_group_id}`,\n `products=${product_ids.length}`,\n );\n }\n return res;\n};\n\nterminal.terminalInfos$\n .pipe(\n mergeMap((terminalInfos) =>\n from(terminalInfos).pipe(\n mergeMap((terminalInfo) =>\n from(Object.values(terminalInfo.serviceInfo || {})).pipe(\n filter((serviceInfo) => serviceInfo.method === 'GetQuotes'),\n map((serviceInfo): IQuoteService | undefined => {\n try {\n const meta = parseQuoteServiceMetadataFromSchema(serviceInfo.schema);\n const service_group_id = encodePath(\n meta.product_id_prefix,\n (meta.fields as any[]).join(','),\n meta.max_products_per_request ?? '',\n );\n return {\n service_id: serviceInfo.service_id,\n service_group_id,\n meta,\n metaFieldsSet: new Set(meta.fields),\n };\n } catch {}\n }),\n filter((x): x is Exclude<typeof x, undefined> => !!x),\n ),\n ),\n toArray(),\n tap((x) => {\n services.length = 0;\n mapGroupIdToServices.clear();\n x.forEach((service) => {\n services.push(service);\n const list = mapGroupIdToServices.get(service.service_group_id) ?? [];\n list.push(service);\n mapGroupIdToServices.set(service.service_group_id, list);\n });\n }),\n ),\n ),\n )\n .subscribe();\n"]}
|
package/dist/quote/service.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { Terminal } from '@yuants/protocol';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { createQuoteState } from './state';
|
|
5
|
-
import { createQuoteProviderRegistry } from './upstream';
|
|
2
|
+
import { quoteState } from './state';
|
|
3
|
+
import { markDirty } from './scheduler';
|
|
6
4
|
const terminal = Terminal.fromNodeEnv();
|
|
7
|
-
const quoteState = createQuoteState();
|
|
8
|
-
const quoteProviderRegistry = createQuoteProviderRegistry(terminal);
|
|
9
5
|
const normalizeStrings = (values) => [...new Set(values)].sort();
|
|
10
6
|
const normalizeFields = (values) => [...new Set(values)].sort();
|
|
11
7
|
const analyzeRequestedQuotes = (quoteState, product_ids, fields, updated_at) => {
|
|
@@ -24,60 +20,6 @@ const analyzeRequestedQuotes = (quoteState, product_ids, fields, updated_at) =>
|
|
|
24
20
|
}
|
|
25
21
|
return { needUpdate };
|
|
26
22
|
};
|
|
27
|
-
const updateQueue$ = new Subject();
|
|
28
|
-
const updateQueueStats = {
|
|
29
|
-
queued_total: 0,
|
|
30
|
-
started_total: 0,
|
|
31
|
-
processed_total: 0,
|
|
32
|
-
};
|
|
33
|
-
const getQueueStatus = () => {
|
|
34
|
-
const pending = updateQueueStats.queued_total - updateQueueStats.started_total;
|
|
35
|
-
const in_flight = updateQueueStats.started_total - updateQueueStats.processed_total;
|
|
36
|
-
return Object.assign({ pending,
|
|
37
|
-
in_flight }, updateQueueStats);
|
|
38
|
-
};
|
|
39
|
-
const enqueueUpdateTask = (task) => {
|
|
40
|
-
updateQueueStats.queued_total++;
|
|
41
|
-
updateQueueStats.last_enqueued_at = Date.now();
|
|
42
|
-
updateQueue$.next(task);
|
|
43
|
-
};
|
|
44
|
-
const summarizeError = (error) => {
|
|
45
|
-
if (typeof error === 'object' && error !== null) {
|
|
46
|
-
const code = 'code' in error ? error.code : undefined;
|
|
47
|
-
const message = 'message' in error ? error.message : undefined;
|
|
48
|
-
return {
|
|
49
|
-
code: typeof code === 'string' ? code : undefined,
|
|
50
|
-
message: typeof message === 'string' ? message : undefined,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
return {};
|
|
54
|
-
};
|
|
55
|
-
const processUpdateTask = async (task) => {
|
|
56
|
-
const { needUpdate } = analyzeRequestedQuotes(quoteState, task.product_ids, task.fields, task.updated_at);
|
|
57
|
-
await quoteProviderRegistry.fillQuoteStateFromUpstream({
|
|
58
|
-
quoteState,
|
|
59
|
-
cacheMissed: needUpdate,
|
|
60
|
-
updated_at: task.updated_at,
|
|
61
|
-
});
|
|
62
|
-
};
|
|
63
|
-
updateQueue$
|
|
64
|
-
.pipe(concatMap((task) => defer(async () => {
|
|
65
|
-
updateQueueStats.started_total++;
|
|
66
|
-
updateQueueStats.last_started_at = Date.now();
|
|
67
|
-
try {
|
|
68
|
-
await processUpdateTask(task);
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
const summary = summarizeError(error);
|
|
72
|
-
updateQueueStats.last_error = Object.assign({ at: Date.now() }, summary);
|
|
73
|
-
console.info(formatTime(Date.now()), `[VEX][Quote]UpdateQueueTaskFailed`, `product_ids=${task.product_ids.length} fields=${task.fields.length} updated_at=${task.updated_at}`, JSON.stringify(summary));
|
|
74
|
-
}
|
|
75
|
-
finally {
|
|
76
|
-
updateQueueStats.processed_total++;
|
|
77
|
-
updateQueueStats.last_processed_at = Date.now();
|
|
78
|
-
}
|
|
79
|
-
})))
|
|
80
|
-
.subscribe();
|
|
81
23
|
terminal.server.provideService('VEX/UpdateQuotes', {}, async (msg) => {
|
|
82
24
|
quoteState.update(msg.req);
|
|
83
25
|
return { res: { code: 0, message: 'OK' } };
|
|
@@ -103,16 +45,11 @@ terminal.server.provideService('VEX/QueryQuotes', {
|
|
|
103
45
|
const product_ids = normalizeStrings(msg.req.product_ids);
|
|
104
46
|
const fields = normalizeFields(msg.req.fields);
|
|
105
47
|
const { updated_at } = msg.req;
|
|
106
|
-
// SWR strategy: if we have stale or missing data, enqueue an update task,
|
|
107
|
-
// but still return the current data immediately.
|
|
108
48
|
const { needUpdate } = analyzeRequestedQuotes(quoteState, product_ids, fields, updated_at);
|
|
109
|
-
|
|
110
|
-
|
|
49
|
+
for (const { product_id, field } of needUpdate) {
|
|
50
|
+
markDirty(product_id, field);
|
|
111
51
|
}
|
|
112
52
|
const data = quoteState.filterValues(product_ids, fields);
|
|
113
53
|
return { res: { code: 0, message: 'OK', data } };
|
|
114
54
|
});
|
|
115
|
-
terminal.server.provideService('VEX/QuoteUpdateQueueStatus', {}, async () => {
|
|
116
|
-
return { res: { code: 0, message: 'OK', data: getQueueStatus() } };
|
|
117
|
-
});
|
|
118
55
|
//# sourceMappingURL=service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/quote/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Terminal } from '@yuants/protocol';\nimport { formatTime } from '@yuants/utils';\nimport { Subject, concatMap, defer } from 'rxjs';\nimport { createQuoteState } from './state';\nimport { IQuoteKey, IQuoteRequire, IQuoteState, IQuoteUpdateAction } from './types';\nimport { createQuoteProviderRegistry } from './upstream';\n\nconst terminal = Terminal.fromNodeEnv();\n\nconst quoteState = createQuoteState();\nconst quoteProviderRegistry = createQuoteProviderRegistry(terminal);\n\ntype UpdateTask = { product_ids: string[]; fields: IQuoteKey[]; updated_at: number };\n\ntype IQuoteUpdateQueueStatus = {\n pending: number;\n in_flight: number;\n queued_total: number;\n started_total: number;\n processed_total: number;\n last_enqueued_at?: number;\n last_started_at?: number;\n last_processed_at?: number;\n last_error?: { at: number; code?: string; message?: string };\n};\n\nconst normalizeStrings = (values: string[]) => [...new Set(values)].sort();\nconst normalizeFields = (values: IQuoteKey[]) => [...new Set(values)].sort();\n\nconst analyzeRequestedQuotes = (\n quoteState: IQuoteState,\n product_ids: string[],\n fields: IQuoteKey[],\n updated_at: number,\n): { needUpdate: IQuoteRequire[] } => {\n const needUpdate: IQuoteRequire[] = [];\n for (const product_id of product_ids) {\n for (const field of fields) {\n const tuple = quoteState.getValueTuple(product_id, field);\n if (tuple === undefined) {\n needUpdate.push({ product_id, field });\n continue;\n }\n if (tuple[1] < updated_at) {\n needUpdate.push({ product_id, field });\n }\n }\n }\n return { needUpdate };\n};\n\nconst updateQueue$ = new Subject<UpdateTask>();\nconst updateQueueStats: Omit<IQuoteUpdateQueueStatus, 'pending' | 'in_flight'> = {\n queued_total: 0,\n started_total: 0,\n processed_total: 0,\n};\n\nconst getQueueStatus = (): IQuoteUpdateQueueStatus => {\n const pending = updateQueueStats.queued_total - updateQueueStats.started_total;\n const in_flight = updateQueueStats.started_total - updateQueueStats.processed_total;\n return {\n pending,\n in_flight,\n ...updateQueueStats,\n };\n};\n\nconst enqueueUpdateTask = (task: UpdateTask) => {\n updateQueueStats.queued_total++;\n updateQueueStats.last_enqueued_at = Date.now();\n updateQueue$.next(task);\n};\n\nconst summarizeError = (error: unknown): { code?: string; message?: string } => {\n if (typeof error === 'object' && error !== null) {\n const code = 'code' in error ? (error as any).code : undefined;\n const message = 'message' in error ? (error as any).message : undefined;\n return {\n code: typeof code === 'string' ? code : undefined,\n message: typeof message === 'string' ? message : undefined,\n };\n }\n return {};\n};\n\nconst processUpdateTask = async (task: UpdateTask) => {\n const { needUpdate } = analyzeRequestedQuotes(quoteState, task.product_ids, task.fields, task.updated_at);\n await quoteProviderRegistry.fillQuoteStateFromUpstream({\n quoteState,\n cacheMissed: needUpdate,\n updated_at: task.updated_at,\n });\n};\n\nupdateQueue$\n .pipe(\n concatMap((task) =>\n defer(async () => {\n updateQueueStats.started_total++;\n updateQueueStats.last_started_at = Date.now();\n\n try {\n await processUpdateTask(task);\n } catch (error) {\n const summary = summarizeError(error);\n updateQueueStats.last_error = { at: Date.now(), ...summary };\n console.info(\n formatTime(Date.now()),\n `[VEX][Quote]UpdateQueueTaskFailed`,\n `product_ids=${task.product_ids.length} fields=${task.fields.length} updated_at=${task.updated_at}`,\n JSON.stringify(summary),\n );\n } finally {\n updateQueueStats.processed_total++;\n updateQueueStats.last_processed_at = Date.now();\n }\n }),\n ),\n )\n .subscribe();\n\nterminal.server.provideService<IQuoteUpdateAction>('VEX/UpdateQuotes', {}, async (msg) => {\n quoteState.update(msg.req);\n return { res: { code: 0, message: 'OK' } };\n});\n\nterminal.server.provideService<{}, IQuoteUpdateAction>('VEX/DumpQuoteState', {}, async () => {\n return { res: { code: 0, message: 'OK', data: quoteState.dumpAsObject() } };\n});\n\nterminal.server.provideService<\n { product_ids: string[]; fields: IQuoteKey[]; updated_at: number },\n Record<string, Partial<Record<IQuoteKey, string>>>\n>(\n 'VEX/QueryQuotes',\n {\n type: 'object',\n required: ['product_ids', 'fields', 'updated_at'],\n properties: {\n product_ids: {\n type: 'array',\n items: { type: 'string' },\n },\n fields: {\n type: 'array',\n items: { type: 'string' },\n },\n updated_at: { type: 'number' },\n },\n },\n async (msg) => {\n const product_ids = normalizeStrings(msg.req.product_ids);\n const fields = normalizeFields(msg.req.fields);\n const { updated_at } = msg.req;\n\n // SWR strategy: if we have stale or missing data, enqueue an update task,\n // but still return the current data immediately.\n const { needUpdate } = analyzeRequestedQuotes(quoteState, product_ids, fields, updated_at);\n if (needUpdate.length > 0) {\n enqueueUpdateTask({ product_ids, fields, updated_at });\n }\n\n const data = quoteState.filterValues(product_ids, fields);\n return { res: { code: 0, message: 'OK', data } };\n },\n);\n\nterminal.server.provideService<{}, IQuoteUpdateQueueStatus>('VEX/QuoteUpdateQueueStatus', {}, async () => {\n return { res: { code: 0, message: 'OK', data: getQueueStatus() } };\n});\n"]}
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/quote/service.ts"],"names":[],"mappings":"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","sourcesContent":["import { Terminal } from '@yuants/protocol';\nimport { IQuoteField } from '@yuants/exchange';\nimport { quoteState } from './state';\nimport { IQuoteKey, IQuoteRequire, IQuoteState, IQuoteUpdateAction } from './types';\nimport { markDirty } from './scheduler';\n\nconst terminal = Terminal.fromNodeEnv();\n\nconst normalizeStrings = (values: string[]) => [...new Set(values)].sort();\nconst normalizeFields = (values: IQuoteKey[]) => [...new Set(values)].sort();\n\nconst analyzeRequestedQuotes = (\n quoteState: IQuoteState,\n product_ids: string[],\n fields: IQuoteKey[],\n updated_at: number,\n): { needUpdate: IQuoteRequire[] } => {\n const needUpdate: IQuoteRequire[] = [];\n for (const product_id of product_ids) {\n for (const field of fields) {\n const tuple = quoteState.getValueTuple(product_id, field);\n if (tuple === undefined) {\n needUpdate.push({ product_id, field });\n continue;\n }\n if (tuple[1] < updated_at) {\n needUpdate.push({ product_id, field });\n }\n }\n }\n return { needUpdate };\n};\n\nterminal.server.provideService<IQuoteUpdateAction>('VEX/UpdateQuotes', {}, async (msg) => {\n quoteState.update(msg.req);\n return { res: { code: 0, message: 'OK' } };\n});\n\nterminal.server.provideService<{}, IQuoteUpdateAction>('VEX/DumpQuoteState', {}, async () => {\n return { res: { code: 0, message: 'OK', data: quoteState.dumpAsObject() } };\n});\n\nterminal.server.provideService<\n { product_ids: string[]; fields: IQuoteKey[]; updated_at: number },\n Record<string, Partial<Record<IQuoteKey, string>>>\n>(\n 'VEX/QueryQuotes',\n {\n type: 'object',\n required: ['product_ids', 'fields', 'updated_at'],\n properties: {\n product_ids: {\n type: 'array',\n items: { type: 'string' },\n },\n fields: {\n type: 'array',\n items: { type: 'string' },\n },\n updated_at: { type: 'number' },\n },\n },\n async (msg) => {\n const product_ids = normalizeStrings(msg.req.product_ids);\n const fields = normalizeFields(msg.req.fields);\n const { updated_at } = msg.req;\n\n const { needUpdate } = analyzeRequestedQuotes(quoteState, product_ids, fields, updated_at);\n for (const { product_id, field } of needUpdate) {\n markDirty(product_id, field as any as IQuoteField);\n }\n\n const data = quoteState.filterValues(product_ids, fields);\n return { res: { code: 0, message: 'OK', data } };\n },\n);\n"]}
|
package/dist/quote/state.js
CHANGED
package/dist/quote/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/quote/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,eAAe;AACf,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC,EAAE,CAAC","sourcesContent":["import { implementations } from './implementations';\n\n// 使用 v1 作为生产版本\nexport const createQuoteState = implementations.v1;\n"]}
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/quote/state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,eAAe;AACf,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAC,EAAE,CAAC;AAEnD,MAAM,CAAC,MAAM,UAAU,GAAG,gBAAgB,EAAE,CAAC","sourcesContent":["import { implementations } from './implementations';\n\n// 使用 v1 作为生产版本\nexport const createQuoteState = implementations.v1;\n\nexport const quoteState = createQuoteState();\n"]}
|
package/lib/position.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAqC5C,eAAO,MAAM,gBAAgB,cAAqB,SAAS,EAAE,KAAG,QAAQ,SAAS,EAAE,CAiFlF,CAAC;AAEF,eAAO,MAAM,cAAc,WAAkB,MAAM,EAAE,KAAG,QAAQ,MAAM,EAAE,CA4BvE,CAAC"}
|