@363045841yyt/klinechart-core 0.7.12-alpha.1 → 0.7.13

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 (277) hide show
  1. package/dist/controllers/createIndicatorSelectorController.d.ts.map +1 -1
  2. package/dist/controllers/createIndicatorSelectorController.js +4 -6
  3. package/dist/controllers/createIndicatorSelectorController.js.map +1 -1
  4. package/dist/controllers/index.d.ts +0 -1
  5. package/dist/controllers/index.d.ts.map +1 -1
  6. package/dist/controllers/index.js +0 -1
  7. package/dist/controllers/index.js.map +1 -1
  8. package/dist/engine/chart.d.ts +6 -2
  9. package/dist/engine/chart.d.ts.map +1 -1
  10. package/dist/engine/chart.js +26 -23
  11. package/dist/engine/chart.js.map +1 -1
  12. package/dist/engine/indicators/indicator.worker.js +32 -4
  13. package/dist/engine/indicators/indicator.worker.js.map +1 -1
  14. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts +5 -3
  15. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
  16. package/dist/engine/indicators/indicatorDefinitionRegistry.js +26 -1
  17. package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
  18. package/dist/engine/indicators/indicatorMetadata.d.ts +54 -1
  19. package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
  20. package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
  21. package/dist/engine/indicators/indicatorRuntime.d.ts +12 -117
  22. package/dist/engine/indicators/indicatorRuntime.d.ts.map +1 -1
  23. package/dist/engine/indicators/indicatorRuntime.js +124 -1308
  24. package/dist/engine/indicators/indicatorRuntime.js.map +1 -1
  25. package/dist/engine/indicators/scheduler.d.ts +19 -143
  26. package/dist/engine/indicators/scheduler.d.ts.map +1 -1
  27. package/dist/engine/indicators/scheduler.js +81 -434
  28. package/dist/engine/indicators/scheduler.js.map +1 -1
  29. package/dist/engine/indicators/stateComposer.d.ts.map +1 -1
  30. package/dist/engine/indicators/stateComposer.js +15 -47
  31. package/dist/engine/indicators/stateComposer.js.map +1 -1
  32. package/dist/engine/indicators/workerProtocol.d.ts +12 -36
  33. package/dist/engine/indicators/workerProtocol.d.ts.map +1 -1
  34. package/dist/engine/indicators/workerProtocol.js.map +1 -1
  35. package/dist/engine/renderers/Indicator/atr.d.ts +2 -1
  36. package/dist/engine/renderers/Indicator/atr.d.ts.map +1 -1
  37. package/dist/engine/renderers/Indicator/atr.js +11 -9
  38. package/dist/engine/renderers/Indicator/atr.js.map +1 -1
  39. package/dist/engine/renderers/Indicator/boll.d.ts +4 -1
  40. package/dist/engine/renderers/Indicator/boll.d.ts.map +1 -1
  41. package/dist/engine/renderers/Indicator/boll.js +24 -10
  42. package/dist/engine/renderers/Indicator/boll.js.map +1 -1
  43. package/dist/engine/renderers/Indicator/cci.d.ts +2 -1
  44. package/dist/engine/renderers/Indicator/cci.d.ts.map +1 -1
  45. package/dist/engine/renderers/Indicator/cci.js +12 -10
  46. package/dist/engine/renderers/Indicator/cci.js.map +1 -1
  47. package/dist/engine/renderers/Indicator/chaikinVol.d.ts +4 -1
  48. package/dist/engine/renderers/Indicator/chaikinVol.d.ts.map +1 -1
  49. package/dist/engine/renderers/Indicator/chaikinVol.js +21 -7
  50. package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
  51. package/dist/engine/renderers/Indicator/cmf.d.ts +4 -1
  52. package/dist/engine/renderers/Indicator/cmf.d.ts.map +1 -1
  53. package/dist/engine/renderers/Indicator/cmf.js +20 -7
  54. package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
  55. package/dist/engine/renderers/Indicator/dema.d.ts +2 -0
  56. package/dist/engine/renderers/Indicator/dema.d.ts.map +1 -1
  57. package/dist/engine/renderers/Indicator/dema.js +19 -8
  58. package/dist/engine/renderers/Indicator/dema.js.map +1 -1
  59. package/dist/engine/renderers/Indicator/donchian.d.ts +4 -1
  60. package/dist/engine/renderers/Indicator/donchian.d.ts.map +1 -1
  61. package/dist/engine/renderers/Indicator/donchian.js +20 -8
  62. package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
  63. package/dist/engine/renderers/Indicator/ene.d.ts +12 -1
  64. package/dist/engine/renderers/Indicator/ene.d.ts.map +1 -1
  65. package/dist/engine/renderers/Indicator/ene.js +25 -11
  66. package/dist/engine/renderers/Indicator/ene.js.map +1 -1
  67. package/dist/engine/renderers/Indicator/expma.d.ts +4 -1
  68. package/dist/engine/renderers/Indicator/expma.d.ts.map +1 -1
  69. package/dist/engine/renderers/Indicator/expma.js +22 -9
  70. package/dist/engine/renderers/Indicator/expma.js.map +1 -1
  71. package/dist/engine/renderers/Indicator/fastk.d.ts +2 -1
  72. package/dist/engine/renderers/Indicator/fastk.d.ts.map +1 -1
  73. package/dist/engine/renderers/Indicator/fastk.js +12 -10
  74. package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
  75. package/dist/engine/renderers/Indicator/fib.d.ts +2 -0
  76. package/dist/engine/renderers/Indicator/fib.d.ts.map +1 -1
  77. package/dist/engine/renderers/Indicator/fib.js +26 -8
  78. package/dist/engine/renderers/Indicator/fib.js.map +1 -1
  79. package/dist/engine/renderers/Indicator/hma.d.ts +2 -0
  80. package/dist/engine/renderers/Indicator/hma.d.ts.map +1 -1
  81. package/dist/engine/renderers/Indicator/hma.js +19 -8
  82. package/dist/engine/renderers/Indicator/hma.js.map +1 -1
  83. package/dist/engine/renderers/Indicator/hv.d.ts +4 -1
  84. package/dist/engine/renderers/Indicator/hv.d.ts.map +1 -1
  85. package/dist/engine/renderers/Indicator/hv.js +18 -8
  86. package/dist/engine/renderers/Indicator/hv.js.map +1 -1
  87. package/dist/engine/renderers/Indicator/ichimoku.d.ts +4 -1
  88. package/dist/engine/renderers/Indicator/ichimoku.d.ts.map +1 -1
  89. package/dist/engine/renderers/Indicator/ichimoku.js +27 -8
  90. package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
  91. package/dist/engine/renderers/Indicator/index.d.ts +27 -28
  92. package/dist/engine/renderers/Indicator/index.d.ts.map +1 -1
  93. package/dist/engine/renderers/Indicator/index.js +27 -28
  94. package/dist/engine/renderers/Indicator/index.js.map +1 -1
  95. package/dist/engine/renderers/Indicator/kama.d.ts +2 -0
  96. package/dist/engine/renderers/Indicator/kama.d.ts.map +1 -1
  97. package/dist/engine/renderers/Indicator/kama.js +19 -8
  98. package/dist/engine/renderers/Indicator/kama.js.map +1 -1
  99. package/dist/engine/renderers/Indicator/keltner.d.ts +4 -1
  100. package/dist/engine/renderers/Indicator/keltner.d.ts.map +1 -1
  101. package/dist/engine/renderers/Indicator/keltner.js +20 -8
  102. package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
  103. package/dist/engine/renderers/Indicator/kst.d.ts +2 -1
  104. package/dist/engine/renderers/Indicator/kst.d.ts.map +1 -1
  105. package/dist/engine/renderers/Indicator/kst.js +13 -11
  106. package/dist/engine/renderers/Indicator/kst.js.map +1 -1
  107. package/dist/engine/renderers/Indicator/ma.d.ts +4 -1
  108. package/dist/engine/renderers/Indicator/ma.d.ts.map +1 -1
  109. package/dist/engine/renderers/Indicator/ma.js +36 -8
  110. package/dist/engine/renderers/Indicator/ma.js.map +1 -1
  111. package/dist/engine/renderers/Indicator/macd.d.ts +1 -1
  112. package/dist/engine/renderers/Indicator/macd.d.ts.map +1 -1
  113. package/dist/engine/renderers/Indicator/macd.js +9 -10
  114. package/dist/engine/renderers/Indicator/macd.js.map +1 -1
  115. package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
  116. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +38 -136
  117. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
  118. package/dist/engine/renderers/Indicator/mfi.d.ts +4 -1
  119. package/dist/engine/renderers/Indicator/mfi.d.ts.map +1 -1
  120. package/dist/engine/renderers/Indicator/mfi.js +20 -7
  121. package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
  122. package/dist/engine/renderers/Indicator/mom.d.ts +2 -1
  123. package/dist/engine/renderers/Indicator/mom.d.ts.map +1 -1
  124. package/dist/engine/renderers/Indicator/mom.js +12 -10
  125. package/dist/engine/renderers/Indicator/mom.js.map +1 -1
  126. package/dist/engine/renderers/Indicator/obv.d.ts +4 -1
  127. package/dist/engine/renderers/Indicator/obv.d.ts.map +1 -1
  128. package/dist/engine/renderers/Indicator/obv.js +19 -7
  129. package/dist/engine/renderers/Indicator/obv.js.map +1 -1
  130. package/dist/engine/renderers/Indicator/parkinson.d.ts +4 -1
  131. package/dist/engine/renderers/Indicator/parkinson.d.ts.map +1 -1
  132. package/dist/engine/renderers/Indicator/parkinson.js +18 -8
  133. package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
  134. package/dist/engine/renderers/Indicator/pivot.d.ts +2 -0
  135. package/dist/engine/renderers/Indicator/pivot.d.ts.map +1 -1
  136. package/dist/engine/renderers/Indicator/pivot.js +43 -8
  137. package/dist/engine/renderers/Indicator/pivot.js.map +1 -1
  138. package/dist/engine/renderers/Indicator/pvt.d.ts +4 -1
  139. package/dist/engine/renderers/Indicator/pvt.d.ts.map +1 -1
  140. package/dist/engine/renderers/Indicator/pvt.js +19 -7
  141. package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
  142. package/dist/engine/renderers/Indicator/roc.d.ts +4 -1
  143. package/dist/engine/renderers/Indicator/roc.d.ts.map +1 -1
  144. package/dist/engine/renderers/Indicator/roc.js +20 -7
  145. package/dist/engine/renderers/Indicator/roc.js.map +1 -1
  146. package/dist/engine/renderers/Indicator/rsi.d.ts +2 -1
  147. package/dist/engine/renderers/Indicator/rsi.d.ts.map +1 -1
  148. package/dist/engine/renderers/Indicator/rsi.js +14 -11
  149. package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
  150. package/dist/engine/renderers/Indicator/sar.d.ts +4 -1
  151. package/dist/engine/renderers/Indicator/sar.d.ts.map +1 -1
  152. package/dist/engine/renderers/Indicator/sar.js +18 -8
  153. package/dist/engine/renderers/Indicator/sar.js.map +1 -1
  154. package/dist/engine/renderers/Indicator/stoch.d.ts +2 -1
  155. package/dist/engine/renderers/Indicator/stoch.d.ts.map +1 -1
  156. package/dist/engine/renderers/Indicator/stoch.js +10 -11
  157. package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
  158. package/dist/engine/renderers/Indicator/structure.d.ts +4 -1
  159. package/dist/engine/renderers/Indicator/structure.d.ts.map +1 -1
  160. package/dist/engine/renderers/Indicator/structure.js +21 -8
  161. package/dist/engine/renderers/Indicator/structure.js.map +1 -1
  162. package/dist/engine/renderers/Indicator/supertrend.d.ts +4 -1
  163. package/dist/engine/renderers/Indicator/supertrend.d.ts.map +1 -1
  164. package/dist/engine/renderers/Indicator/supertrend.js +18 -8
  165. package/dist/engine/renderers/Indicator/supertrend.js.map +1 -1
  166. package/dist/engine/renderers/Indicator/tema.d.ts +2 -0
  167. package/dist/engine/renderers/Indicator/tema.d.ts.map +1 -1
  168. package/dist/engine/renderers/Indicator/tema.js +19 -8
  169. package/dist/engine/renderers/Indicator/tema.js.map +1 -1
  170. package/dist/engine/renderers/Indicator/trix.d.ts +4 -1
  171. package/dist/engine/renderers/Indicator/trix.d.ts.map +1 -1
  172. package/dist/engine/renderers/Indicator/trix.js +30 -8
  173. package/dist/engine/renderers/Indicator/trix.js.map +1 -1
  174. package/dist/engine/renderers/Indicator/vma.d.ts +4 -1
  175. package/dist/engine/renderers/Indicator/vma.d.ts.map +1 -1
  176. package/dist/engine/renderers/Indicator/vma.js +20 -7
  177. package/dist/engine/renderers/Indicator/vma.js.map +1 -1
  178. package/dist/engine/renderers/Indicator/volumeProfile.d.ts +4 -1
  179. package/dist/engine/renderers/Indicator/volumeProfile.d.ts.map +1 -1
  180. package/dist/engine/renderers/Indicator/volumeProfile.js +28 -8
  181. package/dist/engine/renderers/Indicator/volumeProfile.js.map +1 -1
  182. package/dist/engine/renderers/Indicator/vwap.d.ts +4 -1
  183. package/dist/engine/renderers/Indicator/vwap.d.ts.map +1 -1
  184. package/dist/engine/renderers/Indicator/vwap.js +19 -7
  185. package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
  186. package/dist/engine/renderers/Indicator/wma.d.ts +2 -0
  187. package/dist/engine/renderers/Indicator/wma.d.ts.map +1 -1
  188. package/dist/engine/renderers/Indicator/wma.js +19 -8
  189. package/dist/engine/renderers/Indicator/wma.js.map +1 -1
  190. package/dist/engine/renderers/Indicator/wmsr.d.ts +2 -1
  191. package/dist/engine/renderers/Indicator/wmsr.d.ts.map +1 -1
  192. package/dist/engine/renderers/Indicator/wmsr.js +12 -10
  193. package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
  194. package/dist/engine/renderers/Indicator/zones.d.ts +2 -0
  195. package/dist/engine/renderers/Indicator/zones.d.ts.map +1 -1
  196. package/dist/engine/renderers/Indicator/zones.js +24 -8
  197. package/dist/engine/renderers/Indicator/zones.js.map +1 -1
  198. package/dist/engine/renderers/paneTitle.d.ts +4 -10
  199. package/dist/engine/renderers/paneTitle.d.ts.map +1 -1
  200. package/dist/engine/renderers/paneTitle.js +28 -6
  201. package/dist/engine/renderers/paneTitle.js.map +1 -1
  202. package/dist/engine/renderers/subVolume.js +0 -3
  203. package/dist/engine/renderers/subVolume.js.map +1 -1
  204. package/dist/version.d.ts +1 -1
  205. package/dist/version.d.ts.map +1 -1
  206. package/dist/version.js +1 -1
  207. package/dist/version.js.map +1 -1
  208. package/package.json +4 -4
  209. package/src/controllers/__tests__/indicatorSelector.test.ts +4 -3
  210. package/src/controllers/createIndicatorSelectorController.ts +4 -6
  211. package/src/controllers/index.ts +0 -4
  212. package/src/engine/chart.ts +30 -22
  213. package/src/engine/indicators/__tests__/registerBuiltins.test.ts +14 -23
  214. package/src/engine/indicators/__tests__/scheduler.test.ts +14 -14
  215. package/src/engine/indicators/__tests__/stateComposer.test.ts +7 -4
  216. package/src/engine/indicators/indicator.worker.ts +36 -4
  217. package/src/engine/indicators/indicatorDefinitionRegistry.ts +38 -3
  218. package/src/engine/indicators/indicatorMetadata.ts +66 -1
  219. package/src/engine/indicators/indicatorRuntime.ts +121 -1447
  220. package/src/engine/indicators/scheduler.ts +85 -489
  221. package/src/engine/indicators/stateComposer.ts +16 -60
  222. package/src/engine/indicators/workerProtocol.ts +14 -36
  223. package/src/engine/renderers/Indicator/atr.ts +14 -11
  224. package/src/engine/renderers/Indicator/boll.ts +33 -10
  225. package/src/engine/renderers/Indicator/cci.ts +15 -14
  226. package/src/engine/renderers/Indicator/chaikinVol.ts +29 -7
  227. package/src/engine/renderers/Indicator/cmf.ts +28 -7
  228. package/src/engine/renderers/Indicator/dema.ts +28 -9
  229. package/src/engine/renderers/Indicator/donchian.ts +28 -9
  230. package/src/engine/renderers/Indicator/ene.ts +34 -11
  231. package/src/engine/renderers/Indicator/expma.ts +31 -9
  232. package/src/engine/renderers/Indicator/fastk.ts +15 -14
  233. package/src/engine/renderers/Indicator/fib.ts +29 -9
  234. package/src/engine/renderers/Indicator/hma.ts +28 -9
  235. package/src/engine/renderers/Indicator/hv.ts +27 -8
  236. package/src/engine/renderers/Indicator/ichimoku.ts +31 -9
  237. package/src/engine/renderers/Indicator/index.ts +27 -28
  238. package/src/engine/renderers/Indicator/kama.ts +28 -9
  239. package/src/engine/renderers/Indicator/keltner.ts +28 -9
  240. package/src/engine/renderers/Indicator/kst.ts +16 -19
  241. package/src/engine/renderers/Indicator/ma.ts +44 -9
  242. package/src/engine/renderers/Indicator/macd.ts +31 -36
  243. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +43 -162
  244. package/src/engine/renderers/Indicator/mfi.ts +28 -7
  245. package/src/engine/renderers/Indicator/mom.ts +15 -14
  246. package/src/engine/renderers/Indicator/obv.ts +27 -7
  247. package/src/engine/renderers/Indicator/parkinson.ts +27 -8
  248. package/src/engine/renderers/Indicator/pivot.ts +47 -9
  249. package/src/engine/renderers/Indicator/pvt.ts +27 -7
  250. package/src/engine/renderers/Indicator/roc.ts +29 -7
  251. package/src/engine/renderers/Indicator/rsi.ts +14 -17
  252. package/src/engine/renderers/Indicator/sar.ts +26 -9
  253. package/src/engine/renderers/Indicator/stoch.ts +13 -16
  254. package/src/engine/renderers/Indicator/structure.ts +31 -8
  255. package/src/engine/renderers/Indicator/supertrend.ts +26 -9
  256. package/src/engine/renderers/Indicator/tema.ts +28 -9
  257. package/src/engine/renderers/Indicator/trix.ts +39 -8
  258. package/src/engine/renderers/Indicator/vma.ts +28 -7
  259. package/src/engine/renderers/Indicator/volumeProfile.ts +40 -8
  260. package/src/engine/renderers/Indicator/vwap.ts +27 -7
  261. package/src/engine/renderers/Indicator/wma.ts +28 -9
  262. package/src/engine/renderers/Indicator/wmsr.ts +15 -14
  263. package/src/engine/renderers/Indicator/zones.ts +29 -9
  264. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +142 -79
  265. package/src/engine/renderers/paneTitle.ts +43 -18
  266. package/src/engine/renderers/subVolume.ts +0 -2
  267. package/src/version.ts +1 -1
  268. package/dist/engine/renderers/Indicator/macdLegend.d.ts +0 -13
  269. package/dist/engine/renderers/Indicator/macdLegend.d.ts.map +0 -1
  270. package/dist/engine/renderers/Indicator/macdLegend.js +0 -116
  271. package/dist/engine/renderers/Indicator/macdLegend.js.map +0 -1
  272. package/dist/engine/renderers/Indicator/subPaneConfig.d.ts +0 -16
  273. package/dist/engine/renderers/Indicator/subPaneConfig.d.ts.map +0 -1
  274. package/dist/engine/renderers/Indicator/subPaneConfig.js +0 -194
  275. package/dist/engine/renderers/Indicator/subPaneConfig.js.map +0 -1
  276. package/src/engine/renderers/Indicator/macdLegend.ts +0 -141
  277. package/src/engine/renderers/Indicator/subPaneConfig.ts +0 -265
