@363045841yyt/klinechart-core 0.7.12 → 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 (261) 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/indicatorDefinitionRegistry.d.ts +3 -2
  13. package/dist/engine/indicators/indicatorDefinitionRegistry.d.ts.map +1 -1
  14. package/dist/engine/indicators/indicatorDefinitionRegistry.js +26 -1
  15. package/dist/engine/indicators/indicatorDefinitionRegistry.js.map +1 -1
  16. package/dist/engine/indicators/indicatorMetadata.d.ts +29 -2
  17. package/dist/engine/indicators/indicatorMetadata.d.ts.map +1 -1
  18. package/dist/engine/indicators/indicatorMetadata.js.map +1 -1
  19. package/dist/engine/indicators/indicatorRuntime.d.ts.map +1 -1
  20. package/dist/engine/indicators/indicatorRuntime.js +5 -4
  21. package/dist/engine/indicators/indicatorRuntime.js.map +1 -1
  22. package/dist/engine/indicators/scheduler.d.ts +16 -1
  23. package/dist/engine/indicators/scheduler.d.ts.map +1 -1
  24. package/dist/engine/indicators/scheduler.js +26 -7
  25. package/dist/engine/indicators/scheduler.js.map +1 -1
  26. package/dist/engine/renderers/Indicator/atr.d.ts +2 -1
  27. package/dist/engine/renderers/Indicator/atr.d.ts.map +1 -1
  28. package/dist/engine/renderers/Indicator/atr.js +5 -9
  29. package/dist/engine/renderers/Indicator/atr.js.map +1 -1
  30. package/dist/engine/renderers/Indicator/boll.d.ts +4 -1
  31. package/dist/engine/renderers/Indicator/boll.d.ts.map +1 -1
  32. package/dist/engine/renderers/Indicator/boll.js +22 -10
  33. package/dist/engine/renderers/Indicator/boll.js.map +1 -1
  34. package/dist/engine/renderers/Indicator/cci.d.ts +2 -1
  35. package/dist/engine/renderers/Indicator/cci.d.ts.map +1 -1
  36. package/dist/engine/renderers/Indicator/cci.js +6 -10
  37. package/dist/engine/renderers/Indicator/cci.js.map +1 -1
  38. package/dist/engine/renderers/Indicator/chaikinVol.d.ts +4 -1
  39. package/dist/engine/renderers/Indicator/chaikinVol.d.ts.map +1 -1
  40. package/dist/engine/renderers/Indicator/chaikinVol.js +16 -8
  41. package/dist/engine/renderers/Indicator/chaikinVol.js.map +1 -1
  42. package/dist/engine/renderers/Indicator/cmf.d.ts +4 -1
  43. package/dist/engine/renderers/Indicator/cmf.d.ts.map +1 -1
  44. package/dist/engine/renderers/Indicator/cmf.js +15 -8
  45. package/dist/engine/renderers/Indicator/cmf.js.map +1 -1
  46. package/dist/engine/renderers/Indicator/dema.d.ts +2 -0
  47. package/dist/engine/renderers/Indicator/dema.d.ts.map +1 -1
  48. package/dist/engine/renderers/Indicator/dema.js +18 -8
  49. package/dist/engine/renderers/Indicator/dema.js.map +1 -1
  50. package/dist/engine/renderers/Indicator/donchian.d.ts +4 -1
  51. package/dist/engine/renderers/Indicator/donchian.d.ts.map +1 -1
  52. package/dist/engine/renderers/Indicator/donchian.js +19 -8
  53. package/dist/engine/renderers/Indicator/donchian.js.map +1 -1
  54. package/dist/engine/renderers/Indicator/ene.d.ts +12 -1
  55. package/dist/engine/renderers/Indicator/ene.d.ts.map +1 -1
  56. package/dist/engine/renderers/Indicator/ene.js +22 -10
  57. package/dist/engine/renderers/Indicator/ene.js.map +1 -1
  58. package/dist/engine/renderers/Indicator/expma.d.ts +4 -1
  59. package/dist/engine/renderers/Indicator/expma.d.ts.map +1 -1
  60. package/dist/engine/renderers/Indicator/expma.js +21 -10
  61. package/dist/engine/renderers/Indicator/expma.js.map +1 -1
  62. package/dist/engine/renderers/Indicator/fastk.d.ts +2 -1
  63. package/dist/engine/renderers/Indicator/fastk.d.ts.map +1 -1
  64. package/dist/engine/renderers/Indicator/fastk.js +6 -10
  65. package/dist/engine/renderers/Indicator/fastk.js.map +1 -1
  66. package/dist/engine/renderers/Indicator/fib.d.ts +2 -0
  67. package/dist/engine/renderers/Indicator/fib.d.ts.map +1 -1
  68. package/dist/engine/renderers/Indicator/fib.js +25 -8
  69. package/dist/engine/renderers/Indicator/fib.js.map +1 -1
  70. package/dist/engine/renderers/Indicator/hma.d.ts +2 -0
  71. package/dist/engine/renderers/Indicator/hma.d.ts.map +1 -1
  72. package/dist/engine/renderers/Indicator/hma.js +18 -8
  73. package/dist/engine/renderers/Indicator/hma.js.map +1 -1
  74. package/dist/engine/renderers/Indicator/hv.d.ts +4 -1
  75. package/dist/engine/renderers/Indicator/hv.d.ts.map +1 -1
  76. package/dist/engine/renderers/Indicator/hv.js +17 -8
  77. package/dist/engine/renderers/Indicator/hv.js.map +1 -1
  78. package/dist/engine/renderers/Indicator/ichimoku.d.ts +4 -1
  79. package/dist/engine/renderers/Indicator/ichimoku.d.ts.map +1 -1
  80. package/dist/engine/renderers/Indicator/ichimoku.js +26 -8
  81. package/dist/engine/renderers/Indicator/ichimoku.js.map +1 -1
  82. package/dist/engine/renderers/Indicator/index.d.ts +27 -28
  83. package/dist/engine/renderers/Indicator/index.d.ts.map +1 -1
  84. package/dist/engine/renderers/Indicator/index.js +27 -28
  85. package/dist/engine/renderers/Indicator/index.js.map +1 -1
  86. package/dist/engine/renderers/Indicator/kama.d.ts +2 -0
  87. package/dist/engine/renderers/Indicator/kama.d.ts.map +1 -1
  88. package/dist/engine/renderers/Indicator/kama.js +18 -8
  89. package/dist/engine/renderers/Indicator/kama.js.map +1 -1
  90. package/dist/engine/renderers/Indicator/keltner.d.ts +4 -1
  91. package/dist/engine/renderers/Indicator/keltner.d.ts.map +1 -1
  92. package/dist/engine/renderers/Indicator/keltner.js +19 -8
  93. package/dist/engine/renderers/Indicator/keltner.js.map +1 -1
  94. package/dist/engine/renderers/Indicator/kst.d.ts +2 -1
  95. package/dist/engine/renderers/Indicator/kst.d.ts.map +1 -1
  96. package/dist/engine/renderers/Indicator/kst.js +11 -10
  97. package/dist/engine/renderers/Indicator/kst.js.map +1 -1
  98. package/dist/engine/renderers/Indicator/ma.d.ts +4 -1
  99. package/dist/engine/renderers/Indicator/ma.d.ts.map +1 -1
  100. package/dist/engine/renderers/Indicator/ma.js +32 -9
  101. package/dist/engine/renderers/Indicator/ma.js.map +1 -1
  102. package/dist/engine/renderers/Indicator/macd.d.ts +1 -1
  103. package/dist/engine/renderers/Indicator/macd.d.ts.map +1 -1
  104. package/dist/engine/renderers/Indicator/macd.js +9 -10
  105. package/dist/engine/renderers/Indicator/macd.js.map +1 -1
  106. package/dist/engine/renderers/Indicator/mainIndicatorLegend.d.ts.map +1 -1
  107. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js +38 -136
  108. package/dist/engine/renderers/Indicator/mainIndicatorLegend.js.map +1 -1
  109. package/dist/engine/renderers/Indicator/mfi.d.ts +4 -1
  110. package/dist/engine/renderers/Indicator/mfi.d.ts.map +1 -1
  111. package/dist/engine/renderers/Indicator/mfi.js +15 -8
  112. package/dist/engine/renderers/Indicator/mfi.js.map +1 -1
  113. package/dist/engine/renderers/Indicator/mom.d.ts +2 -1
  114. package/dist/engine/renderers/Indicator/mom.d.ts.map +1 -1
  115. package/dist/engine/renderers/Indicator/mom.js +6 -10
  116. package/dist/engine/renderers/Indicator/mom.js.map +1 -1
  117. package/dist/engine/renderers/Indicator/obv.d.ts +4 -1
  118. package/dist/engine/renderers/Indicator/obv.d.ts.map +1 -1
  119. package/dist/engine/renderers/Indicator/obv.js +14 -8
  120. package/dist/engine/renderers/Indicator/obv.js.map +1 -1
  121. package/dist/engine/renderers/Indicator/parkinson.d.ts +4 -1
  122. package/dist/engine/renderers/Indicator/parkinson.d.ts.map +1 -1
  123. package/dist/engine/renderers/Indicator/parkinson.js +17 -8
  124. package/dist/engine/renderers/Indicator/parkinson.js.map +1 -1
  125. package/dist/engine/renderers/Indicator/pivot.d.ts +2 -0
  126. package/dist/engine/renderers/Indicator/pivot.d.ts.map +1 -1
  127. package/dist/engine/renderers/Indicator/pivot.js +42 -8
  128. package/dist/engine/renderers/Indicator/pivot.js.map +1 -1
  129. package/dist/engine/renderers/Indicator/pvt.d.ts +4 -1
  130. package/dist/engine/renderers/Indicator/pvt.d.ts.map +1 -1
  131. package/dist/engine/renderers/Indicator/pvt.js +14 -8
  132. package/dist/engine/renderers/Indicator/pvt.js.map +1 -1
  133. package/dist/engine/renderers/Indicator/roc.d.ts +4 -1
  134. package/dist/engine/renderers/Indicator/roc.d.ts.map +1 -1
  135. package/dist/engine/renderers/Indicator/roc.js +15 -8
  136. package/dist/engine/renderers/Indicator/roc.js.map +1 -1
  137. package/dist/engine/renderers/Indicator/rsi.d.ts +2 -1
  138. package/dist/engine/renderers/Indicator/rsi.d.ts.map +1 -1
  139. package/dist/engine/renderers/Indicator/rsi.js +9 -10
  140. package/dist/engine/renderers/Indicator/rsi.js.map +1 -1
  141. package/dist/engine/renderers/Indicator/sar.d.ts +4 -1
  142. package/dist/engine/renderers/Indicator/sar.d.ts.map +1 -1
  143. package/dist/engine/renderers/Indicator/sar.js +17 -8
  144. package/dist/engine/renderers/Indicator/sar.js.map +1 -1
  145. package/dist/engine/renderers/Indicator/stoch.d.ts +2 -1
  146. package/dist/engine/renderers/Indicator/stoch.d.ts.map +1 -1
  147. package/dist/engine/renderers/Indicator/stoch.js +8 -10
  148. package/dist/engine/renderers/Indicator/stoch.js.map +1 -1
  149. package/dist/engine/renderers/Indicator/structure.d.ts +4 -1
  150. package/dist/engine/renderers/Indicator/structure.d.ts.map +1 -1
  151. package/dist/engine/renderers/Indicator/structure.js +20 -8
  152. package/dist/engine/renderers/Indicator/structure.js.map +1 -1
  153. package/dist/engine/renderers/Indicator/supertrend.d.ts +4 -1
  154. package/dist/engine/renderers/Indicator/supertrend.d.ts.map +1 -1
  155. package/dist/engine/renderers/Indicator/supertrend.js +17 -8
  156. package/dist/engine/renderers/Indicator/supertrend.js.map +1 -1
  157. package/dist/engine/renderers/Indicator/tema.d.ts +2 -0
  158. package/dist/engine/renderers/Indicator/tema.d.ts.map +1 -1
  159. package/dist/engine/renderers/Indicator/tema.js +18 -8
  160. package/dist/engine/renderers/Indicator/tema.js.map +1 -1
  161. package/dist/engine/renderers/Indicator/trix.d.ts +4 -1
  162. package/dist/engine/renderers/Indicator/trix.d.ts.map +1 -1
  163. package/dist/engine/renderers/Indicator/trix.js +29 -8
  164. package/dist/engine/renderers/Indicator/trix.js.map +1 -1
  165. package/dist/engine/renderers/Indicator/vma.d.ts +4 -1
  166. package/dist/engine/renderers/Indicator/vma.d.ts.map +1 -1
  167. package/dist/engine/renderers/Indicator/vma.js +15 -8
  168. package/dist/engine/renderers/Indicator/vma.js.map +1 -1
  169. package/dist/engine/renderers/Indicator/volumeProfile.d.ts +4 -1
  170. package/dist/engine/renderers/Indicator/volumeProfile.d.ts.map +1 -1
  171. package/dist/engine/renderers/Indicator/volumeProfile.js +27 -8
  172. package/dist/engine/renderers/Indicator/volumeProfile.js.map +1 -1
  173. package/dist/engine/renderers/Indicator/vwap.d.ts +4 -1
  174. package/dist/engine/renderers/Indicator/vwap.d.ts.map +1 -1
  175. package/dist/engine/renderers/Indicator/vwap.js +14 -8
  176. package/dist/engine/renderers/Indicator/vwap.js.map +1 -1
  177. package/dist/engine/renderers/Indicator/wma.d.ts +2 -0
  178. package/dist/engine/renderers/Indicator/wma.d.ts.map +1 -1
  179. package/dist/engine/renderers/Indicator/wma.js +18 -8
  180. package/dist/engine/renderers/Indicator/wma.js.map +1 -1
  181. package/dist/engine/renderers/Indicator/wmsr.d.ts +2 -1
  182. package/dist/engine/renderers/Indicator/wmsr.d.ts.map +1 -1
  183. package/dist/engine/renderers/Indicator/wmsr.js +6 -10
  184. package/dist/engine/renderers/Indicator/wmsr.js.map +1 -1
  185. package/dist/engine/renderers/Indicator/zones.d.ts +2 -0
  186. package/dist/engine/renderers/Indicator/zones.d.ts.map +1 -1
  187. package/dist/engine/renderers/Indicator/zones.js +23 -8
  188. package/dist/engine/renderers/Indicator/zones.js.map +1 -1
  189. package/dist/engine/renderers/paneTitle.d.ts +4 -10
  190. package/dist/engine/renderers/paneTitle.d.ts.map +1 -1
  191. package/dist/engine/renderers/paneTitle.js +28 -6
  192. package/dist/engine/renderers/paneTitle.js.map +1 -1
  193. package/dist/engine/renderers/subVolume.js +0 -3
  194. package/dist/engine/renderers/subVolume.js.map +1 -1
  195. package/dist/version.d.ts +1 -1
  196. package/dist/version.js +1 -1
  197. package/package.json +4 -4
  198. package/src/controllers/__tests__/indicatorSelector.test.ts +4 -3
  199. package/src/controllers/createIndicatorSelectorController.ts +4 -6
  200. package/src/controllers/index.ts +0 -4
  201. package/src/engine/chart.ts +30 -22
  202. package/src/engine/indicators/__tests__/registerBuiltins.test.ts +4 -5
  203. package/src/engine/indicators/indicatorDefinitionRegistry.ts +35 -2
  204. package/src/engine/indicators/indicatorMetadata.ts +39 -2
  205. package/src/engine/indicators/indicatorRuntime.ts +5 -4
  206. package/src/engine/indicators/scheduler.ts +32 -8
  207. package/src/engine/renderers/Indicator/atr.ts +8 -11
  208. package/src/engine/renderers/Indicator/boll.ts +31 -10
  209. package/src/engine/renderers/Indicator/cci.ts +9 -14
  210. package/src/engine/renderers/Indicator/chaikinVol.ts +24 -8
  211. package/src/engine/renderers/Indicator/cmf.ts +23 -8
  212. package/src/engine/renderers/Indicator/dema.ts +27 -9
  213. package/src/engine/renderers/Indicator/donchian.ts +27 -9
  214. package/src/engine/renderers/Indicator/ene.ts +31 -10
  215. package/src/engine/renderers/Indicator/expma.ts +30 -10
  216. package/src/engine/renderers/Indicator/fastk.ts +9 -14
  217. package/src/engine/renderers/Indicator/fib.ts +28 -9
  218. package/src/engine/renderers/Indicator/hma.ts +27 -9
  219. package/src/engine/renderers/Indicator/hv.ts +26 -8
  220. package/src/engine/renderers/Indicator/ichimoku.ts +30 -9
  221. package/src/engine/renderers/Indicator/index.ts +27 -28
  222. package/src/engine/renderers/Indicator/kama.ts +27 -9
  223. package/src/engine/renderers/Indicator/keltner.ts +27 -9
  224. package/src/engine/renderers/Indicator/kst.ts +14 -18
  225. package/src/engine/renderers/Indicator/ma.ts +43 -9
  226. package/src/engine/renderers/Indicator/macd.ts +27 -32
  227. package/src/engine/renderers/Indicator/mainIndicatorLegend.ts +43 -162
  228. package/src/engine/renderers/Indicator/mfi.ts +23 -8
  229. package/src/engine/renderers/Indicator/mom.ts +9 -14
  230. package/src/engine/renderers/Indicator/obv.ts +22 -8
  231. package/src/engine/renderers/Indicator/parkinson.ts +26 -8
  232. package/src/engine/renderers/Indicator/pivot.ts +46 -9
  233. package/src/engine/renderers/Indicator/pvt.ts +22 -8
  234. package/src/engine/renderers/Indicator/roc.ts +24 -8
  235. package/src/engine/renderers/Indicator/rsi.ts +12 -16
  236. package/src/engine/renderers/Indicator/sar.ts +25 -9
  237. package/src/engine/renderers/Indicator/stoch.ts +11 -15
  238. package/src/engine/renderers/Indicator/structure.ts +30 -8
  239. package/src/engine/renderers/Indicator/supertrend.ts +25 -9
  240. package/src/engine/renderers/Indicator/tema.ts +27 -9
  241. package/src/engine/renderers/Indicator/trix.ts +38 -8
  242. package/src/engine/renderers/Indicator/vma.ts +23 -8
  243. package/src/engine/renderers/Indicator/volumeProfile.ts +39 -8
  244. package/src/engine/renderers/Indicator/vwap.ts +22 -8
  245. package/src/engine/renderers/Indicator/wma.ts +27 -9
  246. package/src/engine/renderers/Indicator/wmsr.ts +9 -14
  247. package/src/engine/renderers/Indicator/zones.ts +28 -9
  248. package/src/engine/renderers/__tests__/mainIndicatorLegend.renderer.test.ts +142 -79
  249. package/src/engine/renderers/paneTitle.ts +43 -18
  250. package/src/engine/renderers/subVolume.ts +0 -2
  251. package/src/version.ts +1 -1
  252. package/dist/engine/renderers/Indicator/macdLegend.d.ts +0 -13
  253. package/dist/engine/renderers/Indicator/macdLegend.d.ts.map +0 -1
  254. package/dist/engine/renderers/Indicator/macdLegend.js +0 -116
  255. package/dist/engine/renderers/Indicator/macdLegend.js.map +0 -1
  256. package/dist/engine/renderers/Indicator/subPaneConfig.d.ts +0 -16
  257. package/dist/engine/renderers/Indicator/subPaneConfig.d.ts.map +0 -1
  258. package/dist/engine/renderers/Indicator/subPaneConfig.js +0 -194
  259. package/dist/engine/renderers/Indicator/subPaneConfig.js.map +0 -1
  260. package/src/engine/renderers/Indicator/macdLegend.ts +0 -141
  261. package/src/engine/renderers/Indicator/subPaneConfig.ts +0 -265
