@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
@@ -8,11 +8,13 @@ import { ENE_STATE_KEY } from '@/core/indicators/eneState'
8
8
  import type { PluginHost, RenderContext, RendererPluginWithHost } from '@/plugin'
9
9
  import type { KLineData } from '@/types/price'
10
10
  import type { Pane } from '@/core/layout/pane'
11
+ import type { GetTitleInfoFn, TitleInfo, TitleValueItem } from '@/engine/indicators/indicatorMetadata'
12
+ import type { IndicatorScheduler } from '@/engine/indicators/indicatorScheduler'
11
13
 
12
14
  // Type helper for tests - we know these methods exist on the implementation
13
15
  interface TestableLegendRenderer extends RendererPluginWithHost {
14
16
  draw: (context: RenderContext) => void
15
- getConfig: () => { yPaddingPx: number; indicators: Record<string, { enabled: boolean; params: Record<string, unknown> }> }
17
+ getConfig: () => { yPaddingPx: number }
16
18
  setConfig: (config: Record<string, unknown>) => void
17
19
  }
18
20
 
@@ -33,10 +35,73 @@ function createMockCanvasContext(): CanvasRenderingContext2D {
33
35
  } as unknown as CanvasRenderingContext2D
34
36
  }
35
37
 
38
+ /**
39
+ * Create a mock scheduler that returns indicator metadata with getTitleInfo.
40
+ * Keys are case-insensitive (matching real IndicatorScheduler behavior).
41
+ */
42
+ function createMockScheduler(
43
+ metadataMap: Record<string, { getTitleInfo: GetTitleInfoFn }>,
44
+ activeMainIndicators?: string[]
45
+ ): IndicatorScheduler {
46
+ const activeSet = new Set((activeMainIndicators ?? ['ma']).map((i: string) => i.toLowerCase()))
47
+ return {
48
+ getIndicatorMetadata: vi.fn((id: string) => metadataMap[id.toLowerCase()] ?? null),
49
+ getMainIndicators: vi.fn(() => Object.entries(metadataMap).map(([id, meta]) => ({
50
+ name: id,
51
+ getTitleInfo: meta.getTitleInfo,
52
+ category: 'main',
53
+ }))),
54
+ isMainIndicatorActive: vi.fn((id: string) => activeSet.has(id.toLowerCase())),
55
+ getMainIndicatorParams: vi.fn(() => ({})),
56
+ } as unknown as IndicatorScheduler
57
+ }
58
+
59
+ /**
60
+ * Create a mock getTitleInfo for MA that reads from shared state
61
+ */
62
+ function createMAGetTitleInfo(): GetTitleInfoFn {
63
+ return (data, index, params, host, paneId): TitleInfo => {
64
+ const state = host.getSharedState<MARenderState>(MA_STATE_KEY)
65
+ if (!state) return null
66
+
67
+ const ci = index ?? (state.series[5]?.length ?? 100) - 1
68
+ const values: TitleValueItem[] = []
69
+ for (const period of state.enabledPeriods) {
70
+ const v = state.series[period]?.[ci]
71
+ if (v !== undefined) {
72
+ values.push({
73
+ label: `MA${period}`,
74
+ value: v,
75
+ color: '#888888',
76
+ })
77
+ }
78
+ }
79
+ return { name: 'MA', values: values.length > 0 ? values : undefined }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Create simple mock getTitleInfo for BOLL/EXPMA/ENE
85
+ */
86
+ function createSimpleGetTitleInfo(name: string): GetTitleInfoFn {
87
+ return (): TitleInfo => ({
88
+ name,
89
+ params: [20, 2],
90
+ values: [
91
+ { label: 'MID', value: 100.00, color: '#FF0000' },
92
+ { label: 'UP', value: 120.00, color: '#00FF00' },
93
+ { label: 'DN', value: 80.00, color: '#0000FF' },
94
+ ],
95
+ })
96
+ }
97
+
36
98
  /**
37
99
  * 创建 mock PluginHost
38
100
  */
39
- function createMockPluginHost(state?: MARenderState): PluginHost {
101
+ function createMockPluginHost(
102
+ state?: MARenderState,
103
+ scheduler?: IndicatorScheduler
104
+ ): PluginHost {
40
105
  return {
41
106
  setSharedState: vi.fn(),
42
107
  getSharedState: vi.fn(<T>(key: string): T | undefined => {
@@ -59,6 +124,10 @@ function createMockPluginHost(state?: MARenderState): PluginHost {
59
124
  off: vi.fn(),
60
125
  once: vi.fn(),
61
126
  emit: vi.fn(),
127
+ getService: vi.fn((name: string) => {
128
+ if (name === 'indicatorScheduler') return scheduler
129
+ return undefined
130
+ }),
62
131
  } as unknown as PluginHost
63
132
  }
64
133
 
@@ -149,14 +218,9 @@ describe('createMainIndicatorLegendRendererPlugin', () => {
149
218
  expect(typeof plugin.onInstall).toBe('function')
150
219
  })
151
220
 
152
- it('should declare all indicator namespace keys', () => {
221
+ it('should not declare any namespace keys (individual renderers own their state)', () => {
153
222
  const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
154
- expect(plugin.getDeclaredNamespaces()).toEqual([
155
- MA_STATE_KEY,
156
- BOLL_STATE_KEY,
157
- EXPMA_STATE_KEY,
158
- ENE_STATE_KEY,
159
- ])
223
+ expect(plugin.getDeclaredNamespaces()).toEqual([])
160
224
  })
161
225
  })
162
226
 
@@ -168,17 +232,13 @@ describe('MainIndicatorLegend draw', () => {
168
232
  ctx = createMockCanvasContext()
169
233
  })
170
234
 
171
- it('should not draw MA when MA is disabled', () => {
235
+ it('should not draw MA when MA is not active', () => {
172
236
  const state = createTestMARenderState()
173
- const mockHost = createMockPluginHost(state)
237
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, [])
238
+ const mockHost = createMockPluginHost(state, scheduler)
174
239
  plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
175
240
  plugin.onInstall(mockHost)
176
241
 
177
- // Disable MA
178
- plugin.setConfig({
179
- indicators: { MA: { enabled: false, params: {} } },
180
- })
181
-
182
242
  const context = createMockRenderContext(ctx)
183
243
  plugin.draw(context)
184
244
 
@@ -190,7 +250,8 @@ describe('MainIndicatorLegend draw', () => {
190
250
 
191
251
  it('should draw MA values from StateStore', () => {
192
252
  const state = createTestMARenderState()
193
- const mockHost = createMockPluginHost(state)
253
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
254
+ const mockHost = createMockPluginHost(state, scheduler)
194
255
  plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
195
256
  plugin.onInstall(mockHost)
196
257
 
@@ -215,7 +276,8 @@ describe('MainIndicatorLegend draw', () => {
215
276
  },
216
277
  enabledPeriods: [5],
217
278
  })
218
- const mockHost = createMockPluginHost(state)
279
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
280
+ const mockHost = createMockPluginHost(state, scheduler)
219
281
  plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
220
282
  plugin.onInstall(mockHost)
221
283
 
@@ -227,7 +289,7 @@ describe('MainIndicatorLegend draw', () => {
227
289
 
228
290
  // Should show value 150 at index 50 (100 + 50)
229
291
  const maValueCalls = fillTextCalls.filter(call =>
230
- String(call[0]).includes('150.00')
292
+ String(call[0]).includes('150.000')
231
293
  )
232
294
  expect(maValueCalls.length).toBeGreaterThan(0)
233
295
  })
@@ -239,7 +301,8 @@ describe('MainIndicatorLegend draw', () => {
239
301
  },
240
302
  enabledPeriods: [5],
241
303
  })
242
- const mockHost = createMockPluginHost(state)
304
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
305
+ const mockHost = createMockPluginHost(state, scheduler)
243
306
  plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
244
307
  plugin.onInstall(mockHost)
245
308
 
@@ -261,7 +324,7 @@ describe('MainIndicatorLegend draw', () => {
261
324
 
262
325
  // Should show last value (109) at index 9
263
326
  const maValueCalls = fillTextCalls.filter(call =>
264
- String(call[0]).includes('109.00')
327
+ String(call[0]).includes('109.000')
265
328
  )
266
329
  expect(maValueCalls.length).toBeGreaterThan(0)
267
330
  })
@@ -283,27 +346,29 @@ describe('MainIndicatorLegend draw', () => {
283
346
  visibleMax: -Infinity,
284
347
  enabledPeriods: [],
285
348
  })
286
- const mockHost = createMockPluginHost(state)
349
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
350
+ const mockHost = createMockPluginHost(state, scheduler)
287
351
  plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
288
352
  plugin.onInstall(mockHost)
289
353
 
290
354
  const context = createMockRenderContext(ctx)
291
355
  plugin.draw(context)
292
356
 
293
- // Should not draw any MA values
357
+ // Should not draw any MA period values (name 'MA' may still appear but no period texts)
294
358
  const fillTextCalls = vi.mocked(ctx.fillText).mock.calls
295
359
  const ma5Calls = fillTextCalls.filter(call => String(call[0]).includes('MA5'))
296
360
  expect(ma5Calls).toHaveLength(0)
297
361
  })
298
362
 
299
- it('should display values with 2 decimal places', () => {
363
+ it('should display values with 3 decimal places', () => {
300
364
  const state = createTestMARenderState({
301
365
  series: {
302
366
  5: Array.from({ length: 100 }, () => 123.4567),
303
367
  },
304
368
  enabledPeriods: [5],
305
369
  })
306
- const mockHost = createMockPluginHost(state)
370
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
371
+ const mockHost = createMockPluginHost(state, scheduler)
307
372
  plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
308
373
  plugin.onInstall(mockHost)
309
374
 
@@ -314,29 +379,31 @@ describe('MainIndicatorLegend draw', () => {
314
379
 
315
380
  // Should show formatted value
316
381
  const formattedValueCalls = fillTextCalls.filter(call =>
317
- String(call[0]).includes('123.46')
382
+ String(call[0]).includes('123.457')
318
383
  )
319
384
  expect(formattedValueCalls.length).toBeGreaterThan(0)
320
385
  })
321
386
 
322
387
  it('should use correct colors for each MA period', () => {
323
388
  const state = createTestMARenderState()
324
- const mockHost = createMockPluginHost(state)
389
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
390
+ const mockHost = createMockPluginHost(state, scheduler)
325
391
  plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
326
392
  plugin.onInstall(mockHost)
327
393
 
328
394
  const context = createMockRenderContext(ctx)
329
395
  plugin.draw(context)
330
396
 
331
- const fillStyleSetter = vi.mocked(ctx).fillStyle as unknown as ReturnType<typeof vi.fn>
332
-
333
- // Should have set fillStyle for each period's color
334
- expect(fillStyleSetter).not.toBeUndefined()
397
+ // Should have drawn MA period values with proper colors
398
+ const fillTextCalls = vi.mocked(ctx.fillText).mock.calls
399
+ const ma5Calls = fillTextCalls.filter(call => String(call[0]).includes('MA5'))
400
+ expect(ma5Calls.length).toBeGreaterThan(0)
335
401
  })
336
402
 
337
403
  it('should save and restore context', () => {
338
404
  const state = createTestMARenderState()
339
- const mockHost = createMockPluginHost(state)
405
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
406
+ const mockHost = createMockPluginHost(state, scheduler)
340
407
  plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 }) as TestableLegendRenderer
341
408
  plugin.onInstall(mockHost)
342
409
 
@@ -357,10 +424,15 @@ describe('MainIndicatorLegend MA data source', () => {
357
424
  enabledPeriods: [5],
358
425
  })
359
426
  const mockGetSharedState = vi.fn().mockReturnValue(state)
427
+ const scheduler = createMockScheduler({ ma: { getTitleInfo: createMAGetTitleInfo() } }, ['ma'])
360
428
  const mockHost = {
361
429
  setSharedState: vi.fn(),
362
430
  getSharedState: mockGetSharedState,
363
431
  clearByOwner: vi.fn(),
432
+ getService: vi.fn((name: string) => {
433
+ if (name === 'indicatorScheduler') return scheduler
434
+ return undefined
435
+ }),
364
436
  } as unknown as PluginHost
365
437
 
366
438
  const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
@@ -373,7 +445,7 @@ describe('MainIndicatorLegend MA data source', () => {
373
445
  })
374
446
  plugin.draw(context)
375
447
 
376
- // Verify it read from StateStore
448
+ // Verify it read from StateStore (via getTitleInfo calling getSharedState)
377
449
  expect(mockGetSharedState).toHaveBeenCalledWith(MA_STATE_KEY)
378
450
 
379
451
  const fillTextCalls = vi.mocked(ctx.fillText).mock.calls
@@ -387,14 +459,12 @@ describe('MainIndicatorLegend MA data source', () => {
387
459
  })
388
460
 
389
461
  describe('MainIndicatorLegend config management', () => {
390
- it('getConfig should return current config', () => {
462
+ it('getConfig should return current yPaddingPx', () => {
391
463
  const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
392
464
 
393
465
  const config = plugin.getConfig()
394
466
 
395
467
  expect(config.yPaddingPx).toBe(20)
396
- expect(config.indicators.MA.enabled).toBe(true)
397
- expect(config.indicators.BOLL.enabled).toBe(false)
398
468
  })
399
469
 
400
470
  it('setConfig should update yPaddingPx', () => {
@@ -405,37 +475,18 @@ describe('MainIndicatorLegend config management', () => {
405
475
  const config = plugin.getConfig()
406
476
  expect(config.yPaddingPx).toBe(30)
407
477
  })
408
-
409
- it('setConfig should merge indicator config', () => {
410
- const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
411
-
412
- plugin.setConfig({
413
- indicators: {
414
- MA: { enabled: false, params: {} },
415
- BOLL: { enabled: true, params: { period: 26 } },
416
- },
417
- })
418
-
419
- const config = plugin.getConfig()
420
- expect(config.indicators.MA.enabled).toBe(false)
421
- expect(config.indicators.BOLL.enabled).toBe(true)
422
- expect(config.indicators.BOLL.params.period).toBe(26)
423
- })
424
478
  })
425
479
 
426
480
  describe('MainIndicatorLegend with other indicators', () => {
427
- it('should draw BOLL when enabled', () => {
428
- const mockHost = createMockPluginHost(undefined)
481
+ it('should draw BOLL when active', () => {
482
+ const scheduler = createMockScheduler(
483
+ { boll: { getTitleInfo: createSimpleGetTitleInfo('BOLL') } },
484
+ ['boll']
485
+ )
486
+ const mockHost = createMockPluginHost(undefined, scheduler)
429
487
  const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
430
488
  plugin.onInstall(mockHost)
431
489
 
432
- plugin.setConfig({
433
- indicators: {
434
- MA: { enabled: false, params: {} },
435
- BOLL: { enabled: true, params: { period: 20, multiplier: 2 } },
436
- },
437
- })
438
-
439
490
  const ctx = createMockCanvasContext()
440
491
  const context = createMockRenderContext(ctx)
441
492
  plugin.draw(context)
@@ -449,18 +500,15 @@ describe('MainIndicatorLegend with other indicators', () => {
449
500
  expect(bollLabelCalls.length).toBeGreaterThan(0)
450
501
  })
451
502
 
452
- it('should draw EXPMA when enabled', () => {
453
- const mockHost = createMockPluginHost(undefined)
503
+ it('should draw EXPMA when active', () => {
504
+ const scheduler = createMockScheduler(
505
+ { expma: { getTitleInfo: createSimpleGetTitleInfo('EXPMA') } },
506
+ ['expma']
507
+ )
508
+ const mockHost = createMockPluginHost(undefined, scheduler)
454
509
  const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
455
510
  plugin.onInstall(mockHost)
456
511
 
457
- plugin.setConfig({
458
- indicators: {
459
- MA: { enabled: false, params: {} },
460
- EXPMA: { enabled: true, params: { fastPeriod: 12, slowPeriod: 50 } },
461
- },
462
- })
463
-
464
512
  const ctx = createMockCanvasContext()
465
513
  const context = createMockRenderContext(ctx)
466
514
  plugin.draw(context)
@@ -474,18 +522,15 @@ describe('MainIndicatorLegend with other indicators', () => {
474
522
  expect(expmaLabelCalls.length).toBeGreaterThan(0)
475
523
  })
476
524
 
477
- it('should draw ENE when enabled', () => {
478
- const mockHost = createMockPluginHost(undefined)
525
+ it('should draw ENE when active', () => {
526
+ const scheduler = createMockScheduler(
527
+ { ene: { getTitleInfo: createSimpleGetTitleInfo('ENE') } },
528
+ ['ene']
529
+ )
530
+ const mockHost = createMockPluginHost(undefined, scheduler)
479
531
  const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
480
532
  plugin.onInstall(mockHost)
481
533
 
482
- plugin.setConfig({
483
- indicators: {
484
- MA: { enabled: false, params: {} },
485
- ENE: { enabled: true, params: { period: 10, deviation: 11 } },
486
- },
487
- })
488
-
489
534
  const ctx = createMockCanvasContext()
490
535
  const context = createMockRenderContext(ctx)
491
536
  plugin.draw(context)
@@ -498,4 +543,22 @@ describe('MainIndicatorLegend with other indicators', () => {
498
543
  )
499
544
  expect(eneLabelCalls.length).toBeGreaterThan(0)
500
545
  })
546
+
547
+ it('should draw any registered main indicator when active (WMA example)', () => {
548
+ const scheduler = createMockScheduler(
549
+ { wma: { getTitleInfo: createSimpleGetTitleInfo('WMA') } },
550
+ ['wma']
551
+ )
552
+ const mockHost = createMockPluginHost(undefined, scheduler)
553
+ const plugin = createMainIndicatorLegendRendererPlugin({ yPaddingPx: 20 })
554
+ plugin.onInstall(mockHost)
555
+
556
+ const ctx = createMockCanvasContext()
557
+ const context = createMockRenderContext(ctx)
558
+ plugin.draw(context)
559
+
560
+ const fillTextCalls = vi.mocked(ctx.fillText).mock.calls
561
+ const wmaLabelCalls = fillTextCalls.filter(call => String(call[0]).includes('WMA'))
562
+ expect(wmaLabelCalls.length).toBeGreaterThan(0)
563
+ })
501
564
  })
@@ -2,8 +2,33 @@ import type { RendererPluginWithHost, RenderContext, PluginHost } from '../../pl
2
2
  import { RENDERER_PRIORITY } from '../../plugin'
3
3
  import { resolveThemeColors } from '../../tokens'
4
4
  import { getFont, setCanvasFont } from '../theme/fonts'
5
- import { SUB_PANE_INDICATOR_CONFIGS } from './Indicator/subPaneConfig'
6
5
  import type { SubIndicatorType } from './Indicator'
6
+ import type { KLineData } from '../../types/price'
7
+ import type { TitleInfo } from '../indicators/indicatorMetadata'
8
+ import type { IndicatorScheduler } from '../indicators/scheduler'
9
+
10
+ /**
11
+ * @deprecated 请从 indicatorMetadata 导入 TitleInfo
12
+ */
13
+ export type { TitleInfo, TitleValueItem } from '../indicators/indicatorMetadata'
14
+
15
+ function getVolumeTitleInfo(
16
+ data: KLineData[],
17
+ index: number | null,
18
+ _params: Record<string, number | boolean | string>,
19
+ _host: PluginHost,
20
+ _paneId: string,
21
+ ): TitleInfo | null {
22
+ if (index === null) return null
23
+ const kline = data[index]
24
+ if (!kline || kline.volume === undefined) return null
25
+ const color = kline.open < kline.close ? '#ef4444' : '#22c55e'
26
+ return {
27
+ name: 'VOL',
28
+ params: [],
29
+ values: [{ label: 'VOL', value: kline.volume, color }],
30
+ }
31
+ }
7
32
 
8
33
  const textWidthCache = new Map<string, number>()
9
34
  const TEXT_WIDTH_CACHE_LIMIT = 256
@@ -23,18 +48,6 @@ function measureTextWidth(ctx: CanvasRenderingContext2D, text: string): number {
23
48
  return width
24
49
  }
25
50
 
26
- export interface TitleValueItem {
27
- label: string
28
- value: number
29
- color: string
30
- }
31
-
32
- export interface TitleInfo {
33
- name: string
34
- params?: number[]
35
- values?: TitleValueItem[]
36
- }
37
-
38
51
  export interface PaneTitleOptions {
39
52
  paneId: string
40
53
  title: string
@@ -68,7 +81,7 @@ export function createPaneTitleRendererPlugin(options: PaneTitleOptions): Render
68
81
 
69
82
  const fontSize = 12
70
83
  const x = 12
71
- const y = currentOptions.yOffset ?? fontSize
84
+ let y = currentOptions.yOffset ?? fontSize
72
85
  const gap = 8
73
86
 
74
87
  overlayCtx.save()
@@ -76,11 +89,22 @@ export function createPaneTitleRendererPlugin(options: PaneTitleOptions): Render
76
89
  overlayCtx.textAlign = 'left'
77
90
  overlayCtx.textBaseline = 'top'
78
91
 
79
- const config = SUB_PANE_INDICATOR_CONFIGS[currentOptions.indicatorId]
80
92
  const crosshairIndex = context.crosshairIndex ?? null
81
- const titleInfo = config && pluginHost
82
- ? config.getTitleInfo(context.data, crosshairIndex, currentOptions.params as Record<string, number | boolean | string>, pluginHost, currentOptions.paneId)
83
- : null
93
+ const castParams = currentOptions.params as Record<string, number | boolean | string>
94
+ const klineData = context.data as KLineData[]
95
+
96
+ // 优先从 indicator metadata registry 获取 getTitleInfo
97
+ let titleInfo: TitleInfo | null = null
98
+ const scheduler = pluginHost?.getService<IndicatorScheduler>('indicatorScheduler')
99
+ const meta = scheduler?.getIndicatorMetadata(currentOptions.indicatorId)
100
+ if (meta?.getTitleInfo && pluginHost) {
101
+ titleInfo = meta.getTitleInfo(klineData, crosshairIndex, castParams, pluginHost, currentOptions.paneId)
102
+ }
103
+
104
+ // fallback: VOLUME 不是注册指标,内联处理
105
+ if (!titleInfo && pluginHost) {
106
+ titleInfo = getVolumeTitleInfo(klineData, crosshairIndex, castParams, pluginHost, currentOptions.paneId)
107
+ }
84
108
 
85
109
  if (titleInfo) {
86
110
  let currentX = x
@@ -99,6 +123,7 @@ export function createPaneTitleRendererPlugin(options: PaneTitleOptions): Render
99
123
  }
100
124
 
101
125
  if (titleInfo.values && titleInfo.values.length > 0) {
126
+ y += 1
102
127
  for (const item of titleInfo.values) {
103
128
  const valueText = `${item.label} ${item.value.toFixed(3)}`
104
129
  overlayCtx.fillStyle = item.color
@@ -232,10 +232,8 @@ function judgeColor(dayData: KLineData, upColor: string, downColor: string, neut
232
232
  name: 'volume',
233
233
  displayName: 'VOL',
234
234
  category: 'volume',
235
- stateKey: (paneId: string) => createIndicatorStateKey('volume', paneId),
236
235
  defaultPaneId: 'sub',
237
236
  scaleRendererFactory: createVolumeScaleRendererPlugin,
238
- updateConfig: () => {},
239
237
  })
240
238
  class VolumeIndicatorDefinition {
241
239
  static rendererFactory = createVolumeRendererPlugin
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = "0.7.12"
1
+ export const VERSION = "0.7.13"
@@ -1,13 +0,0 @@
1
- import type { RendererPluginWithHost } from '../../../plugin';
2
- export interface MACDLegendOptions {
3
- /** Y 轴内边距(与主图保持一致) */
4
- yPaddingPx?: number;
5
- /** 目标 pane ID(默认 'sub') */
6
- paneId?: string;
7
- }
8
- /**
9
- * 创建 MACD 图例渲染器插件
10
- * 从 StateStore 读取 MACD 状态,不再在 draw 时计算
11
- */
12
- export declare function createMACDLegendRendererPlugin(options?: MACDLegendOptions): RendererPluginWithHost;
13
- //# sourceMappingURL=macdLegend.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"macdLegend.d.ts","sourceRoot":"","sources":["../../../../src/engine/renderers/Indicator/macdLegend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAA6B,MAAM,iBAAiB,CAAA;AAyBxF,MAAM,WAAW,iBAAiB;IAC9B,sBAAsB;IACtB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,GAAE,iBAAsB,GAAG,sBAAsB,CAwGtG"}
@@ -1,116 +0,0 @@
1
- import { RENDERER_PRIORITY } from '../../../plugin';
2
- import { resolveThemeColors } from '../../../tokens';
3
- import { getFont, setCanvasFont } from '../../theme/fonts';
4
- import { createMACDStateKey } from '../../indicators/macdState';
5
- const textWidthCache = new Map();
6
- const TEXT_WIDTH_CACHE_LIMIT = 256;
7
- function measureTextWidth(ctx, text) {
8
- const key = `${ctx.font}\n${text}`;
9
- const cached = textWidthCache.get(key);
10
- if (cached !== undefined) {
11
- return cached;
12
- }
13
- const width = ctx.measureText(text).width;
14
- if (textWidthCache.size >= TEXT_WIDTH_CACHE_LIMIT) {
15
- textWidthCache.clear();
16
- }
17
- textWidthCache.set(key, width);
18
- return width;
19
- }
20
- /**
21
- * 创建 MACD 图例渲染器插件
22
- * 从 StateStore 读取 MACD 状态,不再在 draw 时计算
23
- */
24
- export function createMACDLegendRendererPlugin(options = {}) {
25
- const yPaddingPx = options.yPaddingPx ?? 0;
26
- const paneId = options.paneId ?? 'sub';
27
- let fastPeriod = 12;
28
- let slowPeriod = 26;
29
- let signalPeriod = 9;
30
- let pluginHost = null;
31
- const stateKey = createMACDStateKey(paneId);
32
- return {
33
- name: `macdLegend_${paneId}`,
34
- version: '2.1.0',
35
- description: 'MACD 图例渲染器(StateStore 版)',
36
- debugName: 'MACD 图例',
37
- paneId,
38
- priority: RENDERER_PRIORITY.FOREGROUND,
39
- onInstall(host) {
40
- pluginHost = host;
41
- },
42
- draw(context) {
43
- const { ctx, range } = context;
44
- const colors = resolveThemeColors(context.theme, context.isAsiaMarket, context.colorPresetSettings);
45
- // 从 StateStore 读取 MACD 状态
46
- const state = pluginHost?.getSharedState(stateKey);
47
- if (!state || state.visibleMin > state.visibleMax)
48
- return;
49
- // 获取最新值
50
- let macdValue = null;
51
- // 优先使用 latestValues(scheduler 已计算好)
52
- if (state.latestValues) {
53
- macdValue = state.latestValues;
54
- }
55
- else {
56
- // 回退:从 series 读取最后一个可见点
57
- const lastIndex = Math.min(range.end - 1, state.series.length - 1);
58
- const point = state.series[lastIndex];
59
- if (point) {
60
- macdValue = {
61
- dif: point.dif,
62
- dea: point.dea,
63
- macd: point.macd,
64
- };
65
- }
66
- }
67
- if (!macdValue)
68
- return;
69
- // 从 state.params 读取当前参数
70
- const params = state.params;
71
- fastPeriod = params.fastPeriod;
72
- slowPeriod = params.slowPeriod;
73
- signalPeriod = params.signalPeriod;
74
- const fontSize = 11;
75
- const gap = 12;
76
- let x = 12;
77
- const y = yPaddingPx + fontSize;
78
- ctx.save();
79
- setCanvasFont(ctx, getFont(fontSize));
80
- ctx.textAlign = 'left';
81
- ctx.textBaseline = 'top';
82
- const paramText = `MACD(${fastPeriod},${slowPeriod},${signalPeriod})`;
83
- ctx.fillStyle = colors.text.tertiary;
84
- ctx.fillText(paramText, x, y);
85
- x += measureTextWidth(ctx, paramText) + gap;
86
- const difText = `DIF:${macdValue.dif.toFixed(2)}`;
87
- ctx.fillStyle = colors.macd.dif;
88
- ctx.fillText(difText, x, y);
89
- x += measureTextWidth(ctx, difText) + gap;
90
- const deaText = `DEA:${macdValue.dea.toFixed(2)}`;
91
- ctx.fillStyle = colors.macd.dea;
92
- ctx.fillText(deaText, x, y);
93
- x += measureTextWidth(ctx, deaText) + gap;
94
- const macdText = `MACD:${macdValue.macd.toFixed(2)}`;
95
- ctx.fillStyle = macdValue.macd >= 0 ? colors.macd.barUp : colors.macd.barDown;
96
- ctx.fillText(macdText, x, y);
97
- ctx.restore();
98
- },
99
- getConfig() {
100
- return {
101
- fastPeriod,
102
- slowPeriod,
103
- signalPeriod,
104
- };
105
- },
106
- setConfig(newConfig) {
107
- if (typeof newConfig.fastPeriod === 'number')
108
- fastPeriod = newConfig.fastPeriod;
109
- if (typeof newConfig.slowPeriod === 'number')
110
- slowPeriod = newConfig.slowPeriod;
111
- if (typeof newConfig.signalPeriod === 'number')
112
- signalPeriod = newConfig.signalPeriod;
113
- },
114
- };
115
- }
116
- //# sourceMappingURL=macdLegend.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"macdLegend.js","sourceRoot":"","sources":["../../../../src/engine/renderers/Indicator/macdLegend.ts"],"names":[],"mappings":"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"}