@@ -1,10 +1,12 @@
1
1
  import type { RendererPluginWithHost, RenderContext, PluginHost } from '../../../plugin'
2
2
  import { RENDERER_PRIORITY } from '../../../plugin'
3
+ import type { KLineData } from '../../../types/price'
3
4
  import type { KeltnerRenderState } from '../../indicators/keltnerState'
4
5
  import { createKeltnerStateKey, EMPTY_KELTNER_STATE } from '../../indicators/keltnerState'
5
6
  import { Indicator } from '../../indicators/indicatorDefinitionRegistry'
6
- import { resolveStateKey } from '../../indicators/indicatorMetadata'
7
+ import { resolveStateKey, type TitleInfo, type GetTitleInfoFn } from '../../indicators/indicatorMetadata'
7
8
  import type { IndicatorScheduler, KeltnerSchedulerConfig } from '../../indicators/scheduler'
9
+ import { calcKeltnerData } from '../../indicators/calculators'
8
10
  import { createBandVisibleStateComposer } from '../../indicators/visibleStateComposers'
9
11
 
10
12
  const KELTNER_UPPER_COLOR = '#7c3aed'
@@ -122,23 +124,40 @@ function drawLine(ctx: CanvasRenderingContext2D, pts: Point[], color: string): v
122
124
  ctx.stroke()
