@yuants/app-virtual-exchange 0.18.2 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,7 @@ const FIELDS = ((x) => Object.keys(x).sort())({
12
12
  interest_rate_prev_settled_at: 0,
13
13
  interest_rate_next_settled_at: 0,
14
14
  interest_rate_long: 0,
15
+ interest_rate_settlement_interval: 0,
15
16
  });
16
17
  const FIELD_COUNT = FIELDS.length;
17
18
  const mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2]));
@@ -1 +1 @@
1
- {"version":3,"file":"v1.js","sourceRoot":"","sources":["../../../src/quote/implementations/v1.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2]));\n\n/**\n * 高效的行情状态管理器\n * 内部使用扁平化数组存储数据,避免内存碎片化和过多的 Map/对象开销\n * 提供高效的读写接口,支持按需更新和查询\n */\nexport const createQuoteStateV1 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const data: (string | number)[] = [];\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 0~20 (10 fields * 2 (value, updated_at))\n const getFieldOffset = (product_id: string, field: string): number | undefined => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n return undefined;\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) return undefined;\n const value = data[offset] as string;\n if (value === undefined) return undefined;\n const updated_at = data[offset + 1] as number;\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n let offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n const baseIndex = mapProductIdToIndex.size * FIELD_COUNT * 2;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n offset = baseIndex + mapFieldNameToOffset[field];\n }\n data[offset] = value;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < data.length; i += 2) {\n const value = data[i] as string;\n if (value === undefined) continue;\n const updated_at = data[i + 1] as number;\n\n const productIndex = Math.floor(i / (FIELD_COUNT * 2));\n const product_id = products[productIndex];\n\n const fieldIndex = (i % (FIELD_COUNT * 2)) / 2;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id][field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n result[product_id][field] = '';\n } else {\n result[product_id][field] = (data[offset] as string) || '';\n }\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
1
+ {"version":3,"file":"v1.js","sourceRoot":"","sources":["../../../src/quote/implementations/v1.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n interest_rate_settlement_interval: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2]));\n\n/**\n * 高效的行情状态管理器\n * 内部使用扁平化数组存储数据,避免内存碎片化和过多的 Map/对象开销\n * 提供高效的读写接口,支持按需更新和查询\n */\nexport const createQuoteStateV1 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const data: (string | number)[] = [];\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 0~20 (10 fields * 2 (value, updated_at))\n const getFieldOffset = (product_id: string, field: string): number | undefined => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n return undefined;\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) return undefined;\n const value = data[offset] as string;\n if (value === undefined) return undefined;\n const updated_at = data[offset + 1] as number;\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n let offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n const baseIndex = mapProductIdToIndex.size * FIELD_COUNT * 2;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n offset = baseIndex + mapFieldNameToOffset[field];\n }\n data[offset] = value;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < data.length; i += 2) {\n const value = data[i] as string;\n if (value === undefined) continue;\n const updated_at = data[i + 1] as number;\n\n const productIndex = Math.floor(i / (FIELD_COUNT * 2));\n const product_id = products[productIndex];\n\n const fieldIndex = (i % (FIELD_COUNT * 2)) / 2;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id][field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n result[product_id][field] = '';\n } else {\n result[product_id][field] = (data[offset] as string) || '';\n }\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
@@ -12,6 +12,7 @@ const FIELDS = ((x) => Object.keys(x).sort())({
12
12
  interest_rate_prev_settled_at: 0,
13
13
  interest_rate_next_settled_at: 0,
14
14
  interest_rate_long: 0,
15
+ interest_rate_settlement_interval: 0,
15
16
  });
16
17
  const FIELD_COUNT = FIELDS.length;
17
18
  const mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index])); // 改为单倍偏移
@@ -1 +1 @@
1
- {"version":3,"file":"v2.js","sourceRoot":"","sources":["../../../src/quote/implementations/v2.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index])); // 改为单倍偏移\n\n/**\n * 高效的行情状态管理器 v2\n * 将 value 和 timestamp 拆分为两个数组,使数组元素类型一致,提高内存访问效率\n */\nexport const createQuoteStateV2 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const values: string[] = []; // 存储字段值\n const timestamps: number[] = []; // 存储更新时间戳\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 每个产品占用 FIELD_COUNT 个位置\n const getFieldOffset = (product_id: string, field: string): number => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n baseIndex = mapProductIdToIndex.size * FIELD_COUNT;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n const value = values[offset];\n if (value === undefined) return undefined;\n const updated_at = timestamps[offset];\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n const offset = getFieldOffset(product_id, field);\n values[offset] = value;\n timestamps[offset] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < values.length; i++) {\n const value = values[i];\n if (value === undefined) continue;\n const updated_at = timestamps[i];\n\n const productIndex = Math.floor(i / FIELD_COUNT);\n const product_id = products[productIndex];\n\n const fieldIndex = i % FIELD_COUNT;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id]![field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n result[product_id][field] = tuple ? tuple[0] : '';\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
1
+ {"version":3,"file":"v2.js","sourceRoot":"","sources":["../../../src/quote/implementations/v2.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n interest_rate_settlement_interval: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index])); // 改为单倍偏移\n\n/**\n * 高效的行情状态管理器 v2\n * 将 value 和 timestamp 拆分为两个数组,使数组元素类型一致,提高内存访问效率\n */\nexport const createQuoteStateV2 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const values: string[] = []; // 存储字段值\n const timestamps: number[] = []; // 存储更新时间戳\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 每个产品占用 FIELD_COUNT 个位置\n const getFieldOffset = (product_id: string, field: string): number => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n baseIndex = mapProductIdToIndex.size * FIELD_COUNT;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n const value = values[offset];\n if (value === undefined) return undefined;\n const updated_at = timestamps[offset];\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n const offset = getFieldOffset(product_id, field);\n values[offset] = value;\n timestamps[offset] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < values.length; i++) {\n const value = values[i];\n if (value === undefined) continue;\n const updated_at = timestamps[i];\n\n const productIndex = Math.floor(i / FIELD_COUNT);\n const product_id = products[productIndex];\n\n const fieldIndex = i % FIELD_COUNT;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id]![field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n result[product_id][field] = tuple ? tuple[0] : '';\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
@@ -12,6 +12,7 @@ const FIELDS = ((x) => Object.keys(x).sort())({
12
12
  interest_rate_prev_settled_at: 0,
13
13
  interest_rate_next_settled_at: 0,
14
14
  interest_rate_long: 0,
15
+ interest_rate_settlement_interval: 0,
15
16
  });
16
17
  const FIELD_COUNT = FIELDS.length;
17
18
  const mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2])); // 每个字段占2个位置: [池索引, 时间戳]
@@ -1 +1 @@
1
- {"version":3,"file":"v3.js","sourceRoot":"","sources":["../../../src/quote/implementations/v3.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2])); // 每个字段占2个位置: [池索引, 时间戳]\n\n/**\n * 使用引用计数字符串池的行情状态管理器 v3\n * 保持 v1 的内存局部性,同时通过字符串池减少内存开销\n * 使用 Float64Array 连续存储 [池索引, 时间戳] 对\n */\nexport const createQuoteStateV3 = (): IQuoteState => {\n // 核心数据存储:连续存储 [池索引, 时间戳, 池索引, 时间戳...]\n let data: Float64Array = new Float64Array(1024 * FIELD_COUNT * 2); // 初始容量:1024产品\n let dataLength = 0; // 当前已分配的数据槽数量(以字段为单位)\n\n // 字符串池\n let stringPool: (string | undefined)[] = []; // 索引 -> 字符串(undefined 表示空闲)\n let refCounts: number[] = []; // 索引 -> 引用计数\n let stringToIndex = new Map<string, number>(); // 字符串 -> 索引\n let freeIndices: number[] = []; // 空闲索引列表\n\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n\n // 确保 data 有足够容量\n const ensureDataCapacity = (requiredFields: number) => {\n if (requiredFields * 2 <= data.length) return;\n\n // 倍增扩容\n const newSize = Math.max(data.length * 2, requiredFields * 2 + 1024);\n const newData = new Float64Array(newSize);\n newData.set(data);\n data = newData;\n };\n\n // 获取字段偏移量(以 Float64 元素为单位)\n const getFieldOffset = (product_id: string, field: string): number => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n baseIndex = mapProductIdToIndex.size;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n\n // 确保有足够空间存储该产品的所有字段\n const requiredFields = (baseIndex + 1) * FIELD_COUNT;\n ensureDataCapacity(requiredFields);\n\n // 初始化该产品的所有字段为 [-1, 0](空值)\n const startIdx = baseIndex * FIELD_COUNT * 2;\n for (let i = 0; i < FIELD_COUNT * 2; i += 2) {\n data[startIdx + i] = -1; // 池索引:-1 表示空值\n data[startIdx + i + 1] = 0; // 时间戳:0\n }\n dataLength = Math.max(dataLength, requiredFields);\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex * FIELD_COUNT * 2 + fieldOffset;\n };\n\n // 获取或分配字符串索引(增加引用计数)\n const acquireStringIndex = (value: string): number => {\n let index = stringToIndex.get(value);\n if (index !== undefined) {\n // 字符串已存在,增加引用计数\n refCounts[index]++;\n return index;\n }\n\n // 需要新索引\n if (freeIndices.length > 0) {\n // 重用空闲索引\n index = freeIndices.pop()!;\n stringPool[index] = value;\n refCounts[index] = 1;\n } else {\n // 分配新索引\n index = stringPool.length;\n stringPool.push(value);\n refCounts.push(1);\n }\n\n stringToIndex.set(value, index);\n return index;\n };\n\n // 释放字符串索引(减少引用计数)\n const releaseStringIndex = (index: number): void => {\n if (index === -1) return; // 空值索引\n\n refCounts[index]--;\n if (refCounts[index] === 0) {\n // 引用计数归零,可以回收\n const value = stringPool[index]!;\n stringToIndex.delete(value);\n // 清空池中的引用,允许 GC\n stringPool[index] = undefined;\n refCounts[index] = 0;\n freeIndices.push(index);\n }\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n const poolIndex = data[offset];\n if (poolIndex === -1) return undefined;\n\n const timestamp = data[offset + 1];\n const value = stringPool[poolIndex]!;\n return [value, timestamp];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n const offset = getFieldOffset(product_id, field);\n const oldPoolIndex = data[offset];\n\n // 释放旧字符串的引用\n releaseStringIndex(oldPoolIndex);\n\n // 获取新字符串索引\n const newPoolIndex = acquireStringIndex(value);\n\n // 更新存储\n data[offset] = newPoolIndex;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n\n // 初始化所有产品的结构\n for (const product_id of products) {\n result[product_id] = {};\n }\n\n // 遍历所有字段(dataLength 是以字段为单位)\n for (let fieldIdx = 0; fieldIdx < dataLength; fieldIdx++) {\n const poolIndex = data[fieldIdx * 2];\n if (poolIndex === -1) continue; // 空值\n\n const timestamp = data[fieldIdx * 2 + 1];\n const productIndex = Math.floor(fieldIdx / FIELD_COUNT);\n const product_id = products[productIndex];\n\n const fieldIndex = fieldIdx % FIELD_COUNT;\n const field_name = FIELDS[fieldIndex];\n\n const value = stringPool[poolIndex]!;\n result[product_id]![field_name] = [value, timestamp];\n }\n\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n\n for (const product_id of product_ids) {\n const productIndex = mapProductIdToIndex.get(product_id);\n if (productIndex === undefined) {\n // 产品不存在,创建空对象\n result[product_id] = {};\n continue;\n }\n\n const productResult: Partial<Record<IQuoteKey, [string, number]>> = {};\n const baseOffset = productIndex * FIELD_COUNT * 2;\n\n for (const field of fields) {\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) continue;\n\n const offset = baseOffset + fieldOffset;\n const poolIndex = data[offset];\n if (poolIndex === -1) continue; // 空值\n\n const timestamp = data[offset + 1];\n if (timestamp < updated_at) continue; // 时间戳太旧\n\n const value = stringPool[poolIndex]!;\n productResult[field] = [value, timestamp];\n }\n\n result[product_id] = productResult;\n }\n\n return result;\n };\n\n // 导出内部统计信息用于调试\n const getStats = () => ({\n productCount: products.length,\n fieldCount: dataLength,\n stringPoolSize: stringPool.length,\n activeStrings: stringPool.length - freeIndices.length,\n freeIndices: freeIndices.length,\n memoryEstimate: {\n data: data.length * 8, // Float64Array 每个元素8字节\n pool: stringPool.reduce((sum, str) => sum + (str ? str.length * 2 : 0), 0),\n structures: products.length * 50 + stringPool.length * 16, // 粗略估计\n },\n });\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n result[product_id][field] = tuple ? tuple[0] : '';\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
1
+ {"version":3,"file":"v3.js","sourceRoot":"","sources":["../../../src/quote/implementations/v3.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n interest_rate_settlement_interval: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2])); // 每个字段占2个位置: [池索引, 时间戳]\n\n/**\n * 使用引用计数字符串池的行情状态管理器 v3\n * 保持 v1 的内存局部性,同时通过字符串池减少内存开销\n * 使用 Float64Array 连续存储 [池索引, 时间戳] 对\n */\nexport const createQuoteStateV3 = (): IQuoteState => {\n // 核心数据存储:连续存储 [池索引, 时间戳, 池索引, 时间戳...]\n let data: Float64Array = new Float64Array(1024 * FIELD_COUNT * 2); // 初始容量:1024产品\n let dataLength = 0; // 当前已分配的数据槽数量(以字段为单位)\n\n // 字符串池\n let stringPool: (string | undefined)[] = []; // 索引 -> 字符串(undefined 表示空闲)\n let refCounts: number[] = []; // 索引 -> 引用计数\n let stringToIndex = new Map<string, number>(); // 字符串 -> 索引\n let freeIndices: number[] = []; // 空闲索引列表\n\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n\n // 确保 data 有足够容量\n const ensureDataCapacity = (requiredFields: number) => {\n if (requiredFields * 2 <= data.length) return;\n\n // 倍增扩容\n const newSize = Math.max(data.length * 2, requiredFields * 2 + 1024);\n const newData = new Float64Array(newSize);\n newData.set(data);\n data = newData;\n };\n\n // 获取字段偏移量(以 Float64 元素为单位)\n const getFieldOffset = (product_id: string, field: string): number => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n baseIndex = mapProductIdToIndex.size;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n\n // 确保有足够空间存储该产品的所有字段\n const requiredFields = (baseIndex + 1) * FIELD_COUNT;\n ensureDataCapacity(requiredFields);\n\n // 初始化该产品的所有字段为 [-1, 0](空值)\n const startIdx = baseIndex * FIELD_COUNT * 2;\n for (let i = 0; i < FIELD_COUNT * 2; i += 2) {\n data[startIdx + i] = -1; // 池索引:-1 表示空值\n data[startIdx + i + 1] = 0; // 时间戳:0\n }\n dataLength = Math.max(dataLength, requiredFields);\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex * FIELD_COUNT * 2 + fieldOffset;\n };\n\n // 获取或分配字符串索引(增加引用计数)\n const acquireStringIndex = (value: string): number => {\n let index = stringToIndex.get(value);\n if (index !== undefined) {\n // 字符串已存在,增加引用计数\n refCounts[index]++;\n return index;\n }\n\n // 需要新索引\n if (freeIndices.length > 0) {\n // 重用空闲索引\n index = freeIndices.pop()!;\n stringPool[index] = value;\n refCounts[index] = 1;\n } else {\n // 分配新索引\n index = stringPool.length;\n stringPool.push(value);\n refCounts.push(1);\n }\n\n stringToIndex.set(value, index);\n return index;\n };\n\n // 释放字符串索引(减少引用计数)\n const releaseStringIndex = (index: number): void => {\n if (index === -1) return; // 空值索引\n\n refCounts[index]--;\n if (refCounts[index] === 0) {\n // 引用计数归零,可以回收\n const value = stringPool[index]!;\n stringToIndex.delete(value);\n // 清空池中的引用,允许 GC\n stringPool[index] = undefined;\n refCounts[index] = 0;\n freeIndices.push(index);\n }\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n const poolIndex = data[offset];\n if (poolIndex === -1) return undefined;\n\n const timestamp = data[offset + 1];\n const value = stringPool[poolIndex]!;\n return [value, timestamp];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n const offset = getFieldOffset(product_id, field);\n const oldPoolIndex = data[offset];\n\n // 释放旧字符串的引用\n releaseStringIndex(oldPoolIndex);\n\n // 获取新字符串索引\n const newPoolIndex = acquireStringIndex(value);\n\n // 更新存储\n data[offset] = newPoolIndex;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n\n // 初始化所有产品的结构\n for (const product_id of products) {\n result[product_id] = {};\n }\n\n // 遍历所有字段(dataLength 是以字段为单位)\n for (let fieldIdx = 0; fieldIdx < dataLength; fieldIdx++) {\n const poolIndex = data[fieldIdx * 2];\n if (poolIndex === -1) continue; // 空值\n\n const timestamp = data[fieldIdx * 2 + 1];\n const productIndex = Math.floor(fieldIdx / FIELD_COUNT);\n const product_id = products[productIndex];\n\n const fieldIndex = fieldIdx % FIELD_COUNT;\n const field_name = FIELDS[fieldIndex];\n\n const value = stringPool[poolIndex]!;\n result[product_id]![field_name] = [value, timestamp];\n }\n\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n\n for (const product_id of product_ids) {\n const productIndex = mapProductIdToIndex.get(product_id);\n if (productIndex === undefined) {\n // 产品不存在,创建空对象\n result[product_id] = {};\n continue;\n }\n\n const productResult: Partial<Record<IQuoteKey, [string, number]>> = {};\n const baseOffset = productIndex * FIELD_COUNT * 2;\n\n for (const field of fields) {\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) continue;\n\n const offset = baseOffset + fieldOffset;\n const poolIndex = data[offset];\n if (poolIndex === -1) continue; // 空值\n\n const timestamp = data[offset + 1];\n if (timestamp < updated_at) continue; // 时间戳太旧\n\n const value = stringPool[poolIndex]!;\n productResult[field] = [value, timestamp];\n }\n\n result[product_id] = productResult;\n }\n\n return result;\n };\n\n // 导出内部统计信息用于调试\n const getStats = () => ({\n productCount: products.length,\n fieldCount: dataLength,\n stringPoolSize: stringPool.length,\n activeStrings: stringPool.length - freeIndices.length,\n freeIndices: freeIndices.length,\n memoryEstimate: {\n data: data.length * 8, // Float64Array 每个元素8字节\n pool: stringPool.reduce((sum, str) => sum + (str ? str.length * 2 : 0), 0),\n structures: products.length * 50 + stringPool.length * 16, // 粗略估计\n },\n });\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n result[product_id][field] = tuple ? tuple[0] : '';\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../../src/quote/implementations/v1.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAAsB,MAAM,UAAU,CAAC;AAoBtE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAO,WAiHrC,CAAC"}
1
+ {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../../src/quote/implementations/v1.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAAsB,MAAM,UAAU,CAAC;AAqBtE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAO,WAiHrC,CAAC"}
@@ -15,6 +15,7 @@ const FIELDS = ((x) => Object.keys(x).sort())({
15
15
  interest_rate_prev_settled_at: 0,
16
16
  interest_rate_next_settled_at: 0,
17
17
  interest_rate_long: 0,
18
+ interest_rate_settlement_interval: 0,
18
19
  });
19
20
  const FIELD_COUNT = FIELDS.length;
20
21
  const mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2]));
@@ -1 +1 @@
1
- {"version":3,"file":"v1.js","sourceRoot":"","sources":["../../../src/quote/implementations/v1.ts"],"names":[],"mappings":";;;AAAA,yCAAyC;AAGzC,wBAAwB;AACxB,MAAM,MAAM,GAAG,CAAC,CAAC,CAAiC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,EAAiB,CAAC,CAAC;IAC3F,mDAAmD;IACnD,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,CAAC;IACZ,UAAU,EAAE,CAAC;IACb,UAAU,EAAE,CAAC;IACb,SAAS,EAAE,CAAC;IACZ,mBAAmB,EAAE,CAAC;IACtB,aAAa,EAAE,CAAC;IAChB,6BAA6B,EAAE,CAAC;IAChC,6BAA6B,EAAE,CAAC;IAChC,kBAAkB,EAAE,CAAC;CACtB,CAAC,CAAC;AAEH,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,CAAC;AAClC,MAAM,oBAAoB,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAElG;;;;GAIG;AACI,MAAM,kBAAkB,GAAG,GAAgB,EAAE;IAClD,+BAA+B;IAC/B,MAAM,IAAI,GAAwB,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtD,2CAA2C;IAC3C,MAAM,cAAc,GAAG,CAAC,UAAkB,EAAE,KAAa,EAAsB,EAAE;QAC/E,IAAI,SAAS,GAAG,mBAAmB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACpD,IAAI,SAAS,KAAK,SAAS,EAAE;YAC3B,OAAO,SAAS,CAAC;SAClB;QACD,MAAM,WAAW,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,WAAW,KAAK,SAAS;YAAE,MAAM,IAAA,gBAAQ,EAAC,oBAAoB,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAC;QACzG,OAAO,SAAS,GAAG,WAAW,CAAC;IACjC,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,UAAkB,EAAE,KAAgB,EAAgC,EAAE;QAC3F,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QACjD,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAW,CAAC;QACrC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAW,CAAC;QAC9C,OAAO,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,CAAC,UAAkB,EAAE,KAAgB,EAAE,KAAa,EAAE,UAAkB,EAAE,EAAE;QAChG,IAAI,MAAM,GAAG,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,MAAM,KAAK,SAAS,EAAE;YACxB,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,GAAG,WAAW,GAAG,CAAC,CAAC;YAC7D,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC1B,mBAAmB,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;YAC/C,MAAM,GAAG,SAAS,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;SAClD;QACD,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC;IAChC,CAAC,CAAC;IAEF,MAAM,MAAM,GAAG,CAAC,MAA0B,EAAE,EAAE;QAC5C,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE;YAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YAClC,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE;gBAC/B,MAAM,KAAK,GAAG,UAAuB,CAAC;gBACtC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,GAAG,MAAM,CAAC,KAAK,CAAE,CAAC;gBAC3C,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAClD,IAAI,QAAQ,KAAK,SAAS,IAAI,UAAU,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE;oBACvD,aAAa,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC;iBACrD;aACF;SACF;IACH,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,GAAuB,EAAE;QAC5C,MAAM,MAAM,GAAuB,EAAE,CAAC;QACtC,KAAK,MAAM,UAAU,IAAI,QAAQ,EAAE;YACjC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;SACzB;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE;YACvC,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAW,CAAC;YAChC,IAAI,KAAK,KAAK,SAAS;gBAAE,SAAS;YAClC,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAW,CAAC;YAEzC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;YACvD,MAAM,UAAU,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;YAE1C,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;YAEtC,MAAM,CAAC,UAAU,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;SACtD;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF;;;;;;OAMG;IACH,MAAM,MAAM,GAAG,CAAC,WAAqB,EAAE,MAAmB,EAAE,UAAkB,EAAsB,EAAE;QACpG,MAAM,MAAM,GAAuB,EAAE,CAAC;QACtC,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;YACpC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC;YACxB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;gBAC1B,MAAM,KAAK,GAAG,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBAC/C,IAAI,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,EAAE;oBACnC,MAAM,CAAC,UAAU,CAAE,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;iBACpC;aACF;SACF;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,MAAM,YAAY,GAAG,CACnB,WAAqB,EACrB,MAAW,EACwB,EAAE;QACrC,MAAM,MAAM,GAAsC,EAAE,CAAC;QACrD,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE;YACpC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAuB,CAAC;YAC7C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE;gBAC1B,MAAM,MAAM,GAAG,cAAc,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;gBACjD,IAAI,MAAM,KAAK,SAAS,EAAE;oBACxB,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;iBAChC;qBAAM;oBACL,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,GAAI,IAAI,CAAC,MAAM,CAAY,IAAI,EAAE,CAAC;iBAC5D;aACF;SACF;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;AACvE,CAAC,CAAC;AAjHW,QAAA,kBAAkB,sBAiH7B","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2]));\n\n/**\n * 高效的行情状态管理器\n * 内部使用扁平化数组存储数据,避免内存碎片化和过多的 Map/对象开销\n * 提供高效的读写接口,支持按需更新和查询\n */\nexport const createQuoteStateV1 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const data: (string | number)[] = [];\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 0~20 (10 fields * 2 (value, updated_at))\n const getFieldOffset = (product_id: string, field: string): number | undefined => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n return undefined;\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) return undefined;\n const value = data[offset] as string;\n if (value === undefined) return undefined;\n const updated_at = data[offset + 1] as number;\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n let offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n const baseIndex = mapProductIdToIndex.size * FIELD_COUNT * 2;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n offset = baseIndex + mapFieldNameToOffset[field];\n }\n data[offset] = value;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < data.length; i += 2) {\n const value = data[i] as string;\n if (value === undefined) continue;\n const updated_at = data[i + 1] as number;\n\n const productIndex = Math.floor(i / (FIELD_COUNT * 2));\n const product_id = products[productIndex];\n\n const fieldIndex = (i % (FIELD_COUNT * 2)) / 2;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id][field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n result[product_id][field] = '';\n } else {\n result[product_id][field] = (data[offset] as string) || '';\n }\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
1
+ {"version":3,"file":"v1.js","sourceRoot":"","sources":["../../../src/quote/implementations/v1.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n interest_rate_settlement_interval: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2]));\n\n/**\n * 高效的行情状态管理器\n * 内部使用扁平化数组存储数据,避免内存碎片化和过多的 Map/对象开销\n * 提供高效的读写接口,支持按需更新和查询\n */\nexport const createQuoteStateV1 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const data: (string | number)[] = [];\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 0~20 (10 fields * 2 (value, updated_at))\n const getFieldOffset = (product_id: string, field: string): number | undefined => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n return undefined;\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) return undefined;\n const value = data[offset] as string;\n if (value === undefined) return undefined;\n const updated_at = data[offset + 1] as number;\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n let offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n const baseIndex = mapProductIdToIndex.size * FIELD_COUNT * 2;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n offset = baseIndex + mapFieldNameToOffset[field];\n }\n data[offset] = value;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < data.length; i += 2) {\n const value = data[i] as string;\n if (value === undefined) continue;\n const updated_at = data[i + 1] as number;\n\n const productIndex = Math.floor(i / (FIELD_COUNT * 2));\n const product_id = products[productIndex];\n\n const fieldIndex = (i % (FIELD_COUNT * 2)) / 2;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id][field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const offset = getFieldOffset(product_id, field);\n if (offset === undefined) {\n result[product_id][field] = '';\n } else {\n result[product_id][field] = (data[offset] as string) || '';\n }\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../../src/quote/implementations/v2.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAAsB,MAAM,UAAU,CAAC;AAoBtE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,WA0GrC,CAAC"}
1
+ {"version":3,"file":"v2.d.ts","sourceRoot":"","sources":["../../../src/quote/implementations/v2.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAAsB,MAAM,UAAU,CAAC;AAqBtE;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAO,WA0GrC,CAAC"}
@@ -15,6 +15,7 @@ const FIELDS = ((x) => Object.keys(x).sort())({
15
15
  interest_rate_prev_settled_at: 0,
16
16
  interest_rate_next_settled_at: 0,
17
17
  interest_rate_long: 0,
18
+ interest_rate_settlement_interval: 0,
18
19
  });
19
20
  const FIELD_COUNT = FIELDS.length;
20
21
  const mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index])); // 改为单倍偏移
@@ -1 +1 @@
1
- {"version":3,"file":"v2.js","sourceRoot":"","sources":["../../../src/quote/implementations/v2.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index])); // 改为单倍偏移\n\n/**\n * 高效的行情状态管理器 v2\n * 将 value 和 timestamp 拆分为两个数组,使数组元素类型一致,提高内存访问效率\n */\nexport const createQuoteStateV2 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const values: string[] = []; // 存储字段值\n const timestamps: number[] = []; // 存储更新时间戳\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 每个产品占用 FIELD_COUNT 个位置\n const getFieldOffset = (product_id: string, field: string): number => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n baseIndex = mapProductIdToIndex.size * FIELD_COUNT;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n const value = values[offset];\n if (value === undefined) return undefined;\n const updated_at = timestamps[offset];\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n const offset = getFieldOffset(product_id, field);\n values[offset] = value;\n timestamps[offset] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < values.length; i++) {\n const value = values[i];\n if (value === undefined) continue;\n const updated_at = timestamps[i];\n\n const productIndex = Math.floor(i / FIELD_COUNT);\n const product_id = products[productIndex];\n\n const fieldIndex = i % FIELD_COUNT;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id]![field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n result[product_id][field] = tuple ? tuple[0] : '';\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
1
+ {"version":3,"file":"v2.js","sourceRoot":"","sources":["../../../src/quote/implementations/v2.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n interest_rate_settlement_interval: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index])); // 改为单倍偏移\n\n/**\n * 高效的行情状态管理器 v2\n * 将 value 和 timestamp 拆分为两个数组,使数组元素类型一致,提高内存访问效率\n */\nexport const createQuoteStateV2 = (): IQuoteState => {\n // 内部数据结构的设计需要考虑高效的读写性能,防止内存碎片化\n const values: string[] = []; // 存储字段值\n const timestamps: number[] = []; // 存储更新时间戳\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n // 每个产品占用 FIELD_COUNT 个位置\n const getFieldOffset = (product_id: string, field: string): number => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n baseIndex = mapProductIdToIndex.size * FIELD_COUNT;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex + fieldOffset;\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n const value = values[offset];\n if (value === undefined) return undefined;\n const updated_at = timestamps[offset];\n return [value, updated_at];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n const offset = getFieldOffset(product_id, field);\n values[offset] = value;\n timestamps[offset] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of products) {\n result[product_id] = {};\n }\n for (let i = 0; i < values.length; i++) {\n const value = values[i];\n if (value === undefined) continue;\n const updated_at = timestamps[i];\n\n const productIndex = Math.floor(i / FIELD_COUNT);\n const product_id = products[productIndex];\n\n const fieldIndex = i % FIELD_COUNT;\n const field_name = FIELDS[fieldIndex];\n\n result[product_id]![field_name] = [value, updated_at];\n }\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n for (const product_id of product_ids) {\n result[product_id] = {};\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n if (tuple && tuple[1] >= updated_at) {\n result[product_id]![field] = tuple;\n }\n }\n }\n return result;\n };\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n result[product_id][field] = tuple ? tuple[0] : '';\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"v3.d.ts","sourceRoot":"","sources":["../../../src/quote/implementations/v3.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAAsB,MAAM,UAAU,CAAC;AAoBtE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAO,WAsOrC,CAAC"}
1
+ {"version":3,"file":"v3.d.ts","sourceRoot":"","sources":["../../../src/quote/implementations/v3.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,WAAW,EAAsB,MAAM,UAAU,CAAC;AAqBtE;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,QAAO,WAsOrC,CAAC"}
@@ -15,6 +15,7 @@ const FIELDS = ((x) => Object.keys(x).sort())({
15
15
  interest_rate_prev_settled_at: 0,
16
16
  interest_rate_next_settled_at: 0,
17
17
  interest_rate_long: 0,
18
+ interest_rate_settlement_interval: 0,
18
19
  });
19
20
  const FIELD_COUNT = FIELDS.length;
20
21
  const mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2])); // 每个字段占2个位置: [池索引, 时间戳]
@@ -1 +1 @@
1
- {"version":3,"file":"v3.js","sourceRoot":"","sources":["../../../src/quote/implementations/v3.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2])); // 每个字段占2个位置: [池索引, 时间戳]\n\n/**\n * 使用引用计数字符串池的行情状态管理器 v3\n * 保持 v1 的内存局部性,同时通过字符串池减少内存开销\n * 使用 Float64Array 连续存储 [池索引, 时间戳] 对\n */\nexport const createQuoteStateV3 = (): IQuoteState => {\n // 核心数据存储:连续存储 [池索引, 时间戳, 池索引, 时间戳...]\n let data: Float64Array = new Float64Array(1024 * FIELD_COUNT * 2); // 初始容量:1024产品\n let dataLength = 0; // 当前已分配的数据槽数量(以字段为单位)\n\n // 字符串池\n let stringPool: (string | undefined)[] = []; // 索引 -> 字符串(undefined 表示空闲)\n let refCounts: number[] = []; // 索引 -> 引用计数\n let stringToIndex = new Map<string, number>(); // 字符串 -> 索引\n let freeIndices: number[] = []; // 空闲索引列表\n\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n\n // 确保 data 有足够容量\n const ensureDataCapacity = (requiredFields: number) => {\n if (requiredFields * 2 <= data.length) return;\n\n // 倍增扩容\n const newSize = Math.max(data.length * 2, requiredFields * 2 + 1024);\n const newData = new Float64Array(newSize);\n newData.set(data);\n data = newData;\n };\n\n // 获取字段偏移量(以 Float64 元素为单位)\n const getFieldOffset = (product_id: string, field: string): number => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n baseIndex = mapProductIdToIndex.size;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n\n // 确保有足够空间存储该产品的所有字段\n const requiredFields = (baseIndex + 1) * FIELD_COUNT;\n ensureDataCapacity(requiredFields);\n\n // 初始化该产品的所有字段为 [-1, 0](空值)\n const startIdx = baseIndex * FIELD_COUNT * 2;\n for (let i = 0; i < FIELD_COUNT * 2; i += 2) {\n data[startIdx + i] = -1; // 池索引:-1 表示空值\n data[startIdx + i + 1] = 0; // 时间戳:0\n }\n dataLength = Math.max(dataLength, requiredFields);\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex * FIELD_COUNT * 2 + fieldOffset;\n };\n\n // 获取或分配字符串索引(增加引用计数)\n const acquireStringIndex = (value: string): number => {\n let index = stringToIndex.get(value);\n if (index !== undefined) {\n // 字符串已存在,增加引用计数\n refCounts[index]++;\n return index;\n }\n\n // 需要新索引\n if (freeIndices.length > 0) {\n // 重用空闲索引\n index = freeIndices.pop()!;\n stringPool[index] = value;\n refCounts[index] = 1;\n } else {\n // 分配新索引\n index = stringPool.length;\n stringPool.push(value);\n refCounts.push(1);\n }\n\n stringToIndex.set(value, index);\n return index;\n };\n\n // 释放字符串索引(减少引用计数)\n const releaseStringIndex = (index: number): void => {\n if (index === -1) return; // 空值索引\n\n refCounts[index]--;\n if (refCounts[index] === 0) {\n // 引用计数归零,可以回收\n const value = stringPool[index]!;\n stringToIndex.delete(value);\n // 清空池中的引用,允许 GC\n stringPool[index] = undefined;\n refCounts[index] = 0;\n freeIndices.push(index);\n }\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n const poolIndex = data[offset];\n if (poolIndex === -1) return undefined;\n\n const timestamp = data[offset + 1];\n const value = stringPool[poolIndex]!;\n return [value, timestamp];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n const offset = getFieldOffset(product_id, field);\n const oldPoolIndex = data[offset];\n\n // 释放旧字符串的引用\n releaseStringIndex(oldPoolIndex);\n\n // 获取新字符串索引\n const newPoolIndex = acquireStringIndex(value);\n\n // 更新存储\n data[offset] = newPoolIndex;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n\n // 初始化所有产品的结构\n for (const product_id of products) {\n result[product_id] = {};\n }\n\n // 遍历所有字段(dataLength 是以字段为单位)\n for (let fieldIdx = 0; fieldIdx < dataLength; fieldIdx++) {\n const poolIndex = data[fieldIdx * 2];\n if (poolIndex === -1) continue; // 空值\n\n const timestamp = data[fieldIdx * 2 + 1];\n const productIndex = Math.floor(fieldIdx / FIELD_COUNT);\n const product_id = products[productIndex];\n\n const fieldIndex = fieldIdx % FIELD_COUNT;\n const field_name = FIELDS[fieldIndex];\n\n const value = stringPool[poolIndex]!;\n result[product_id]![field_name] = [value, timestamp];\n }\n\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n\n for (const product_id of product_ids) {\n const productIndex = mapProductIdToIndex.get(product_id);\n if (productIndex === undefined) {\n // 产品不存在,创建空对象\n result[product_id] = {};\n continue;\n }\n\n const productResult: Partial<Record<IQuoteKey, [string, number]>> = {};\n const baseOffset = productIndex * FIELD_COUNT * 2;\n\n for (const field of fields) {\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) continue;\n\n const offset = baseOffset + fieldOffset;\n const poolIndex = data[offset];\n if (poolIndex === -1) continue; // 空值\n\n const timestamp = data[offset + 1];\n if (timestamp < updated_at) continue; // 时间戳太旧\n\n const value = stringPool[poolIndex]!;\n productResult[field] = [value, timestamp];\n }\n\n result[product_id] = productResult;\n }\n\n return result;\n };\n\n // 导出内部统计信息用于调试\n const getStats = () => ({\n productCount: products.length,\n fieldCount: dataLength,\n stringPoolSize: stringPool.length,\n activeStrings: stringPool.length - freeIndices.length,\n freeIndices: freeIndices.length,\n memoryEstimate: {\n data: data.length * 8, // Float64Array 每个元素8字节\n pool: stringPool.reduce((sum, str) => sum + (str ? str.length * 2 : 0), 0),\n structures: products.length * 50 + stringPool.length * 16, // 粗略估计\n },\n });\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n result[product_id][field] = tuple ? tuple[0] : '';\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
1
+ {"version":3,"file":"v3.js","sourceRoot":"","sources":["../../../src/quote/implementations/v3.ts"],"names":[],"mappings":"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","sourcesContent":["import { newError } from '@yuants/utils';\nimport { IQuoteKey, IQuoteState, IQuoteUpdateAction } from '../types';\n\n// TRICK: 固定字段顺序,方便计算偏移量\nconst FIELDS = ((x: { [key in IQuoteKey]: number }) => Object.keys(x).sort() as IQuoteKey[])({\n // TS TRICK: 强制运行时数组具有 IQuoteKey 的所有字段。不重不漏,味道真是好极了\n last_price: 0,\n ask_price: 0,\n ask_volume: 0,\n bid_volume: 0,\n bid_price: 0,\n interest_rate_short: 0,\n open_interest: 0,\n interest_rate_prev_settled_at: 0,\n interest_rate_next_settled_at: 0,\n interest_rate_long: 0,\n interest_rate_settlement_interval: 0,\n});\n\nconst FIELD_COUNT = FIELDS.length;\nconst mapFieldNameToOffset = Object.fromEntries(FIELDS.map((field, index) => [field, index * 2])); // 每个字段占2个位置: [池索引, 时间戳]\n\n/**\n * 使用引用计数字符串池的行情状态管理器 v3\n * 保持 v1 的内存局部性,同时通过字符串池减少内存开销\n * 使用 Float64Array 连续存储 [池索引, 时间戳] 对\n */\nexport const createQuoteStateV3 = (): IQuoteState => {\n // 核心数据存储:连续存储 [池索引, 时间戳, 池索引, 时间戳...]\n let data: Float64Array = new Float64Array(1024 * FIELD_COUNT * 2); // 初始容量:1024产品\n let dataLength = 0; // 当前已分配的数据槽数量(以字段为单位)\n\n // 字符串池\n let stringPool: (string | undefined)[] = []; // 索引 -> 字符串(undefined 表示空闲)\n let refCounts: number[] = []; // 索引 -> 引用计数\n let stringToIndex = new Map<string, number>(); // 字符串 -> 索引\n let freeIndices: number[] = []; // 空闲索引列表\n\n const products: string[] = [];\n const mapProductIdToIndex = new Map<string, number>();\n\n // 确保 data 有足够容量\n const ensureDataCapacity = (requiredFields: number) => {\n if (requiredFields * 2 <= data.length) return;\n\n // 倍增扩容\n const newSize = Math.max(data.length * 2, requiredFields * 2 + 1024);\n const newData = new Float64Array(newSize);\n newData.set(data);\n data = newData;\n };\n\n // 获取字段偏移量(以 Float64 元素为单位)\n const getFieldOffset = (product_id: string, field: string): number => {\n let baseIndex = mapProductIdToIndex.get(product_id);\n if (baseIndex === undefined) {\n baseIndex = mapProductIdToIndex.size;\n products.push(product_id);\n mapProductIdToIndex.set(product_id, baseIndex);\n\n // 确保有足够空间存储该产品的所有字段\n const requiredFields = (baseIndex + 1) * FIELD_COUNT;\n ensureDataCapacity(requiredFields);\n\n // 初始化该产品的所有字段为 [-1, 0](空值)\n const startIdx = baseIndex * FIELD_COUNT * 2;\n for (let i = 0; i < FIELD_COUNT * 2; i += 2) {\n data[startIdx + i] = -1; // 池索引:-1 表示空值\n data[startIdx + i + 1] = 0; // 时间戳:0\n }\n dataLength = Math.max(dataLength, requiredFields);\n }\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) throw newError('INVALID_FIELD_NAME', { field, available_fields: FIELDS });\n return baseIndex * FIELD_COUNT * 2 + fieldOffset;\n };\n\n // 获取或分配字符串索引(增加引用计数)\n const acquireStringIndex = (value: string): number => {\n let index = stringToIndex.get(value);\n if (index !== undefined) {\n // 字符串已存在,增加引用计数\n refCounts[index]++;\n return index;\n }\n\n // 需要新索引\n if (freeIndices.length > 0) {\n // 重用空闲索引\n index = freeIndices.pop()!;\n stringPool[index] = value;\n refCounts[index] = 1;\n } else {\n // 分配新索引\n index = stringPool.length;\n stringPool.push(value);\n refCounts.push(1);\n }\n\n stringToIndex.set(value, index);\n return index;\n };\n\n // 释放字符串索引(减少引用计数)\n const releaseStringIndex = (index: number): void => {\n if (index === -1) return; // 空值索引\n\n refCounts[index]--;\n if (refCounts[index] === 0) {\n // 引用计数归零,可以回收\n const value = stringPool[index]!;\n stringToIndex.delete(value);\n // 清空池中的引用,允许 GC\n stringPool[index] = undefined;\n refCounts[index] = 0;\n freeIndices.push(index);\n }\n };\n\n const getValueTuple = (product_id: string, field: IQuoteKey): [string, number] | undefined => {\n const offset = getFieldOffset(product_id, field);\n const poolIndex = data[offset];\n if (poolIndex === -1) return undefined;\n\n const timestamp = data[offset + 1];\n const value = stringPool[poolIndex]!;\n return [value, timestamp];\n };\n\n const setValueTuple = (product_id: string, field: IQuoteKey, value: string, updated_at: number) => {\n const offset = getFieldOffset(product_id, field);\n const oldPoolIndex = data[offset];\n\n // 释放旧字符串的引用\n releaseStringIndex(oldPoolIndex);\n\n // 获取新字符串索引\n const newPoolIndex = acquireStringIndex(value);\n\n // 更新存储\n data[offset] = newPoolIndex;\n data[offset + 1] = updated_at;\n };\n\n const update = (action: IQuoteUpdateAction) => {\n for (const product_id in action) {\n const fields = action[product_id];\n for (const field_name in fields) {\n const field = field_name as IQuoteKey;\n const [value, updated_at] = fields[field]!;\n const oldTuple = getValueTuple(product_id, field);\n if (oldTuple === undefined || updated_at >= oldTuple[1]) {\n setValueTuple(product_id, field, value, updated_at);\n }\n }\n }\n };\n\n const dumpAsObject = (): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n\n // 初始化所有产品的结构\n for (const product_id of products) {\n result[product_id] = {};\n }\n\n // 遍历所有字段(dataLength 是以字段为单位)\n for (let fieldIdx = 0; fieldIdx < dataLength; fieldIdx++) {\n const poolIndex = data[fieldIdx * 2];\n if (poolIndex === -1) continue; // 空值\n\n const timestamp = data[fieldIdx * 2 + 1];\n const productIndex = Math.floor(fieldIdx / FIELD_COUNT);\n const product_id = products[productIndex];\n\n const fieldIndex = fieldIdx % FIELD_COUNT;\n const field_name = FIELDS[fieldIndex];\n\n const value = stringPool[poolIndex]!;\n result[product_id]![field_name] = [value, timestamp];\n }\n\n return result;\n };\n\n /**\n * 过滤状态,返回指定 product_id 列表和字段列表中,且更新时间不早于指定时间的字段数据\n * @param product_ids - 需要过滤的 product_id 列表\n * @param fields - 需要过滤的字段列表\n * @param updated_at - 需要过滤的更新时间阈值 (仅返回更新时间不早于该值的字段)\n * @returns 过滤后的数据\n */\n const filter = (product_ids: string[], fields: IQuoteKey[], updated_at: number): IQuoteUpdateAction => {\n const result: IQuoteUpdateAction = {};\n\n for (const product_id of product_ids) {\n const productIndex = mapProductIdToIndex.get(product_id);\n if (productIndex === undefined) {\n // 产品不存在,创建空对象\n result[product_id] = {};\n continue;\n }\n\n const productResult: Partial<Record<IQuoteKey, [string, number]>> = {};\n const baseOffset = productIndex * FIELD_COUNT * 2;\n\n for (const field of fields) {\n const fieldOffset = mapFieldNameToOffset[field];\n if (fieldOffset === undefined) continue;\n\n const offset = baseOffset + fieldOffset;\n const poolIndex = data[offset];\n if (poolIndex === -1) continue; // 空值\n\n const timestamp = data[offset + 1];\n if (timestamp < updated_at) continue; // 时间戳太旧\n\n const value = stringPool[poolIndex]!;\n productResult[field] = [value, timestamp];\n }\n\n result[product_id] = productResult;\n }\n\n return result;\n };\n\n // 导出内部统计信息用于调试\n const getStats = () => ({\n productCount: products.length,\n fieldCount: dataLength,\n stringPoolSize: stringPool.length,\n activeStrings: stringPool.length - freeIndices.length,\n freeIndices: freeIndices.length,\n memoryEstimate: {\n data: data.length * 8, // Float64Array 每个元素8字节\n pool: stringPool.reduce((sum, str) => sum + (str ? str.length * 2 : 0), 0),\n structures: products.length * 50 + stringPool.length * 16, // 粗略估计\n },\n });\n\n const filterValues = <K extends IQuoteKey>(\n product_ids: string[],\n fields: K[],\n ): Record<string, Record<K, string>> => {\n const result: Record<string, Record<K, string>> = {};\n for (const product_id of product_ids) {\n result[product_id] = {} as Record<K, string>;\n for (const field of fields) {\n const tuple = getValueTuple(product_id, field);\n result[product_id][field] = tuple ? tuple[0] : '';\n }\n }\n return result;\n };\n\n return { update, dumpAsObject, getValueTuple, filter, filterValues };\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuants/app-virtual-exchange",
3
- "version": "0.18.2",
3
+ "version": "0.18.3",
4
4
  "main": "lib/index.js",
5
5
  "files": [
6
6
  "dist",
@@ -8,18 +8,18 @@
8
8
  "temp"
9
9
  ],
10
10
  "dependencies": {
11
- "@yuants/protocol": "0.53.8",
12
- "@yuants/utils": "0.19.1",
13
- "@yuants/data-product": "0.5.6",
14
- "@yuants/data-account": "0.11.5",
15
- "@yuants/data-order": "0.7.6",
16
- "@yuants/data-quote": "0.4.5",
17
- "@yuants/data-ohlc": "0.6.1",
18
- "@yuants/data-interest-rate": "0.2.4",
19
- "@yuants/secret": "0.4.6",
20
- "@yuants/sql": "0.9.36",
21
- "@yuants/exchange": "0.8.7",
22
- "@yuants/cache": "0.3.9",
11
+ "@yuants/protocol": "0.53.9",
12
+ "@yuants/utils": "0.19.2",
13
+ "@yuants/data-product": "0.5.7",
14
+ "@yuants/data-account": "0.11.6",
15
+ "@yuants/data-order": "0.7.7",
16
+ "@yuants/data-quote": "0.4.6",
17
+ "@yuants/data-ohlc": "0.6.2",
18
+ "@yuants/data-interest-rate": "0.2.5",
19
+ "@yuants/secret": "0.4.7",
20
+ "@yuants/sql": "0.9.37",
21
+ "@yuants/exchange": "0.8.8",
22
+ "@yuants/cache": "0.3.10",
23
23
  "rxjs": "~7.5.6",
24
24
  "ajv": "~8.12.0"
25
25
  },