@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
@@ -6,15 +6,18 @@ import type {
6
6
  ScaleRendererFactory,
7
7
  IndicatorConfigUpdater,
8
8
  IndicatorRuntimeDescriptor,
9
+ GetTitleInfoFn,
9
10
  } from './indicatorMetadata'
10
11
  import type { PluginHost } from '../../plugin'
12
+ import { createIndicatorStateKey } from '../../plugin/stateKeys'
13
+ import { resolveStateKey } from './indicatorMetadata'
11
14
 
12
15
  export type IndicatorDefinitionConfig<T = unknown> = {
13
16
  name: string
14
17
  aliases?: readonly string[]
15
18
  displayName: string
16
19
  category: IndicatorCategory
17
- stateKey: StateKey
20
+ stateKey?: StateKey
18
21
  defaultPaneId: string
19
22
  paneIdField?: string
20
23
  allowMainPane?: boolean
@@ -26,6 +29,7 @@ export type IndicatorDefinitionConfig<T = unknown> = {
26
29
  visibleState?: IndicatorMetadata['visibleState']
27
30
  runtime?: IndicatorRuntimeDescriptor
28
31
  semantic?: IndicatorMetadata<T>['semantic']
32
+ getTitleInfo?: GetTitleInfoFn
29
33
  }
30
34
 
31
35
  type IndicatorDefinitionClass = {
@@ -75,12 +79,41 @@ export function Indicator(config: IndicatorDefinitionConfig) {
75
79
  const normalizedName = normalizeIndicatorId(config.name)
76
80
  removeAliasesFor(normalizedName)
77
81
 
82
+ // 自动生成 stateKey
83
+ const stateKey: StateKey = config.stateKey ?? (
84
+ config.category === 'main'
85
+ ? createIndicatorStateKey(config.name, 'main')
86
+ : (paneId: string) => createIndicatorStateKey(config.name, paneId)
87
+ )
88
+
89
+ // runtime.configKey 默认等于 name
90
+ const runtime = config.runtime && {
91
+ ...config.runtime,
92
+ configKey: config.runtime.configKey ?? config.name,
93
+ }
94
+
95
+ // 有 runtime 时自动生成 updateConfig / applyResult
96
+ const updateConfig = runtime
97
+ ? (config.updateConfig ?? ((scheduler: any, params: any, paneId?: string) => {
98
+ scheduler.updateIndicatorConfig(config.name, params, paneId)
99
+ }))
100
+ : config.updateConfig
101
+
102
+ const applyResult = runtime
103
+ ? (config.applyResult ?? ((host: any, state: any, paneId: string) => {
104
+ host.setSharedState(resolveStateKey(stateKey, paneId), state as any, 'indicator_scheduler')
105
+ }))
106
+ : config.applyResult
107
+
78
108
  indicatorDefinitions.set(normalizedName, {
79
109
  ...config,
110
+ stateKey,
111
+ runtime,
112
+ updateConfig,
113
+ applyResult,
80
114
  rendererFactory,
81
115
  paneIdField: config.paneIdField,
82
116
  allowMainPane: config.allowMainPane,
83
- applyResult: config.applyResult,
84
117
  })
85
118
  indexAlias(config.name, normalizedName)
86
119
  indexAlias(config.displayName, normalizedName)
@@ -86,6 +86,35 @@ export type IndicatorVisibleStateComposer = (
86
86
  context: IndicatorVisibleStateComposeContext,
87
87
  ) => unknown
88
88
 
89
+ /**
90
+ * 标题值项:颜色 + 数值 + 标签
91
+ */
92
+ export interface TitleValueItem {
93
+ label: string
94
+ value: number
95
+ color: string
96
+ }
97
+
98
+ /**
99
+ * 标题信息:指标名称 + 参数 + 各线实时值
100
+ */
101
+ export interface TitleInfo {
102
+ name: string
103
+ params?: number[]
104
+ values?: TitleValueItem[]
105
+ }
106
+
107
+ /**
108
+ * 获取标题信息的回调类型
109
+ */
110
+ export type GetTitleInfoFn = (
111
+ data: KLineData[],
112
+ index: number | null,
113
+ params: Record<string, number | boolean | string>,
114
+ host: PluginHost,
115
+ paneId: string,
116
+ ) => TitleInfo | null
117
+
89
118
  /**
90
119
  * 指标计算描述符
91
120
  * 描述每个指标的计算逻辑,供 IndicatorRuntime 驱动
@@ -95,8 +124,8 @@ export type IndicatorVisibleStateComposer = (
95
124
  * - 自定义运行时指标:无 computeKey,仅主线程 inline 运行
96
125
  */
97
126
  export interface IndicatorRuntimeDescriptor<C = any> {
98
- /** configSnapshot 中的 key,通常等于 name(如 'macd') */
99
- configKey: string
127
+ /** configSnapshot 中的 key,默认等于 name(如 'macd') */
128
+ configKey?: string
100
129
  /** paneId 在 configSnapshot 中的 key(如 'macdPaneId'),可省略 */
101
130
  paneIdKey?: string
102
131
  /** 默认配置值 */
@@ -224,6 +253,14 @@ export interface IndicatorMetadata<T = unknown> {
224
253
  semantic?: {
225
254
  apply?: (chart: IndicatorSemanticChartAdapter, indicator: T) => void
226
255
  }
256
+
257
+ /**
258
+ * 标题信息获取回调(决定 pane 标题栏显示内容)
259
+ * - 副图指标:由 paneTitle 渲染器调用
260
+ * - 主图指标:由 mainIndicatorLegend 渲染器调用
261
+ * 未提供时 fallback 到 displayName
262
+ */
263
+ getTitleInfo?: GetTitleInfoFn
227
264
  }
228
265
 
229
266
  /**
@@ -124,13 +124,14 @@ export class IndicatorRuntime {
124
124
  }
125
125
 
126
126
  addDescriptor(d: IndicatorRuntimeDescriptor): void {
127
- if (this.descriptorMap.has(d.configKey as string)) return
128
- this.descriptorMap.set(d.configKey as string, d)
127
+ const configKey = d.configKey ?? 'unknown'
128
+ if (this.descriptorMap.has(configKey)) return
129
+ this.descriptorMap.set(configKey, d)
129
130
  const def = typeof d.defaultConfig === 'function'
130
131
  ? (d.defaultConfig as () => any)()
131
132
  : d.defaultConfig
132
- this.configMap.set(d.configKey as string, { ...def })
133
- this.dirtyFlags.set(d.configKey as string, true)
133
+ this.configMap.set(configKey, { ...def })
134
+ this.dirtyFlags.set(configKey, true)
134
135
  }
135
136
 
136
137
  setData(data: KLineData[], version: number): void {
@@ -109,7 +109,7 @@ export type {
109
109
  export class IndicatorScheduler {
110
110
  private pluginHost: PluginHost | null = null
111
111
  private visibleRange: VisibleRange = { start: 0, end: 0 }
112
- private activeMainIndicators: Set<string> = new Set()
112
+ private activeMainIndicators = new Map<string, Record<string, number | boolean | string>>()
113
113
 
114
114
  // 版本控制
115
115
  private dataVersion = 0
@@ -172,7 +172,7 @@ export class IndicatorScheduler {
172
172
  this.worker.postMessage({
173
173
  type: 'addDescriptor',
174
174
  descriptor: {
175
- configKey: rt.configKey,
175
+ configKey: rt.configKey ?? meta.name,
176
176
  paneIdKey: rt.paneIdKey,
177
177
  defaultConfig: typeof rt.defaultConfig === 'function' ? (rt.defaultConfig as () => any)() : rt.defaultConfig,
178
178
  computeKey: rt.computeKey,
@@ -468,7 +468,7 @@ export class IndicatorScheduler {
468
468
  this.worker!.postMessage({
469
469
  type: 'addDescriptor',
470
470
  descriptor: {
471
- configKey: rt.configKey,
471
+ configKey: rt.configKey ?? meta.name,
472
472
  paneIdKey: rt.paneIdKey,
473
473
  defaultConfig: typeof rt.defaultConfig === 'function' ? (rt.defaultConfig as () => any)() : rt.defaultConfig,
474
474
  computeKey: rt.computeKey,
@@ -703,13 +703,14 @@ export class IndicatorScheduler {
703
703
  return
704
704
  }
705
705
  const rt = meta.runtime
706
+ const configKey = rt.configKey ?? indicatorId
706
707
  // Update paneId if provided
707
708
  if (paneId !== undefined) {
708
709
  this.paneIdOverrides.set(indicatorId, paneId)
709
710
  }
710
711
  // Merge config
711
- ;(this.configSnapshot as any)[rt.configKey] = {
712
- ...((this.configSnapshot as any)[rt.configKey] ?? {}),
712
+ ;(this.configSnapshot as any)[configKey] = {
713
+ ...((this.configSnapshot as any)[configKey] ?? {}),
713
714
  ...config,
714
715
  }
715
716
  this.configVersion++
@@ -719,8 +720,10 @@ export class IndicatorScheduler {
719
720
  /**
720
721
  * 设置当前激活的主图指标
721
722
  */
722
- setActiveMainIndicators(indicators: string[]): void {
723
- this.activeMainIndicators = new Set(indicators.map(i => i.toLowerCase()))
723
+ setActiveMainIndicators(indicators: Array<{ id: string; params: Record<string, number | boolean | string> }>): void {
724
+ this.activeMainIndicators = new Map(
725
+ indicators.map(i => [i.id.toLowerCase(), i.params])
726
+ )
724
727
  }
725
728
 
726
729
  /**
@@ -731,12 +734,33 @@ export class IndicatorScheduler {
731
734
  const result = computeMainIndicatorPriceRange(
732
735
  this.latestResult,
733
736
  this.visibleRange,
734
- this.activeMainIndicators,
737
+ new Set(this.activeMainIndicators.keys()),
735
738
  (indicatorId) => this.registry.get(indicatorId),
736
739
  )
737
740
  return result
738
741
  }
739
742
 
743
+ /**
744
+ * 获取所有已注册的主图指标
745
+ */
746
+ getMainIndicators(): readonly IndicatorMetadata[] {
747
+ return this.registry.getMainIndicators()
748
+ }
749
+
750
+ /**
751
+ * 检查主图指标是否激活
752
+ */
753
+ isMainIndicatorActive(indicatorId: string): boolean {
754
+ return this.activeMainIndicators.has(indicatorId.toLowerCase())
755
+ }
756
+
757
+ /**
758
+ * 获取主图指标当前参数
759
+ */
760
+ getMainIndicatorParams(indicatorId: string): Record<string, number | boolean | string> {
761
+ return this.activeMainIndicators.get(indicatorId.toLowerCase()) ?? {}
762
+ }
763
+
740
764
  /**
741
765
  * 强制全部重算
742
766
  */
@@ -10,6 +10,7 @@ import { resolveStateKey } from '../../indicators/indicatorMetadata'
10
10
  import type { IndicatorScheduler, ATRSchedulerConfig } from '../../indicators/scheduler'
11
11
  import { createAtrScaleRendererPlugin } from './scale/atr_scale'
12
12
  import { calcATRData } from '../../indicators/calculators'
13
+ import type { KLineData } from '../../../types/price'
13
14
 
14
15
  type LinePoint = { x: number; y: number }
15
16
 
@@ -209,11 +210,14 @@ function drawWithCanvas2D(
209
210
  * 获取 ATR 标题信息(供 paneTitle 使用)
210
211
  */
211
212
  export function getATRTitleInfo(
212
- index: number,
213
- period: number,
213
+ _data: KLineData[],
214
+ index: number | null,
215
+ params: Record<string, number | boolean | string>,
214
216
  pluginHost: PluginHost,
215
- paneId: string = 'sub_ATR',
217
+ paneId: string,
216
218
  ): { name: string; params: number[]; values: Array<{ label: string; value: number; color: string }> } | null {
219
+ if (index === null) return null
220
+ const period = (params.period as number) ?? 14
217
221
  const state = pluginHost.getSharedState<ATRRenderState>(createATRStateKey(paneId))
218
222
  if (!state) return null
219
223
 
@@ -233,18 +237,11 @@ export function getATRTitleInfo(
233
237
  name: 'atr',
234
238
  displayName: 'ATR',
235
239
  category: 'oscillator',
236
- stateKey: createATRStateKey,
237
240
  defaultPaneId: 'sub_ATR',
238
241
  scaleRendererFactory: createAtrScaleRendererPlugin,
239
- updateConfig: (scheduler, params, paneId) => {
240
- (scheduler as IndicatorScheduler).updateIndicatorConfig('atr', params, paneId)
241
- },
242
242
  visibleState: { compose: createNonNegativeSparseVisibleStateComposer('atr', EMPTY_ATR_STATE) },
243
- applyResult: (host, state, paneId) => {
244
- host.setSharedState(createATRStateKey(paneId), state as any, 'indicator_scheduler')
245
- },
243
+ getTitleInfo: getATRTitleInfo,
246
244
  runtime: {
247
- configKey: 'atr',
248
245
  defaultConfig: { period: 14, showATR: true },
249
246
  computeKey: 'calcATRData',
250
247
  compute: (data, c) => calcATRData(data, c.period),
@@ -6,7 +6,7 @@ import { resolveThemeColors } from '../../../tokens'
6
6
  import { BOLL_STATE_KEY, type BOLLRenderState } from '../../indicators/bollState'
7
7
  import { Indicator } from '../../indicators/indicatorDefinitionRegistry'
8
8
  import { resolveStateKey } from '../../indicators/indicatorMetadata'
9
- import type { IndicatorPriceRangeComputer, IndicatorRenderStateComposer } from '../../indicators/indicatorMetadata'
9
+ import type { IndicatorPriceRangeComputer, IndicatorRenderStateComposer, GetTitleInfoFn, TitleInfo, TitleValueItem } from '../../indicators/indicatorMetadata'
10
10
  import type { BOLLSchedulerConfig, IndicatorScheduler } from '../../indicators/scheduler'
11
11
  import { calcBOLLData } from '../../indicators/calculators'
12
12
 
@@ -110,7 +110,7 @@ function buildPriceCacheKey(
110
110
  return `${range.start}|${range.end}|${dataLength}|${lastTimestamp}|${period}`
111
111
  }
112
112
 
113
- function getBOLLStateKey(host: PluginHost | null): string | null {
113
+ export function getBOLLStateKey(host: PluginHost | null): string | null {
114
114
  const scheduler = host?.getService<IndicatorScheduler>('indicatorScheduler')
115
115
  if (!scheduler) {
116
116
  console.warn('[BOLLRenderer] Scheduler not available via service locator')
@@ -155,11 +155,37 @@ const composeBOLLRenderState: IndicatorRenderStateComposer = (bundle, range, tim
155
155
  }
156
156
  }
157
157
 
158
+ export const getBOLLTitleInfo: GetTitleInfoFn = (
159
+ _data: KLineData[],
160
+ index: number | null,
161
+ _params: Record<string, number | boolean | string>,
162
+ pluginHost: PluginHost,
163
+ _paneId: string,
164
+ ): TitleInfo | null => {
165
+ if (index === null) return null
166
+
167
+ const stateKey = getBOLLStateKey(pluginHost)
168
+ if (!stateKey) return null
169
+
170
+ const state = pluginHost?.getSharedState<BOLLRenderState>(stateKey)
171
+ if (!state || state.visibleMin > state.visibleMax) return null
172
+
173
+ const bollPoint = state.series[index]
174
+ if (!bollPoint) return null
175
+
176
+ const values: TitleValueItem[] = [
177
+ { label: 'UP', value: bollPoint.upper, color: '#C83C3C' },
178
+ { label: 'MID', value: bollPoint.middle, color: '#5A8CFF' },
179
+ { label: 'DN', value: bollPoint.lower, color: '#32AA3C' },
180
+ ]
181
+
182
+ return { name: 'BOLL', params: [state.params.period, state.params.multiplier], values }
183
+ }
184
+
158
185
  @Indicator({
159
186
  name: 'boll',
160
187
  displayName: 'BOLL',
161
188
  category: 'main',
162
- stateKey: BOLL_STATE_KEY,
163
189
  defaultPaneId: 'main',
164
190
  mainPane: {
165
191
  rendererName: 'boll',
@@ -169,9 +195,6 @@ const composeBOLLRenderState: IndicatorRenderStateComposer = (bundle, range, tim
169
195
  computePriceRange: computeBOLLPriceRange,
170
196
  composeRenderState: composeBOLLRenderState,
171
197
  },
172
- updateConfig: (scheduler, params) => {
173
- (scheduler as IndicatorScheduler).updateIndicatorConfig('boll', params)
174
- },
175
198
  semantic: {
176
199
  apply: (chart, indicator) => {
177
200
  const params = (indicator as { params?: { period?: number; multiplier?: number } }).params
@@ -181,10 +204,8 @@ const composeBOLLRenderState: IndicatorRenderStateComposer = (bundle, range, tim
181
204
  })
182
205
  },
183
206
  },
184
- applyResult: (host, state, _paneId) => {
185
- host.setSharedState(BOLL_STATE_KEY, state as any, 'indicator_scheduler')
186
- },
187
- runtime: { configKey:'boll', defaultConfig:{period:20,multiplier:2,showUpper:true,showMiddle:true,showLower:true,showBand:true}, computeKey:'calcBOLLData', compute:(data,c)=>calcBOLLData(data,c.period,c.multiplier) },
207
+ runtime: { defaultConfig:{period:20,multiplier:2,showUpper:true,showMiddle:true,showLower:true,showBand:true}, computeKey:'calcBOLLData', compute:(data,c)=>calcBOLLData(data,c.period,c.multiplier) },
208
+ getTitleInfo: getBOLLTitleInfo,
188
209
  })
189
210
  class BOLLDefinition {
190
211
  static rendererFactory = createBOLLRendererPlugin
@@ -9,6 +9,7 @@ import { resolveStateKey } from '../../indicators/indicatorMetadata'
9
9
  import type { IndicatorScheduler, CCISchedulerConfig } from '../../indicators/scheduler'
10
10
  import { createCciScaleRendererPlugin } from './scale/cci_scale'
11
11
  import { calcCCIData } from '../../indicators/calculators'
12
+ import type { KLineData } from '../../../types/price'
12
13
 
13
14
  type LinePoint = { x: number; y: number }
14
15
 
@@ -240,14 +241,15 @@ function drawCCILineWithCanvas2D(
240
241
  * 获取 CCI 标题信息(供 paneTitle 使用)
241
242
  */
242
243
  export function getCCITitleInfo(
243
- index: number,
244
- period: number,
244
+ _data: KLineData[],
245
+ index: number | null,
246
+ params: Record<string, number | boolean | string>,
245
247
  pluginHost: PluginHost,
246
- paneId: string = 'sub_CCI',
247
- theme: 'light' | 'dark' = 'light',
248
- isAsiaMarket?: boolean
248
+ paneId: string,
249
249
  ): { name: string; params: number[]; values: Array<{ label: string; value: number; color: string }> } | null {
250
- const colors = resolveThemeColors(theme, isAsiaMarket)
250
+ if (index === null) return null
251
+ const period = (params.period as number) ?? 14
252
+ const colors = resolveThemeColors('light')
251
253
  const state = pluginHost.getSharedState<CCIRenderState>(createCCIStateKey(paneId))
252
254
  if (!state) return null
253
255
 
@@ -267,18 +269,11 @@ export function getCCITitleInfo(
267
269
  name: 'cci',
268
270
  displayName: 'CCI',
269
271
  category: 'oscillator',
270
- stateKey: createCCIStateKey,
271
272
  defaultPaneId: 'sub_CCI',
272
273
  scaleRendererFactory: createCciScaleRendererPlugin,
273
274
  visibleState: { compose: createCCIVisibleStateComposer('cci', EMPTY_CCI_STATE) },
274
- updateConfig: (scheduler, params, paneId) => {
275
- (scheduler as IndicatorScheduler).updateIndicatorConfig('cci', params, paneId)
276
- },
277
- applyResult: (host, state, paneId) => {
278
- host.setSharedState(createCCIStateKey(paneId), state as any, 'indicator_scheduler')
279
- },
275
+ getTitleInfo: getCCITitleInfo,
280
276
  runtime: {
281
- configKey: 'cci',
282
277
  defaultConfig: { period: 14, showCCI: true },
283
278
  computeKey: 'calcCCIData',
284
279
  compute: (data, c) => calcCCIData(data, c.period),
@@ -2,6 +2,8 @@ import type { RendererPluginWithHost, RenderContext, PluginHost } from '../../..
2
2
  import { RENDERER_PRIORITY } from '../../../plugin'
3
3
  import type { ChaikinVolRenderState } from '../../indicators/chaikinVolState'
4
4
  import { createChaikinVolStateKey, EMPTY_CHAIKIN_VOL_STATE } from '../../indicators/chaikinVolState'
5
+ import type { TitleInfo } from '../../indicators/indicatorMetadata'
6
+ import type { KLineData } from '../../../types/price'
5
7
  import { Indicator } from '../../indicators/indicatorDefinitionRegistry'
6
8
  import { resolveStateKey } from '../../indicators/indicatorMetadata'
7
9
  import { createSparseVisibleStateComposer } from '../../indicators/visibleStateComposers'
@@ -124,22 +126,36 @@ export function createChaikinVolRendererPlugin(options: { paneId?: string } = {}
124
126
  }
125
127
  }
126
128
 
129
+ export function getChaikinVolTitleInfo(
130
+ _data: KLineData[],
131
+ index: number | null,
132
+ params: Record<string, number | boolean | string>,
133
+ host: PluginHost,
134
+ paneId: string,
135
+ ): TitleInfo | null {
136
+ if (index === null) return null
137
+ const emaPeriod = (params.emaPeriod as number) ?? 10
138
+ const rocPeriod = (params.rocPeriod as number) ?? 10
139
+ const state = host.getSharedState<ChaikinVolRenderState>(createChaikinVolStateKey(paneId))
140
+ const value = state?.series[index]
141
+ if (value === undefined) return null
142
+
143
+ return {
144
+ name: 'ChaikinVol',
145
+ params: [emaPeriod, rocPeriod],
146
+ values: [{ label: 'ChaikinVol', value, color: CHAIKIN_VOL_COLOR }],
147
+ }
148
+ }
149
+
127
150
  @Indicator({
128
151
  name: 'chaikinVol',
129
152
  displayName: 'ChaikinVol',
130
153
  category: 'oscillator',
131
- stateKey: createChaikinVolStateKey,
132
154
  defaultPaneId: 'sub_ChaikinVol',
133
155
  visibleState: { compose: createSparseVisibleStateComposer('chaikinVol', EMPTY_CHAIKIN_VOL_STATE) },
134
156
  scale: { indicatorKey: 'chaikinVol', label: 'ChaikinVol', decimals: 2 },
135
- updateConfig: (scheduler, params, paneId) => {
136
- (scheduler as IndicatorScheduler).updateIndicatorConfig('chaikinVol', params, paneId)
137
- },
138
- applyResult: (host, state, paneId) => {
139
- host.setSharedState(createChaikinVolStateKey(paneId), state as any, 'indicator_scheduler')
140
- },
157
+ getTitleInfo: getChaikinVolTitleInfo,
141
158
  runtime: {
142
- configKey: 'chaikinVol',
143
159
  defaultConfig: { emaPeriod: 10, rocPeriod: 10, showChaikinVol: true },
144
160
  computeKey: 'calcChaikinVolData',
145
161
  compute: (data, c) => calcChaikinVolData(data, c.emaPeriod, c.rocPeriod),
@@ -2,6 +2,8 @@ import type { RendererPluginWithHost, RenderContext, PluginHost } from '../../..
2
2
  import { RENDERER_PRIORITY } from '../../../plugin'
3
3
  import type { CMFRenderState } from '../../indicators/cmfState'
4
4
  import { createCMFStateKey, EMPTY_CMF_STATE } from '../../indicators/cmfState'
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'
@@ -123,22 +125,35 @@ export function createCMFRendererPlugin(options: { paneId?: string } = {}): Rend
123
125
  }
124
126
  }
125
127
 
128
+ export function getCMFTitleInfo(
129
+ _data: KLineData[],
130
+ index: number | null,
131
+ params: Record<string, number | boolean | string>,
132
+ host: PluginHost,
133
+ paneId: string,
134
+ ): TitleInfo | null {
135
+ if (index === null) return null
136
+ const period = (params.period as number) ?? 20
137
+ const state = host.getSharedState<CMFRenderState>(createCMFStateKey(paneId))
138
+ const value = state?.series[index]
139
+ if (value === undefined) return null
140
+
141
+ return {
142
+ name: 'CMF',
143
+ params: [period],
144
+ values: [{ label: 'CMF', value, color: CMF_COLOR }],
145
+ }
146
+ }
147
+
126
148
  @Indicator({
127
149
  name: 'cmf',
128
150
  displayName: 'CMF',
129
151
  category: 'volume',
130
- stateKey: createCMFStateKey,
131
152
  defaultPaneId: 'sub_CMF',
132
153
  visibleState: { compose: createFixedRangeSparseVisibleStateComposer('cmf', EMPTY_CMF_STATE) },
133
154
  scale: { indicatorKey: 'cmf', label: 'CMF', decimals: 4 },
134
- updateConfig: (scheduler, params, paneId) => {
135
- (scheduler as IndicatorScheduler).updateIndicatorConfig('cmf', params, paneId)
136
- },
137
- applyResult: (host, state, paneId) => {
138
- host.setSharedState(createCMFStateKey(paneId), state as any, 'indicator_scheduler')
139
- },
155
+ getTitleInfo: getCMFTitleInfo,
140
156
  runtime: {
141
- configKey: 'cmf',
142
157
  defaultConfig: { period: 20, showCMF: true },
143
158
  computeKey: 'calcCMFData',
144
159
  compute: (data, c) => calcCMFData(data, c.period),
@@ -1,9 +1,10 @@
1
1
  import type { RendererPluginWithHost, RenderContext, PluginHost } from '../../../plugin'
2
+ import type { KLineData } from '../../../types/price'
2
3
  import { RENDERER_PRIORITY } from '../../../plugin'
3
4
  import type { DEMARenderState } from '../../indicators/demaState'
4
5
  import { createDEMAStateKey, EMPTY_DEMA_STATE } from '../../indicators/demaState'
5
6
  import { Indicator } from '../../indicators/indicatorDefinitionRegistry'
6
- import { resolveStateKey } from '../../indicators/indicatorMetadata'
7
+ import { resolveStateKey, type TitleInfo, type TitleValueItem, type GetTitleInfoFn } from '../../indicators/indicatorMetadata'
7
8
  import { createSparseVisibleStateComposer } from '../../indicators/visibleStateComposers'
8
9
  import type { IndicatorScheduler, DEMASchedulerConfig } from '../../indicators/scheduler'
9
10
  import { calcDEMAData } from '../../indicators/calculators'
@@ -121,23 +122,40 @@ export function createDEMARendererPlugin(options: DEMARendererOptions = {}): Ren
121
122
  }
122
123
  }
123
124
 
125
+ export const getDEMATitleInfo: GetTitleInfoFn = (
126
+ _data: KLineData[],
127
+ index: number | null,
128
+ _params: Record<string, number | boolean | string>,
129
+ pluginHost: PluginHost,
130
+ paneId: string,
131
+ ): TitleInfo | null => {
132
+ if (index === null) return null
133
+
134
+ const stateKey = createDEMAStateKey(paneId)
135
+ const state = pluginHost?.getSharedState<DEMARenderState>(stateKey)
136
+ if (!state || state.visibleMin > state.visibleMax) return null
137
+
138
+ const value = state.series[index]
139
+ if (value === undefined) return null
140
+
141
+ return {
142
+ name: 'DEMA',
143
+ params: [state.params.period],
144
+ values: [{ label: 'DEMA', value, color: '#6366f1' }],
145
+ }
146
+ }
147
+
124
148
  @Indicator({
125
149
  name: 'dema',
126
150
  displayName: 'DEMA',
151
+ getTitleInfo: getDEMATitleInfo,
127
152
  category: 'main',
128
- stateKey: createDEMAStateKey,
129
153
  defaultPaneId: 'main',
130
154
  allowMainPane: true,
131
155
  mainPane: { rendererName: 'dema_main', toActiveConfig: (params, active) => ({ ...params, showDEMA: active }) },
132
156
  visibleState: { compose: createSparseVisibleStateComposer('dema', EMPTY_DEMA_STATE) },
133
157
  scale: { indicatorKey: 'dema', label: 'DEMA', decimals: 2 },
134
- updateConfig: (scheduler, params, paneId) => {
135
- (scheduler as IndicatorScheduler).updateIndicatorConfig('dema', params, paneId)
136
- },
137
- applyResult: (host, state, paneId) => {
138
- host.setSharedState(createDEMAStateKey(paneId), state as any, 'indicator_scheduler')
139
- },
140
- runtime: { configKey:'dema', defaultConfig:{period:14,showDEMA:true}, computeKey:'calcDEMAData', compute:(data,c)=>calcDEMAData(data,c.period) },
158
+ runtime: { defaultConfig:{period:14,showDEMA:true}, computeKey:'calcDEMAData', compute:(data,c)=>calcDEMAData(data,c.period) },
141
159
  })
142
160
  class DEMADefinition {
143
161
  static rendererFactory = createDEMARendererPlugin
@@ -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 { DonchianRenderState } from '../../indicators/donchianState'
4
5
  import { createDonchianStateKey, EMPTY_DONCHIAN_STATE } from '../../indicators/donchianState'
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, DonchianSchedulerConfig } from '../../indicators/scheduler'
8
9
  import { calcDonchianData } 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 getDonchianTitleInfo(
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<DonchianRenderState>(createDonchianStateKey(paneId))
136
+ const p = state?.series[index]
137
+ if (!p) return null
138
+
139
+ return {
140
+ name: 'Donchian',
141
+ params: [(params.period as number) ?? 20],
142
+ values: [
143
+ { label: 'Upper', value: p.upper, color: '#0891b2' },
144
+ { label: 'Mid', value: p.middle, color: '#94a3b8' },
145
+ { label: 'Lower', value: p.lower, color: '#0891b2' },
146
+ ],
147
+ }
148
+ }
149
+
126
150
  @Indicator({
127
151
  name: 'donchian',
128
152
  displayName: 'Donchian',
153
+ getTitleInfo: getDonchianTitleInfo,
129
154
  category: 'main',
130
- stateKey: createDonchianStateKey,
131
155
  defaultPaneId: 'main',
132
156
  allowMainPane: true,
133
157
  mainPane: { rendererName: 'donchian_main', toActiveConfig: (params, active) => ({ ...params, showUpper: active, showMiddle: active, showLower: active }) },
134
158
  scale: { indicatorKey: 'donchian', label: 'Donchian', decimals: 2 },
135
159
  visibleState: { compose: createBandVisibleStateComposer('donchian', EMPTY_DONCHIAN_STATE, 'lower', 'upper') },
136
- updateConfig: (scheduler, params, paneId) => {
137
- (scheduler as IndicatorScheduler).updateIndicatorConfig('donchian', params, paneId)
138
- },
139
- applyResult: (host, state, paneId) => {
140
- host.setSharedState(createDonchianStateKey(paneId), state as any, 'indicator_scheduler')
141
- },
142
- runtime: { configKey:'donchian', defaultConfig:{period:20,showUpper:true,showMiddle:true,showLower:true}, computeKey:'calcDonchianData', compute:(data,c)=>calcDonchianData(data,c.period) },
160
+ runtime: { defaultConfig:{period:20,showUpper:true,showMiddle:true,showLower:true}, computeKey:'calcDonchianData', compute:(data,c)=>calcDonchianData(data,c.period) },
143
161
  })
144
162
  class DonchianDefinition {
145
163
  static rendererFactory = createDonchianRendererPlugin