@@ -1,9 +1,10 @@
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'
8
9
  import { calcKeltnerData } from '../../indicators/calculators'
9
10
  import { createBandVisibleStateComposer } from '../../indicators/visibleStateComposers'
@@ -123,23 +124,40 @@ function drawLine(ctx: CanvasRenderingContext2D, pts: Point[], color: string): v
123
124
  ctx.stroke()
124
125
  }
125
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
+
126
150
  @Indicator({
127
151
  name: 'keltner',
128
152
  displayName: 'Keltner',
153
+ getTitleInfo: getKeltnerTitleInfo,
129
154
  category: 'main',
130
- stateKey: createKeltnerStateKey,
131
155
  defaultPaneId: 'main',
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).updateIndicatorConfig('keltner', params, paneId)
138
- },
139
- applyResult: (host, state, paneId) => {
140
- host.setSharedState(createKeltnerStateKey(paneId), state as any, 'indicator_scheduler')
141
- },
142
- runtime: { configKey:'keltner', 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) },
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) },
143
161
  })
144
162
  class KeltnerDefinition {
145
163
  static rendererFactory = createKeltnerRendererPlugin
@@ -10,6 +10,7 @@ import { resolveStateKey } from '../../indicators/indicatorMetadata'
10
10
  import type { IndicatorScheduler, KSTSchedulerConfig } from '../../indicators/scheduler'
11
11
  import { createKstScaleRendererPlugin } from './scale/kst_scale'
12
12
  import { calcKSTData } from '../../indicators/calculators'
13
+ import type { KLineData } from '../../../types/price'
13
14
 
14
15
  type LinePoint = { x: number; y: number }
15
16
 
@@ -260,18 +261,19 @@ function drawKSTLinesWithCanvas2D(
260
261
  * 获取 KST 标题信息(供 paneTitle 使用)
261
262
  */
262
263
  export function getKSTTitleInfo(
263
- index: number,
264
- roc1: number,
265
- roc2: number,
266
- roc3: number,
267
- roc4: number,
268
- signalPeriod: number,
264
+ _data: KLineData[],
265
+ index: number | null,
266
+ params: Record<string, number | boolean | string>,
269
267
  pluginHost: PluginHost,
270
- paneId: string = 'sub_KST',
271
- theme: 'light' | 'dark' = 'light',
272
- isAsiaMarket?: boolean
268
+ paneId: string,
273
269
  ): { name: string; params: number[]; values: Array<{ label: string; value: number; color: string }> } | null {
274
- 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')
275
277
  const state = pluginHost.getSharedState<KSTRenderState>(createKSTStateKey(paneId))
276
278
  if (!state) return null
277
279
 
@@ -295,17 +297,11 @@ export function getKSTTitleInfo(
295
297
  name: 'kst',
296
298
  displayName: 'KST',
297
299
  category: 'oscillator',
298
- stateKey: createKSTStateKey,
299
300
  defaultPaneId: 'sub_KST',
300
301
  scaleRendererFactory: createKstScaleRendererPlugin,
301
- updateConfig: (scheduler, params, paneId) => {
302
- (scheduler as IndicatorScheduler).updateIndicatorConfig('kst', params, 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
- },
308
- runtime: { configKey:'kst', 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) },
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) },
309
305
  })
