react-klinecharts-ui 0.2.0 → 0.4.0

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.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  **react-klinecharts-ui** is a headless React library for building financial trading terminals on top of [klinecharts](https://github.com/liihuu/KLineChart). It provides a state provider, a set of hooks, and overlay templates. No UI components are included — use any UI framework you prefer.
4
4
 
5
+ **[Live Demo](https://nemezzizz.github.io/react-klinecharts-ui/)**
6
+
5
7
  ### Acknowledgments
6
8
 
7
9
  Many features in this library — including 11 TradingView-style indicators, 9 drawing overlays, the TA math library, undo/redo, layout manager, and script editor — were ported from the [QUANTIX Extended Edition](https://github.com/dsavenk0/KLineChart-Pro) fork of KLineChart-Pro by [@dsavenk0](https://github.com/dsavenk0). The original fork implements these as a tightly-coupled Vue 3 application; this library re-implements them as headless React hooks following the composable, framework-agnostic architecture.
@@ -34,6 +36,11 @@ Many features in this library — including 11 TradingView-style indicators, 9 d
34
36
  - [useUndoRedo](#useundoredo)
35
37
  - [useLayoutManager](#uselayoutmanager)
36
38
  - [useScriptEditor](#usescripteditor)
39
+ - [useWatchlist](#usewatchlist)
40
+ - [useCompare](#usecompare)
41
+ - [useMeasure](#usemeasure)
42
+ - [useAnnotations](#useannotations)
43
+ - [useReplay](#usereplay)
37
44
  6. [Utilities](#utilities)
38
45
  - [createDataLoader](#createdataloader)
39
46
  - [TA (Technical Analysis)](#ta-technical-analysis)
@@ -48,14 +55,12 @@ Many features in this library — including 11 TradingView-style indicators, 9 d
48
55
 
49
56
  ## Installation
50
57
 
51
- > **Note:** `react-klinecharts` is not yet published to the npm registry. Install it directly from GitHub:
52
-
53
58
  ```bash
54
- npm install react-klinecharts-ui github:NemeZZiZZ/react-klinecharts
59
+ npm install react-klinecharts-ui react-klinecharts
55
60
  # or with pnpm
56
- pnpm add react-klinecharts-ui github:NemeZZiZZ/react-klinecharts
61
+ pnpm add react-klinecharts-ui react-klinecharts
57
62
  # or with yarn
58
- yarn add react-klinecharts-ui github:NemeZZiZZ/react-klinecharts
63
+ yarn add react-klinecharts-ui react-klinecharts
59
64
  ```
60
65
 
61
66
  ---
@@ -201,6 +206,7 @@ interface KlinechartsUIState {
201
206
  periods: TerminalPeriod[]; // List of available timeframes
202
207
  mainIndicators: string[]; // Active main chart indicators
203
208
  subIndicators: Record<string, string>; // Active sub-indicators: { name → paneId }
209
+ indicatorAxes: Record<string, string>; // Custom Y-axis bindings: { indicatorId → yAxisId }
204
210
  styles: DeepPartial<Styles> | undefined; // Custom klinecharts styles
205
211
  screenshotUrl: string | null; // URL of the last screenshot
206
212
  }
@@ -220,6 +226,7 @@ type KlinechartsUIAction =
220
226
  | { type: "SET_LOADING"; isLoading: boolean }
221
227
  | { type: "SET_MAIN_INDICATORS"; indicators: string[] }
222
228
  | { type: "SET_SUB_INDICATORS"; indicators: Record<string, string> }
229
+ | { type: "SET_INDICATOR_AXES"; axes: Record<string, string> }
223
230
  | { type: "SET_STYLES"; styles: DeepPartial<Styles> | undefined }
224
231
  | { type: "SET_LOCALE"; locale: string }
225
232
  | { type: "SET_SCREENSHOT_URL"; url: string | null };
@@ -454,6 +461,9 @@ const {
454
461
  getIndicatorParams,
455
462
  isMainIndicatorActive,
456
463
  isSubIndicatorActive,
464
+ indicatorAxes,
465
+ getIndicatorAxis,
466
+ bindIndicatorToNewAxis,
457
467
  } = useIndicators();
458
468
  ```
459
469
 
@@ -479,9 +489,9 @@ interface IndicatorInfo {
479
489
 
480
490
  | Method | Description |
481
491
  | --------------------------------------------- | ------------------------------------------------------------- |
482
- | `addMainIndicator(name)` | Creates the indicator on `candle_pane` with id `main_${name}` |
492
+ | `addMainIndicator(name, options?)` | Creates the indicator on `candle_pane` with id `main_${name}`. Pass `{ yAxis }` to bind it to a secondary axis (see below) |
483
493
  | `removeMainIndicator(name)` | Removes the indicator and updates state |
484
- | `addSubIndicator(name)` | Creates the indicator on a new sub-pane with id `sub_${name}` |
494
+ | `addSubIndicator(name, options?)` | Creates the indicator on a new sub-pane with id `sub_${name}`. Also accepts `{ yAxis }` |
485
495
  | `removeSubIndicator(name)` | Removes the sub-indicator and its pane |
486
496
  | `toggleMainIndicator(name)` | `add` if inactive, `remove` if active |
487
497
  | `toggleSubIndicator(name)` | Same for sub-indicators |
@@ -492,6 +502,32 @@ interface IndicatorInfo {
492
502
  | `getIndicatorParams(name)` | Returns `[{ label, defaultValue }]` or `[]` if no parameters |
493
503
  | `isMainIndicatorActive(name)` | Quick active check |
494
504
  | `isSubIndicatorActive(name)` | Quick active check |
505
+ | `getIndicatorAxis(name, isMain)` | Returns the custom `yAxisId` an indicator is bound to, or `undefined` for the default axis |
506
+ | `bindIndicatorToNewAxis(name, isMain, yAxis?)`| Rebinds an existing indicator to a different Y-axis (omit `yAxis` to return it to the default axis) |
507
+
508
+ #### Secondary Y-axis binding (multiple Y-axes)
509
+
510
+ klinecharts v10 supports several independent Y-axes on one pane, so an indicator whose value range differs sharply from price (RSI 0–100, volume, …) can get its own scale instead of distorting the shared price axis or being forced into a separate sub-pane.
511
+
512
+ ```typescript
513
+ // Add RSI on the main pane, on its own left axis (price scale stays intact)
514
+ addMainIndicator("RSI", { yAxis: { id: "rsi_axis", position: "left" } });
515
+
516
+ // Later: move it back to the shared price axis
517
+ bindIndicatorToNewAxis("RSI", true);
518
+
519
+ // Or move an indicator already on the chart onto a dedicated right axis
520
+ bindIndicatorToNewAxis("VOL", true, { id: "vol_axis", position: "right" });
521
+ ```
522
+
523
+ `yAxis` is a klinecharts `YAxisOverride`; the key field is **`id`** — indicators sharing the same `id` share one axis. Useful extras: `position` (`"left" | "right"`), `name` (scale type: `"normal" | "percentage" | "log"`), `inside`, and `needWidget` (set `false` for an invisible axis — own scale, no labels).
524
+
525
+ This binding is a **persistent property, not a one-shot action**. It is tracked in provider state (`indicatorAxes`, keyed by indicator id) and preserved across:
526
+
527
+ - **undo/redo** — toggling an indicator off and back on (`useUndoRedo`) restores it on the same axis, not the price axis;
528
+ - **layout presets** — `useLayoutManager` save/load reproduces each indicator's axis 1:1.
529
+
530
+ > Note: `bindIndicatorToNewAxis` removes and recreates the indicator (v10 `overrideIndicator` cannot rebind an axis), preserving calc params, styles and visibility. The rebind action itself is not pushed onto the undo stack.
495
531
 
496
532
  #### Available indicators
497
533
 
@@ -909,6 +945,188 @@ return rsi.map((v, i) => ({
909
945
 
910
946
  ---
911
947
 
948
+ ### useWatchlist
949
+
950
+ Manage a list of tracked symbols with live price updates from your datafeed.
951
+
952
+ ```typescript
953
+ import { useWatchlist } from "react-klinecharts-ui";
954
+
955
+ const {
956
+ items,
957
+ addSymbol,
958
+ removeSymbol,
959
+ switchSymbol,
960
+ activeSymbol,
961
+ } = useWatchlist();
962
+ ```
963
+
964
+ #### Return type: `UseWatchlistReturn`
965
+
966
+ | Property | Type | Description |
967
+ |----------|------|-------------|
968
+ | `items` | `WatchlistItem[]` | Array of tracked symbols with price data |
969
+ | `addSymbol` | `(ticker: string) => void` | Add a symbol to the watchlist |
970
+ | `removeSymbol` | `(ticker: string) => void` | Remove a symbol from the watchlist |
971
+ | `switchSymbol` | `(ticker: string) => void` | Change the chart to display a symbol |
972
+ | `activeSymbol` | `string \| null` | Currently displayed symbol ticker |
973
+
974
+ #### WatchlistItem
975
+
976
+ | Property | Type | Description |
977
+ |----------|------|-------------|
978
+ | `ticker` | `string` | Symbol identifier |
979
+ | `lastPrice` | `number \| null` | Last traded price |
980
+ | `change` | `number \| null` | Absolute price change |
981
+ | `changePercent` | `number \| null` | 24h percentage change |
982
+
983
+ ---
984
+
985
+ ### useCompare
986
+
987
+ Compare multiple symbols on the same chart with individual colors and visibility toggle.
988
+
989
+ ```typescript
990
+ import { useCompare } from "react-klinecharts-ui";
991
+
992
+ const {
993
+ symbols,
994
+ addSymbol,
995
+ removeSymbol,
996
+ toggleSymbol,
997
+ clearAll,
998
+ } = useCompare();
999
+ ```
1000
+
1001
+ #### Return type: `UseCompareReturn`
1002
+
1003
+ | Property | Type | Description |
1004
+ |----------|------|-------------|
1005
+ | `symbols` | `CompareSymbol[]` | Array of comparison symbols with metadata |
1006
+ | `addSymbol` | `(ticker: string) => void` | Add a symbol to compare |
1007
+ | `removeSymbol` | `(ticker: string) => void` | Remove a comparison symbol |
1008
+ | `toggleSymbol` | `(ticker: string) => void` | Toggle visibility of a symbol |
1009
+ | `clearAll` | `() => void` | Remove all comparison symbols |
1010
+
1011
+ #### CompareSymbol
1012
+
1013
+ | Property | Type | Description |
1014
+ |----------|------|-------------|
1015
+ | `ticker` | `string` | Symbol identifier |
1016
+ | `visible` | `boolean` | Whether the symbol is currently visible on chart |
1017
+ | `color` | `string` | Hex color code for the symbol's line |
1018
+
1019
+ ---
1020
+
1021
+ ### useMeasure
1022
+
1023
+ Measure price changes, percentage swings, bar count, and time intervals between two points on the chart.
1024
+
1025
+ ```typescript
1026
+ import { useMeasure } from "react-klinecharts-ui";
1027
+
1028
+ const {
1029
+ isActive,
1030
+ startMeasure,
1031
+ cancelMeasure,
1032
+ result,
1033
+ } = useMeasure();
1034
+ ```
1035
+
1036
+ #### Return type: `UseMeasureReturn`
1037
+
1038
+ | Property | Type | Description |
1039
+ |----------|------|-------------|
1040
+ | `isActive` | `boolean` | Whether measurement mode is enabled |
1041
+ | `startMeasure` | `() => void` | Enter measurement mode (click two points) |
1042
+ | `cancelMeasure` | `() => void` | Exit measurement mode |
1043
+ | `result` | `MeasureResult \| null` | Measurement data (null if not measured yet) |
1044
+
1045
+ #### MeasureResult
1046
+
1047
+ | Property | Type | Description |
1048
+ |----------|------|-------------|
1049
+ | `priceDiff` | `number` | Absolute price difference |
1050
+ | `pricePercent` | `number` | Percentage change |
1051
+ | `bars` | `number` | Number of bars between points |
1052
+ | `timeDiff` | `number` | Time difference in milliseconds |
1053
+
1054
+ ---
1055
+
1056
+ ### useAnnotations
1057
+
1058
+ Add text annotations at specific price levels and timestamps with optional colors.
1059
+
1060
+ ```typescript
1061
+ import { useAnnotations } from "react-klinecharts-ui";
1062
+
1063
+ const {
1064
+ annotations,
1065
+ addAnnotation,
1066
+ removeAnnotation,
1067
+ updateAnnotation,
1068
+ clearAnnotations,
1069
+ } = useAnnotations();
1070
+ ```
1071
+
1072
+ #### Return type: `UseAnnotationsReturn`
1073
+
1074
+ | Property | Type | Description |
1075
+ |----------|------|-------------|
1076
+ | `annotations` | `Annotation[]` | Array of all annotations |
1077
+ | `addAnnotation` | `(text, price, timestamp, color?) => string` | Add annotation; returns ID |
1078
+ | `removeAnnotation` | `(id: string) => void` | Remove annotation by ID |
1079
+ | `updateAnnotation` | `(id, updates) => void` | Update text or color |
1080
+ | `clearAnnotations` | `() => void` | Remove all annotations |
1081
+
1082
+ #### Annotation
1083
+
1084
+ | Property | Type | Description |
1085
+ |----------|------|-------------|
1086
+ | `id` | `string` | Unique identifier |
1087
+ | `text` | `string` | Annotation text |
1088
+ | `price` | `number` | Price level |
1089
+ | `timestamp` | `number` | Candle timestamp |
1090
+ | `color` | `string \| undefined` | Optional hex color |
1091
+
1092
+ ---
1093
+
1094
+ ### useReplay
1095
+
1096
+ Replay historical candles at various speeds with play/pause/step controls and progress tracking.
1097
+
1098
+ ```typescript
1099
+ import { useReplay } from "react-klinecharts-ui";
1100
+
1101
+ const {
1102
+ isPlaying,
1103
+ speed,
1104
+ currentIndex,
1105
+ totalBars,
1106
+ play,
1107
+ pause,
1108
+ stop,
1109
+ step,
1110
+ setSpeed,
1111
+ } = useReplay();
1112
+ ```
1113
+
1114
+ #### Return type: `UseReplayReturn`
1115
+
1116
+ | Property | Type | Description |
1117
+ |----------|------|-------------|
1118
+ | `isPlaying` | `boolean` | Whether replay is running |
1119
+ | `speed` | `number` | Current playback speed (0.25, 0.5, 1, 2, 4) |
1120
+ | `currentIndex` | `number` | Current bar index being displayed |
1121
+ | `totalBars` | `number` | Total bars in the dataset |
1122
+ | `play` | `() => void` | Start replay from current position |
1123
+ | `pause` | `() => void` | Pause replay (can resume with play) |
1124
+ | `stop` | `() => void` | Stop replay and reset to start |
1125
+ | `step` | `() => void` | Advance one bar when paused |
1126
+ | `setSpeed` | `(speed: number) => void` | Set playback speed multiplier |
1127
+
1128
+ ---
1129
+
912
1130
  ## Utilities
913
1131
 
914
1132
  ### createDataLoader
@@ -1208,6 +1426,58 @@ interface OrderLinePadding {
1208
1426
 
1209
1427
  All sub-interfaces (`OrderLineLineStyle`, `OrderLineMarkStyle`, `OrderLineLabelStyle`, `OrderLineFontStyle`, `OrderLinePadding`) are exported as named types from both the main and `extensions` entry points.
1210
1428
 
1429
+ ### depthOverlay
1430
+
1431
+ Overlay template for visualizing order book depth as horizontal liquidity bars at each price level. Shows cumulative buy/sell volume at price levels, useful for understanding support/resistance zones.
1432
+
1433
+ ```typescript
1434
+ import { depthOverlay, KlinechartsUIProvider } from "react-klinecharts-ui/extensions";
1435
+
1436
+ <KlinechartsUIProvider overlays={[depthOverlay]}>...</KlinechartsUIProvider>;
1437
+ ```
1438
+
1439
+ **Implementation details:**
1440
+
1441
+ - `needDefaultPointFigure: false`, `needDefaultXAxisFigure: false`, `needDefaultYAxisFigure: false` — entirely custom rendering
1442
+ - `createPointFigures` — draws rect figures at each price level using `yAxis.convertToPixel(price)` for positioning
1443
+ - `zLevel: -1` — renders behind the main candlestick data
1444
+ - Dynamic data via `extendData` — update with `chart.overrideOverlay({ id, extendData: newData })`
1445
+
1446
+ #### DepthOverlayExtendData
1447
+
1448
+ ```typescript
1449
+ interface DepthOverlayExtendData {
1450
+ rows?: DepthOverlayRow[];
1451
+ /** Optional: customize buy side color. Default: "rgba(16, 186, 156, 0.3)" */
1452
+ buyColor?: string;
1453
+ /** Optional: customize sell side color. Default: "rgba(239, 68, 68, 0.3)" */
1454
+ sellColor?: string;
1455
+ }
1456
+
1457
+ interface DepthOverlayRow {
1458
+ price: number;
1459
+ buyVolume: number;
1460
+ sellVolume: number;
1461
+ }
1462
+ ```
1463
+
1464
+ **Usage example:**
1465
+
1466
+ ```typescript
1467
+ const chart = state.chart;
1468
+ if (chart) {
1469
+ const rows: DepthOverlayRow[] = [
1470
+ { price: 40000, buyVolume: 5.2, sellVolume: 3.1 },
1471
+ { price: 40100, buyVolume: 4.8, sellVolume: 4.2 },
1472
+ // ...
1473
+ ];
1474
+ chart.overrideOverlay({
1475
+ id: "depthOverlay",
1476
+ extendData: { rows, buyColor: "rgba(34, 197, 94, 0.3)" },
1477
+ });
1478
+ }
1479
+ ```
1480
+
1211
1481
  ### overlays (array)
1212
1482
 
1213
1483
  Array of all built-in drawing overlays — for direct access to the list:
@@ -2410,6 +2410,50 @@ var orderLine = {
2410
2410
  };
2411
2411
  var orderLine_default = orderLine;
2412
2412
 
2413
+ // src/extensions/overlays/depthOverlay.ts
2414
+ var depthOverlay = {
2415
+ name: "depthOverlay",
2416
+ totalStep: 2,
2417
+ needDefaultPointFigure: false,
2418
+ needDefaultXAxisFigure: false,
2419
+ needDefaultYAxisFigure: false,
2420
+ lock: true,
2421
+ visible: true,
2422
+ zLevel: -1,
2423
+ createPointFigures: ({ overlay, bounding, yAxis }) => {
2424
+ const d = overlay.extendData;
2425
+ if (!d || !d.rows || d.rows.length === 0 || !yAxis) return [];
2426
+ const maxBarFraction = d.maxBarWidth ?? 0.3;
2427
+ const maxBarPx = bounding.width * maxBarFraction;
2428
+ const askColor = d.askColor ?? "rgba(239,83,80,0.25)";
2429
+ const bidColor = d.bidColor ?? "rgba(38,166,154,0.25)";
2430
+ const maxQty = d.maxQty || 1;
2431
+ const figures = [];
2432
+ for (const row of d.rows) {
2433
+ const y = yAxis.convertToPixel(row.price);
2434
+ if (y == null || y < 0 || y > bounding.height) continue;
2435
+ const barWidth = row.qty / maxQty * maxBarPx;
2436
+ const barHeight = Math.max(2, bounding.height / d.rows.length * 0.8);
2437
+ const color = row.side === "ask" ? askColor : bidColor;
2438
+ figures.push({
2439
+ type: "rect",
2440
+ attrs: {
2441
+ x: bounding.width - barWidth,
2442
+ y: y - barHeight / 2,
2443
+ width: barWidth,
2444
+ height: barHeight
2445
+ },
2446
+ styles: {
2447
+ color
2448
+ },
2449
+ ignoreEvent: true
2450
+ });
2451
+ }
2452
+ return figures;
2453
+ }
2454
+ };
2455
+ var depthOverlay_default = depthOverlay;
2456
+
2413
2457
  // src/extensions/index.ts
2414
2458
  var overlays = Object.values(overlays_exports);
2415
2459
  var indicators = Object.values(indicators_exports);
@@ -2429,6 +2473,7 @@ exports.bollTv_default = bollTv_default;
2429
2473
  exports.brush_default = brush_default;
2430
2474
  exports.cci_default = cci_default;
2431
2475
  exports.circle_default = circle_default;
2476
+ exports.depthOverlay_default = depthOverlay_default;
2432
2477
  exports.eightWaves_default = eightWaves_default;
2433
2478
  exports.elliottWave_default = elliottWave_default;
2434
2479
  exports.fibRetracement_default = fibRetracement_default;
@@ -2463,5 +2508,5 @@ exports.threeWaves_default = threeWaves_default;
2463
2508
  exports.triangle_default = triangle_default;
2464
2509
  exports.vwap_default = vwap_default;
2465
2510
  exports.xabcd_default = xabcd_default;
2466
- //# sourceMappingURL=chunk-47JXSAIT.cjs.map
2467
- //# sourceMappingURL=chunk-47JXSAIT.cjs.map
2511
+ //# sourceMappingURL=chunk-PIFLJKYF.cjs.map
2512
+ //# sourceMappingURL=chunk-PIFLJKYF.cjs.map