@oanda/labs-crowd-view-widget 1.0.43 → 1.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/CHANGELOG.md +180 -0
  2. package/dist/main/CrowdViewWidget/CrowdViewWidget.js +3 -3
  3. package/dist/main/CrowdViewWidget/CrowdViewWidget.js.map +1 -1
  4. package/dist/main/CrowdViewWidget/Main.js +8 -9
  5. package/dist/main/CrowdViewWidget/Main.js.map +1 -1
  6. package/dist/main/CrowdViewWidget/components/Chart/Chart.js +11 -8
  7. package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  8. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +16 -13
  9. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  10. package/dist/main/CrowdViewWidget/components/Chart/{getOption.js → chartOptions.js} +53 -51
  11. package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -0
  12. package/dist/main/CrowdViewWidget/components/Chart/index.js +44 -0
  13. package/dist/main/CrowdViewWidget/components/Chart/index.js.map +1 -1
  14. package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
  15. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js +170 -0
  16. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -0
  17. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js +70 -0
  18. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -0
  19. package/dist/main/CrowdViewWidget/components/Legend/Legend.js +3 -2
  20. package/dist/main/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  21. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js +9 -11
  22. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  23. package/dist/main/CrowdViewWidget/constants.js +30 -0
  24. package/dist/main/CrowdViewWidget/constants.js.map +1 -0
  25. package/dist/main/CrowdViewWidget/render.js +1 -0
  26. package/dist/main/CrowdViewWidget/render.js.map +1 -1
  27. package/dist/main/CrowdViewWidget/selectConfig.js +121 -0
  28. package/dist/main/CrowdViewWidget/selectConfig.js.map +1 -0
  29. package/dist/main/CrowdViewWidget/types/index.js +17 -0
  30. package/dist/main/CrowdViewWidget/types/index.js.map +1 -0
  31. package/dist/main/CrowdViewWidget/types/instruments.js +45 -0
  32. package/dist/main/CrowdViewWidget/types/instruments.js.map +1 -0
  33. package/dist/main/CrowdViewWidget/types.js +0 -44
  34. package/dist/main/CrowdViewWidget/types.js.map +1 -1
  35. package/dist/main/CrowdViewWidget/utils/instrumentUtils.js +13 -0
  36. package/dist/main/CrowdViewWidget/utils/instrumentUtils.js.map +1 -0
  37. package/dist/main/gql/getOrderPositionBooks.js +1 -1
  38. package/dist/main/gql/getOrderPositionBooks.js.map +1 -1
  39. package/dist/main/gql/getPriceCandles.js +11 -0
  40. package/dist/main/gql/getPriceCandles.js.map +1 -0
  41. package/dist/main/gql/types/gql.js +2 -3
  42. package/dist/main/gql/types/gql.js.map +1 -1
  43. package/dist/main/gql/types/graphql.js +161 -160
  44. package/dist/main/gql/types/graphql.js.map +1 -1
  45. package/dist/module/CrowdViewWidget/CrowdViewWidget.js +3 -3
  46. package/dist/module/CrowdViewWidget/CrowdViewWidget.js.map +1 -1
  47. package/dist/module/CrowdViewWidget/Main.js +6 -7
  48. package/dist/module/CrowdViewWidget/Main.js.map +1 -1
  49. package/dist/module/CrowdViewWidget/components/Chart/Chart.js +9 -6
  50. package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  51. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +16 -12
  52. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  53. package/dist/module/CrowdViewWidget/components/Chart/{getOption.js → chartOptions.js} +51 -48
  54. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -0
  55. package/dist/module/CrowdViewWidget/components/Chart/index.js +4 -0
  56. package/dist/module/CrowdViewWidget/components/Chart/index.js.map +1 -1
  57. package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
  58. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +163 -0
  59. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -0
  60. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js +59 -0
  61. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -0
  62. package/dist/module/CrowdViewWidget/components/Legend/Legend.js +3 -2
  63. package/dist/module/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  64. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js +9 -11
  65. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  66. package/dist/module/CrowdViewWidget/constants.js +24 -0
  67. package/dist/module/CrowdViewWidget/constants.js.map +1 -0
  68. package/dist/module/CrowdViewWidget/render.js +1 -0
  69. package/dist/module/CrowdViewWidget/render.js.map +1 -1
  70. package/dist/module/CrowdViewWidget/selectConfig.js +116 -0
  71. package/dist/module/CrowdViewWidget/selectConfig.js.map +1 -0
  72. package/dist/module/CrowdViewWidget/types/index.js +2 -0
  73. package/dist/module/CrowdViewWidget/types/index.js.map +1 -0
  74. package/dist/module/CrowdViewWidget/types/instruments.js +39 -0
  75. package/dist/module/CrowdViewWidget/types/instruments.js.map +1 -0
  76. package/dist/module/CrowdViewWidget/types.js +1 -43
  77. package/dist/module/CrowdViewWidget/types.js.map +1 -1
  78. package/dist/module/CrowdViewWidget/utils/instrumentUtils.js +6 -0
  79. package/dist/module/CrowdViewWidget/utils/instrumentUtils.js.map +1 -0
  80. package/dist/module/gql/getOrderPositionBooks.js +1 -1
  81. package/dist/module/gql/getOrderPositionBooks.js.map +1 -1
  82. package/dist/module/gql/getPriceCandles.js +6 -0
  83. package/dist/module/gql/getPriceCandles.js.map +1 -0
  84. package/dist/module/gql/types/gql.js +2 -3
  85. package/dist/module/gql/types/gql.js.map +1 -1
  86. package/dist/module/gql/types/graphql.js +160 -159
  87. package/dist/module/gql/types/graphql.js.map +1 -1
  88. package/dist/types/CrowdViewWidget/CrowdViewWidget.d.ts +1 -1
  89. package/dist/types/CrowdViewWidget/components/Chart/ChartWithData.d.ts +1 -1
  90. package/dist/types/CrowdViewWidget/components/Chart/index.d.ts +4 -0
  91. package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +19 -16
  92. package/dist/types/CrowdViewWidget/components/Chart/useCrowdViewData.d.ts +2 -0
  93. package/dist/types/CrowdViewWidget/components/Chart/{utils.d.ts → utils/chartUtils.d.ts} +4 -11
  94. package/dist/types/CrowdViewWidget/components/Legend/Legend.d.ts +3 -3
  95. package/dist/types/CrowdViewWidget/constants.d.ts +23 -0
  96. package/dist/types/CrowdViewWidget/selectConfig.d.ts +19 -0
  97. package/dist/types/CrowdViewWidget/types/index.d.ts +1 -0
  98. package/dist/types/CrowdViewWidget/types/instruments.d.ts +36 -0
  99. package/dist/types/CrowdViewWidget/types.d.ts +2 -50
  100. package/dist/types/CrowdViewWidget/utils/instrumentUtils.d.ts +8 -0
  101. package/dist/types/gql/types/gql.d.ts +10 -14
  102. package/dist/types/gql/types/graphql.d.ts +71 -63
  103. package/package.json +6 -4
  104. package/src/CrowdViewWidget/CrowdViewWidget.tsx +2 -2
  105. package/src/CrowdViewWidget/Main.tsx +10 -16
  106. package/src/CrowdViewWidget/components/Chart/Chart.tsx +15 -5
  107. package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +12 -12
  108. package/src/CrowdViewWidget/components/Chart/{getOption.ts → chartOptions.ts} +51 -70
  109. package/src/CrowdViewWidget/components/Chart/index.ts +4 -0
  110. package/src/CrowdViewWidget/components/Chart/types.ts +25 -23
  111. package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +263 -0
  112. package/src/CrowdViewWidget/components/Chart/utils/chartUtils.ts +93 -0
  113. package/src/CrowdViewWidget/components/Legend/Legend.tsx +7 -3
  114. package/src/CrowdViewWidget/components/Legend/LegendBar.tsx +16 -20
  115. package/src/CrowdViewWidget/constants.ts +27 -0
  116. package/src/CrowdViewWidget/render.tsx +1 -0
  117. package/src/CrowdViewWidget/{config.ts → selectConfig.ts} +65 -43
  118. package/src/CrowdViewWidget/types/index.ts +1 -0
  119. package/src/CrowdViewWidget/types/instruments.ts +37 -0
  120. package/src/CrowdViewWidget/types.ts +4 -55
  121. package/src/CrowdViewWidget/utils/instrumentUtils.ts +11 -0
  122. package/src/gql/getOrderPositionBooks.ts +9 -4
  123. package/src/gql/{mock/getPriceCandles.ts → getPriceCandles.ts} +5 -5
  124. package/src/gql/types/gql.ts +6 -14
  125. package/src/gql/types/graphql.ts +170 -160
  126. package/test/Main.test.tsx +1 -1
  127. package/test/components/Legend.test.tsx +3 -6
  128. package/test/components/LegendBar.test.tsx +7 -8
  129. package/test/utils/instrumentUtils.test.ts +52 -0
  130. package/dist/main/CrowdViewWidget/components/Chart/constants.js +0 -14
  131. package/dist/main/CrowdViewWidget/components/Chart/constants.js.map +0 -1
  132. package/dist/main/CrowdViewWidget/components/Chart/getOption.js.map +0 -1
  133. package/dist/main/CrowdViewWidget/components/Chart/getOrderPositionDataMock.js +0 -47
  134. package/dist/main/CrowdViewWidget/components/Chart/getOrderPositionDataMock.js.map +0 -1
  135. package/dist/main/CrowdViewWidget/components/Chart/getPriceCandlesMock.js +0 -36
  136. package/dist/main/CrowdViewWidget/components/Chart/getPriceCandlesMock.js.map +0 -1
  137. package/dist/main/CrowdViewWidget/components/Chart/utils.js +0 -166
  138. package/dist/main/CrowdViewWidget/components/Chart/utils.js.map +0 -1
  139. package/dist/main/CrowdViewWidget/config.js +0 -107
  140. package/dist/main/CrowdViewWidget/config.js.map +0 -1
  141. package/dist/main/gql/mock/getPriceCandles.js +0 -11
  142. package/dist/main/gql/mock/getPriceCandles.js.map +0 -1
  143. package/dist/main/gql/mock/schema.graphqls +0 -62
  144. package/dist/main/gql/validateInstruments.js +0 -11
  145. package/dist/main/gql/validateInstruments.js.map +0 -1
  146. package/dist/module/CrowdViewWidget/components/Chart/constants.js +0 -8
  147. package/dist/module/CrowdViewWidget/components/Chart/constants.js.map +0 -1
  148. package/dist/module/CrowdViewWidget/components/Chart/getOption.js.map +0 -1
  149. package/dist/module/CrowdViewWidget/components/Chart/getOrderPositionDataMock.js +0 -40
  150. package/dist/module/CrowdViewWidget/components/Chart/getOrderPositionDataMock.js.map +0 -1
  151. package/dist/module/CrowdViewWidget/components/Chart/getPriceCandlesMock.js +0 -29
  152. package/dist/module/CrowdViewWidget/components/Chart/getPriceCandlesMock.js.map +0 -1
  153. package/dist/module/CrowdViewWidget/components/Chart/utils.js +0 -156
  154. package/dist/module/CrowdViewWidget/components/Chart/utils.js.map +0 -1
  155. package/dist/module/CrowdViewWidget/config.js +0 -102
  156. package/dist/module/CrowdViewWidget/config.js.map +0 -1
  157. package/dist/module/gql/mock/getPriceCandles.js +0 -6
  158. package/dist/module/gql/mock/getPriceCandles.js.map +0 -1
  159. package/dist/module/gql/mock/schema.graphqls +0 -62
  160. package/dist/module/gql/validateInstruments.js +0 -5
  161. package/dist/module/gql/validateInstruments.js.map +0 -1
  162. package/dist/types/CrowdViewWidget/components/Chart/constants.d.ts +0 -7
  163. package/dist/types/CrowdViewWidget/components/Chart/getOrderPositionDataMock.d.ts +0 -14
  164. package/dist/types/CrowdViewWidget/components/Chart/getPriceCandlesMock.d.ts +0 -2
  165. package/dist/types/CrowdViewWidget/config.d.ts +0 -22
  166. package/dist/types/gql/validateInstruments.d.ts +0 -1
  167. package/src/CrowdViewWidget/components/Chart/constants.tsx +0 -8
  168. package/src/CrowdViewWidget/components/Chart/getOrderPositionDataMock.ts +0 -66
  169. package/src/CrowdViewWidget/components/Chart/getPriceCandlesMock.ts +0 -43
  170. package/src/CrowdViewWidget/components/Chart/utils.ts +0 -191
  171. package/src/gql/mock/schema.graphqls +0 -62
  172. package/src/gql/validateInstruments.ts +0 -10
  173. /package/dist/types/CrowdViewWidget/components/Chart/{getOption.d.ts → chartOptions.d.ts} +0 -0
  174. /package/dist/types/gql/{mock/getPriceCandles.d.ts → getPriceCandles.d.ts} +0 -0