123
125
  }
124
126
 
127
+ export function getKeltnerTitleInfo(
128
+ _data: KLineData[],
129
+ index: number | null,
130
+ params: Record<string, number | boolean | string>,
131
+ host: PluginHost,
132
+ paneId: string,
133
+ ): TitleInfo | null {
134
+ if (index === null) return null
135
+ const state = host.getSharedState<KeltnerRenderState>(createKeltnerStateKey(paneId))
136
+ const p = state?.series[index]
137
+ if (!p) return null
138
+
139
+ return {
140
+ name: 'Keltner',
141
+ params: [(params.emaPeriod as number) ?? 20, (params.atrPeriod as number) ?? 10, (params.multiplier as number) ?? 2],
142
+ values: [
143
+ { label: 'Upper', value: p.upper, color: '#7c3aed' },
144
+ { label: 'Mid', value: p.middle, color: '#f59e0b' },
145
+ { label: 'Lower', value: p.lower, color: '#7c3aed' },
146
+ ],
147
+ }
148
+ }
149
+
125
150
  @Indicator({
126
151
  name: 'keltner',
127
152
  displayName: 'Keltner',
153
+ getTitleInfo: getKeltnerTitleInfo,
128
154
  category: 'main',
129
- stateKey: createKeltnerStateKey,
130
155
  defaultPaneId: 'main',
131
- paneIdField: 'keltnerPaneId',
132
156
  allowMainPane: true,
133
157
  mainPane: { rendererName: 'keltner_main', toActiveConfig: (params, active) => ({ ...params, showUpper: active, showMiddle: active, showLower: active }) },
134
158
  scale: { indicatorKey: 'keltner', label: 'Keltner', decimals: 2 },
135
159
  visibleState: { compose: createBandVisibleStateComposer('keltner', EMPTY_KELTNER_STATE, 'lower', 'upper') },
136
- updateConfig: (scheduler, params, paneId) => {
137
- (scheduler as IndicatorScheduler).updateKeltnerConfig(params as Partial<KeltnerSchedulerConfig>, paneId)
138
- },
139
- applyResult: (host, state, paneId) => {
140
- host.setSharedState(createKeltnerStateKey(paneId), state as any, 'indicator_scheduler')
141
- },
160
+ runtime: { defaultConfig:{emaPeriod:20,atrPeriod:10,multiplier:2,showUpper:true,showMiddle:true,showLower:true}, computeKey:'calcKeltnerData', compute:(data,c)=>calcKeltnerData(data,c.emaPeriod,c.atrPeriod,c.multiplier) },
142
161
  })
