@yuants/app-virtual-exchange 0.7.2 → 0.8.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 +49 -44
- package/dist/credential.js.map +1 -1
- package/dist/general.js +16 -7
- package/dist/general.js.map +1 -1
- package/dist/legacy-services.js +11 -4
- package/dist/legacy-services.js.map +1 -1
- package/dist/position.js +35 -4
- package/dist/position.js.map +1 -1
- package/lib/credential.d.ts +14 -11
- package/lib/credential.d.ts.map +1 -1
- package/lib/credential.js +49 -46
- package/lib/credential.js.map +1 -1
- package/lib/general.js +15 -6
- package/lib/general.js.map +1 -1
- package/lib/legacy-services.js +10 -3
- package/lib/legacy-services.js.map +1 -1
- package/lib/position.d.ts +2 -0
- package/lib/position.d.ts.map +1 -1
- package/lib/position.js +37 -5
- package/lib/position.js.map +1 -1
- package/package.json +5 -5
- package/temp/package-deps.json +11 -11
package/dist/credential.js
CHANGED
|
@@ -1,35 +1,43 @@
|
|
|
1
1
|
import { createCache } from '@yuants/cache';
|
|
2
2
|
import { getCredentialId } from '@yuants/exchange';
|
|
3
3
|
import { Terminal } from '@yuants/protocol';
|
|
4
|
-
import { readSecret, writeSecret } from '@yuants/secret';
|
|
4
|
+
import { listSecrets, readSecret, writeSecret } from '@yuants/secret';
|
|
5
5
|
import { escapeSQL, requestSQL } from '@yuants/sql';
|
|
6
6
|
import { newError } from '@yuants/utils';
|
|
7
7
|
import { defer, firstValueFrom, map, repeat, retry, shareReplay } from 'rxjs';
|
|
8
8
|
const terminal = Terminal.fromNodeEnv();
|
|
9
9
|
const credentialReader = process.env.NODE_UNIT_PUBLIC_KEY || terminal.keyPair.public_key;
|
|
10
|
+
/**
|
|
11
|
+
* 根据 secret sign 解析出对应的 exchange credential
|
|
12
|
+
* 此处可以做缓存,因为同一个 secret sign 对应的 credential 信息永远不会变化,可以节约解密和后续 SQL 查询的开销
|
|
13
|
+
* 得到 credential 后,此 credential 不一定是有效的,因为可能凭证信息已经过期或被撤销
|
|
14
|
+
*/
|
|
15
|
+
const secretSignToCredentialIdCache = createCache(async (sign) => {
|
|
16
|
+
const sql = `SELECT * FROM secret WHERE sign = ${escapeSQL(sign)} LIMIT 1;`;
|
|
17
|
+
const res = await requestSQL(terminal, sql);
|
|
18
|
+
if (res.length === 0)
|
|
19
|
+
throw newError('SECRET_NOT_FOUND', { sign });
|
|
20
|
+
const secret = res[0];
|
|
21
|
+
const decrypted = await readSecret(terminal, secret);
|
|
22
|
+
const credential = JSON.parse(new TextDecoder().decode(decrypted));
|
|
23
|
+
return credential;
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* 根据 credential 信息解析出对应的 credential ID
|
|
27
|
+
* 此处可以做缓存,因为同一个 credential 永远对应同一个 credential ID,可以节约后续 SQL 查询的开销
|
|
28
|
+
* 但是需要注意的是,credential ID 可能会因为凭证被撤销而失效,但是可以在下游调用其他服务时感知到,因此可以永久缓存
|
|
29
|
+
*/
|
|
10
30
|
const credentialIdCache = createCache(async (credentialKey) => {
|
|
11
31
|
const credential = JSON.parse(credentialKey);
|
|
12
32
|
const res = await getCredentialId(terminal, credential);
|
|
13
33
|
return res.data;
|
|
14
34
|
});
|
|
15
|
-
|
|
16
|
-
const secrets = await
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const credential = JSON.parse(new TextDecoder().decode(decrypted));
|
|
22
|
-
result.credential = credential;
|
|
23
|
-
const res = await credentialIdCache.query(JSON.stringify(credential));
|
|
24
|
-
if (res) {
|
|
25
|
-
result.credentialId = res;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
catch (e) {
|
|
29
|
-
result.reason = `${e}`;
|
|
30
|
-
}
|
|
31
|
-
}));
|
|
32
|
-
return results;
|
|
35
|
+
const listAllCredentials = async () => {
|
|
36
|
+
const secrets = await listSecrets(terminal, {
|
|
37
|
+
reader: credentialReader,
|
|
38
|
+
tags: { type: 'exchange_credential' },
|
|
39
|
+
});
|
|
40
|
+
return Promise.allSettled(secrets.map((secret) => getCredentialBySecretId(secret.sign)));
|
|
33
41
|
};
|
|
34
42
|
terminal.server.provideService('VEX/RegisterExchangeCredential', {
|
|
35
43
|
type: 'object',
|
|
@@ -44,6 +52,7 @@ terminal.server.provideService('VEX/RegisterExchangeCredential', {
|
|
|
44
52
|
const secret = await writeSecret(terminal, credentialReader, { type: 'exchange_credential' }, secretData);
|
|
45
53
|
return { res: { code: 0, message: 'OK', data: secret } };
|
|
46
54
|
});
|
|
55
|
+
// For Debugging Purpose
|
|
47
56
|
terminal.server.provideService('VEX/ListExchangeCredential', {}, async () => {
|
|
48
57
|
return { res: { code: 0, message: 'OK', data: await listAllCredentials() } };
|
|
49
58
|
});
|
|
@@ -51,14 +60,15 @@ terminal.server.provideService('VEX/ListCredentials', {}, async () => {
|
|
|
51
60
|
const credentials = await firstValueFrom(validCredentials$);
|
|
52
61
|
return { res: { code: 0, message: 'OK', data: [...credentials.keys()] } };
|
|
53
62
|
});
|
|
54
|
-
const
|
|
55
|
-
export const validCredentials$ = defer(() => credentialCache.query('')).pipe(map((x) => {
|
|
63
|
+
export const validCredentials$ = defer(() => listAllCredentials()).pipe(map((x) => {
|
|
56
64
|
const map = new Map();
|
|
57
65
|
if (!x)
|
|
58
66
|
return map;
|
|
59
67
|
for (const xx of x) {
|
|
60
|
-
if (xx.
|
|
61
|
-
|
|
68
|
+
if (xx.status !== 'fulfilled')
|
|
69
|
+
continue;
|
|
70
|
+
if (xx.value.credentialId && xx.value.credential) {
|
|
71
|
+
map.set(xx.value.credentialId, xx.value.credential);
|
|
62
72
|
}
|
|
63
73
|
}
|
|
64
74
|
return map;
|
|
@@ -70,26 +80,21 @@ export const validCredentialTypes$ = validCredentials$.pipe(map((credentials) =>
|
|
|
70
80
|
});
|
|
71
81
|
return Array.from(types);
|
|
72
82
|
}));
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
if (!
|
|
84
|
-
throw newError('CREDENTIAL_NOT_RESOLVED', {
|
|
85
|
-
|
|
86
|
-
if (!
|
|
87
|
-
throw newError('CREDENTIAL_ID_NOT_RESOLVED', {
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
secret: theCredential.secret,
|
|
91
|
-
credential: theCredential.credential,
|
|
92
|
-
credentialId: theCredential.credentialId,
|
|
93
|
-
};
|
|
83
|
+
/**
|
|
84
|
+
* 根据 secret sign 解析出对应的 credential 以及 credential ID
|
|
85
|
+
* @param sign - secret sign
|
|
86
|
+
* @returns 解析得到的 credential 以及对应的 credential ID
|
|
87
|
+
* @throws 如果无法解析出对应的 credential 或 credential ID,则抛出异常
|
|
88
|
+
*
|
|
89
|
+
* 不依赖 List Credential 服务,可以及时感知凭证的新增和变更
|
|
90
|
+
*/
|
|
91
|
+
export const getCredentialBySecretId = async (sign) => {
|
|
92
|
+
const credential = await secretSignToCredentialIdCache.query(sign);
|
|
93
|
+
if (!credential)
|
|
94
|
+
throw newError('CREDENTIAL_NOT_RESOLVED', { sign });
|
|
95
|
+
const credentialId = await credentialIdCache.query(JSON.stringify(credential));
|
|
96
|
+
if (!credentialId)
|
|
97
|
+
throw newError('CREDENTIAL_ID_NOT_RESOLVED', { sign });
|
|
98
|
+
return { sign, credential, credentialId };
|
|
94
99
|
};
|
|
95
100
|
//# sourceMappingURL=credential.js.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,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,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 return Promise.allSettled(secrets.map((secret) => getCredentialBySecretId(secret.sign)));\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.status !== 'fulfilled') continue;\n if (xx.value.credentialId && xx.value.credential) {\n map.set(xx.value.credentialId, xx.value.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/general.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cancelOrder, getOrders, getPositions, modifyOrder, submitOrder } from '@yuants/exchange';
|
|
2
2
|
import { Terminal } from '@yuants/protocol';
|
|
3
3
|
import { getCredentialBySecretId } from './credential';
|
|
4
|
-
import { polyfillPosition } from './position';
|
|
4
|
+
import { polyfillOrders, polyfillPosition } from './position';
|
|
5
5
|
const terminal = Terminal.fromNodeEnv();
|
|
6
6
|
terminal.server.provideService('VEX/GetPositions', {
|
|
7
7
|
type: 'object',
|
|
@@ -16,6 +16,9 @@ terminal.server.provideService('VEX/GetPositions', {
|
|
|
16
16
|
if (!res.data)
|
|
17
17
|
return { res };
|
|
18
18
|
const positions = await polyfillPosition(res.data);
|
|
19
|
+
positions.forEach((pos) => {
|
|
20
|
+
pos.account_id = credential.credentialId;
|
|
21
|
+
});
|
|
19
22
|
return { res: { code: 0, message: 'OK', data: positions } };
|
|
20
23
|
});
|
|
21
24
|
terminal.server.provideService('VEX/GetOrders', {
|
|
@@ -26,13 +29,16 @@ terminal.server.provideService('VEX/GetOrders', {
|
|
|
26
29
|
product_id: { type: 'string' },
|
|
27
30
|
},
|
|
28
31
|
}, async (msg) => {
|
|
29
|
-
var _a;
|
|
30
32
|
const credential = await getCredentialBySecretId(msg.req.secret_id);
|
|
31
33
|
const res = await getOrders(terminal, credential.credential, msg.req.product_id);
|
|
32
|
-
(
|
|
34
|
+
if (!res.data)
|
|
35
|
+
return { res };
|
|
36
|
+
const orders = res.data;
|
|
37
|
+
orders.forEach((order) => {
|
|
33
38
|
order.account_id = credential.credentialId;
|
|
34
39
|
});
|
|
35
|
-
|
|
40
|
+
await polyfillOrders(orders);
|
|
41
|
+
return { res: { code: 0, message: 'OK', data: orders } };
|
|
36
42
|
});
|
|
37
43
|
// 10. Proxy Orders
|
|
38
44
|
// SubmitOrder
|
|
@@ -45,7 +51,8 @@ terminal.server.provideService('VEX/SubmitOrder', {
|
|
|
45
51
|
},
|
|
46
52
|
}, async (msg) => {
|
|
47
53
|
const credential = await getCredentialBySecretId(msg.req.secret_id);
|
|
48
|
-
const
|
|
54
|
+
const [order] = await polyfillOrders([msg.req.order]);
|
|
55
|
+
const res = await submitOrder(terminal, credential.credential, order);
|
|
49
56
|
return { res };
|
|
50
57
|
});
|
|
51
58
|
// ModifyOrder
|
|
@@ -58,7 +65,8 @@ terminal.server.provideService('VEX/ModifyOrder', {
|
|
|
58
65
|
},
|
|
59
66
|
}, async (msg) => {
|
|
60
67
|
const credential = await getCredentialBySecretId(msg.req.secret_id);
|
|
61
|
-
const
|
|
68
|
+
const [order] = await polyfillOrders([msg.req.order]);
|
|
69
|
+
const res = await modifyOrder(terminal, credential.credential, order);
|
|
62
70
|
return { res };
|
|
63
71
|
});
|
|
64
72
|
// CancelOrder
|
|
@@ -71,7 +79,8 @@ terminal.server.provideService('VEX/CancelOrder', {
|
|
|
71
79
|
},
|
|
72
80
|
}, async (msg) => {
|
|
73
81
|
const credential = await getCredentialBySecretId(msg.req.secret_id);
|
|
74
|
-
const
|
|
82
|
+
const [order] = await polyfillOrders([msg.req.order]);
|
|
83
|
+
const res = await cancelOrder(terminal, credential.credential, order);
|
|
75
84
|
return { res };
|
|
76
85
|
});
|
|
77
86
|
//# sourceMappingURL=general.js.map
|
package/dist/general.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"general.js","sourceRoot":"","sources":["../src/general.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"general.js","sourceRoot":"","sources":["../src/general.ts"],"names":[],"mappings":"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","sourcesContent":["import { IPosition } from '@yuants/data-account';\nimport { IOrder } from '@yuants/data-order';\nimport { cancelOrder, getOrders, getPositions, modifyOrder, submitOrder } from '@yuants/exchange';\nimport { Terminal } from '@yuants/protocol';\nimport { getCredentialBySecretId } from './credential';\nimport { polyfillOrders, polyfillPosition } from './position';\n\nconst terminal = Terminal.fromNodeEnv();\n\nterminal.server.provideService<{ secret_id: string; product_id?: string }, IPosition[]>(\n 'VEX/GetPositions',\n {\n type: 'object',\n required: ['secret_id'],\n properties: {\n secret_id: { type: 'string' },\n product_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const res = await getPositions(terminal, credential.credential, msg.req.product_id);\n if (!res.data) return { res };\n const positions = await polyfillPosition(res.data);\n positions.forEach((pos) => {\n pos.account_id = credential.credentialId;\n });\n return { res: { code: 0, message: 'OK', data: positions } };\n },\n);\n\nterminal.server.provideService<{ secret_id: string; product_id?: string }, IOrder[]>(\n 'VEX/GetOrders',\n {\n type: 'object',\n required: ['secret_id'],\n properties: {\n secret_id: { type: 'string' },\n product_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const res = await getOrders(terminal, credential.credential, msg.req.product_id);\n if (!res.data) return { res };\n const orders = res.data;\n orders.forEach((order) => {\n order.account_id = credential.credentialId;\n });\n await polyfillOrders(orders);\n return { res: { code: 0, message: 'OK', data: orders } };\n },\n);\n\n// 10. Proxy Orders\n// SubmitOrder\nterminal.server.provideService<{ order: IOrder; secret_id: string }, { order_id: string }>(\n 'VEX/SubmitOrder',\n {\n type: 'object',\n required: ['order', 'secret_id'],\n properties: {\n order: { type: 'object' },\n secret_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const [order] = await polyfillOrders([msg.req.order]);\n const res = await submitOrder(terminal, credential.credential, order);\n return { res };\n },\n);\n\n// ModifyOrder\nterminal.server.provideService<{ order: IOrder; secret_id: string }, void>(\n 'VEX/ModifyOrder',\n {\n type: 'object',\n required: ['order', 'secret_id'],\n properties: {\n order: { type: 'object' },\n secret_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const [order] = await polyfillOrders([msg.req.order]);\n const res = await modifyOrder(terminal, credential.credential, order);\n return { res };\n },\n);\n\n// CancelOrder\nterminal.server.provideService<{ order: IOrder; secret_id: string }, void>(\n 'VEX/CancelOrder',\n {\n type: 'object',\n required: ['order', 'secret_id'],\n properties: {\n order: { type: 'object' },\n secret_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const [order] = await polyfillOrders([msg.req.order]);\n const res = await cancelOrder(terminal, credential.credential, order);\n return { res };\n },\n);\n"]}
|
package/dist/legacy-services.js
CHANGED
|
@@ -5,7 +5,7 @@ import { Terminal } from '@yuants/protocol';
|
|
|
5
5
|
import { listWatch, newError } from '@yuants/utils';
|
|
6
6
|
import { map, Observable } from 'rxjs';
|
|
7
7
|
import { validCredentials$ } from './credential';
|
|
8
|
-
import { polyfillPosition } from './position';
|
|
8
|
+
import { polyfillOrders, polyfillPosition } from './position';
|
|
9
9
|
const terminal = Terminal.fromNodeEnv();
|
|
10
10
|
validCredentials$
|
|
11
11
|
.pipe(map((x) => Array.from(x.entries())), listWatch(([id]) => id, ([credential_id, credential]) => new Observable((sub) => {
|
|
@@ -17,6 +17,9 @@ validCredentials$
|
|
|
17
17
|
if (!res.data)
|
|
18
18
|
throw newError('FETCH_POSITIONS_FAILED', { credential_id, res });
|
|
19
19
|
const polyfilledPositions = await polyfillPosition(res.data);
|
|
20
|
+
polyfilledPositions.forEach((pos) => {
|
|
21
|
+
pos.account_id = credential_id;
|
|
22
|
+
});
|
|
20
23
|
return polyfilledPositions;
|
|
21
24
|
}, {
|
|
22
25
|
auto_refresh_interval: 1000,
|
|
@@ -34,6 +37,7 @@ validCredentials$
|
|
|
34
37
|
res.data.forEach((order) => {
|
|
35
38
|
order.account_id = credential_id;
|
|
36
39
|
});
|
|
40
|
+
await polyfillOrders(res.data);
|
|
37
41
|
return res.data;
|
|
38
42
|
}, {
|
|
39
43
|
auto_refresh_interval: 10000,
|
|
@@ -51,7 +55,8 @@ validCredentials$
|
|
|
51
55
|
account_id: { type: 'string', const: credential_id },
|
|
52
56
|
},
|
|
53
57
|
}, async (msg) => {
|
|
54
|
-
const
|
|
58
|
+
const [order] = await polyfillOrders([msg.req]);
|
|
59
|
+
const res = await submitOrder(terminal, credential, order);
|
|
55
60
|
return { res };
|
|
56
61
|
});
|
|
57
62
|
sub.add(() => {
|
|
@@ -67,7 +72,8 @@ validCredentials$
|
|
|
67
72
|
account_id: { type: 'string', const: credential_id },
|
|
68
73
|
},
|
|
69
74
|
}, async (msg) => {
|
|
70
|
-
const
|
|
75
|
+
const [order] = await polyfillOrders([msg.req]);
|
|
76
|
+
const res = await modifyOrder(terminal, credential, order);
|
|
71
77
|
return { res };
|
|
72
78
|
});
|
|
73
79
|
sub.add(() => {
|
|
@@ -83,7 +89,8 @@ validCredentials$
|
|
|
83
89
|
account_id: { type: 'string', const: credential_id },
|
|
84
90
|
},
|
|
85
91
|
}, async (msg) => {
|
|
86
|
-
const
|
|
92
|
+
const [order] = await polyfillOrders([msg.req]);
|
|
93
|
+
const res = await cancelOrder(terminal, credential, order);
|
|
87
94
|
return { res };
|
|
88
95
|
});
|
|
89
96
|
sub.add(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy-services.js","sourceRoot":"","sources":["../src/legacy-services.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAAU,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAClG,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"legacy-services.js","sourceRoot":"","sources":["../src/legacy-services.ts"],"names":[],"mappings":"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","sourcesContent":["import { provideAccountInfoService } from '@yuants/data-account';\nimport { IOrder, providePendingOrdersService } from '@yuants/data-order';\nimport { cancelOrder, getOrders, getPositions, modifyOrder, submitOrder } from '@yuants/exchange';\nimport { Terminal } from '@yuants/protocol';\nimport { listWatch, newError } from '@yuants/utils';\nimport { map, Observable } from 'rxjs';\nimport { validCredentials$ } from './credential';\nimport { polyfillOrders, polyfillPosition } from './position';\n\nconst terminal = Terminal.fromNodeEnv();\n\nvalidCredentials$\n .pipe(\n map((x) => Array.from(x.entries())),\n listWatch(\n ([id]) => id,\n ([credential_id, credential]) =>\n new Observable((sub) => {\n console.info(`Setting up VEX services for credential: ${credential_id}`);\n // Setup AccountInfo Service\n {\n const service = provideAccountInfoService(\n terminal,\n credential_id,\n async () => {\n const res = await getPositions(terminal, credential);\n if (!res.data) throw newError('FETCH_POSITIONS_FAILED', { credential_id, res });\n const polyfilledPositions = await polyfillPosition(res.data);\n polyfilledPositions.forEach((pos) => {\n pos.account_id = credential_id;\n });\n return polyfilledPositions;\n },\n {\n auto_refresh_interval: 1000,\n },\n );\n sub.add(() => {\n service.dispose$.next();\n });\n }\n // Setup Pending Orders Service\n {\n const service = providePendingOrdersService(\n terminal,\n credential_id,\n async () => {\n const res = await getOrders(terminal, credential);\n if (!res.data) throw newError('FETCH_ORDERS_FAILED', { credential_id, res });\n\n res.data.forEach((order) => {\n order.account_id = credential_id;\n });\n\n await polyfillOrders(res.data);\n\n return res.data;\n },\n {\n auto_refresh_interval: 10000,\n },\n );\n sub.add(() => {\n service.dispose$.next();\n });\n }\n // Setup SubmitOrder Service\n {\n const service = terminal.server.provideService<IOrder, { order_id: string }>(\n 'SubmitOrder',\n {\n type: 'object',\n required: ['account_id'],\n properties: {\n account_id: { type: 'string', const: credential_id },\n },\n },\n async (msg) => {\n const [order] = await polyfillOrders([msg.req]);\n const res = await submitOrder(terminal, credential, order);\n return { res };\n },\n );\n sub.add(() => {\n service.dispose();\n });\n }\n\n // Setup ModifyOrder Service\n {\n const service = terminal.server.provideService<IOrder, void>(\n 'ModifyOrder',\n {\n type: 'object',\n required: ['account_id'],\n properties: {\n account_id: { type: 'string', const: credential_id },\n },\n },\n async (msg) => {\n const [order] = await polyfillOrders([msg.req]);\n const res = await modifyOrder(terminal, credential, order);\n return { res };\n },\n );\n sub.add(() => {\n service.dispose();\n });\n }\n\n // Setup CancelOrder Service\n {\n const service = terminal.server.provideService<IOrder, void>(\n 'CancelOrder',\n {\n type: 'object',\n required: ['account_id'],\n properties: {\n account_id: { type: 'string', const: credential_id },\n },\n },\n async (msg) => {\n const [order] = await polyfillOrders([msg.req]);\n const res = await cancelOrder(terminal, credential, order);\n return { res };\n },\n );\n sub.add(() => {\n service.dispose();\n });\n }\n }),\n ),\n )\n .subscribe();\n"]}
|
package/dist/position.js
CHANGED
|
@@ -2,6 +2,7 @@ import { createCache } from '@yuants/cache';
|
|
|
2
2
|
import { createClientProductCache } from '@yuants/data-product';
|
|
3
3
|
import { Terminal } from '@yuants/protocol';
|
|
4
4
|
import { escapeSQL, requestSQL } from '@yuants/sql';
|
|
5
|
+
import { newError } from '@yuants/utils';
|
|
5
6
|
const terminal = Terminal.fromNodeEnv();
|
|
6
7
|
const productCache = createClientProductCache(terminal);
|
|
7
8
|
const quoteCache = createCache(async (product_id) => {
|
|
@@ -17,14 +18,12 @@ const interestRateIntervalCache = createCache(async (product_id) => {
|
|
|
17
18
|
const prev = new Date(rates[0].created_at).getTime();
|
|
18
19
|
const prevOfPrev = new Date(rates[1].created_at).getTime();
|
|
19
20
|
const interval = prev - prevOfPrev;
|
|
20
|
-
const next = prev + interval;
|
|
21
21
|
return {
|
|
22
22
|
prev,
|
|
23
23
|
prevOfPrev,
|
|
24
24
|
interval,
|
|
25
|
-
next,
|
|
26
25
|
};
|
|
27
|
-
});
|
|
26
|
+
}, { swrAfter: 3600000, expire: 8 * 3600000 });
|
|
28
27
|
export const polyfillPosition = async (positions) => {
|
|
29
28
|
// TODO: 使用 batch query SQL 优化 product / quote 查询性能
|
|
30
29
|
for (const pos of positions) {
|
|
@@ -58,7 +57,9 @@ export const polyfillPosition = async (positions) => {
|
|
|
58
57
|
}
|
|
59
58
|
else if (quote.interest_rate_next_settled_at === null && interestRateInterval !== undefined) {
|
|
60
59
|
// 估算下一个结算时间
|
|
61
|
-
|
|
60
|
+
// 找到 prev + k * interval > now 的最小 k,则下一个结算时间为 prev + k * interval
|
|
61
|
+
const k = Math.ceil((Date.now() - interestRateInterval.prev) / interestRateInterval.interval);
|
|
62
|
+
pos.settlement_scheduled_at = interestRateInterval.prev + k * interestRateInterval.interval;
|
|
62
63
|
}
|
|
63
64
|
if (pos.direction === 'LONG') {
|
|
64
65
|
if (quote.interest_rate_long !== null) {
|
|
@@ -77,4 +78,34 @@ export const polyfillPosition = async (positions) => {
|
|
|
77
78
|
}
|
|
78
79
|
return positions;
|
|
79
80
|
};
|
|
81
|
+
export const polyfillOrders = async (orders) => {
|
|
82
|
+
for (const order of orders) {
|
|
83
|
+
const theProduct = await productCache.query(order.product_id);
|
|
84
|
+
if (theProduct) {
|
|
85
|
+
if (order.size !== undefined) {
|
|
86
|
+
const sizeNum = +order.size;
|
|
87
|
+
const sizeStep = theProduct.volume_step * theProduct.value_scale;
|
|
88
|
+
if (!(sizeStep > 0))
|
|
89
|
+
throw newError('INVALID_SIZE_STEP', { product: theProduct, sizeStep });
|
|
90
|
+
// check size is multiple of sizeStep
|
|
91
|
+
if (Math.abs(sizeNum - Math.round(sizeNum / sizeStep) * sizeStep) > 1e-16) {
|
|
92
|
+
throw newError('INVALID_ORDER_SIZE_NOT_MULTIPLE_OF_SIZE_STEP', {
|
|
93
|
+
order,
|
|
94
|
+
sizeStep,
|
|
95
|
+
sizeNum,
|
|
96
|
+
product: theProduct,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (sizeNum >= 0) {
|
|
100
|
+
order.order_direction = order.is_close ? 'CLOSE_SHORT' : 'OPEN_LONG';
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
order.order_direction = order.is_close ? 'CLOSE_LONG' : 'OPEN_SHORT';
|
|
104
|
+
}
|
|
105
|
+
order.volume = Math.abs(sizeNum) / theProduct.value_scale;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return orders;
|
|
110
|
+
};
|
|
80
111
|
//# sourceMappingURL=position.js.map
|
package/dist/position.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"position.js","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
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"]}
|
package/lib/credential.d.ts
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
export interface IExchangeCredential {
|
|
1
|
+
interface IExchangeCredential {
|
|
3
2
|
type: string;
|
|
4
3
|
payload: any;
|
|
5
4
|
}
|
|
6
|
-
interface ICredentialResolvedStatus {
|
|
7
|
-
secret: ISecret;
|
|
8
|
-
credential?: IExchangeCredential;
|
|
9
|
-
credentialId?: string;
|
|
10
|
-
reason?: string;
|
|
11
|
-
}
|
|
12
|
-
export declare const listAllCredentials: () => Promise<ICredentialResolvedStatus[]>;
|
|
13
5
|
export declare const validCredentials$: import("rxjs").Observable<Map<string, IExchangeCredential>>;
|
|
14
6
|
export declare const validCredentialTypes$: import("rxjs").Observable<string[]>;
|
|
15
|
-
|
|
16
|
-
|
|
7
|
+
/**
|
|
8
|
+
* 根据 secret sign 解析出对应的 credential 以及 credential ID
|
|
9
|
+
* @param sign - secret sign
|
|
10
|
+
* @returns 解析得到的 credential 以及对应的 credential ID
|
|
11
|
+
* @throws 如果无法解析出对应的 credential 或 credential ID,则抛出异常
|
|
12
|
+
*
|
|
13
|
+
* 不依赖 List Credential 服务,可以及时感知凭证的新增和变更
|
|
14
|
+
*/
|
|
15
|
+
export declare const getCredentialBySecretId: (sign: string) => Promise<{
|
|
16
|
+
sign: string;
|
|
17
|
+
credential: IExchangeCredential;
|
|
18
|
+
credentialId: string;
|
|
19
|
+
}>;
|
|
17
20
|
export {};
|
|
18
21
|
//# sourceMappingURL=credential.d.ts.map
|
package/lib/credential.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"credential.d.ts","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":"AAQA,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,GAAG,CAAC;CACd;AAqED,eAAO,MAAM,iBAAiB,6DAe7B,CAAC;AAEF,eAAO,MAAM,qBAAqB,qCAQjC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,SAAgB,MAAM;;;;EAMzD,CAAC"}
|
package/lib/credential.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCredentialBySecretId = exports.
|
|
3
|
+
exports.getCredentialBySecretId = exports.validCredentialTypes$ = exports.validCredentials$ = void 0;
|
|
4
4
|
const cache_1 = require("@yuants/cache");
|
|
5
5
|
const exchange_1 = require("@yuants/exchange");
|
|
6
6
|
const protocol_1 = require("@yuants/protocol");
|
|
@@ -10,31 +10,38 @@ const utils_1 = require("@yuants/utils");
|
|
|
10
10
|
const rxjs_1 = require("rxjs");
|
|
11
11
|
const terminal = protocol_1.Terminal.fromNodeEnv();
|
|
12
12
|
const credentialReader = process.env.NODE_UNIT_PUBLIC_KEY || terminal.keyPair.public_key;
|
|
13
|
+
/**
|
|
14
|
+
* 根据 secret sign 解析出对应的 exchange credential
|
|
15
|
+
* 此处可以做缓存,因为同一个 secret sign 对应的 credential 信息永远不会变化,可以节约解密和后续 SQL 查询的开销
|
|
16
|
+
* 得到 credential 后,此 credential 不一定是有效的,因为可能凭证信息已经过期或被撤销
|
|
17
|
+
*/
|
|
18
|
+
const secretSignToCredentialIdCache = (0, cache_1.createCache)(async (sign) => {
|
|
19
|
+
const sql = `SELECT * FROM secret WHERE sign = ${(0, sql_1.escapeSQL)(sign)} LIMIT 1;`;
|
|
20
|
+
const res = await (0, sql_1.requestSQL)(terminal, sql);
|
|
21
|
+
if (res.length === 0)
|
|
22
|
+
throw (0, utils_1.newError)('SECRET_NOT_FOUND', { sign });
|
|
23
|
+
const secret = res[0];
|
|
24
|
+
const decrypted = await (0, secret_1.readSecret)(terminal, secret);
|
|
25
|
+
const credential = JSON.parse(new TextDecoder().decode(decrypted));
|
|
26
|
+
return credential;
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* 根据 credential 信息解析出对应的 credential ID
|
|
30
|
+
* 此处可以做缓存,因为同一个 credential 永远对应同一个 credential ID,可以节约后续 SQL 查询的开销
|
|
31
|
+
* 但是需要注意的是,credential ID 可能会因为凭证被撤销而失效,但是可以在下游调用其他服务时感知到,因此可以永久缓存
|
|
32
|
+
*/
|
|
13
33
|
const credentialIdCache = (0, cache_1.createCache)(async (credentialKey) => {
|
|
14
34
|
const credential = JSON.parse(credentialKey);
|
|
15
35
|
const res = await (0, exchange_1.getCredentialId)(terminal, credential);
|
|
16
36
|
return res.data;
|
|
17
37
|
});
|
|
18
38
|
const listAllCredentials = async () => {
|
|
19
|
-
const secrets = await (0,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const credential = JSON.parse(new TextDecoder().decode(decrypted));
|
|
25
|
-
result.credential = credential;
|
|
26
|
-
const res = await credentialIdCache.query(JSON.stringify(credential));
|
|
27
|
-
if (res) {
|
|
28
|
-
result.credentialId = res;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
catch (e) {
|
|
32
|
-
result.reason = `${e}`;
|
|
33
|
-
}
|
|
34
|
-
}));
|
|
35
|
-
return results;
|
|
39
|
+
const secrets = await (0, secret_1.listSecrets)(terminal, {
|
|
40
|
+
reader: credentialReader,
|
|
41
|
+
tags: { type: 'exchange_credential' },
|
|
42
|
+
});
|
|
43
|
+
return Promise.allSettled(secrets.map((secret) => (0, exports.getCredentialBySecretId)(secret.sign)));
|
|
36
44
|
};
|
|
37
|
-
exports.listAllCredentials = listAllCredentials;
|
|
38
45
|
terminal.server.provideService('VEX/RegisterExchangeCredential', {
|
|
39
46
|
type: 'object',
|
|
40
47
|
required: ['type', 'payload'],
|
|
@@ -48,21 +55,23 @@ terminal.server.provideService('VEX/RegisterExchangeCredential', {
|
|
|
48
55
|
const secret = await (0, secret_1.writeSecret)(terminal, credentialReader, { type: 'exchange_credential' }, secretData);
|
|
49
56
|
return { res: { code: 0, message: 'OK', data: secret } };
|
|
50
57
|
});
|
|
58
|
+
// For Debugging Purpose
|
|
51
59
|
terminal.server.provideService('VEX/ListExchangeCredential', {}, async () => {
|
|
52
|
-
return { res: { code: 0, message: 'OK', data: await
|
|
60
|
+
return { res: { code: 0, message: 'OK', data: await listAllCredentials() } };
|
|
53
61
|
});
|
|
54
62
|
terminal.server.provideService('VEX/ListCredentials', {}, async () => {
|
|
55
63
|
const credentials = await (0, rxjs_1.firstValueFrom)(exports.validCredentials$);
|
|
56
64
|
return { res: { code: 0, message: 'OK', data: [...credentials.keys()] } };
|
|
57
65
|
});
|
|
58
|
-
|
|
59
|
-
exports.validCredentials$ = (0, rxjs_1.defer)(() => credentialCache.query('')).pipe((0, rxjs_1.map)((x) => {
|
|
66
|
+
exports.validCredentials$ = (0, rxjs_1.defer)(() => listAllCredentials()).pipe((0, rxjs_1.map)((x) => {
|
|
60
67
|
const map = new Map();
|
|
61
68
|
if (!x)
|
|
62
69
|
return map;
|
|
63
70
|
for (const xx of x) {
|
|
64
|
-
if (xx.
|
|
65
|
-
|
|
71
|
+
if (xx.status !== 'fulfilled')
|
|
72
|
+
continue;
|
|
73
|
+
if (xx.value.credentialId && xx.value.credential) {
|
|
74
|
+
map.set(xx.value.credentialId, xx.value.credential);
|
|
66
75
|
}
|
|
67
76
|
}
|
|
68
77
|
return map;
|
|
@@ -74,28 +83,22 @@ exports.validCredentialTypes$ = exports.validCredentials$.pipe((0, rxjs_1.map)((
|
|
|
74
83
|
});
|
|
75
84
|
return Array.from(types);
|
|
76
85
|
}));
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
return {
|
|
95
|
-
secret: theCredential.secret,
|
|
96
|
-
credential: theCredential.credential,
|
|
97
|
-
credentialId: theCredential.credentialId,
|
|
98
|
-
};
|
|
86
|
+
/**
|
|
87
|
+
* 根据 secret sign 解析出对应的 credential 以及 credential ID
|
|
88
|
+
* @param sign - secret sign
|
|
89
|
+
* @returns 解析得到的 credential 以及对应的 credential ID
|
|
90
|
+
* @throws 如果无法解析出对应的 credential 或 credential ID,则抛出异常
|
|
91
|
+
*
|
|
92
|
+
* 不依赖 List Credential 服务,可以及时感知凭证的新增和变更
|
|
93
|
+
*/
|
|
94
|
+
const getCredentialBySecretId = async (sign) => {
|
|
95
|
+
const credential = await secretSignToCredentialIdCache.query(sign);
|
|
96
|
+
if (!credential)
|
|
97
|
+
throw (0, utils_1.newError)('CREDENTIAL_NOT_RESOLVED', { sign });
|
|
98
|
+
const credentialId = await credentialIdCache.query(JSON.stringify(credential));
|
|
99
|
+
if (!credentialId)
|
|
100
|
+
throw (0, utils_1.newError)('CREDENTIAL_ID_NOT_RESOLVED', { sign });
|
|
101
|
+
return { sign, credential, credentialId };
|
|
99
102
|
};
|
|
100
103
|
exports.getCredentialBySecretId = getCredentialBySecretId;
|
|
101
104
|
//# sourceMappingURL=credential.js.map
|
package/lib/credential.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credential.js","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":";;;AAAA,yCAA4C;AAC5C,+CAAmD;AACnD,+CAA4C;AAC5C,
|
|
1
|
+
{"version":3,"file":"credential.js","sourceRoot":"","sources":["../src/credential.ts"],"names":[],"mappings":";;;AAAA,yCAA4C;AAC5C,+CAAmD;AACnD,+CAA4C;AAC5C,2CAA+E;AAC/E,qCAAoD;AACpD,yCAAyC;AACzC,+BAA8E;AAO9E,MAAM,QAAQ,GAAG,mBAAQ,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,IAAA,mBAAW,EAAC,KAAK,EAAE,IAAY,EAAE,EAAE;IACvE,MAAM,GAAG,GAAG,qCAAqC,IAAA,eAAS,EAAC,IAAI,CAAC,WAAW,CAAC;IAC5E,MAAM,GAAG,GAAG,MAAM,IAAA,gBAAU,EAAY,QAAQ,EAAE,GAAG,CAAC,CAAC;IACvD,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAA,gBAAQ,EAAC,kBAAkB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACnE,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACtB,MAAM,SAAS,GAAG,MAAM,IAAA,mBAAU,EAAC,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,IAAA,mBAAW,EAAC,KAAK,EAAE,aAAqB,EAAE,EAAE;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAwB,CAAC;IACpE,MAAM,GAAG,GAAG,MAAM,IAAA,0BAAe,EAAC,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,IAAA,oBAAW,EAAC,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,IAAA,+BAAuB,EAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3F,CAAC,CAAC;AAEF,QAAQ,CAAC,MAAM,CAAC,cAAc,CAC5B,gCAAgC,EAChC;IACE,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC;IAC7B,UAAU,EAAE;QACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;QACxB,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;KAC5B;CACF,EACD,KAAK,EAAE,GAAG,EAAE,EAAE;IACZ,MAAM,UAAU,GAAG,GAAG,CAAC,GAAG,CAAC;IAC3B,MAAM,UAAU,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,MAAM,IAAA,oBAAW,EAAC,QAAQ,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,qBAAqB,EAAE,EAAE,UAAU,CAAC,CAAC;IAC1G,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;AAC3D,CAAC,CACF,CAAC;AAEF,wBAAwB;AACxB,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC,4BAA4B,EAAE,EAAE,EAAE,KAAK,IAAI,EAAE;IAC1E,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,kBAAkB,EAAE,EAAE,EAAE,CAAC;AAC/E,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAiB,qBAAqB,EAAE,EAAE,EAAE,KAAK,IAAI,EAAE;IACnF,MAAM,WAAW,GAAG,MAAM,IAAA,qBAAc,EAAC,yBAAiB,CAAC,CAAC;IAC5D,OAAO,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;AAC5E,CAAC,CAAC,CAAC;AAEU,QAAA,iBAAiB,GAAG,IAAA,YAAK,EAAC,GAAG,EAAE,CAAC,kBAAkB,EAAE,CAAC,CAAC,IAAI,CACrE,IAAA,UAAG,EAAC,CAAC,CAAC,EAAE,EAAE;IACR,MAAM,GAAG,GAAG,IAAI,GAAG,EAA+B,CAAC;IACnD,IAAI,CAAC,CAAC;QAAE,OAAO,GAAG,CAAC;IACnB,KAAK,MAAM,EAAE,IAAI,CAAC,EAAE;QAClB,IAAI,EAAE,CAAC,MAAM,KAAK,WAAW;YAAE,SAAS;QACxC,IAAI,EAAE,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE;YAChD,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;SACrD;KACF;IACD,OAAO,GAAG,CAAC;AACb,CAAC,CAAC,EACF,IAAA,aAAM,EAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EACxB,IAAA,YAAK,EAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EACtB,IAAA,kBAAW,EAAC,CAAC,CAAC,CACf,CAAC;AAEW,QAAA,qBAAqB,GAAG,yBAAiB,CAAC,IAAI,CACzD,IAAA,UAAG,EAAC,CAAC,WAAW,EAAE,EAAE;IAClB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,WAAW,CAAC,OAAO,CAAC,CAAC,UAAU,EAAE,EAAE;QACjC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC,CAAC,CACH,CAAC;AAEF;;;;;;;GAOG;AACI,MAAM,uBAAuB,GAAG,KAAK,EAAE,IAAY,EAAE,EAAE;IAC5D,MAAM,UAAU,GAAG,MAAM,6BAA6B,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnE,IAAI,CAAC,UAAU;QAAE,MAAM,IAAA,gBAAQ,EAAC,yBAAyB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IACrE,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/E,IAAI,CAAC,YAAY;QAAE,MAAM,IAAA,gBAAQ,EAAC,4BAA4B,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1E,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AAC5C,CAAC,CAAC;AANW,QAAA,uBAAuB,2BAMlC","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 return Promise.allSettled(secrets.map((secret) => getCredentialBySecretId(secret.sign)));\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.status !== 'fulfilled') continue;\n if (xx.value.credentialId && xx.value.credential) {\n map.set(xx.value.credentialId, xx.value.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/lib/general.js
CHANGED
|
@@ -18,6 +18,9 @@ terminal.server.provideService('VEX/GetPositions', {
|
|
|
18
18
|
if (!res.data)
|
|
19
19
|
return { res };
|
|
20
20
|
const positions = await (0, position_1.polyfillPosition)(res.data);
|
|
21
|
+
positions.forEach((pos) => {
|
|
22
|
+
pos.account_id = credential.credentialId;
|
|
23
|
+
});
|
|
21
24
|
return { res: { code: 0, message: 'OK', data: positions } };
|
|
22
25
|
});
|
|
23
26
|
terminal.server.provideService('VEX/GetOrders', {
|
|
@@ -28,13 +31,16 @@ terminal.server.provideService('VEX/GetOrders', {
|
|
|
28
31
|
product_id: { type: 'string' },
|
|
29
32
|
},
|
|
30
33
|
}, async (msg) => {
|
|
31
|
-
var _a;
|
|
32
34
|
const credential = await (0, credential_1.getCredentialBySecretId)(msg.req.secret_id);
|
|
33
35
|
const res = await (0, exchange_1.getOrders)(terminal, credential.credential, msg.req.product_id);
|
|
34
|
-
(
|
|
36
|
+
if (!res.data)
|
|
37
|
+
return { res };
|
|
38
|
+
const orders = res.data;
|
|
39
|
+
orders.forEach((order) => {
|
|
35
40
|
order.account_id = credential.credentialId;
|
|
36
41
|
});
|
|
37
|
-
|
|
42
|
+
await (0, position_1.polyfillOrders)(orders);
|
|
43
|
+
return { res: { code: 0, message: 'OK', data: orders } };
|
|
38
44
|
});
|
|
39
45
|
// 10. Proxy Orders
|
|
40
46
|
// SubmitOrder
|
|
@@ -47,7 +53,8 @@ terminal.server.provideService('VEX/SubmitOrder', {
|
|
|
47
53
|
},
|
|
48
54
|
}, async (msg) => {
|
|
49
55
|
const credential = await (0, credential_1.getCredentialBySecretId)(msg.req.secret_id);
|
|
50
|
-
const
|
|
56
|
+
const [order] = await (0, position_1.polyfillOrders)([msg.req.order]);
|
|
57
|
+
const res = await (0, exchange_1.submitOrder)(terminal, credential.credential, order);
|
|
51
58
|
return { res };
|
|
52
59
|
});
|
|
53
60
|
// ModifyOrder
|
|
@@ -60,7 +67,8 @@ terminal.server.provideService('VEX/ModifyOrder', {
|
|
|
60
67
|
},
|
|
61
68
|
}, async (msg) => {
|
|
62
69
|
const credential = await (0, credential_1.getCredentialBySecretId)(msg.req.secret_id);
|
|
63
|
-
const
|
|
70
|
+
const [order] = await (0, position_1.polyfillOrders)([msg.req.order]);
|
|
71
|
+
const res = await (0, exchange_1.modifyOrder)(terminal, credential.credential, order);
|
|
64
72
|
return { res };
|
|
65
73
|
});
|
|
66
74
|
// CancelOrder
|
|
@@ -73,7 +81,8 @@ terminal.server.provideService('VEX/CancelOrder', {
|
|
|
73
81
|
},
|
|
74
82
|
}, async (msg) => {
|
|
75
83
|
const credential = await (0, credential_1.getCredentialBySecretId)(msg.req.secret_id);
|
|
76
|
-
const
|
|
84
|
+
const [order] = await (0, position_1.polyfillOrders)([msg.req.order]);
|
|
85
|
+
const res = await (0, exchange_1.cancelOrder)(terminal, credential.credential, order);
|
|
77
86
|
return { res };
|
|
78
87
|
});
|
|
79
88
|
//# sourceMappingURL=general.js.map
|
package/lib/general.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"general.js","sourceRoot":"","sources":["../src/general.ts"],"names":[],"mappings":";;AAEA,+CAAkG;AAClG,+CAA4C;AAC5C,6CAAuD;AACvD,
|
|
1
|
+
{"version":3,"file":"general.js","sourceRoot":"","sources":["../src/general.ts"],"names":[],"mappings":"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","sourcesContent":["import { IPosition } from '@yuants/data-account';\nimport { IOrder } from '@yuants/data-order';\nimport { cancelOrder, getOrders, getPositions, modifyOrder, submitOrder } from '@yuants/exchange';\nimport { Terminal } from '@yuants/protocol';\nimport { getCredentialBySecretId } from './credential';\nimport { polyfillOrders, polyfillPosition } from './position';\n\nconst terminal = Terminal.fromNodeEnv();\n\nterminal.server.provideService<{ secret_id: string; product_id?: string }, IPosition[]>(\n 'VEX/GetPositions',\n {\n type: 'object',\n required: ['secret_id'],\n properties: {\n secret_id: { type: 'string' },\n product_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const res = await getPositions(terminal, credential.credential, msg.req.product_id);\n if (!res.data) return { res };\n const positions = await polyfillPosition(res.data);\n positions.forEach((pos) => {\n pos.account_id = credential.credentialId;\n });\n return { res: { code: 0, message: 'OK', data: positions } };\n },\n);\n\nterminal.server.provideService<{ secret_id: string; product_id?: string }, IOrder[]>(\n 'VEX/GetOrders',\n {\n type: 'object',\n required: ['secret_id'],\n properties: {\n secret_id: { type: 'string' },\n product_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const res = await getOrders(terminal, credential.credential, msg.req.product_id);\n if (!res.data) return { res };\n const orders = res.data;\n orders.forEach((order) => {\n order.account_id = credential.credentialId;\n });\n await polyfillOrders(orders);\n return { res: { code: 0, message: 'OK', data: orders } };\n },\n);\n\n// 10. Proxy Orders\n// SubmitOrder\nterminal.server.provideService<{ order: IOrder; secret_id: string }, { order_id: string }>(\n 'VEX/SubmitOrder',\n {\n type: 'object',\n required: ['order', 'secret_id'],\n properties: {\n order: { type: 'object' },\n secret_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const [order] = await polyfillOrders([msg.req.order]);\n const res = await submitOrder(terminal, credential.credential, order);\n return { res };\n },\n);\n\n// ModifyOrder\nterminal.server.provideService<{ order: IOrder; secret_id: string }, void>(\n 'VEX/ModifyOrder',\n {\n type: 'object',\n required: ['order', 'secret_id'],\n properties: {\n order: { type: 'object' },\n secret_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const [order] = await polyfillOrders([msg.req.order]);\n const res = await modifyOrder(terminal, credential.credential, order);\n return { res };\n },\n);\n\n// CancelOrder\nterminal.server.provideService<{ order: IOrder; secret_id: string }, void>(\n 'VEX/CancelOrder',\n {\n type: 'object',\n required: ['order', 'secret_id'],\n properties: {\n order: { type: 'object' },\n secret_id: { type: 'string' },\n },\n },\n async (msg) => {\n const credential = await getCredentialBySecretId(msg.req.secret_id);\n const [order] = await polyfillOrders([msg.req.order]);\n const res = await cancelOrder(terminal, credential.credential, order);\n return { res };\n },\n);\n"]}
|
package/lib/legacy-services.js
CHANGED
|
@@ -19,6 +19,9 @@ credential_1.validCredentials$
|
|
|
19
19
|
if (!res.data)
|
|
20
20
|
throw (0, utils_1.newError)('FETCH_POSITIONS_FAILED', { credential_id, res });
|
|
21
21
|
const polyfilledPositions = await (0, position_1.polyfillPosition)(res.data);
|
|
22
|
+
polyfilledPositions.forEach((pos) => {
|
|
23
|
+
pos.account_id = credential_id;
|
|
24
|
+
});
|
|
22
25
|
return polyfilledPositions;
|
|
23
26
|
}, {
|
|
24
27
|
auto_refresh_interval: 1000,
|
|
@@ -36,6 +39,7 @@ credential_1.validCredentials$
|
|
|
36
39
|
res.data.forEach((order) => {
|
|
37
40
|
order.account_id = credential_id;
|
|
38
41
|
});
|
|
42
|
+
await (0, position_1.polyfillOrders)(res.data);
|
|
39
43
|
return res.data;
|
|
40
44
|
}, {
|
|
41
45
|
auto_refresh_interval: 10000,
|
|
@@ -53,7 +57,8 @@ credential_1.validCredentials$
|
|
|
53
57
|
account_id: { type: 'string', const: credential_id },
|
|
54
58
|
},
|
|
55
59
|
}, async (msg) => {
|
|
56
|
-
const
|
|
60
|
+
const [order] = await (0, position_1.polyfillOrders)([msg.req]);
|
|
61
|
+
const res = await (0, exchange_1.submitOrder)(terminal, credential, order);
|
|
57
62
|
return { res };
|
|
58
63
|
});
|
|
59
64
|
sub.add(() => {
|
|
@@ -69,7 +74,8 @@ credential_1.validCredentials$
|
|
|
69
74
|
account_id: { type: 'string', const: credential_id },
|
|
70
75
|
},
|
|
71
76
|
}, async (msg) => {
|
|
72
|
-
const
|
|
77
|
+
const [order] = await (0, position_1.polyfillOrders)([msg.req]);
|
|
78
|
+
const res = await (0, exchange_1.modifyOrder)(terminal, credential, order);
|
|
73
79
|
return { res };
|
|
74
80
|
});
|
|
75
81
|
sub.add(() => {
|
|
@@ -85,7 +91,8 @@ credential_1.validCredentials$
|
|
|
85
91
|
account_id: { type: 'string', const: credential_id },
|
|
86
92
|
},
|
|
87
93
|
}, async (msg) => {
|
|
88
|
-
const
|
|
94
|
+
const [order] = await (0, position_1.polyfillOrders)([msg.req]);
|
|
95
|
+
const res = await (0, exchange_1.cancelOrder)(terminal, credential, order);
|
|
89
96
|
return { res };
|
|
90
97
|
});
|
|
91
98
|
sub.add(() => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"legacy-services.js","sourceRoot":"","sources":["../src/legacy-services.ts"],"names":[],"mappings":";;AAAA,uDAAiE;AACjE,mDAAyE;AACzE,+CAAkG;AAClG,+CAA4C;AAC5C,yCAAoD;AACpD,+BAAuC;AACvC,6CAAiD;AACjD,
|
|
1
|
+
{"version":3,"file":"legacy-services.js","sourceRoot":"","sources":["../src/legacy-services.ts"],"names":[],"mappings":"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","sourcesContent":["import { provideAccountInfoService } from '@yuants/data-account';\nimport { IOrder, providePendingOrdersService } from '@yuants/data-order';\nimport { cancelOrder, getOrders, getPositions, modifyOrder, submitOrder } from '@yuants/exchange';\nimport { Terminal } from '@yuants/protocol';\nimport { listWatch, newError } from '@yuants/utils';\nimport { map, Observable } from 'rxjs';\nimport { validCredentials$ } from './credential';\nimport { polyfillOrders, polyfillPosition } from './position';\n\nconst terminal = Terminal.fromNodeEnv();\n\nvalidCredentials$\n .pipe(\n map((x) => Array.from(x.entries())),\n listWatch(\n ([id]) => id,\n ([credential_id, credential]) =>\n new Observable((sub) => {\n console.info(`Setting up VEX services for credential: ${credential_id}`);\n // Setup AccountInfo Service\n {\n const service = provideAccountInfoService(\n terminal,\n credential_id,\n async () => {\n const res = await getPositions(terminal, credential);\n if (!res.data) throw newError('FETCH_POSITIONS_FAILED', { credential_id, res });\n const polyfilledPositions = await polyfillPosition(res.data);\n polyfilledPositions.forEach((pos) => {\n pos.account_id = credential_id;\n });\n return polyfilledPositions;\n },\n {\n auto_refresh_interval: 1000,\n },\n );\n sub.add(() => {\n service.dispose$.next();\n });\n }\n // Setup Pending Orders Service\n {\n const service = providePendingOrdersService(\n terminal,\n credential_id,\n async () => {\n const res = await getOrders(terminal, credential);\n if (!res.data) throw newError('FETCH_ORDERS_FAILED', { credential_id, res });\n\n res.data.forEach((order) => {\n order.account_id = credential_id;\n });\n\n await polyfillOrders(res.data);\n\n return res.data;\n },\n {\n auto_refresh_interval: 10000,\n },\n );\n sub.add(() => {\n service.dispose$.next();\n });\n }\n // Setup SubmitOrder Service\n {\n const service = terminal.server.provideService<IOrder, { order_id: string }>(\n 'SubmitOrder',\n {\n type: 'object',\n required: ['account_id'],\n properties: {\n account_id: { type: 'string', const: credential_id },\n },\n },\n async (msg) => {\n const [order] = await polyfillOrders([msg.req]);\n const res = await submitOrder(terminal, credential, order);\n return { res };\n },\n );\n sub.add(() => {\n service.dispose();\n });\n }\n\n // Setup ModifyOrder Service\n {\n const service = terminal.server.provideService<IOrder, void>(\n 'ModifyOrder',\n {\n type: 'object',\n required: ['account_id'],\n properties: {\n account_id: { type: 'string', const: credential_id },\n },\n },\n async (msg) => {\n const [order] = await polyfillOrders([msg.req]);\n const res = await modifyOrder(terminal, credential, order);\n return { res };\n },\n );\n sub.add(() => {\n service.dispose();\n });\n }\n\n // Setup CancelOrder Service\n {\n const service = terminal.server.provideService<IOrder, void>(\n 'CancelOrder',\n {\n type: 'object',\n required: ['account_id'],\n properties: {\n account_id: { type: 'string', const: credential_id },\n },\n },\n async (msg) => {\n const [order] = await polyfillOrders([msg.req]);\n const res = await cancelOrder(terminal, credential, order);\n return { res };\n },\n );\n sub.add(() => {\n service.dispose();\n });\n }\n }),\n ),\n )\n .subscribe();\n"]}
|
package/lib/position.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import { IPosition } from '@yuants/data-account';
|
|
2
|
+
import { IOrder } from '@yuants/data-order';
|
|
2
3
|
export declare const polyfillPosition: (positions: IPosition[]) => Promise<IPosition[]>;
|
|
4
|
+
export declare const polyfillOrders: (orders: IOrder[]) => Promise<IOrder[]>;
|
|
3
5
|
//# sourceMappingURL=position.d.ts.map
|
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;
|
|
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,CAwDlF,CAAC;AAEF,eAAO,MAAM,cAAc,WAAkB,MAAM,EAAE,KAAG,QAAQ,MAAM,EAAE,CA4BvE,CAAC"}
|
package/lib/position.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.polyfillPosition = void 0;
|
|
3
|
+
exports.polyfillOrders = exports.polyfillPosition = void 0;
|
|
4
4
|
const cache_1 = require("@yuants/cache");
|
|
5
5
|
const data_product_1 = require("@yuants/data-product");
|
|
6
6
|
const protocol_1 = require("@yuants/protocol");
|
|
7
7
|
const sql_1 = require("@yuants/sql");
|
|
8
|
+
const utils_1 = require("@yuants/utils");
|
|
8
9
|
const terminal = protocol_1.Terminal.fromNodeEnv();
|
|
9
10
|
const productCache = (0, data_product_1.createClientProductCache)(terminal);
|
|
10
11
|
const quoteCache = (0, cache_1.createCache)(async (product_id) => {
|
|
@@ -20,14 +21,12 @@ const interestRateIntervalCache = (0, cache_1.createCache)(async (product_id) =>
|
|
|
20
21
|
const prev = new Date(rates[0].created_at).getTime();
|
|
21
22
|
const prevOfPrev = new Date(rates[1].created_at).getTime();
|
|
22
23
|
const interval = prev - prevOfPrev;
|
|
23
|
-
const next = prev + interval;
|
|
24
24
|
return {
|
|
25
25
|
prev,
|
|
26
26
|
prevOfPrev,
|
|
27
27
|
interval,
|
|
28
|
-
next,
|
|
29
28
|
};
|
|
30
|
-
});
|
|
29
|
+
}, { swrAfter: 3600000, expire: 8 * 3600000 });
|
|
31
30
|
const polyfillPosition = async (positions) => {
|
|
32
31
|
// TODO: 使用 batch query SQL 优化 product / quote 查询性能
|
|
33
32
|
for (const pos of positions) {
|
|
@@ -61,7 +60,9 @@ const polyfillPosition = async (positions) => {
|
|
|
61
60
|
}
|
|
62
61
|
else if (quote.interest_rate_next_settled_at === null && interestRateInterval !== undefined) {
|
|
63
62
|
// 估算下一个结算时间
|
|
64
|
-
|
|
63
|
+
// 找到 prev + k * interval > now 的最小 k,则下一个结算时间为 prev + k * interval
|
|
64
|
+
const k = Math.ceil((Date.now() - interestRateInterval.prev) / interestRateInterval.interval);
|
|
65
|
+
pos.settlement_scheduled_at = interestRateInterval.prev + k * interestRateInterval.interval;
|
|
65
66
|
}
|
|
66
67
|
if (pos.direction === 'LONG') {
|
|
67
68
|
if (quote.interest_rate_long !== null) {
|
|
@@ -81,4 +82,35 @@ const polyfillPosition = async (positions) => {
|
|
|
81
82
|
return positions;
|
|
82
83
|
};
|
|
83
84
|
exports.polyfillPosition = polyfillPosition;
|
|
85
|
+
const polyfillOrders = async (orders) => {
|
|
86
|
+
for (const order of orders) {
|
|
87
|
+
const theProduct = await productCache.query(order.product_id);
|
|
88
|
+
if (theProduct) {
|
|
89
|
+
if (order.size !== undefined) {
|
|
90
|
+
const sizeNum = +order.size;
|
|
91
|
+
const sizeStep = theProduct.volume_step * theProduct.value_scale;
|
|
92
|
+
if (!(sizeStep > 0))
|
|
93
|
+
throw (0, utils_1.newError)('INVALID_SIZE_STEP', { product: theProduct, sizeStep });
|
|
94
|
+
// check size is multiple of sizeStep
|
|
95
|
+
if (Math.abs(sizeNum - Math.round(sizeNum / sizeStep) * sizeStep) > 1e-16) {
|
|
96
|
+
throw (0, utils_1.newError)('INVALID_ORDER_SIZE_NOT_MULTIPLE_OF_SIZE_STEP', {
|
|
97
|
+
order,
|
|
98
|
+
sizeStep,
|
|
99
|
+
sizeNum,
|
|
100
|
+
product: theProduct,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
if (sizeNum >= 0) {
|
|
104
|
+
order.order_direction = order.is_close ? 'CLOSE_SHORT' : 'OPEN_LONG';
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
order.order_direction = order.is_close ? 'CLOSE_LONG' : 'OPEN_SHORT';
|
|
108
|
+
}
|
|
109
|
+
order.volume = Math.abs(sizeNum) / theProduct.value_scale;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return orders;
|
|
114
|
+
};
|
|
115
|
+
exports.polyfillOrders = polyfillOrders;
|
|
84
116
|
//# sourceMappingURL=position.js.map
|
package/lib/position.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"position.js","sourceRoot":"","sources":["../src/position.ts"],"names":[],"mappings":";;;AAAA,yCAA4C;
|
|
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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yuants/app-virtual-exchange",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
"@yuants/protocol": "0.53.2",
|
|
12
12
|
"@yuants/utils": "0.14.0",
|
|
13
13
|
"@yuants/data-product": "0.5.0",
|
|
14
|
-
"@yuants/data-account": "0.10.
|
|
15
|
-
"@yuants/data-order": "0.
|
|
14
|
+
"@yuants/data-account": "0.10.1",
|
|
15
|
+
"@yuants/data-order": "0.7.0",
|
|
16
16
|
"@yuants/data-quote": "0.2.43",
|
|
17
|
-
"@yuants/secret": "0.
|
|
17
|
+
"@yuants/secret": "0.4.0",
|
|
18
18
|
"@yuants/sql": "0.9.30",
|
|
19
|
-
"@yuants/exchange": "0.5.
|
|
19
|
+
"@yuants/exchange": "0.5.1",
|
|
20
20
|
"@yuants/cache": "0.3.3",
|
|
21
21
|
"rxjs": "~7.5.6",
|
|
22
22
|
"ajv": "~8.12.0"
|
package/temp/package-deps.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
{
|
|
2
|
-
"apps/virtual-exchange/CHANGELOG.json": "
|
|
3
|
-
"apps/virtual-exchange/CHANGELOG.md": "
|
|
2
|
+
"apps/virtual-exchange/CHANGELOG.json": "df0691c9a901de4e3c328d62f38272a0b3bdae3f",
|
|
3
|
+
"apps/virtual-exchange/CHANGELOG.md": "d6932a16d2c0fb598e42f4d57a79c28b700ccb4e",
|
|
4
4
|
"apps/virtual-exchange/api-extractor.json": "62f4fd324425b9a235f0c117975967aab09ced0c",
|
|
5
5
|
"apps/virtual-exchange/config/jest.config.json": "4bb17bde3ee911163a3edb36a6eb71491d80b1bd",
|
|
6
6
|
"apps/virtual-exchange/config/rig.json": "f6c7b5537dc77a3170ba9f008bae3b6c3ee11956",
|
|
7
7
|
"apps/virtual-exchange/config/typescript.json": "854907e8a821f2050f6533368db160c649c25348",
|
|
8
8
|
"apps/virtual-exchange/etc/app-virtual-exchange.api.md": "6cb40ec1fa2d40a31a7b0dd3f02b8b24a4d7c4de",
|
|
9
|
-
"apps/virtual-exchange/package.json": "
|
|
10
|
-
"apps/virtual-exchange/src/credential.ts": "
|
|
11
|
-
"apps/virtual-exchange/src/general.ts": "
|
|
9
|
+
"apps/virtual-exchange/package.json": "3281548ed4ee4726782a671c76c58d16f115a6e7",
|
|
10
|
+
"apps/virtual-exchange/src/credential.ts": "edf0901b4e1038233bff07b8486d88c777fda3f5",
|
|
11
|
+
"apps/virtual-exchange/src/general.ts": "b3d0cd8c57975b9711008beaa05ad7f6812bd57e",
|
|
12
12
|
"apps/virtual-exchange/src/index.ts": "8d7f19a07e6be09c4d8fd4a49ddb3127d3fbf3de",
|
|
13
|
-
"apps/virtual-exchange/src/legacy-services.ts": "
|
|
14
|
-
"apps/virtual-exchange/src/position.ts": "
|
|
13
|
+
"apps/virtual-exchange/src/legacy-services.ts": "a9f6b6c61b7a0efc909a443e6fde88c2766562bf",
|
|
14
|
+
"apps/virtual-exchange/src/position.ts": "41aba2456041b80074801e110b0becf58bf4cc7e",
|
|
15
15
|
"apps/virtual-exchange/src/product-collector.ts": "15ba0a692d694d20b607eaad0287a864577ef30c",
|
|
16
16
|
"apps/virtual-exchange/tsconfig.json": "22f94ca28b507f8ddcc21b9053158eefd3f726a9",
|
|
17
17
|
"apps/virtual-exchange/.rush/temp/shrinkwrap-deps.json": "2c8344167a574161e8a89138bc1eb686bf6339de",
|
|
18
18
|
"libraries/protocol/temp/package-deps.json": "0bd43721e96039b52d7b59c834dc6df45cf75e3f",
|
|
19
19
|
"libraries/utils/temp/package-deps.json": "6d58e9b325e8d16de8a878c32010f626b12a01da",
|
|
20
20
|
"libraries/data-product/temp/package-deps.json": "a03f08f30800d5fb52c5d019bda4f8e7ec04e344",
|
|
21
|
-
"libraries/data-account/temp/package-deps.json": "
|
|
22
|
-
"libraries/data-order/temp/package-deps.json": "
|
|
21
|
+
"libraries/data-account/temp/package-deps.json": "e6ba0067a1b68f17266564f15987936ab2672eb9",
|
|
22
|
+
"libraries/data-order/temp/package-deps.json": "ccdc9819f254f37a3591bb37876a86c703df33a6",
|
|
23
23
|
"libraries/data-quote/temp/package-deps.json": "34d079eab44d2bf65e07b112ac2099c6e92a015e",
|
|
24
|
-
"libraries/secret/temp/package-deps.json": "
|
|
24
|
+
"libraries/secret/temp/package-deps.json": "28f70cf8095162d1adb59070f30bf21f452537df",
|
|
25
25
|
"libraries/sql/temp/package-deps.json": "4a9a7ec55f04b20459e664e81e76fa74b6c77b39",
|
|
26
|
-
"libraries/exchange/temp/package-deps.json": "
|
|
26
|
+
"libraries/exchange/temp/package-deps.json": "2679f6d65ffd785cbca43cfbe5b6b9a13c7c003d",
|
|
27
27
|
"libraries/cache/temp/package-deps.json": "a4afa15e6462983f9d3735d31dc1ed8a683fb4dc",
|
|
28
28
|
"tools/toolkit/temp/package-deps.json": "23e053490eb8feade23e4d45de4e54883e322711"
|
|
29
29
|
}
|