@quantlife/qlchart 0.0.1

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 (321) hide show
  1. package/.idea/QLChart.iml +12 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/README.md +75 -0
  5. package/demo/App.css +213 -0
  6. package/demo/App.tsx +46 -0
  7. package/demo/components/ControlPanel.tsx +13 -0
  8. package/demo/components/DemoNav.tsx +27 -0
  9. package/demo/index.html +16 -0
  10. package/demo/main.tsx +10 -0
  11. package/demo/pages/BasicChartDemo.tsx +61 -0
  12. package/demo/pages/DrawingDemo.tsx +22 -0
  13. package/demo/pages/IndicatorDemo.tsx +22 -0
  14. package/demo/pages/LayoutDemo.tsx +35 -0
  15. package/demo/pages/MultiPeriodDemo.tsx +31 -0
  16. package/demo/pages/ReplayDemo.tsx +195 -0
  17. package/demo/pages/SaveDemo.tsx +27 -0
  18. package/demo/pages/ThemeDemo.tsx +29 -0
  19. package/demo/standalone-demo.html +597 -0
  20. package/demo/vite.config.demo.ts +17 -0
  21. package/dist/index.d.ts +1973 -0
  22. package/dist/qlchart.js +23169 -0
  23. package/dist/style.css +1 -0
  24. package/doc/api/indicator-data-processor.md +35 -0
  25. package/doc/api-reference/.nojekyll +1 -0
  26. package/doc/api-reference/assets/hierarchy.js +1 -0
  27. package/doc/api-reference/assets/highlight.css +43 -0
  28. package/doc/api-reference/assets/icons.js +18 -0
  29. package/doc/api-reference/assets/icons.svg +1 -0
  30. package/doc/api-reference/assets/main.js +60 -0
  31. package/doc/api-reference/assets/navigation.js +1 -0
  32. package/doc/api-reference/assets/search.js +1 -0
  33. package/doc/api-reference/assets/style.css +1611 -0
  34. package/doc/api-reference/classes/ChartManager.html +16 -0
  35. package/doc/api-reference/classes/DataManager.html +13 -0
  36. package/doc/api-reference/classes/DrawingAdapter.html +64 -0
  37. package/doc/api-reference/classes/DrawingPersistence.html +21 -0
  38. package/doc/api-reference/classes/EventManager.html +12 -0
  39. package/doc/api-reference/classes/HollowCandlestickSeries.html +22 -0
  40. package/doc/api-reference/classes/IndicatorRenderer.html +20 -0
  41. package/doc/api-reference/classes/KlineReplay.html +31 -0
  42. package/doc/api-reference/classes/MockDataService.html +13 -0
  43. package/doc/api-reference/classes/MockIndicatorService.html +4 -0
  44. package/doc/api-reference/classes/OverlayIndicator.html +11 -0
  45. package/doc/api-reference/classes/PaneIndicator.html +16 -0
  46. package/doc/api-reference/classes/PaneManager.html +24 -0
  47. package/doc/api-reference/classes/RealtimeDataFeed.html +22 -0
  48. package/doc/api-reference/classes/RenkoSeries.html +22 -0
  49. package/doc/api-reference/classes/ScreenshotUtil.html +10 -0
  50. package/doc/api-reference/classes/SeriesManager.html +30 -0
  51. package/doc/api-reference/classes/ThemeManager.html +18 -0
  52. package/doc/api-reference/enums/ChartEvent.html +12 -0
  53. package/doc/api-reference/enums/IndicatorType.html +4 -0
  54. package/doc/api-reference/enums/SeriesType.html +13 -0
  55. package/doc/api-reference/functions/ChartFunctionMenu.html +1 -0
  56. package/doc/api-reference/functions/DrawingModule.html +8 -0
  57. package/doc/api-reference/functions/IndicatorPanel.html +2 -0
  58. package/doc/api-reference/functions/IndicatorTag.html +2 -0
  59. package/doc/api-reference/functions/KlineTypeSelector.html +1 -0
  60. package/doc/api-reference/functions/LayoutSwitcher.html +1 -0
  61. package/doc/api-reference/functions/PeriodSelector.html +1 -0
  62. package/doc/api-reference/functions/QLChartLayout.html +1 -0
  63. package/doc/api-reference/functions/QLChartPanel.html +10 -0
  64. package/doc/api-reference/functions/QLChartProvider.html +2 -0
  65. package/doc/api-reference/functions/QLToolbar.html +1 -0
  66. package/doc/api-reference/functions/ReplayController.html +1 -0
  67. package/doc/api-reference/functions/TimeBarModule.html +4 -0
  68. package/doc/api-reference/functions/TimeRangeSelector.html +1 -0
  69. package/doc/api-reference/functions/createIndicatorConfig.html +2 -0
  70. package/doc/api-reference/functions/getToolConfig.html +2 -0
  71. package/doc/api-reference/functions/getToolsByCategory.html +2 -0
  72. package/doc/api-reference/functions/getToolsByPriority.html +2 -0
  73. package/doc/api-reference/functions/mapLibTypeToOurs.html +2 -0
  74. package/doc/api-reference/functions/mapToolTypeToLib.html +3 -0
  75. package/doc/api-reference/functions/transformCandlestickData.html +3 -0
  76. package/doc/api-reference/functions/transformIndicatorData.html +2 -0
  77. package/doc/api-reference/functions/transformVolumeData.html +3 -0
  78. package/doc/api-reference/functions/useChart.html +4 -0
  79. package/doc/api-reference/functions/useChartStore.html +8 -0
  80. package/doc/api-reference/functions/useCrosshairSync.html +8 -0
  81. package/doc/api-reference/functions/useDrawingModule.html +1 -0
  82. package/doc/api-reference/functions/useDrawingStore.html +8 -0
  83. package/doc/api-reference/functions/useIndicatorStore.html +8 -0
  84. package/doc/api-reference/functions/useQLChartConfig.html +2 -0
  85. package/doc/api-reference/functions/useReplayStore.html +8 -0
  86. package/doc/api-reference/functions/useTheme.html +2 -0
  87. package/doc/api-reference/functions/useTimeBarStore.html +8 -0
  88. package/doc/api-reference/index.html +1 -0
  89. package/doc/api-reference/interfaces/CandlestickData.html +7 -0
  90. package/doc/api-reference/interfaces/CandlestickRawData.html +8 -0
  91. package/doc/api-reference/interfaces/ChartFunctionMenuProps.html +2 -0
  92. package/doc/api-reference/interfaces/ChartManagerCreateOptions.html +4 -0
  93. package/doc/api-reference/interfaces/ChartOptions.html +8 -0
  94. package/doc/api-reference/interfaces/ChartRequestParams.html +8 -0
  95. package/doc/api-reference/interfaces/ChartResponse.html +5 -0
  96. package/doc/api-reference/interfaces/ChartState.html +24 -0
  97. package/doc/api-reference/interfaces/ChartThemeOptions.html +5 -0
  98. package/doc/api-reference/interfaces/CrosshairData.html +5 -0
  99. package/doc/api-reference/interfaces/DrawingModuleProps.html +5 -0
  100. package/doc/api-reference/interfaces/DrawingState.html +48 -0
  101. package/doc/api-reference/interfaces/HistogramData.html +5 -0
  102. package/doc/api-reference/interfaces/HollowCandlestickData.html +14 -0
  103. package/doc/api-reference/interfaces/IndicatorConfig.html +11 -0
  104. package/doc/api-reference/interfaces/IndicatorDataPoint.html +4 -0
  105. package/doc/api-reference/interfaces/IndicatorDataResponse.html +5 -0
  106. package/doc/api-reference/interfaces/IndicatorDefinition.html +9 -0
  107. package/doc/api-reference/interfaces/IndicatorPanelProps.html +3 -0
  108. package/doc/api-reference/interfaces/IndicatorParamDef.html +8 -0
  109. package/doc/api-reference/interfaces/IndicatorRawData.html +4 -0
  110. package/doc/api-reference/interfaces/IndicatorState.html +19 -0
  111. package/doc/api-reference/interfaces/IndicatorTagProps.html +2 -0
  112. package/doc/api-reference/interfaces/KlineReplayOptions.html +4 -0
  113. package/doc/api-reference/interfaces/LayoutSwitcherProps.html +3 -0
  114. package/doc/api-reference/interfaces/LineData.html +4 -0
  115. package/doc/api-reference/interfaces/MockDataConfig.html +8 -0
  116. package/doc/api-reference/interfaces/MockIndicatorConfig.html +5 -0
  117. package/doc/api-reference/interfaces/PairInfo.html +6 -0
  118. package/doc/api-reference/interfaces/PaneInfo.html +6 -0
  119. package/doc/api-reference/interfaces/PanelConfig.html +9 -0
  120. package/doc/api-reference/interfaces/PersistenceConfig.html +12 -0
  121. package/doc/api-reference/interfaces/QLChartConfig.html +18 -0
  122. package/doc/api-reference/interfaces/QLChartLayoutProps.html +9 -0
  123. package/doc/api-reference/interfaces/QLChartPanelProps.html +13 -0
  124. package/doc/api-reference/interfaces/QLChartPanelRef.html +14 -0
  125. package/doc/api-reference/interfaces/QLToolbarProps.html +7 -0
  126. package/doc/api-reference/interfaces/RealtimeCandle.html +9 -0
  127. package/doc/api-reference/interfaces/RealtimeSubscribeFn.html +2 -0
  128. package/doc/api-reference/interfaces/RenkoData.html +16 -0
  129. package/doc/api-reference/interfaces/ReplayControllerProps.html +2 -0
  130. package/doc/api-reference/interfaces/ReplayState.html +19 -0
  131. package/doc/api-reference/interfaces/ThemeConfig.html +14 -0
  132. package/doc/api-reference/interfaces/TimeBarModuleProps.html +5 -0
  133. package/doc/api-reference/interfaces/TimeBarState.html +13 -0
  134. package/doc/api-reference/interfaces/UseChartReturn.html +4 -0
  135. package/doc/api-reference/interfaces/UseDrawingModuleOptions.html +11 -0
  136. package/doc/api-reference/interfaces/UseDrawingModuleReturn.html +6 -0
  137. package/doc/api-reference/interfaces/UseThemeReturn.html +5 -0
  138. package/doc/api-reference/types/EventHandler.html +2 -0
  139. package/doc/api-reference/types/FetchFn.html +2 -0
  140. package/doc/api-reference/types/LayoutMode.html +2 -0
  141. package/doc/api-reference/types/MarketTrend.html +2 -0
  142. package/doc/api-reference/types/ThemePreset.html +2 -0
  143. package/doc/api-reference/variables/BUILTIN_INDICATORS.html +2 -0
  144. package/doc/api-reference/variables/CATEGORY_LABELS.html +2 -0
  145. package/doc/api-reference/variables/DRAWING_TOOLS.html +3 -0
  146. package/doc/api-reference/variables/MARKET_PRESETS.html +1 -0
  147. package/doc/api-reference/variables/PAIR_PRESETS.html +1 -0
  148. package/doc/api-reference/variables/darkPreset.html +1 -0
  149. package/doc/api-reference/variables/lightPreset.html +1 -0
  150. package/doc/components/drawing-module.md +24 -0
  151. package/doc/components/indicator-list-panel.md +24 -0
  152. package/doc/components/indicator-panel.md +17 -0
  153. package/doc/components/pane-divider.md +25 -0
  154. package/doc/components/qlchart-layout.md +30 -0
  155. package/doc/components/qlchart-panel.md +93 -0
  156. package/doc/components/qlchart-provider.md +73 -0
  157. package/doc/components/qltoolbar.md +17 -0
  158. package/doc/components/replay-controller.md +23 -0
  159. package/doc/components/timebar-module.md +13 -0
  160. package/doc/core/chart-manager.md +14 -0
  161. package/doc/core/data-manager.md +33 -0
  162. package/doc/core/event-manager.md +26 -0
  163. package/doc/core/pane-manager.md +13 -0
  164. package/doc/core/series-manager.md +19 -0
  165. package/doc/core/theme-manager.md +21 -0
  166. package/doc/examples/basic-chart.md +24 -0
  167. package/doc/examples/data-format-guide.md +119 -0
  168. package/doc/examples/drawing-tools.md +30 -0
  169. package/doc/examples/indicator-properties.md +34 -0
  170. package/doc/examples/multi-pane.md +24 -0
  171. package/doc/examples/multi-panel.md +23 -0
  172. package/doc/examples/realtime-data.md +147 -0
  173. package/doc/examples/standalone-js.md +333 -0
  174. package/doc/guide/architecture.md +87 -0
  175. package/doc/guide/data-flow.md +310 -0
  176. package/doc/guide/deployment.md +59 -0
  177. package/doc/guide/drawing-properties.md +40 -0
  178. package/doc/guide/getting-started.md +94 -0
  179. package/doc/guide/pane-system.md +47 -0
  180. package/doc/guide/theme-switching.md +58 -0
  181. package/doc/hooks/use-chart.md +20 -0
  182. package/doc/hooks/use-crosshair-sync.md +14 -0
  183. package/doc/hooks/use-drawing-module.md +43 -0
  184. package/doc/hooks/use-theme.md +15 -0
  185. package/doc/index.md +33 -0
  186. package/doc/plugins/drawing/overview.md +36 -0
  187. package/doc/plugins/drawing/persistence.md +42 -0
  188. package/doc/plugins/drawing/tool-registry.md +29 -0
  189. package/doc/plugins/hollow-candlestick.md +18 -0
  190. package/doc/plugins/indicators.md +28 -0
  191. package/doc/plugins/renko.md +17 -0
  192. package/doc/plugins/replay.md +21 -0
  193. package/doc/plugins/screenshot.md +20 -0
  194. package/docs/api.md +94 -0
  195. package/package.json +54 -0
  196. package/python/qlchart/__init__.py +9 -0
  197. package/python/qlchart/__pycache__/__init__.cpython-311.pyc +0 -0
  198. package/python/qlchart/__pycache__/chart.cpython-311.pyc +0 -0
  199. package/python/qlchart/chart.py +333 -0
  200. package/python/qlchart/templates/chart_template.html +304 -0
  201. package/python/requirements.txt +1 -0
  202. package/python/setup.py +18 -0
  203. package/python/tests/__init__.py +1 -0
  204. package/python/tests/__pycache__/__init__.cpython-311.pyc +0 -0
  205. package/python/tests/__pycache__/test_chart.cpython-311-pytest-8.3.3.pyc +0 -0
  206. package/python/tests/test_chart.py +114 -0
  207. package/quantlife-qlchart-0.0.1.tgz +0 -0
  208. package/src/api/chartApi.ts +30 -0
  209. package/src/api/indicatorApi.ts +27 -0
  210. package/src/components/ChartFunctionMenu.tsx +64 -0
  211. package/src/components/PaneChartPanel.tsx +116 -0
  212. package/src/components/PaneDivider.tsx +66 -0
  213. package/src/components/QLChartLayout.tsx +151 -0
  214. package/src/components/QLChartPanel.tsx +560 -0
  215. package/src/components/QLChartProvider.tsx +90 -0
  216. package/src/components/context-menu/ChartContextMenu.tsx +139 -0
  217. package/src/components/context-menu/index.ts +2 -0
  218. package/src/components/drawing/DrawingModule.tsx +36 -0
  219. package/src/components/drawing/DrawingPropertyPanel.tsx +347 -0
  220. package/src/components/drawing/DrawingToolbar.tsx +305 -0
  221. package/src/components/drawing/index.ts +5 -0
  222. package/src/components/index.ts +43 -0
  223. package/src/components/indicator/IndicatorListPanel.tsx +94 -0
  224. package/src/components/indicator/IndicatorModal.tsx +171 -0
  225. package/src/components/indicator/IndicatorPanel.tsx +9 -0
  226. package/src/components/indicator/IndicatorPropertyPanel.tsx +130 -0
  227. package/src/components/indicator/IndicatorTag.tsx +173 -0
  228. package/src/components/indicator/index.ts +4 -0
  229. package/src/components/replay/ReplayController.css +97 -0
  230. package/src/components/replay/ReplayController.tsx +138 -0
  231. package/src/components/timebar/TimeBarModule.tsx +30 -0
  232. package/src/components/timebar/TimeRangeSelector.tsx +96 -0
  233. package/src/components/timebar/index.ts +3 -0
  234. package/src/components/toolbar/GlobalToolbar.tsx +58 -0
  235. package/src/components/toolbar/KlineTypeSelector.tsx +123 -0
  236. package/src/components/toolbar/LayoutSwitcher.tsx +45 -0
  237. package/src/components/toolbar/PeriodSelector.tsx +35 -0
  238. package/src/components/toolbar/QLToolbar.tsx +71 -0
  239. package/src/components/toolbar/TimeRangeSelector.tsx +89 -0
  240. package/src/components/ui/Modal.tsx +67 -0
  241. package/src/core/ChartManager.ts +95 -0
  242. package/src/core/DataManager.ts +427 -0
  243. package/src/core/EventManager.ts +63 -0
  244. package/src/core/IndicatorDataProcessor.ts +104 -0
  245. package/src/core/PaneManager.ts +121 -0
  246. package/src/core/RealtimeDataFeed.ts +110 -0
  247. package/src/core/SeriesManager.ts +210 -0
  248. package/src/core/ThemeManager.ts +59 -0
  249. package/src/core/index.ts +10 -0
  250. package/src/css.d.ts +4 -0
  251. package/src/hooks/useChart.ts +62 -0
  252. package/src/hooks/useCrosshairSync.ts +109 -0
  253. package/src/hooks/useDrawingModule.ts +475 -0
  254. package/src/hooks/useTheme.ts +31 -0
  255. package/src/index.ts +170 -0
  256. package/src/mock/MockDataService.ts +102 -0
  257. package/src/mock/MockIndicatorService.ts +40 -0
  258. package/src/mock/index.ts +5 -0
  259. package/src/mock/presets.ts +16 -0
  260. package/src/plugins/drawing/DrawingAdapter.ts +1762 -0
  261. package/src/plugins/drawing/DrawingPersistence.ts +273 -0
  262. package/src/plugins/drawing/DrawingPropertyTemplates.ts +327 -0
  263. package/src/plugins/drawing/DrawingSharedService.ts +125 -0
  264. package/src/plugins/drawing/DrawingToolRegistry.ts +684 -0
  265. package/src/plugins/drawing/TextLabelOverlay.ts +101 -0
  266. package/src/plugins/drawing/colorUtils.ts +53 -0
  267. package/src/plugins/drawing/index.ts +10 -0
  268. package/src/plugins/drawing/lineStyleMap.ts +46 -0
  269. package/src/plugins/drawing/migration.ts +105 -0
  270. package/src/plugins/drawing/patterns/PatternDefinitions.ts +57 -0
  271. package/src/plugins/drawing/patterns/index.ts +2 -0
  272. package/src/plugins/drawing/periodUtils.ts +51 -0
  273. package/src/plugins/indicators/AutoIndicatorRenderer.ts +204 -0
  274. package/src/plugins/indicators/IndicatorRenderer.ts +350 -0
  275. package/src/plugins/indicators/OverlayIndicator.ts +114 -0
  276. package/src/plugins/indicators/PaneIndicator.ts +137 -0
  277. package/src/plugins/indicators/index.ts +4 -0
  278. package/src/plugins/replay/KlineReplay.ts +163 -0
  279. package/src/plugins/replay/index.ts +2 -0
  280. package/src/plugins/screenshot/ScreenshotUtil.ts +123 -0
  281. package/src/plugins/screenshot/index.ts +1 -0
  282. package/src/plugins/series/HollowCandlestickSeries.ts +111 -0
  283. package/src/plugins/series/RenkoSeries.ts +104 -0
  284. package/src/plugins/series/VolumeCandlestickSeries.ts +127 -0
  285. package/src/plugins/series/index.ts +6 -0
  286. package/src/standalone.ts +386 -0
  287. package/src/store/useChartStore.ts +101 -0
  288. package/src/store/useDrawingStore.ts +135 -0
  289. package/src/store/useIndicatorStore.ts +100 -0
  290. package/src/store/usePanelRegistry.ts +50 -0
  291. package/src/store/useReplayStore.ts +42 -0
  292. package/src/store/useTimeBarStore.ts +34 -0
  293. package/src/styles/chart.css +312 -0
  294. package/src/styles/components.css +184 -0
  295. package/src/styles/context-menu.css +60 -0
  296. package/src/styles/drawing.css +524 -0
  297. package/src/styles/indicator-modal.css +216 -0
  298. package/src/styles/indicator-tag.css +210 -0
  299. package/src/styles/pane-chart.css +9 -0
  300. package/src/styles/responsive.css +71 -0
  301. package/src/styles/themes/dark.css +63 -0
  302. package/src/styles/themes/light.css +61 -0
  303. package/src/styles/toolbar.css +129 -0
  304. package/src/types/api.ts +36 -0
  305. package/src/types/chart.ts +44 -0
  306. package/src/types/drawing.ts +265 -0
  307. package/src/types/index.ts +40 -0
  308. package/src/types/indicator.ts +344 -0
  309. package/src/types/series.ts +53 -0
  310. package/src/types/theme.ts +48 -0
  311. package/src/utils/dataTransformer.ts +63 -0
  312. package/src/utils/heikinAshi.ts +41 -0
  313. package/src/utils/index.ts +3 -0
  314. package/src/utils/lineBreak.ts +88 -0
  315. package/src/utils/themePresets.ts +69 -0
  316. package/src/utils/timeFormatter.ts +29 -0
  317. package/src/utils/timeScaleUtils.ts +68 -0
  318. package/tsconfig.json +21 -0
  319. package/typedoc.json +10 -0
  320. package/vite.config.standalone.ts +31 -0
  321. package/vite.config.ts +24 -0
