@363045841yyt/klinechart-core 0.7.3 → 0.7.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/README.md +201 -201
  2. package/README.zh-CN.md +201 -201
  3. package/dist/controllers/index.d.ts +1 -0
  4. package/dist/controllers/index.d.ts.map +1 -1
  5. package/dist/controllers/index.js +1 -0
  6. package/dist/controllers/index.js.map +1 -1
  7. package/dist/engine/chart.d.ts +11 -19
  8. package/dist/engine/chart.d.ts.map +1 -1
  9. package/dist/engine/chart.js +92 -109
  10. package/dist/engine/chart.js.map +1 -1
  11. package/dist/engine/renderers/Indicator/indicatorData.d.ts +1 -0
  12. package/dist/engine/renderers/Indicator/indicatorData.d.ts.map +1 -1
  13. package/dist/engine/renderers/Indicator/indicatorData.js +1 -1
  14. package/dist/engine/renderers/Indicator/indicatorData.js.map +1 -1
  15. package/dist/engine/renderers/webgl/candleSurface.js +47 -47
  16. package/dist/engine/subPaneManager.d.ts +4 -0
  17. package/dist/engine/subPaneManager.d.ts.map +1 -1
  18. package/dist/engine/subPaneManager.js +13 -0
  19. package/dist/engine/subPaneManager.js.map +1 -1
  20. package/dist/version.d.ts +1 -1
  21. package/dist/version.d.ts.map +1 -1
  22. package/dist/version.js +1 -2
  23. package/dist/version.js.map +1 -1
  24. package/package.json +129 -122
  25. package/src/__tests__/signal.test.ts +124 -124
  26. package/src/config/chartSettings.ts +66 -66
  27. package/src/controllers/__tests__/drawing.test.ts +214 -214
  28. package/src/controllers/__tests__/indicatorSelector.test.ts +481 -481
  29. package/src/controllers/__tests__/toolbar.test.ts +225 -225
  30. package/src/controllers/createChartController.ts +665 -665
  31. package/src/controllers/createDrawingController.ts +96 -96
  32. package/src/controllers/createIndicatorSelectorController.ts +307 -307
  33. package/src/controllers/createToolbarController.ts +146 -146
  34. package/src/controllers/index.ts +20 -19
  35. package/src/controllers/types.ts +284 -284
  36. package/src/engine/__tests__/chart.dpr.test.ts +401 -401
  37. package/src/engine/__tests__/paneRenderer.resize.test.ts +92 -92
  38. package/src/engine/chart-store.ts +121 -121
  39. package/src/engine/chart.d.ts +617 -617
  40. package/src/engine/chart.ts +2803 -2815
  41. package/src/engine/controller/__tests__/interaction.dpr.test.ts +259 -259
  42. package/src/engine/controller/interaction.ts +722 -722
  43. package/src/engine/controller/markerInteraction.ts +130 -130
  44. package/src/engine/controller/pinchTracker.ts +82 -82
  45. package/src/engine/controller/tooltipPosition.ts +48 -48
  46. package/src/engine/draw/__tests__/pixelAlign.spec.ts +176 -176
  47. package/src/engine/draw/pixelAlign.ts +259 -259
  48. package/src/engine/drawing/index.ts +655 -655
  49. package/src/engine/drawing/interaction.ts +842 -842
  50. package/src/engine/drawing/plugin.ts +343 -343
  51. package/src/engine/indicators/__tests__/__fixtures__/golden/atr.json +38 -38
  52. package/src/engine/indicators/__tests__/__fixtures__/golden/dema.json +14 -14
  53. package/src/engine/indicators/__tests__/__fixtures__/golden/hma.json +14 -14
  54. package/src/engine/indicators/__tests__/__fixtures__/golden/index.ts +55 -55
  55. package/src/engine/indicators/__tests__/__fixtures__/golden/kama.json +14 -14
  56. package/src/engine/indicators/__tests__/__fixtures__/golden/tema.json +14 -14
  57. package/src/engine/indicators/__tests__/__fixtures__/golden/wma.json +40 -40
  58. package/src/engine/indicators/__tests__/__fixtures__/synthetic.ts +65 -65
  59. package/src/engine/indicators/__tests__/_propertyAssertions.ts +76 -76
  60. package/src/engine/indicators/__tests__/atr.test.ts +153 -153
  61. package/src/engine/indicators/__tests__/calculators.test.ts +614 -614
  62. package/src/engine/indicators/__tests__/cmf-mfi.test.ts +100 -100
  63. package/src/engine/indicators/__tests__/dema.test.ts +73 -73
  64. package/src/engine/indicators/__tests__/donchian.test.ts +70 -70
  65. package/src/engine/indicators/__tests__/hma.test.ts +73 -73
  66. package/src/engine/indicators/__tests__/ichimoku.test.ts +105 -105
  67. package/src/engine/indicators/__tests__/kama.test.ts +80 -80
  68. package/src/engine/indicators/__tests__/keltner.test.ts +65 -65
  69. package/src/engine/indicators/__tests__/pivot-fib.test.ts +110 -110
  70. package/src/engine/indicators/__tests__/roc.test.ts +68 -68
  71. package/src/engine/indicators/__tests__/sar.test.ts +86 -86
  72. package/src/engine/indicators/__tests__/scheduler.test.ts +831 -831
  73. package/src/engine/indicators/__tests__/soa.test.ts +533 -533
  74. package/src/engine/indicators/__tests__/structure.test.ts +110 -110
  75. package/src/engine/indicators/__tests__/supertrend.test.ts +65 -65
  76. package/src/engine/indicators/__tests__/tema.test.ts +68 -68
  77. package/src/engine/indicators/__tests__/trix.test.ts +70 -70
  78. package/src/engine/indicators/__tests__/volatility.test.ts +117 -117
  79. package/src/engine/indicators/__tests__/volume.test.ts +115 -115
  80. package/src/engine/indicators/__tests__/volumeProfile.test.ts +74 -74
  81. package/src/engine/indicators/__tests__/vwap.test.ts +69 -69
  82. package/src/engine/indicators/__tests__/wma.test.ts +112 -112
  83. package/src/engine/indicators/__tests__/zones.test.ts +95 -95
  84. package/src/engine/indicators/atrState.ts +27 -27
  85. package/src/engine/indicators/bollState.ts +51 -51
  86. package/src/engine/indicators/calculators.ts +2593 -2593
  87. package/src/engine/indicators/cciState.ts +25 -25
  88. package/src/engine/indicators/chaikinVolState.ts +32 -32
  89. package/src/engine/indicators/cmfState.ts +27 -27
  90. package/src/engine/indicators/demaState.ts +27 -27
  91. package/src/engine/indicators/donchianState.ts +43 -43
  92. package/src/engine/indicators/eneState.ts +43 -43
  93. package/src/engine/indicators/expmaState.ts +43 -43
  94. package/src/engine/indicators/fastkState.ts +25 -25
  95. package/src/engine/indicators/fibState.ts +41 -41
  96. package/src/engine/indicators/hmaState.ts +27 -27
  97. package/src/engine/indicators/hvState.ts +28 -28
  98. package/src/engine/indicators/ichimokuState.ts +70 -70
  99. package/src/engine/indicators/indicator.worker.ts +169 -169
  100. package/src/engine/indicators/indicatorDefinitionRegistry.ts +62 -62
  101. package/src/engine/indicators/indicatorMetadata.ts +110 -110
  102. package/src/engine/indicators/indicatorRegistry.ts +106 -106
  103. package/src/engine/indicators/indicatorRuntime.ts +1548 -1548
  104. package/src/engine/indicators/kamaState.ts +34 -34
  105. package/src/engine/indicators/keltnerState.ts +49 -49
  106. package/src/engine/indicators/kstState.ts +42 -42
  107. package/src/engine/indicators/maState.ts +36 -36
  108. package/src/engine/indicators/macdState.ts +76 -76
  109. package/src/engine/indicators/mfiState.ts +27 -27
  110. package/src/engine/indicators/momState.ts +25 -25
  111. package/src/engine/indicators/obvState.ts +25 -25
  112. package/src/engine/indicators/parkinsonState.ts +28 -28
  113. package/src/engine/indicators/pivotState.ts +51 -51
  114. package/src/engine/indicators/pvtState.ts +25 -25
  115. package/src/engine/indicators/rocState.ts +27 -27
  116. package/src/engine/indicators/rsiState.ts +65 -65
  117. package/src/engine/indicators/sarState.ts +41 -41
  118. package/src/engine/indicators/scheduler.ts +1205 -1205
  119. package/src/engine/indicators/soa.ts +352 -352
  120. package/src/engine/indicators/stateComposer.ts +1262 -1262
  121. package/src/engine/indicators/stochState.ts +26 -26
  122. package/src/engine/indicators/structureState.ts +69 -69
  123. package/src/engine/indicators/supertrendState.ts +37 -37
  124. package/src/engine/indicators/temaState.ts +27 -27
  125. package/src/engine/indicators/trixState.ts +35 -35
  126. package/src/engine/indicators/vmaState.ts +27 -27
  127. package/src/engine/indicators/volumeProfileState.ts +63 -63
  128. package/src/engine/indicators/vwapState.ts +29 -29
  129. package/src/engine/indicators/wmaState.ts +27 -27
  130. package/src/engine/indicators/wmsrState.ts +25 -25
  131. package/src/engine/indicators/workerProtocol.ts +613 -613
  132. package/src/engine/indicators/zonesState.ts +47 -47
  133. package/src/engine/layout/pane.ts +161 -161
  134. package/src/engine/marker/registry.ts +265 -265
  135. package/src/engine/paneRenderer.ts +169 -169
  136. package/src/engine/renderers/Indicator/atr.ts +237 -237
  137. package/src/engine/renderers/Indicator/boll.ts +317 -317
  138. package/src/engine/renderers/Indicator/cci.ts +275 -275
  139. package/src/engine/renderers/Indicator/chaikinVol.ts +138 -138
  140. package/src/engine/renderers/Indicator/cmf.ts +137 -137
  141. package/src/engine/renderers/Indicator/dema.ts +136 -136
  142. package/src/engine/renderers/Indicator/donchian.ts +137 -137
  143. package/src/engine/renderers/Indicator/ene.ts +271 -271
  144. package/src/engine/renderers/Indicator/expma.ts +197 -197
  145. package/src/engine/renderers/Indicator/fastk.ts +316 -316
  146. package/src/engine/renderers/Indicator/fib.ts +141 -141
  147. package/src/engine/renderers/Indicator/hma.ts +136 -136
  148. package/src/engine/renderers/Indicator/hv.ts +124 -124
  149. package/src/engine/renderers/Indicator/ichimoku.ts +181 -181
  150. package/src/engine/renderers/Indicator/index.ts +241 -241
  151. package/src/engine/renderers/Indicator/indicatorData.ts +650 -650
  152. package/src/engine/renderers/Indicator/kama.ts +136 -136
  153. package/src/engine/renderers/Indicator/keltner.ts +137 -137
  154. package/src/engine/renderers/Indicator/kst.ts +302 -302
  155. package/src/engine/renderers/Indicator/ma.ts +200 -200
  156. package/src/engine/renderers/Indicator/macd.ts +477 -477
  157. package/src/engine/renderers/Indicator/macdLegend.ts +141 -141
  158. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +272 -272
  159. package/src/engine/renderers/Indicator/mfi.ts +142 -142
  160. package/src/engine/renderers/Indicator/mom.ts +311 -311
  161. package/src/engine/renderers/Indicator/obv.ts +123 -123
  162. package/src/engine/renderers/Indicator/parkinson.ts +124 -124
  163. package/src/engine/renderers/Indicator/pivot.ts +131 -131
  164. package/src/engine/renderers/Indicator/pvt.ts +123 -123
  165. package/src/engine/renderers/Indicator/roc.ts +143 -143
  166. package/src/engine/renderers/Indicator/rsi.ts +390 -390
  167. package/src/engine/renderers/Indicator/sar.ts +113 -113
  168. package/src/engine/renderers/Indicator/scale/atr_scale.ts +19 -19
  169. package/src/engine/renderers/Indicator/scale/cci_scale.ts +19 -19
  170. package/src/engine/renderers/Indicator/scale/fastk_scale.ts +19 -19
  171. package/src/engine/renderers/Indicator/scale/indicator_scale.ts +204 -204
  172. package/src/engine/renderers/Indicator/scale/kst_scale.ts +19 -19
  173. package/src/engine/renderers/Indicator/scale/macd_scale.ts +22 -22
  174. package/src/engine/renderers/Indicator/scale/mom_scale.ts +19 -19
  175. package/src/engine/renderers/Indicator/scale/rsi_scale.ts +19 -19
  176. package/src/engine/renderers/Indicator/scale/stoch_scale.ts +19 -19
  177. package/src/engine/renderers/Indicator/scale/volume_scale.ts +26 -26
  178. package/src/engine/renderers/Indicator/scale/wmsr_scale.ts +19 -19
  179. package/src/engine/renderers/Indicator/stoch.ts +359 -359
  180. package/src/engine/renderers/Indicator/structure.ts +126 -126
  181. package/src/engine/renderers/Indicator/subPaneConfig.ts +265 -265
  182. package/src/engine/renderers/Indicator/supertrend.ts +115 -115
  183. package/src/engine/renderers/Indicator/tema.ts +136 -136
  184. package/src/engine/renderers/Indicator/trix.ts +158 -158
  185. package/src/engine/renderers/Indicator/vma.ts +124 -124
  186. package/src/engine/renderers/Indicator/volumeProfile.ts +125 -125
  187. package/src/engine/renderers/Indicator/vwap.ts +123 -123
  188. package/src/engine/renderers/Indicator/wma.ts +136 -136
  189. package/src/engine/renderers/Indicator/wmsr.ts +328 -328
  190. package/src/engine/renderers/Indicator/zones.ts +104 -104
  191. package/src/engine/renderers/__tests__/boll.renderer.test.ts +314 -314
  192. package/src/engine/renderers/__tests__/ene.renderer.test.ts +305 -305
  193. package/src/engine/renderers/__tests__/expma.renderer.test.ts +279 -279
  194. package/src/engine/renderers/__tests__/ma.renderer.test.ts +426 -426
  195. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +502 -502
  196. package/src/engine/renderers/__tests__/yAxis.renderer.test.ts +173 -173
  197. package/src/engine/renderers/candle.ts +459 -459
  198. package/src/engine/renderers/crosshair.ts +69 -69
  199. package/src/engine/renderers/customMarkers.ts +162 -162
  200. package/src/engine/renderers/extremaMarkers.ts +246 -246
  201. package/src/engine/renderers/gridLines.ts +90 -90
  202. package/src/engine/renderers/lastPrice.ts +97 -97
  203. package/src/engine/renderers/paneTitle.ts +136 -136
  204. package/src/engine/renderers/subVolume.ts +236 -236
  205. package/src/engine/renderers/timeAxis.ts +121 -121
  206. package/src/engine/renderers/webgl/candleSurface.ts +955 -955
  207. package/src/engine/renderers/webgl/sharedWebGLSurface.ts +146 -146
  208. package/src/engine/renderers/yAxis.ts +105 -105
  209. package/src/engine/scale/__tests__/logFormula.spec.ts +148 -148
  210. package/src/engine/scale/logFormula.ts +130 -130
  211. package/src/engine/scale/price.ts +39 -39
  212. package/src/engine/scale/priceScale.ts +264 -264
  213. package/src/engine/subPaneManager.ts +442 -427
  214. package/src/engine/theme/colors.ts +642 -642
  215. package/src/engine/theme/fonts.ts +20 -20
  216. package/src/engine/utils/klineConfig.ts +49 -49
  217. package/src/engine/utils/tickCount.ts +11 -11
  218. package/src/engine/utils/tickPosition.ts +214 -214
  219. package/src/engine/utils/zoom.ts +83 -83
  220. package/src/engine/viewport/viewport.ts +67 -67
  221. package/src/index.ts +3 -3
  222. package/src/plugin/ConfigManager.ts +93 -93
  223. package/src/plugin/EventBus.ts +77 -77
  224. package/src/plugin/HookSystem.ts +106 -106
  225. package/src/plugin/PluginHost.ts +243 -243
  226. package/src/plugin/PluginRegistry.ts +92 -92
  227. package/src/plugin/StateStore.ts +73 -73
  228. package/src/plugin/index.ts +19 -19
  229. package/src/plugin/rendererPluginManager.ts +368 -368
  230. package/src/plugin/stateKeys.ts +8 -8
  231. package/src/plugin/types.ts +526 -526
  232. package/src/reactivity/index.ts +2 -2
  233. package/src/reactivity/signal.ts +119 -119
  234. package/src/semantic/controller.ts +251 -251
  235. package/src/semantic/drawShape.ts +260 -260
  236. package/src/semantic/index.ts +28 -28
  237. package/src/semantic/schema.json +256 -256
  238. package/src/semantic/types.ts +251 -251
  239. package/src/semantic/validator.ts +349 -349
  240. package/src/types/kLine.ts +13 -13
  241. package/src/types/price.ts +56 -56
  242. package/src/types/volumePrice.ts +33 -33
  243. package/src/utils/dateFormat.ts +208 -208
  244. package/src/utils/kLineDraw/axis.ts +562 -562
  245. package/src/utils/priceToY.ts +34 -34
  246. package/src/utils/volumePrice.ts +202 -202
  247. package/src/version.ts +1 -1