310
306
  class KSTIndicatorDefinition {
311
307
  static rendererFactory = createKSTRendererPlugin
@@ -3,7 +3,8 @@ 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
9
  import { calcMAData, type MAFlags } from '../../indicators/calculators'
9
10
  import { alignToPhysicalPixelCenter } from '../../draw/pixelAlign'
@@ -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).updateIndicatorConfig('ma', params)
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,10 +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
- },
131
- runtime: { configKey:'ma', 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} },
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,
132
166
  })
133
167
  class MADefinition {
134
168
  static rendererFactory = createMARendererPlugin
@@ -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
474
  scaleRendererFactory: createMacdScaleRendererPlugin,
475
475
  visibleState: { compose: createMACDVisibleStateComposer('macd', EMPTY_MACD_STATE) },
476
- updateConfig: (scheduler, params, paneId) => {
477
- (scheduler as IndicatorScheduler).updateIndicatorConfig('macd', params, paneId)
478
- },
479
- applyResult: (host, state, paneId) => {
480
- host.setSharedState(createMACDStateKey(paneId), state as any, 'indicator_scheduler')
481
- },
482
- runtime: { configKey:'macd', 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) },
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,6 +2,8 @@ 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'
@@ -128,22 +130,35 @@ export function createMFIRendererPlugin(options: { paneId?: string } = {}): Rend
128
130
  }