@@ -0,0 +1,101 @@
1
+ /**
2
+ * 绘图文字标签覆盖层(Bug4修复)
3
+ * 在图表 Canvas 上方叠加 DOM 层,显示用户自定义文字标注
4
+ */
5
+ export class TextLabelOverlay {
6
+ private container: HTMLElement | null = null;
7
+ private layer: HTMLDivElement | null = null;
8
+ private labels: Map<string, { text: string; x: number; y: number }> = new Map();
9
+
10
+ attach(container: HTMLElement): void {
11
+ this.container = container;
12
+
13
+ // 创建 overlay 层
14
+ const layer = document.createElement('div');
15
+ layer.className = 'qlchart-text-label-overlay';
16
+ layer.style.cssText = `
17
+ position: absolute;
18
+ top: 0; left: 0;
19
+ width: 100%; height: 100%;
20
+ pointer-events: none;
21
+ z-index: 10;
22
+ `;
23
+ container.appendChild(layer);
24
+ this.layer = layer;
25
+ }
26
+
27
+ detach(): void {
28
+ this.layer?.remove();
29
+ this.layer = null;
30
+ this.container = null;
31
+ this.labels.clear();
32
+ }
33
+
34
+ /**
35
+ * 设置某个绘图的文字标签
36
+ */
37
+ setLabel(drawingId: string, text: string, x: number, y: number): void {
38
+ if (!this.layer) return;
39
+
40
+ if (!text) {
41
+ this.removeLabel(drawingId);
42
+ return;
43
+ }
44
+
45
+ let el = this.layer.querySelector<HTMLDivElement>(
46
+ `[data-drawing-id="${drawingId}"]`,
47
+ );
48
+
49
+ if (!el) {
50
+ el = document.createElement('div');
51
+ el.className = 'qlchart-text-label';
52
+ el.dataset.drawingId = drawingId;
53
+ el.style.cssText = `
54
+ position: absolute;
55
+ padding: 2px 6px;
56
+ background: rgba(41, 98, 255, 0.9);
57
+ color: #fff;
58
+ font-size: 11px;
59
+ border-radius: 3px;
60
+ pointer-events: auto;
61
+ cursor: pointer;
62
+ white-space: nowrap;
63
+ `;
64
+ this.layer.appendChild(el);
65
+ }
66
+
67
+ el.textContent = text;
68
+ el.style.left = `${x + 8}px`;
69
+ el.style.top = `${y - 10}px`;
70
+ this.labels.set(drawingId, { text, x, y });
71
+ }
72
+
73
+ removeLabel(drawingId: string): void {
74
+ this.layer?.querySelector(`[data-drawing-id="${drawingId}"]`)?.remove();
75
+ this.labels.delete(drawingId);
76
+ }
77
+
78
+ getLabel(drawingId: string): string | undefined {
79
+ return this.labels.get(drawingId)?.text;
80
+ }
81
+
82
+ /** 更新所有标签位置(图表滚动/缩放后调用) */
83
+ updatePositions(
84
+ getDrawingPixel: (id: string) => { x: number; y: number } | null,
85
+ ): void {
86
+ for (const [id, label] of this.labels) {
87
+ const pixel = getDrawingPixel(id);
88
+ if (pixel) {
89
+ const el = this.layer?.querySelector<HTMLDivElement>(
90
+ `[data-drawing-id="${id}"]`,
91
+ );
92
+ if (el) {
93
+ el.style.left = `${pixel.x + 8}px`;
94
+ el.style.top = `${pixel.y - 10}px`;
95
+ }
96
+ // Update stored position
97
+ this.labels.set(id, { ...label, x: pixel.x, y: pixel.y });
98
+ }
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 颜色工具函数
3
+ * 用于 hex/rgba 之间的转换(Bug5修复)
4
+ */
5
+
6
+ /**
7
+ * 将 hex 颜色和透明度合成为 rgba 字符串
8
+ * 支持: hex (#rrggbb)、rgba (r,g,b,a)、rgb (r,g,b)
9
+ */
10
+ export function composeRgbaColor(
11
+ fillColor: string,
12
+ fillOpacity: number,
13
+ ): string {
14
+ // 已是 rgba/rgb 格式 → 替换 alpha
15
+ const rgbaMatch = fillColor.match(
16
+ /rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)/,
17
+ );
18
+ if (rgbaMatch) {
19
+ const [, r, g, b] = rgbaMatch;
20
+ return `rgba(${r}, ${g}, ${b}, ${fillOpacity})`;
21
+ }
22
+
23
+ // hex 格式 → 转 rgba
24
+ const hexMatch = fillColor.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);
25
+ if (hexMatch) {
26
+ const r = parseInt(hexMatch[1], 16);
27
+ const g = parseInt(hexMatch[2], 16);
28
+ const b = parseInt(hexMatch[3], 16);
29
+ return `rgba(${r}, ${g}, ${b}, ${fillOpacity})`;
30
+ }
31
+
32
+ // 无法解析 → 原样返回
33
+ return fillColor;
34
+ }
35
+
36
+ /**
37
+ * 从 rgba 字符串中提取 alpha 值
38
+ */
39
+ export function extractAlpha(color: string): number | null {
40
+ const match = color.match(/rgba?\([^,]+,[^,]+,[^,]+,\s*([\d.]+)\s*\)/);
41
+ return match ? parseFloat(match[1]) : null;
42
+ }
43
+
44
+ /**
45
+ * 从 rgba 字符串中提取 hex 颜色(用于 input[type=color] 回显)
46
+ */
47
+ export function rgbaToHex(color: string): string {
48
+ const match = color.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
49
+ if (!match) return '#2962ff';
50
+ const [, r, g, b] = match;
51
+ const toHex = (n: string) => parseInt(n).toString(16).padStart(2, '0');
52
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
53
+ }
@@ -0,0 +1,10 @@
1
+ export { DrawingAdapter } from './DrawingAdapter.js';
2
+ export {
3
+ DRAWING_TOOLS,
4
+ getToolsByCategory,
5
+ getToolsByPriority,
6
+ getToolConfig,
7
+ CATEGORY_LABELS,
8
+ mapToolTypeToLib,
9
+ mapLibTypeToOurs,
10
+ } from './DrawingToolRegistry.js';
@@ -0,0 +1,46 @@
1
+ /**
2
+ * 线条样式 → dash 数组映射
3
+ * 用于将属性面板的 lineStyle 字符串转换为 LWC 的 lineDash 数组
4
+ */
5
+
6
+ const LINE_STYLE_MAP: Record<string, number[]> = {
7
+ 'solid': [],
8
+ 'dashed': [6, 4],
9
+ 'dotted': [2, 3],
10
+ 'dash-dot': [6, 3, 2, 3],
11
+ 'long-dash': [10, 4],
12
+ };
13
+
14
+ /** 将线条样式名称转换为 dash 数组 */
15
+ export function lineStyleToDash(style: string): number[] {
16
+ return LINE_STYLE_MAP[style] ?? [];
17
+ }
18
+
19
+ /** dash → style 反向映射表 */
20
+ const DASH_TO_STYLE: Array<{ dash: number[]; style: string }> = [
21
+ { dash: [], style: 'solid' },
22
+ { dash: [6, 4], style: 'dashed' },
23
+ { dash: [2, 3], style: 'dotted' },
24
+ { dash: [6, 3, 2, 3], style: 'dash-dot' },
25
+ { dash: [10, 4], style: 'long-dash' },
26
+ ];
27
+
28
+ /** 从 dash 数组反推线条样式名称 */
29
+ export function dashToLineStyle(dash?: number[]): string {
30
+ if (!dash || dash.length === 0) return 'solid';
31
+ const found = DASH_TO_STYLE.find(
32
+ (m) => m.dash.length === dash.length && m.dash.every((n, i) => n === dash[i]),
33
+ );
34
+ return found?.style ?? 'solid';
35
+ }
36
+
37
+ /** 获取所有可选线型 */
38
+ export function getLineStyleOptions() {
39
+ return [
40
+ { label: '实线', value: 'solid' },
41
+ { label: '虚线', value: 'dashed' },
42
+ { label: '点线', value: 'dotted' },
43
+ { label: '点划线', value: 'dash-dot' },
44
+ { label: '长虚线', value: 'long-dash' },
45
+ ];
46
+ }
@@ -0,0 +1,105 @@
1
+ import type { DrawingPersistData } from '../../types/drawing.js';
2
+ import { STORAGE_PREFIX, STORAGE_VERSION } from './DrawingPersistence.js';
3
+ import type { StorageEnvelope } from './DrawingPersistence.js';
4
+
5
+ /** 简单hash函数(与DrawingPersistence中一致) */
6
+ function simpleHash(str: string): string {
7
+ let hash = 0;
8
+ for (let i = 0; i < str.length; i++) {
9
+ const char = str.charCodeAt(i);
10
+ hash = ((hash << 5) - hash) + char;
11
+ hash |= 0;
12
+ }
13
+ return Math.abs(hash).toString(36);
14
+ }
15
+
16
+ /** 从旧数据内容中推断product */
17
+ function inferProductFromDrawings(drawings: DrawingPersistData[]): string | null {
18
+ // 旧版DrawingPersistData没有product字段
19
+ // 但chartId字段在旧版等于 pairId,可以用于分组
20
+ // product信息无法从数据中可靠获取,使用默认值
21
+ // 如果所有绘图的chartId一致,说明是同一交易对的数据
22
+ if (drawings.length === 0) return null;
23
+ // 默认使用 'digital' 作为product(量化系统主要场景)
24
+ // 用户可以后续手动调整
25
+ return 'digital';
26
+ }
27
+
28
+ /**
29
+ * 迁移旧版LocalStorage数据(含chartId+period的key → 新key)
30
+ *
31
+ * 迁移策略:
32
+ * 1. 扫描所有以 STORAGE_PREFIX 开头的 key
33
+ * 2. 从数据内容中提取 pairId 和 product 信息
34
+ * 3. 按 pairId+product 重新聚合,去重
35
+ * 4. 写入新格式 key
36
+ * 5. 删除旧 key
37
+ *
38
+ * 幂等设计:多次调用不会产生数据丢失
39
+ * 失败的旧数据保留不删
40
+ */
41
+ export function migrateLegacyDrawings(): void {
42
+ const legacyKeys: string[] = [];
43
+ for (let i = 0; i < localStorage.length; i++) {
44
+ const key = localStorage.key(i);
45
+ if (key?.startsWith(STORAGE_PREFIX)) {
46
+ legacyKeys.push(key);
47
+ }
48
+ }
49
+
50
+ // 按交易对分组(解析旧数据,按 pairId+product 重新聚合)
51
+ const mergedByPair = new Map<string, DrawingPersistData[]>();
52
+
53
+ for (const oldKey of legacyKeys) {
54
+ const json = localStorage.getItem(oldKey);
55
+ if (!json) continue;
56
+ try {
57
+ const envelope: StorageEnvelope = JSON.parse(json);
58
+ if (!envelope.drawings?.length) continue;
59
+
60
+ // 从绘图数据中提取 pairId(drawing.chartId 在旧版等于 pairId)
61
+ const pairId = envelope.drawings[0]?.chartId;
62
+ if (!pairId) {
63
+ // 无法识别的旧数据,保留不动
64
+ continue;
65
+ }
66
+
67
+ // 尝试从数据内容中推断product
68
+ const product = inferProductFromDrawings(envelope.drawings);
69
+ if (!product) {
70
+ // 无法识别的旧数据,保留不删
71
+ continue;
72
+ }
73
+
74
+ const pairKey = `${pairId}_${product}`;
75
+ const existing = mergedByPair.get(pairKey) ?? [];
76
+ // 合并去重(按id)
77
+ const allIds = new Set(existing.map(d => d.id));
78
+ for (const d of envelope.drawings) {
79
+ if (!allIds.has(d.id)) {
80
+ existing.push(d);
81
+ allIds.add(d.id);
82
+ }
83
+ }
84
+ mergedByPair.set(pairKey, existing);
85
+
86
+ // 删除旧key
87
+ localStorage.removeItem(oldKey);
88
+ } catch {
89
+ // 解析失败,跳过
90
+ }
91
+ }
92
+
93
+ // 写入新格式
94
+ for (const [pairKey, drawings] of mergedByPair) {
95
+ const [pairId, product] = pairKey.split('_');
96
+ const hash = simpleHash(pairKey);
97
+ const newStorageKey = `${STORAGE_PREFIX}${hash}`;
98
+ const envelope: StorageEnvelope = {
99
+ version: STORAGE_VERSION,
100
+ drawings,
101
+ updatedAt: Date.now(),
102
+ };
103
+ localStorage.setItem(newStorageKey, JSON.stringify(envelope));
104
+ }
105
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * 图表形态工具 — 锚点定义与标签生成
3
+ *
4
+ * 形态工具底层使用 path 渲染,但带有预定义的锚点结构:
5
+ * - ABCD: A→B→C→D 四个点
6
+ * - XABCD: X→A→B→C→D 五个点
7
+ * - HeadShoulders: 左肩→头→右肩→颈线左→颈线右
8
+ * - Triangle: 三角形四个顶点
9
+ * - Cypher: X→A→B→C→D 带斐波那契比例
10
+ */
11
+
12
+ export interface PatternDefinition {
13
+ /** 锚点标签 */
14
+ anchorLabels: string[];
15
+ /** 描述 */
16
+ description: string;
17
+ /** 形态类型 */
18
+ patternType: string;
19
+ }
20
+
21
+ export const PATTERN_DEFINITIONS: Record<string, PatternDefinition> = {
22
+ 'pattern-abcd': {
23
+ patternType: 'ABCD',
24
+ anchorLabels: ['A', 'B', 'C', 'D'],
25
+ description: 'ABCD 谐波形态:AB驱动浪,BC回调(0.382-0.886),CD完成浪',
26
+ },
27
+ 'pattern-xabcd': {
28
+ patternType: 'XABCD',
29
+ anchorLabels: ['X', 'A', 'B', 'C', 'D'],
30
+ description: 'XABCD 蝴蝶/蝙蝠/螃蟹形态:5点谐波结构',
31
+ },
32
+ 'pattern-head-shoulders': {
33
+ patternType: 'HeadShoulders',
34
+ anchorLabels: ['左肩', '头', '右肩', '颈线左', '颈线右'],
35
+ description: '头肩顶/底反转形态:左右肩对称,头部为极值',
36
+ },
37
+ 'pattern-triangle': {
38
+ patternType: 'Triangle',
39
+ anchorLabels: ['顶点1', '顶点2', '顶点3', '顶点4'],
40
+ description: '三角形整理形态:对称/上升/下降三角形',
41
+ },
42
+ 'pattern-cypher': {
43
+ patternType: 'Cypher',
44
+ anchorLabels: ['X', 'A', 'B', 'C', 'D'],
45
+ description: '赛福形态:XA=XC,AB回调0.382-0.618,BC投影1.272-1.414',
46
+ },
47
+ };
48
+
49
+ /** 获取形态定义 */
50
+ export function getPatternDefinition(type: string): PatternDefinition | undefined {
51
+ return PATTERN_DEFINITIONS[type];
52
+ }
53
+
54
+ /** 获取所有形态工具类型 */
55
+ export function getAllPatternTypes(): string[] {
56
+ return Object.keys(PATTERN_DEFINITIONS);
57
+ }
@@ -0,0 +1,2 @@
1
+ export { PATTERN_DEFINITIONS, getPatternDefinition, getAllPatternTypes } from './PatternDefinitions.js';
2
+ export type { PatternDefinition } from './PatternDefinitions.js';
@@ -0,0 +1,51 @@
1
+ /**
2
+ * 周期换算工具函数(Bug1修复)
3
+ * 用于测量工具按 K 线周期换算 bars/时间
4
+ */
5
+
6
+ /**
7
+ * 将周期字符串转换为秒数
8
+ * @param period 如 '1m', '5m', '15m', '1h', '4h', '1d', '1w'
9
+ */
10
+ export function periodToSeconds(period: string): number {
11
+ const match = period.match(/^(\d+)([mhdwM])$/);
12
+ if (!match) return 3600; // 默认1小时
13
+ const [, num, unit] = match;
14
+ const n = parseInt(num, 10);
15
+ switch (unit) {
16
+ case 'm': return n * 60;
17
+ case 'h': return n * 3600;
18
+ case 'd': return n * 86400;
19
+ case 'w': return n * 604800;
20
+ case 'M': return n * 2592000; // 近似月
21
+ default: return 3600;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * 根据周期和总秒数,计算 K 线根数和显示标签
27
+ */
28
+ export function formatPeriodBars(
29
+ totalSeconds: number,
30
+ period: string,
31
+ ): { bars: number; label: string } {
32
+ const periodSec = periodToSeconds(period);
33
+ if (periodSec <= 0) return { bars: 0, label: '0' };
34
+
35
+ const bars = Math.round(totalSeconds / periodSec);
36
+ const periodNum = parseInt(period.match(/^(\d+)/)?.[1] ?? '1', 10);
37
+ const unit = period.match(/([mhdwM])$/)?.[1];
38
+
39
+ switch (unit) {
40
+ case 'm':
41
+ return { bars, label: `${bars * periodNum}分钟 (${bars}根)` };
42
+ case 'h':
43
+ return { bars, label: `${bars * periodNum}小时 (${bars}根)` };
44
+ case 'd':
45
+ return { bars, label: `${bars * periodNum}天 (${bars}根)` };
46
+ case 'w':
47
+ return { bars, label: `${bars * periodNum}周 (${bars}根)` };
48
+ default:
49
+ return { bars, label: `${bars}根K线` };
50
+ }
51
+ }
@@ -0,0 +1,204 @@
1
+ import type { IChartApi, ISeriesApi, UTCTimestamp } from 'lightweight-charts';
2
+ import { LineSeries, HistogramSeries, AreaSeries } from 'lightweight-charts';
3
+ import type { IndicatorRenderSpec } from '../../types/index.js';
4
+
5
+ /**
6
+ * AutoIndicatorRenderer — 数据驱动的指标自动渲染引擎
7
+ *
8
+ * 根据 IndicatorRenderSpec 自动创建 / 更新 / 移除 series,
9
+ * 替代 IndicatorRenderer 中的 switch-case 硬编码。
10
+ *
11
+ * 支持的渲染类型:line / histogram / area
12
+ * 支持的位置: overlay(主图叠加)/ pane(独立副图)
13
+ *
14
+ * 核心优势:后端新增指标只需在响应中附带 spec + data,
15
+ * 前端零改动即可自动渲染。
16
+ */
17
+ export class AutoIndicatorRenderer {
18
+ private chart: IChartApi;
19
+ /** spec.key → 创建的 series 列表 */
20
+ private renderedSpecs = new Map<string, ISeriesApi<any>[]>();
21
+ /** spec.key → pane index(仅 placement='pane' 的指标有值) */
22
+ private paneIndices = new Map<string, number>();
23
+
24
+ constructor(chart: IChartApi) {
25
+ this.chart = chart;
26
+ }
27
+
28
+ /**
29
+ * 渲染一个指标
30
+ *
31
+ * @param spec — 指标渲染描述(线条定义、位置等)
32
+ * @param data — 指标数据数组
33
+ */
34
+ render(
35
+ spec: IndicatorRenderSpec,
36
+ data: Array<{ time: number; values: Record<string, number> }>,
37
+ ): void {
38
+ // 先移除旧的渲染(如果存在)
39
+ this.remove(spec.key);
40
+
41
+ const seriesList: ISeriesApi<any>[] = [];
42
+
43
+ // 分配 pane index:overlay 用主图(0),pane 用递增 index
44
+ const paneIndex = spec.placement === 'pane' ? this.allocPane(spec.key) : 0;
45
+
46
+ for (const line of spec.lines) {
47
+ // 过滤出有对应字段的数据点
48
+ const lineData = data
49
+ .filter((d) => d.values[line.field] !== undefined)
50
+ .map((d) => ({
51
+ time: (d.time / 1000) as UTCTimestamp,
52
+ value: d.values[line.field],
53
+ }));
54
+
55
+ let series: ISeriesApi<any>;
56
+
57
+ switch (line.type) {
58
+ case 'line':
59
+ series = this.chart.addSeries(
60
+ LineSeries,
61
+ {
62
+ color: line.color,
63
+ lineWidth: (line.lineWidth ?? 1) as any,
64
+ priceLineVisible: false,
65
+ lastValueVisible: false,
66
+ },
67
+ paneIndex,
68
+ );
69
+ series.setData(lineData);
70
+ break;
71
+
72
+ case 'histogram': {
73
+ const positiveColor = line.positiveColor ?? line.color;
74
+ const negativeColor = line.negativeColor ?? '#ef5350';
75
+ series = this.chart.addSeries(
76
+ HistogramSeries,
77
+ {
78
+ color: line.color,
79
+ priceLineVisible: false,
80
+ lastValueVisible: false,
81
+ },
82
+ paneIndex,
83
+ );
84
+ // 柱状图:按正负值着色
85
+ const histData = lineData.map((d) => ({
86
+ ...d,
87
+ color: d.value >= 0 ? positiveColor : negativeColor,
88
+ }));
89
+ series.setData(histData);
90
+ break;
91
+ }
92
+
93
+ case 'area':
94
+ series = this.chart.addSeries(
95
+ AreaSeries,
96
+ {
97
+ lineColor: line.color,
98
+ topColor: line.color + '40',
99
+ bottomColor: line.color + '00',
100
+ lineWidth: (line.lineWidth ?? 1) as any,
101
+ priceLineVisible: false,
102
+ lastValueVisible: false,
103
+ },
104
+ paneIndex,
105
+ );
106
+ series.setData(lineData);
107
+ break;
108
+
109
+ default:
110
+ console.warn(`[AutoIndicatorRenderer] Unknown line type: ${line.type}, skipping`);
111
+ continue;
112
+ }
113
+
114
+ seriesList.push(series);
115
+ }
116
+
117
+ // 设置副图高度比例
118
+ if (spec.placement === 'pane' && spec.paneHeightRatio && paneIndex > 0) {
119
+ this.setPaneHeight(paneIndex, spec.paneHeightRatio);
120
+ }
121
+
122
+ this.renderedSpecs.set(spec.key, seriesList);
123
+ }
124
+
125
+ /**
126
+ * 移除一个指标的渲染
127
+ */
128
+ remove(key: string): void {
129
+ const seriesList = this.renderedSpecs.get(key);
130
+ if (!seriesList) return;
131
+
132
+ for (const series of seriesList) {
133
+ try {
134
+ this.chart.removeSeries(series);
135
+ } catch {
136
+ // series 可能已被 chart 销毁
137
+ }
138
+ }
139
+
140
+ this.renderedSpecs.delete(key);
141
+
142
+ // 清理 pane index
143
+ const paneIdx = this.paneIndices.get(key);
144
+ if (paneIdx !== undefined) {
145
+ this.paneIndices.delete(key);
146
+ }
147
+ }
148
+
149
+ /**
150
+ * 移除所有已渲染的指标
151
+ */
152
+ removeAll(): void {
153
+ for (const key of Array.from(this.renderedSpecs.keys())) {
154
+ this.remove(key);
155
+ }
156
+ }
157
+
158
+ /**
159
+ * 获取已渲染的指标 key 列表
160
+ */
161
+ getRenderedKeys(): string[] {
162
+ return Array.from(this.renderedSpecs.keys());
163
+ }
164
+
165
+ /**
166
+ * 检查指定 key 的指标是否已渲染
167
+ */
168
+ isRendered(key: string): boolean {
169
+ return this.renderedSpecs.has(key);
170
+ }
171
+
172
+ // ════════════════════════════════════════
173
+ // 内部方法
174
+ // ════════════════════════════════════════
175
+
176
+ /**
177
+ * 分配副图 pane index(递增策略)
178
+ */
179
+ private allocPane(key: string): number {
180
+ let maxPane = 0;
181
+ for (const idx of this.paneIndices.values()) {
182
+ if (idx > maxPane) maxPane = idx;
183
+ }
184
+ const newIdx = maxPane + 1;
185
+ this.paneIndices.set(key, newIdx);
186
+ return newIdx;
187
+ }
188
+
189
+ /**
190
+ * 设置 pane 高度比例
191
+ */
192
+ private setPaneHeight(paneIndex: number, ratio: number): void {
193
+ try {
194
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
195
+ const panes = (this.chart as any).panes?.();
196
+ if (panes && panes[paneIndex]) {
197
+ const mainHeight = panes[0]?.getHeight?.() ?? 300;
198
+ panes[paneIndex].setHeight?.(mainHeight * ratio);
199
+ }
200
+ } catch {
201
+ // LWC v5 pane API 可能不存在,静默失败
202
+ }
203
+ }
204
+ }