@@ -1,352 +1,352 @@
1
- import type { KLineData } from '../../types/price'
2
-
3
- /**
4
- * KLineData 的 SoA (Structure of Arrays) 布局
5
- * 用于零拷贝传输到 Web Worker
6
- *
7
- * 内存布局:
8
- * | timestamps | opens | highs | lows | closes | volumes | turnovers |
9
- * | 8*N | 8*N | 8*N | 8*N | 8*N | 8*N | 8*N |
10
- *
11
- * 其中 N 为数据长度,每列 8 字节(Float64)
12
- */
13
- export interface KLineSoALayout {
14
- /** 底层缓冲区(SharedArrayBuffer 或 ArrayBuffer) */
15
- buffer: SharedArrayBuffer | ArrayBuffer
16
- /** 数据长度 */
17
- length: number
18
- /** 是否使用 SharedArrayBuffer */
19
- isShared: boolean
20
- /** 是否有成交量数据 */
21
- hasVolume: boolean
22
- /** 是否有成交额数据 */
23
- hasTurnover: boolean
24
- /** 时间戳数组(毫秒) */
25
- timestamps: Float64Array
26
- /** 开盘价数组 */
27
- opens: Float64Array
28
- /** 最高价数组 */
29
- highs: Float64Array
30
- /** 最低价数组 */
31
- lows: Float64Array
32
- /** 收盘价数组 */
33
- closes: Float64Array
34
- /** 成交量数组(无值时为 0) */
35
- volumes: Float64Array
36
- /** 成交额数组(无值时为 0) */
37
- turnovers: Float64Array
38
- }
39
-
40
- /** 列索引定义(用于计算 offset) */
41
- const enum ColumnIndex {
42
- TIMESTAMP = 0,
43
- OPEN = 1,
44
- HIGH = 2,
45
- LOW = 3,
46
- CLOSE = 4,
47
- VOLUME = 5,
48
- TURNOVER = 6,
49
- COUNT = 7,
50
- }
51
-
52
- /** 每列的字节数 */
53
- const BYTES_PER_COLUMN = 8 // Float64
54
-
55
- /**
56
- * SharedKLineBuffer - K线数据的 SoA 管理类
57
- *
58
- * 提供 AoS (KLineData[]) 与 SoA 布局之间的转换,
59
- * 支持 SharedArrayBuffer(零拷贝)和 ArrayBuffer(降级)
60
- */
61
- export class SharedKLineBuffer {
62
- /**
63
- * 检测 SharedArrayBuffer 是否可用
64
- * 需要页面有 COOP/COEP 头支持
65
- */
66
- static detectSupport(): boolean {
67
- try {
68
- if (typeof SharedArrayBuffer === 'undefined') {
69
- return false
70
- }
71
- // 尝试创建并检测能否成功创建视图
72
- const testSab = new SharedArrayBuffer(8)
73
- const testView = new Float64Array(testSab)
74
- testView[0] = 1.0
75
- return testView[0] === 1.0
76
- } catch {
77
- return false
78
- }
79
- }
80
-
81
- /**
82
- * 计算 SoA 布局所需的总字节数
83
- */
84
- static calculateByteLength(length: number): number {
85
- return length * BYTES_PER_COLUMN * ColumnIndex.COUNT
86
- }
87
-
88
- /**
89
- * 将 KLineData[] 转换为 SoA 布局
90
- * @param data K线数据数组(AoS 格式)
91
- * @param preferShared 是否优先使用 SharedArrayBuffer(默认 true)
92
- * @returns SoA 布局对象
93
- */
94
- static fromKLineData(
95
- data: KLineData[],
96
- preferShared: boolean = true
97
- ): KLineSoALayout {
98
- const length = data.length
99
- const byteLength = SharedKLineBuffer.calculateByteLength(length)
100
- const useShared = preferShared && SharedKLineBuffer.detectSupport()
101
-
102
- // 检测是否有 volume 和 turnover 数据
103
- let hasVolume = false
104
- let hasTurnover = false
105
- for (const item of data) {
106
- if (item.volume !== undefined && item.volume !== 0) {
107
- hasVolume = true
108
- }
109
- if (item.turnover !== undefined && item.turnover !== 0) {
110
- hasTurnover = true
111
- }
112
- if (hasVolume && hasTurnover) break
113
- }
114
-
115
- // 创建缓冲区
116
- const buffer = useShared
117
- ? new SharedArrayBuffer(byteLength)
118
- : new ArrayBuffer(byteLength)
119
-
120
- // 创建列视图
121
- const timestamps = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.TIMESTAMP, length)
122
- const opens = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.OPEN, length)
123
- const highs = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.HIGH, length)
124
- const lows = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.LOW, length)
125
- const closes = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.CLOSE, length)
126
- const volumes = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.VOLUME, length)
127
- const turnovers = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.TURNOVER, length)
128
-
129
- // 填充数据
130
- for (let i = 0; i < length; i++) {
131
- const item = data[i]!
132
- timestamps[i] = item.timestamp
133
- opens[i] = item.open
134
- highs[i] = item.high
135
- lows[i] = item.low
136
- closes[i] = item.close
137
- volumes[i] = item.volume ?? 0
138
- turnovers[i] = item.turnover ?? 0
139
- }
140
-
141
- return {
142
- buffer,
143
- length,
144
- isShared: useShared,
145
- hasVolume,
146
- hasTurnover,
147
- timestamps,
148
- opens,
149
- highs,
150
- lows,
151
- closes,
152
- volumes,
153
- turnovers,
154
- }
155
- }
156
-
157
- /**
158
- * 更新现有 SoA 布局的数据(尽可能复用缓冲区)
159
- * 如果新数据长度超过原缓冲区,会创建新缓冲区
160
- *
161
- * @param layout 现有 SoA 布局
162
- * @param data 新的 K线数据
163
- * @returns 更新后的 SoA 布局(可能是新对象)
164
- */
165
- static updateExisting(layout: KLineSoALayout, data: KLineData[]): KLineSoALayout {
166
- const newLength = data.length
167
- const oldLength = layout.length
168
-
169
- // 如果长度变化或缓冲区类型不匹配,创建新布局
170
- if (newLength !== oldLength) {
171
- return SharedKLineBuffer.fromKLineData(data, layout.isShared)
172
- }
173
-
174
- // 检测是否有 volume 和 turnover 数据
175
- let hasVolume = false
176
- let hasTurnover = false
177
- for (const item of data) {
178
- if (item.volume !== undefined && item.volume !== 0) {
179
- hasVolume = true
180
- }
181
- if (item.turnover !== undefined && item.turnover !== 0) {
182
- hasTurnover = true
183
- }
184
- if (hasVolume && hasTurnover) break
185
- }
186
-
187
- // 复用现有视图,直接更新数据
188
- const { timestamps, opens, highs, lows, closes, volumes, turnovers } = layout
189
-
190
- for (let i = 0; i < newLength; i++) {
191
- const item = data[i]!
192
- timestamps[i] = item.timestamp
193
- opens[i] = item.open
194
- highs[i] = item.high
195
- lows[i] = item.low
196
- closes[i] = item.close
197
- volumes[i] = item.volume ?? 0
198
- turnovers[i] = item.turnover ?? 0
199
- }
200
-
201
- return {
202
- ...layout,
203
- hasVolume,
204
- hasTurnover,
205
- }
206
- }
207
-
208
- /**
209
- * 将 SoA 布局转换回 KLineData[](用于测试和兼容性)
210
- * @param layout SoA 布局
211
- * @returns K线数据数组
212
- */
213
- static toKLineData(layout: KLineSoALayout): KLineData[] {
214
- const result: KLineData[] = new Array(layout.length)
215
- const { timestamps, opens, highs, lows, closes, volumes, turnovers, hasVolume, hasTurnover } = layout
216
-
217
- for (let i = 0; i < layout.length; i++) {
218
- const item: KLineData = {
219
- timestamp: timestamps[i]!,
220
- open: opens[i]!,
221
- high: highs[i]!,
222
- low: lows[i]!,
223
- close: closes[i]!,
224
- }
225
-
226
- if (hasVolume) {
227
- const vol = volumes[i]!
228
- if (vol !== 0) item.volume = vol
229
- }
230
- if (hasTurnover) {
231
- const to = turnovers[i]!
232
- if (to !== 0) item.turnover = to
233
- }
234
-
235
- result[i] = item
236
- }
237
-
238
- return result
239
- }
240
-
241
- /**
242
- * 创建子视图(用于 Worker 中处理部分数据范围)
243
- * @param layout 原 SoA 布局
244
- * @param start 起始索引(包含)
245
- * @param end 结束索引(不包含)
246
- * @returns 子视图对象(共享同一缓冲区)
247
- */
248
- static createSubview(layout: KLineSoALayout, start: number, end: number): KLineSoALayout {
249
- if (start < 0) start = 0
250
- if (end > layout.length) end = layout.length
251
- if (start >= end) {
252
- throw new Error(`Invalid subview range: [${start}, ${end})`)
253
- }
254
-
255
- const length = end - start
256
- // 每列的总字节数 = 数据长度 * 每元素字节数
257
- const columnByteLength = layout.length * BYTES_PER_COLUMN
258
-
259
- return {
260
- buffer: layout.buffer,
261
- length,
262
- isShared: layout.isShared,
263
- hasVolume: layout.hasVolume,
264
- hasTurnover: layout.hasTurnover,
265
- timestamps: new Float64Array(
266
- layout.buffer,
267
- start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.TIMESTAMP,
268
- length
269
- ),
270
- opens: new Float64Array(
271
- layout.buffer,
272
- start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.OPEN,
273
- length
274
- ),
275
- highs: new Float64Array(
276
- layout.buffer,
277
- start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.HIGH,
278
- length
279
- ),
280
- lows: new Float64Array(
281
- layout.buffer,
282
- start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.LOW,
283
- length
284
- ),
285
- closes: new Float64Array(
286
- layout.buffer,
287
- start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.CLOSE,
288
- length
289
- ),
290
- volumes: new Float64Array(
291
- layout.buffer,
292
- start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.VOLUME,
293
- length
294
- ),
295
- turnovers: new Float64Array(
296
- layout.buffer,
297
- start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.TURNOVER,
298
- length
299
- ),
300
- }
301
- }
302
-
303
- /**
304
- * 获取缓冲区信息(用于调试和序列化)
305
- */
306
- static getBufferInfo(layout: KLineSoALayout): {
307
- byteLength: number
308
- isShared: boolean
309
- length: number
310
- columns: string[]
311
- } {
312
- return {
313
- byteLength: layout.buffer.byteLength,
314
- isShared: layout.isShared,
315
- length: layout.length,
316
- columns: ['timestamp', 'open', 'high', 'low', 'close', 'volume', 'turnover'],
317
- }
318
- }
319
- }
320
-
321
- /**
322
- * 获取 SoA 布局中 closes 列的视图(最常用列)
323
- * 用于指标计算中快速访问收盘价
324
- */
325
- export function getClosesView(layout: KLineSoALayout): Float64Array {
326
- return layout.closes
327
- }
328
-
329
- /**
330
- * 获取 SoA 布局中 highs/lows 列的视图
331
- * 用于需要高低价的指标(如 BOLL、STOCH、WMSR)
332
- */
333
- export function getHighsLowsViews(layout: KLineSoALayout): { highs: Float64Array; lows: Float64Array } {
334
- return { highs: layout.highs, lows: layout.lows }
335
- }
336
-
337
- /**
338
- * 获取 SoA 布局中 OHLC 四价视图
339
- */
340
- export function getOHLCViews(layout: KLineSoALayout): {
341
- opens: Float64Array
342
- highs: Float64Array
343
- lows: Float64Array
344
- closes: Float64Array
345
- } {
346
- return {
347
- opens: layout.opens,
348
- highs: layout.highs,
349
- lows: layout.lows,
350
- closes: layout.closes,
351
- }
352
- }
1
+ import type { KLineData } from '../../types/price'
2
+
3
+ /**
4
+ * KLineData 的 SoA (Structure of Arrays) 布局
5
+ * 用于零拷贝传输到 Web Worker
6
+ *
7
+ * 内存布局:
8
+ * | timestamps | opens | highs | lows | closes | volumes | turnovers |
9
+ * | 8*N | 8*N | 8*N | 8*N | 8*N | 8*N | 8*N |
10
+ *
11
+ * 其中 N 为数据长度,每列 8 字节(Float64)
12
+ */
13
+ export interface KLineSoALayout {
14
+ /** 底层缓冲区(SharedArrayBuffer 或 ArrayBuffer) */
15
+ buffer: SharedArrayBuffer | ArrayBuffer
16
+ /** 数据长度 */
17
+ length: number
18
+ /** 是否使用 SharedArrayBuffer */
19
+ isShared: boolean
20
+ /** 是否有成交量数据 */
21
+ hasVolume: boolean
22
+ /** 是否有成交额数据 */
23
+ hasTurnover: boolean
24
+ /** 时间戳数组(毫秒) */
25
+ timestamps: Float64Array
26
+ /** 开盘价数组 */
27
+ opens: Float64Array
28
+ /** 最高价数组 */
29
+ highs: Float64Array
30
+ /** 最低价数组 */
31
+ lows: Float64Array
32
+ /** 收盘价数组 */
33
+ closes: Float64Array
34
+ /** 成交量数组(无值时为 0) */
35
+ volumes: Float64Array
36
+ /** 成交额数组(无值时为 0) */
37
+ turnovers: Float64Array
38
+ }
39
+
40
+ /** 列索引定义(用于计算 offset) */
41
+ const enum ColumnIndex {
42
+ TIMESTAMP = 0,
43
+ OPEN = 1,
44
+ HIGH = 2,
45
+ LOW = 3,
46
+ CLOSE = 4,
47
+ VOLUME = 5,
48
+ TURNOVER = 6,
49
+ COUNT = 7,
50
+ }
51
+
52
+ /** 每列的字节数 */
53
+ const BYTES_PER_COLUMN = 8 // Float64
54
+
55
+ /**
56
+ * SharedKLineBuffer - K线数据的 SoA 管理类
57
+ *
58
+ * 提供 AoS (KLineData[]) 与 SoA 布局之间的转换,
59
+ * 支持 SharedArrayBuffer(零拷贝)和 ArrayBuffer(降级)
60
+ */
61
+ export class SharedKLineBuffer {
62
+ /**
63
+ * 检测 SharedArrayBuffer 是否可用
64
+ * 需要页面有 COOP/COEP 头支持
65
+ */
66
+ static detectSupport(): boolean {
67
+ try {
68
+ if (typeof SharedArrayBuffer === 'undefined') {
69
+ return false
70
+ }
71
+ // 尝试创建并检测能否成功创建视图
72
+ const testSab = new SharedArrayBuffer(8)
73
+ const testView = new Float64Array(testSab)
74
+ testView[0] = 1.0
75
+ return testView[0] === 1.0
76
+ } catch {
77
+ return false
78
+ }
79
+ }
80
+
81
+ /**
82
+ * 计算 SoA 布局所需的总字节数
83
+ */
84
+ static calculateByteLength(length: number): number {
85
+ return length * BYTES_PER_COLUMN * ColumnIndex.COUNT
86
+ }
87
+
88
+ /**
89
+ * 将 KLineData[] 转换为 SoA 布局
90
+ * @param data K线数据数组(AoS 格式)
91
+ * @param preferShared 是否优先使用 SharedArrayBuffer(默认 true)
92
+ * @returns SoA 布局对象
93
+ */
94
+ static fromKLineData(
95
+ data: KLineData[],
96
+ preferShared: boolean = true
97
+ ): KLineSoALayout {
98
+ const length = data.length
99
+ const byteLength = SharedKLineBuffer.calculateByteLength(length)
100
+ const useShared = preferShared && SharedKLineBuffer.detectSupport()
101
+
102
+ // 检测是否有 volume 和 turnover 数据
103
+ let hasVolume = false
104
+ let hasTurnover = false
105
+ for (const item of data) {
106
+ if (item.volume !== undefined && item.volume !== 0) {
107
+ hasVolume = true
108
+ }
109
+ if (item.turnover !== undefined && item.turnover !== 0) {
110
+ hasTurnover = true
111
+ }
112
+ if (hasVolume && hasTurnover) break
113
+ }
114
+
115
+ // 创建缓冲区
116
+ const buffer = useShared
117
+ ? new SharedArrayBuffer(byteLength)
118
+ : new ArrayBuffer(byteLength)
119
+
120
+ // 创建列视图
121
+ const timestamps = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.TIMESTAMP, length)
122
+ const opens = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.OPEN, length)
123
+ const highs = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.HIGH, length)
124
+ const lows = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.LOW, length)
125
+ const closes = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.CLOSE, length)
126
+ const volumes = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.VOLUME, length)
127
+ const turnovers = new Float64Array(buffer, length * BYTES_PER_COLUMN * ColumnIndex.TURNOVER, length)
128
+
129
+ // 填充数据
130
+ for (let i = 0; i < length; i++) {
131
+ const item = data[i]!
132
+ timestamps[i] = item.timestamp
133
+ opens[i] = item.open
134
+ highs[i] = item.high
135
+ lows[i] = item.low
136
+ closes[i] = item.close
137
+ volumes[i] = item.volume ?? 0
138
+ turnovers[i] = item.turnover ?? 0
139
+ }
140
+
141
+ return {
142
+ buffer,
143
+ length,
144
+ isShared: useShared,
145
+ hasVolume,
146
+ hasTurnover,
147
+ timestamps,
148
+ opens,
149
+ highs,
150
+ lows,
151
+ closes,
152
+ volumes,
153
+ turnovers,
154
+ }
155
+ }
156
+
157
+ /**
158
+ * 更新现有 SoA 布局的数据(尽可能复用缓冲区)
159
+ * 如果新数据长度超过原缓冲区,会创建新缓冲区
160
+ *
161
+ * @param layout 现有 SoA 布局
162
+ * @param data 新的 K线数据
163
+ * @returns 更新后的 SoA 布局(可能是新对象)
164
+ */
165
+ static updateExisting(layout: KLineSoALayout, data: KLineData[]): KLineSoALayout {
166
+ const newLength = data.length
167
+ const oldLength = layout.length
168
+
169
+ // 如果长度变化或缓冲区类型不匹配,创建新布局
170
+ if (newLength !== oldLength) {
171
+ return SharedKLineBuffer.fromKLineData(data, layout.isShared)
172
+ }
173
+
174
+ // 检测是否有 volume 和 turnover 数据
175
+ let hasVolume = false
176
+ let hasTurnover = false
177
+ for (const item of data) {
178
+ if (item.volume !== undefined && item.volume !== 0) {
179
+ hasVolume = true
180
+ }
181
+ if (item.turnover !== undefined && item.turnover !== 0) {
182
+ hasTurnover = true
183
+ }
184
+ if (hasVolume && hasTurnover) break
185
+ }
186
+
187
+ // 复用现有视图,直接更新数据
188
+ const { timestamps, opens, highs, lows, closes, volumes, turnovers } = layout
189
+
190
+ for (let i = 0; i < newLength; i++) {
191
+ const item = data[i]!
192
+ timestamps[i] = item.timestamp
193
+ opens[i] = item.open
194
+ highs[i] = item.high
195
+ lows[i] = item.low
196
+ closes[i] = item.close
197
+ volumes[i] = item.volume ?? 0
198
+ turnovers[i] = item.turnover ?? 0
199
+ }
200
+
201
+ return {
202
+ ...layout,
203
+ hasVolume,
204
+ hasTurnover,
205
+ }
206
+ }
207
+
208
+ /**
209
+ * 将 SoA 布局转换回 KLineData[](用于测试和兼容性)
210
+ * @param layout SoA 布局
211
+ * @returns K线数据数组
212
+ */
213
+ static toKLineData(layout: KLineSoALayout): KLineData[] {
214
+ const result: KLineData[] = new Array(layout.length)
215
+ const { timestamps, opens, highs, lows, closes, volumes, turnovers, hasVolume, hasTurnover } = layout
216
+
217
+ for (let i = 0; i < layout.length; i++) {
218
+ const item: KLineData = {
219
+ timestamp: timestamps[i]!,
220
+ open: opens[i]!,
221
+ high: highs[i]!,
222
+ low: lows[i]!,
223
+ close: closes[i]!,
224
+ }
225
+
226
+ if (hasVolume) {
227
+ const vol = volumes[i]!
228
+ if (vol !== 0) item.volume = vol
229
+ }
230
+ if (hasTurnover) {
231
+ const to = turnovers[i]!
232
+ if (to !== 0) item.turnover = to
233
+ }
234
+
235
+ result[i] = item
236
+ }
237
+
238
+ return result
239
+ }
240
+
241
+ /**
242
+ * 创建子视图(用于 Worker 中处理部分数据范围)
243
+ * @param layout 原 SoA 布局
244
+ * @param start 起始索引(包含)
245
+ * @param end 结束索引(不包含)
246
+ * @returns 子视图对象(共享同一缓冲区)
247
+ */
248
+ static createSubview(layout: KLineSoALayout, start: number, end: number): KLineSoALayout {
249
+ if (start < 0) start = 0
250
+ if (end > layout.length) end = layout.length
251
+ if (start >= end) {
252
+ throw new Error(`Invalid subview range: [${start}, ${end})`)
253
+ }
254
+
255
+ const length = end - start
256
+ // 每列的总字节数 = 数据长度 * 每元素字节数
257
+ const columnByteLength = layout.length * BYTES_PER_COLUMN
258
+
259
+ return {
260
+ buffer: layout.buffer,
261
+ length,
262
+ isShared: layout.isShared,
263
+ hasVolume: layout.hasVolume,
264
+ hasTurnover: layout.hasTurnover,
265
+ timestamps: new Float64Array(
266
+ layout.buffer,
267
+ start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.TIMESTAMP,
268
+ length
269
+ ),
270
+ opens: new Float64Array(
271
+ layout.buffer,
272
+ start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.OPEN,
273
+ length
274
+ ),
275
+ highs: new Float64Array(
276
+ layout.buffer,
277
+ start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.HIGH,
278
+ length
279
+ ),
280
+ lows: new Float64Array(
281
+ layout.buffer,
282
+ start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.LOW,
283
+ length
284
+ ),
285
+ closes: new Float64Array(
286
+ layout.buffer,
287
+ start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.CLOSE,
288
+ length
289
+ ),
290
+ volumes: new Float64Array(
291
+ layout.buffer,
292
+ start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.VOLUME,
293
+ length
294
+ ),
295
+ turnovers: new Float64Array(
296
+ layout.buffer,
297
+ start * BYTES_PER_COLUMN + columnByteLength * ColumnIndex.TURNOVER,
298
+ length
299
+ ),
300
+ }
301
+ }
302
+
303
+ /**
304
+ * 获取缓冲区信息(用于调试和序列化)
305
+ */
306
+ static getBufferInfo(layout: KLineSoALayout): {
307
+ byteLength: number
308
+ isShared: boolean
309
+ length: number
310
+ columns: string[]
311
+ } {
312
+ return {
313
+ byteLength: layout.buffer.byteLength,
314
+ isShared: layout.isShared,
315
+ length: layout.length,
316
+ columns: ['timestamp', 'open', 'high', 'low', 'close', 'volume', 'turnover'],
317
+ }
318
+ }
319
+ }
320
+
321
+ /**
322
+ * 获取 SoA 布局中 closes 列的视图(最常用列)
323
+ * 用于指标计算中快速访问收盘价
324
+ */
325
+ export function getClosesView(layout: KLineSoALayout): Float64Array {
326
+ return layout.closes
327
+ }
328
+
329
+ /**
330
+ * 获取 SoA 布局中 highs/lows 列的视图
331
+ * 用于需要高低价的指标(如 BOLL、STOCH、WMSR)
332
+ */
333
+ export function getHighsLowsViews(layout: KLineSoALayout): { highs: Float64Array; lows: Float64Array } {
334
+ return { highs: layout.highs, lows: layout.lows }
335
+ }
336
+
337
+ /**
338
+ * 获取 SoA 布局中 OHLC 四价视图
339
+ */
340
+ export function getOHLCViews(layout: KLineSoALayout): {
341
+ opens: Float64Array
342
+ highs: Float64Array
343
+ lows: Float64Array
344
+ closes: Float64Array
345
+ } {
346
+ return {
347
+ opens: layout.opens,
348
+ highs: layout.highs,
349
+ lows: layout.lows,
350
+ closes: layout.closes,
351
+ }
352
+ }