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 +277 -7
- package/dist/{chunk-47JXSAIT.cjs → chunk-PIFLJKYF.cjs} +47 -2
- package/dist/chunk-PIFLJKYF.cjs.map +1 -0
- package/dist/{chunk-B3NZ66YA.js → chunk-U325AHHX.js} +47 -3
- package/dist/chunk-U325AHHX.js.map +1 -0
- package/dist/extensions.cjs +9 -5
- package/dist/extensions.d.cts +18 -1
- package/dist/extensions.d.ts +18 -1
- package/dist/extensions.js +1 -1
- package/dist/index.cjs +1117 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +232 -5
- package/dist/index.d.ts +232 -5
- package/dist/index.js +1063 -81
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/dist/chunk-47JXSAIT.cjs.map +0 -1
- package/dist/chunk-B3NZ66YA.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkPIFLJKYF_cjs = require('./chunk-PIFLJKYF.cjs');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var reactKlinecharts = require('react-klinecharts');
|
|
6
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
@@ -58,6 +58,8 @@ function reducer(state, action) {
|
|
|
58
58
|
return { ...state, mainIndicators: action.indicators };
|
|
59
59
|
case "SET_SUB_INDICATORS":
|
|
60
60
|
return { ...state, subIndicators: action.indicators };
|
|
61
|
+
case "SET_INDICATOR_AXES":
|
|
62
|
+
return { ...state, indicatorAxes: action.axes };
|
|
61
63
|
case "SET_STYLES":
|
|
62
64
|
return { ...state, styles: action.styles };
|
|
63
65
|
case "SET_LOCALE":
|
|
@@ -120,6 +122,7 @@ function KlinechartsUIProvider({
|
|
|
120
122
|
(acc, name) => ({ ...acc, [name]: "" }),
|
|
121
123
|
{}
|
|
122
124
|
),
|
|
125
|
+
indicatorAxes: {},
|
|
123
126
|
styles: opts.styles,
|
|
124
127
|
screenshotUrl: null
|
|
125
128
|
})
|
|
@@ -177,7 +180,7 @@ function KlinechartsUIProvider({
|
|
|
177
180
|
const extraOverlaysRef = react.useRef(extraOverlays);
|
|
178
181
|
react.useEffect(() => {
|
|
179
182
|
if (shouldRegister) {
|
|
180
|
-
|
|
183
|
+
chunkPIFLJKYF_cjs.registerExtensions();
|
|
181
184
|
}
|
|
182
185
|
extraOverlaysRef.current?.forEach((overlay) => reactKlinecharts.registerOverlay(overlay));
|
|
183
186
|
}, [shouldRegister]);
|
|
@@ -644,9 +647,12 @@ var INDICATOR_PARAMS = {
|
|
|
644
647
|
};
|
|
645
648
|
|
|
646
649
|
// src/hooks/useIndicators.ts
|
|
650
|
+
var COLLAPSED_HEIGHT = 30;
|
|
647
651
|
function useIndicators() {
|
|
648
652
|
const { state, dispatch } = useKlinechartsUI();
|
|
649
653
|
const { undoRedoListenerRef } = useKlinechartsUIDispatch();
|
|
654
|
+
const paneHeightsRef = react.useRef({});
|
|
655
|
+
const collapsedPanesRef = react.useRef(/* @__PURE__ */ new Set());
|
|
650
656
|
const mainIndicators = react.useMemo(() => {
|
|
651
657
|
const activeNames = state.mainIndicators;
|
|
652
658
|
const inactive = MAIN_INDICATORS.filter((n) => !activeNames.includes(n));
|
|
@@ -664,62 +670,95 @@ function useIndicators() {
|
|
|
664
670
|
];
|
|
665
671
|
}, [state.subIndicators]);
|
|
666
672
|
const addMainIndicator = react.useCallback(
|
|
667
|
-
(name) => {
|
|
673
|
+
(name, options) => {
|
|
668
674
|
if (state.mainIndicators.includes(name)) return;
|
|
675
|
+
const yAxis = options?.yAxis;
|
|
669
676
|
state.chart?.createIndicator(
|
|
670
|
-
{ name, id: `main_${name}
|
|
671
|
-
true,
|
|
672
|
-
{ id: "candle_pane" }
|
|
677
|
+
{ name, id: `main_${name}` },
|
|
678
|
+
{ isStack: true, pane: { id: "candle_pane" }, yAxis }
|
|
673
679
|
);
|
|
674
680
|
const newIndicators = [...state.mainIndicators, name];
|
|
675
681
|
dispatch({ type: "SET_MAIN_INDICATORS", indicators: newIndicators });
|
|
682
|
+
if (yAxis?.id) {
|
|
683
|
+
dispatch({
|
|
684
|
+
type: "SET_INDICATOR_AXES",
|
|
685
|
+
axes: { ...state.indicatorAxes, [`main_${name}`]: yAxis.id }
|
|
686
|
+
});
|
|
687
|
+
}
|
|
676
688
|
undoRedoListenerRef.current?.({
|
|
677
689
|
type: "indicator_toggled",
|
|
678
|
-
data: {
|
|
690
|
+
data: {
|
|
691
|
+
name,
|
|
692
|
+
wasActive: false,
|
|
693
|
+
isMain: true,
|
|
694
|
+
paneId: "candle_pane",
|
|
695
|
+
yAxisId: yAxis?.id
|
|
696
|
+
}
|
|
679
697
|
});
|
|
680
698
|
},
|
|
681
|
-
[state.chart, state.mainIndicators, dispatch, undoRedoListenerRef]
|
|
699
|
+
[state.chart, state.mainIndicators, state.indicatorAxes, dispatch, undoRedoListenerRef]
|
|
682
700
|
);
|
|
683
701
|
const removeMainIndicator = react.useCallback(
|
|
684
702
|
(name) => {
|
|
685
|
-
|
|
703
|
+
const id = `main_${name}`;
|
|
704
|
+
const yAxisId = state.indicatorAxes[id];
|
|
705
|
+
state.chart?.removeIndicator({ id });
|
|
686
706
|
const newIndicators = state.mainIndicators.filter((n) => n !== name);
|
|
687
707
|
dispatch({ type: "SET_MAIN_INDICATORS", indicators: newIndicators });
|
|
708
|
+
if (yAxisId) {
|
|
709
|
+
const nextAxes = { ...state.indicatorAxes };
|
|
710
|
+
delete nextAxes[id];
|
|
711
|
+
dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
|
|
712
|
+
}
|
|
688
713
|
undoRedoListenerRef.current?.({
|
|
689
714
|
type: "indicator_toggled",
|
|
690
|
-
data: { name, wasActive: true, isMain: true, paneId: "candle_pane" }
|
|
715
|
+
data: { name, wasActive: true, isMain: true, paneId: "candle_pane", yAxisId }
|
|
691
716
|
});
|
|
692
717
|
},
|
|
693
|
-
[state.chart, state.mainIndicators, dispatch, undoRedoListenerRef]
|
|
718
|
+
[state.chart, state.mainIndicators, state.indicatorAxes, dispatch, undoRedoListenerRef]
|
|
694
719
|
);
|
|
695
720
|
const addSubIndicator = react.useCallback(
|
|
696
|
-
(name) => {
|
|
721
|
+
(name, options) => {
|
|
697
722
|
if (name in state.subIndicators) return;
|
|
698
723
|
const id = `sub_${name}`;
|
|
699
|
-
|
|
724
|
+
const yAxis = options?.yAxis;
|
|
725
|
+
state.chart?.createIndicator({ name, id }, { yAxis });
|
|
700
726
|
const paneId = state.chart?.getIndicators({ id })?.[0]?.paneId ?? "";
|
|
701
727
|
const newIndicators = { ...state.subIndicators, [name]: paneId };
|
|
702
728
|
dispatch({ type: "SET_SUB_INDICATORS", indicators: newIndicators });
|
|
729
|
+
if (yAxis?.id) {
|
|
730
|
+
dispatch({
|
|
731
|
+
type: "SET_INDICATOR_AXES",
|
|
732
|
+
axes: { ...state.indicatorAxes, [id]: yAxis.id }
|
|
733
|
+
});
|
|
734
|
+
}
|
|
703
735
|
undoRedoListenerRef.current?.({
|
|
704
736
|
type: "indicator_toggled",
|
|
705
|
-
data: { name, wasActive: false, isMain: false, paneId }
|
|
737
|
+
data: { name, wasActive: false, isMain: false, paneId, yAxisId: yAxis?.id }
|
|
706
738
|
});
|
|
707
739
|
},
|
|
708
|
-
[state.chart, state.subIndicators, dispatch, undoRedoListenerRef]
|
|
740
|
+
[state.chart, state.subIndicators, state.indicatorAxes, dispatch, undoRedoListenerRef]
|
|
709
741
|
);
|
|
710
742
|
const removeSubIndicator = react.useCallback(
|
|
711
743
|
(name) => {
|
|
744
|
+
const id = `sub_${name}`;
|
|
712
745
|
const paneId = state.subIndicators[name] ?? "";
|
|
713
|
-
state.
|
|
746
|
+
const yAxisId = state.indicatorAxes[id];
|
|
747
|
+
state.chart?.removeIndicator({ id });
|
|
714
748
|
const newIndicators = { ...state.subIndicators };
|
|
715
749
|
delete newIndicators[name];
|
|
716
750
|
dispatch({ type: "SET_SUB_INDICATORS", indicators: newIndicators });
|
|
751
|
+
if (yAxisId) {
|
|
752
|
+
const nextAxes = { ...state.indicatorAxes };
|
|
753
|
+
delete nextAxes[id];
|
|
754
|
+
dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
|
|
755
|
+
}
|
|
717
756
|
undoRedoListenerRef.current?.({
|
|
718
757
|
type: "indicator_toggled",
|
|
719
|
-
data: { name, wasActive: true, isMain: false, paneId }
|
|
758
|
+
data: { name, wasActive: true, isMain: false, paneId, yAxisId }
|
|
720
759
|
});
|
|
721
760
|
},
|
|
722
|
-
[state.chart, state.subIndicators, dispatch, undoRedoListenerRef]
|
|
761
|
+
[state.chart, state.subIndicators, state.indicatorAxes, dispatch, undoRedoListenerRef]
|
|
723
762
|
);
|
|
724
763
|
const toggleMainIndicator = react.useCallback(
|
|
725
764
|
(name) => {
|
|
@@ -749,8 +788,10 @@ function useIndicators() {
|
|
|
749
788
|
[state.chart]
|
|
750
789
|
);
|
|
751
790
|
const updateIndicatorParams = react.useCallback(
|
|
752
|
-
|
|
753
|
-
|
|
791
|
+
// `paneId` is kept in the signature for API stability; klinecharts v10
|
|
792
|
+
// `overrideIndicator` no longer accepts it and targets by name/id instead.
|
|
793
|
+
(name, _paneId, params) => {
|
|
794
|
+
state.chart?.overrideIndicator({ name, calcParams: params });
|
|
754
795
|
},
|
|
755
796
|
[state.chart]
|
|
756
797
|
);
|
|
@@ -771,11 +812,9 @@ function useIndicators() {
|
|
|
771
812
|
{
|
|
772
813
|
name,
|
|
773
814
|
id: `main_${name}`,
|
|
774
|
-
paneId: "candle_pane",
|
|
775
815
|
...prevCalcParams ? { calcParams: prevCalcParams } : {}
|
|
776
816
|
},
|
|
777
|
-
true,
|
|
778
|
-
{ id: "candle_pane" }
|
|
817
|
+
{ isStack: true, pane: { id: "candle_pane" } }
|
|
779
818
|
);
|
|
780
819
|
const newSub = { ...state.subIndicators };
|
|
781
820
|
delete newSub[name];
|
|
@@ -816,6 +855,128 @@ function useIndicators() {
|
|
|
816
855
|
(name) => name in state.subIndicators,
|
|
817
856
|
[state.subIndicators]
|
|
818
857
|
);
|
|
858
|
+
const collapseSubIndicator = react.useCallback(
|
|
859
|
+
(name) => {
|
|
860
|
+
const paneId = state.subIndicators[name];
|
|
861
|
+
if (!state.chart || !paneId) return;
|
|
862
|
+
const currentSize = state.chart.getSize?.(paneId);
|
|
863
|
+
if (currentSize?.height && currentSize.height > COLLAPSED_HEIGHT) {
|
|
864
|
+
paneHeightsRef.current[paneId] = currentSize.height;
|
|
865
|
+
}
|
|
866
|
+
collapsedPanesRef.current.add(paneId);
|
|
867
|
+
state.chart.setPaneOptions?.({
|
|
868
|
+
id: paneId,
|
|
869
|
+
height: COLLAPSED_HEIGHT
|
|
870
|
+
});
|
|
871
|
+
state.chart.overrideIndicator({ name, id: `sub_${name}`, visible: false });
|
|
872
|
+
},
|
|
873
|
+
[state.chart, state.subIndicators]
|
|
874
|
+
);
|
|
875
|
+
const expandSubIndicator = react.useCallback(
|
|
876
|
+
(name) => {
|
|
877
|
+
const paneId = state.subIndicators[name];
|
|
878
|
+
if (!state.chart || !paneId) return;
|
|
879
|
+
const savedHeight = paneHeightsRef.current[paneId] ?? 100;
|
|
880
|
+
collapsedPanesRef.current.delete(paneId);
|
|
881
|
+
state.chart.setPaneOptions?.({
|
|
882
|
+
id: paneId,
|
|
883
|
+
height: savedHeight
|
|
884
|
+
});
|
|
885
|
+
state.chart.overrideIndicator({ name, id: `sub_${name}`, visible: true });
|
|
886
|
+
},
|
|
887
|
+
[state.chart, state.subIndicators]
|
|
888
|
+
);
|
|
889
|
+
const isSubIndicatorCollapsed = react.useCallback(
|
|
890
|
+
(name) => {
|
|
891
|
+
const paneId = state.subIndicators[name];
|
|
892
|
+
return paneId ? collapsedPanesRef.current.has(paneId) : false;
|
|
893
|
+
},
|
|
894
|
+
[state.subIndicators]
|
|
895
|
+
);
|
|
896
|
+
const reorderSubIndicator = react.useCallback(
|
|
897
|
+
(name, direction) => {
|
|
898
|
+
if (!state.chart) return;
|
|
899
|
+
const subNames = Object.keys(state.subIndicators);
|
|
900
|
+
const idx = subNames.indexOf(name);
|
|
901
|
+
if (idx === -1) return;
|
|
902
|
+
const swapIdx = direction === "up" ? idx - 1 : idx + 1;
|
|
903
|
+
if (swapIdx < 0 || swapIdx >= subNames.length) return;
|
|
904
|
+
const subStates = subNames.map((n) => {
|
|
905
|
+
const id = `sub_${n}`;
|
|
906
|
+
const indicator = state.chart.getIndicators({ id })?.[0];
|
|
907
|
+
return {
|
|
908
|
+
name: n,
|
|
909
|
+
calcParams: indicator?.calcParams,
|
|
910
|
+
visible: indicator?.visible ?? true,
|
|
911
|
+
styles: indicator?.styles,
|
|
912
|
+
paneId: state.subIndicators[n]
|
|
913
|
+
};
|
|
914
|
+
});
|
|
915
|
+
[subStates[idx], subStates[swapIdx]] = [subStates[swapIdx], subStates[idx]];
|
|
916
|
+
for (const n of subNames) {
|
|
917
|
+
state.chart.removeIndicator({ id: `sub_${n}` });
|
|
918
|
+
}
|
|
919
|
+
const newSubIndicators = {};
|
|
920
|
+
for (const sub of subStates) {
|
|
921
|
+
const id = `sub_${sub.name}`;
|
|
922
|
+
state.chart.createIndicator({
|
|
923
|
+
name: sub.name,
|
|
924
|
+
id,
|
|
925
|
+
...sub.calcParams ? { calcParams: sub.calcParams } : {},
|
|
926
|
+
visible: sub.visible
|
|
927
|
+
});
|
|
928
|
+
const paneId = state.chart.getIndicators({ id })?.[0]?.paneId ?? "";
|
|
929
|
+
newSubIndicators[sub.name] = paneId;
|
|
930
|
+
if (sub.styles) {
|
|
931
|
+
state.chart.overrideIndicator({
|
|
932
|
+
name: sub.name,
|
|
933
|
+
id,
|
|
934
|
+
styles: sub.styles
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
dispatch({ type: "SET_SUB_INDICATORS", indicators: newSubIndicators });
|
|
939
|
+
},
|
|
940
|
+
[state.chart, state.subIndicators, dispatch]
|
|
941
|
+
);
|
|
942
|
+
const getIndicatorAxis = react.useCallback(
|
|
943
|
+
(name, isMain) => state.indicatorAxes[isMain ? `main_${name}` : `sub_${name}`],
|
|
944
|
+
[state.indicatorAxes]
|
|
945
|
+
);
|
|
946
|
+
const bindIndicatorToNewAxis = react.useCallback(
|
|
947
|
+
(name, isMain, yAxis) => {
|
|
948
|
+
if (!state.chart) return;
|
|
949
|
+
const id = isMain ? `main_${name}` : `sub_${name}`;
|
|
950
|
+
const current = state.chart.getIndicators({ id })?.[0];
|
|
951
|
+
if (!current) return;
|
|
952
|
+
const calcParams = current.calcParams;
|
|
953
|
+
const styles = current.styles;
|
|
954
|
+
const visible = current.visible ?? true;
|
|
955
|
+
const paneId = isMain ? "candle_pane" : current.paneId ?? state.subIndicators[name] ?? "";
|
|
956
|
+
state.chart.removeIndicator({ id });
|
|
957
|
+
state.chart.createIndicator(
|
|
958
|
+
{
|
|
959
|
+
name,
|
|
960
|
+
id,
|
|
961
|
+
...calcParams ? { calcParams } : {},
|
|
962
|
+
visible
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
isStack: isMain,
|
|
966
|
+
...paneId ? { pane: { id: paneId } } : {},
|
|
967
|
+
yAxis
|
|
968
|
+
}
|
|
969
|
+
);
|
|
970
|
+
if (styles) {
|
|
971
|
+
state.chart.overrideIndicator({ name, id, styles });
|
|
972
|
+
}
|
|
973
|
+
const nextAxes = { ...state.indicatorAxes };
|
|
974
|
+
if (yAxis?.id) nextAxes[id] = yAxis.id;
|
|
975
|
+
else delete nextAxes[id];
|
|
976
|
+
dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
|
|
977
|
+
},
|
|
978
|
+
[state.chart, state.indicatorAxes, state.subIndicators, dispatch]
|
|
979
|
+
);
|
|
819
980
|
return {
|
|
820
981
|
mainIndicators,
|
|
821
982
|
subIndicators,
|
|
@@ -835,7 +996,14 @@ function useIndicators() {
|
|
|
835
996
|
updateIndicatorParams,
|
|
836
997
|
getIndicatorParams,
|
|
837
998
|
isMainIndicatorActive,
|
|
838
|
-
isSubIndicatorActive
|
|
999
|
+
isSubIndicatorActive,
|
|
1000
|
+
collapseSubIndicator,
|
|
1001
|
+
expandSubIndicator,
|
|
1002
|
+
isSubIndicatorCollapsed,
|
|
1003
|
+
reorderSubIndicator,
|
|
1004
|
+
indicatorAxes: state.indicatorAxes,
|
|
1005
|
+
getIndicatorAxis,
|
|
1006
|
+
bindIndicatorToNewAxis
|
|
839
1007
|
};
|
|
840
1008
|
}
|
|
841
1009
|
|
|
@@ -934,6 +1102,17 @@ function useDrawingTools() {
|
|
|
934
1102
|
const [magnetMode, setMagnetModeState] = react.useState("normal");
|
|
935
1103
|
const [isLocked, setIsLocked] = react.useState(false);
|
|
936
1104
|
const [isVisible, setIsVisible] = react.useState(true);
|
|
1105
|
+
const [autoRetrigger, setAutoRetrigger] = react.useState(true);
|
|
1106
|
+
const activeToolRef = react.useRef(activeTool);
|
|
1107
|
+
activeToolRef.current = activeTool;
|
|
1108
|
+
const autoRetriggerRef = react.useRef(autoRetrigger);
|
|
1109
|
+
autoRetriggerRef.current = autoRetrigger;
|
|
1110
|
+
const isLockedRef = react.useRef(isLocked);
|
|
1111
|
+
isLockedRef.current = isLocked;
|
|
1112
|
+
const isVisibleRef = react.useRef(isVisible);
|
|
1113
|
+
isVisibleRef.current = isVisible;
|
|
1114
|
+
const magnetModeRef = react.useRef(magnetMode);
|
|
1115
|
+
magnetModeRef.current = magnetMode;
|
|
937
1116
|
const categories = react.useMemo(
|
|
938
1117
|
() => DRAWING_CATEGORIES.map((cat) => ({
|
|
939
1118
|
key: cat.key,
|
|
@@ -944,15 +1123,15 @@ function useDrawingTools() {
|
|
|
944
1123
|
})),
|
|
945
1124
|
[]
|
|
946
1125
|
);
|
|
947
|
-
const
|
|
1126
|
+
const createOverlayForTool = react.useCallback(
|
|
948
1127
|
(name) => {
|
|
949
|
-
|
|
1128
|
+
const mode = magnetModeRef.current === "strong" ? "strong_magnet" : magnetModeRef.current === "weak" ? "weak_magnet" : "normal";
|
|
950
1129
|
state.chart?.createOverlay({
|
|
951
1130
|
name,
|
|
952
1131
|
groupId: DRAWING_GROUP_ID,
|
|
953
|
-
lock:
|
|
954
|
-
visible:
|
|
955
|
-
mode
|
|
1132
|
+
lock: isLockedRef.current,
|
|
1133
|
+
visible: isVisibleRef.current,
|
|
1134
|
+
mode,
|
|
956
1135
|
onDrawEnd: (event) => {
|
|
957
1136
|
const o = event.overlay;
|
|
958
1137
|
undoRedoListenerRef.current?.({
|
|
@@ -967,10 +1146,22 @@ function useDrawingTools() {
|
|
|
967
1146
|
}
|
|
968
1147
|
}
|
|
969
1148
|
});
|
|
1149
|
+
if (autoRetriggerRef.current && activeToolRef.current === name) {
|
|
1150
|
+
requestAnimationFrame(() => {
|
|
1151
|
+
createOverlayForTool(name);
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
970
1154
|
}
|
|
971
1155
|
});
|
|
972
1156
|
},
|
|
973
|
-
[state.chart,
|
|
1157
|
+
[state.chart, undoRedoListenerRef]
|
|
1158
|
+
);
|
|
1159
|
+
const selectTool = react.useCallback(
|
|
1160
|
+
(name) => {
|
|
1161
|
+
setActiveTool(name);
|
|
1162
|
+
createOverlayForTool(name);
|
|
1163
|
+
},
|
|
1164
|
+
[createOverlayForTool]
|
|
974
1165
|
);
|
|
975
1166
|
const clearActiveTool = react.useCallback(() => {
|
|
976
1167
|
setActiveTool(null);
|
|
@@ -1041,12 +1232,14 @@ function useDrawingTools() {
|
|
|
1041
1232
|
magnetMode,
|
|
1042
1233
|
isLocked,
|
|
1043
1234
|
isVisible,
|
|
1235
|
+
autoRetrigger,
|
|
1044
1236
|
selectTool,
|
|
1045
1237
|
clearActiveTool,
|
|
1046
1238
|
setMagnetMode,
|
|
1047
1239
|
toggleLock,
|
|
1048
1240
|
toggleVisibility,
|
|
1049
|
-
removeAllDrawings
|
|
1241
|
+
removeAllDrawings,
|
|
1242
|
+
setAutoRetrigger
|
|
1050
1243
|
};
|
|
1051
1244
|
}
|
|
1052
1245
|
|
|
@@ -1120,20 +1313,23 @@ function useKlinechartsUISettings() {
|
|
|
1120
1313
|
() => TOOLTIP_SHOW_RULES.map((r) => ({ key: r, localeKey: r })),
|
|
1121
1314
|
[]
|
|
1122
1315
|
);
|
|
1316
|
+
const applyPaneAxis = react.useCallback(
|
|
1317
|
+
(axis) => {
|
|
1318
|
+
state.chart?.overrideYAxis?.({ paneId: "candle_pane", ...axis });
|
|
1319
|
+
},
|
|
1320
|
+
[state.chart]
|
|
1321
|
+
);
|
|
1123
1322
|
const hasAppliedInitial = react.useRef(false);
|
|
1124
1323
|
react.useEffect(() => {
|
|
1125
1324
|
if (!state.chart || hasAppliedInitial.current) return;
|
|
1126
1325
|
hasAppliedInitial.current = true;
|
|
1127
|
-
const
|
|
1128
|
-
if (
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
...settings.yAxisPosition !== "right" && { position: settings.yAxisPosition },
|
|
1135
|
-
...settings.yAxisInside && { inside: true }
|
|
1136
|
-
}
|
|
1326
|
+
const needsAxisOverride = settings.reverseCoordinate || settings.priceAxisType !== "normal" || settings.yAxisPosition !== "right" || settings.yAxisInside;
|
|
1327
|
+
if (needsAxisOverride) {
|
|
1328
|
+
applyPaneAxis({
|
|
1329
|
+
...settings.priceAxisType !== "normal" && { name: settings.priceAxisType },
|
|
1330
|
+
...settings.reverseCoordinate && { reverse: true },
|
|
1331
|
+
...settings.yAxisPosition !== "right" && { position: settings.yAxisPosition },
|
|
1332
|
+
...settings.yAxisInside && { inside: true }
|
|
1137
1333
|
});
|
|
1138
1334
|
}
|
|
1139
1335
|
if (settings.showIndicatorLastValue) {
|
|
@@ -1156,12 +1352,6 @@ function useKlinechartsUISettings() {
|
|
|
1156
1352
|
},
|
|
1157
1353
|
[state.chart]
|
|
1158
1354
|
);
|
|
1159
|
-
const applyPaneAxis = react.useCallback(
|
|
1160
|
-
(axis) => {
|
|
1161
|
-
state.chart?.setPaneOptions({ id: "candle_pane", axis });
|
|
1162
|
-
},
|
|
1163
|
-
[state.chart]
|
|
1164
|
-
);
|
|
1165
1355
|
const setCandleType = react.useCallback(
|
|
1166
1356
|
(type) => {
|
|
1167
1357
|
applyStyle("candle.type", type);
|
|
@@ -1309,11 +1499,8 @@ function useKlinechartsUISettings() {
|
|
|
1309
1499
|
const resetToDefaults = react.useCallback(() => {
|
|
1310
1500
|
setSettings(defaultSettings);
|
|
1311
1501
|
state.chart?.setStyles(state.theme);
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
axis: { name: "normal", reverse: false, position: "right", inside: false }
|
|
1315
|
-
});
|
|
1316
|
-
}, [state.chart, state.theme]);
|
|
1502
|
+
applyPaneAxis({ name: "normal", reverse: false, position: "right", inside: false });
|
|
1503
|
+
}, [state.chart, state.theme, applyPaneAxis]);
|
|
1317
1504
|
return {
|
|
1318
1505
|
...settings,
|
|
1319
1506
|
candleTypes,
|
|
@@ -1570,21 +1757,28 @@ function useUndoRedo() {
|
|
|
1570
1757
|
break;
|
|
1571
1758
|
}
|
|
1572
1759
|
case "indicator_toggled": {
|
|
1573
|
-
const { name, wasActive, isMain, paneId } = action.data;
|
|
1760
|
+
const { name, wasActive, isMain, paneId, yAxisId } = action.data;
|
|
1761
|
+
const id = isMain ? `main_${name}` : `sub_${name}`;
|
|
1574
1762
|
if (wasActive) {
|
|
1575
1763
|
if (isMain) {
|
|
1576
1764
|
state.chart.createIndicator(
|
|
1577
|
-
{ name, id
|
|
1578
|
-
|
|
1579
|
-
|
|
1765
|
+
{ name, id },
|
|
1766
|
+
{
|
|
1767
|
+
isStack: true,
|
|
1768
|
+
pane: { id: "candle_pane" },
|
|
1769
|
+
...yAxisId ? { yAxis: { id: yAxisId } } : {}
|
|
1770
|
+
}
|
|
1580
1771
|
);
|
|
1581
1772
|
dispatch({
|
|
1582
1773
|
type: "SET_MAIN_INDICATORS",
|
|
1583
1774
|
indicators: [...state.mainIndicators, name]
|
|
1584
1775
|
});
|
|
1585
1776
|
} else {
|
|
1586
|
-
state.chart.createIndicator(
|
|
1587
|
-
|
|
1777
|
+
state.chart.createIndicator(
|
|
1778
|
+
{ name, id },
|
|
1779
|
+
yAxisId ? { yAxis: { id: yAxisId } } : void 0
|
|
1780
|
+
);
|
|
1781
|
+
const newPaneId = state.chart.getIndicators({ id })?.[0]?.paneId ?? "";
|
|
1588
1782
|
dispatch({
|
|
1589
1783
|
type: "SET_SUB_INDICATORS",
|
|
1590
1784
|
indicators: {
|
|
@@ -1593,9 +1787,15 @@ function useUndoRedo() {
|
|
|
1593
1787
|
}
|
|
1594
1788
|
});
|
|
1595
1789
|
}
|
|
1790
|
+
if (yAxisId) {
|
|
1791
|
+
dispatch({
|
|
1792
|
+
type: "SET_INDICATOR_AXES",
|
|
1793
|
+
axes: { ...state.indicatorAxes, [id]: yAxisId }
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1596
1796
|
} else {
|
|
1797
|
+
state.chart.removeIndicator({ id });
|
|
1597
1798
|
if (isMain) {
|
|
1598
|
-
state.chart.removeIndicator({ id: `main_${name}` });
|
|
1599
1799
|
dispatch({
|
|
1600
1800
|
type: "SET_MAIN_INDICATORS",
|
|
1601
1801
|
indicators: state.mainIndicators.filter(
|
|
@@ -1603,7 +1803,6 @@ function useUndoRedo() {
|
|
|
1603
1803
|
)
|
|
1604
1804
|
});
|
|
1605
1805
|
} else {
|
|
1606
|
-
state.chart.removeIndicator({ id: `sub_${name}` });
|
|
1607
1806
|
const newSub = { ...state.subIndicators };
|
|
1608
1807
|
delete newSub[name];
|
|
1609
1808
|
dispatch({
|
|
@@ -1611,6 +1810,11 @@ function useUndoRedo() {
|
|
|
1611
1810
|
indicators: newSub
|
|
1612
1811
|
});
|
|
1613
1812
|
}
|
|
1813
|
+
if (state.indicatorAxes[id]) {
|
|
1814
|
+
const nextAxes = { ...state.indicatorAxes };
|
|
1815
|
+
delete nextAxes[id];
|
|
1816
|
+
dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
|
|
1817
|
+
}
|
|
1614
1818
|
}
|
|
1615
1819
|
setRedoStack((prev) => [
|
|
1616
1820
|
...prev,
|
|
@@ -1620,7 +1824,8 @@ function useUndoRedo() {
|
|
|
1620
1824
|
name,
|
|
1621
1825
|
wasActive: !wasActive,
|
|
1622
1826
|
isMain,
|
|
1623
|
-
paneId
|
|
1827
|
+
paneId,
|
|
1828
|
+
yAxisId
|
|
1624
1829
|
}
|
|
1625
1830
|
}
|
|
1626
1831
|
]);
|
|
@@ -1630,7 +1835,7 @@ function useUndoRedo() {
|
|
|
1630
1835
|
} finally {
|
|
1631
1836
|
isProcessingRef.current = false;
|
|
1632
1837
|
}
|
|
1633
|
-
}, [undoStack, state.chart, state.mainIndicators, state.subIndicators, dispatch]);
|
|
1838
|
+
}, [undoStack, state.chart, state.mainIndicators, state.subIndicators, state.indicatorAxes, dispatch]);
|
|
1634
1839
|
const redo = react.useCallback(() => {
|
|
1635
1840
|
if (redoStack.length === 0 || !state.chart || isProcessingRef.current)
|
|
1636
1841
|
return;
|
|
@@ -1682,21 +1887,28 @@ function useUndoRedo() {
|
|
|
1682
1887
|
break;
|
|
1683
1888
|
}
|
|
1684
1889
|
case "indicator_toggled": {
|
|
1685
|
-
const { name, wasActive, isMain } = action.data;
|
|
1890
|
+
const { name, wasActive, isMain, paneId, yAxisId } = action.data;
|
|
1891
|
+
const id = isMain ? `main_${name}` : `sub_${name}`;
|
|
1686
1892
|
if (wasActive) {
|
|
1687
1893
|
if (isMain) {
|
|
1688
1894
|
state.chart.createIndicator(
|
|
1689
|
-
{ name, id
|
|
1690
|
-
|
|
1691
|
-
|
|
1895
|
+
{ name, id },
|
|
1896
|
+
{
|
|
1897
|
+
isStack: true,
|
|
1898
|
+
pane: { id: "candle_pane" },
|
|
1899
|
+
...yAxisId ? { yAxis: { id: yAxisId } } : {}
|
|
1900
|
+
}
|
|
1692
1901
|
);
|
|
1693
1902
|
dispatch({
|
|
1694
1903
|
type: "SET_MAIN_INDICATORS",
|
|
1695
1904
|
indicators: [...state.mainIndicators, name]
|
|
1696
1905
|
});
|
|
1697
1906
|
} else {
|
|
1698
|
-
state.chart.createIndicator(
|
|
1699
|
-
|
|
1907
|
+
state.chart.createIndicator(
|
|
1908
|
+
{ name, id },
|
|
1909
|
+
yAxisId ? { yAxis: { id: yAxisId } } : void 0
|
|
1910
|
+
);
|
|
1911
|
+
const newPaneId = state.chart.getIndicators({ id })?.[0]?.paneId ?? "";
|
|
1700
1912
|
dispatch({
|
|
1701
1913
|
type: "SET_SUB_INDICATORS",
|
|
1702
1914
|
indicators: {
|
|
@@ -1705,9 +1917,15 @@ function useUndoRedo() {
|
|
|
1705
1917
|
}
|
|
1706
1918
|
});
|
|
1707
1919
|
}
|
|
1920
|
+
if (yAxisId) {
|
|
1921
|
+
dispatch({
|
|
1922
|
+
type: "SET_INDICATOR_AXES",
|
|
1923
|
+
axes: { ...state.indicatorAxes, [id]: yAxisId }
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1708
1926
|
} else {
|
|
1927
|
+
state.chart.removeIndicator({ id });
|
|
1709
1928
|
if (isMain) {
|
|
1710
|
-
state.chart.removeIndicator({ id: `main_${name}` });
|
|
1711
1929
|
dispatch({
|
|
1712
1930
|
type: "SET_MAIN_INDICATORS",
|
|
1713
1931
|
indicators: state.mainIndicators.filter(
|
|
@@ -1715,7 +1933,6 @@ function useUndoRedo() {
|
|
|
1715
1933
|
)
|
|
1716
1934
|
});
|
|
1717
1935
|
} else {
|
|
1718
|
-
state.chart.removeIndicator({ id: `sub_${name}` });
|
|
1719
1936
|
const newSub = { ...state.subIndicators };
|
|
1720
1937
|
delete newSub[name];
|
|
1721
1938
|
dispatch({
|
|
@@ -1723,6 +1940,11 @@ function useUndoRedo() {
|
|
|
1723
1940
|
indicators: newSub
|
|
1724
1941
|
});
|
|
1725
1942
|
}
|
|
1943
|
+
if (state.indicatorAxes[id]) {
|
|
1944
|
+
const nextAxes = { ...state.indicatorAxes };
|
|
1945
|
+
delete nextAxes[id];
|
|
1946
|
+
dispatch({ type: "SET_INDICATOR_AXES", axes: nextAxes });
|
|
1947
|
+
}
|
|
1726
1948
|
}
|
|
1727
1949
|
setUndoStack((prev) => [
|
|
1728
1950
|
...prev,
|
|
@@ -1731,7 +1953,9 @@ function useUndoRedo() {
|
|
|
1731
1953
|
data: {
|
|
1732
1954
|
name,
|
|
1733
1955
|
wasActive: !wasActive,
|
|
1734
|
-
isMain
|
|
1956
|
+
isMain,
|
|
1957
|
+
paneId,
|
|
1958
|
+
yAxisId
|
|
1735
1959
|
}
|
|
1736
1960
|
}
|
|
1737
1961
|
]);
|
|
@@ -1741,7 +1965,7 @@ function useUndoRedo() {
|
|
|
1741
1965
|
} finally {
|
|
1742
1966
|
isProcessingRef.current = false;
|
|
1743
1967
|
}
|
|
1744
|
-
}, [redoStack, state.chart, state.mainIndicators, state.subIndicators, dispatch]);
|
|
1968
|
+
}, [redoStack, state.chart, state.mainIndicators, state.subIndicators, state.indicatorAxes, dispatch]);
|
|
1745
1969
|
react.useEffect(() => {
|
|
1746
1970
|
const handleKeyDown = (e) => {
|
|
1747
1971
|
const isCtrlOrMeta = e.ctrlKey || e.metaKey;
|
|
@@ -1815,12 +2039,14 @@ function useLayoutManager() {
|
|
|
1815
2039
|
if (indicatorMap) {
|
|
1816
2040
|
indicatorMap.forEach((paneIndicators, paneId) => {
|
|
1817
2041
|
paneIndicators.forEach((indicator) => {
|
|
2042
|
+
const yAxisId = state.indicatorAxes[indicator.id];
|
|
1818
2043
|
indicators2.push({
|
|
1819
2044
|
paneId,
|
|
1820
2045
|
name: indicator.name,
|
|
1821
2046
|
calcParams: indicator.calcParams,
|
|
1822
2047
|
visible: indicator.visible,
|
|
1823
|
-
...indicator.styles ? { styles: indicator.styles } : {}
|
|
2048
|
+
...indicator.styles ? { styles: indicator.styles } : {},
|
|
2049
|
+
...yAxisId ? { yAxisId } : {}
|
|
1824
2050
|
});
|
|
1825
2051
|
});
|
|
1826
2052
|
});
|
|
@@ -1848,7 +2074,7 @@ function useLayoutManager() {
|
|
|
1848
2074
|
indicators: indicators2,
|
|
1849
2075
|
drawings
|
|
1850
2076
|
};
|
|
1851
|
-
}, [state.chart, state.symbol, state.period]);
|
|
2077
|
+
}, [state.chart, state.symbol, state.period, state.indicatorAxes]);
|
|
1852
2078
|
const saveLayout = react.useCallback(
|
|
1853
2079
|
(name) => {
|
|
1854
2080
|
const chartState = serializeState();
|
|
@@ -1896,25 +2122,35 @@ function useLayoutManager() {
|
|
|
1896
2122
|
}
|
|
1897
2123
|
const newMainIndicators = [];
|
|
1898
2124
|
const newSubIndicators = {};
|
|
2125
|
+
const restoredAxes = {};
|
|
1899
2126
|
if (chartState.indicators) {
|
|
1900
2127
|
for (const ind of chartState.indicators) {
|
|
2128
|
+
const isMain = ind.paneId === "candle_pane";
|
|
2129
|
+
const id2 = isMain ? `main_${ind.name}` : `sub_${ind.name}`;
|
|
1901
2130
|
chart.createIndicator(
|
|
1902
2131
|
{
|
|
1903
2132
|
name: ind.name,
|
|
2133
|
+
id: id2,
|
|
1904
2134
|
calcParams: ind.calcParams,
|
|
1905
2135
|
visible: ind.visible
|
|
1906
2136
|
},
|
|
1907
|
-
|
|
1908
|
-
|
|
2137
|
+
{
|
|
2138
|
+
isStack: ind.paneId !== "candle_pane",
|
|
2139
|
+
pane: { id: ind.paneId },
|
|
2140
|
+
...ind.yAxisId ? { yAxis: { id: ind.yAxisId } } : {}
|
|
2141
|
+
}
|
|
1909
2142
|
);
|
|
1910
2143
|
if (ind.styles) {
|
|
1911
2144
|
chart.overrideIndicator({
|
|
1912
2145
|
name: ind.name,
|
|
1913
|
-
|
|
2146
|
+
id: id2,
|
|
1914
2147
|
styles: ind.styles
|
|
1915
2148
|
});
|
|
1916
2149
|
}
|
|
1917
|
-
if (ind.
|
|
2150
|
+
if (ind.yAxisId) {
|
|
2151
|
+
restoredAxes[id2] = ind.yAxisId;
|
|
2152
|
+
}
|
|
2153
|
+
if (isMain) {
|
|
1918
2154
|
newMainIndicators.push(ind.name);
|
|
1919
2155
|
} else {
|
|
1920
2156
|
newSubIndicators[ind.name] = ind.paneId;
|
|
@@ -1929,6 +2165,10 @@ function useLayoutManager() {
|
|
|
1929
2165
|
type: "SET_SUB_INDICATORS",
|
|
1930
2166
|
indicators: newSubIndicators
|
|
1931
2167
|
});
|
|
2168
|
+
dispatch({
|
|
2169
|
+
type: "SET_INDICATOR_AXES",
|
|
2170
|
+
axes: restoredAxes
|
|
2171
|
+
});
|
|
1932
2172
|
if (chartState.drawings) {
|
|
1933
2173
|
for (const drawing of chartState.drawings) {
|
|
1934
2174
|
chart.createOverlay({
|
|
@@ -2112,7 +2352,7 @@ ${code}`
|
|
|
2112
2352
|
];
|
|
2113
2353
|
const fn = new Function(...allArgs);
|
|
2114
2354
|
const shadowValues = SHADOW_KEYS.map(() => void 0);
|
|
2115
|
-
const result = fn(...shadowValues,
|
|
2355
|
+
const result = fn(...shadowValues, chunkPIFLJKYF_cjs.TA_default, dataList, parsedParams);
|
|
2116
2356
|
if (!Array.isArray(result)) {
|
|
2117
2357
|
throw new Error("Script must return an Array.");
|
|
2118
2358
|
}
|
|
@@ -2154,8 +2394,7 @@ ${code}`
|
|
|
2154
2394
|
if (placement === "main") {
|
|
2155
2395
|
paneId = chart.createIndicator(
|
|
2156
2396
|
{ name: indicatorName },
|
|
2157
|
-
true,
|
|
2158
|
-
{ id: "candle_pane" }
|
|
2397
|
+
{ isStack: true, pane: { id: "candle_pane" } }
|
|
2159
2398
|
);
|
|
2160
2399
|
} else {
|
|
2161
2400
|
paneId = chart.createIndicator({ name: indicatorName });
|
|
@@ -2245,6 +2484,749 @@ ${code}`
|
|
|
2245
2484
|
defaultScript: DEFAULT_SCRIPT
|
|
2246
2485
|
};
|
|
2247
2486
|
}
|
|
2487
|
+
function useCrosshair() {
|
|
2488
|
+
const { state } = useKlinechartsUI();
|
|
2489
|
+
const [barData, setBarData] = react.useState(null);
|
|
2490
|
+
const rafRef = react.useRef(0);
|
|
2491
|
+
const handler = react.useCallback(
|
|
2492
|
+
(event) => {
|
|
2493
|
+
if (rafRef.current) {
|
|
2494
|
+
cancelAnimationFrame(rafRef.current);
|
|
2495
|
+
}
|
|
2496
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
2497
|
+
rafRef.current = 0;
|
|
2498
|
+
const klineData = event?.kLineData;
|
|
2499
|
+
if (!klineData) {
|
|
2500
|
+
setBarData(null);
|
|
2501
|
+
return;
|
|
2502
|
+
}
|
|
2503
|
+
const pricePrecision = state.symbol?.pricePrecision ?? 2;
|
|
2504
|
+
const open = klineData.open ?? 0;
|
|
2505
|
+
const close = klineData.close ?? 0;
|
|
2506
|
+
const change = parseFloat((close - open).toFixed(pricePrecision));
|
|
2507
|
+
const changePercent = open !== 0 ? parseFloat((change / open * 100).toFixed(2)) : 0;
|
|
2508
|
+
setBarData({
|
|
2509
|
+
open,
|
|
2510
|
+
high: klineData.high ?? 0,
|
|
2511
|
+
low: klineData.low ?? 0,
|
|
2512
|
+
close,
|
|
2513
|
+
volume: klineData.volume ?? 0,
|
|
2514
|
+
timestamp: klineData.timestamp ?? 0,
|
|
2515
|
+
change,
|
|
2516
|
+
changePercent
|
|
2517
|
+
});
|
|
2518
|
+
});
|
|
2519
|
+
},
|
|
2520
|
+
[state.symbol?.pricePrecision]
|
|
2521
|
+
);
|
|
2522
|
+
react.useEffect(() => {
|
|
2523
|
+
const chart = state.chart;
|
|
2524
|
+
if (!chart) return;
|
|
2525
|
+
chart.subscribeAction?.("onCrosshairChange", handler);
|
|
2526
|
+
return () => {
|
|
2527
|
+
if (rafRef.current) {
|
|
2528
|
+
cancelAnimationFrame(rafRef.current);
|
|
2529
|
+
rafRef.current = 0;
|
|
2530
|
+
}
|
|
2531
|
+
chart.unsubscribeAction?.("onCrosshairChange", handler);
|
|
2532
|
+
};
|
|
2533
|
+
}, [state.chart, handler]);
|
|
2534
|
+
return { barData };
|
|
2535
|
+
}
|
|
2536
|
+
var alertCounter = 0;
|
|
2537
|
+
function useAlerts() {
|
|
2538
|
+
const { state } = useKlinechartsUI();
|
|
2539
|
+
const [alerts, setAlerts] = react.useState([]);
|
|
2540
|
+
const callbackRef = react.useRef(null);
|
|
2541
|
+
const prevCloseRef = react.useRef(null);
|
|
2542
|
+
const addAlert = react.useCallback(
|
|
2543
|
+
(price, condition, message) => {
|
|
2544
|
+
const id = `alert_${++alertCounter}`;
|
|
2545
|
+
const alert = {
|
|
2546
|
+
id,
|
|
2547
|
+
price,
|
|
2548
|
+
condition,
|
|
2549
|
+
message,
|
|
2550
|
+
triggered: false
|
|
2551
|
+
};
|
|
2552
|
+
setAlerts((prev) => [...prev, alert]);
|
|
2553
|
+
if (state.chart) {
|
|
2554
|
+
state.chart.createOverlay({
|
|
2555
|
+
name: "horizontalStraightLine",
|
|
2556
|
+
id,
|
|
2557
|
+
groupId: "price_alerts",
|
|
2558
|
+
points: [{ value: price }],
|
|
2559
|
+
styles: {
|
|
2560
|
+
line: {
|
|
2561
|
+
style: "dashed",
|
|
2562
|
+
color: "#ff9800",
|
|
2563
|
+
size: 1
|
|
2564
|
+
}
|
|
2565
|
+
},
|
|
2566
|
+
lock: true
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
return id;
|
|
2570
|
+
},
|
|
2571
|
+
[state.chart]
|
|
2572
|
+
);
|
|
2573
|
+
const removeAlert = react.useCallback(
|
|
2574
|
+
(id) => {
|
|
2575
|
+
setAlerts((prev) => prev.filter((a) => a.id !== id));
|
|
2576
|
+
state.chart?.removeOverlay({ id });
|
|
2577
|
+
},
|
|
2578
|
+
[state.chart]
|
|
2579
|
+
);
|
|
2580
|
+
const clearAlerts = react.useCallback(() => {
|
|
2581
|
+
setAlerts((prev) => {
|
|
2582
|
+
for (const alert of prev) {
|
|
2583
|
+
state.chart?.removeOverlay({ id: alert.id });
|
|
2584
|
+
}
|
|
2585
|
+
return [];
|
|
2586
|
+
});
|
|
2587
|
+
}, [state.chart]);
|
|
2588
|
+
const onAlertTriggered = react.useCallback(
|
|
2589
|
+
(callback) => {
|
|
2590
|
+
callbackRef.current = callback;
|
|
2591
|
+
},
|
|
2592
|
+
[]
|
|
2593
|
+
);
|
|
2594
|
+
react.useEffect(() => {
|
|
2595
|
+
if (!state.chart) return;
|
|
2596
|
+
const interval = setInterval(() => {
|
|
2597
|
+
const dataList = state.chart?.getDataList();
|
|
2598
|
+
if (!dataList || dataList.length === 0) return;
|
|
2599
|
+
const lastBar = dataList[dataList.length - 1];
|
|
2600
|
+
const currentClose = lastBar.close;
|
|
2601
|
+
const prevClose = prevCloseRef.current;
|
|
2602
|
+
if (prevClose === null) {
|
|
2603
|
+
prevCloseRef.current = currentClose;
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
prevCloseRef.current = currentClose;
|
|
2607
|
+
setAlerts((prev) => {
|
|
2608
|
+
let changed = false;
|
|
2609
|
+
const next = prev.map((alert) => {
|
|
2610
|
+
if (alert.triggered) return alert;
|
|
2611
|
+
let shouldTrigger = false;
|
|
2612
|
+
if (alert.condition === "crossing_up") {
|
|
2613
|
+
shouldTrigger = prevClose < alert.price && currentClose >= alert.price;
|
|
2614
|
+
} else if (alert.condition === "crossing_down") {
|
|
2615
|
+
shouldTrigger = prevClose > alert.price && currentClose <= alert.price;
|
|
2616
|
+
} else if (alert.condition === "crossing") {
|
|
2617
|
+
shouldTrigger = prevClose < alert.price && currentClose >= alert.price || prevClose > alert.price && currentClose <= alert.price;
|
|
2618
|
+
}
|
|
2619
|
+
if (shouldTrigger) {
|
|
2620
|
+
changed = true;
|
|
2621
|
+
const triggered = { ...alert, triggered: true };
|
|
2622
|
+
callbackRef.current?.(triggered);
|
|
2623
|
+
return triggered;
|
|
2624
|
+
}
|
|
2625
|
+
return alert;
|
|
2626
|
+
});
|
|
2627
|
+
return changed ? next : prev;
|
|
2628
|
+
});
|
|
2629
|
+
}, 1e3);
|
|
2630
|
+
return () => clearInterval(interval);
|
|
2631
|
+
}, [state.chart]);
|
|
2632
|
+
return {
|
|
2633
|
+
alerts,
|
|
2634
|
+
addAlert,
|
|
2635
|
+
removeAlert,
|
|
2636
|
+
clearAlerts,
|
|
2637
|
+
onAlertTriggered
|
|
2638
|
+
};
|
|
2639
|
+
}
|
|
2640
|
+
function useDataExport() {
|
|
2641
|
+
const { state } = useKlinechartsUI();
|
|
2642
|
+
const exportAll = react.useCallback(
|
|
2643
|
+
(format) => {
|
|
2644
|
+
if (!state.chart) return;
|
|
2645
|
+
const dataList = state.chart.getDataList();
|
|
2646
|
+
if (!dataList || dataList.length === 0) return;
|
|
2647
|
+
downloadData(dataList, format, state.symbol?.ticker);
|
|
2648
|
+
},
|
|
2649
|
+
[state.chart, state.symbol?.ticker]
|
|
2650
|
+
);
|
|
2651
|
+
const exportVisible = react.useCallback(
|
|
2652
|
+
(format) => {
|
|
2653
|
+
if (!state.chart) return;
|
|
2654
|
+
const dataList = state.chart.getDataList();
|
|
2655
|
+
if (!dataList || dataList.length === 0) return;
|
|
2656
|
+
const visibleRange = state.chart.getVisibleRange?.();
|
|
2657
|
+
if (!visibleRange) {
|
|
2658
|
+
downloadData(dataList, format, state.symbol?.ticker);
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
const { from, to } = visibleRange;
|
|
2662
|
+
const visibleData = dataList.slice(from, to);
|
|
2663
|
+
downloadData(visibleData, format, state.symbol?.ticker);
|
|
2664
|
+
},
|
|
2665
|
+
[state.chart, state.symbol?.ticker]
|
|
2666
|
+
);
|
|
2667
|
+
return { exportAll, exportVisible };
|
|
2668
|
+
}
|
|
2669
|
+
function formatRow(bar) {
|
|
2670
|
+
return {
|
|
2671
|
+
date: new Date(bar.timestamp).toISOString(),
|
|
2672
|
+
open: bar.open,
|
|
2673
|
+
high: bar.high,
|
|
2674
|
+
low: bar.low,
|
|
2675
|
+
close: bar.close,
|
|
2676
|
+
volume: bar.volume ?? 0
|
|
2677
|
+
};
|
|
2678
|
+
}
|
|
2679
|
+
function buildCsv(dataList) {
|
|
2680
|
+
const header = "Date,Open,High,Low,Close,Volume\n";
|
|
2681
|
+
const rows = dataList.map((bar) => {
|
|
2682
|
+
const r = formatRow(bar);
|
|
2683
|
+
return `${r.date},${r.open},${r.high},${r.low},${r.close},${r.volume}`;
|
|
2684
|
+
});
|
|
2685
|
+
return header + rows.join("\n");
|
|
2686
|
+
}
|
|
2687
|
+
function buildJson(dataList) {
|
|
2688
|
+
return JSON.stringify(dataList.map(formatRow), null, 2);
|
|
2689
|
+
}
|
|
2690
|
+
function downloadData(dataList, format, ticker) {
|
|
2691
|
+
const content = format === "csv" ? buildCsv(dataList) : buildJson(dataList);
|
|
2692
|
+
const mimeType = format === "csv" ? "text/csv" : "application/json";
|
|
2693
|
+
const blob = new Blob([content], { type: mimeType });
|
|
2694
|
+
const url = URL.createObjectURL(blob);
|
|
2695
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
2696
|
+
const prefix = ticker ?? "chart";
|
|
2697
|
+
const filename = `${prefix}_${date}.${format}`;
|
|
2698
|
+
const link = document.createElement("a");
|
|
2699
|
+
link.href = url;
|
|
2700
|
+
link.download = filename;
|
|
2701
|
+
document.body.appendChild(link);
|
|
2702
|
+
link.click();
|
|
2703
|
+
document.body.removeChild(link);
|
|
2704
|
+
URL.revokeObjectURL(url);
|
|
2705
|
+
}
|
|
2706
|
+
function useWatchlist() {
|
|
2707
|
+
const { state } = useKlinechartsUI();
|
|
2708
|
+
const { dispatch, datafeed } = useKlinechartsUIDispatch();
|
|
2709
|
+
const [items, setItems] = react.useState([]);
|
|
2710
|
+
const subscriptionsRef = react.useRef(/* @__PURE__ */ new Map());
|
|
2711
|
+
const activeSymbol = state.symbol?.ticker ?? null;
|
|
2712
|
+
const addSymbol = react.useCallback(
|
|
2713
|
+
(ticker) => {
|
|
2714
|
+
setItems((prev) => {
|
|
2715
|
+
if (prev.some((item) => item.ticker === ticker)) return prev;
|
|
2716
|
+
return [
|
|
2717
|
+
...prev,
|
|
2718
|
+
{ ticker, lastPrice: null, change: null, changePercent: null }
|
|
2719
|
+
];
|
|
2720
|
+
});
|
|
2721
|
+
if (subscriptionsRef.current.has(ticker)) return;
|
|
2722
|
+
const symbolInfo = { ticker };
|
|
2723
|
+
const period = state.period;
|
|
2724
|
+
subscriptionsRef.current.set(ticker, { symbolInfo, period });
|
|
2725
|
+
datafeed.subscribe(symbolInfo, period, (bar) => {
|
|
2726
|
+
setItems(
|
|
2727
|
+
(prev) => prev.map((item) => {
|
|
2728
|
+
if (item.ticker !== ticker) return item;
|
|
2729
|
+
const change = bar.close - bar.open;
|
|
2730
|
+
const changePercent = bar.open !== 0 ? change / bar.open * 100 : null;
|
|
2731
|
+
return {
|
|
2732
|
+
...item,
|
|
2733
|
+
lastPrice: bar.close,
|
|
2734
|
+
change,
|
|
2735
|
+
changePercent
|
|
2736
|
+
};
|
|
2737
|
+
})
|
|
2738
|
+
);
|
|
2739
|
+
});
|
|
2740
|
+
},
|
|
2741
|
+
[datafeed, state.period]
|
|
2742
|
+
);
|
|
2743
|
+
const removeSymbol = react.useCallback(
|
|
2744
|
+
(ticker) => {
|
|
2745
|
+
const sub = subscriptionsRef.current.get(ticker);
|
|
2746
|
+
if (sub) {
|
|
2747
|
+
datafeed.unsubscribe(sub.symbolInfo, sub.period);
|
|
2748
|
+
subscriptionsRef.current.delete(ticker);
|
|
2749
|
+
}
|
|
2750
|
+
setItems((prev) => prev.filter((item) => item.ticker !== ticker));
|
|
2751
|
+
},
|
|
2752
|
+
[datafeed]
|
|
2753
|
+
);
|
|
2754
|
+
const switchSymbol = react.useCallback(
|
|
2755
|
+
(ticker) => {
|
|
2756
|
+
dispatch({ type: "SET_SYMBOL", symbol: { ticker } });
|
|
2757
|
+
},
|
|
2758
|
+
[dispatch]
|
|
2759
|
+
);
|
|
2760
|
+
react.useEffect(() => {
|
|
2761
|
+
return () => {
|
|
2762
|
+
subscriptionsRef.current.forEach((sub) => {
|
|
2763
|
+
datafeed.unsubscribe(sub.symbolInfo, sub.period);
|
|
2764
|
+
});
|
|
2765
|
+
subscriptionsRef.current.clear();
|
|
2766
|
+
};
|
|
2767
|
+
}, [datafeed]);
|
|
2768
|
+
return {
|
|
2769
|
+
items,
|
|
2770
|
+
addSymbol,
|
|
2771
|
+
removeSymbol,
|
|
2772
|
+
switchSymbol,
|
|
2773
|
+
activeSymbol
|
|
2774
|
+
};
|
|
2775
|
+
}
|
|
2776
|
+
function useReplay() {
|
|
2777
|
+
const { state } = useKlinechartsUI();
|
|
2778
|
+
const [isReplaying, setIsReplaying] = react.useState(false);
|
|
2779
|
+
const [isPaused, setIsPaused] = react.useState(false);
|
|
2780
|
+
const [speed, setSpeedState] = react.useState(1);
|
|
2781
|
+
const [barIndex, setBarIndex] = react.useState(0);
|
|
2782
|
+
const [totalBars, setTotalBars] = react.useState(0);
|
|
2783
|
+
const intervalRef = react.useRef(null);
|
|
2784
|
+
const savedDataRef = react.useRef([]);
|
|
2785
|
+
const currentIndexRef = react.useRef(0);
|
|
2786
|
+
const clearInterval_ = react.useCallback(() => {
|
|
2787
|
+
if (intervalRef.current !== null) {
|
|
2788
|
+
clearInterval(intervalRef.current);
|
|
2789
|
+
intervalRef.current = null;
|
|
2790
|
+
}
|
|
2791
|
+
}, []);
|
|
2792
|
+
const addNextBar = react.useCallback(() => {
|
|
2793
|
+
if (!state.chart) return;
|
|
2794
|
+
const data = savedDataRef.current;
|
|
2795
|
+
const idx = currentIndexRef.current;
|
|
2796
|
+
if (idx >= data.length) {
|
|
2797
|
+
clearInterval_();
|
|
2798
|
+
setIsPaused(true);
|
|
2799
|
+
return;
|
|
2800
|
+
}
|
|
2801
|
+
const bar = data[idx];
|
|
2802
|
+
state.chart.updateData?.(bar);
|
|
2803
|
+
currentIndexRef.current = idx + 1;
|
|
2804
|
+
setBarIndex(idx + 1);
|
|
2805
|
+
}, [state.chart, clearInterval_]);
|
|
2806
|
+
const startInterval = react.useCallback(
|
|
2807
|
+
(currentSpeed) => {
|
|
2808
|
+
clearInterval_();
|
|
2809
|
+
intervalRef.current = setInterval(addNextBar, 1e3 / currentSpeed);
|
|
2810
|
+
},
|
|
2811
|
+
[addNextBar, clearInterval_]
|
|
2812
|
+
);
|
|
2813
|
+
const startReplay = react.useCallback(() => {
|
|
2814
|
+
if (!state.chart) return;
|
|
2815
|
+
const dataList = state.chart.getDataList();
|
|
2816
|
+
if (!dataList || dataList.length === 0) return;
|
|
2817
|
+
savedDataRef.current = [...dataList];
|
|
2818
|
+
currentIndexRef.current = 0;
|
|
2819
|
+
setTotalBars(dataList.length);
|
|
2820
|
+
setBarIndex(0);
|
|
2821
|
+
setIsReplaying(true);
|
|
2822
|
+
setIsPaused(false);
|
|
2823
|
+
state.chart.clearData?.();
|
|
2824
|
+
startInterval(speed);
|
|
2825
|
+
}, [state.chart, speed, startInterval]);
|
|
2826
|
+
const stopReplay = react.useCallback(() => {
|
|
2827
|
+
if (!state.chart) return;
|
|
2828
|
+
clearInterval_();
|
|
2829
|
+
const data = savedDataRef.current;
|
|
2830
|
+
if (data.length > 0) {
|
|
2831
|
+
state.chart.clearData?.();
|
|
2832
|
+
for (const bar of data) {
|
|
2833
|
+
state.chart.updateData?.(bar);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
savedDataRef.current = [];
|
|
2837
|
+
currentIndexRef.current = 0;
|
|
2838
|
+
setIsReplaying(false);
|
|
2839
|
+
setIsPaused(false);
|
|
2840
|
+
setBarIndex(0);
|
|
2841
|
+
setTotalBars(0);
|
|
2842
|
+
}, [state.chart, clearInterval_]);
|
|
2843
|
+
const togglePause = react.useCallback(() => {
|
|
2844
|
+
if (!isReplaying) return;
|
|
2845
|
+
if (isPaused) {
|
|
2846
|
+
startInterval(speed);
|
|
2847
|
+
setIsPaused(false);
|
|
2848
|
+
} else {
|
|
2849
|
+
clearInterval_();
|
|
2850
|
+
setIsPaused(true);
|
|
2851
|
+
}
|
|
2852
|
+
}, [isReplaying, isPaused, speed, startInterval, clearInterval_]);
|
|
2853
|
+
const stepForward = react.useCallback(() => {
|
|
2854
|
+
if (!isReplaying || !isPaused) return;
|
|
2855
|
+
addNextBar();
|
|
2856
|
+
}, [isReplaying, isPaused, addNextBar]);
|
|
2857
|
+
const stepBackward = react.useCallback(() => {
|
|
2858
|
+
if (!isReplaying || !isPaused || !state.chart) return;
|
|
2859
|
+
const idx = currentIndexRef.current;
|
|
2860
|
+
if (idx <= 1) return;
|
|
2861
|
+
const data = savedDataRef.current;
|
|
2862
|
+
state.chart.clearData?.();
|
|
2863
|
+
for (let i = 0; i < idx - 1; i++) {
|
|
2864
|
+
state.chart.updateData?.(data[i]);
|
|
2865
|
+
}
|
|
2866
|
+
currentIndexRef.current = idx - 1;
|
|
2867
|
+
setBarIndex(idx - 1);
|
|
2868
|
+
}, [isReplaying, isPaused, state.chart]);
|
|
2869
|
+
const seekTo = react.useCallback(
|
|
2870
|
+
(targetIndex) => {
|
|
2871
|
+
if (!isReplaying || !state.chart) return;
|
|
2872
|
+
const data = savedDataRef.current;
|
|
2873
|
+
const clamped = Math.max(0, Math.min(targetIndex, data.length));
|
|
2874
|
+
clearInterval_();
|
|
2875
|
+
setIsPaused(true);
|
|
2876
|
+
state.chart.clearData?.();
|
|
2877
|
+
for (let i = 0; i < clamped; i++) {
|
|
2878
|
+
state.chart.updateData?.(data[i]);
|
|
2879
|
+
}
|
|
2880
|
+
currentIndexRef.current = clamped;
|
|
2881
|
+
setBarIndex(clamped);
|
|
2882
|
+
},
|
|
2883
|
+
[isReplaying, state.chart, clearInterval_]
|
|
2884
|
+
);
|
|
2885
|
+
const setSpeed = react.useCallback(
|
|
2886
|
+
(newSpeed) => {
|
|
2887
|
+
setSpeedState(newSpeed);
|
|
2888
|
+
if (isReplaying && !isPaused) {
|
|
2889
|
+
startInterval(newSpeed);
|
|
2890
|
+
}
|
|
2891
|
+
},
|
|
2892
|
+
[isReplaying, isPaused, startInterval]
|
|
2893
|
+
);
|
|
2894
|
+
react.useEffect(() => {
|
|
2895
|
+
return () => {
|
|
2896
|
+
clearInterval_();
|
|
2897
|
+
};
|
|
2898
|
+
}, [clearInterval_]);
|
|
2899
|
+
return {
|
|
2900
|
+
isReplaying,
|
|
2901
|
+
isPaused,
|
|
2902
|
+
speed,
|
|
2903
|
+
barIndex,
|
|
2904
|
+
totalBars,
|
|
2905
|
+
startReplay,
|
|
2906
|
+
stopReplay,
|
|
2907
|
+
togglePause,
|
|
2908
|
+
stepForward,
|
|
2909
|
+
stepBackward,
|
|
2910
|
+
seekTo,
|
|
2911
|
+
setSpeed
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2914
|
+
var DEFAULT_COLORS = [
|
|
2915
|
+
"#2196f3",
|
|
2916
|
+
"#ff9800",
|
|
2917
|
+
"#4caf50",
|
|
2918
|
+
"#e91e63",
|
|
2919
|
+
"#9c27b0",
|
|
2920
|
+
"#00bcd4",
|
|
2921
|
+
"#ff5722",
|
|
2922
|
+
"#8bc34a"
|
|
2923
|
+
];
|
|
2924
|
+
var colorIndex = 0;
|
|
2925
|
+
var compareCounter = 0;
|
|
2926
|
+
function useCompare() {
|
|
2927
|
+
const { state, datafeed } = useKlinechartsUI();
|
|
2928
|
+
const [symbols, setSymbols] = react.useState([]);
|
|
2929
|
+
const indicatorsRef = react.useRef(/* @__PURE__ */ new Map());
|
|
2930
|
+
const addSymbol = react.useCallback(
|
|
2931
|
+
async (ticker, color) => {
|
|
2932
|
+
if (!state.chart || !datafeed) return;
|
|
2933
|
+
if (indicatorsRef.current.has(ticker)) return;
|
|
2934
|
+
const assignedColor = color ?? DEFAULT_COLORS[colorIndex++ % DEFAULT_COLORS.length];
|
|
2935
|
+
const mainDataList = state.chart.getDataList();
|
|
2936
|
+
if (!mainDataList || mainDataList.length === 0) return;
|
|
2937
|
+
const from = mainDataList[0].timestamp;
|
|
2938
|
+
const to = mainDataList[mainDataList.length - 1].timestamp;
|
|
2939
|
+
const compareData = await datafeed.getHistoryKLineData(
|
|
2940
|
+
{ ticker },
|
|
2941
|
+
{ ...state.period, label: "" },
|
|
2942
|
+
from,
|
|
2943
|
+
to
|
|
2944
|
+
);
|
|
2945
|
+
if (!compareData || compareData.length === 0) return;
|
|
2946
|
+
const compareMap = /* @__PURE__ */ new Map();
|
|
2947
|
+
for (const bar of compareData) {
|
|
2948
|
+
compareMap.set(bar.timestamp, bar.close);
|
|
2949
|
+
}
|
|
2950
|
+
let basePrice = null;
|
|
2951
|
+
for (const bar of mainDataList) {
|
|
2952
|
+
const close = compareMap.get(bar.timestamp);
|
|
2953
|
+
if (close != null) {
|
|
2954
|
+
basePrice = close;
|
|
2955
|
+
break;
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
if (basePrice === null || basePrice === 0) return;
|
|
2959
|
+
const bp = basePrice;
|
|
2960
|
+
const normalizedData = mainDataList.map((mainBar) => {
|
|
2961
|
+
const close = compareMap.get(mainBar.timestamp);
|
|
2962
|
+
return {
|
|
2963
|
+
pct: close != null ? (close - bp) / bp * 100 : NaN
|
|
2964
|
+
};
|
|
2965
|
+
});
|
|
2966
|
+
const indicatorName = `__cmp_${++compareCounter}_${ticker}`;
|
|
2967
|
+
reactKlinecharts.registerIndicator({
|
|
2968
|
+
name: indicatorName,
|
|
2969
|
+
shortName: ticker,
|
|
2970
|
+
figures: [
|
|
2971
|
+
{
|
|
2972
|
+
key: "pct",
|
|
2973
|
+
title: `${ticker} %: `,
|
|
2974
|
+
type: "line",
|
|
2975
|
+
styles: () => ({ color: assignedColor })
|
|
2976
|
+
}
|
|
2977
|
+
],
|
|
2978
|
+
calc: () => normalizedData
|
|
2979
|
+
});
|
|
2980
|
+
const paneId = state.chart.createIndicator(
|
|
2981
|
+
{ name: indicatorName },
|
|
2982
|
+
{ isStack: true, pane: { id: "candle_pane" } }
|
|
2983
|
+
);
|
|
2984
|
+
indicatorsRef.current.set(ticker, {
|
|
2985
|
+
name: indicatorName,
|
|
2986
|
+
paneId: typeof paneId === "string" ? paneId : "candle_pane"
|
|
2987
|
+
});
|
|
2988
|
+
setSymbols((prev) => {
|
|
2989
|
+
if (prev.some((s) => s.ticker === ticker)) return prev;
|
|
2990
|
+
return [
|
|
2991
|
+
...prev,
|
|
2992
|
+
{ ticker, basePrice: bp, color: assignedColor, visible: true }
|
|
2993
|
+
];
|
|
2994
|
+
});
|
|
2995
|
+
},
|
|
2996
|
+
[state.chart, state.period, datafeed]
|
|
2997
|
+
);
|
|
2998
|
+
const removeSymbol = react.useCallback(
|
|
2999
|
+
(ticker) => {
|
|
3000
|
+
const info = indicatorsRef.current.get(ticker);
|
|
3001
|
+
if (info && state.chart) {
|
|
3002
|
+
try {
|
|
3003
|
+
state.chart.removeIndicator({ name: info.name });
|
|
3004
|
+
} catch {
|
|
3005
|
+
}
|
|
3006
|
+
indicatorsRef.current.delete(ticker);
|
|
3007
|
+
}
|
|
3008
|
+
setSymbols((prev) => prev.filter((s) => s.ticker !== ticker));
|
|
3009
|
+
},
|
|
3010
|
+
[state.chart]
|
|
3011
|
+
);
|
|
3012
|
+
const toggleSymbol = react.useCallback(
|
|
3013
|
+
(ticker) => {
|
|
3014
|
+
const info = indicatorsRef.current.get(ticker);
|
|
3015
|
+
if (!info || !state.chart) return;
|
|
3016
|
+
setSymbols((prev) => {
|
|
3017
|
+
const sym = prev.find((s) => s.ticker === ticker);
|
|
3018
|
+
if (!sym) return prev;
|
|
3019
|
+
const newVisible = !sym.visible;
|
|
3020
|
+
state.chart?.overrideIndicator({
|
|
3021
|
+
name: info.name,
|
|
3022
|
+
visible: newVisible
|
|
3023
|
+
});
|
|
3024
|
+
return prev.map(
|
|
3025
|
+
(s) => s.ticker === ticker ? { ...s, visible: newVisible } : s
|
|
3026
|
+
);
|
|
3027
|
+
});
|
|
3028
|
+
},
|
|
3029
|
+
[state.chart]
|
|
3030
|
+
);
|
|
3031
|
+
const clearAll = react.useCallback(() => {
|
|
3032
|
+
indicatorsRef.current.forEach((info) => {
|
|
3033
|
+
try {
|
|
3034
|
+
state.chart?.removeIndicator({ name: info.name });
|
|
3035
|
+
} catch {
|
|
3036
|
+
}
|
|
3037
|
+
});
|
|
3038
|
+
indicatorsRef.current.clear();
|
|
3039
|
+
setSymbols([]);
|
|
3040
|
+
}, [state.chart]);
|
|
3041
|
+
react.useEffect(() => {
|
|
3042
|
+
return () => {
|
|
3043
|
+
indicatorsRef.current.forEach((info) => {
|
|
3044
|
+
try {
|
|
3045
|
+
state.chart?.removeIndicator({ name: info.name });
|
|
3046
|
+
} catch {
|
|
3047
|
+
}
|
|
3048
|
+
});
|
|
3049
|
+
indicatorsRef.current.clear();
|
|
3050
|
+
};
|
|
3051
|
+
}, [state.chart]);
|
|
3052
|
+
return { symbols, addSymbol, removeSymbol, toggleSymbol, clearAll };
|
|
3053
|
+
}
|
|
3054
|
+
var MEASURE_OVERLAY_ID = "__measure_line__";
|
|
3055
|
+
function useMeasure() {
|
|
3056
|
+
const { state } = useKlinechartsUI();
|
|
3057
|
+
const [isActive, setIsActive] = react.useState(false);
|
|
3058
|
+
const [fromPoint, setFromPoint] = react.useState(null);
|
|
3059
|
+
const [result, setResult] = react.useState(null);
|
|
3060
|
+
const clickCountRef = react.useRef(0);
|
|
3061
|
+
const computeResult = react.useCallback(
|
|
3062
|
+
(from, to) => {
|
|
3063
|
+
const priceDiff = to.price - from.price;
|
|
3064
|
+
const pricePercent = from.price !== 0 ? priceDiff / from.price * 100 : 0;
|
|
3065
|
+
const bars = Math.abs(to.barIndex - from.barIndex);
|
|
3066
|
+
const timeDiff = Math.abs(to.timestamp - from.timestamp);
|
|
3067
|
+
return { from, to, priceDiff, pricePercent, bars, timeDiff };
|
|
3068
|
+
},
|
|
3069
|
+
[]
|
|
3070
|
+
);
|
|
3071
|
+
const cleanup = react.useCallback(() => {
|
|
3072
|
+
state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
|
|
3073
|
+
}, [state.chart]);
|
|
3074
|
+
const startMeasure = react.useCallback(() => {
|
|
3075
|
+
cleanup();
|
|
3076
|
+
setIsActive(true);
|
|
3077
|
+
setFromPoint(null);
|
|
3078
|
+
setResult(null);
|
|
3079
|
+
clickCountRef.current = 0;
|
|
3080
|
+
if (!state.chart) return;
|
|
3081
|
+
state.chart.createOverlay({
|
|
3082
|
+
name: "segment",
|
|
3083
|
+
id: MEASURE_OVERLAY_ID,
|
|
3084
|
+
groupId: "measure",
|
|
3085
|
+
styles: {
|
|
3086
|
+
line: {
|
|
3087
|
+
style: "dashed",
|
|
3088
|
+
color: "#FFD600",
|
|
3089
|
+
size: 1
|
|
3090
|
+
}
|
|
3091
|
+
},
|
|
3092
|
+
lock: true,
|
|
3093
|
+
onDrawEnd: (event) => {
|
|
3094
|
+
const overlay = event?.overlay;
|
|
3095
|
+
const points = overlay?.points;
|
|
3096
|
+
if (!points || points.length < 2) return;
|
|
3097
|
+
const chart = state.chart;
|
|
3098
|
+
if (!chart) return;
|
|
3099
|
+
const dataList = chart.getDataList();
|
|
3100
|
+
if (!dataList || dataList.length === 0) return;
|
|
3101
|
+
const makePoint = (p) => {
|
|
3102
|
+
const idx = Math.max(
|
|
3103
|
+
0,
|
|
3104
|
+
Math.min(p.dataIndex ?? 0, dataList.length - 1)
|
|
3105
|
+
);
|
|
3106
|
+
const bar = dataList[idx];
|
|
3107
|
+
return {
|
|
3108
|
+
price: p.value ?? bar?.close ?? 0,
|
|
3109
|
+
timestamp: bar?.timestamp ?? 0,
|
|
3110
|
+
barIndex: idx
|
|
3111
|
+
};
|
|
3112
|
+
};
|
|
3113
|
+
const from = makePoint(points[0]);
|
|
3114
|
+
const to = makePoint(points[1]);
|
|
3115
|
+
setFromPoint(from);
|
|
3116
|
+
setResult(computeResult(from, to));
|
|
3117
|
+
setIsActive(false);
|
|
3118
|
+
}
|
|
3119
|
+
});
|
|
3120
|
+
}, [state.chart, cleanup, computeResult]);
|
|
3121
|
+
const cancelMeasure = react.useCallback(() => {
|
|
3122
|
+
cleanup();
|
|
3123
|
+
setIsActive(false);
|
|
3124
|
+
setFromPoint(null);
|
|
3125
|
+
clickCountRef.current = 0;
|
|
3126
|
+
}, [cleanup]);
|
|
3127
|
+
const clearResult = react.useCallback(() => {
|
|
3128
|
+
cleanup();
|
|
3129
|
+
setResult(null);
|
|
3130
|
+
setFromPoint(null);
|
|
3131
|
+
}, [cleanup]);
|
|
3132
|
+
react.useEffect(() => {
|
|
3133
|
+
return () => {
|
|
3134
|
+
state.chart?.removeOverlay({ id: MEASURE_OVERLAY_ID });
|
|
3135
|
+
};
|
|
3136
|
+
}, [state.chart]);
|
|
3137
|
+
return {
|
|
3138
|
+
isActive,
|
|
3139
|
+
startMeasure,
|
|
3140
|
+
cancelMeasure,
|
|
3141
|
+
result,
|
|
3142
|
+
clearResult,
|
|
3143
|
+
fromPoint
|
|
3144
|
+
};
|
|
3145
|
+
}
|
|
3146
|
+
var annotationCounter = 0;
|
|
3147
|
+
function useAnnotations() {
|
|
3148
|
+
const { state } = useKlinechartsUI();
|
|
3149
|
+
const [annotations, setAnnotations] = react.useState([]);
|
|
3150
|
+
const addAnnotation = react.useCallback(
|
|
3151
|
+
(text, price, timestamp, color) => {
|
|
3152
|
+
const id = `annotation_${++annotationCounter}`;
|
|
3153
|
+
const annotation = {
|
|
3154
|
+
id,
|
|
3155
|
+
text,
|
|
3156
|
+
price,
|
|
3157
|
+
timestamp,
|
|
3158
|
+
color
|
|
3159
|
+
};
|
|
3160
|
+
setAnnotations((prev) => [...prev, annotation]);
|
|
3161
|
+
if (state.chart) {
|
|
3162
|
+
state.chart.createOverlay({
|
|
3163
|
+
name: "simpleAnnotation",
|
|
3164
|
+
id,
|
|
3165
|
+
groupId: "annotations",
|
|
3166
|
+
points: [{ timestamp, value: price }],
|
|
3167
|
+
extendData: text,
|
|
3168
|
+
styles: color ? {
|
|
3169
|
+
text: {
|
|
3170
|
+
color
|
|
3171
|
+
}
|
|
3172
|
+
} : void 0,
|
|
3173
|
+
lock: true
|
|
3174
|
+
});
|
|
3175
|
+
}
|
|
3176
|
+
return id;
|
|
3177
|
+
},
|
|
3178
|
+
[state.chart]
|
|
3179
|
+
);
|
|
3180
|
+
const removeAnnotation = react.useCallback(
|
|
3181
|
+
(id) => {
|
|
3182
|
+
setAnnotations((prev) => prev.filter((a) => a.id !== id));
|
|
3183
|
+
state.chart?.removeOverlay({ id });
|
|
3184
|
+
},
|
|
3185
|
+
[state.chart]
|
|
3186
|
+
);
|
|
3187
|
+
const updateAnnotation = react.useCallback(
|
|
3188
|
+
(id, updates) => {
|
|
3189
|
+
setAnnotations(
|
|
3190
|
+
(prev) => prev.map((a) => a.id === id ? { ...a, ...updates } : a)
|
|
3191
|
+
);
|
|
3192
|
+
if (state.chart) {
|
|
3193
|
+
const overrideData = { id };
|
|
3194
|
+
if (updates.text !== void 0) {
|
|
3195
|
+
overrideData.extendData = updates.text;
|
|
3196
|
+
}
|
|
3197
|
+
if (updates.color !== void 0) {
|
|
3198
|
+
overrideData.styles = {
|
|
3199
|
+
text: {
|
|
3200
|
+
color: updates.color
|
|
3201
|
+
}
|
|
3202
|
+
};
|
|
3203
|
+
}
|
|
3204
|
+
state.chart.overrideOverlay(overrideData);
|
|
3205
|
+
}
|
|
3206
|
+
},
|
|
3207
|
+
[state.chart]
|
|
3208
|
+
);
|
|
3209
|
+
const clearAnnotations = react.useCallback(() => {
|
|
3210
|
+
setAnnotations((prev) => {
|
|
3211
|
+
for (const annotation of prev) {
|
|
3212
|
+
state.chart?.removeOverlay({ id: annotation.id });
|
|
3213
|
+
}
|
|
3214
|
+
return [];
|
|
3215
|
+
});
|
|
3216
|
+
}, [state.chart]);
|
|
3217
|
+
react.useEffect(() => {
|
|
3218
|
+
return () => {
|
|
3219
|
+
state.chart?.removeOverlay({ groupId: "annotations" });
|
|
3220
|
+
};
|
|
3221
|
+
}, [state.chart]);
|
|
3222
|
+
return {
|
|
3223
|
+
annotations,
|
|
3224
|
+
addAnnotation,
|
|
3225
|
+
removeAnnotation,
|
|
3226
|
+
updateAnnotation,
|
|
3227
|
+
clearAnnotations
|
|
3228
|
+
};
|
|
3229
|
+
}
|
|
2248
3230
|
|
|
2249
3231
|
// src/utils/createDataLoader.ts
|
|
2250
3232
|
function createDataLoader(datafeed, dispatch) {
|
|
@@ -2312,171 +3294,175 @@ function createDataLoader(datafeed, dispatch) {
|
|
|
2312
3294
|
|
|
2313
3295
|
Object.defineProperty(exports, "TA", {
|
|
2314
3296
|
enumerable: true,
|
|
2315
|
-
get: function () { return
|
|
3297
|
+
get: function () { return chunkPIFLJKYF_cjs.TA_default; }
|
|
2316
3298
|
});
|
|
2317
3299
|
Object.defineProperty(exports, "abcd", {
|
|
2318
3300
|
enumerable: true,
|
|
2319
|
-
get: function () { return
|
|
3301
|
+
get: function () { return chunkPIFLJKYF_cjs.abcd_default; }
|
|
2320
3302
|
});
|
|
2321
3303
|
Object.defineProperty(exports, "anyWaves", {
|
|
2322
3304
|
enumerable: true,
|
|
2323
|
-
get: function () { return
|
|
3305
|
+
get: function () { return chunkPIFLJKYF_cjs.anyWaves_default; }
|
|
2324
3306
|
});
|
|
2325
3307
|
Object.defineProperty(exports, "arrow", {
|
|
2326
3308
|
enumerable: true,
|
|
2327
|
-
get: function () { return
|
|
3309
|
+
get: function () { return chunkPIFLJKYF_cjs.arrow_default; }
|
|
2328
3310
|
});
|
|
2329
3311
|
Object.defineProperty(exports, "bollTv", {
|
|
2330
3312
|
enumerable: true,
|
|
2331
|
-
get: function () { return
|
|
3313
|
+
get: function () { return chunkPIFLJKYF_cjs.bollTv_default; }
|
|
2332
3314
|
});
|
|
2333
3315
|
Object.defineProperty(exports, "brush", {
|
|
2334
3316
|
enumerable: true,
|
|
2335
|
-
get: function () { return
|
|
3317
|
+
get: function () { return chunkPIFLJKYF_cjs.brush_default; }
|
|
2336
3318
|
});
|
|
2337
3319
|
Object.defineProperty(exports, "cci", {
|
|
2338
3320
|
enumerable: true,
|
|
2339
|
-
get: function () { return
|
|
3321
|
+
get: function () { return chunkPIFLJKYF_cjs.cci_default; }
|
|
2340
3322
|
});
|
|
2341
3323
|
Object.defineProperty(exports, "circle", {
|
|
2342
3324
|
enumerable: true,
|
|
2343
|
-
get: function () { return
|
|
3325
|
+
get: function () { return chunkPIFLJKYF_cjs.circle_default; }
|
|
3326
|
+
});
|
|
3327
|
+
Object.defineProperty(exports, "depthOverlay", {
|
|
3328
|
+
enumerable: true,
|
|
3329
|
+
get: function () { return chunkPIFLJKYF_cjs.depthOverlay_default; }
|
|
2344
3330
|
});
|
|
2345
3331
|
Object.defineProperty(exports, "eightWaves", {
|
|
2346
3332
|
enumerable: true,
|
|
2347
|
-
get: function () { return
|
|
3333
|
+
get: function () { return chunkPIFLJKYF_cjs.eightWaves_default; }
|
|
2348
3334
|
});
|
|
2349
3335
|
Object.defineProperty(exports, "elliottWave", {
|
|
2350
3336
|
enumerable: true,
|
|
2351
|
-
get: function () { return
|
|
3337
|
+
get: function () { return chunkPIFLJKYF_cjs.elliottWave_default; }
|
|
2352
3338
|
});
|
|
2353
3339
|
Object.defineProperty(exports, "fibRetracement", {
|
|
2354
3340
|
enumerable: true,
|
|
2355
|
-
get: function () { return
|
|
3341
|
+
get: function () { return chunkPIFLJKYF_cjs.fibRetracement_default; }
|
|
2356
3342
|
});
|
|
2357
3343
|
Object.defineProperty(exports, "fibonacciCircle", {
|
|
2358
3344
|
enumerable: true,
|
|
2359
|
-
get: function () { return
|
|
3345
|
+
get: function () { return chunkPIFLJKYF_cjs.fibonacciCircle_default; }
|
|
2360
3346
|
});
|
|
2361
3347
|
Object.defineProperty(exports, "fibonacciExtension", {
|
|
2362
3348
|
enumerable: true,
|
|
2363
|
-
get: function () { return
|
|
3349
|
+
get: function () { return chunkPIFLJKYF_cjs.fibonacciExtension_default; }
|
|
2364
3350
|
});
|
|
2365
3351
|
Object.defineProperty(exports, "fibonacciSegment", {
|
|
2366
3352
|
enumerable: true,
|
|
2367
|
-
get: function () { return
|
|
3353
|
+
get: function () { return chunkPIFLJKYF_cjs.fibonacciSegment_default; }
|
|
2368
3354
|
});
|
|
2369
3355
|
Object.defineProperty(exports, "fibonacciSpeedResistanceFan", {
|
|
2370
3356
|
enumerable: true,
|
|
2371
|
-
get: function () { return
|
|
3357
|
+
get: function () { return chunkPIFLJKYF_cjs.fibonacciSpeedResistanceFan_default; }
|
|
2372
3358
|
});
|
|
2373
3359
|
Object.defineProperty(exports, "fibonacciSpiral", {
|
|
2374
3360
|
enumerable: true,
|
|
2375
|
-
get: function () { return
|
|
3361
|
+
get: function () { return chunkPIFLJKYF_cjs.fibonacciSpiral_default; }
|
|
2376
3362
|
});
|
|
2377
3363
|
Object.defineProperty(exports, "fiveWaves", {
|
|
2378
3364
|
enumerable: true,
|
|
2379
|
-
get: function () { return
|
|
3365
|
+
get: function () { return chunkPIFLJKYF_cjs.fiveWaves_default; }
|
|
2380
3366
|
});
|
|
2381
3367
|
Object.defineProperty(exports, "gannBox", {
|
|
2382
3368
|
enumerable: true,
|
|
2383
|
-
get: function () { return
|
|
3369
|
+
get: function () { return chunkPIFLJKYF_cjs.gannBox_default; }
|
|
2384
3370
|
});
|
|
2385
3371
|
Object.defineProperty(exports, "gannFan", {
|
|
2386
3372
|
enumerable: true,
|
|
2387
|
-
get: function () { return
|
|
3373
|
+
get: function () { return chunkPIFLJKYF_cjs.gannFan_default; }
|
|
2388
3374
|
});
|
|
2389
3375
|
Object.defineProperty(exports, "hma", {
|
|
2390
3376
|
enumerable: true,
|
|
2391
|
-
get: function () { return
|
|
3377
|
+
get: function () { return chunkPIFLJKYF_cjs.hma_default; }
|
|
2392
3378
|
});
|
|
2393
3379
|
Object.defineProperty(exports, "ichimoku", {
|
|
2394
3380
|
enumerable: true,
|
|
2395
|
-
get: function () { return
|
|
3381
|
+
get: function () { return chunkPIFLJKYF_cjs.ichimoku_default; }
|
|
2396
3382
|
});
|
|
2397
3383
|
Object.defineProperty(exports, "indicators", {
|
|
2398
3384
|
enumerable: true,
|
|
2399
|
-
get: function () { return
|
|
3385
|
+
get: function () { return chunkPIFLJKYF_cjs.indicators; }
|
|
2400
3386
|
});
|
|
2401
3387
|
Object.defineProperty(exports, "longPosition", {
|
|
2402
3388
|
enumerable: true,
|
|
2403
|
-
get: function () { return
|
|
3389
|
+
get: function () { return chunkPIFLJKYF_cjs.longPosition_default; }
|
|
2404
3390
|
});
|
|
2405
3391
|
Object.defineProperty(exports, "maRibbon", {
|
|
2406
3392
|
enumerable: true,
|
|
2407
|
-
get: function () { return
|
|
3393
|
+
get: function () { return chunkPIFLJKYF_cjs.maRibbon_default; }
|
|
2408
3394
|
});
|
|
2409
3395
|
Object.defineProperty(exports, "macdTv", {
|
|
2410
3396
|
enumerable: true,
|
|
2411
|
-
get: function () { return
|
|
3397
|
+
get: function () { return chunkPIFLJKYF_cjs.macdTv_default; }
|
|
2412
3398
|
});
|
|
2413
3399
|
Object.defineProperty(exports, "measure", {
|
|
2414
3400
|
enumerable: true,
|
|
2415
|
-
get: function () { return
|
|
3401
|
+
get: function () { return chunkPIFLJKYF_cjs.measure_default; }
|
|
2416
3402
|
});
|
|
2417
3403
|
Object.defineProperty(exports, "orderLine", {
|
|
2418
3404
|
enumerable: true,
|
|
2419
|
-
get: function () { return
|
|
3405
|
+
get: function () { return chunkPIFLJKYF_cjs.orderLine_default; }
|
|
2420
3406
|
});
|
|
2421
3407
|
Object.defineProperty(exports, "overlays", {
|
|
2422
3408
|
enumerable: true,
|
|
2423
|
-
get: function () { return
|
|
3409
|
+
get: function () { return chunkPIFLJKYF_cjs.overlays; }
|
|
2424
3410
|
});
|
|
2425
3411
|
Object.defineProperty(exports, "parallelChannel", {
|
|
2426
3412
|
enumerable: true,
|
|
2427
|
-
get: function () { return
|
|
3413
|
+
get: function () { return chunkPIFLJKYF_cjs.parallelChannel_default; }
|
|
2428
3414
|
});
|
|
2429
3415
|
Object.defineProperty(exports, "parallelogram", {
|
|
2430
3416
|
enumerable: true,
|
|
2431
|
-
get: function () { return
|
|
3417
|
+
get: function () { return chunkPIFLJKYF_cjs.parallelogram_default; }
|
|
2432
3418
|
});
|
|
2433
3419
|
Object.defineProperty(exports, "pivotPoints", {
|
|
2434
3420
|
enumerable: true,
|
|
2435
|
-
get: function () { return
|
|
3421
|
+
get: function () { return chunkPIFLJKYF_cjs.pivotPoints_default; }
|
|
2436
3422
|
});
|
|
2437
3423
|
Object.defineProperty(exports, "ray", {
|
|
2438
3424
|
enumerable: true,
|
|
2439
|
-
get: function () { return
|
|
3425
|
+
get: function () { return chunkPIFLJKYF_cjs.ray_default; }
|
|
2440
3426
|
});
|
|
2441
3427
|
Object.defineProperty(exports, "rect", {
|
|
2442
3428
|
enumerable: true,
|
|
2443
|
-
get: function () { return
|
|
3429
|
+
get: function () { return chunkPIFLJKYF_cjs.rect_default; }
|
|
2444
3430
|
});
|
|
2445
3431
|
Object.defineProperty(exports, "registerExtensions", {
|
|
2446
3432
|
enumerable: true,
|
|
2447
|
-
get: function () { return
|
|
3433
|
+
get: function () { return chunkPIFLJKYF_cjs.registerExtensions; }
|
|
2448
3434
|
});
|
|
2449
3435
|
Object.defineProperty(exports, "rsiTv", {
|
|
2450
3436
|
enumerable: true,
|
|
2451
|
-
get: function () { return
|
|
3437
|
+
get: function () { return chunkPIFLJKYF_cjs.rsiTv_default; }
|
|
2452
3438
|
});
|
|
2453
3439
|
Object.defineProperty(exports, "shortPosition", {
|
|
2454
3440
|
enumerable: true,
|
|
2455
|
-
get: function () { return
|
|
3441
|
+
get: function () { return chunkPIFLJKYF_cjs.shortPosition_default; }
|
|
2456
3442
|
});
|
|
2457
3443
|
Object.defineProperty(exports, "stochastic", {
|
|
2458
3444
|
enumerable: true,
|
|
2459
|
-
get: function () { return
|
|
3445
|
+
get: function () { return chunkPIFLJKYF_cjs.stochastic_default; }
|
|
2460
3446
|
});
|
|
2461
3447
|
Object.defineProperty(exports, "superTrend", {
|
|
2462
3448
|
enumerable: true,
|
|
2463
|
-
get: function () { return
|
|
3449
|
+
get: function () { return chunkPIFLJKYF_cjs.superTrend_default; }
|
|
2464
3450
|
});
|
|
2465
3451
|
Object.defineProperty(exports, "threeWaves", {
|
|
2466
3452
|
enumerable: true,
|
|
2467
|
-
get: function () { return
|
|
3453
|
+
get: function () { return chunkPIFLJKYF_cjs.threeWaves_default; }
|
|
2468
3454
|
});
|
|
2469
3455
|
Object.defineProperty(exports, "triangle", {
|
|
2470
3456
|
enumerable: true,
|
|
2471
|
-
get: function () { return
|
|
3457
|
+
get: function () { return chunkPIFLJKYF_cjs.triangle_default; }
|
|
2472
3458
|
});
|
|
2473
3459
|
Object.defineProperty(exports, "vwap", {
|
|
2474
3460
|
enumerable: true,
|
|
2475
|
-
get: function () { return
|
|
3461
|
+
get: function () { return chunkPIFLJKYF_cjs.vwap_default; }
|
|
2476
3462
|
});
|
|
2477
3463
|
Object.defineProperty(exports, "xabcd", {
|
|
2478
3464
|
enumerable: true,
|
|
2479
|
-
get: function () { return
|
|
3465
|
+
get: function () { return chunkPIFLJKYF_cjs.xabcd_default; }
|
|
2480
3466
|
});
|
|
2481
3467
|
exports.CANDLE_TYPES = CANDLE_TYPES;
|
|
2482
3468
|
exports.COMPARE_RULES = COMPARE_RULES;
|
|
@@ -2493,6 +3479,11 @@ exports.TIMEZONES = TIMEZONES;
|
|
|
2493
3479
|
exports.TOOLTIP_SHOW_RULES = TOOLTIP_SHOW_RULES;
|
|
2494
3480
|
exports.YAXIS_POSITIONS = YAXIS_POSITIONS;
|
|
2495
3481
|
exports.createDataLoader = createDataLoader;
|
|
3482
|
+
exports.useAlerts = useAlerts;
|
|
3483
|
+
exports.useAnnotations = useAnnotations;
|
|
3484
|
+
exports.useCompare = useCompare;
|
|
3485
|
+
exports.useCrosshair = useCrosshair;
|
|
3486
|
+
exports.useDataExport = useDataExport;
|
|
2496
3487
|
exports.useDrawingTools = useDrawingTools;
|
|
2497
3488
|
exports.useFullscreen = useFullscreen;
|
|
2498
3489
|
exports.useIndicators = useIndicators;
|
|
@@ -2501,12 +3492,15 @@ exports.useKlinechartsUILoading = useKlinechartsUILoading;
|
|
|
2501
3492
|
exports.useKlinechartsUISettings = useKlinechartsUISettings;
|
|
2502
3493
|
exports.useKlinechartsUITheme = useKlinechartsUITheme;
|
|
2503
3494
|
exports.useLayoutManager = useLayoutManager;
|
|
3495
|
+
exports.useMeasure = useMeasure;
|
|
2504
3496
|
exports.useOrderLines = useOrderLines;
|
|
2505
3497
|
exports.usePeriods = usePeriods;
|
|
3498
|
+
exports.useReplay = useReplay;
|
|
2506
3499
|
exports.useScreenshot = useScreenshot;
|
|
2507
3500
|
exports.useScriptEditor = useScriptEditor;
|
|
2508
3501
|
exports.useSymbolSearch = useSymbolSearch;
|
|
2509
3502
|
exports.useTimezone = useTimezone;
|
|
2510
3503
|
exports.useUndoRedo = useUndoRedo;
|
|
3504
|
+
exports.useWatchlist = useWatchlist;
|
|
2511
3505
|
//# sourceMappingURL=index.cjs.map
|
|
2512
3506
|
//# sourceMappingURL=index.cjs.map
|