@oanda/labs-crowd-view-widget 1.0.19 → 1.0.21
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 +172 -0
- package/dist/main/CrowdViewWidget/Main.js +4 -1
- package/dist/main/CrowdViewWidget/Main.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/Chart.js +45 -0
- package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +67 -0
- package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/constants.js +11 -0
- package/dist/main/CrowdViewWidget/components/Chart/constants.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/getOption.js +142 -0
- package/dist/main/CrowdViewWidget/components/Chart/getOption.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/index.js +28 -0
- package/dist/main/CrowdViewWidget/components/Chart/index.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/types.js +6 -0
- package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils.js +65 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js +3 -6
- package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/index.js +11 -0
- package/dist/main/CrowdViewWidget/components/index.js.map +1 -1
- package/dist/main/CrowdViewWidget/config.js +5 -1
- package/dist/main/CrowdViewWidget/config.js.map +1 -1
- package/dist/main/CrowdViewWidget/types.js.map +1 -1
- package/dist/main/gql/getOrderPositionBooks.js +30 -0
- package/dist/main/gql/getOrderPositionBooks.js.map +1 -0
- package/dist/main/gql/types/gql.js +1 -0
- package/dist/main/gql/types/gql.js.map +1 -1
- package/dist/main/gql/types/graphql.js +172 -1
- package/dist/main/gql/types/graphql.js.map +1 -1
- package/dist/module/CrowdViewWidget/Main.js +6 -3
- package/dist/module/CrowdViewWidget/Main.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/Chart.js +37 -0
- package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +58 -0
- package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/constants.js +5 -0
- package/dist/module/CrowdViewWidget/components/Chart/constants.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/getOption.js +132 -0
- package/dist/module/CrowdViewWidget/components/Chart/getOption.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/index.js +3 -0
- package/dist/module/CrowdViewWidget/components/Chart/index.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/types.js +2 -0
- package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils.js +57 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js +2 -5
- package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/index.js +1 -0
- package/dist/module/CrowdViewWidget/components/index.js.map +1 -1
- package/dist/module/CrowdViewWidget/config.js +4 -0
- package/dist/module/CrowdViewWidget/config.js.map +1 -1
- package/dist/module/CrowdViewWidget/types.js.map +1 -1
- package/dist/module/gql/getOrderPositionBooks.js +25 -0
- package/dist/module/gql/getOrderPositionBooks.js.map +1 -0
- package/dist/module/gql/types/gql.js +1 -0
- package/dist/module/gql/types/gql.js.map +1 -1
- package/dist/module/gql/types/graphql.js +171 -0
- package/dist/module/gql/types/graphql.js.map +1 -1
- package/dist/types/CrowdViewWidget/components/Chart/Chart.d.ts +4 -0
- package/dist/types/CrowdViewWidget/components/Chart/ChartWithData.d.ts +4 -0
- package/dist/types/CrowdViewWidget/components/Chart/constants.d.ts +4 -0
- package/dist/types/CrowdViewWidget/components/Chart/getOption.d.ts +115 -0
- package/dist/types/CrowdViewWidget/components/Chart/index.d.ts +2 -0
- package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +25 -0
- package/dist/types/CrowdViewWidget/components/Chart/utils.d.ts +3 -0
- package/dist/types/CrowdViewWidget/components/Legend/LegendBar.d.ts +1 -1
- package/dist/types/CrowdViewWidget/components/index.d.ts +1 -0
- package/dist/types/CrowdViewWidget/config.d.ts +2 -0
- package/dist/types/CrowdViewWidget/types.d.ts +10 -0
- package/dist/types/gql/getOrderPositionBooks.d.ts +2 -0
- package/dist/types/gql/types/gql.d.ts +9 -0
- package/dist/types/gql/types/graphql.d.ts +40 -0
- package/package.json +3 -3
- package/src/CrowdViewWidget/Main.tsx +4 -3
- package/src/CrowdViewWidget/components/Chart/Chart.tsx +59 -0
- package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +88 -0
- package/src/CrowdViewWidget/components/Chart/constants.tsx +4 -0
- package/src/CrowdViewWidget/components/Chart/getOption.ts +159 -0
- package/src/CrowdViewWidget/components/Chart/index.ts +2 -0
- package/src/CrowdViewWidget/components/Chart/types.ts +36 -0
- package/src/CrowdViewWidget/components/Chart/utils.ts +77 -0
- package/src/CrowdViewWidget/components/Legend/LegendBar.tsx +4 -10
- package/src/CrowdViewWidget/components/index.ts +1 -0
- package/src/CrowdViewWidget/config.ts +5 -0
- package/src/CrowdViewWidget/types.ts +11 -0
- package/src/gql/getOrderPositionBooks.ts +26 -0
- package/src/gql/types/gql.ts +8 -0
- package/src/gql/types/graphql.ts +156 -0
- package/test/Main.test.tsx +55 -4
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { BaseChartRef } from '@oanda/labs-widget-common';
|
|
2
|
+
import {
|
|
3
|
+
BaseChart,
|
|
4
|
+
getChartTheme,
|
|
5
|
+
Theme,
|
|
6
|
+
useLayoutProvider,
|
|
7
|
+
} from '@oanda/labs-widget-common';
|
|
8
|
+
import { BarChart, CustomChart } from 'echarts/charts';
|
|
9
|
+
import {
|
|
10
|
+
DataZoomInsideComponent,
|
|
11
|
+
GraphicComponent,
|
|
12
|
+
GridSimpleComponent,
|
|
13
|
+
TooltipComponent,
|
|
14
|
+
} from 'echarts/components';
|
|
15
|
+
import * as echarts from 'echarts/core';
|
|
16
|
+
import { CanvasRenderer } from 'echarts/renderers';
|
|
17
|
+
import React, { useEffect, useRef } from 'react';
|
|
18
|
+
|
|
19
|
+
import { CHART_HEIGHT } from './constants';
|
|
20
|
+
import { getDesktopOption, getOption } from './getOption';
|
|
21
|
+
import type { ChartProps } from './types';
|
|
22
|
+
|
|
23
|
+
echarts.use([
|
|
24
|
+
GridSimpleComponent,
|
|
25
|
+
GraphicComponent,
|
|
26
|
+
BarChart,
|
|
27
|
+
CanvasRenderer,
|
|
28
|
+
DataZoomInsideComponent,
|
|
29
|
+
CustomChart,
|
|
30
|
+
TooltipComponent,
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
echarts.registerTheme('dark_theme', getChartTheme(Theme.Dark));
|
|
34
|
+
echarts.registerTheme('light_theme', getChartTheme(Theme.Light));
|
|
35
|
+
|
|
36
|
+
const Chart = ({ data }: ChartProps) => {
|
|
37
|
+
const { isDark } = useLayoutProvider();
|
|
38
|
+
const echartRef = useRef<BaseChartRef | null>(null);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (echartRef.current) {
|
|
42
|
+
const echartInstance = echartRef.current.getEchartsInstance();
|
|
43
|
+
|
|
44
|
+
echartInstance.setOption(getDesktopOption({ isDark }));
|
|
45
|
+
}
|
|
46
|
+
}, [echartRef, isDark]);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<BaseChart
|
|
50
|
+
ref={echartRef}
|
|
51
|
+
chartHeight={CHART_HEIGHT}
|
|
52
|
+
echarts={echarts}
|
|
53
|
+
isDark={isDark}
|
|
54
|
+
option={getOption(data)}
|
|
55
|
+
/>
|
|
56
|
+
);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export { Chart };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { useQuery } from '@apollo/client';
|
|
2
|
+
import type {
|
|
3
|
+
GetOrderPositionBooksQuery,
|
|
4
|
+
GetOrderPositionBooksQueryVariables,
|
|
5
|
+
} from '@oanda/labs-order-book-widget/src/gql/types/graphql';
|
|
6
|
+
import {
|
|
7
|
+
ChartError,
|
|
8
|
+
Size,
|
|
9
|
+
Spinner,
|
|
10
|
+
SpinnerSize,
|
|
11
|
+
useLayoutProvider,
|
|
12
|
+
} from '@oanda/labs-widget-common';
|
|
13
|
+
import classnames from 'classnames';
|
|
14
|
+
import React, { useMemo } from 'react';
|
|
15
|
+
|
|
16
|
+
import { getOrderPositionBooks } from '../../../gql/getOrderPositionBooks';
|
|
17
|
+
import { Chart } from './Chart';
|
|
18
|
+
import type { ChartWithDataProps } from './types';
|
|
19
|
+
import { transformDataForChart } from './utils';
|
|
20
|
+
|
|
21
|
+
const ChartWithData = ({ instrument, bookType }: ChartWithDataProps) => {
|
|
22
|
+
const { size } = useLayoutProvider();
|
|
23
|
+
const isDesktop = size === Size.DESKTOP;
|
|
24
|
+
|
|
25
|
+
const { loading, data, error } = useQuery<
|
|
26
|
+
GetOrderPositionBooksQuery,
|
|
27
|
+
GetOrderPositionBooksQueryVariables
|
|
28
|
+
>(getOrderPositionBooks, {
|
|
29
|
+
variables: {
|
|
30
|
+
instrument,
|
|
31
|
+
bookType,
|
|
32
|
+
recentHours: 1,
|
|
33
|
+
},
|
|
34
|
+
fetchPolicy: 'network-only',
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const isError = !!error;
|
|
38
|
+
|
|
39
|
+
const transformedData = useMemo(() => {
|
|
40
|
+
if (!data) return undefined;
|
|
41
|
+
return transformDataForChart(data);
|
|
42
|
+
}, [data]);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<>
|
|
46
|
+
<div
|
|
47
|
+
className={classnames('lw-relative lw-w-full', {
|
|
48
|
+
'lw-h-[450px]': isDesktop,
|
|
49
|
+
'lw-h-[390px]': !isDesktop,
|
|
50
|
+
})}
|
|
51
|
+
>
|
|
52
|
+
{isError && (
|
|
53
|
+
<div
|
|
54
|
+
className={classnames(
|
|
55
|
+
'lw-absolute lw-left-0 lw-top-0 lw-flex lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary',
|
|
56
|
+
{
|
|
57
|
+
'lw-h-full': isDesktop,
|
|
58
|
+
'lw-h-[calc(100%-40px)]': !isDesktop,
|
|
59
|
+
}
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
<ChartError />
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
{loading && (
|
|
66
|
+
<div
|
|
67
|
+
className={classnames(
|
|
68
|
+
'lw-absolute lw-left-0 lw-top-0 lw-flex lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary',
|
|
69
|
+
{
|
|
70
|
+
'lw-h-full': isDesktop,
|
|
71
|
+
'lw-h-[calc(100%-40px)]': !isDesktop,
|
|
72
|
+
}
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
<Spinner size={SpinnerSize.lg} />
|
|
76
|
+
</div>
|
|
77
|
+
)}
|
|
78
|
+
{!isError && transformedData && (
|
|
79
|
+
<div className="lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full">
|
|
80
|
+
<Chart data={transformedData} />
|
|
81
|
+
</div>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
</>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export { ChartWithData };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { getGridLines, getLineCommons } from '@oanda/labs-widget-common';
|
|
2
|
+
import * as echarts from 'echarts/core';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
CHART_HEIGHT,
|
|
6
|
+
CHART_WIDTH,
|
|
7
|
+
X_LABEL_SIZE,
|
|
8
|
+
Y_LABEL_SIZE_DESKTOP,
|
|
9
|
+
} from './constants';
|
|
10
|
+
import type { GetOptionType, GetResponsiveOptionsProps } from './types';
|
|
11
|
+
import { getRectColor } from './utils';
|
|
12
|
+
|
|
13
|
+
export const getDesktopOption = ({ isDark }: GetResponsiveOptionsProps) => {
|
|
14
|
+
const desktopGridLines = getGridLines({
|
|
15
|
+
isDark,
|
|
16
|
+
chartWidth: CHART_WIDTH,
|
|
17
|
+
chartHeight: CHART_HEIGHT,
|
|
18
|
+
xLabelsSize: X_LABEL_SIZE,
|
|
19
|
+
yLabelSize: Y_LABEL_SIZE_DESKTOP,
|
|
20
|
+
bottomLeftBox: false,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
yAxis: {
|
|
25
|
+
nameTextStyle: {
|
|
26
|
+
align: 'left',
|
|
27
|
+
padding: [0, 0, 0, 8],
|
|
28
|
+
},
|
|
29
|
+
axisLabel: {
|
|
30
|
+
margin: 8,
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
grid: [
|
|
34
|
+
{
|
|
35
|
+
name: 'main-grid',
|
|
36
|
+
top: '0px',
|
|
37
|
+
left: '0px',
|
|
38
|
+
right: `${Y_LABEL_SIZE_DESKTOP}px`,
|
|
39
|
+
bottom: `${X_LABEL_SIZE}px`,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
graphic: [
|
|
43
|
+
...desktopGridLines,
|
|
44
|
+
{
|
|
45
|
+
...getLineCommons(isDark),
|
|
46
|
+
top: 0,
|
|
47
|
+
right: 0,
|
|
48
|
+
shape: {
|
|
49
|
+
x1: 0,
|
|
50
|
+
y1: 0,
|
|
51
|
+
x2: 0,
|
|
52
|
+
y2: 0,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const getOption: GetOptionType = ({
|
|
60
|
+
xAxisData,
|
|
61
|
+
ordersPositionsChartData,
|
|
62
|
+
ordersPositionsBucketWidth,
|
|
63
|
+
}) => {
|
|
64
|
+
return {
|
|
65
|
+
animation: false,
|
|
66
|
+
dataZoom: [
|
|
67
|
+
{
|
|
68
|
+
type: 'inside',
|
|
69
|
+
xAxisIndex: 0,
|
|
70
|
+
start: 80,
|
|
71
|
+
end: 100,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'inside',
|
|
75
|
+
yAxisIndex: 0,
|
|
76
|
+
startValue: 1.5,
|
|
77
|
+
endValue: 1.7,
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
tooltip: {
|
|
81
|
+
trigger: 'axis',
|
|
82
|
+
formatter: '{c}',
|
|
83
|
+
},
|
|
84
|
+
xAxis: {
|
|
85
|
+
type: 'category',
|
|
86
|
+
data: xAxisData,
|
|
87
|
+
axisTick: {
|
|
88
|
+
show: false,
|
|
89
|
+
},
|
|
90
|
+
axisLabel: {
|
|
91
|
+
padding: [8, 16, 8, 16],
|
|
92
|
+
margin: 0,
|
|
93
|
+
formatter: (value) =>
|
|
94
|
+
`${new Date(value).toLocaleTimeString(undefined, {
|
|
95
|
+
hour: '2-digit',
|
|
96
|
+
minute: '2-digit',
|
|
97
|
+
})}`,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
yAxis: {
|
|
101
|
+
type: 'value',
|
|
102
|
+
position: 'right',
|
|
103
|
+
axisLine: { show: false },
|
|
104
|
+
axisTick: { show: false },
|
|
105
|
+
axisLabel: {
|
|
106
|
+
showMaxLabel: false,
|
|
107
|
+
showMinLabel: false,
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
series: [
|
|
111
|
+
{
|
|
112
|
+
type: 'custom',
|
|
113
|
+
name: 'heatmap',
|
|
114
|
+
silent: true,
|
|
115
|
+
renderItem: (params, api) => {
|
|
116
|
+
const { coordSys } = params;
|
|
117
|
+
|
|
118
|
+
const xVal = api.value(0);
|
|
119
|
+
const yVal = api.value(1);
|
|
120
|
+
const sentiment = api.value(2) as number;
|
|
121
|
+
const start = api.coord([xVal, yVal]);
|
|
122
|
+
|
|
123
|
+
const [rectWidth, rectHeight] = api.size!([
|
|
124
|
+
0,
|
|
125
|
+
ordersPositionsBucketWidth,
|
|
126
|
+
]) as number[];
|
|
127
|
+
|
|
128
|
+
const boundaries = coordSys as unknown as {
|
|
129
|
+
x: number;
|
|
130
|
+
y: number;
|
|
131
|
+
width: number;
|
|
132
|
+
height: number;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const shape = echarts.graphic.clipRectByRect(
|
|
136
|
+
{
|
|
137
|
+
x: start[0] - rectWidth / 2,
|
|
138
|
+
y: start[1],
|
|
139
|
+
width: rectWidth,
|
|
140
|
+
height: rectHeight,
|
|
141
|
+
},
|
|
142
|
+
boundaries
|
|
143
|
+
);
|
|
144
|
+
return (
|
|
145
|
+
shape && {
|
|
146
|
+
type: 'rect',
|
|
147
|
+
shape,
|
|
148
|
+
style: {
|
|
149
|
+
fill: getRectColor(sentiment),
|
|
150
|
+
},
|
|
151
|
+
emphasisDisabled: true,
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
},
|
|
155
|
+
data: ordersPositionsChartData,
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { GetOrderPositionBooksQuery } from '@oanda/labs-order-book-widget/src/gql/types/graphql';
|
|
2
|
+
import type { EChartsOption } from 'echarts';
|
|
3
|
+
|
|
4
|
+
import type { BookType } from '../../../gql/types/graphql';
|
|
5
|
+
|
|
6
|
+
interface ChartOptionsProps {
|
|
7
|
+
xAxisData: string[];
|
|
8
|
+
ordersPositionsChartData: [
|
|
9
|
+
// timestamp
|
|
10
|
+
string,
|
|
11
|
+
// price
|
|
12
|
+
number,
|
|
13
|
+
// orders-positions sentiment
|
|
14
|
+
number,
|
|
15
|
+
][];
|
|
16
|
+
ordersPositionsBucketWidth: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type GetOptionType = (props: ChartOptionsProps) => EChartsOption;
|
|
20
|
+
|
|
21
|
+
export interface GetResponsiveOptionsProps {
|
|
22
|
+
isDark: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ChartProps {
|
|
26
|
+
data: ChartOptionsProps;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ChartWithDataProps {
|
|
30
|
+
bookType: BookType;
|
|
31
|
+
instrument: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type TransformDataForChartType = (
|
|
35
|
+
props: GetOrderPositionBooksQuery
|
|
36
|
+
) => ChartOptionsProps;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { GetOrderPositionBooksQuery } from '../../../gql/types/graphql';
|
|
2
|
+
import { colorMap } from '../../config';
|
|
3
|
+
import type { TransformDataForChartType } from './types';
|
|
4
|
+
|
|
5
|
+
export const getRectColor = (sentiment: number) => {
|
|
6
|
+
const colors = sentiment < 0 ? colorMap.short : colorMap.long;
|
|
7
|
+
const absSentiment = Math.abs(sentiment);
|
|
8
|
+
|
|
9
|
+
if (absSentiment < 0.15) {
|
|
10
|
+
return colors[3];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (absSentiment < 0.25) {
|
|
14
|
+
return colors[2];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (absSentiment < 0.4) {
|
|
18
|
+
return colors[1];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (absSentiment >= 0.4) {
|
|
22
|
+
return colors[0];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return 'transparent';
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const mockOrderPositionDataForHistoricalData = (
|
|
29
|
+
data: GetOrderPositionBooksQuery
|
|
30
|
+
) => {
|
|
31
|
+
const oneMinuteInMilliseconds = 60000;
|
|
32
|
+
const granularity = oneMinuteInMilliseconds * 5; // 5min
|
|
33
|
+
const timeSpan = oneMinuteInMilliseconds * 60 * 24; // 24h
|
|
34
|
+
// const timeSpan = oneMinuteInMilliseconds * 60 * 6 // 6h
|
|
35
|
+
// const timeSpan = oneMinuteInMilliseconds * 15 //15min
|
|
36
|
+
|
|
37
|
+
const currentTime = data.orderPositionBooks[0]!.time;
|
|
38
|
+
|
|
39
|
+
const startDate = new Date(currentTime as string);
|
|
40
|
+
const totalSteps = timeSpan / granularity;
|
|
41
|
+
|
|
42
|
+
const orderPositionDataForHistorical = new Array(totalSteps)
|
|
43
|
+
.fill(null)
|
|
44
|
+
.map((_, index) => {
|
|
45
|
+
const newDate = new Date(startDate.getTime() - index * granularity);
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
time: `${newDate.toISOString().slice(0, 19)}Z`,
|
|
49
|
+
buckets: data.orderPositionBooks[0]!.buckets,
|
|
50
|
+
bucketWidth: data.orderPositionBooks[0]!.bucketWidth,
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return orderPositionDataForHistorical;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const transformDataForChart: TransformDataForChartType = (data) => {
|
|
58
|
+
const mockedData = mockOrderPositionDataForHistoricalData(data);
|
|
59
|
+
|
|
60
|
+
const xAxisData = mockedData.map(({ time }) => time);
|
|
61
|
+
const ordersPositionsChartData = mockedData
|
|
62
|
+
.flatMap(
|
|
63
|
+
({ buckets, time }) =>
|
|
64
|
+
buckets.map((bucket) => [
|
|
65
|
+
time,
|
|
66
|
+
bucket!.price,
|
|
67
|
+
bucket!.longCountPercent - bucket!.shortCountPercent,
|
|
68
|
+
]) as [string, number, number][]
|
|
69
|
+
)
|
|
70
|
+
.filter((item) => Math.abs(item[2]) > 0.15);
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
xAxisData,
|
|
74
|
+
ordersPositionsChartData,
|
|
75
|
+
ordersPositionsBucketWidth: mockedData[0]!.bucketWidth,
|
|
76
|
+
};
|
|
77
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import { colorMap } from '../../config';
|
|
4
|
+
|
|
5
|
+
export type LegendType = 'long' | 'short';
|
|
4
6
|
|
|
5
7
|
interface LegendBarProps {
|
|
6
8
|
values: number[];
|
|
@@ -8,11 +10,6 @@ interface LegendBarProps {
|
|
|
8
10
|
label: string;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
const colorMap: Record<LegendType, string[]> = {
|
|
12
|
-
long: ['#00857333', '#00715F99', '#00857399', '#008573CC'],
|
|
13
|
-
short: ['#DA632566', '#DA632599', '#DA6325CC', '#DA6325'],
|
|
14
|
-
};
|
|
15
|
-
|
|
16
13
|
export const LegendBar = ({ values, type, label }: LegendBarProps) => {
|
|
17
14
|
const colors = colorMap[type];
|
|
18
15
|
|
|
@@ -33,10 +30,7 @@ export const LegendBar = ({ values, type, label }: LegendBarProps) => {
|
|
|
33
30
|
|
|
34
31
|
<div className="lw-flex lw-w-full">
|
|
35
32
|
{values.map((value) => (
|
|
36
|
-
<div
|
|
37
|
-
key={`${value}_${type}`}
|
|
38
|
-
className="lw-flex lw-w-1/4 lw-justify-center"
|
|
39
|
-
>
|
|
33
|
+
<div key={`${value}_${type}`} className="lw-flex lw-w-1/4">
|
|
40
34
|
<span className="lw-whitespace-nowrap lw-font-sans lw-text-xs lw-text-text-primary">
|
|
41
35
|
{value.toFixed(2)}% {label}
|
|
42
36
|
</span>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BookType } from '../gql/types/graphql';
|
|
2
|
+
import type { LegendType } from './components/Legend/LegendBar';
|
|
2
3
|
import { GranularityId, InstrumentId } from './types';
|
|
3
4
|
|
|
4
5
|
const navigationConfig = [
|
|
@@ -128,3 +129,7 @@ export {
|
|
|
128
129
|
instrumentSelectConfigOC,
|
|
129
130
|
navigationConfig,
|
|
130
131
|
};
|
|
132
|
+
export const colorMap: Record<LegendType, string[]> = {
|
|
133
|
+
long: ['#00857333', '#00715F99', '#00857399', '#008573CC'],
|
|
134
|
+
short: ['#DA632566', '#DA632599', '#DA6325CC', '#DA6325'],
|
|
135
|
+
};
|
|
@@ -34,3 +34,14 @@ export enum GranularityId {
|
|
|
34
34
|
H1 = '1H',
|
|
35
35
|
D1 = '1D',
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
export interface OrdersPositionsData {
|
|
39
|
+
price: number;
|
|
40
|
+
buckets: {
|
|
41
|
+
price: number;
|
|
42
|
+
longCountPercent: number;
|
|
43
|
+
shortCountPercent: number;
|
|
44
|
+
}[];
|
|
45
|
+
bucketWidth: number;
|
|
46
|
+
timestamp: number;
|
|
47
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { gql } from '@apollo/client';
|
|
2
|
+
|
|
3
|
+
const getOrderPositionBooks = gql`
|
|
4
|
+
query GetOrderPositionBooks(
|
|
5
|
+
$instrument: String!
|
|
6
|
+
$bookType: BookType!
|
|
7
|
+
$recentHours: Int
|
|
8
|
+
) {
|
|
9
|
+
orderPositionBooks(
|
|
10
|
+
instrument: $instrument
|
|
11
|
+
bookType: $bookType
|
|
12
|
+
recentHours: $recentHours
|
|
13
|
+
) {
|
|
14
|
+
bucketWidth
|
|
15
|
+
price
|
|
16
|
+
time
|
|
17
|
+
buckets {
|
|
18
|
+
price
|
|
19
|
+
longCountPercent
|
|
20
|
+
shortCountPercent
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
export { getOrderPositionBooks };
|
package/src/gql/types/gql.ts
CHANGED
|
@@ -13,6 +13,8 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
|
|
|
13
13
|
* Therefore it is highly recommended to use the babel or swc plugin for production.
|
|
14
14
|
*/
|
|
15
15
|
const documents = {
|
|
16
|
+
'\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $recentHours: Int\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n recentHours: $recentHours\n ) {\n bucketWidth\n price\n time\n buckets {\n price\n longCountPercent\n shortCountPercent\n }\n }\n }\n':
|
|
17
|
+
types.GetOrderPositionBooksDocument,
|
|
16
18
|
'\n query validateInstruments($instruments: [String]!, $division: Division) {\n mapInstrumentNames(instruments: $instruments, division: $division) {\n name\n displayName\n }\n }\n':
|
|
17
19
|
types.ValidateInstrumentsDocument,
|
|
18
20
|
};
|
|
@@ -31,6 +33,12 @@ const documents = {
|
|
|
31
33
|
*/
|
|
32
34
|
export function graphql(source: string): unknown;
|
|
33
35
|
|
|
36
|
+
/**
|
|
37
|
+
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
38
|
+
*/
|
|
39
|
+
export function graphql(
|
|
40
|
+
source: '\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $recentHours: Int\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n recentHours: $recentHours\n ) {\n bucketWidth\n price\n time\n buckets {\n price\n longCountPercent\n shortCountPercent\n }\n }\n }\n'
|
|
41
|
+
): (typeof documents)['\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $recentHours: Int\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n recentHours: $recentHours\n ) {\n bucketWidth\n price\n time\n buckets {\n price\n longCountPercent\n shortCountPercent\n }\n }\n }\n'];
|
|
34
42
|
/**
|
|
35
43
|
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
|
|
36
44
|
*/
|