@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,121 @@
1
+ import type { IChartApi, ISeriesApi, SeriesType as LWCSeriesType } from 'lightweight-charts';
2
+ import { HistogramSeries } from 'lightweight-charts';
3
+ import { EventManager, ChartEvent } from './EventManager.js';
4
+
5
+ /** 面板信息 */
6
+ export interface PaneInfo {
7
+ index: number;
8
+ heightPercent: number;
9
+ seriesIds: string[];
10
+ label: string;
11
+ }
12
+
13
+ /**
14
+ * 多面板管理器
15
+ * 管理 LWC v5 的 pane 系统,用于副图指标
16
+ */
17
+ export class PaneManager {
18
+ private chart: IChartApi | null = null;
19
+ private eventManager: EventManager;
20
+ private panes: Map<number, PaneInfo> = new Map();
21
+ private nextPaneIndex = 1; // 0 是主图
22
+
23
+ constructor(eventManager: EventManager) {
24
+ this.eventManager = eventManager;
25
+ }
26
+
27
+ /** 设置图表实例 */
28
+ setChart(chart: IChartApi): void {
29
+ this.chart = chart;
30
+ this.dispose();
31
+ }
32
+
33
+ /** 获取主图 pane 信息 */
34
+ getMainPane(): PaneInfo {
35
+ return { index: 0, heightPercent: 70, seriesIds: [], label: '主图' };
36
+ }
37
+
38
+ /**
39
+ * 添加成交量叠加层
40
+ * 在主图底部用 HistogramSeries + priceScaleMargins 实现
41
+ */
42
+ addVolumeOverlay(heightPercent = 20): ISeriesApi<LWCSeriesType> | null {
43
+ if (!this.chart) return null;
44
+
45
+ const volumeSeries = this.chart.addSeries(HistogramSeries, {
46
+ priceFormat: { type: 'volume' },
47
+ priceScaleId: 'volume',
48
+ });
49
+
50
+ this.chart.priceScale('volume').applyOptions({
51
+ scaleMargins: { top: 1 - heightPercent / 100, bottom: 0 },
52
+ });
53
+
54
+ return volumeSeries as ISeriesApi<LWCSeriesType>;
55
+ }
56
+
57
+ /**
58
+ * 添加指标面板(副图)
59
+ * 使用独立的 priceScaleId 创建新 pane
60
+ */
61
+ addIndicatorPane(indicatorName: string, heightPercent = 20): string {
62
+ if (!this.chart) throw new Error('Chart not initialized');
63
+
64
+ const priceScaleId = `pane_${this.nextPaneIndex}_${indicatorName}`;
65
+ const paneInfo: PaneInfo = {
66
+ index: this.nextPaneIndex,
67
+ heightPercent,
68
+ seriesIds: [],
69
+ label: indicatorName,
70
+ };
71
+
72
+ this.panes.set(this.nextPaneIndex, paneInfo);
73
+ this.nextPaneIndex++;
74
+
75
+ this.eventManager.emit(ChartEvent.SeriesAdded, {
76
+ id: priceScaleId,
77
+ type: 'pane',
78
+ paneIndex: paneInfo.index,
79
+ });
80
+
81
+ return priceScaleId;
82
+ }
83
+
84
+ /** 移除面板 */
85
+ removePane(paneIndex: number): void {
86
+ const pane = this.panes.get(paneIndex);
87
+ if (!pane) return;
88
+
89
+ // 移除该面板上的所有序列(通过 seriesManager 管理)
90
+ this.eventManager.emit(ChartEvent.SeriesRemoved, {
91
+ id: `pane_${paneIndex}`,
92
+ paneIndex,
93
+ });
94
+
95
+ this.panes.delete(paneIndex);
96
+ }
97
+
98
+ /** 设置面板高度 */
99
+ setPaneHeight(paneIndex: number, heightPercent: number): void {
100
+ const pane = this.panes.get(paneIndex);
101
+ if (pane) {
102
+ pane.heightPercent = heightPercent;
103
+ }
104
+ }
105
+
106
+ /** 获取所有面板 */
107
+ getPanes(): PaneInfo[] {
108
+ return [this.getMainPane(), ...Array.from(this.panes.values())];
109
+ }
110
+
111
+ /** 根据 priceScaleId 获取面板信息 */
112
+ getPaneByIndex(index: number): PaneInfo | undefined {
113
+ return this.panes.get(index);
114
+ }
115
+
116
+ /** 清理所有面板 */
117
+ dispose(): void {
118
+ this.panes.clear();
119
+ this.nextPaneIndex = 1;
120
+ }
121
+ }
@@ -0,0 +1,110 @@
1
+ import type { ISeriesApi, SeriesType as LWCSeriesType, UTCTimestamp } from 'lightweight-charts';
2
+
3
+ /**
4
+ * 实时K线数据格式(秒级时间戳,与LWC对齐)
5
+ */
6
+ export interface RealtimeCandle {
7
+ /** K线时间戳(秒级,与LWC一致) */
8
+ time: number;
9
+ open: number;
10
+ high: number;
11
+ low: number;
12
+ close: number;
13
+ volume?: number;
14
+ }
15
+
16
+ /**
17
+ * 实时数据馈送管理器
18
+ *
19
+ * 接收外部推送的增量K线数据,通过 LWC 的 series.update() 实时更新图表。
20
+ *
21
+ * LWC update() 自动判断:
22
+ * - time === 最新K线time → 更新该K线
23
+ * - time > 最新K线time → 追加新K线
24
+ * - time < 最新K线time → 忽略
25
+ */
26
+ export class RealtimeDataFeed {
27
+ private mainSeries: ISeriesApi<LWCSeriesType> | null = null;
28
+ private volumeSeries: ISeriesApi<LWCSeriesType> | null = null;
29
+ private lastTime: number = 0;
30
+
31
+ /**
32
+ * 绑定到图表序列
33
+ */
34
+ attach(
35
+ mainSeries: ISeriesApi<LWCSeriesType>,
36
+ volumeSeries?: ISeriesApi<LWCSeriesType> | null,
37
+ ): void {
38
+ this.mainSeries = mainSeries;
39
+ this.volumeSeries = volumeSeries ?? null;
40
+ this.lastTime = 0;
41
+ }
42
+
43
+ /**
44
+ * 解绑
45
+ */
46
+ detach(): void {
47
+ this.mainSeries = null;
48
+ this.volumeSeries = null;
49
+ this.lastTime = 0;
50
+ }
51
+
52
+ /**
53
+ * 推送单条增量数据
54
+ */
55
+ push(data: RealtimeCandle): void {
56
+ if (!this.mainSeries) {
57
+ console.warn('[RealtimeDataFeed] Not attached, ignoring push');
58
+ return;
59
+ }
60
+
61
+ const candle = {
62
+ time: data.time as UTCTimestamp,
63
+ open: data.open,
64
+ high: data.high,
65
+ low: data.low,
66
+ close: data.close,
67
+ };
68
+
69
+ this.mainSeries.update(candle);
70
+
71
+ if (this.volumeSeries && data.volume !== undefined) {
72
+ this.volumeSeries.update({
73
+ time: data.time as UTCTimestamp,
74
+ value: data.volume,
75
+ color: data.close >= data.open
76
+ ? 'rgba(38, 166, 154, 0.35)'
77
+ : 'rgba(239, 83, 80, 0.35)',
78
+ });
79
+ }
80
+
81
+ this.lastTime = data.time;
82
+ }
83
+
84
+ /**
85
+ * 批量推送
86
+ */
87
+ pushBatch(datas: RealtimeCandle[]): void {
88
+ const sorted = [...datas].sort((a, b) => a.time - b.time);
89
+ for (const data of sorted) {
90
+ this.push(data);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * 获取最后推送的time
96
+ */
97
+ getLastTime(): number {
98
+ return this.lastTime;
99
+ }
100
+
101
+ /**
102
+ * 更新绑定的series引用(chart重建后调用)
103
+ */
104
+ updateSeries(
105
+ mainSeries: ISeriesApi<LWCSeriesType>,
106
+ volumeSeries?: ISeriesApi<LWCSeriesType> | null,
107
+ ): void {
108
+ this.attach(mainSeries, volumeSeries);
109
+ }
110
+ }
@@ -0,0 +1,210 @@
1
+ import {
2
+ type IChartApi,
3
+ type ISeriesApi,
4
+ type SeriesType as LWCSeriesType,
5
+ CandlestickSeries,
6
+ BarSeries,
7
+ LineSeries,
8
+ AreaSeries,
9
+ BaselineSeries,
10
+ HistogramSeries,
11
+ } from 'lightweight-charts';
12
+ import { SeriesType } from '../types/index.js';
13
+ import { EventManager, ChartEvent } from './EventManager.js';
14
+ import { HollowCandlestickSeries } from '../plugins/series/HollowCandlestickSeries.js';
15
+ import { RenkoSeries } from '../plugins/series/RenkoSeries.js';
16
+ import { VolumeCandlestickSeries } from '../plugins/series/VolumeCandlestickSeries.js';
17
+
18
+ /** LWC series definition map */
19
+ const SERIES_DEFINITIONS: Record<SeriesType, unknown> = {
20
+ [SeriesType.Candlestick]: CandlestickSeries,
21
+ [SeriesType.Bar]: BarSeries,
22
+ [SeriesType.Line]: LineSeries,
23
+ [SeriesType.Area]: AreaSeries,
24
+ [SeriesType.Baseline]: BaselineSeries,
25
+ [SeriesType.Histogram]: HistogramSeries,
26
+ // 自定义Series
27
+ [SeriesType.HollowCandlestick]: HollowCandlestickSeries,
28
+ [SeriesType.Renko]: RenkoSeries,
29
+ [SeriesType.VolumeCandlestick]: VolumeCandlestickSeries,
30
+ // 复用LWC蜡烛图(数据预处理)
31
+ [SeriesType.HeikinAshi]: CandlestickSeries,
32
+ [SeriesType.LineBreak]: CandlestickSeries,
33
+ };
34
+
35
+ /**
36
+ * 序列管理器
37
+ * 管理图表上所有的序列(K线、指标、成交量等)
38
+ */
39
+ export class SeriesManager {
40
+ private seriesMap = new Map<string, ISeriesApi<LWCSeriesType>>();
41
+ private chart: IChartApi | null = null;
42
+ private eventManager: EventManager;
43
+
44
+ constructor(chart: IChartApi, eventManager: EventManager) {
45
+ this.chart = chart;
46
+ this.eventManager = eventManager;
47
+ }
48
+
49
+ /** 更新chart实例(chart重建时调用) */
50
+ setChart(chart: IChartApi): void {
51
+ this.chart = chart;
52
+ this.seriesMap.clear();
53
+ }
54
+
55
+ /** 添加主序列(K线等) */
56
+ addMainSeries(
57
+ type: SeriesType,
58
+ options?: Record<string, unknown>,
59
+ ): ISeriesApi<LWCSeriesType> | null {
60
+ if (!this.chart) return null;
61
+ const definition = SERIES_DEFINITIONS[type];
62
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
63
+ const series = this.chart.addSeries(definition as any, options as any);
64
+ const id = `main_${type}_${Date.now()}`;
65
+ this.seriesMap.set(id, series as ISeriesApi<LWCSeriesType>);
66
+ this.eventManager.emit(ChartEvent.SeriesAdded, { id, type });
67
+ return series as ISeriesApi<LWCSeriesType>;
68
+ }
69
+
70
+ /** 添加叠加序列(指标、成交量等) */
71
+ addOverlaySeries(
72
+ type: SeriesType,
73
+ options?: Record<string, unknown>,
74
+ priceScaleId?: string,
75
+ ): ISeriesApi<LWCSeriesType> | null {
76
+ if (!this.chart) return null;
77
+ const definition = SERIES_DEFINITIONS[type];
78
+ const seriesOpts = priceScaleId
79
+ ? { ...options, priceScaleId }
80
+ : options;
81
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
+ const series = this.chart.addSeries(definition as any, seriesOpts as any);
83
+ const id = `overlay_${type}_${Date.now()}`;
84
+ this.seriesMap.set(id, series as ISeriesApi<LWCSeriesType>);
85
+ this.eventManager.emit(ChartEvent.SeriesAdded, { id, type });
86
+ return series as ISeriesApi<LWCSeriesType>;
87
+ }
88
+
89
+ /** 设置序列数据 */
90
+ setData(seriesId: string, data: unknown[]): void {
91
+ const series = this.seriesMap.get(seriesId);
92
+ if (series) {
93
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
94
+ (series as any).setData(data);
95
+ }
96
+ }
97
+
98
+ /** 更新序列最新数据点 */
99
+ update(seriesId: string, data: unknown): void {
100
+ const series = this.seriesMap.get(seriesId);
101
+ if (series) {
102
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
103
+ (series as any).update(data);
104
+ }
105
+ }
106
+
107
+ /** 移除序列 */
108
+ removeSeries(seriesId: string): void {
109
+ const series = this.seriesMap.get(seriesId);
110
+ if (series) {
111
+ this.chart?.removeSeries(series);
112
+ this.seriesMap.delete(seriesId);
113
+ this.eventManager.emit(ChartEvent.SeriesRemoved, { id: seriesId });
114
+ }
115
+ }
116
+
117
+ /** 切换序列类型 */
118
+ switchType(seriesId: string, newType: SeriesType, data?: unknown[]): string | null {
119
+ if (!this.chart) return null;
120
+
121
+ this.removeSeries(seriesId);
122
+
123
+ const definition = SERIES_DEFINITIONS[newType];
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
+ const newSeries = this.chart.addSeries(definition as any);
126
+ const newId = `main_${newType}_${Date.now()}`;
127
+ this.seriesMap.set(newId, newSeries as ISeriesApi<LWCSeriesType>);
128
+
129
+ if (data) {
130
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
131
+ (newSeries as any).setData(data);
132
+ }
133
+
134
+ return newId;
135
+ }
136
+
137
+ /** 获取序列 */
138
+ getSeries(seriesId: string): ISeriesApi<LWCSeriesType> | undefined {
139
+ return this.seriesMap.get(seriesId);
140
+ }
141
+
142
+ /** 获取第一个主序列(用于DrawingAdapter attach) */
143
+ getFirstMainSeries(): ISeriesApi<LWCSeriesType> | null {
144
+ for (const [id, series] of this.seriesMap) {
145
+ if (id.startsWith('main_')) return series;
146
+ }
147
+ return null;
148
+ }
149
+
150
+ /** 获取第一个任何类型的序列 */
151
+ getFirstSeries(): ISeriesApi<LWCSeriesType> | null {
152
+ const first = this.seriesMap.values().next();
153
+ return first.done ? null : first.value;
154
+ }
155
+
156
+ /** 清除所有序列 */
157
+ clearAll(): void {
158
+ this.seriesMap.forEach((series) => {
159
+ this.chart?.removeSeries(series);
160
+ });
161
+ this.seriesMap.clear();
162
+ }
163
+
164
+ /** 添加叠加品种(对比模式) */
165
+ addComparisonSeries(
166
+ type: SeriesType,
167
+ data: any[],
168
+ options?: Record<string, unknown>,
169
+ ): string | null {
170
+ if (!this.chart) return null;
171
+
172
+ // 使用独立 priceScaleId,右侧Y轴
173
+ const priceScaleId = 'comparison';
174
+ const definition = SERIES_DEFINITIONS[type];
175
+ const seriesOpts = {
176
+ ...options,
177
+ priceScaleId,
178
+ };
179
+
180
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
181
+ const series = this.chart.addSeries(definition as any, seriesOpts as any);
182
+ const id = `comparison_${type}_${Date.now()}`;
183
+ this.seriesMap.set(id, series as ISeriesApi<LWCSeriesType>);
184
+
185
+ // 设置右侧Y轴
186
+ this.chart.priceScale(priceScaleId).applyOptions({
187
+ scaleMargins: { top: 0.1, bottom: 0.2 },
188
+ });
189
+
190
+ // 设置数据
191
+ if (data.length > 0) {
192
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
193
+ (series as any).setData(data);
194
+ }
195
+
196
+ this.eventManager.emit(ChartEvent.SeriesAdded, { id, type });
197
+ return id;
198
+ }
199
+
200
+ /** 移除所有对比序列 */
201
+ clearComparisonSeries(): void {
202
+ const toRemove: string[] = [];
203
+ this.seriesMap.forEach((_series, id) => {
204
+ if (id.startsWith('comparison_')) {
205
+ toRemove.push(id);
206
+ }
207
+ });
208
+ toRemove.forEach((id) => this.removeSeries(id));
209
+ }
210
+ }
@@ -0,0 +1,59 @@
1
+ import type { DeepPartial, ChartOptions as LWCChartOptions } from 'lightweight-charts';
2
+ import type { ThemeConfig, ThemePreset } from '../types/index.js';
3
+ import { darkPreset, lightPreset } from '../utils/themePresets.js';
4
+ import { EventManager, ChartEvent } from './EventManager.js';
5
+
6
+ /**
7
+ * 主题管理器
8
+ * 管理图表主题的切换与配置
9
+ */
10
+ export class ThemeManager {
11
+ private currentTheme: ThemePreset = 'dark';
12
+ private eventManager: EventManager;
13
+ private presets: Record<ThemePreset, ThemeConfig>;
14
+
15
+ constructor(eventManager: EventManager) {
16
+ this.eventManager = eventManager;
17
+ this.presets = { dark: darkPreset, light: lightPreset };
18
+ }
19
+
20
+ /** 设置主题 */
21
+ setTheme(theme: ThemePreset): void {
22
+ this.currentTheme = theme;
23
+ this.eventManager.emit(ChartEvent.ThemeChanged, { theme });
24
+ }
25
+
26
+ /** 获取当前主题名称 */
27
+ getCurrentTheme(): ThemePreset {
28
+ return this.currentTheme;
29
+ }
30
+
31
+ /** 获取当前主题配置 */
32
+ getCurrentConfig(): ThemeConfig {
33
+ return this.presets[this.currentTheme];
34
+ }
35
+
36
+ /** 获取映射到LWC的图表选项 */
37
+ getChartOptions(theme?: ThemePreset): DeepPartial<LWCChartOptions> {
38
+ const config = this.presets[theme ?? this.currentTheme];
39
+ return {
40
+ layout: config.chart.layout,
41
+ grid: config.chart.grid,
42
+ };
43
+ }
44
+
45
+ /** 获取涨色 */
46
+ getUpColor(): string {
47
+ return this.presets[this.currentTheme].upColor;
48
+ }
49
+
50
+ /** 获取跌色 */
51
+ getDownColor(): string {
52
+ return this.presets[this.currentTheme].downColor;
53
+ }
54
+
55
+ /** 注册自定义主题预设 */
56
+ registerPreset(name: ThemePreset, config: ThemeConfig): void {
57
+ this.presets[name] = config;
58
+ }
59
+ }
@@ -0,0 +1,10 @@
1
+ export { EventManager, ChartEvent } from './EventManager.js';
2
+ export type { EventHandler } from './EventManager.js';
3
+ export { ChartManager } from './ChartManager.js';
4
+ export type { ChartManagerCreateOptions } from './ChartManager.js';
5
+ export { SeriesManager } from './SeriesManager.js';
6
+ export { DataManager } from './DataManager.js';
7
+ export type { FetchFn } from './DataManager.js';
8
+ export { ThemeManager } from './ThemeManager.js';
9
+ export { PaneManager } from './PaneManager.js';
10
+ export type { PaneInfo } from './PaneManager.js';
package/src/css.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ declare module '*.css' {
2
+ const content: string;
3
+ export default content;
4
+ }
@@ -0,0 +1,62 @@
1
+ import { useRef, useEffect, useState } from 'react';
2
+ import { EventManager } from '../core/EventManager.js';
3
+ import { ChartManager } from '../core/ChartManager.js';
4
+ import { SeriesManager } from '../core/SeriesManager.js';
5
+
6
+ export interface UseChartReturn {
7
+ containerRef: React.RefObject<HTMLDivElement | null>;
8
+ chartManager: ChartManager | null;
9
+ seriesManager: SeriesManager | null;
10
+ }
11
+
12
+ /**
13
+ * 图表Hook
14
+ * 创建ChartManager + SeriesManager,管理生命周期
15
+ * 使用 useState 触发重渲染,确保下游 effects 能拿到非null的 managers
16
+ */
17
+ export function useChart(): UseChartReturn {
18
+ const containerRef = useRef<HTMLDivElement | null>(null);
19
+ const eventManagerRef = useRef<EventManager>(new EventManager());
20
+ const [managers, setManagers] = useState<{
21
+ chartManager: ChartManager | null;
22
+ seriesManager: SeriesManager | null;
23
+ }>({ chartManager: null, seriesManager: null });
24
+
25
+ useEffect(() => {
26
+ const container = containerRef.current;
27
+ if (!container) return;
28
+
29
+ const em = eventManagerRef.current;
30
+ const cm = new ChartManager(em);
31
+ const chart = cm.create(container);
32
+ const sm = new SeriesManager(chart, em);
33
+
34
+ // 使用 useState 触发重渲染,让依赖 chartManager/seriesManager 的 effects 正确执行
35
+ setManagers({ chartManager: cm, seriesManager: sm });
36
+
37
+ // ResizeObserver 监听容器大小变化
38
+ const observer = new ResizeObserver((entries) => {
39
+ for (const entry of entries) {
40
+ const { width, height } = entry.contentRect;
41
+ if (width > 0 && height > 0) {
42
+ cm.resize(width, height);
43
+ }
44
+ }
45
+ });
46
+ observer.observe(container);
47
+
48
+ return () => {
49
+ observer.disconnect();
50
+ sm.clearAll();
51
+ cm.dispose();
52
+ em.clear();
53
+ setManagers({ chartManager: null, seriesManager: null });
54
+ };
55
+ }, []);
56
+
57
+ return {
58
+ containerRef,
59
+ chartManager: managers.chartManager,
60
+ seriesManager: managers.seriesManager,
61
+ };
62
+ }
@@ -0,0 +1,109 @@
1
+ import { useEffect, useRef, type RefObject } from 'react';
2
+ import type { IChartApi, ISeriesApi, SeriesType as LWCSeriesType } from 'lightweight-charts';
3
+ import { usePanelRegistry } from '../store/usePanelRegistry.js';
4
+
5
+ /**
6
+ * 多图表光标十字线联动 Hook
7
+ * 监听每个 chart 的 crosshairMove 事件,同步到其他 chart
8
+ *
9
+ * 修复点:
10
+ * 1. setCrosshairPosition 第三参数从 IChartApi 改为 ISeriesApi(通过 PanelRegistry 获取)
11
+ * 2. 依赖数组增加 panelIds.join(','),面板变化时重新订阅
12
+ */
13
+ export function useCrosshairSync(
14
+ chartRefs: RefObject<(IChartApi | null)[]>,
15
+ panelIds: string[] = [],
16
+ ): void {
17
+ const isSyncingRef = useRef(false);
18
+ const rafIdRef = useRef<number | null>(null);
19
+ const lastTimeRef = useRef<number>(0);
20
+
21
+ const chartsLength = chartRefs.current?.length ?? 0;
22
+ const panelIdsKey = panelIds.join(',');
23
+
24
+ useEffect(() => {
25
+ const charts = chartRefs.current;
26
+ if (!charts || charts.length <= 1) return;
27
+
28
+ const subscriptions: (() => void)[] = [];
29
+
30
+ charts.forEach((sourceChart, sourceIndex) => {
31
+ if (!sourceChart) return;
32
+
33
+ const handler = (param: any) => {
34
+ if (isSyncingRef.current) return;
35
+ if (!param.time) return;
36
+
37
+ // 16ms 节流
38
+ const now = performance.now();
39
+ if (now - lastTimeRef.current < 16) {
40
+ return;
41
+ }
42
+ lastTimeRef.current = now;
43
+
44
+ isSyncingRef.current = true;
45
+
46
+ if (rafIdRef.current !== null) {
47
+ cancelAnimationFrame(rafIdRef.current);
48
+ }
49
+
50
+ const time = param.time;
51
+
52
+ rafIdRef.current = requestAnimationFrame(() => {
53
+ charts.forEach((targetChart, targetIndex) => {
54
+ if (targetIndex === sourceIndex || !targetChart) return;
55
+
56
+ // 通过 PanelRegistry 获取目标面板的 series
57
+ const targetPanelId = panelIds[targetIndex];
58
+ if (!targetPanelId) return;
59
+
60
+ const handle = usePanelRegistry.getState().getHandle(targetPanelId);
61
+ const targetSeries = handle?.seriesManager?.getFirstMainSeries();
62
+ if (!targetSeries) return;
63
+
64
+ try {
65
+ // 获取源 chart 上对应 series 的价格
66
+ const seriesData = param.seriesData;
67
+ let price: number | null = null;
68
+
69
+ if (seriesData && seriesData.size > 0) {
70
+ const entries = Array.from(seriesData.values());
71
+ if (entries.length > 0) {
72
+ const data = entries[0] as any;
73
+ price = data.value ?? data.close ?? null;
74
+ }
75
+ }
76
+
77
+ const finalPrice = price ?? 0;
78
+
79
+ targetChart.setCrosshairPosition(
80
+ finalPrice,
81
+ time,
82
+ targetSeries as ISeriesApi<LWCSeriesType>,
83
+ );
84
+ } catch {
85
+ // 静默处理同步错误(time 在目标 chart 上不存在等情况)
86
+ }
87
+ });
88
+
89
+ isSyncingRef.current = false;
90
+ rafIdRef.current = null;
91
+ });
92
+ };
93
+
94
+ sourceChart.subscribeCrosshairMove(handler);
95
+ subscriptions.push(() => {
96
+ sourceChart!.unsubscribeCrosshairMove(handler);
97
+ });
98
+ });
99
+
100
+ return () => {
101
+ subscriptions.forEach((unsub) => unsub());
102
+ if (rafIdRef.current !== null) {
103
+ cancelAnimationFrame(rafIdRef.current);
104
+ }
105
+ isSyncingRef.current = false;
106
+ };
107
+ // eslint-disable-next-line react-hooks/exhaustive-deps
108
+ }, [chartRefs, chartsLength, panelIdsKey]);
109
+ }