@yuants/app-virtual-exchange 0.8.1 → 0.9.1
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/credential.js +21 -5
- package/dist/credential.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/position.js +12 -5
- package/dist/position.js.map +1 -1
- package/dist/quote/benchmark/ForkedQuoteStateComparisonTest.js +356 -0
- package/dist/quote/benchmark/ForkedQuoteStateComparisonTest.js.map +1 -0
- package/dist/quote/benchmark/PerformanceTester.js +131 -0
- package/dist/quote/benchmark/PerformanceTester.js.map +1 -0
- package/dist/quote/benchmark/QuoteStateComparisonTest.js +261 -0
- package/dist/quote/benchmark/QuoteStateComparisonTest.js.map +1 -0
- package/dist/quote/benchmark/QuoteStateTestRunner.js +313 -0
- package/dist/quote/benchmark/QuoteStateTestRunner.js.map +1 -0
- package/dist/quote/benchmark/index.js +43 -0
- package/dist/quote/benchmark/index.js.map +1 -0
- package/dist/quote/benchmark/test-helpers.js +66 -0
- package/dist/quote/benchmark/test-helpers.js.map +1 -0
- package/dist/quote/benchmark/worker.js +263 -0
- package/dist/quote/benchmark/worker.js.map +1 -0
- package/dist/quote/implementations/index.js +13 -0
- package/dist/quote/implementations/index.js.map +1 -0
- package/dist/quote/implementations/v0.js +64 -0
- package/dist/quote/implementations/v0.js.map +1 -0
- package/dist/quote/implementations/v1.js +107 -0
- package/dist/quote/implementations/v1.js.map +1 -0
- package/dist/quote/implementations/v2.js +107 -0
- package/dist/quote/implementations/v2.js.map +1 -0
- package/dist/quote/implementations/v3.js +211 -0
- package/dist/quote/implementations/v3.js.map +1 -0
- package/dist/quote/service.js +45 -0
- package/dist/quote/service.js.map +1 -0
- package/dist/quote/state.benchmark.js +32 -0
- package/dist/quote/state.benchmark.js.map +1 -0
- package/dist/quote/state.js +4 -0
- package/dist/quote/state.js.map +1 -0
- package/dist/quote/types.js +2 -0
- package/dist/quote/types.js.map +1 -0
- package/lib/credential.d.ts.map +1 -1
- package/lib/credential.js +21 -5
- package/lib/credential.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/position.d.ts.map +1 -1
- package/lib/position.js +12 -5
- package/lib/position.js.map +1 -1
- package/lib/quote/benchmark/ForkedQuoteStateComparisonTest.d.ts +62 -0
- package/lib/quote/benchmark/ForkedQuoteStateComparisonTest.d.ts.map +1 -0
- package/lib/quote/benchmark/ForkedQuoteStateComparisonTest.js +360 -0
- package/lib/quote/benchmark/ForkedQuoteStateComparisonTest.js.map +1 -0
- package/lib/quote/benchmark/PerformanceTester.d.ts +53 -0
- package/lib/quote/benchmark/PerformanceTester.d.ts.map +1 -0
- package/lib/quote/benchmark/PerformanceTester.js +135 -0
- package/lib/quote/benchmark/PerformanceTester.js.map +1 -0
- package/lib/quote/benchmark/QuoteStateComparisonTest.d.ts +25 -0
- package/lib/quote/benchmark/QuoteStateComparisonTest.d.ts.map +1 -0
- package/lib/quote/benchmark/QuoteStateComparisonTest.js +265 -0
- package/lib/quote/benchmark/QuoteStateComparisonTest.js.map +1 -0
- package/lib/quote/benchmark/QuoteStateTestRunner.d.ts +61 -0
- package/lib/quote/benchmark/QuoteStateTestRunner.d.ts.map +1 -0
- package/lib/quote/benchmark/QuoteStateTestRunner.js +317 -0
- package/lib/quote/benchmark/QuoteStateTestRunner.js.map +1 -0
- package/lib/quote/benchmark/index.d.ts +13 -0
- package/lib/quote/benchmark/index.d.ts.map +1 -0
- package/lib/quote/benchmark/index.js +53 -0
- package/lib/quote/benchmark/index.js.map +1 -0
- package/lib/quote/benchmark/test-helpers.d.ts +18 -0
- package/lib/quote/benchmark/test-helpers.d.ts.map +1 -0
- package/lib/quote/benchmark/test-helpers.js +73 -0
- package/lib/quote/benchmark/test-helpers.js.map +1 -0
- package/lib/quote/benchmark/worker.d.ts +3 -0
- package/lib/quote/benchmark/worker.d.ts.map +1 -0
- package/lib/quote/benchmark/worker.js +265 -0
- package/lib/quote/benchmark/worker.js.map +1 -0
- package/lib/quote/implementations/index.d.ts +10 -0
- package/lib/quote/implementations/index.d.ts.map +1 -0
- package/lib/quote/implementations/index.js +16 -0
- package/lib/quote/implementations/index.js.map +1 -0
- package/lib/quote/implementations/v0.d.ts +7 -0
- package/lib/quote/implementations/v0.d.ts.map +1 -0
- package/lib/quote/implementations/v0.js +68 -0
- package/lib/quote/implementations/v0.js.map +1 -0
- package/lib/quote/implementations/v1.d.ts +8 -0
- package/lib/quote/implementations/v1.d.ts.map +1 -0
- package/lib/quote/implementations/v1.js +111 -0
- package/lib/quote/implementations/v1.js.map +1 -0
- package/lib/quote/implementations/v2.d.ts +7 -0
- package/lib/quote/implementations/v2.d.ts.map +1 -0
- package/lib/quote/implementations/v2.js +111 -0
- package/lib/quote/implementations/v2.js.map +1 -0
- package/lib/quote/implementations/v3.d.ts +8 -0
- package/lib/quote/implementations/v3.d.ts.map +1 -0
- package/lib/quote/implementations/v3.js +215 -0
- package/lib/quote/implementations/v3.js.map +1 -0
- package/lib/quote/service.d.ts +2 -0
- package/lib/quote/service.d.ts.map +1 -0
- package/lib/quote/service.js +47 -0
- package/lib/quote/service.js.map +1 -0
- package/lib/quote/state.benchmark.d.ts +2 -0
- package/lib/quote/state.benchmark.d.ts.map +1 -0
- package/lib/quote/state.benchmark.js +34 -0
- package/lib/quote/state.benchmark.js.map +1 -0
- package/lib/quote/state.d.ts +2 -0
- package/lib/quote/state.d.ts.map +1 -0
- package/lib/quote/state.js +7 -0
- package/lib/quote/state.js.map +1 -0
- package/lib/quote/types.d.ts +30 -0
- package/lib/quote/types.d.ts.map +1 -0
- package/lib/quote/types.js +3 -0
- package/lib/quote/types.js.map +1 -0
- package/package.json +1 -1
- package/temp/package-deps.json +24 -6
package/dist/credential.js
CHANGED
|
@@ -37,7 +37,25 @@ const listAllCredentials = async () => {
|
|
|
37
37
|
reader: credentialReader,
|
|
38
38
|
tags: { type: 'exchange_credential' },
|
|
39
39
|
});
|
|
40
|
-
|
|
40
|
+
const results = await Promise.allSettled(secrets.map((secret) => getCredentialBySecretId(secret.sign)));
|
|
41
|
+
return results.map((result, index) => {
|
|
42
|
+
if (result.status === 'fulfilled') {
|
|
43
|
+
return {
|
|
44
|
+
sign: secrets[index].sign,
|
|
45
|
+
credential: result.value.credential,
|
|
46
|
+
credentialId: result.value.credentialId,
|
|
47
|
+
error: null,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
return {
|
|
52
|
+
sign: secrets[index].sign,
|
|
53
|
+
credential: null,
|
|
54
|
+
credentialId: null,
|
|
55
|
+
error: `${result.reason}`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
});
|
|
41
59
|
};
|
|
42
60
|
terminal.server.provideService('VEX/RegisterExchangeCredential', {
|
|
43
61
|
type: 'object',
|
|
@@ -65,10 +83,8 @@ export const validCredentials$ = defer(() => listAllCredentials()).pipe(map((x)
|
|
|
65
83
|
if (!x)
|
|
66
84
|
return map;
|
|
67
85
|
for (const xx of x) {
|
|
68
|
-
if (xx.
|
|
69
|
-
|
|
70
|
-
if (xx.value.credentialId && xx.value.credential) {
|
|
71
|
-
map.set(xx.value.credentialId, xx.value.credential);
|
|
86
|
+
if (xx.credentialId && xx.credential) {
|
|
87
|
+
map.set(xx.credentialId, xx.credential);
|
|
72
88
|
}
|
|
73
89
|
}
|
|
74
90
|
return map;
|
package/dist/credential.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential.js","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAW,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC/E,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,MAAM,CAAC;AAO9E,MAAM,QAAQ,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;AAExC,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,QAAQ,CAAC,OAAO,CAAC,UAAU,CAAC;AAEzF;;;;GAIG;AACH,MAAM,6BAA6B,GAAG,WAAW,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;IACvE,MAAM,GAAG,GAAG,qCAAqC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC;IAC5E,MAAM,GAAG,GAAG,MAAM,UAAU,CAAY,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,QAAQ,CAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,SAAS,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAwB,CAAC;IAC1F,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,WAAW,CAAC,KAAK,EAAE,aAAqB,EAAE,EAAE;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAwB,CAAC;IACpE,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACxD,OAAO,GAAG,CAAC,IAAI,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,MAAM,kBAAkB,GAAG,KAAK,IAAI,EAAE;IACpC,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,QAAQ,EAAE;QAC1C,MAAM,EAAE,gBAAgB;QACxB,IAAI,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE;KACtC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,uBAAuB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"credential.js","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCache } from '@yuants/cache';\nimport { getCredentialId } from '@yuants/exchange';\nimport { Terminal } from '@yuants/protocol';\nimport { ISecret, listSecrets, readSecret, writeSecret } from '@yuants/secret';\nimport { escapeSQL, requestSQL } from '@yuants/sql';\nimport { newError } from '@yuants/utils';\nimport { defer, firstValueFrom, map, repeat, retry, shareReplay } from 'rxjs';\n\ninterface IExchangeCredential {\n type: string;\n payload: any;\n}\n\nconst terminal = Terminal.fromNodeEnv();\n\nconst credentialReader = process.env.NODE_UNIT_PUBLIC_KEY || terminal.keyPair.public_key;\n\n/**\n * 根据 secret sign 解析出对应的 exchange credential\n * 此处可以做缓存,因为同一个 secret sign 对应的 credential 信息永远不会变化,可以节约解密和后续 SQL 查询的开销\n * 得到 credential 后,此 credential 不一定是有效的,因为可能凭证信息已经过期或被撤销\n */\nconst secretSignToCredentialIdCache = createCache(async (sign: string) => {\n const sql = `SELECT * FROM secret WHERE sign = ${escapeSQL(sign)} LIMIT 1;`;\n const res = await requestSQL<ISecret[]>(terminal, sql);\n if (res.length === 0) throw newError('SECRET_NOT_FOUND', { sign });\n const secret = res[0];\n const decrypted = await readSecret(terminal, secret);\n const credential = JSON.parse(new TextDecoder().decode(decrypted)) as IExchangeCredential;\n return credential;\n});\n\n/**\n * 根据 credential 信息解析出对应的 credential ID\n * 此处可以做缓存,因为同一个 credential 永远对应同一个 credential ID,可以节约后续 SQL 查询的开销\n * 但是需要注意的是,credential ID 可能会因为凭证被撤销而失效,但是可以在下游调用其他服务时感知到,因此可以永久缓存\n */\nconst credentialIdCache = createCache(async (credentialKey: string) => {\n const credential = JSON.parse(credentialKey) as IExchangeCredential;\n const res = await getCredentialId(terminal, credential);\n return res.data;\n});\n\nconst listAllCredentials = async () => {\n const secrets = await listSecrets(terminal, {\n reader: credentialReader,\n tags: { type: 'exchange_credential' },\n });\n\n const results = await Promise.allSettled(secrets.map((secret) => getCredentialBySecretId(secret.sign)));\n return results.map(\n (\n result,\n index,\n ): {\n sign: string;\n credential: IExchangeCredential | null;\n credentialId: string | null;\n error: any;\n } => {\n if (result.status === 'fulfilled') {\n return {\n sign: secrets[index].sign,\n credential: result.value.credential,\n credentialId: result.value.credentialId,\n error: null,\n };\n } else {\n return {\n sign: secrets[index].sign,\n credential: null,\n credentialId: null,\n error: `${result.reason}`,\n };\n }\n },\n );\n};\n\nterminal.server.provideService<IExchangeCredential, ISecret>(\n 'VEX/RegisterExchangeCredential',\n {\n type: 'object',\n required: ['type', 'payload'],\n properties: {\n type: { type: 'string' },\n payload: { type: 'object' },\n },\n },\n async (msg) => {\n const credential = msg.req;\n const secretData = new TextEncoder().encode(JSON.stringify(credential));\n const secret = await writeSecret(terminal, credentialReader, { type: 'exchange_credential' }, secretData);\n return { res: { code: 0, message: 'OK', data: secret } };\n },\n);\n\n// For Debugging Purpose\nterminal.server.provideService('VEX/ListExchangeCredential', {}, async () => {\n return { res: { code: 0, message: 'OK', data: await listAllCredentials() } };\n});\n\nterminal.server.provideService<void, string[]>('VEX/ListCredentials', {}, async () => {\n const credentials = await firstValueFrom(validCredentials$);\n return { res: { code: 0, message: 'OK', data: [...credentials.keys()] } };\n});\n\nexport const validCredentials$ = defer(() => listAllCredentials()).pipe(\n map((x) => {\n const map = new Map<string, IExchangeCredential>();\n if (!x) return map;\n for (const xx of x) {\n if (xx.credentialId && xx.credential) {\n map.set(xx.credentialId, xx.credential);\n }\n }\n return map;\n }),\n repeat({ delay: 10000 }),\n retry({ delay: 5000 }),\n shareReplay(1),\n);\n\nexport const validCredentialTypes$ = validCredentials$.pipe(\n map((credentials) => {\n const types = new Set<string>();\n credentials.forEach((credential) => {\n types.add(credential.type);\n });\n return Array.from(types);\n }),\n);\n\n/**\n * 根据 secret sign 解析出对应的 credential 以及 credential ID\n * @param sign - secret sign\n * @returns 解析得到的 credential 以及对应的 credential ID\n * @throws 如果无法解析出对应的 credential 或 credential ID,则抛出异常\n *\n * 不依赖 List Credential 服务,可以及时感知凭证的新增和变更\n */\nexport const getCredentialBySecretId = async (sign: string) => {\n const credential = await secretSignToCredentialIdCache.query(sign);\n if (!credential) throw newError('CREDENTIAL_NOT_RESOLVED', { sign });\n const credentialId = await credentialIdCache.query(JSON.stringify(credential));\n if (!credentialId) throw newError('CREDENTIAL_ID_NOT_RESOLVED', { sign });\n return { sign, credential, credentialId };\n};\n"]}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,WAAW,CAAC;AACnB,OAAO,mBAAmB,CAAC;AAC3B,OAAO,qBAAqB,CAAC","sourcesContent":["import './credential';\nimport './general';\nimport './legacy-services';\nimport './product-collector';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,CAAC;AACtB,OAAO,WAAW,CAAC;AACnB,OAAO,mBAAmB,CAAC;AAC3B,OAAO,qBAAqB,CAAC;AAC7B,OAAO,iBAAiB,CAAC","sourcesContent":["import './credential';\nimport './general';\nimport './legacy-services';\nimport './product-collector';\nimport './quote/service';\n"]}
|
package/dist/position.js
CHANGED
|
@@ -23,7 +23,7 @@ const interestRateIntervalCache = createCache(async (product_id) => {
|
|
|
23
23
|
prevOfPrev,
|
|
24
24
|
interval,
|
|
25
25
|
};
|
|
26
|
-
}, { swrAfter:
|
|
26
|
+
}, { swrAfter: 60000, expire: 3600000 });
|
|
27
27
|
export const polyfillPosition = async (positions) => {
|
|
28
28
|
// TODO: 使用 batch query SQL 优化 product / quote 查询性能
|
|
29
29
|
for (const pos of positions) {
|
|
@@ -52,8 +52,14 @@ export const polyfillPosition = async (positions) => {
|
|
|
52
52
|
// 利率相关信息的追加
|
|
53
53
|
if (quote) {
|
|
54
54
|
if (quote.interest_rate_next_settled_at !== null) {
|
|
55
|
+
const nextSettledAt = new Date(quote.interest_rate_next_settled_at).getTime();
|
|
55
56
|
// 优先使用行情数据中的下一个结算时间
|
|
56
|
-
pos.settlement_scheduled_at =
|
|
57
|
+
pos.settlement_scheduled_at = nextSettledAt;
|
|
58
|
+
// 优先使用下一个结算时间推算结算间隔
|
|
59
|
+
if (interestRateInterval !== undefined) {
|
|
60
|
+
const interval = nextSettledAt - interestRateInterval.prev;
|
|
61
|
+
pos.settlement_interval = interval;
|
|
62
|
+
}
|
|
57
63
|
}
|
|
58
64
|
else if (quote.interest_rate_next_settled_at === null && interestRateInterval !== undefined) {
|
|
59
65
|
// 估算下一个结算时间
|
|
@@ -61,6 +67,10 @@ export const polyfillPosition = async (positions) => {
|
|
|
61
67
|
const k = Math.ceil((Date.now() - interestRateInterval.prev) / interestRateInterval.interval);
|
|
62
68
|
pos.settlement_scheduled_at = interestRateInterval.prev + k * interestRateInterval.interval;
|
|
63
69
|
}
|
|
70
|
+
// 如果还没有结算间隔,则使用 interest rate 表的时间间隔作为结算间隔
|
|
71
|
+
if (pos.settlement_interval === undefined && interestRateInterval) {
|
|
72
|
+
pos.settlement_interval = interestRateInterval.interval;
|
|
73
|
+
}
|
|
64
74
|
if (pos.direction === 'LONG') {
|
|
65
75
|
if (quote.interest_rate_long !== null) {
|
|
66
76
|
pos.interest_to_settle = +quote.interest_rate_long * pos.valuation;
|
|
@@ -72,9 +82,6 @@ export const polyfillPosition = async (positions) => {
|
|
|
72
82
|
}
|
|
73
83
|
}
|
|
74
84
|
}
|
|
75
|
-
if (interestRateInterval) {
|
|
76
|
-
pos.settlement_interval = interestRateInterval.interval;
|
|
77
|
-
}
|
|
78
85
|
}
|
|
79
86
|
return positions;
|
|
80
87
|
};
|
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 } 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: 3600_000, expire: 8 * 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 // 利率相关信息的追加\n if (quote) {\n if (quote.interest_rate_next_settled_at !== null) {\n // 优先使用行情数据中的下一个结算时间\n pos.settlement_scheduled_at = new Date(quote.interest_rate_next_settled_at).getTime();\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 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 if (interestRateInterval) {\n pos.settlement_interval = interestRateInterval.interval;\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 // 利率相关信息的追加\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"]}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { exec } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { QuoteStateComparisonTest } from './QuoteStateComparisonTest';
|
|
4
|
+
import { PerformanceTester } from './PerformanceTester';
|
|
5
|
+
const execAsync = promisify(exec);
|
|
6
|
+
/**
|
|
7
|
+
* 使用子进程进行隔离内存测试的对比测试类
|
|
8
|
+
* 每个实现都在独立的子进程中运行,确保内存测试的公平性
|
|
9
|
+
*/
|
|
10
|
+
export class ForkedQuoteStateComparisonTest {
|
|
11
|
+
constructor(nameA = 'Current', nameB = 'Baseline', workerScriptPath = __dirname + '/worker.ts') {
|
|
12
|
+
this.nameA = nameA;
|
|
13
|
+
this.nameB = nameB;
|
|
14
|
+
this.workerScriptPath = workerScriptPath;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 运行单个测试场景的子进程
|
|
18
|
+
*/
|
|
19
|
+
async runWorkerTest(implName, testType, productCount, extraArgs = {}) {
|
|
20
|
+
// 构建命令行参数
|
|
21
|
+
const args = [`--impl=${implName}`, `--test=${testType}`, `--product-count=${productCount}`];
|
|
22
|
+
// 添加额外参数
|
|
23
|
+
for (const [key, value] of Object.entries(extraArgs)) {
|
|
24
|
+
args.push(`--${key}=${value}`);
|
|
25
|
+
}
|
|
26
|
+
// 执行 worker 脚本,启用 GC 以进行公平的内存测试
|
|
27
|
+
const command = `node --expose-gc $(which ts-node) ${this.workerScriptPath} ${args.join(' ')}`;
|
|
28
|
+
try {
|
|
29
|
+
const { stdout, stderr } = await execAsync(command);
|
|
30
|
+
if (stderr && stderr.trim()) {
|
|
31
|
+
console.error(`Worker stderr (${implName}, ${testType}, ${productCount}):`, stderr);
|
|
32
|
+
}
|
|
33
|
+
const result = JSON.parse(stdout.trim());
|
|
34
|
+
if (!result.success) {
|
|
35
|
+
// 子进程返回了失败结果
|
|
36
|
+
return {
|
|
37
|
+
success: false,
|
|
38
|
+
error: result.error || 'Unknown error',
|
|
39
|
+
impl: implName,
|
|
40
|
+
test: testType,
|
|
41
|
+
productCount,
|
|
42
|
+
avgTime: null,
|
|
43
|
+
time: null,
|
|
44
|
+
heapUsedDiff: null,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
// 子进程执行失败(如 OOM killed、命令不存在等)
|
|
51
|
+
console.error(`Worker execution failed (${implName}, ${testType}, ${productCount}):`, error);
|
|
52
|
+
// 返回一个失败对象,而不是抛出错误
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
error: error instanceof Error ? error.message : String(error),
|
|
56
|
+
impl: implName,
|
|
57
|
+
test: testType,
|
|
58
|
+
productCount,
|
|
59
|
+
avgTime: null,
|
|
60
|
+
time: null,
|
|
61
|
+
heapUsedDiff: null,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 运行初始化测试对比
|
|
67
|
+
*/
|
|
68
|
+
async runInitializationTestComparison(productCount, iterations = 3) {
|
|
69
|
+
console.log(`运行初始化对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品)`);
|
|
70
|
+
const resultA = await this.runWorkerTest('current', 'init', productCount, {
|
|
71
|
+
iterations: iterations.toString(),
|
|
72
|
+
});
|
|
73
|
+
const resultB = await this.runWorkerTest('baseline', 'init', productCount, {
|
|
74
|
+
iterations: iterations.toString(),
|
|
75
|
+
});
|
|
76
|
+
return { resultA, resultB };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 运行更新测试对比
|
|
80
|
+
*/
|
|
81
|
+
async runUpdateTestComparison(productCount, updateCount) {
|
|
82
|
+
console.log(`运行更新对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品, ${updateCount} 更新)`);
|
|
83
|
+
const resultA = await this.runWorkerTest('current', 'update', productCount, {
|
|
84
|
+
'update-count': updateCount.toString(),
|
|
85
|
+
});
|
|
86
|
+
const resultB = await this.runWorkerTest('baseline', 'update', productCount, {
|
|
87
|
+
'update-count': updateCount.toString(),
|
|
88
|
+
});
|
|
89
|
+
return { resultA, resultB };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 运行查询测试对比
|
|
93
|
+
*/
|
|
94
|
+
async runQueryTestComparison(productCount, queryCount) {
|
|
95
|
+
console.log(`运行查询对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品, ${queryCount} 查询)`);
|
|
96
|
+
const resultA = await this.runWorkerTest('current', 'query', productCount, {
|
|
97
|
+
'query-count': queryCount.toString(),
|
|
98
|
+
});
|
|
99
|
+
const resultB = await this.runWorkerTest('baseline', 'query', productCount, {
|
|
100
|
+
'query-count': queryCount.toString(),
|
|
101
|
+
});
|
|
102
|
+
return { resultA, resultB };
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 运行过滤测试对比
|
|
106
|
+
*/
|
|
107
|
+
async runFilterTestComparison(productCount, filterCount) {
|
|
108
|
+
console.log(`运行过滤对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品, ${filterCount} 过滤)`);
|
|
109
|
+
const resultA = await this.runWorkerTest('current', 'filter', productCount, {
|
|
110
|
+
'filter-count': filterCount.toString(),
|
|
111
|
+
});
|
|
112
|
+
const resultB = await this.runWorkerTest('baseline', 'filter', productCount, {
|
|
113
|
+
'filter-count': filterCount.toString(),
|
|
114
|
+
});
|
|
115
|
+
return { resultA, resultB };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 运行转储测试对比
|
|
119
|
+
*/
|
|
120
|
+
async runDumpTestComparison(productCount) {
|
|
121
|
+
console.log(`运行转储对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品)`);
|
|
122
|
+
const resultA = await this.runWorkerTest('current', 'dump', productCount);
|
|
123
|
+
const resultB = await this.runWorkerTest('baseline', 'dump', productCount);
|
|
124
|
+
return { resultA, resultB };
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 打印对比结果(复用现有的对比结果打印逻辑)
|
|
128
|
+
*/
|
|
129
|
+
printComparisonResults(resultsA, resultsB, testType) {
|
|
130
|
+
// 创建一个临时的 QuoteStateComparisonTest 实例来复用其打印逻辑
|
|
131
|
+
const tempComparison = new QuoteStateComparisonTest(() => {
|
|
132
|
+
throw new Error('Not implemented');
|
|
133
|
+
}, () => {
|
|
134
|
+
throw new Error('Not implemented');
|
|
135
|
+
}, this.nameA, this.nameB);
|
|
136
|
+
// 使用私有方法,需要类型断言来访问
|
|
137
|
+
const privateMethod = tempComparison.printComparisonResults;
|
|
138
|
+
if (typeof privateMethod === 'function') {
|
|
139
|
+
privateMethod.call(tempComparison, resultsA, resultsB, testType);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// 如果无法访问私有方法,使用简化版本
|
|
143
|
+
console.log(`\n${this.nameA} vs ${this.nameB} - ${testType} 对比结果:`);
|
|
144
|
+
for (let i = 0; i < Math.min(resultsA.length, resultsB.length); i++) {
|
|
145
|
+
const a = resultsA[i];
|
|
146
|
+
const b = resultsB[i];
|
|
147
|
+
// 检查是否有失败的测试
|
|
148
|
+
const isAFailed = a.success === false;
|
|
149
|
+
const isBFailed = b.success === false;
|
|
150
|
+
if (isAFailed || isBFailed) {
|
|
151
|
+
console.log(`\n产品数量: ${a.productCount || b.productCount}`);
|
|
152
|
+
if (isAFailed) {
|
|
153
|
+
console.log(`❌ ${this.nameA} 测试失败: ${a.error || 'Unknown error'}`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
console.log(`${this.nameA}: ${a.avgTime || a.time}ms`);
|
|
157
|
+
}
|
|
158
|
+
if (isBFailed) {
|
|
159
|
+
console.log(`❌ ${this.nameB} 测试失败: ${b.error || 'Unknown error'}`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.log(`${this.nameB}: ${b.avgTime || b.time}ms`);
|
|
163
|
+
}
|
|
164
|
+
// 打印内存使用(即使只有一个实现有数据)
|
|
165
|
+
const memoryUsedA = a.heapUsedDiff;
|
|
166
|
+
const memoryUsedB = b.heapUsedDiff;
|
|
167
|
+
const hasMemoryA = memoryUsedA !== undefined && memoryUsedA !== null && !isNaN(memoryUsedA);
|
|
168
|
+
const hasMemoryB = memoryUsedB !== undefined && memoryUsedB !== null && !isNaN(memoryUsedB);
|
|
169
|
+
if (hasMemoryA || hasMemoryB) {
|
|
170
|
+
console.log(`\n内存使用:`);
|
|
171
|
+
if (hasMemoryA) {
|
|
172
|
+
console.log(`${this.nameA}: ${PerformanceTester.formatBytes(memoryUsedA)}`);
|
|
173
|
+
}
|
|
174
|
+
if (hasMemoryB) {
|
|
175
|
+
console.log(`${this.nameB}: ${PerformanceTester.formatBytes(memoryUsedB)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
console.log('-'.repeat(80));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
console.log(`产品数量 ${a.productCount}: ${a.avgTime || a.time}ms vs ${b.avgTime || b.time}ms`);
|
|
182
|
+
// 打印内存使用(即使只有一个实现有数据)
|
|
183
|
+
const memoryUsedA = a.heapUsedDiff;
|
|
184
|
+
const memoryUsedB = b.heapUsedDiff;
|
|
185
|
+
const hasMemoryA = memoryUsedA !== undefined && memoryUsedA !== null && !isNaN(memoryUsedA);
|
|
186
|
+
const hasMemoryB = memoryUsedB !== undefined && memoryUsedB !== null && !isNaN(memoryUsedB);
|
|
187
|
+
if (hasMemoryA || hasMemoryB) {
|
|
188
|
+
console.log(`\n内存使用:`);
|
|
189
|
+
if (hasMemoryA) {
|
|
190
|
+
console.log(`${this.nameA}: ${PerformanceTester.formatBytes(memoryUsedA)}`);
|
|
191
|
+
}
|
|
192
|
+
if (hasMemoryB) {
|
|
193
|
+
console.log(`${this.nameB}: ${PerformanceTester.formatBytes(memoryUsedB)}`);
|
|
194
|
+
}
|
|
195
|
+
// 只有当两个实现都有有效内存数据时才进行对比
|
|
196
|
+
if (hasMemoryA && hasMemoryB && memoryUsedA > 0 && memoryUsedB > 0) {
|
|
197
|
+
const memoryRatio = memoryUsedA / memoryUsedB;
|
|
198
|
+
const memoryPercentDiff = ((memoryRatio - 1) * 100).toFixed(2);
|
|
199
|
+
if (memoryRatio > 1) {
|
|
200
|
+
console.log(`${this.nameB} 节省 ${memoryPercentDiff}% 内存`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
console.log(`${this.nameA} 节省 ${(-parseFloat(memoryPercentDiff)).toFixed(2)}% 内存`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
console.log('-'.repeat(80));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 运行完整的对比测试套件
|
|
213
|
+
*/
|
|
214
|
+
async runComparisonTestSuite() {
|
|
215
|
+
console.log(`开始子进程隔离内存对比测试: ${this.nameA} vs ${this.nameB}`);
|
|
216
|
+
console.log('注意:每个测试都在独立的子进程中运行,确保内存测试的公平性');
|
|
217
|
+
const testScenarios = [
|
|
218
|
+
{
|
|
219
|
+
productCount: 10000,
|
|
220
|
+
label: '10K',
|
|
221
|
+
updateIterations: 1000,
|
|
222
|
+
queryIterations: 10000,
|
|
223
|
+
filterIterations: 1000,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
productCount: 100000,
|
|
227
|
+
label: '100K',
|
|
228
|
+
updateIterations: 100,
|
|
229
|
+
queryIterations: 1000,
|
|
230
|
+
filterIterations: 100,
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
productCount: 1000000,
|
|
234
|
+
label: '1M',
|
|
235
|
+
updateIterations: 10,
|
|
236
|
+
queryIterations: 100,
|
|
237
|
+
filterIterations: 10,
|
|
238
|
+
},
|
|
239
|
+
];
|
|
240
|
+
// 初始化测试对比
|
|
241
|
+
console.log('\n运行初始化测试对比...');
|
|
242
|
+
const initResultsA = [];
|
|
243
|
+
const initResultsB = [];
|
|
244
|
+
for (const scenario of testScenarios) {
|
|
245
|
+
console.log(`测试 ${scenario.label} 产品...`);
|
|
246
|
+
try {
|
|
247
|
+
const { resultA, resultB } = await this.runInitializationTestComparison(scenario.productCount, 3);
|
|
248
|
+
initResultsA.push(resultA);
|
|
249
|
+
initResultsB.push(resultB);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
console.error(`初始化对比测试失败 (${scenario.label}):`, error);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
this.printComparisonResults(initResultsA, initResultsB, '初始化测试');
|
|
256
|
+
// 更新测试对比
|
|
257
|
+
console.log('\n运行更新测试对比...');
|
|
258
|
+
const updateResultsA = [];
|
|
259
|
+
const updateResultsB = [];
|
|
260
|
+
for (const scenario of testScenarios) {
|
|
261
|
+
console.log(`测试 ${scenario.label} 产品...`);
|
|
262
|
+
try {
|
|
263
|
+
const { resultA, resultB } = await this.runUpdateTestComparison(scenario.productCount, scenario.updateIterations);
|
|
264
|
+
updateResultsA.push(resultA);
|
|
265
|
+
updateResultsB.push(resultB);
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
console.error(`更新对比测试失败 (${scenario.label}):`, error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
this.printComparisonResults(updateResultsA, updateResultsB, '更新测试');
|
|
272
|
+
// 查询测试对比
|
|
273
|
+
console.log('\n运行查询测试对比...');
|
|
274
|
+
const queryResultsA = [];
|
|
275
|
+
const queryResultsB = [];
|
|
276
|
+
for (const scenario of testScenarios) {
|
|
277
|
+
console.log(`测试 ${scenario.label} 产品...`);
|
|
278
|
+
try {
|
|
279
|
+
const { resultA, resultB } = await this.runQueryTestComparison(scenario.productCount, scenario.queryIterations);
|
|
280
|
+
queryResultsA.push(resultA);
|
|
281
|
+
queryResultsB.push(resultB);
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
console.error(`查询对比测试失败 (${scenario.label}):`, error);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
this.printComparisonResults(queryResultsA, queryResultsB, '查询测试');
|
|
288
|
+
// 过滤测试对比
|
|
289
|
+
console.log('\n运行过滤测试对比...');
|
|
290
|
+
const filterResultsA = [];
|
|
291
|
+
const filterResultsB = [];
|
|
292
|
+
for (const scenario of testScenarios) {
|
|
293
|
+
console.log(`测试 ${scenario.label} 产品...`);
|
|
294
|
+
try {
|
|
295
|
+
const { resultA, resultB } = await this.runFilterTestComparison(scenario.productCount, scenario.filterIterations);
|
|
296
|
+
filterResultsA.push(resultA);
|
|
297
|
+
filterResultsB.push(resultB);
|
|
298
|
+
}
|
|
299
|
+
catch (error) {
|
|
300
|
+
console.error(`过滤对比测试失败 (${scenario.label}):`, error);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
this.printComparisonResults(filterResultsA, filterResultsB, '过滤测试');
|
|
304
|
+
// 转储测试对比
|
|
305
|
+
console.log('\n运行转储测试对比...');
|
|
306
|
+
const dumpResultsA = [];
|
|
307
|
+
const dumpResultsB = [];
|
|
308
|
+
for (const scenario of testScenarios) {
|
|
309
|
+
console.log(`测试 ${scenario.label} 产品...`);
|
|
310
|
+
try {
|
|
311
|
+
const { resultA, resultB } = await this.runDumpTestComparison(scenario.productCount);
|
|
312
|
+
dumpResultsA.push(resultA);
|
|
313
|
+
dumpResultsB.push(resultB);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
console.error(`转储对比测试失败 (${scenario.label}):`, error);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
this.printComparisonResults(dumpResultsA, dumpResultsB, '转储测试');
|
|
320
|
+
console.log('\n子进程隔离内存对比测试完成!');
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* 运行快速对比测试(仅测试小数据量)
|
|
324
|
+
*/
|
|
325
|
+
async runQuickComparisonTest() {
|
|
326
|
+
console.log(`开始快速子进程对比测试: ${this.nameA} vs ${this.nameB}`);
|
|
327
|
+
const quickScenarios = [
|
|
328
|
+
{
|
|
329
|
+
productCount: 1000,
|
|
330
|
+
label: '1K',
|
|
331
|
+
updateIterations: 100,
|
|
332
|
+
queryIterations: 1000,
|
|
333
|
+
filterIterations: 100,
|
|
334
|
+
},
|
|
335
|
+
{ productCount: 10000, label: '10K', updateIterations: 10, queryIterations: 100, filterIterations: 10 },
|
|
336
|
+
];
|
|
337
|
+
// 初始化测试对比
|
|
338
|
+
console.log('\n运行初始化测试对比...');
|
|
339
|
+
const initResultsA = [];
|
|
340
|
+
const initResultsB = [];
|
|
341
|
+
for (const scenario of quickScenarios) {
|
|
342
|
+
console.log(`测试 ${scenario.label} 产品...`);
|
|
343
|
+
try {
|
|
344
|
+
const { resultA, resultB } = await this.runInitializationTestComparison(scenario.productCount, 2);
|
|
345
|
+
initResultsA.push(resultA);
|
|
346
|
+
initResultsB.push(resultB);
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
console.error(`初始化对比测试失败 (${scenario.label}):`, error);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
this.printComparisonResults(initResultsA, initResultsB, '快速初始化测试');
|
|
353
|
+
console.log('\n快速子进程对比测试完成!');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=ForkedQuoteStateComparisonTest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ForkedQuoteStateComparisonTest.js","sourceRoot":"","sources":["../../../src/quote/benchmark/ForkedQuoteStateComparisonTest.ts"],"names":[],"mappings":"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","sourcesContent":["import { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { QuoteStateComparisonTest } from './QuoteStateComparisonTest';\nimport { PerformanceTester } from './PerformanceTester';\n\nconst execAsync = promisify(exec);\n\n/**\n * 使用子进程进行隔离内存测试的对比测试类\n * 每个实现都在独立的子进程中运行,确保内存测试的公平性\n */\nexport class ForkedQuoteStateComparisonTest {\n private nameA: string;\n private nameB: string;\n private workerScriptPath: string;\n\n constructor(\n nameA: string = 'Current',\n nameB: string = 'Baseline',\n workerScriptPath: string = __dirname + '/worker.ts',\n ) {\n this.nameA = nameA;\n this.nameB = nameB;\n this.workerScriptPath = workerScriptPath;\n }\n\n /**\n * 运行单个测试场景的子进程\n */\n private async runWorkerTest(\n implName: string,\n testType: string,\n productCount: number,\n extraArgs: Record<string, string> = {},\n ): Promise<any> {\n // 构建命令行参数\n const args = [`--impl=${implName}`, `--test=${testType}`, `--product-count=${productCount}`];\n\n // 添加额外参数\n for (const [key, value] of Object.entries(extraArgs)) {\n args.push(`--${key}=${value}`);\n }\n\n // 执行 worker 脚本,启用 GC 以进行公平的内存测试\n const command = `node --expose-gc $(which ts-node) ${this.workerScriptPath} ${args.join(' ')}`;\n\n try {\n const { stdout, stderr } = await execAsync(command);\n\n if (stderr && stderr.trim()) {\n console.error(`Worker stderr (${implName}, ${testType}, ${productCount}):`, stderr);\n }\n\n const result = JSON.parse(stdout.trim());\n\n if (!result.success) {\n // 子进程返回了失败结果\n return {\n success: false,\n error: result.error || 'Unknown error',\n impl: implName,\n test: testType,\n productCount,\n avgTime: null,\n time: null,\n heapUsedDiff: null,\n };\n }\n\n return result;\n } catch (error) {\n // 子进程执行失败(如 OOM killed、命令不存在等)\n console.error(`Worker execution failed (${implName}, ${testType}, ${productCount}):`, error);\n\n // 返回一个失败对象,而不是抛出错误\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n impl: implName,\n test: testType,\n productCount,\n avgTime: null,\n time: null,\n heapUsedDiff: null,\n };\n }\n }\n\n /**\n * 运行初始化测试对比\n */\n async runInitializationTestComparison(\n productCount: number,\n iterations: number = 3,\n ): Promise<{\n resultA: any;\n resultB: any;\n }> {\n console.log(`运行初始化对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品)`);\n\n const resultA = await this.runWorkerTest('current', 'init', productCount, {\n iterations: iterations.toString(),\n });\n const resultB = await this.runWorkerTest('baseline', 'init', productCount, {\n iterations: iterations.toString(),\n });\n\n return { resultA, resultB };\n }\n\n /**\n * 运行更新测试对比\n */\n async runUpdateTestComparison(\n productCount: number,\n updateCount: number,\n ): Promise<{\n resultA: any;\n resultB: any;\n }> {\n console.log(\n `运行更新对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品, ${updateCount} 更新)`,\n );\n\n const resultA = await this.runWorkerTest('current', 'update', productCount, {\n 'update-count': updateCount.toString(),\n });\n const resultB = await this.runWorkerTest('baseline', 'update', productCount, {\n 'update-count': updateCount.toString(),\n });\n\n return { resultA, resultB };\n }\n\n /**\n * 运行查询测试对比\n */\n async runQueryTestComparison(\n productCount: number,\n queryCount: number,\n ): Promise<{\n resultA: any;\n resultB: any;\n }> {\n console.log(\n `运行查询对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品, ${queryCount} 查询)`,\n );\n\n const resultA = await this.runWorkerTest('current', 'query', productCount, {\n 'query-count': queryCount.toString(),\n });\n const resultB = await this.runWorkerTest('baseline', 'query', productCount, {\n 'query-count': queryCount.toString(),\n });\n\n return { resultA, resultB };\n }\n\n /**\n * 运行过滤测试对比\n */\n async runFilterTestComparison(\n productCount: number,\n filterCount: number,\n ): Promise<{\n resultA: any;\n resultB: any;\n }> {\n console.log(\n `运行过滤对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品, ${filterCount} 过滤)`,\n );\n\n const resultA = await this.runWorkerTest('current', 'filter', productCount, {\n 'filter-count': filterCount.toString(),\n });\n const resultB = await this.runWorkerTest('baseline', 'filter', productCount, {\n 'filter-count': filterCount.toString(),\n });\n\n return { resultA, resultB };\n }\n\n /**\n * 运行转储测试对比\n */\n async runDumpTestComparison(productCount: number): Promise<{\n resultA: any;\n resultB: any;\n }> {\n console.log(`运行转储对比测试: ${this.nameA} vs ${this.nameB} (${productCount} 产品)`);\n\n const resultA = await this.runWorkerTest('current', 'dump', productCount);\n const resultB = await this.runWorkerTest('baseline', 'dump', productCount);\n\n return { resultA, resultB };\n }\n\n /**\n * 打印对比结果(复用现有的对比结果打印逻辑)\n */\n private printComparisonResults(resultsA: any[], resultsB: any[], testType: string): void {\n // 创建一个临时的 QuoteStateComparisonTest 实例来复用其打印逻辑\n const tempComparison = new QuoteStateComparisonTest(\n () => {\n throw new Error('Not implemented');\n },\n () => {\n throw new Error('Not implemented');\n },\n this.nameA,\n this.nameB,\n );\n\n // 使用私有方法,需要类型断言来访问\n const privateMethod = (tempComparison as any).printComparisonResults;\n if (typeof privateMethod === 'function') {\n privateMethod.call(tempComparison, resultsA, resultsB, testType);\n } else {\n // 如果无法访问私有方法,使用简化版本\n console.log(`\\n${this.nameA} vs ${this.nameB} - ${testType} 对比结果:`);\n for (let i = 0; i < Math.min(resultsA.length, resultsB.length); i++) {\n const a = resultsA[i];\n const b = resultsB[i];\n\n // 检查是否有失败的测试\n const isAFailed = a.success === false;\n const isBFailed = b.success === false;\n\n if (isAFailed || isBFailed) {\n console.log(`\\n产品数量: ${a.productCount || b.productCount}`);\n\n if (isAFailed) {\n console.log(`❌ ${this.nameA} 测试失败: ${a.error || 'Unknown error'}`);\n } else {\n console.log(`${this.nameA}: ${a.avgTime || a.time}ms`);\n }\n\n if (isBFailed) {\n console.log(`❌ ${this.nameB} 测试失败: ${b.error || 'Unknown error'}`);\n } else {\n console.log(`${this.nameB}: ${b.avgTime || b.time}ms`);\n }\n\n // 打印内存使用(即使只有一个实现有数据)\n const memoryUsedA = a.heapUsedDiff;\n const memoryUsedB = b.heapUsedDiff;\n const hasMemoryA = memoryUsedA !== undefined && memoryUsedA !== null && !isNaN(memoryUsedA);\n const hasMemoryB = memoryUsedB !== undefined && memoryUsedB !== null && !isNaN(memoryUsedB);\n\n if (hasMemoryA || hasMemoryB) {\n console.log(`\\n内存使用:`);\n if (hasMemoryA) {\n console.log(`${this.nameA}: ${PerformanceTester.formatBytes(memoryUsedA)}`);\n }\n if (hasMemoryB) {\n console.log(`${this.nameB}: ${PerformanceTester.formatBytes(memoryUsedB)}`);\n }\n }\n\n console.log('-'.repeat(80));\n continue;\n }\n\n console.log(`产品数量 ${a.productCount}: ${a.avgTime || a.time}ms vs ${b.avgTime || b.time}ms`);\n\n // 打印内存使用(即使只有一个实现有数据)\n const memoryUsedA = a.heapUsedDiff;\n const memoryUsedB = b.heapUsedDiff;\n const hasMemoryA = memoryUsedA !== undefined && memoryUsedA !== null && !isNaN(memoryUsedA);\n const hasMemoryB = memoryUsedB !== undefined && memoryUsedB !== null && !isNaN(memoryUsedB);\n\n if (hasMemoryA || hasMemoryB) {\n console.log(`\\n内存使用:`);\n if (hasMemoryA) {\n console.log(`${this.nameA}: ${PerformanceTester.formatBytes(memoryUsedA)}`);\n }\n if (hasMemoryB) {\n console.log(`${this.nameB}: ${PerformanceTester.formatBytes(memoryUsedB)}`);\n }\n\n // 只有当两个实现都有有效内存数据时才进行对比\n if (hasMemoryA && hasMemoryB && memoryUsedA > 0 && memoryUsedB > 0) {\n const memoryRatio = memoryUsedA / memoryUsedB;\n const memoryPercentDiff = ((memoryRatio - 1) * 100).toFixed(2);\n if (memoryRatio > 1) {\n console.log(`${this.nameB} 节省 ${memoryPercentDiff}% 内存`);\n } else {\n console.log(`${this.nameA} 节省 ${(-parseFloat(memoryPercentDiff)).toFixed(2)}% 内存`);\n }\n }\n }\n\n console.log('-'.repeat(80));\n }\n }\n }\n\n /**\n * 运行完整的对比测试套件\n */\n async runComparisonTestSuite(): Promise<void> {\n console.log(`开始子进程隔离内存对比测试: ${this.nameA} vs ${this.nameB}`);\n console.log('注意:每个测试都在独立的子进程中运行,确保内存测试的公平性');\n\n const testScenarios = [\n {\n productCount: 10000,\n label: '10K',\n updateIterations: 1000,\n queryIterations: 10000,\n filterIterations: 1000,\n },\n {\n productCount: 100000,\n label: '100K',\n updateIterations: 100,\n queryIterations: 1000,\n filterIterations: 100,\n },\n {\n productCount: 1000000,\n label: '1M',\n updateIterations: 10,\n queryIterations: 100,\n filterIterations: 10,\n },\n ];\n\n // 初始化测试对比\n console.log('\\n运行初始化测试对比...');\n const initResultsA = [];\n const initResultsB = [];\n for (const scenario of testScenarios) {\n console.log(`测试 ${scenario.label} 产品...`);\n try {\n const { resultA, resultB } = await this.runInitializationTestComparison(scenario.productCount, 3);\n initResultsA.push(resultA);\n initResultsB.push(resultB);\n } catch (error) {\n console.error(`初始化对比测试失败 (${scenario.label}):`, error);\n }\n }\n this.printComparisonResults(initResultsA, initResultsB, '初始化测试');\n\n // 更新测试对比\n console.log('\\n运行更新测试对比...');\n const updateResultsA = [];\n const updateResultsB = [];\n for (const scenario of testScenarios) {\n console.log(`测试 ${scenario.label} 产品...`);\n try {\n const { resultA, resultB } = await this.runUpdateTestComparison(\n scenario.productCount,\n scenario.updateIterations,\n );\n updateResultsA.push(resultA);\n updateResultsB.push(resultB);\n } catch (error) {\n console.error(`更新对比测试失败 (${scenario.label}):`, error);\n }\n }\n this.printComparisonResults(updateResultsA, updateResultsB, '更新测试');\n\n // 查询测试对比\n console.log('\\n运行查询测试对比...');\n const queryResultsA = [];\n const queryResultsB = [];\n for (const scenario of testScenarios) {\n console.log(`测试 ${scenario.label} 产品...`);\n try {\n const { resultA, resultB } = await this.runQueryTestComparison(\n scenario.productCount,\n scenario.queryIterations,\n );\n queryResultsA.push(resultA);\n queryResultsB.push(resultB);\n } catch (error) {\n console.error(`查询对比测试失败 (${scenario.label}):`, error);\n }\n }\n this.printComparisonResults(queryResultsA, queryResultsB, '查询测试');\n\n // 过滤测试对比\n console.log('\\n运行过滤测试对比...');\n const filterResultsA = [];\n const filterResultsB = [];\n for (const scenario of testScenarios) {\n console.log(`测试 ${scenario.label} 产品...`);\n try {\n const { resultA, resultB } = await this.runFilterTestComparison(\n scenario.productCount,\n scenario.filterIterations,\n );\n filterResultsA.push(resultA);\n filterResultsB.push(resultB);\n } catch (error) {\n console.error(`过滤对比测试失败 (${scenario.label}):`, error);\n }\n }\n this.printComparisonResults(filterResultsA, filterResultsB, '过滤测试');\n\n // 转储测试对比\n console.log('\\n运行转储测试对比...');\n const dumpResultsA = [];\n const dumpResultsB = [];\n for (const scenario of testScenarios) {\n console.log(`测试 ${scenario.label} 产品...`);\n try {\n const { resultA, resultB } = await this.runDumpTestComparison(scenario.productCount);\n dumpResultsA.push(resultA);\n dumpResultsB.push(resultB);\n } catch (error) {\n console.error(`转储对比测试失败 (${scenario.label}):`, error);\n }\n }\n this.printComparisonResults(dumpResultsA, dumpResultsB, '转储测试');\n\n console.log('\\n子进程隔离内存对比测试完成!');\n }\n\n /**\n * 运行快速对比测试(仅测试小数据量)\n */\n async runQuickComparisonTest(): Promise<void> {\n console.log(`开始快速子进程对比测试: ${this.nameA} vs ${this.nameB}`);\n\n const quickScenarios = [\n {\n productCount: 1000,\n label: '1K',\n updateIterations: 100,\n queryIterations: 1000,\n filterIterations: 100,\n },\n { productCount: 10000, label: '10K', updateIterations: 10, queryIterations: 100, filterIterations: 10 },\n ];\n\n // 初始化测试对比\n console.log('\\n运行初始化测试对比...');\n const initResultsA = [];\n const initResultsB = [];\n for (const scenario of quickScenarios) {\n console.log(`测试 ${scenario.label} 产品...`);\n try {\n const { resultA, resultB } = await this.runInitializationTestComparison(scenario.productCount, 2);\n initResultsA.push(resultA);\n initResultsB.push(resultB);\n } catch (error) {\n console.error(`初始化对比测试失败 (${scenario.label}):`, error);\n }\n }\n this.printComparisonResults(initResultsA, initResultsB, '快速初始化测试');\n\n console.log('\\n快速子进程对比测试完成!');\n }\n}\n"]}
|