@363045841yyt/klinechart-core 0.7.9 → 0.7.10

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 (234) hide show
  1. package/dist/engine/chart.d.ts.map +1 -1
  2. package/dist/engine/chart.js +39 -229
  3. package/dist/engine/chart.js.map +1 -1
  4. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts +10 -3
  5. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
  6. package/dist/engine/indicators/indicatorDefinitionRegistry.js +29 -2
  7. package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
  8. package/dist/engine/indicators/indicatorMetadata.d.ts +79 -2
  9. package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
  10. package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
  11. package/dist/engine/indicators/indicatorRegistry.d.ts +5 -0
  12. package/dist/engine/indicators/indicatorRegistry.d.ts.map +1 -1
  13. package/dist/engine/indicators/indicatorRegistry.js +49 -5
  14. package/dist/engine/indicators/indicatorRegistry.js.map +1 -1
  15. package/dist/engine/indicators/registerBuiltins.d.ts +42 -0
  16. package/dist/engine/indicators/registerBuiltins.d.ts.map +1 -0
  17. package/dist/engine/indicators/registerBuiltins.js +46 -0
  18. package/dist/engine/indicators/registerBuiltins.js.map +1 -0
  19. package/dist/engine/indicators/scheduler.d.ts +2 -2
  20. package/dist/engine/indicators/scheduler.d.ts.map +1 -1
  21. package/dist/engine/indicators/scheduler.js +41 -11
  22. package/dist/engine/indicators/scheduler.js.map +1 -1
  23. package/dist/engine/indicators/stateComposer.d.ts +6 -4
  24. package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
  25. package/dist/engine/indicators/stateComposer.js +80 -984
  26. package/dist/engine/indicators/stateComposer.js.map +1 -1
  27. package/dist/engine/indicators/visibleStateComposers.d.ts +145 -0
  28. package/dist/engine/indicators/visibleStateComposers.d.ts.map +1 -0
  29. package/dist/engine/indicators/visibleStateComposers.js +469 -0
  30. package/dist/engine/indicators/visibleStateComposers.js.map +1 -0
  31. package/dist/engine/renderers/Indicator/atr.d.ts.map +1 -1
  32. package/dist/engine/renderers/Indicator/atr.js +8 -0
  33. package/dist/engine/renderers/Indicator/atr.js.map +1 -1
  34. package/dist/engine/renderers/Indicator/boll.d.ts.map +1 -1
  35. package/dist/engine/renderers/Indicator/boll.js +47 -0
  36. package/dist/engine/renderers/Indicator/boll.js.map +1 -1
  37. package/dist/engine/renderers/Indicator/cci.d.ts.map +1 -1
  38. package/dist/engine/renderers/Indicator/cci.js +8 -1
  39. package/dist/engine/renderers/Indicator/cci.js.map +1 -1
  40. package/dist/engine/renderers/Indicator/chaikinVol.d.ts.map +1 -1
  41. package/dist/engine/renderers/Indicator/chaikinVol.js +7 -1
  42. package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
  43. package/dist/engine/renderers/Indicator/cmf.d.ts.map +1 -1
  44. package/dist/engine/renderers/Indicator/cmf.js +7 -1
  45. package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
  46. package/dist/engine/renderers/Indicator/dema.d.ts.map +1 -1
  47. package/dist/engine/renderers/Indicator/dema.js +8 -1
  48. package/dist/engine/renderers/Indicator/dema.js.map +1 -1
  49. package/dist/engine/renderers/Indicator/donchian.d.ts.map +1 -1
  50. package/dist/engine/renderers/Indicator/donchian.js +8 -1
  51. package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
  52. package/dist/engine/renderers/Indicator/ene.d.ts.map +1 -1
  53. package/dist/engine/renderers/Indicator/ene.js +45 -0
  54. package/dist/engine/renderers/Indicator/ene.js.map +1 -1
  55. package/dist/engine/renderers/Indicator/expma.d.ts.map +1 -1
  56. package/dist/engine/renderers/Indicator/expma.js +45 -0
  57. package/dist/engine/renderers/Indicator/expma.js.map +1 -1
  58. package/dist/engine/renderers/Indicator/fastk.d.ts.map +1 -1
  59. package/dist/engine/renderers/Indicator/fastk.js +8 -1
  60. package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
  61. package/dist/engine/renderers/Indicator/fib.d.ts.map +1 -1
  62. package/dist/engine/renderers/Indicator/fib.js +8 -1
  63. package/dist/engine/renderers/Indicator/fib.js.map +1 -1
  64. package/dist/engine/renderers/Indicator/hma.d.ts.map +1 -1
  65. package/dist/engine/renderers/Indicator/hma.js +8 -1
  66. package/dist/engine/renderers/Indicator/hma.js.map +1 -1
  67. package/dist/engine/renderers/Indicator/hv.d.ts.map +1 -1
  68. package/dist/engine/renderers/Indicator/hv.js +7 -0
  69. package/dist/engine/renderers/Indicator/hv.js.map +1 -1
  70. package/dist/engine/renderers/Indicator/ichimoku.d.ts.map +1 -1
  71. package/dist/engine/renderers/Indicator/ichimoku.js +8 -1
  72. package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
  73. package/dist/engine/renderers/Indicator/index.d.ts +7 -2
  74. package/dist/engine/renderers/Indicator/index.d.ts.map +1 -1
  75. package/dist/engine/renderers/Indicator/index.js +2 -114
  76. package/dist/engine/renderers/Indicator/index.js.map +1 -1
  77. package/dist/engine/renderers/Indicator/kama.d.ts.map +1 -1
  78. package/dist/engine/renderers/Indicator/kama.js +8 -1
  79. package/dist/engine/renderers/Indicator/kama.js.map +1 -1
  80. package/dist/engine/renderers/Indicator/keltner.d.ts.map +1 -1
  81. package/dist/engine/renderers/Indicator/keltner.js +8 -1
  82. package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
  83. package/dist/engine/renderers/Indicator/kst.d.ts.map +1 -1
  84. package/dist/engine/renderers/Indicator/kst.js +8 -0
  85. package/dist/engine/renderers/Indicator/kst.js.map +1 -1
  86. package/dist/engine/renderers/Indicator/ma.d.ts.map +1 -1
  87. package/dist/engine/renderers/Indicator/ma.js +63 -0
  88. package/dist/engine/renderers/Indicator/ma.js.map +1 -1
  89. package/dist/engine/renderers/Indicator/macd.d.ts.map +1 -1
  90. package/dist/engine/renderers/Indicator/macd.js +8 -1
  91. package/dist/engine/renderers/Indicator/macd.js.map +1 -1
  92. package/dist/engine/renderers/Indicator/mfi.d.ts.map +1 -1
  93. package/dist/engine/renderers/Indicator/mfi.js +7 -1
  94. package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
  95. package/dist/engine/renderers/Indicator/mom.d.ts.map +1 -1
  96. package/dist/engine/renderers/Indicator/mom.js +8 -0
  97. package/dist/engine/renderers/Indicator/mom.js.map +1 -1
  98. package/dist/engine/renderers/Indicator/obv.d.ts.map +1 -1
  99. package/dist/engine/renderers/Indicator/obv.js +7 -1
  100. package/dist/engine/renderers/Indicator/obv.js.map +1 -1
  101. package/dist/engine/renderers/Indicator/parkinson.d.ts.map +1 -1
  102. package/dist/engine/renderers/Indicator/parkinson.js +7 -0
  103. package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
  104. package/dist/engine/renderers/Indicator/pivot.d.ts.map +1 -1
  105. package/dist/engine/renderers/Indicator/pivot.js +8 -1
  106. package/dist/engine/renderers/Indicator/pivot.js.map +1 -1
  107. package/dist/engine/renderers/Indicator/pvt.d.ts.map +1 -1
  108. package/dist/engine/renderers/Indicator/pvt.js +7 -1
  109. package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
  110. package/dist/engine/renderers/Indicator/roc.d.ts.map +1 -1
  111. package/dist/engine/renderers/Indicator/roc.js +7 -1
  112. package/dist/engine/renderers/Indicator/roc.js.map +1 -1
  113. package/dist/engine/renderers/Indicator/rsi.d.ts.map +1 -1
  114. package/dist/engine/renderers/Indicator/rsi.js +8 -1
  115. package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
  116. package/dist/engine/renderers/Indicator/sar.d.ts.map +1 -1
  117. package/dist/engine/renderers/Indicator/sar.js +8 -1
  118. package/dist/engine/renderers/Indicator/sar.js.map +1 -1
  119. package/dist/engine/renderers/Indicator/stoch.d.ts.map +1 -1
  120. package/dist/engine/renderers/Indicator/stoch.js +8 -1
  121. package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
  122. package/dist/engine/renderers/Indicator/structure.d.ts.map +1 -1
  123. package/dist/engine/renderers/Indicator/structure.js +8 -1
  124. package/dist/engine/renderers/Indicator/structure.js.map +1 -1
  125. package/dist/engine/renderers/Indicator/subPaneConfig.d.ts +1 -1
  126. package/dist/engine/renderers/Indicator/subPaneConfig.d.ts.map +1 -1
  127. package/dist/engine/renderers/Indicator/subPaneConfig.js.map +1 -1
  128. package/dist/engine/renderers/Indicator/supertrend.d.ts.map +1 -1
  129. package/dist/engine/renderers/Indicator/supertrend.js +8 -1
  130. package/dist/engine/renderers/Indicator/supertrend.js.map +1 -1
  131. package/dist/engine/renderers/Indicator/tema.d.ts.map +1 -1
  132. package/dist/engine/renderers/Indicator/tema.js +8 -1
  133. package/dist/engine/renderers/Indicator/tema.js.map +1 -1
  134. package/dist/engine/renderers/Indicator/trix.d.ts.map +1 -1
  135. package/dist/engine/renderers/Indicator/trix.js +7 -0
  136. package/dist/engine/renderers/Indicator/trix.js.map +1 -1
  137. package/dist/engine/renderers/Indicator/vma.d.ts.map +1 -1
  138. package/dist/engine/renderers/Indicator/vma.js +7 -0
  139. package/dist/engine/renderers/Indicator/vma.js.map +1 -1
  140. package/dist/engine/renderers/Indicator/volumeProfile.d.ts.map +1 -1
  141. package/dist/engine/renderers/Indicator/volumeProfile.js +7 -1
  142. package/dist/engine/renderers/Indicator/volumeProfile.js.map +1 -1
  143. package/dist/engine/renderers/Indicator/vwap.d.ts.map +1 -1
  144. package/dist/engine/renderers/Indicator/vwap.js +7 -1
  145. package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
  146. package/dist/engine/renderers/Indicator/wma.d.ts.map +1 -1
  147. package/dist/engine/renderers/Indicator/wma.js +8 -1
  148. package/dist/engine/renderers/Indicator/wma.js.map +1 -1
  149. package/dist/engine/renderers/Indicator/wmsr.d.ts.map +1 -1
  150. package/dist/engine/renderers/Indicator/wmsr.js +8 -1
  151. package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
  152. package/dist/engine/renderers/Indicator/zones.d.ts.map +1 -1
  153. package/dist/engine/renderers/Indicator/zones.js +8 -1
  154. package/dist/engine/renderers/Indicator/zones.js.map +1 -1
  155. package/dist/engine/renderers/extremaMarkers.d.ts.map +1 -1
  156. package/dist/engine/renderers/extremaMarkers.js +0 -22
  157. package/dist/engine/renderers/extremaMarkers.js.map +1 -1
  158. package/dist/engine/renderers/subVolume.d.ts.map +1 -1
  159. package/dist/engine/renderers/subVolume.js +3 -0
  160. package/dist/engine/renderers/subVolume.js.map +1 -1
  161. package/dist/engine/subPaneManager.d.ts +1 -0
  162. package/dist/engine/subPaneManager.d.ts.map +1 -1
  163. package/dist/engine/subPaneManager.js +30 -237
  164. package/dist/engine/subPaneManager.js.map +1 -1
  165. package/dist/semantic/controller.d.ts +9 -4
  166. package/dist/semantic/controller.d.ts.map +1 -1
  167. package/dist/semantic/controller.js +57 -53
  168. package/dist/semantic/controller.js.map +1 -1
  169. package/dist/version.d.ts +1 -1
  170. package/dist/version.d.ts.map +1 -1
  171. package/dist/version.js +1 -1
  172. package/dist/version.js.map +1 -1
  173. package/package.json +1 -1
  174. package/src/engine/chart.ts +49 -229
  175. package/src/engine/indicators/__tests__/indicatorDefinitionRegistry.test.ts +59 -0
  176. package/src/engine/indicators/__tests__/indicatorRegistry.test.ts +62 -0
  177. package/src/engine/indicators/__tests__/registerBuiltins.test.ts +380 -0
  178. package/src/engine/indicators/__tests__/scheduler.test.ts +62 -32
  179. package/src/engine/indicators/__tests__/stateComposer.test.ts +444 -0
  180. package/src/engine/indicators/indicatorDefinitionRegistry.ts +49 -4
  181. package/src/engine/indicators/indicatorMetadata.ts +107 -2
  182. package/src/engine/indicators/indicatorRegistry.ts +54 -5
  183. package/src/engine/indicators/registerBuiltins.ts +47 -0
  184. package/src/engine/indicators/scheduler.ts +61 -13
  185. package/src/engine/indicators/stateComposer.ts +110 -1040
  186. package/src/engine/indicators/visibleStateComposers.ts +734 -0
  187. package/src/engine/renderers/Indicator/__tests__/createSubIndicatorRenderer.test.ts +61 -0
  188. package/src/engine/renderers/Indicator/atr.ts +9 -1
  189. package/src/engine/renderers/Indicator/boll.ts +53 -1
  190. package/src/engine/renderers/Indicator/cci.ts +9 -2
  191. package/src/engine/renderers/Indicator/chaikinVol.ts +8 -2
  192. package/src/engine/renderers/Indicator/cmf.ts +8 -2
  193. package/src/engine/renderers/Indicator/dema.ts +9 -2
  194. package/src/engine/renderers/Indicator/donchian.ts +9 -2
  195. package/src/engine/renderers/Indicator/ene.ts +51 -1
  196. package/src/engine/renderers/Indicator/expma.ts +51 -1
  197. package/src/engine/renderers/Indicator/fastk.ts +9 -2
  198. package/src/engine/renderers/Indicator/fib.ts +9 -2
  199. package/src/engine/renderers/Indicator/hma.ts +9 -2
  200. package/src/engine/renderers/Indicator/hv.ts +8 -1
  201. package/src/engine/renderers/Indicator/ichimoku.ts +9 -2
  202. package/src/engine/renderers/Indicator/index.ts +9 -121
  203. package/src/engine/renderers/Indicator/kama.ts +9 -2
  204. package/src/engine/renderers/Indicator/keltner.ts +9 -2
  205. package/src/engine/renderers/Indicator/kst.ts +9 -1
  206. package/src/engine/renderers/Indicator/ma.ts +69 -0
  207. package/src/engine/renderers/Indicator/macd.ts +9 -2
  208. package/src/engine/renderers/Indicator/mfi.ts +8 -2
  209. package/src/engine/renderers/Indicator/mom.ts +9 -1
  210. package/src/engine/renderers/Indicator/obv.ts +8 -2
  211. package/src/engine/renderers/Indicator/parkinson.ts +8 -1
  212. package/src/engine/renderers/Indicator/pivot.ts +9 -2
  213. package/src/engine/renderers/Indicator/pvt.ts +8 -2
  214. package/src/engine/renderers/Indicator/roc.ts +8 -2
  215. package/src/engine/renderers/Indicator/rsi.ts +9 -2
  216. package/src/engine/renderers/Indicator/sar.ts +9 -2
  217. package/src/engine/renderers/Indicator/stoch.ts +9 -2
  218. package/src/engine/renderers/Indicator/structure.ts +9 -2
  219. package/src/engine/renderers/Indicator/subPaneConfig.ts +1 -1
  220. package/src/engine/renderers/Indicator/supertrend.ts +9 -2
  221. package/src/engine/renderers/Indicator/tema.ts +9 -2
  222. package/src/engine/renderers/Indicator/trix.ts +8 -1
  223. package/src/engine/renderers/Indicator/vma.ts +8 -1
  224. package/src/engine/renderers/Indicator/volumeProfile.ts +8 -2
  225. package/src/engine/renderers/Indicator/vwap.ts +8 -2
  226. package/src/engine/renderers/Indicator/wma.ts +9 -2
  227. package/src/engine/renderers/Indicator/wmsr.ts +9 -2
  228. package/src/engine/renderers/Indicator/zones.ts +9 -2
  229. package/src/engine/renderers/extremaMarkers.ts +0 -24
  230. package/src/engine/renderers/subVolume.ts +3 -0
  231. package/src/engine/subPaneManager.ts +34 -274
  232. package/src/semantic/__tests__/controller.test.ts +84 -0
  233. package/src/semantic/controller.ts +69 -65
  234. package/src/version.ts +1 -1