@@ -5,7 +5,12 @@ import {
5
5
  useLayoutProvider,
6
6
  } from '@oanda/labs-widget-common';
7
7
  import type { EChartsType } from 'echarts';
8
- import { BarChart, CandlestickChart, CustomChart } from 'echarts/charts';
8
+ import {
9
+ BarChart,
10
+ CandlestickChart,
11
+ CustomChart,
12
+ ScatterChart,
13
+ } from 'echarts/charts';
9
14
  import {
10
15
  DataZoomInsideComponent,
11
16
  GraphicComponent,
@@ -17,10 +22,13 @@ import * as echarts from 'echarts/core';
17
22
  import { CanvasRenderer } from 'echarts/renderers';
18
23
  import React from 'react';
19
24
 
20
- import { CHART_HEIGHT } from './constants';
21
- import { getOption } from './getOption';
25
+ import { CHART_CONFIG } from '../../constants';
26
+ import { getOption } from './chartOptions';
22
27
  import type { ChartProps } from './types';
23
- import { getLabelData, isDifferenceGreaterThanTwoWeeks } from './utils';
28
+ import {
29
+ getLabelData,
30
+ isDifferenceGreaterThanTwoWeeks,
31
+ } from './utils/chartUtils';
24
32
 
25
33
  echarts.use([
26
34
  GridSimpleComponent,
@@ -32,6 +40,7 @@ echarts.use([
32
40
  TooltipComponent,
33
41
  CandlestickChart,
34
42
  MarkPointComponent,
43
+ ScatterChart,
35
44
  ]);
36
45
 
37
46
  echarts.registerTheme('dark_theme', getChartTheme(Theme.Dark));
@@ -42,11 +51,12 @@ const Chart = ({ data }: ChartProps) => {
42
51
 
43
52
  return (
44
53
  <BaseChart
45
- chartHeight={CHART_HEIGHT}
54
+ chartHeight={CHART_CONFIG.HEIGHT}
46
55
  echarts={echarts}
47
56
  isDark={isDark}
48
57
  lazyUpdate={true}
49
58
  option={getOption(data, isDark)}
59
+ opts={{ renderer: 'canvas' }}
50
60
  onEvents={{
51
61
  datazoom: (_params: unknown, instance: EChartsType) => {
52
62
  const { dataZoom } = instance.getOption();
@@ -6,27 +6,27 @@ import {
6
6
  useLayoutProvider,
7
7
  } from '@oanda/labs-widget-common';
8
8
  import classnames from 'classnames';
9
- import React, { useMemo } from 'react';
9
+ import React from 'react';
10
10
 
11
11
  import { Chart } from './Chart';
12
12
  import type { ChartWithDataProps } from './types';
13
- import { transformDataForChart } from './utils';
13
+ import { useCrowdViewData } from './useCrowdViewData';
14
14
 
15
15
  const ChartWithData = ({
16
16
  instrument,
17
17
  bookType,
18
+ division,
18
19
  granularity,
19
20
  }: ChartWithDataProps) => {
20
21
  const { size } = useLayoutProvider();
21
22
  const isDesktop = size === Size.DESKTOP;
22
23
 
23
- const isError = false;
24
- const loading = false;
25
-
26
- const transformedData = useMemo(() => {
27
- return transformDataForChart({ granularity });
28
- // eslint-disable-next-line react-hooks/exhaustive-deps
29
- }, [instrument, granularity, bookType]);
24
+ const { data, loading, error } = useCrowdViewData({
25
+ instrument,
26
+ bookType,
27
+ division,
28
+ granularity,
29
+ });
30
30
 
31
31
  return (
32
32
  <>
@@ -36,7 +36,7 @@ const ChartWithData = ({
36
36
  'lw-h-[390px]': !isDesktop,
37
37
  })}
38
38
  >
39
- {isError && (
39
+ {error && (
40
40
  <div
41
41
  className={classnames(
42
42
  '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',
@@ -62,9 +62,9 @@ const ChartWithData = ({
62
62
  <Spinner size={SpinnerSize.lg} />
63
63
  </div>
64
64
  )}
65
- {!isError && transformedData && (
65
+ {!loading && !error && !!data && (
66
66
  <div className="lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full">
67
- <Chart data={transformedData} />
67
+ <Chart data={data} />
68
68
  </div>
69
69
  )}
70
70
  </div>
@@ -3,36 +3,23 @@ import {
3
3
  getGridLines,
4
4
  getLineCommons,
5
5
  } from '@oanda/labs-widget-common';
6
- import * as echarts from 'echarts/core';
7
6
 
8
- import {
9
- CHART_HEIGHT,
10
- CHART_WIDTH,
11
- INITIAL_END_ZOOM,
12
- INITIAL_START_ZOOM,
13
- X_LABEL_SIZE,
14
- Y_LABEL_SIZE_DESKTOP,
15
- } from './constants';
7
+ import { CHART_CONFIG } from '../../constants';
16
8
  import type { GetOptionType } from './types';
17
9
  import {
18
10
  getLabelData,
19
11
  getRectColor,
20
12
  isDifferenceGreaterThanTwoWeeks,
21
- } from './utils';
13
+ } from './utils/chartUtils';
22
14
 
23
15
  // @ts-expect-error
24
16
  export const getOption: GetOptionType = (
25
- {
26
- xAxisData,
27
- ordersPositionsChartData,
28
- ordersPositionsBucketWidth,
29
- candlesSeriesData,
30
- },
17
+ { xAxisData, candlesSeriesData, orderPositionBooks, bucketWidth },
31
18
  isDark
32
19
  ) => {
33
20
  const visibleXAxisData = xAxisData.slice(
34
- (xAxisData.length * INITIAL_START_ZOOM) / 100,
35
- (xAxisData.length * INITIAL_END_ZOOM) / 100
21
+ (xAxisData.length * CHART_CONFIG.INITIAL_START_ZOOM) / 100,
22
+ (xAxisData.length * CHART_CONFIG.INITIAL_END_ZOOM) / 100
36
23
  );
37
24
 
38
25
  const isGreaterThanTwoWeeks = isDifferenceGreaterThanTwoWeeks(
@@ -47,10 +34,10 @@ export const getOption: GetOptionType = (
47
34
 
48
35
  const gridLines = getGridLines({
49
36
  isDark,
50
- chartWidth: CHART_WIDTH,
51
- chartHeight: CHART_HEIGHT,
52
- xLabelsSize: X_LABEL_SIZE,
53
- yLabelSize: Y_LABEL_SIZE_DESKTOP,
37
+ chartWidth: CHART_CONFIG.WIDTH,
38
+ chartHeight: CHART_CONFIG.HEIGHT,
39
+ xLabelsSize: CHART_CONFIG.X_LABEL_SIZE,
40
+ yLabelSize: CHART_CONFIG.Y_LABEL_SIZE_DESKTOP,
54
41
  bottomLeftBox: false,
55
42
  });
56
43
 
@@ -60,25 +47,14 @@ export const getOption: GetOptionType = (
60
47
  {
61
48
  type: 'inside',
62
49
  xAxisIndex: 0,
63
- start: INITIAL_START_ZOOM,
64
- end: INITIAL_END_ZOOM,
50
+ start: CHART_CONFIG.INITIAL_START_ZOOM,
51
+ end: CHART_CONFIG.INITIAL_END_ZOOM,
65
52
  },
66
53
  ],
67
54
  tooltip: {
68
55
  trigger: 'axis',
69
- formatter: (val) => {
70
- // @ts-expect-error
71
- const timestamp = val[0].axisValue as string;
72
-
73
- return `${new Date(timestamp).toLocaleTimeString(undefined, {
74
- hour: '2-digit',
75
- minute: '2-digit',
76
- })}
77
- ${new Date(timestamp).toLocaleDateString(undefined, {
78
- day: 'numeric',
79
- month: 'short',
80
- })}
81
- `;
56
+ axisPointer: {
57
+ type: 'cross',
82
58
  },
83
59
  },
84
60
  xAxis: {
@@ -90,6 +66,7 @@ export const getOption: GetOptionType = (
90
66
  axisLabel: {
91
67
  padding: [8, 16, 8, 16],
92
68
  margin: 0,
69
+ showMinLabel: false,
93
70
  formatter: (value) => {
94
71
  const date = new Date(value as string);
95
72
  return isGreaterThanTwoWeeks
@@ -106,7 +83,8 @@ export const getOption: GetOptionType = (
106
83
  yAxis: {
107
84
  type: 'value',
108
85
  position: 'right',
109
- scale: true,
86
+ min: (val) => val.min - bucketWidth * 2,
87
+ max: (val) => val.max + bucketWidth * 2,
110
88
  axisLine: { show: false },
111
89
  axisTick: { show: false },
112
90
  axisLabel: {
@@ -134,43 +112,46 @@ export const getOption: GetOptionType = (
134
112
  name: 'heatmap',
135
113
  id: 'heatmap',
136
114
  silent: true,
137
- renderItem: (params, api) => {
138
- const { coordSys } = params;
115
+ clip: true,
116
+ renderItem: (_params, api) => {
139
117
  const xVal = api.value(0);
140
- const yVal = api.value(1);
141
- const sentiment = api.value(2) as number;
142
- const start = api.coord([xVal, yVal]);
118
+ const metaValues = JSON.parse(api.value(2) as string);
119
+
143
120
  const [rectWidth, rectHeight] = api.size!([
144
121
  0,
145
- ordersPositionsBucketWidth,
122
+ bucketWidth,
146
123
  ]) as number[];
147
- const boundaries = coordSys as unknown as {
148
- x: number;
149
- y: number;
150
- width: number;
151
- height: number;
152
- };
153
- const shape = echarts.graphic.clipRectByRect(
154
- {
155
- x: start[0] - rectWidth / 2,
156
- y: start[1],
157
- width: rectWidth,
158
- height: rectHeight,
159
- },
160
- boundaries
161
- );
162
- return (
163
- shape && {
164
- type: 'rect',
165
- shape,
166
- style: {
167
- fill: getRectColor(sentiment),
168
- },
169
- emphasisDisabled: true,
124
+
125
+ const items = metaValues.map(
126
+ ({ price, sentiment }: { price: number; sentiment: number }) => {
127
+ const start = api.coord([xVal, price]);
128
+
129
+ return {
130
+ type: 'rect',
131
+ shape: {
132
+ x: start[0] - rectWidth / 2,
133
+ y: start[1],
134
+ width: rectWidth,
135
+ height: rectHeight,
136
+ },
137
+ style: {
138
+ fill: getRectColor(sentiment),
139
+ opacity: 0.7,
140
+ },
141
+ silent: true,
142
+ emphasisDisabled: true,
143
+ };
170
144
  }
171
145
  );
146
+
147
+ return {
148
+ type: 'group',
149
+ children: items,
150
+ silent: true,
151
+ emphasisDisabled: true,
152
+ };
172
153
  },
173
- data: ordersPositionsChartData,
154
+ data: orderPositionBooks,
174
155
  },
175
156
  ],
176
157
  grid: [
@@ -178,8 +159,8 @@ export const getOption: GetOptionType = (
178
159
  name: 'main-grid',
179
160
  top: '0px',
180
161
  left: '0px',
181
- right: `${Y_LABEL_SIZE_DESKTOP}px`,
182
- bottom: `${X_LABEL_SIZE}px`,
162
+ right: `${CHART_CONFIG.Y_LABEL_SIZE_DESKTOP}px`,
163
+ bottom: `${CHART_CONFIG.X_LABEL_SIZE}px`,
183
164
  },
184
165
  ],
185
166
  graphic: [
@@ -1,2 +1,6 @@
1
1
  export * from './Chart';
2
+ export * from './chartOptions';
2
3
  export * from './ChartWithData';
4
+ export * from './types';
5
+ export * from './useCrowdViewData';
6
+ export * from './utils/chartUtils';
@@ -1,45 +1,47 @@
1
1
  import type { EChartsOption } from 'echarts';
2
2
 
3
- import type { BookType } from '../../../gql/types/graphql';
4
- import type { GranularityId } from '../../types';
3
+ import type {
4
+ BookType,
5
+ Division,
6
+ Granularity,
7
+ } from '../../../gql/types/graphql';
5
8
 
6
- interface ChartOptionsProps {
9
+ export interface UseCrowdViewDataProps {
10
+ instrument: string;
11
+ bookType: BookType;
12
+ division: Division;
13
+ granularity: Granularity;
14
+ }
15
+
16
+ interface CrowdViewData {
7
17
  xAxisData: string[];
8
- ordersPositionsChartData: [
9
- // timestamp
10
- string,
11
- // price
12
- number,
13
- // orders-positions sentiment
14
- number,
15
- ][];
16
- ordersPositionsBucketWidth: number;
17
18
  candlesSeriesData: [number, number, number, number][];
19
+ orderPositionBooks: ([string, number, string] | null)[];
20
+ bucketWidth: number;
21
+ }
22
+
23
+ export interface UseCrowdViewDataReturn {
24
+ data: CrowdViewData | null;
25
+ loading: boolean;
26
+ error: boolean;
18
27
  }
19
28
 
20
29
  export type GetOptionType = (
21
- props: ChartOptionsProps,
30
+ props: CrowdViewData,
22
31
  isDark: boolean
23
32
  ) => EChartsOption;
24
33
 
25
34
  export interface ChartProps {
26
- data: ChartOptionsProps;
35
+ data: CrowdViewData;
27
36
  }
28
37
 
29
38
  export interface ChartWithDataProps {
30
39
  bookType: BookType;
40
+ division: Division;
31
41
  instrument: string;
32
- granularity: GranularityId;
33
- }
34
-
35
- interface TransformDataForChartTypeProps {
36
- granularity: GranularityId;
42
+ granularity: Granularity;
37
43
  }
38
44
 
39
- export type TransformDataForChartType = (
40
- props: TransformDataForChartTypeProps
41
- ) => ChartOptionsProps;
42
-
43
45
  export interface GetLabelsDataProps {
44
46
  xAxisData: string[];
45
47
  isGreaterThanTwoWeeks: boolean;
@@ -0,0 +1,263 @@
1
+ import { useQuery } from '@apollo/client';
2
+ import { useMemo } from 'react';
3
+
4
+ import { getOrderPositionBooks } from '../../../gql/getOrderPositionBooks';
5
+ import { getPriceCandles } from '../../../gql/getPriceCandles';
6
+ import type {
7
+ GetOrderPositionBooksQuery,
8
+ GetOrderPositionBooksQueryVariables,
9
+ GetPriceCandlesQuery,
10
+ GetPriceCandlesQueryVariables,
11
+ } from '../../../gql/types/graphql';
12
+ import { BookType, DataSource, Division } from '../../../gql/types/graphql';
13
+ import { BOOKS_THRESHOLDS, BUCKET_CONFIG } from '../../constants';
14
+ import type { UseCrowdViewDataProps, UseCrowdViewDataReturn } from './types';
15
+ import { getTimeSpanForGranularity } from './utils/chartUtils';
16
+
17
+ const processPriceCandles = (
18
+ priceCandlesData: GetPriceCandlesQuery | undefined
19
+ ) => {
20
+ if (!priceCandlesData?.priceCandles?.candle?.length) {
21
+ return {
22
+ minPrice: 0,
23
+ maxPrice: 0,
24
+ hasValidCandles: false,
25
+ candleMap: new Map(),
26
+ candles: [],
27
+ };
28
+ }
29
+
30
+ const candles = priceCandlesData.priceCandles.candle;
31
+ let calculatedMinPrice = Number.MAX_VALUE;
32
+ let calculatedMaxPrice = Number.MIN_VALUE;
33
+
34
+ const candleMap = new Map<
35
+ string,
36
+ {
37
+ point?: string;
38
+ high?: number;
39
+ low?: number;
40
+ open?: number;
41
+ close?: number;
42
+ }
43
+ >();
44
+
45
+ candles.forEach((candle) => {
46
+ if (!candle) return;
47
+
48
+ if (candle.high > calculatedMaxPrice) {
49
+ calculatedMaxPrice = candle.high;
50
+ }
51
+ if (candle.low < calculatedMinPrice) {
52
+ calculatedMinPrice = candle.low;
53
+ }
54
+
55
+ if (candle.point) {
56
+ candleMap.set(candle.point, candle);
57
+ }
58
+ });
59
+
60
+ return {
61
+ minPrice: calculatedMinPrice,
62
+ maxPrice: calculatedMaxPrice,
63
+ hasValidCandles: true,
64
+ candleMap,
65
+ candles,
66
+ };
67
+ };
68
+
69
+ const processOrderPositionBooks = (
70
+ orderPositionData: GetOrderPositionBooksQuery | undefined,
71
+ candleMap: Map<
72
+ string,
73
+ {
74
+ point?: string;
75
+ high?: number;
76
+ low?: number;
77
+ open?: number;
78
+ close?: number;
79
+ }
80
+ >
81
+ ) => {
82
+ if (!orderPositionData?.orderPositionBooks?.length) {
83
+ return [];
84
+ }
85
+
86
+ return orderPositionData.orderPositionBooks
87
+ .filter((book): book is NonNullable<typeof book> => {
88
+ return book !== null && book.buckets?.length > 0;
89
+ })
90
+ .map((book) => {
91
+ const filteredBuckets = book.buckets
92
+ .filter((bucket) => {
93
+ if (
94
+ !bucket ||
95
+ bucket.sentiment === undefined ||
96
+ bucket.sentiment === null ||
97
+ bucket.price === undefined
98
+ ) {
99
+ return false;
100
+ }
101
+ return Math.abs(bucket.sentiment) >= BOOKS_THRESHOLDS.MIN;
102
+ })
103
+ .map((bucket) => ({
104
+ price: bucket!.price!,
105
+ sentiment: bucket!.sentiment!,
106
+ }));
107
+
108
+ const candle = candleMap.get(book.time);
109
+ const price = candle?.high ?? null;
110
+
111
+ return [book.time, price, JSON.stringify(filteredBuckets)] as [
112
+ string,
113
+ number,
114
+ string,
115
+ ];
116
+ });
117
+ };
118
+
119
+ const validateData = (
120
+ priceCandlesData: GetPriceCandlesQuery | undefined,
121
+ orderPositionData: GetOrderPositionBooksQuery | undefined,
122
+ hasValidCandles: boolean
123
+ ): Error | null => {
124
+ const hasValidPriceData =
125
+ (priceCandlesData?.priceCandles?.candle?.length ?? 0) >= 1;
126
+ const hasValidOrderData =
127
+ (orderPositionData?.orderPositionBooks?.length ?? 0) >= 1;
128
+
129
+ if (!hasValidPriceData) {
130
+ return new Error('Insufficient price candle data');
131
+ }
132
+ if (!hasValidOrderData) {
133
+ return new Error('Insufficient order position data');
134
+ }
135
+ if (!hasValidCandles) {
136
+ return new Error('Invalid candle data');
137
+ }
138
+
139
+ return null;
140
+ };
141
+
142
+ export const useCrowdViewData = ({
143
+ instrument,
144
+ bookType,
145
+ division,
146
+ granularity,
147
+ }: UseCrowdViewDataProps): UseCrowdViewDataReturn => {
148
+ const {
149
+ loading: priceCandlesLoading,
150
+ data: priceCandlesData,
151
+ error: priceCandlesError,
152
+ } = useQuery<GetPriceCandlesQuery, GetPriceCandlesQueryVariables>(
153
+ getPriceCandles,
154
+ {
155
+ variables: {
156
+ dataSource: division === Division.Oc ? DataSource.V20 : DataSource.Mt5,
157
+ division,
158
+ instrument,
159
+ granularity,
160
+ timeSpan: getTimeSpanForGranularity(granularity),
161
+ },
162
+ fetchPolicy: 'no-cache',
163
+ }
164
+ );
165
+
166
+ const priceCandlesProcessed = useMemo(
167
+ () => processPriceCandles(priceCandlesData),
168
+ [priceCandlesData]
169
+ );
170
+
171
+ const { minPrice, maxPrice, hasValidCandles, candleMap, candles } =
172
+ priceCandlesProcessed;
173
+
174
+ const maxBookPrice = useMemo(
175
+ () =>
176
+ maxPrice +
177
+ BUCKET_CONFIG.DEFAULT_WIDTH * BUCKET_CONFIG.PRICE_PADDING_MULTIPLIER,
178
+ [maxPrice]
179
+ );
180
+
181
+ const minBookPrice = useMemo(
182
+ () =>
183
+ minPrice -
184
+ BUCKET_CONFIG.DEFAULT_WIDTH * BUCKET_CONFIG.PRICE_PADDING_MULTIPLIER,
185
+ [minPrice]
186
+ );
187
+
188
+ const {
189
+ loading: orderPositionLoading,
190
+ data: orderPositionData,
191
+ error: orderPositionError,
192
+ } = useQuery<GetOrderPositionBooksQuery, GetOrderPositionBooksQueryVariables>(
193
+ getOrderPositionBooks,
194
+ {
195
+ variables: {
196
+ instrument,
197
+ bookType: bookType || BookType.Order,
198
+ timeSpan: getTimeSpanForGranularity(granularity),
199
+ granularity,
200
+ maxBookPrice,
201
+ minBookPrice,
202
+ },
203
+ fetchPolicy: 'no-cache',
204
+ skip: priceCandlesLoading || !!priceCandlesError,
205
+ }
206
+ );
207
+
208
+ const loading = priceCandlesLoading || orderPositionLoading;
209
+
210
+ const orderPositionBooks = useMemo(
211
+ () => processOrderPositionBooks(orderPositionData, candleMap),
212
+ [orderPositionData, candleMap]
213
+ );
214
+
215
+ const error = useMemo((): Error | null => {
216
+ if (priceCandlesError) {
217
+ return new Error(`Price candles error: ${priceCandlesError.message}`);
218
+ }
219
+ if (orderPositionError) {
220
+ return new Error(`Order position error: ${orderPositionError.message}`);
221
+ }
222
+ if (loading) {
223
+ return null;
224
+ }
225
+ return validateData(priceCandlesData, orderPositionData, hasValidCandles);
226
+ }, [
227
+ priceCandlesError,
228
+ orderPositionError,
229
+ loading,
230
+ priceCandlesData,
231
+ orderPositionData,
232
+ hasValidCandles,
233
+ ]);
234
+
235
+ const data = useMemo(() => {
236
+ if (!priceCandlesData || !orderPositionData || error) {
237
+ return null;
238
+ }
239
+
240
+ const xAxisData = candles.map((candle) => candle?.point || '');
241
+ const candlesSeriesData: [number, number, number, number][] = candles.map(
242
+ (candle) => [
243
+ candle?.open || 0,
244
+ candle?.close || 0,
245
+ candle?.low || 0,
246
+ candle?.high || 0,
247
+ ]
248
+ );
249
+
250
+ return {
251
+ xAxisData,
252
+ candlesSeriesData,
253
+ orderPositionBooks,
254
+ bucketWidth: orderPositionData.orderPositionBooks?.[0]?.bucketWidth || 0,
255
+ };
256
+ }, [priceCandlesData, orderPositionData, error, candles, orderPositionBooks]);
257
+
258
+ return {
259
+ data,
260
+ loading,
261
+ error: !!error,
262
+ };
263
+ };