@oanda/labs-crowd-view-widget 1.0.30 → 1.0.31
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/CHANGELOG.md +128 -0
- package/dist/main/CrowdViewWidget/Main.js +1 -0
- package/dist/main/CrowdViewWidget/Main.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/Chart.js +68 -2
- package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +7 -3
- package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/constants.js +4 -1
- package/dist/main/CrowdViewWidget/components/Chart/constants.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/getOption.js +44 -12
- package/dist/main/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/utils.js +103 -16
- package/dist/main/CrowdViewWidget/components/Chart/utils.js.map +1 -1
- package/dist/main/CrowdViewWidget/config.js +10 -4
- package/dist/main/CrowdViewWidget/config.js.map +1 -1
- package/dist/main/CrowdViewWidget/types.js +22 -5
- package/dist/main/CrowdViewWidget/types.js.map +1 -1
- package/dist/module/CrowdViewWidget/Main.js +1 -0
- package/dist/module/CrowdViewWidget/Main.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/Chart.js +70 -4
- package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +7 -3
- package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/constants.js +3 -0
- package/dist/module/CrowdViewWidget/components/Chart/constants.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/getOption.js +46 -14
- package/dist/module/CrowdViewWidget/components/Chart/getOption.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/utils.js +100 -15
- package/dist/module/CrowdViewWidget/components/Chart/utils.js.map +1 -1
- package/dist/module/CrowdViewWidget/config.js +10 -4
- package/dist/module/CrowdViewWidget/config.js.map +1 -1
- package/dist/module/CrowdViewWidget/types.js +21 -4
- package/dist/module/CrowdViewWidget/types.js.map +1 -1
- package/dist/types/CrowdViewWidget/components/Chart/ChartWithData.d.ts +1 -1
- package/dist/types/CrowdViewWidget/components/Chart/constants.d.ts +3 -0
- package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +11 -1
- package/dist/types/CrowdViewWidget/components/Chart/utils.d.ts +26 -1
- package/dist/types/CrowdViewWidget/types.d.ts +20 -4
- package/package.json +3 -3
- package/src/CrowdViewWidget/Main.tsx +5 -1
- package/src/CrowdViewWidget/components/Chart/Chart.tsx +99 -2
- package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +7 -3
- package/src/CrowdViewWidget/components/Chart/constants.tsx +4 -0
- package/src/CrowdViewWidget/components/Chart/getOption.ts +72 -19
- package/src/CrowdViewWidget/components/Chart/types.ts +13 -2
- package/src/CrowdViewWidget/components/Chart/utils.ts +110 -11
- package/src/CrowdViewWidget/config.ts +12 -4
- package/src/CrowdViewWidget/types.ts +21 -4
|
@@ -10,15 +10,17 @@ import {
|
|
|
10
10
|
DataZoomInsideComponent,
|
|
11
11
|
GraphicComponent,
|
|
12
12
|
GridSimpleComponent,
|
|
13
|
+
MarkPointComponent,
|
|
13
14
|
TooltipComponent,
|
|
14
15
|
} from 'echarts/components';
|
|
15
16
|
import * as echarts from 'echarts/core';
|
|
16
17
|
import { CanvasRenderer } from 'echarts/renderers';
|
|
17
18
|
import React, { useEffect, useRef } from 'react';
|
|
18
19
|
|
|
19
|
-
import { CHART_HEIGHT } from './constants';
|
|
20
|
+
import { CHART_HEIGHT, MAX_LABELS_COUNT } from './constants';
|
|
20
21
|
import { getDesktopOption, getOption } from './getOption';
|
|
21
22
|
import type { ChartProps } from './types';
|
|
23
|
+
import { getLabelData, isDifferenceGreaterThanTwoWeeks } from './utils';
|
|
22
24
|
|
|
23
25
|
echarts.use([
|
|
24
26
|
GridSimpleComponent,
|
|
@@ -29,6 +31,7 @@ echarts.use([
|
|
|
29
31
|
CustomChart,
|
|
30
32
|
TooltipComponent,
|
|
31
33
|
CandlestickChart,
|
|
34
|
+
MarkPointComponent,
|
|
32
35
|
]);
|
|
33
36
|
|
|
34
37
|
echarts.registerTheme('dark_theme', getChartTheme(Theme.Dark));
|
|
@@ -43,8 +46,101 @@ const Chart = ({ data }: ChartProps) => {
|
|
|
43
46
|
const echartInstance = echartRef.current.getEchartsInstance();
|
|
44
47
|
|
|
45
48
|
echartInstance.setOption(getDesktopOption({ isDark }));
|
|
49
|
+
|
|
50
|
+
echartInstance.off('datazoom');
|
|
51
|
+
echartInstance.on('datazoom', () => {
|
|
52
|
+
const {
|
|
53
|
+
dataZoom,
|
|
54
|
+
// @ts-ignore
|
|
55
|
+
title: { text },
|
|
56
|
+
} = echartInstance.getOption();
|
|
57
|
+
|
|
58
|
+
const { start, end } = (
|
|
59
|
+
dataZoom as { start: number; end: number }[]
|
|
60
|
+
)[0];
|
|
61
|
+
const prevInterval = Number((text as string).split(',')[0]);
|
|
62
|
+
const prevIsGreaterThanTwoWeeks = Boolean(
|
|
63
|
+
Number((text as string).split(',')[1])
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const visibleItemsCount = Math.round(
|
|
67
|
+
(data.xAxisData.length * (end - start)) / 100
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const interval =
|
|
71
|
+
visibleItemsCount > MAX_LABELS_COUNT
|
|
72
|
+
? Math.floor(visibleItemsCount / MAX_LABELS_COUNT)
|
|
73
|
+
: 0;
|
|
74
|
+
|
|
75
|
+
if (interval !== prevInterval) {
|
|
76
|
+
echartInstance.setOption({
|
|
77
|
+
xAxis: {
|
|
78
|
+
axisLabel: {
|
|
79
|
+
interval,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const visibleXAxisData = data.xAxisData.slice(
|
|
86
|
+
(data.xAxisData.length * start) / 100,
|
|
87
|
+
(data.xAxisData.length * end) / 100
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const isGreaterThanTwoWeeks = isDifferenceGreaterThanTwoWeeks(
|
|
91
|
+
visibleXAxisData[0],
|
|
92
|
+
visibleXAxisData[visibleXAxisData.length - 1]
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
if (isGreaterThanTwoWeeks !== prevIsGreaterThanTwoWeeks) {
|
|
96
|
+
const labelsData = getLabelData({
|
|
97
|
+
xAxisData: data.xAxisData,
|
|
98
|
+
isGreaterThanTwoWeeks,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
echartInstance.setOption({
|
|
102
|
+
series: [
|
|
103
|
+
{
|
|
104
|
+
id: 'candlestick',
|
|
105
|
+
markPoint: {
|
|
106
|
+
data: labelsData,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
echartInstance.setOption({
|
|
113
|
+
xAxis: {
|
|
114
|
+
axisLabel: {
|
|
115
|
+
formatter: (value: string) => {
|
|
116
|
+
const date = new Date(value);
|
|
117
|
+
return isGreaterThanTwoWeeks
|
|
118
|
+
? `${date.toLocaleTimeString(undefined, {
|
|
119
|
+
hour: '2-digit',
|
|
120
|
+
minute: '2-digit',
|
|
121
|
+
})}`
|
|
122
|
+
: `${date.toLocaleDateString(undefined, {
|
|
123
|
+
day: 'numeric',
|
|
124
|
+
})}`;
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (
|
|
132
|
+
interval !== prevInterval ||
|
|
133
|
+
isGreaterThanTwoWeeks !== prevIsGreaterThanTwoWeeks
|
|
134
|
+
) {
|
|
135
|
+
echartInstance.setOption({
|
|
136
|
+
title: {
|
|
137
|
+
text: `${interval},${isGreaterThanTwoWeeks ? 1 : 0}`,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
});
|
|
46
142
|
}
|
|
47
|
-
}, [echartRef, isDark]);
|
|
143
|
+
}, [echartRef, isDark, data]);
|
|
48
144
|
|
|
49
145
|
return (
|
|
50
146
|
<BaseChart
|
|
@@ -52,6 +148,7 @@ const Chart = ({ data }: ChartProps) => {
|
|
|
52
148
|
chartHeight={CHART_HEIGHT}
|
|
53
149
|
echarts={echarts}
|
|
54
150
|
isDark={isDark}
|
|
151
|
+
lazyUpdate={true}
|
|
55
152
|
option={getOption(data)}
|
|
56
153
|
/>
|
|
57
154
|
);
|
|
@@ -18,7 +18,11 @@ import { Chart } from './Chart';
|
|
|
18
18
|
import type { ChartWithDataProps } from './types';
|
|
19
19
|
import { transformDataForChart } from './utils';
|
|
20
20
|
|
|
21
|
-
const ChartWithData = ({
|
|
21
|
+
const ChartWithData = ({
|
|
22
|
+
instrument,
|
|
23
|
+
bookType,
|
|
24
|
+
granularity,
|
|
25
|
+
}: ChartWithDataProps) => {
|
|
22
26
|
const { size } = useLayoutProvider();
|
|
23
27
|
const isDesktop = size === Size.DESKTOP;
|
|
24
28
|
|
|
@@ -38,8 +42,8 @@ const ChartWithData = ({ instrument, bookType }: ChartWithDataProps) => {
|
|
|
38
42
|
|
|
39
43
|
const transformedData = useMemo(() => {
|
|
40
44
|
if (!data) return undefined;
|
|
41
|
-
return transformDataForChart(data);
|
|
42
|
-
}, [data]);
|
|
45
|
+
return transformDataForChart({ data, granularity });
|
|
46
|
+
}, [data, granularity]);
|
|
43
47
|
|
|
44
48
|
return (
|
|
45
49
|
<>
|
|
@@ -8,11 +8,18 @@ import * as echarts from 'echarts/core';
|
|
|
8
8
|
import {
|
|
9
9
|
CHART_HEIGHT,
|
|
10
10
|
CHART_WIDTH,
|
|
11
|
+
INITIAL_END_ZOOM,
|
|
12
|
+
INITIAL_START_ZOOM,
|
|
13
|
+
MAX_LABELS_COUNT,
|
|
11
14
|
X_LABEL_SIZE,
|
|
12
15
|
Y_LABEL_SIZE_DESKTOP,
|
|
13
16
|
} from './constants';
|
|
14
17
|
import type { GetOptionType, GetResponsiveOptionsProps } from './types';
|
|
15
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
getLabelData,
|
|
20
|
+
getRectColor,
|
|
21
|
+
isDifferenceGreaterThanTwoWeeks,
|
|
22
|
+
} from './utils';
|
|
16
23
|
|
|
17
24
|
export const getDesktopOption = ({ isDark }: GetResponsiveOptionsProps) => {
|
|
18
25
|
const desktopGridLines = getGridLines({
|
|
@@ -60,31 +67,66 @@ export const getDesktopOption = ({ isDark }: GetResponsiveOptionsProps) => {
|
|
|
60
67
|
};
|
|
61
68
|
};
|
|
62
69
|
|
|
70
|
+
// @ts-ignore
|
|
63
71
|
export const getOption: GetOptionType = ({
|
|
64
72
|
xAxisData,
|
|
65
73
|
ordersPositionsChartData,
|
|
66
74
|
ordersPositionsBucketWidth,
|
|
67
75
|
candlesSeriesData,
|
|
68
76
|
}) => {
|
|
77
|
+
const visibleXAxisData = xAxisData.slice(
|
|
78
|
+
(xAxisData.length * INITIAL_START_ZOOM) / 100,
|
|
79
|
+
(xAxisData.length * INITIAL_END_ZOOM) / 100
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const isGreaterThanTwoWeeks = isDifferenceGreaterThanTwoWeeks(
|
|
83
|
+
visibleXAxisData[0],
|
|
84
|
+
visibleXAxisData[visibleXAxisData.length - 1]
|
|
85
|
+
);
|
|
86
|
+
const labelsData = getLabelData({
|
|
87
|
+
xAxisData,
|
|
88
|
+
isGreaterThanTwoWeeks,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const visibleItemsCount = Math.round(
|
|
92
|
+
(xAxisData.length * (INITIAL_END_ZOOM - INITIAL_START_ZOOM)) / 100
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const initialInterval =
|
|
96
|
+
visibleItemsCount > MAX_LABELS_COUNT
|
|
97
|
+
? Math.floor(visibleItemsCount / MAX_LABELS_COUNT)
|
|
98
|
+
: 0;
|
|
99
|
+
|
|
69
100
|
return {
|
|
101
|
+
title: {
|
|
102
|
+
show: false,
|
|
103
|
+
text: `${initialInterval},${isGreaterThanTwoWeeks}`,
|
|
104
|
+
},
|
|
70
105
|
animation: false,
|
|
71
106
|
dataZoom: [
|
|
72
107
|
{
|
|
73
108
|
type: 'inside',
|
|
74
109
|
xAxisIndex: 0,
|
|
75
|
-
start:
|
|
76
|
-
end:
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
type: 'inside',
|
|
80
|
-
yAxisIndex: 0,
|
|
81
|
-
startValue: 1.5,
|
|
82
|
-
endValue: 1.7,
|
|
110
|
+
start: INITIAL_START_ZOOM,
|
|
111
|
+
end: INITIAL_END_ZOOM,
|
|
83
112
|
},
|
|
84
113
|
],
|
|
85
114
|
tooltip: {
|
|
86
115
|
trigger: 'axis',
|
|
87
|
-
formatter:
|
|
116
|
+
formatter: (val) => {
|
|
117
|
+
// @ts-ignore
|
|
118
|
+
const timestamp = val[0].axisValue as string;
|
|
119
|
+
|
|
120
|
+
return `${new Date(timestamp).toLocaleTimeString(undefined, {
|
|
121
|
+
hour: '2-digit',
|
|
122
|
+
minute: '2-digit',
|
|
123
|
+
})}
|
|
124
|
+
${new Date(timestamp).toLocaleDateString(undefined, {
|
|
125
|
+
day: 'numeric',
|
|
126
|
+
month: 'short',
|
|
127
|
+
})}
|
|
128
|
+
`;
|
|
129
|
+
},
|
|
88
130
|
},
|
|
89
131
|
xAxis: {
|
|
90
132
|
type: 'category',
|
|
@@ -95,16 +137,24 @@ export const getOption: GetOptionType = ({
|
|
|
95
137
|
axisLabel: {
|
|
96
138
|
padding: [8, 16, 8, 16],
|
|
97
139
|
margin: 0,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
140
|
+
interval: initialInterval,
|
|
141
|
+
formatter: (value) => {
|
|
142
|
+
const date = new Date(value);
|
|
143
|
+
return isGreaterThanTwoWeeks
|
|
144
|
+
? `${date.toLocaleTimeString(undefined, {
|
|
145
|
+
hour: '2-digit',
|
|
146
|
+
minute: '2-digit',
|
|
147
|
+
})}`
|
|
148
|
+
: `${date.toLocaleDateString(undefined, {
|
|
149
|
+
day: 'numeric',
|
|
150
|
+
})}`;
|
|
151
|
+
},
|
|
103
152
|
},
|
|
104
153
|
},
|
|
105
154
|
yAxis: {
|
|
106
155
|
type: 'value',
|
|
107
156
|
position: 'right',
|
|
157
|
+
scale: true,
|
|
108
158
|
axisLine: { show: false },
|
|
109
159
|
axisTick: { show: false },
|
|
110
160
|
axisLabel: {
|
|
@@ -115,36 +165,39 @@ export const getOption: GetOptionType = ({
|
|
|
115
165
|
series: [
|
|
116
166
|
{
|
|
117
167
|
type: 'candlestick',
|
|
168
|
+
id: 'candlestick',
|
|
118
169
|
data: candlesSeriesData,
|
|
119
170
|
itemStyle: {
|
|
120
171
|
color: colorPalette.raspberryLight,
|
|
121
172
|
color0: colorPalette.bottleGreenLight,
|
|
122
173
|
},
|
|
174
|
+
markPoint: {
|
|
175
|
+
symbol: 'circle',
|
|
176
|
+
symbolSize: 0,
|
|
177
|
+
data: labelsData,
|
|
178
|
+
},
|
|
123
179
|
},
|
|
124
180
|
{
|
|
125
181
|
type: 'custom',
|
|
126
182
|
name: 'heatmap',
|
|
183
|
+
id: 'heatmap',
|
|
127
184
|
silent: true,
|
|
128
185
|
renderItem: (params, api) => {
|
|
129
186
|
const { coordSys } = params;
|
|
130
|
-
|
|
131
187
|
const xVal = api.value(0);
|
|
132
188
|
const yVal = api.value(1);
|
|
133
189
|
const sentiment = api.value(2) as number;
|
|
134
190
|
const start = api.coord([xVal, yVal]);
|
|
135
|
-
|
|
136
191
|
const [rectWidth, rectHeight] = api.size!([
|
|
137
192
|
0,
|
|
138
193
|
ordersPositionsBucketWidth,
|
|
139
194
|
]) as number[];
|
|
140
|
-
|
|
141
195
|
const boundaries = coordSys as unknown as {
|
|
142
196
|
x: number;
|
|
143
197
|
y: number;
|
|
144
198
|
width: number;
|
|
145
199
|
height: number;
|
|
146
200
|
};
|
|
147
|
-
|
|
148
201
|
const shape = echarts.graphic.clipRectByRect(
|
|
149
202
|
{
|
|
150
203
|
x: start[0] - rectWidth / 2,
|
|
@@ -2,6 +2,7 @@ import type { GetOrderPositionBooksQuery } from '@oanda/labs-order-book-widget/s
|
|
|
2
2
|
import type { EChartsOption } from 'echarts';
|
|
3
3
|
|
|
4
4
|
import type { BookType } from '../../../gql/types/graphql';
|
|
5
|
+
import type { GranularityId } from '../../types';
|
|
5
6
|
|
|
6
7
|
interface ChartOptionsProps {
|
|
7
8
|
xAxisData: string[];
|
|
@@ -18,7 +19,6 @@ interface ChartOptionsProps {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export type GetOptionType = (props: ChartOptionsProps) => EChartsOption;
|
|
21
|
-
|
|
22
22
|
export interface GetResponsiveOptionsProps {
|
|
23
23
|
isDark: boolean;
|
|
24
24
|
}
|
|
@@ -30,8 +30,19 @@ export interface ChartProps {
|
|
|
30
30
|
export interface ChartWithDataProps {
|
|
31
31
|
bookType: BookType;
|
|
32
32
|
instrument: string;
|
|
33
|
+
granularity: GranularityId;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface TransformDataForChartTypeProps {
|
|
37
|
+
data: GetOrderPositionBooksQuery;
|
|
38
|
+
granularity: GranularityId;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
export type TransformDataForChartType = (
|
|
36
|
-
props:
|
|
42
|
+
props: TransformDataForChartTypeProps
|
|
37
43
|
) => ChartOptionsProps;
|
|
44
|
+
|
|
45
|
+
export interface GetLabelsDataProps {
|
|
46
|
+
xAxisData: string[];
|
|
47
|
+
isGreaterThanTwoWeeks: boolean;
|
|
48
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import type { GetOrderPositionBooksQuery } from '../../../gql/types/graphql';
|
|
2
2
|
import { colorMap } from '../../config';
|
|
3
|
+
import { TimeSpanId } from '../../types';
|
|
4
|
+
import { CHART_HEIGHT } from './constants';
|
|
3
5
|
import { getPriceCandlesMock } from './getPriceCandlesMock';
|
|
4
|
-
import type { TransformDataForChartType } from './types';
|
|
6
|
+
import type { GetLabelsDataProps, TransformDataForChartType } from './types';
|
|
5
7
|
|
|
6
8
|
export const getRectColor = (sentiment: number) => {
|
|
7
9
|
const colors = sentiment < 0 ? colorMap.short : colorMap.long;
|
|
@@ -26,14 +28,55 @@ export const getRectColor = (sentiment: number) => {
|
|
|
26
28
|
return 'transparent';
|
|
27
29
|
};
|
|
28
30
|
|
|
31
|
+
export enum GranularityId {
|
|
32
|
+
MINUTE_5 = 'MINUTE_5',
|
|
33
|
+
MINUTE_15 = 'MINUTE_15',
|
|
34
|
+
MINUTE_30 = 'MINUTE_30',
|
|
35
|
+
HOUR_1 = 'HOUR_1',
|
|
36
|
+
HOUR_4 = 'HOUR_4',
|
|
37
|
+
DAY_1 = 'DAY_1',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const timeSpansInMilliseconds: Record<TimeSpanId, number> = {
|
|
41
|
+
[TimeSpanId.HOUR_1]: 60 * 60 * 1000,
|
|
42
|
+
[TimeSpanId.HOUR_12]: 12 * 60 * 60 * 1000,
|
|
43
|
+
[TimeSpanId.DAY_1]: 24 * 60 * 60 * 1000,
|
|
44
|
+
[TimeSpanId.DAY_2]: 2 * 24 * 60 * 60 * 1000,
|
|
45
|
+
[TimeSpanId.WEEK_1]: 7 * 24 * 60 * 60 * 1000,
|
|
46
|
+
[TimeSpanId.WEEK_2]: 14 * 24 * 60 * 60 * 1000,
|
|
47
|
+
[TimeSpanId.WEEK_3]: 21 * 24 * 60 * 60 * 1000,
|
|
48
|
+
[TimeSpanId.MONTH_1]: 30 * 24 * 60 * 60 * 1000, // Approximation
|
|
49
|
+
[TimeSpanId.MONTH_3]: 3 * 30 * 24 * 60 * 60 * 1000, // Approximation
|
|
50
|
+
[TimeSpanId.MONTH_6]: 6 * 30 * 24 * 60 * 60 * 1000, // Approximation
|
|
51
|
+
[TimeSpanId.YEAR_1]: 365 * 24 * 60 * 60 * 1000, // Approximation
|
|
52
|
+
[TimeSpanId.YEAR_5]: 5 * 365 * 24 * 60 * 60 * 1000, // Approximation
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const granularityInMilliseconds: Record<GranularityId, number> = {
|
|
56
|
+
[GranularityId.MINUTE_5]: 5 * 60 * 1000,
|
|
57
|
+
[GranularityId.MINUTE_15]: 15 * 60 * 1000,
|
|
58
|
+
[GranularityId.MINUTE_30]: 30 * 60 * 1000,
|
|
59
|
+
[GranularityId.HOUR_1]: 60 * 60 * 1000,
|
|
60
|
+
[GranularityId.HOUR_4]: 4 * 60 * 60 * 1000,
|
|
61
|
+
[GranularityId.DAY_1]: 24 * 60 * 60 * 1000,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const temporaryGranularityTimeSpanMap: Record<GranularityId, TimeSpanId> = {
|
|
65
|
+
[GranularityId.MINUTE_5]: TimeSpanId.DAY_2,
|
|
66
|
+
[GranularityId.MINUTE_15]: TimeSpanId.WEEK_1,
|
|
67
|
+
[GranularityId.MINUTE_30]: TimeSpanId.WEEK_2,
|
|
68
|
+
[GranularityId.HOUR_1]: TimeSpanId.MONTH_1,
|
|
69
|
+
[GranularityId.HOUR_4]: TimeSpanId.MONTH_3,
|
|
70
|
+
[GranularityId.DAY_1]: TimeSpanId.MONTH_6,
|
|
71
|
+
};
|
|
72
|
+
|
|
29
73
|
const mockOrderPositionDataForHistoricalData = (
|
|
30
|
-
data: GetOrderPositionBooksQuery
|
|
74
|
+
data: GetOrderPositionBooksQuery,
|
|
75
|
+
granularityId: GranularityId
|
|
31
76
|
) => {
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
// const timeSpan = oneMinuteInMilliseconds * 60 * 6 // 6h
|
|
36
|
-
// const timeSpan = oneMinuteInMilliseconds * 15 //15min
|
|
77
|
+
const granularity = granularityInMilliseconds[granularityId];
|
|
78
|
+
const timeSpan =
|
|
79
|
+
timeSpansInMilliseconds[temporaryGranularityTimeSpanMap[granularityId]];
|
|
37
80
|
|
|
38
81
|
const currentTime = data.orderPositionBooks[0]!.time;
|
|
39
82
|
|
|
@@ -50,13 +93,68 @@ const mockOrderPositionDataForHistoricalData = (
|
|
|
50
93
|
buckets: data.orderPositionBooks[0]!.buckets,
|
|
51
94
|
bucketWidth: data.orderPositionBooks[0]!.bucketWidth,
|
|
52
95
|
};
|
|
53
|
-
})
|
|
96
|
+
})
|
|
97
|
+
.reverse();
|
|
54
98
|
|
|
55
99
|
return orderPositionDataForHistorical;
|
|
56
100
|
};
|
|
57
101
|
|
|
58
|
-
export const
|
|
59
|
-
|
|
102
|
+
export const getLabelData = ({
|
|
103
|
+
xAxisData,
|
|
104
|
+
isGreaterThanTwoWeeks,
|
|
105
|
+
}: GetLabelsDataProps) =>
|
|
106
|
+
xAxisData
|
|
107
|
+
.filter((record, index, arr) => {
|
|
108
|
+
if (index === 0) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const previousTimestamp = arr[index - 1];
|
|
112
|
+
const currentDate = new Date(record);
|
|
113
|
+
const previousDate = new Date(previousTimestamp);
|
|
114
|
+
|
|
115
|
+
return isGreaterThanTwoWeeks
|
|
116
|
+
? currentDate.getDate() !== previousDate.getDate()
|
|
117
|
+
: currentDate.getMonth() !== previousDate.getMonth();
|
|
118
|
+
})
|
|
119
|
+
.map((item) => ({
|
|
120
|
+
name: new Date(item).toLocaleDateString(undefined, {
|
|
121
|
+
month: 'short',
|
|
122
|
+
day: isGreaterThanTwoWeeks ? 'numeric' : undefined,
|
|
123
|
+
}),
|
|
124
|
+
xAxis: item,
|
|
125
|
+
y: CHART_HEIGHT - 22,
|
|
126
|
+
silent: true,
|
|
127
|
+
emphasis: {
|
|
128
|
+
disabled: true,
|
|
129
|
+
},
|
|
130
|
+
label: {
|
|
131
|
+
fontFamily: 'Sofia W03',
|
|
132
|
+
fontSize: 10,
|
|
133
|
+
position: 'bottom',
|
|
134
|
+
align: 'center',
|
|
135
|
+
formatter: '{b}',
|
|
136
|
+
},
|
|
137
|
+
}));
|
|
138
|
+
|
|
139
|
+
export const isDifferenceGreaterThanTwoWeeks = (
|
|
140
|
+
startDate: string,
|
|
141
|
+
endDate: string
|
|
142
|
+
) => {
|
|
143
|
+
const date1 = new Date(startDate);
|
|
144
|
+
const date2 = new Date(endDate);
|
|
145
|
+
|
|
146
|
+
const TWO_WEEKS_IN_MS = 14 * 24 * 60 * 60 * 1000;
|
|
147
|
+
|
|
148
|
+
const differenceInMs = Math.abs(date2.getTime() - date1.getTime());
|
|
149
|
+
|
|
150
|
+
return differenceInMs < TWO_WEEKS_IN_MS;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
export const transformDataForChart: TransformDataForChartType = ({
|
|
154
|
+
data,
|
|
155
|
+
granularity,
|
|
156
|
+
}) => {
|
|
157
|
+
const mockedData = mockOrderPositionDataForHistoricalData(data, granularity);
|
|
60
158
|
|
|
61
159
|
const xAxisData = mockedData.map(({ time }) => time);
|
|
62
160
|
const ordersPositionsChartData = mockedData
|
|
@@ -70,7 +168,7 @@ export const transformDataForChart: TransformDataForChartType = (data) => {
|
|
|
70
168
|
)
|
|
71
169
|
.filter((item) => Math.abs(item[2]) > 0.15);
|
|
72
170
|
|
|
73
|
-
const mockedCandles = getPriceCandlesMock(
|
|
171
|
+
const mockedCandles = getPriceCandlesMock(xAxisData.length);
|
|
74
172
|
|
|
75
173
|
const candlesSeriesData: [number, number, number, number][] =
|
|
76
174
|
mockedCandles.map(({ close, open, low, high }) => [
|
|
@@ -85,5 +183,6 @@ export const transformDataForChart: TransformDataForChartType = (data) => {
|
|
|
85
183
|
ordersPositionsChartData,
|
|
86
184
|
ordersPositionsBucketWidth: mockedData[0]!.bucketWidth,
|
|
87
185
|
candlesSeriesData,
|
|
186
|
+
granularity,
|
|
88
187
|
};
|
|
89
188
|
};
|
|
@@ -105,19 +105,27 @@ const instrumentPrecisionConfig: Record<InstrumentId, number> = {
|
|
|
105
105
|
|
|
106
106
|
const granularitySelectConfig = [
|
|
107
107
|
{
|
|
108
|
-
id: GranularityId.
|
|
108
|
+
id: GranularityId.MINUTE_5,
|
|
109
109
|
label: '5_minutes',
|
|
110
110
|
},
|
|
111
111
|
{
|
|
112
|
-
id: GranularityId.
|
|
112
|
+
id: GranularityId.MINUTE_15,
|
|
113
113
|
label: '15_minutes',
|
|
114
114
|
},
|
|
115
115
|
{
|
|
116
|
-
id: GranularityId.
|
|
116
|
+
id: GranularityId.MINUTE_30,
|
|
117
|
+
label: '30_minutes',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: GranularityId.HOUR_1,
|
|
117
121
|
label: '1_hour',
|
|
118
122
|
},
|
|
119
123
|
{
|
|
120
|
-
id: GranularityId.
|
|
124
|
+
id: GranularityId.HOUR_4,
|
|
125
|
+
label: '4_hours',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
id: GranularityId.DAY_1,
|
|
121
129
|
label: '1_day',
|
|
122
130
|
},
|
|
123
131
|
];
|
|
@@ -29,10 +29,27 @@ export enum InstrumentId {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export enum GranularityId {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
MINUTE_5 = 'MINUTE_5',
|
|
33
|
+
MINUTE_15 = 'MINUTE_15',
|
|
34
|
+
MINUTE_30 = 'MINUTE_30',
|
|
35
|
+
HOUR_1 = 'HOUR_1',
|
|
36
|
+
HOUR_4 = 'HOUR_4',
|
|
37
|
+
DAY_1 = 'DAY_1',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export enum TimeSpanId {
|
|
41
|
+
HOUR_1 = 'HOUR_1',
|
|
42
|
+
HOUR_12 = 'HOUR_12',
|
|
43
|
+
DAY_1 = 'DAY_1',
|
|
44
|
+
DAY_2 = 'DAY_2',
|
|
45
|
+
WEEK_1 = 'WEEK_1',
|
|
46
|
+
WEEK_2 = 'WEEK_2',
|
|
47
|
+
WEEK_3 = 'WEEK_3',
|
|
48
|
+
MONTH_1 = 'MONTH_1',
|
|
49
|
+
MONTH_3 = 'MONTH_3',
|
|
50
|
+
MONTH_6 = 'MONTH_6',
|
|
51
|
+
YEAR_1 = 'YEAR_1',
|
|
52
|
+
YEAR_5 = 'YEAR_5',
|
|
36
53
|
}
|
|
37
54
|
|
|
38
55
|
export interface OrdersPositionsData {
|