143
162
  class KeltnerDefinition {
144
163
  static rendererFactory = createKeltnerRendererPlugin
@@ -9,6 +9,8 @@ import { Indicator } from '../../indicators/indicatorDefinitionRegistry'
9
9
  import { resolveStateKey } from '../../indicators/indicatorMetadata'
10
10
  import type { IndicatorScheduler, KSTSchedulerConfig } from '../../indicators/scheduler'
11
11
  import { createKstScaleRendererPlugin } from './scale/kst_scale'
12
+ import { calcKSTData } from '../../indicators/calculators'
13
+ import type { KLineData } from '../../../types/price'
12
14
 
13
15
  type LinePoint = { x: number; y: number }
14
16
 
@@ -208,7 +210,7 @@ const { ctx, pane, range, scrollLeft, dpr, kLineCenters, lineWebGLSurface } = co
208
210
  },
209
211
 
210
212
  setConfig() {
211
- // no-op: 配置通过 scheduler.updateKSTConfig() 更新
213
+ // no-op: 配置通过 scheduler.updateIndicatorConfig() 更新
212
214
  },
213
215
  }
214
216
  }
@@ -259,18 +261,19 @@ function drawKSTLinesWithCanvas2D(
259
261
  * 获取 KST 标题信息(供 paneTitle 使用)
260
262
  */
261
263
  export function getKSTTitleInfo(
262
- index: number,
263
- roc1: number,
264
- roc2: number,
265
- roc3: number,
266
- roc4: number,
267
- signalPeriod: number,
264
+ _data: KLineData[],
265
+ index: number | null,
266
+ params: Record<string, number | boolean | string>,
268
267
  pluginHost: PluginHost,
269
- paneId: string = 'sub_KST',
270
- theme: 'light' | 'dark' = 'light',
271
- isAsiaMarket?: boolean
268
+ paneId: string,
272
269
  ): { name: string; params: number[]; values: Array<{ label: string; value: number; color: string }> } | null {
273
- const colors = resolveThemeColors(theme, isAsiaMarket)
270
+ if (index === null) return null
271
+ const roc1 = (params.roc1 as number) ?? 10
272
+ const roc2 = (params.roc2 as number) ?? 15
273
+ const roc3 = (params.roc3 as number) ?? 20
274
+ const roc4 = (params.roc4 as number) ?? 30
275
+ const signalPeriod = (params.signalPeriod as number) ?? 9
276
+ const colors = resolveThemeColors('light')
274
277
  const state = pluginHost.getSharedState<KSTRenderState>(createKSTStateKey(paneId))
275
278
  if (!state) return null
276
279
 
@@ -294,17 +297,11 @@ export function getKSTTitleInfo(
294
297
  name: 'kst',
295
298
  displayName: 'KST',
296
299
  category: 'oscillator',
297
- stateKey: createKSTStateKey,
298
300
  defaultPaneId: 'sub_KST',
299
- paneIdField: 'kstPaneId',
300
301
  scaleRendererFactory: createKstScaleRendererPlugin,
301
- updateConfig: (scheduler, params, paneId) => {
302
- (scheduler as IndicatorScheduler).updateKSTConfig(params as Partial<KSTSchedulerConfig>, paneId)
303
- },
304
302
  visibleState: { compose: createPaddedPointVisibleStateComposer('kst', EMPTY_KST_STATE, ['kst', 'signal'] as const) },
305
- applyResult: (host, state, paneId) => {
306
- host.setSharedState(createKSTStateKey(paneId), state as any, 'indicator_scheduler')
307
- },
303
+ getTitleInfo: getKSTTitleInfo,
304
+ runtime: { defaultConfig:{roc1:10,roc2:15,roc3:20,roc4:30,signalPeriod:9,showKST:true,showSignal:true}, computeKey:'calcKSTData', compute:(data,c)=>calcKSTData(data,c.roc1,c.roc2,c.roc3,c.roc4,c.signalPeriod) },
308
305
  })
309
306
  class KSTIndicatorDefinition {
310
307
  static rendererFactory = createKSTRendererPlugin
@@ -3,9 +3,10 @@ import { RENDERER_PRIORITY } from '../../../plugin'
3
3
  import { MA_STATE_KEY, type MARenderState } from '../../indicators/maState'
4
4
  import { Indicator } from '../../indicators/indicatorDefinitionRegistry'
5
5
  import { resolveStateKey } from '../../indicators/indicatorMetadata'
6
- import type { IndicatorPriceRangeComputer, IndicatorRenderStateComposer } from '../../indicators/indicatorMetadata'
6
+ import type { IndicatorPriceRangeComputer, IndicatorRenderStateComposer, GetTitleInfoFn, TitleInfo, TitleValueItem } from '../../indicators/indicatorMetadata'
7
+ import type { KLineData } from '../../../types/price'
7
8
  import type { IndicatorScheduler } from '../../indicators/scheduler'
8
- import type { MAFlags } from '../../indicators/calculators'
9
+ import { calcMAData, type MAFlags } from '../../indicators/calculators'
9
10
  import { alignToPhysicalPixelCenter } from '../../draw/pixelAlign'
10
11
  import { resolveThemeColors } from '../../../tokens'
11
12
 
@@ -93,11 +94,49 @@ function getMAStateKey(host: PluginHost | null): string | null {
93
94
  return resolveStateKey(meta.stateKey)
94
95
  }
95
96
 
97
+ export function getMATitleInfo(
98
+ _data: KLineData[],
99
+ index: number | null,
100
+ _params: Record<string, number | boolean | string>,
101
+ pluginHost: PluginHost,
102
+ _paneId: string,
103
+ ): TitleInfo | null {
104
+ if (index === null) return null
105
+
106
+ const stateKey = getMAStateKey(pluginHost)
107
+ if (!stateKey) return null
108
+
109
+ const state = pluginHost?.getSharedState<MARenderState>(stateKey)
110
+ if (!state || state.visibleMin > state.visibleMax) return null
111
+
112
+ const maColors: Record<number, string> = {
113
+ 5: '#f5a623',
114
+ 10: '#4ecdc4',
115
+ 20: '#45b7d1',
116
+ 30: '#96ceb4',
117
+ 60: '#dda0dd',
118
+ }
119
+
120
+ const values: TitleValueItem[] = []
121
+ for (const period of state.enabledPeriods) {
122
+ const series = state.series[period]
123
+ const value = series?.[index]
124
+ if (value === undefined) continue
125
+
126
+ values.push({
127
+ label: `MA${period}`,
128
+ value,
129
+ color: maColors[period] ?? '#f5a623',
130
+ })
131
+ }
132
+
133
+ return { name: 'MA', params: [], values }
134
+ }
135
+
96
136
  @Indicator({
97
137
  name: 'ma',
98
138
  displayName: 'MA',
99
139
  category: 'main',
100
- stateKey: MA_STATE_KEY,
101
140
  defaultPaneId: 'main',
102
141
  mainPane: {
103
142
  rendererName: 'ma',
@@ -111,9 +150,6 @@ function getMAStateKey(host: PluginHost | null): string | null {
111
150
  computePriceRange: computeMAPriceRange,
112
151
  composeRenderState: composeMARenderState,
113
152
  },
114
- updateConfig: (scheduler, params) => {
115
- (scheduler as IndicatorScheduler).updateMAConfig(params as MAFlags)
116
- },
117
153
  semantic: {
118
154
  apply: (chart, indicator) => {
119
155
  const periods = (indicator as { params?: { periods?: number[] } }).params?.periods ?? [5, 10, 20, 30, 60]
@@ -125,9 +161,8 @@ function getMAStateKey(host: PluginHost | null): string | null {
125
161
  chart.updateRendererConfig('ma', maFlags)
126
162
  },
127
163
  },
128
- applyResult: (host, state, _paneId) => {
129
- host.setSharedState(MA_STATE_KEY, state as any, 'ma_scheduler')
130
- },
164
+ runtime: { defaultConfig:{ma5:true,ma10:true,ma20:true,ma30:true,ma60:true}, computeKey:'calcMAData', compute:(data,c)=>{const p=[5,10,20,30,60];const r:Record<number,(number|undefined)[]>={};for(const o of p){if((c as any)['ma'+o])r[o]=calcMAData(data,o)}return r} },
165
+ getTitleInfo: getMATitleInfo,
131
166
  })
132
167
  class MADefinition {
133
168
  static rendererFactory = createMARendererPlugin
@@ -345,25 +345,25 @@ function drawMacdBarsWithCanvas2D(
345
345
  ctx.fillStyle = barUpColor
346
346
  for (let i = 0; i < barUpCount; i++) {
347
347
  const off = i * 4
348
- ctx.fillRect(barUpBuf[off], barUpBuf[off + 1], barUpBuf[off + 2], barUpBuf[off + 3])
348
+ ctx.fillRect(barUpBuf[off]!, barUpBuf[off + 1]!, barUpBuf[off + 2]!, barUpBuf[off + 3]!)
349
349
  }
350
350
 
351
351
  ctx.fillStyle = barUpLightColor
352
352
  for (let i = 0; i < barUpLightCount; i++) {
353
353
  const off = i * 4
354
- ctx.fillRect(barUpLightBuf[off], barUpLightBuf[off + 1], barUpLightBuf[off + 2], barUpLightBuf[off + 3])
354
+ ctx.fillRect(barUpLightBuf[off]!, barUpLightBuf[off + 1]!, barUpLightBuf[off + 2]!, barUpLightBuf[off + 3]!)
355
355
  }
356
356
 
357
357
  ctx.fillStyle = barDownColor
358
358
  for (let i = 0; i < barDownCount; i++) {
359
359
  const off = i * 4
360
- ctx.fillRect(barDownBuf[off], barDownBuf[off + 1], barDownBuf[off + 2], barDownBuf[off + 3])
360
+ ctx.fillRect(barDownBuf[off]!, barDownBuf[off + 1]!, barDownBuf[off + 2]!, barDownBuf[off + 3]!)
361
361
  }
362
362
 
363
363
  ctx.fillStyle = barDownLightColor
364
364
  for (let i = 0; i < barDownLightCount; i++) {
365
365
  const off = i * 4
366
- ctx.fillRect(barDownLightBuf[off], barDownLightBuf[off + 1], barDownLightBuf[off + 2], barDownLightBuf[off + 3])
366
+ ctx.fillRect(barDownLightBuf[off]!, barDownLightBuf[off + 1]!, barDownLightBuf[off + 2]!, barDownLightBuf[off + 3]!)
367
367
  }
368
368
 
369
369
  ctx.restore()
@@ -438,48 +438,43 @@ export function calcMACDAtIndex(
438
438
  * 从 pluginHost 获取已计算好的数据,避免重复计算
439
439
  */
440
440
  export function getMACDTitleInfo(
441
- index: number,
442
- fastPeriod: number,
443
- slowPeriod: number,
444
- signalPeriod: number,
445
- pluginHost: PluginHost,
446
- paneId: string = 'sub_MACD',
447
- theme: 'light' | 'dark' = 'light',
448
- isAsiaMarket?: boolean
441
+ _data: KLineData[],
442
+ index: number | null,
443
+ params: Record<string, number | boolean | string>,
444
+ pluginHost: PluginHost,
445
+ paneId: string,
449
446
  ): { name: string; params: number[]; values: Array<{ label: string; value: number; color: string }> } | null {
450
- const colors = resolveThemeColors(theme, isAsiaMarket)
451
- const state = pluginHost.getSharedState<MACDRenderState>(createMACDStateKey(paneId))
452
- if (!state) return null
453
-
454
- const point = state.series[index]
455
- if (!point) return null
456
-
457
- return {
458
- name: 'MACD',
459
- params: [fastPeriod, slowPeriod, signalPeriod],
460
- values: [
461
- { label: 'DIF', value: point.dif, color: colors.macd.dif },
462
- { label: 'DEA', value: point.dea, color: colors.macd.dea },
463
- { label: 'MACD', value: point.macd, color: point.macd >= 0 ? colors.macd.barUp : colors.macd.barDown },
464
- ],
465
- }
447
+ if (index === null) return null
448
+ const fastPeriod = (params.fastPeriod as number) ?? 12
449
+ const slowPeriod = (params.slowPeriod as number) ?? 26
450
+ const signalPeriod = (params.signalPeriod as number) ?? 9
451
+ const colors = resolveThemeColors('light')
452
+ const state = pluginHost.getSharedState<MACDRenderState>(createMACDStateKey(paneId))
453
+ if (!state) return null
454
+
455
+ const point = state.series[index]
456
+ if (!point) return null
457
+
458
+ return {
459
+ name: 'MACD',
460
+ params: [fastPeriod, slowPeriod, signalPeriod],
461
+ values: [
462
+ { label: 'DIF', value: point.dif, color: colors.macd.dif },
463
+ { label: 'DEA', value: point.dea, color: colors.macd.dea },
464
+ { label: 'MACD', value: point.macd, color: point.macd >= 0 ? colors.macd.barUp : colors.macd.barDown },
465
+ ],
466
+ }
466
467
  }
467
468
 
468
469
  @Indicator({
469
470
  name: 'macd',
470
471
  displayName: 'MACD',
471
472
  category: 'oscillator',
472
- stateKey: createMACDStateKey,
473
473
  defaultPaneId: 'sub_MACD',
474
- paneIdField: 'macdPaneId',
475
474
  scaleRendererFactory: createMacdScaleRendererPlugin,
476
475
  visibleState: { compose: createMACDVisibleStateComposer('macd', EMPTY_MACD_STATE) },
477
- updateConfig: (scheduler, params, paneId) => {
478
- (scheduler as IndicatorScheduler).updateMACDConfig(params as Partial<MACDSchedulerConfig>, paneId)
479
- },
480
- applyResult: (host, state, paneId) => {
481
- host.setSharedState(createMACDStateKey(paneId), state as any, 'indicator_scheduler')
482
- },
476
+ getTitleInfo: getMACDTitleInfo,
477
+ runtime: { defaultConfig:{fastPeriod:12,slowPeriod:26,signalPeriod:9,showDIF:true,showDEA:true,showBAR:true}, computeKey:'calcMACDData', compute:(data,c)=>calcMACDData(data,c.fastPeriod,c.slowPeriod,c.signalPeriod) },
483
478
  })
484
479
  class MACDIndicatorDefinition {
485
480
  static rendererFactory = createMACDRendererPlugin
@@ -1,12 +1,9 @@
1
1
  import type { RendererPluginWithHost, PluginHost, RenderContext } from '../../../plugin'
2
2
  import { RENDERER_PRIORITY } from '../../../plugin'
3
3
  import type { KLineData } from '../../../types/price'
4
- import { MA_STATE_KEY, type MARenderState } from '../../indicators/maState'
5
- import { BOLL_STATE_KEY, type BOLLRenderState } from '../../indicators/bollState'
6
- import { EXPMA_STATE_KEY, type EXPMARenderState } from '../../indicators/expmaState'
7
- import { ENE_STATE_KEY, type ENERenderState } from '../../indicators/eneState'
8
4
  import { resolveThemeColors } from '../../../tokens'
9
5
  import { getFont, setCanvasFont } from '../../theme/fonts'
6
+ import type { IndicatorScheduler } from '../../indicators/scheduler'
10
7
 
11
8
  const textWidthCache = new Map<string, number>()
12
9
  const TEXT_WIDTH_CACHE_LIMIT = 512
@@ -26,16 +23,9 @@ function measureTextWidth(ctx: CanvasRenderingContext2D, text: string): number {
26
23
  return width
27
24
  }
28
25
 
29
- /** 指标行数据 */
30
- interface IndicatorRow {
31
- enabled: boolean
32
- params: Record<string, unknown>
33
- }
34
-
35
26
  /** 渲染器配置 */
36
27
  interface MainIndicatorLegendConfig {
37
28
  yPaddingPx: number
38
- indicators: Record<string, IndicatorRow>
39
29
  }
40
30
 
41
31
  /**
@@ -49,12 +39,6 @@ export function createMainIndicatorLegendRendererPlugin(options: {
49
39
  }): RendererPluginWithHost {
50
40
  const config: MainIndicatorLegendConfig = {
51
41
  yPaddingPx: options.yPaddingPx,
52
- indicators: {
53
- MA: { enabled: true, params: {} },
54
- BOLL: { enabled: false, params: { period: 20, multiplier: 2 } },
55
- EXPMA: { enabled: false, params: { fastPeriod: 12, slowPeriod: 50 } },
56
- ENE: { enabled: false, params: { period: 10, deviation: 11 } },
57
- },
58
42
  }
59
43
 
60
44
  let pluginHost: PluginHost | null = null
@@ -74,7 +58,7 @@ export function createMainIndicatorLegendRendererPlugin(options: {
74
58
  },
75
59
 
76
60
  getDeclaredNamespaces(): string[] {
77
- return [MA_STATE_KEY, BOLL_STATE_KEY, EXPMA_STATE_KEY, ENE_STATE_KEY]
61
+ return []
78
62
  },
79
63
 
80
64
  draw(context: RenderContext) {
@@ -87,153 +71,64 @@ export function createMainIndicatorLegendRendererPlugin(options: {
87
71
  const lineHeight = fontSize + 6
88
72
  const legendX = 12
89
73
  const gap = 10
74
+ const legendYOffset = 6
90
75
 
91
76
  overlayCtx.save()
92
77
  setCanvasFont(overlayCtx, getFont(fontSize))
93
78
  overlayCtx.textAlign = 'left'
79
+ overlayCtx.textBaseline = 'top'
94
80
 
95
81
  const targetIndex = crosshairIndex ?? Math.min(range.end - 1, klineData.length - 1)
96
82
  const rows: Array<{ draw: (rowIndex: number) => void }> = []
97
83
 
98
- const maIndicator = config.indicators.MA
99
- if (maIndicator?.enabled) {
100
- rows.push({
101
- draw: (rowIndex: number) => {
102
- const items: Array<{ label: string; color: string; value?: number }> = []
103
- const state = pluginHost?.getSharedState<MARenderState>(MA_STATE_KEY)
104
-
105
- if (state && state.visibleMin <= state.visibleMax) {
106
- for (const period of state.enabledPeriods) {
107
- const colorKey = `ma${period}` as keyof typeof colors.ma
108
- const series = state.series[period]
109
- const value = series?.[targetIndex]
110
-
111
- items.push({
112
- label: `MA${period}`,
113
- color: colors.ma[colorKey] || colors.ma.ma5,
114
- value: value,
115
- })
116
- }
117
- }
118
-
119
- if (items.length > 0) {
120
- let x = legendX
121
- const y = config.yPaddingPx / 2 + fontSize + rowIndex * lineHeight
122
-
123
- overlayCtx.fillStyle = colors.text.primary
124
- overlayCtx.fillText('MA', x, y)
125
- x += measureTextWidth(overlayCtx, 'MA') + gap
126
-
127
- for (const it of items) {
128
- const valText = typeof it.value === 'number' ? ` ${it.value.toFixed(2)}` : ''
129
- const text = `${it.label}${valText}`
130
- overlayCtx.fillStyle = it.color
131
- overlayCtx.fillText(text, x, y)
132
- x += measureTextWidth(overlayCtx, text) + gap
133
- }
134
- }
135
- }
136
- })
137
- }
138
-
139
- const bollIndicator = config.indicators.BOLL
140
- if (bollIndicator?.enabled) {
141
- rows.push({
142
- draw: (rowIndex: number) => {
143
- const bollState = pluginHost?.getSharedState<BOLLRenderState>(BOLL_STATE_KEY)
144
- const boll = bollState?.series[targetIndex]
145
- const period = bollState?.params.period ?? 20
146
- const multiplier = bollState?.params.multiplier ?? 2
147
-
148
- let x = legendX
149
- const y = config.yPaddingPx / 2 + fontSize + rowIndex * lineHeight
150
- const titleText = `BOLL(${period},${multiplier})`
151
-
152
- overlayCtx.fillStyle = colors.text.primary
153
- overlayCtx.fillText(titleText, x, y)
154
- x += measureTextWidth(overlayCtx, titleText) + gap
155
-
156
- if (boll) {
157
- const upperText = `上轨:${boll.upper.toFixed(2)}`
158
- overlayCtx.fillStyle = colors.boll.upper
159
- overlayCtx.fillText(upperText, x, y)
160
- x += measureTextWidth(overlayCtx, upperText) + gap
84
+ const scheduler = pluginHost && typeof pluginHost.getService === 'function'
85
+ ? pluginHost.getService<IndicatorScheduler>('indicatorScheduler')
86
+ : undefined
161
87
 
162
- const middleText = `中轨:${boll.middle.toFixed(2)}`
163
- overlayCtx.fillStyle = colors.boll.middle
164
- overlayCtx.fillText(middleText, x, y)
165
- x += measureTextWidth(overlayCtx, middleText) + gap
88
+ const mainIndicators = scheduler?.getMainIndicators() ?? []
89
+ for (const meta of mainIndicators) {
90
+ if (!meta.getTitleInfo) continue
91
+ if (!scheduler?.isMainIndicatorActive(meta.name)) continue
92
+ const params = scheduler?.getMainIndicatorParams(meta.name) ?? {}
93
+ const getTitleInfo = meta.getTitleInfo
166
94
 
167
- const lowerText = `下轨:${boll.lower.toFixed(2)}`
168
- overlayCtx.fillStyle = colors.boll.lower
169
- overlayCtx.fillText(lowerText, x, y)
170
- }
171
- }
172
- })
173
- }
174
-
175
- const expmaIndicator = config.indicators.EXPMA
176
- if (expmaIndicator?.enabled) {
177
95
  rows.push({
178
96
  draw: (rowIndex: number) => {
179
- const expmaState = pluginHost?.getSharedState<EXPMARenderState>(EXPMA_STATE_KEY)
180
- const expma = expmaState?.series[targetIndex]
181
- const fastPeriod = expmaState?.params.fastPeriod ?? 12
182
- const slowPeriod = expmaState?.params.slowPeriod ?? 50
97
+ const titleInfo = getTitleInfo(
98
+ klineData,
99
+ targetIndex,
100
+ params as Record<string, number | boolean | string>,
101
+ pluginHost!,
102
+ 'main',
103
+ )
104
+ if (!titleInfo) return
183
105
 
184
106
  let x = legendX
185
- const y = config.yPaddingPx / 2 + fontSize + rowIndex * lineHeight
186
- const titleText = `EXPMA(${fastPeriod},${slowPeriod})`
187
-
107
+ let y = config.yPaddingPx / 2 + legendYOffset + rowIndex * lineHeight
108
+ // 指标名称
188
109
  overlayCtx.fillStyle = colors.text.primary
189
- overlayCtx.fillText(titleText, x, y)
190
- x += measureTextWidth(overlayCtx, titleText) + gap
191
-
192
- if (expma) {
193
- const fastText = `快:${expma.fast.toFixed(2)}`
194
- overlayCtx.fillStyle = colors.expma.fast
195
- overlayCtx.fillText(fastText, x, y)
196
- x += measureTextWidth(overlayCtx, fastText) + gap
197
-
198
- const slowText = `慢:${expma.slow.toFixed(2)}`
199
- overlayCtx.fillStyle = colors.expma.slow
200
- overlayCtx.fillText(slowText, x, y)
110
+ overlayCtx.fillText(titleInfo.name, x, y)
111
+ x += measureTextWidth(overlayCtx, titleInfo.name)
112
+
113
+ // 指标参数
114
+ if (titleInfo.params && titleInfo.params.length > 0) {
115
+ const paramText = `(${titleInfo.params.join(',')})`
116
+ overlayCtx.fillStyle = colors.text.tertiary
117
+ overlayCtx.fillText(paramText, x, y)
118
+ x += measureTextWidth(overlayCtx, paramText) + gap
119
+ } else {
120
+ x += gap
201
121
  }
202
- }
203
- })
204
- }
205
122
 
206
- const eneIndicator = config.indicators.ENE
207
- if (eneIndicator?.enabled) {
208
- rows.push({
209
- draw: (rowIndex: number) => {
210
- const eneState = pluginHost?.getSharedState<ENERenderState>(ENE_STATE_KEY)
211
- const ene = eneState?.series[targetIndex]
212
- const period = eneState?.params.period ?? 10
213
- const deviation = eneState?.params.deviation ?? 11
214
-
215
- let x = legendX
216
- const y = config.yPaddingPx / 2 + fontSize + rowIndex * lineHeight
217
- const titleText = `ENE(${period},${deviation})`
218
-
219
- overlayCtx.fillStyle = colors.text.primary
220
- overlayCtx.fillText(titleText, x, y)
221
- x += measureTextWidth(overlayCtx, titleText) + gap
222
-
223
- if (ene) {
224
- const upperText = `上轨:${ene.upper.toFixed(2)}`
225
- overlayCtx.fillStyle = colors.ene.upper
226
- overlayCtx.fillText(upperText, x, y)
227
- x += measureTextWidth(overlayCtx, upperText) + gap
228
-
229
- const middleText = `中轨:${ene.middle.toFixed(2)}`
230
- overlayCtx.fillStyle = colors.ene.middle
231
- overlayCtx.fillText(middleText, x, y)
232
- x += measureTextWidth(overlayCtx, middleText) + gap
233
-
234
- const lowerText = `下轨:${ene.lower.toFixed(2)}`
235
- overlayCtx.fillStyle = colors.ene.lower
236
- overlayCtx.fillText(lowerText, x, y)
123
+ // 指标数值
124
+ if (titleInfo.values) {
125
+ y += 1
126
+ for (const item of titleInfo.values) {
127
+ const valText = `${item.label} ${item.value.toFixed(3)}`
128
+ overlayCtx.fillStyle = item.color
129
+ overlayCtx.fillText(valText, x, y)
130
+ x += measureTextWidth(overlayCtx, valText) + gap
131
+ }
237
132
  }
238
133
  }
239
134
  })
@@ -246,7 +141,6 @@ export function createMainIndicatorLegendRendererPlugin(options: {
246
141
  getConfig() {
247
142
  return {
248
143
  yPaddingPx: config.yPaddingPx,
249
- indicators: { ...config.indicators },
250
144
  }
251
145
  },
252
146
 
@@ -254,19 +148,6 @@ export function createMainIndicatorLegendRendererPlugin(options: {
254
148
  if (typeof newConfig.yPaddingPx === 'number') {
255
149
  config.yPaddingPx = newConfig.yPaddingPx
256
150
  }
257
- if (newConfig.indicators && typeof newConfig.indicators === 'object') {
258
- for (const [id, row] of Object.entries(newConfig.indicators) as [string, IndicatorRow][]) {
259
- if (!config.indicators[id]) {
260
- config.indicators[id] = { enabled: false, params: {} }
261
- }
262
- if (row.enabled !== undefined) {
263
- config.indicators[id].enabled = row.enabled
264
- }
265
- if (row.params) {
266
- config.indicators[id].params = row.params
267
- }
268
- }
269
- }
270
151
  },
271
152
  }
272
153
  }
@@ -2,10 +2,13 @@ import type { RendererPluginWithHost, RenderContext, PluginHost } from '../../..
2
2
  import { RENDERER_PRIORITY } from '../../../plugin'
3
3
  import type { MFIRenderState } from '../../indicators/mfiState'
4
4
  import { createMFIStateKey, EMPTY_MFI_STATE } from '../../indicators/mfiState'
5
+ import type { TitleInfo } from '../../indicators/indicatorMetadata'
6
+ import type { KLineData } from '../../../types/price'
5
7
  import { Indicator } from '../../indicators/indicatorDefinitionRegistry'
6
8
  import { createFixedRangeSparseVisibleStateComposer } from '../../indicators/visibleStateComposers'
7
9
  import { resolveStateKey } from '../../indicators/indicatorMetadata'
8
10
  import type { IndicatorScheduler, MFISchedulerConfig } from '../../indicators/scheduler'
11
+ import { calcMFIData } from '../../indicators/calculators'
9
12
 
10
13
  const MFI_COLOR = '#fb923c'
11
14
 
@@ -127,20 +130,38 @@ export function createMFIRendererPlugin(options: { paneId?: string } = {}): Rend
127
130
  }
128
131
  }
129
132
 
133
+ export function getMFITitleInfo(
134
+ _data: KLineData[],
135
+ index: number | null,
136
+ params: Record<string, number | boolean | string>,
137
+ host: PluginHost,
138
+ paneId: string,
139
+ ): TitleInfo | null {
140
+ if (index === null) return null
141
+ const period = (params.period as number) ?? 14
142
+ const state = host.getSharedState<MFIRenderState>(createMFIStateKey(paneId))
143
+ const value = state?.series[index]
144
+ if (value === undefined) return null
145
+
146
+ return {
147
+ name: 'MFI',
148
+ params: [period],
149
+ values: [{ label: 'MFI', value, color: MFI_COLOR }],
150
+ }
151
+ }
152
+
130
153
  @Indicator({
131
154
  name: 'mfi',
132
155
  displayName: 'MFI',
133
156
  category: 'volume',
134
- stateKey: createMFIStateKey,
135
157
  defaultPaneId: 'sub_MFI',
136
- paneIdField: 'mfiPaneId',
137
158
  visibleState: { compose: createFixedRangeSparseVisibleStateComposer('mfi', EMPTY_MFI_STATE) },
138
159
  scale: { indicatorKey: 'mfi', label: 'MFI', decimals: 2 },
139
- updateConfig: (scheduler, params, paneId) => {
140
- (scheduler as IndicatorScheduler).updateMFIConfig(params as Partial<MFISchedulerConfig>, paneId)
141
- },
142
- applyResult: (host, state, paneId) => {
143
- host.setSharedState(createMFIStateKey(paneId), state as any, 'indicator_scheduler')
160
+ getTitleInfo: getMFITitleInfo,
161
+ runtime: {
162
+ defaultConfig: { period: 14, showMFI: true },
163
+ computeKey: 'calcMFIData',
164
+ compute: (data, c) => calcMFIData(data, c.period),
144
165
  },
145
166
  })
146
167
  class MFIIndicatorDefinition {