@@ -0,0 +1,734 @@
1
+ import type { IndicatorVisibleStateComposer } from './indicatorMetadata'
2
+ import type { IndicatorSeriesBundle } from './workerProtocol'
3
+
4
+ type SparseIndicatorSeries = {
5
+ series: (number | undefined)[]
6
+ params: unknown
7
+ }
8
+
9
+ type SparseState = {
10
+ timestamp: number
11
+ series: (number | undefined)[]
12
+ params: unknown
13
+ valueMin: number
14
+ valueMax: number
15
+ visibleMin: number
16
+ visibleMax: number
17
+ }
18
+
19
+ function getSparseSeriesBundle(bundle: IndicatorSeriesBundle, bundleKey: string): SparseIndicatorSeries {
20
+ return (bundle as unknown as Record<string, SparseIndicatorSeries>)[bundleKey]!
21
+ }
22
+
23
+ function calcSparseExtremes(series: (number | undefined)[], range: { start: number; end: number }): { min: number; max: number } {
24
+ if (series.length === 0 || range.start >= series.length) {
25
+ return { min: Infinity, max: -Infinity }
26
+ }
27
+ let min = Infinity
28
+ let max = -Infinity
29
+ const end = Math.min(range.end, series.length)
30
+ for (let i = range.start; i < end; i++) {
31
+ const v = series[i]
32
+ if (v !== undefined) {
33
+ min = Math.min(min, v)
34
+ max = Math.max(max, v)
35
+ }
36
+ }
37
+ return { min, max }
38
+ }
39
+
40
+ function computePaddedBounds(
41
+ extremes: { min: number; max: number },
42
+ emptyState: Pick<SparseState, 'valueMin' | 'valueMax'>,
43
+ ): { valueMin: number; valueMax: number } {
44
+ if (!Number.isFinite(extremes.min) || !Number.isFinite(extremes.max)) {
45
+ return { valueMin: emptyState.valueMin, valueMax: emptyState.valueMax }
46
+ }
47
+
48
+ const range = extremes.max - extremes.min
49
+ const padding = range > 0 ? range * 0.05 : Math.max(1, Math.abs(extremes.max) * 0.05)
50
+ return { valueMin: extremes.min - padding, valueMax: extremes.max + padding }
51
+ }
52
+
53
+ export function createSparseVisibleStateComposer(
54
+ bundleKey: string,
55
+ emptyState: SparseState,
56
+ ): IndicatorVisibleStateComposer {
57
+ return ({ bundle, visibleRange, timestamp, active }) => {
58
+ const source = getSparseSeriesBundle(bundle, bundleKey)
59
+ if (!active) {
60
+ return {
61
+ ...emptyState,
62
+ timestamp,
63
+ series: source.series,
64
+ params: source.params,
65
+ }
66
+ }
67
+
68
+ const extremes = calcSparseExtremes(source.series, visibleRange)
69
+ const bounds = computePaddedBounds(extremes, emptyState)
70
+ return {
71
+ timestamp,
72
+ series: source.series,
73
+ params: source.params,
74
+ valueMin: bounds.valueMin,
75
+ valueMax: bounds.valueMax,
76
+ visibleMin: extremes.min,
77
+ visibleMax: extremes.max,
78
+ }
79
+ }
80
+ }
81
+
82
+ type RecordIndicatorSeries = {
83
+ series: Record<number, (number | undefined)[]>
84
+ enabledPeriods: number[]
85
+ params: unknown
86
+ }
87
+
88
+ function getRecordSeriesBundle(
89
+ bundle: IndicatorSeriesBundle,
90
+ bundleKey: string,
91
+ ): RecordIndicatorSeries {
92
+ return (bundle as unknown as Record<string, RecordIndicatorSeries>)[bundleKey]!
93
+ }
94
+
95
+ function calcRecordExtremes(
96
+ series: Record<number, (number | undefined)[]>,
97
+ range: { start: number; end: number },
98
+ ): { min: number; max: number } {
99
+ let min = Infinity
100
+ let max = -Infinity
101
+ for (const key of Object.keys(series)) {
102
+ const arr = series[Number(key)]
103
+ if (!arr) continue
104
+ const end = Math.min(range.end, arr.length)
105
+ for (let i = range.start; i < end; i++) {
106
+ const v = arr[i]
107
+ if (v !== undefined) {
108
+ min = Math.min(min, v)
109
+ max = Math.max(max, v)
110
+ }
111
+ }
112
+ }
113
+ return { min, max }
114
+ }
115
+
116
+ export function createFixedRangeSparseVisibleStateComposer(
117
+ bundleKey: string,
118
+ emptyState: {
119
+ timestamp: number
120
+ series: (number | undefined)[]
121
+ params: unknown
122
+ valueMin: number
123
+ valueMax: number
124
+ visibleMin: number
125
+ visibleMax: number
126
+ },
127
+ ): IndicatorVisibleStateComposer {
128
+ return ({ bundle, visibleRange, timestamp, active }) => {
129
+ const source = getSparseSeriesBundle(bundle, bundleKey)
130
+ if (!active) {
131
+ return {
132
+ ...emptyState,
133
+ timestamp,
134
+ series: source.series,
135
+ params: source.params,
136
+ }
137
+ }
138
+
139
+ const extremes = calcSparseExtremes(source.series, visibleRange)
140
+ return {
141
+ timestamp,
142
+ series: source.series,
143
+ params: source.params,
144
+ valueMin: emptyState.valueMin,
145
+ valueMax: emptyState.valueMax,
146
+ visibleMin: extremes.min,
147
+ visibleMax: extremes.max,
148
+ }
149
+ }
150
+ }
151
+
152
+ export function createFixedRangeRecordVisibleStateComposer(
153
+ bundleKey: string,
154
+ emptyState: {
155
+ timestamp: number
156
+ series: Record<number, (number | undefined)[]>
157
+ enabledPeriods: number[]
158
+ params: unknown
159
+ valueMin: number
160
+ valueMax: number
161
+ visibleMin: number
162
+ visibleMax: number
163
+ },
164
+ ): IndicatorVisibleStateComposer {
165
+ return ({ bundle, visibleRange, timestamp, active }) => {
166
+ const source = getRecordSeriesBundle(bundle, bundleKey)
167
+ if (!active) {
168
+ return {
169
+ ...emptyState,
170
+ timestamp,
171
+ series: source.series,
172
+ enabledPeriods: source.enabledPeriods,
173
+ params: source.params,
174
+ }
175
+ }
176
+
177
+ const extremes = calcRecordExtremes(source.series, visibleRange)
178
+ return {
179
+ timestamp,
180
+ series: source.series,
181
+ enabledPeriods: source.enabledPeriods,
182
+ params: source.params,
183
+ valueMin: emptyState.valueMin,
184
+ valueMax: emptyState.valueMax,
185
+ visibleMin: extremes.min,
186
+ visibleMax: extremes.max,
187
+ }
188
+ }
189
+ }
190
+
191
+ export function createFixedRangePointVisibleStateComposer<T extends object>(
192
+ bundleKey: string,
193
+ emptyState: {
194
+ timestamp: number
195
+ series: (T | undefined)[]
196
+ params: unknown
197
+ valueMin: number
198
+ valueMax: number
199
+ visibleMin: number
200
+ visibleMax: number
201
+ },
202
+ fields: readonly (keyof T)[],
203
+ ): IndicatorVisibleStateComposer {
204
+ return ({ bundle, visibleRange, timestamp, active }) => {
205
+ const source = getPointArraySeriesBundle<T>(bundle, bundleKey)
206
+ if (!active) {
207
+ return {
208
+ ...emptyState,
209
+ timestamp,
210
+ series: source.series,
211
+ params: source.params,
212
+ }
213
+ }
214
+
215
+ const extremes = calcPointArrayExtremes(source.series, fields, visibleRange)
216
+ return {
217
+ timestamp,
218
+ series: source.series,
219
+ params: source.params,
220
+ valueMin: emptyState.valueMin,
221
+ valueMax: emptyState.valueMax,
222
+ visibleMin: extremes.min,
223
+ visibleMax: extremes.max,
224
+ }
225
+ }
226
+ }
227
+
228
+ type DualSparseIndicatorSeries = {
229
+ series: (number | undefined)[]
230
+ signalSeries: (number | undefined)[]
231
+ params: unknown
232
+ }
233
+
234
+ function getDualSparseSeriesBundle(
235
+ bundle: IndicatorSeriesBundle,
236
+ bundleKey: string,
237
+ ): DualSparseIndicatorSeries {
238
+ return (bundle as unknown as Record<string, DualSparseIndicatorSeries>)[bundleKey]!
239
+ }
240
+
241
+ function getPointArraySeriesBundle<T extends object>(
242
+ bundle: IndicatorSeriesBundle,
243
+ bundleKey: string,
244
+ ): { series: (T | undefined)[]; params: unknown } {
245
+ return (bundle as unknown as Record<string, { series: (T | undefined)[]; params: unknown }>)[bundleKey]!
246
+ }
247
+
248
+ function calcPointArrayExtremes<T extends object>(
249
+ series: (T | undefined)[],
250
+ fields: readonly (keyof T)[],
251
+ range: { start: number; end: number },
252
+ ): { min: number; max: number } {
253
+ if (series.length === 0 || range.start >= series.length) {
254
+ return { min: Infinity, max: -Infinity }
255
+ }
256
+ let min = Infinity
257
+ let max = -Infinity
258
+ const end = Math.min(range.end, series.length)
259
+ for (let i = range.start; i < end; i++) {
260
+ const p = series[i]
261
+ if (p) {
262
+ for (const field of fields) {
263
+ const v = p[field]
264
+ if (typeof v === 'number' && Number.isFinite(v)) {
265
+ min = Math.min(min, v)
266
+ max = Math.max(max, v)
267
+ }
268
+ }
269
+ }
270
+ }
271
+ return { min, max }
272
+ }
273
+
274
+ function computeMAFamilyBounds(
275
+ extremes: { min: number; max: number } | null,
276
+ emptyBounds: { valueMin: number; valueMax: number },
277
+ ): { valueMin: number; valueMax: number } {
278
+ if (!extremes || !Number.isFinite(extremes.min) || !Number.isFinite(extremes.max)) {
279
+ return { valueMin: emptyBounds.valueMin, valueMax: emptyBounds.valueMax }
280
+ }
281
+ const range = extremes.max - extremes.min
282
+ const padding = range > 0 ? range * 0.05 : Math.max(1, Math.abs(extremes.max) * 0.05)
283
+ return { valueMin: extremes.min - padding, valueMax: extremes.max + padding }
284
+ }
285
+
286
+ export function createPaddedSparseVisibleStateComposer(
287
+ bundleKey: string,
288
+ emptyState: {
289
+ timestamp: number
290
+ series: (number | undefined)[]
291
+ params: unknown
292
+ valueMin: number
293
+ valueMax: number
294
+ visibleMin: number
295
+ visibleMax: number
296
+ },
297
+ ): IndicatorVisibleStateComposer {
298
+ return ({ bundle, visibleRange, timestamp, active }) => {
299
+ const source = getSparseSeriesBundle(bundle, bundleKey)
300
+ if (!active) {
301
+ return {
302
+ ...emptyState,
303
+ timestamp,
304
+ series: source.series,
305
+ params: source.params,
306
+ }
307
+ }
308
+
309
+ const extremes = calcSparseExtremes(source.series, visibleRange)
310
+ const padding = Number.isFinite(extremes.max) && Number.isFinite(extremes.min)
311
+ ? Math.max(Math.abs(extremes.max), Math.abs(extremes.min)) * 0.1
312
+ : 0
313
+ return {
314
+ timestamp,
315
+ series: source.series,
316
+ params: source.params,
317
+ valueMin: Number.isFinite(extremes.min) ? extremes.min - padding : emptyState.valueMin,
318
+ valueMax: Number.isFinite(extremes.max) ? extremes.max + padding : emptyState.valueMax,
319
+ visibleMin: extremes.min,
320
+ visibleMax: extremes.max,
321
+ }
322
+ }
323
+ }
324
+
325
+ export function createPaddedPointVisibleStateComposer<T extends object>(
326
+ bundleKey: string,
327
+ emptyState: {
328
+ timestamp: number
329
+ series: (T | undefined)[]
330
+ params: unknown
331
+ valueMin: number
332
+ valueMax: number
333
+ visibleMin: number
334
+ visibleMax: number
335
+ },
336
+ fields: readonly (keyof T)[],
337
+ ): IndicatorVisibleStateComposer {
338
+ return ({ bundle, visibleRange, timestamp, active }) => {
339
+ const source = getPointArraySeriesBundle<T>(bundle, bundleKey)
340
+ if (!active) {
341
+ return {
342
+ ...emptyState,
343
+ timestamp,
344
+ series: source.series,
345
+ params: source.params,
346
+ }
347
+ }
348
+
349
+ const extremes = calcPointArrayExtremes(source.series, fields, visibleRange)
350
+ const range = extremes.max - extremes.min
351
+ const padding = range > 0 ? range * 0.1 : 0
352
+ return {
353
+ timestamp,
354
+ series: source.series,
355
+ params: source.params,
356
+ valueMin: Number.isFinite(extremes.min) ? extremes.min - padding : emptyState.valueMin,
357
+ valueMax: Number.isFinite(extremes.max) ? extremes.max + padding : emptyState.valueMax,
358
+ visibleMin: extremes.min,
359
+ visibleMax: extremes.max,
360
+ }
361
+ }
362
+ }
363
+
364
+ export function createNonNegativeSparseVisibleStateComposer(
365
+ bundleKey: string,
366
+ emptyState: {
367
+ timestamp: number
368
+ series: (number | undefined)[]
369
+ params: unknown
370
+ valueMin: number
371
+ valueMax: number
372
+ visibleMin: number
373
+ visibleMax: number
374
+ },
375
+ ): IndicatorVisibleStateComposer {
376
+ return ({ bundle, visibleRange, timestamp, active }) => {
377
+ const source = getSparseSeriesBundle(bundle, bundleKey)
378
+ if (!active) {
379
+ return {
380
+ ...emptyState,
381
+ timestamp,
382
+ series: source.series,
383
+ params: source.params,
384
+ }
385
+ }
386
+
387
+ const extremes = calcSparseExtremes(source.series, visibleRange)
388
+ const valueMax = Number.isFinite(extremes.max) ? extremes.max * 1.1 : emptyState.valueMax
389
+ return {
390
+ timestamp,
391
+ series: source.series,
392
+ params: source.params,
393
+ valueMin: 0,
394
+ valueMax,
395
+ visibleMin: extremes.min,
396
+ visibleMax: extremes.max,
397
+ }
398
+ }
399
+ }
400
+
401
+ export function createMACDVisibleStateComposer(
402
+ bundleKey: string,
403
+ emptyState: {
404
+ timestamp: number
405
+ series: { dif: number; dea: number; macd: number }[]
406
+ params: unknown
407
+ valueMin: number
408
+ valueMax: number
409
+ visibleMin: number
410
+ visibleMax: number
411
+ },
412
+ ): IndicatorVisibleStateComposer {
413
+ return ({ bundle, visibleRange, timestamp, active }) => {
414
+ const source = getPointArraySeriesBundle<{ dif: number; dea: number; macd: number }>(bundle, bundleKey)
415
+ if (!active) {
416
+ return {
417
+ ...emptyState,
418
+ timestamp,
419
+ series: source.series,
420
+ params: source.params,
421
+ }
422
+ }
423
+
424
+ const extremes = calcPointArrayExtremes(source.series, ['dif', 'dea', 'macd'], visibleRange)
425
+ const padding = Number.isFinite(extremes.max) && Number.isFinite(extremes.min)
426
+ ? Math.max(Math.abs(extremes.max), Math.abs(extremes.min)) * 0.1
427
+ : 0
428
+
429
+ const latestIndex = visibleRange.end - 1
430
+ const latestPoint = latestIndex >= 0 && latestIndex < source.series.length
431
+ ? source.series[latestIndex]
432
+ : null
433
+
434
+ return {
435
+ timestamp,
436
+ series: source.series,
437
+ params: source.params,
438
+ valueMin: Number.isFinite(extremes.min) ? extremes.min - padding : emptyState.valueMin,
439
+ valueMax: Number.isFinite(extremes.max) ? extremes.max + padding : emptyState.valueMax,
440
+ visibleMin: extremes.min,
441
+ visibleMax: extremes.max,
442
+ latestValues: latestPoint ? {
443
+ dif: latestPoint.dif,
444
+ dea: latestPoint.dea,
445
+ macd: latestPoint.macd,
446
+ } : undefined,
447
+ }
448
+ }
449
+ }
450
+
451
+ export function createDualSparseVisibleStateComposer(
452
+ bundleKey: string,
453
+ emptyState: {
454
+ timestamp: number
455
+ series: (number | undefined)[]
456
+ signalSeries: (number | undefined)[]
457
+ params: unknown
458
+ valueMin: number
459
+ valueMax: number
460
+ visibleMin: number
461
+ visibleMax: number
462
+ },
463
+ ): IndicatorVisibleStateComposer {
464
+ return ({ bundle, visibleRange, timestamp, active }) => {
465
+ const source = getDualSparseSeriesBundle(bundle, bundleKey)
466
+ if (!active) {
467
+ return {
468
+ ...emptyState,
469
+ timestamp,
470
+ series: source.series,
471
+ signalSeries: source.signalSeries,
472
+ params: source.params,
473
+ }
474
+ }
475
+
476
+ const seriesExtremes = calcSparseExtremes(source.series, visibleRange)
477
+ const signalExtremes = calcSparseExtremes(source.signalSeries, visibleRange)
478
+
479
+ const combinedMin = Math.min(seriesExtremes.min, signalExtremes.min)
480
+ const combinedMax = Math.max(seriesExtremes.max, signalExtremes.max)
481
+
482
+ let valueMin = emptyState.valueMin
483
+ let valueMax = emptyState.valueMax
484
+ if (Number.isFinite(combinedMin) && Number.isFinite(combinedMax)) {
485
+ const range = combinedMax - combinedMin
486
+ const padding = range > 0 ? range * 0.05 : Math.max(1, Math.abs(combinedMax) * 0.05)
487
+ valueMin = combinedMin - padding
488
+ valueMax = combinedMax + padding
489
+ }
490
+
491
+ return {
492
+ timestamp,
493
+ series: source.series,
494
+ signalSeries: source.signalSeries,
495
+ params: source.params,
496
+ valueMin,
497
+ valueMax,
498
+ visibleMin: combinedMin,
499
+ visibleMax: combinedMax,
500
+ }
501
+ }
502
+ }
503
+
504
+ export function createValuePointVisibleStateComposer<T extends object>(
505
+ bundleKey: string,
506
+ emptyState: {
507
+ timestamp: number
508
+ series: (T | undefined)[]
509
+ params: unknown
510
+ valueMin: number
511
+ valueMax: number
512
+ visibleMin: number
513
+ visibleMax: number
514
+ },
515
+ fields: readonly (keyof T)[],
516
+ ): IndicatorVisibleStateComposer {
517
+ return ({ bundle, visibleRange, timestamp, active }) => {
518
+ const source = getPointArraySeriesBundle<T>(bundle, bundleKey)
519
+ if (!active) {
520
+ return {
521
+ ...emptyState,
522
+ timestamp,
523
+ series: source.series,
524
+ params: source.params,
525
+ }
526
+ }
527
+
528
+ const extremes = calcPointArrayExtremes(source.series, fields, visibleRange)
529
+ const bounds = computeMAFamilyBounds(
530
+ Number.isFinite(extremes.min) && Number.isFinite(extremes.max) ? extremes : null,
531
+ emptyState,
532
+ )
533
+ return {
534
+ timestamp,
535
+ series: source.series,
536
+ params: source.params,
537
+ valueMin: bounds.valueMin,
538
+ valueMax: bounds.valueMax,
539
+ visibleMin: extremes.min,
540
+ visibleMax: extremes.max,
541
+ }
542
+ }
543
+ }
544
+
545
+ export function createBandVisibleStateComposer<T extends object>(
546
+ bundleKey: string,
547
+ emptyState: {
548
+ timestamp: number
549
+ series: (T | undefined)[]
550
+ params: unknown
551
+ valueMin: number
552
+ valueMax: number
553
+ visibleMin: number
554
+ visibleMax: number
555
+ },
556
+ minField: keyof T,
557
+ maxField: keyof T,
558
+ ): IndicatorVisibleStateComposer {
559
+ return ({ bundle, visibleRange, timestamp, active }) => {
560
+ const source = getPointArraySeriesBundle<T>(bundle, bundleKey)
561
+ if (!active) {
562
+ return {
563
+ ...emptyState,
564
+ timestamp,
565
+ series: source.series,
566
+ params: source.params,
567
+ }
568
+ }
569
+
570
+ const minExtremes = calcPointArrayExtremes(source.series, [minField], visibleRange)
571
+ const maxExtremes = calcPointArrayExtremes(source.series, [maxField], visibleRange)
572
+ const extremes = {
573
+ min: minExtremes.min,
574
+ max: maxExtremes.max,
575
+ }
576
+ const bounds = computeMAFamilyBounds(
577
+ Number.isFinite(extremes.min) && Number.isFinite(extremes.max) ? extremes : null,
578
+ emptyState,
579
+ )
580
+ return {
581
+ timestamp,
582
+ series: source.series,
583
+ params: source.params,
584
+ valueMin: bounds.valueMin,
585
+ valueMax: bounds.valueMax,
586
+ visibleMin: extremes.min,
587
+ visibleMax: extremes.max,
588
+ }
589
+ }
590
+ }
591
+
592
+ export function createExactRangePointVisibleStateComposer<T extends object>(
593
+ bundleKey: string,
594
+ emptyState: {
595
+ timestamp: number
596
+ series: (T | undefined)[]
597
+ params: unknown
598
+ valueMin: number
599
+ valueMax: number
600
+ visibleMin: number
601
+ visibleMax: number
602
+ },
603
+ fields: readonly (keyof T)[],
604
+ ): IndicatorVisibleStateComposer {
605
+ return ({ bundle, visibleRange, timestamp, active }) => {
606
+ const source = getPointArraySeriesBundle<T>(bundle, bundleKey)
607
+ if (!active) {
608
+ return {
609
+ ...emptyState,
610
+ timestamp,
611
+ series: source.series,
612
+ params: source.params,
613
+ }
614
+ }
615
+
616
+ const extremes = calcPointArrayExtremes(source.series, fields, visibleRange)
617
+ return {
618
+ timestamp,
619
+ series: source.series,
620
+ params: source.params,
621
+ valueMin: extremes.min,
622
+ valueMax: extremes.max,
623
+ visibleMin: extremes.min,
624
+ visibleMax: extremes.max,
625
+ }
626
+ }
627
+ }
628
+
629
+ export function createFixedUnitVisibleStateComposer(
630
+ bundleKey: string,
631
+ emptyState: {
632
+ timestamp: number
633
+ series: unknown
634
+ params: unknown
635
+ valueMin: number
636
+ valueMax: number
637
+ visibleMin: number
638
+ visibleMax: number
639
+ },
640
+ ): IndicatorVisibleStateComposer {
641
+ return ({ bundle, timestamp, active }) => {
642
+ const source = (bundle as unknown as Record<string, { series: unknown; params: unknown }>)[bundleKey]!
643
+ if (!active) {
644
+ return {
645
+ ...emptyState,
646
+ timestamp,
647
+ series: source.series,
648
+ params: source.params,
649
+ }
650
+ }
651
+
652
+ return {
653
+ timestamp,
654
+ series: source.series,
655
+ params: source.params,
656
+ valueMin: 0,
657
+ valueMax: 1,
658
+ visibleMin: 0,
659
+ visibleMax: 1,
660
+ }
661
+ }
662
+ }
663
+
664
+ export function createCCIVisibleStateComposer(
665
+ bundleKey: string,
666
+ emptyState: {
667
+ timestamp: number
668
+ series: (number | undefined)[]
669
+ params: unknown
670
+ valueMin: number
671
+ valueMax: number
672
+ visibleMin: number
673
+ visibleMax: number
674
+ },
675
+ ): IndicatorVisibleStateComposer {
676
+ return ({ bundle, visibleRange, timestamp, active }) => {
677
+ const source = getSparseSeriesBundle(bundle, bundleKey)
678
+ if (!active) {
679
+ return {
680
+ ...emptyState,
681
+ timestamp,
682
+ series: source.series,
683
+ params: source.params,
684
+ }
685
+ }
686
+
687
+ const extremes = calcSparseExtremes(source.series, visibleRange)
688
+ return {
689
+ timestamp,
690
+ series: source.series,
691
+ params: source.params,
692
+ valueMin: Math.min(extremes.min, emptyState.valueMin),
693
+ valueMax: Math.max(extremes.max, emptyState.valueMax),
694
+ visibleMin: extremes.min,
695
+ visibleMax: extremes.max,
696
+ }
697
+ }
698
+ }
699
+
700
+ export function createVolumeProfileVisibleStateComposer(
701
+ bundleKey: string,
702
+ emptyState: {
703
+ timestamp: number
704
+ series: unknown
705
+ params: unknown
706
+ valueMin: number
707
+ valueMax: number
708
+ visibleMin: number
709
+ visibleMax: number
710
+ },
711
+ ): IndicatorVisibleStateComposer {
712
+ return ({ bundle, timestamp, active }) => {
713
+ const source = (bundle as unknown as Record<string, { series: { bins: { priceLow: number; priceHigh: number }[]; val: number; vah: number; poc: number }; params: unknown }>)[bundleKey]!
714
+ if (!active) {
715
+ return {
716
+ ...emptyState,
717
+ timestamp,
718
+ series: source.series,
719
+ params: source.params,
720
+ }
721
+ }
722
+
723
+ const bins = source.series.bins
724
+ return {
725
+ timestamp,
726
+ series: source.series,
727
+ params: source.params,
728
+ valueMin: bins[0]?.priceLow ?? 0,
729
+ valueMax: bins[bins.length - 1]?.priceHigh ?? 1,
730
+ visibleMin: source.series.val,
731
+ visibleMax: source.series.vah,
732
+ }
733
+ }
734
+ }