129
131
  }
130
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
+
131
153
  @Indicator({
132
154
  name: 'mfi',
133
155
  displayName: 'MFI',
134
156
  category: 'volume',
135
- stateKey: createMFIStateKey,
136
157
  defaultPaneId: 'sub_MFI',
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).updateIndicatorConfig('mfi', params, paneId)
141
- },
142
- applyResult: (host, state, paneId) => {
143
- host.setSharedState(createMFIStateKey(paneId), state as any, 'indicator_scheduler')
144
- },
160
+ getTitleInfo: getMFITitleInfo,
145
161
  runtime: {
146
- configKey: 'mfi',
147
162
  defaultConfig: { period: 14, showMFI: true },
148
163
  computeKey: 'calcMFIData',
149
164
  compute: (data, c) => calcMFIData(data, c.period),
@@ -11,6 +11,7 @@ import { resolveStateKey } from '../../indicators/indicatorMetadata'
11
11
  import type { IndicatorScheduler, MOMSchedulerConfig } from '../../indicators/scheduler'
12
12
  import { createMomScaleRendererPlugin } from './scale/mom_scale'
13
13
  import { calcMOMData } from '../../indicators/calculators'
14
+ import type { KLineData } from '../../../types/price'
14
15
 
15
16
  type LinePoint = { x: number; y: number }
16
17
 
@@ -277,14 +278,15 @@ function drawMOMLineWithCanvas2D(
277
278
  * 获取 MOM 标题信息(供 paneTitle 使用)
278
279
  */
279
280
  export function getMOMTitleInfo(
280
- index: number,
281
- period: number,
281
+ _data: KLineData[],
282
+ index: number | null,
283
+ params: Record<string, number | boolean | string>,
282
284
  pluginHost: PluginHost,
283
- paneId: string = 'sub_MOM',
284
- theme: 'light' | 'dark' = 'light',
285
- isAsiaMarket?: boolean
285
+ paneId: string,
286
286
  ): { name: string; params: number[]; values: Array<{ label: string; value: number; color: string }> } | null {
287
- const colors = resolveThemeColors(theme, isAsiaMarket)
287
+ if (index === null) return null
288
+ const period = (params.period as number) ?? 10
289
+ const colors = resolveThemeColors('light')
288
290
  const state = pluginHost.getSharedState<MOMRenderState>(createMOMStateKey(paneId))
289
291
  if (!state) return null
290
292
 
@@ -304,18 +306,11 @@ export function getMOMTitleInfo(
304
306
  name: 'mom',
305
307
  displayName: 'MOM',
306
308
  category: 'oscillator',
307
- stateKey: createMOMStateKey,
308
309
  defaultPaneId: 'sub_MOM',
309
310
  scaleRendererFactory: createMomScaleRendererPlugin,
310
- updateConfig: (scheduler, params, paneId) => {
311
- (scheduler as IndicatorScheduler).updateIndicatorConfig('mom', params, paneId)
312
- },
313
311
  visibleState: { compose: createPaddedSparseVisibleStateComposer('mom', EMPTY_MOM_STATE) },
314
- applyResult: (host, state, paneId) => {
315
- host.setSharedState(createMOMStateKey(paneId), state as any, 'indicator_scheduler')
316
- },
312
+ getTitleInfo: getMOMTitleInfo,
317
313
  runtime: {
318
- configKey: 'mom',
319
314
  defaultConfig: { period: 10, showMOM: true },
320
315
  computeKey: 'calcMOMData',
321
316
  compute: (data, c) => calcMOMData(data, c.period),