@oanda/labs-crowd-view-widget 1.0.50 → 1.0.52

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 (88) hide show
  1. package/CHANGELOG.md +420 -0
  2. package/dist/main/CrowdViewWidget/components/Chart/Chart.js +60 -21
  3. package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  4. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +3 -3
  5. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  6. package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js +208 -42
  7. package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
  8. package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
  9. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js +25 -6
  10. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  11. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js +12 -10
  12. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
  13. package/dist/main/CrowdViewWidget/components/Chart/utils/getChartStyles.js +27 -0
  14. package/dist/main/CrowdViewWidget/components/Chart/utils/getChartStyles.js.map +1 -0
  15. package/dist/main/CrowdViewWidget/components/Chart/utils/getGridLines.js +123 -0
  16. package/dist/main/CrowdViewWidget/components/Chart/utils/getGridLines.js.map +1 -0
  17. package/dist/main/CrowdViewWidget/components/Chart/utils/index.js +22 -0
  18. package/dist/main/CrowdViewWidget/components/Chart/utils/index.js.map +1 -1
  19. package/dist/main/CrowdViewWidget/components/Chart/utils/processSentiments.js +28 -0
  20. package/dist/main/CrowdViewWidget/components/Chart/utils/processSentiments.js.map +1 -0
  21. package/dist/main/CrowdViewWidget/components/Legend/Legend.js +1 -1
  22. package/dist/main/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  23. package/dist/main/CrowdViewWidget/constants.js +13 -3
  24. package/dist/main/CrowdViewWidget/constants.js.map +1 -1
  25. package/dist/main/gql/getSentiments.js +11 -0
  26. package/dist/main/gql/getSentiments.js.map +1 -0
  27. package/dist/main/gql/types/gql.js +2 -1
  28. package/dist/main/gql/types/gql.js.map +1 -1
  29. package/dist/main/gql/types/graphql.js +162 -1
  30. package/dist/main/gql/types/graphql.js.map +1 -1
  31. package/dist/module/CrowdViewWidget/components/Chart/Chart.js +63 -24
  32. package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  33. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +3 -3
  34. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  35. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js +208 -43
  36. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
  37. package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
  38. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +26 -7
  39. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  40. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js +12 -10
  41. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
  42. package/dist/module/CrowdViewWidget/components/Chart/utils/getChartStyles.js +20 -0
  43. package/dist/module/CrowdViewWidget/components/Chart/utils/getChartStyles.js.map +1 -0
  44. package/dist/module/CrowdViewWidget/components/Chart/utils/getGridLines.js +116 -0
  45. package/dist/module/CrowdViewWidget/components/Chart/utils/getGridLines.js.map +1 -0
  46. package/dist/module/CrowdViewWidget/components/Chart/utils/index.js +2 -0
  47. package/dist/module/CrowdViewWidget/components/Chart/utils/index.js.map +1 -1
  48. package/dist/module/CrowdViewWidget/components/Chart/utils/processSentiments.js +21 -0
  49. package/dist/module/CrowdViewWidget/components/Chart/utils/processSentiments.js.map +1 -0
  50. package/dist/module/CrowdViewWidget/components/Legend/Legend.js +1 -1
  51. package/dist/module/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  52. package/dist/module/CrowdViewWidget/constants.js +12 -2
  53. package/dist/module/CrowdViewWidget/constants.js.map +1 -1
  54. package/dist/module/gql/getSentiments.js +6 -0
  55. package/dist/module/gql/getSentiments.js.map +1 -0
  56. package/dist/module/gql/types/gql.js +2 -1
  57. package/dist/module/gql/types/gql.js.map +1 -1
  58. package/dist/module/gql/types/graphql.js +161 -0
  59. package/dist/module/gql/types/graphql.js.map +1 -1
  60. package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +43 -0
  61. package/dist/types/CrowdViewWidget/components/Chart/utils/chartUtils.d.ts +2 -2
  62. package/dist/types/CrowdViewWidget/components/Chart/utils/getChartStyles.d.ts +10 -0
  63. package/dist/types/CrowdViewWidget/components/Chart/utils/getGridLines.d.ts +97 -0
  64. package/dist/types/CrowdViewWidget/components/Chart/utils/index.d.ts +2 -0
  65. package/dist/types/CrowdViewWidget/components/Chart/utils/processSentiments.d.ts +3 -0
  66. package/dist/types/CrowdViewWidget/constants.d.ts +11 -1
  67. package/dist/types/gql/getSentiments.d.ts +2 -0
  68. package/dist/types/gql/types/gql.d.ts +9 -0
  69. package/dist/types/gql/types/graphql.d.ts +36 -0
  70. package/package.json +3 -3
  71. package/src/CrowdViewWidget/components/Chart/Chart.tsx +86 -34
  72. package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +3 -3
  73. package/src/CrowdViewWidget/components/Chart/chartOptions.ts +242 -72
  74. package/src/CrowdViewWidget/components/Chart/types.ts +55 -0
  75. package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +35 -3
  76. package/src/CrowdViewWidget/components/Chart/utils/chartUtils.ts +33 -14
  77. package/src/CrowdViewWidget/components/Chart/utils/getChartStyles.ts +42 -0
  78. package/src/CrowdViewWidget/components/Chart/utils/getGridLines.ts +148 -0
  79. package/src/CrowdViewWidget/components/Chart/utils/index.ts +2 -0
  80. package/src/CrowdViewWidget/components/Chart/utils/processSentiments.ts +42 -0
  81. package/src/CrowdViewWidget/components/Legend/Legend.tsx +1 -1
  82. package/src/CrowdViewWidget/constants.ts +17 -1
  83. package/src/gql/getSentiments.ts +25 -0
  84. package/src/gql/types/gql.ts +8 -0
  85. package/src/gql/types/graphql.ts +161 -0
  86. package/test/components/Chart/utils/chartUtils.test.ts +76 -2
  87. package/test/components/Chart/utils/getChartStyles.test.ts +64 -0
  88. package/test/components/Chart/utils/processSentiments.test.ts +130 -0
@@ -6,26 +6,28 @@ import {
6
6
  } from '@oanda/labs-widget-common';
7
7
  import { useLocale } from '@oanda/mono-i18n';
8
8
  import type { EChartsType } from 'echarts';
9
- import {
10
- BarChart,
11
- CandlestickChart,
12
- CustomChart,
13
- ScatterChart,
14
- } from 'echarts/charts';
9
+ import { CandlestickChart, CustomChart, LineChart } from 'echarts/charts';
15
10
  import {
16
11
  DataZoomInsideComponent,
17
12
  GraphicComponent,
18
13
  GridSimpleComponent,
14
+ MarkAreaComponent,
19
15
  MarkPointComponent,
20
16
  TooltipComponent,
17
+ VisualMapComponent,
21
18
  } from 'echarts/components';
22
19
  import * as echarts from 'echarts/core';
23
20
  import { CanvasRenderer } from 'echarts/renderers';
24
21
  import React from 'react';
25
22
 
26
- import { CHART_CONFIG } from '../../constants';
23
+ import { CHART_CONFIG_CALCULATED } from '../../constants';
27
24
  import { getOption } from './chartOptions';
28
- import type { ChartProps } from './types';
25
+ import type {
26
+ ChartProps,
27
+ DataZoomArray,
28
+ DataZoomEvent,
29
+ XAxisArray,
30
+ } from './types';
29
31
  import {
30
32
  formatXAxisLabel,
31
33
  getLabelData,
@@ -35,14 +37,15 @@ import {
35
37
  echarts.use([
36
38
  GridSimpleComponent,
37
39
  GraphicComponent,
38
- BarChart,
39
40
  CanvasRenderer,
40
41
  DataZoomInsideComponent,
41
42
  CustomChart,
42
43
  TooltipComponent,
43
44
  CandlestickChart,
44
45
  MarkPointComponent,
45
- ScatterChart,
46
+ MarkAreaComponent,
47
+ LineChart,
48
+ VisualMapComponent,
46
49
  ]);
47
50
 
48
51
  echarts.registerTheme('dark_theme', getChartTheme(Theme.Dark));
@@ -54,46 +57,95 @@ const Chart = ({ data, isDesktop }: ChartProps) => {
54
57
 
55
58
  return (
56
59
  <BaseChart
57
- chartHeight={CHART_CONFIG.HEIGHT}
60
+ chartHeight={CHART_CONFIG_CALCULATED.FULL_HEIGHT}
58
61
  echarts={echarts}
59
62
  isDark={isDark}
60
63
  lazyUpdate={true}
61
64
  option={getOption(data, isDark, isDesktop, lang)}
62
65
  opts={{ renderer: 'canvas' }}
63
66
  onEvents={{
64
- datazoom: (_params: unknown, instance: EChartsType) => {
65
- const { dataZoom } = instance.getOption();
67
+ datazoom: (params: DataZoomEvent, instance: EChartsType) => {
68
+ if (!params.batch?.[0]) {
69
+ return;
70
+ }
71
+
72
+ const batchItem = params.batch[0];
73
+ const { start, end } = batchItem;
74
+
75
+ const { dataZoom, xAxis } = instance.getOption();
76
+ if (
77
+ !Array.isArray(dataZoom) ||
78
+ dataZoom.length === 0 ||
79
+ !dataZoom[0] ||
80
+ typeof dataZoom[0].startValue !== 'number' ||
81
+ typeof dataZoom[0].endValue !== 'number'
82
+ ) {
83
+ return;
84
+ }
85
+
86
+ const { startValue, endValue } = dataZoom[0] as DataZoomArray[0];
66
87
 
67
- const { startValue, endValue } = (
68
- dataZoom as { startValue: number; endValue: number }[]
69
- )[0];
88
+ if (batchItem.dataZoomId === 'main') {
89
+ instance.dispatchAction({
90
+ type: 'dataZoom',
91
+ dataZoomId: 'sentiment',
92
+ start,
93
+ end,
94
+ filterMode: 'none',
95
+ });
96
+ }
97
+
98
+ if (batchItem.dataZoomId === 'sentiment') {
99
+ instance.dispatchAction({
100
+ type: 'dataZoom',
101
+ dataZoomId: 'main',
102
+ start,
103
+ end,
104
+ filterMode: 'filter',
105
+ });
106
+ }
107
+
108
+ if (!data.xAxisData[startValue] || !data.xAxisData[endValue]) {
109
+ return;
110
+ }
70
111
 
71
112
  const isGreaterThanTwoWeeks = isDifferenceGreaterThanTwoWeeks(
72
113
  data.xAxisData[startValue],
73
114
  data.xAxisData[endValue]
74
115
  );
75
116
 
76
- const labelsData = getLabelData({
77
- xAxisData: data.xAxisData,
78
- isGreaterThanTwoWeeks,
79
- });
117
+ const wasGreaterThanTwoWeeks =
118
+ Array.isArray(xAxis) &&
119
+ xAxis.length > 0 &&
120
+ (xAxis as XAxisArray)[0]?.name === 'xAxis-greater-than-two-weeks';
80
121
 
81
- instance.setOption({
82
- xAxis: {
83
- axisLabel: {
84
- formatter: (value: string) =>
85
- formatXAxisLabel(value, isGreaterThanTwoWeeks),
86
- },
87
- },
88
- series: [
89
- {
90
- id: 'candlestick',
91
- markPoint: {
92
- data: labelsData,
122
+ if (isGreaterThanTwoWeeks !== wasGreaterThanTwoWeeks) {
123
+ const labelsData = getLabelData({
124
+ xAxisData: data.xAxisData,
125
+ isGreaterThanTwoWeeks,
126
+ });
127
+
128
+ instance.setOption({
129
+ xAxis: {
130
+ id: 'main-xAxis',
131
+ name: isGreaterThanTwoWeeks
132
+ ? 'xAxis-greater-than-two-weeks'
133
+ : 'xAxis-less-than-two-weeks',
134
+ axisLabel: {
135
+ formatter: (value: string) =>
136
+ formatXAxisLabel(value, isGreaterThanTwoWeeks),
93
137
  },
94
138
  },
95
- ],
96
- });
139
+ series: [
140
+ {
141
+ id: 'candlestick',
142
+ markPoint: {
143
+ data: labelsData,
144
+ },
145
+ },
146
+ ],
147
+ });
148
+ }
97
149
  },
98
150
  }}
99
151
  />
@@ -29,14 +29,14 @@ const ChartWithData = ({
29
29
 
30
30
  return (
31
31
  <>
32
- <div className="lw-relative lw-h-[450px] lw-w-full">
32
+ <div className="lw-relative lw-h-[610px] lw-w-full">
33
33
  {error && (
34
- <div className="lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-[calc(100%-30px)] lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary">
34
+ <div className="lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary">
35
35
  <ChartError />
36
36
  </div>
37
37
  )}
38
38
  {loading && (
39
- <div className="lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-[calc(100%-30px)] lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary">
39
+ <div className="lw-absolute lw-left-0 lw-top-0 lw-flex lw-h-full lw-w-full lw-items-center lw-justify-center lw-border lw-border-solid lw-border-border-primary">
40
40
  <Spinner size={SpinnerSize.lg} />
41
41
  </div>
42
42
  )}
@@ -1,12 +1,7 @@
1
- import {
2
- colorPalette,
3
- getGridLines,
4
- getLineCommons,
5
- themeColors,
6
- } from '@oanda/labs-widget-common';
1
+ import chroma from 'chroma-js';
7
2
 
8
- import { CHART_CONFIG } from '../../constants';
9
- import type { Bucket, GetOptionType } from './types';
3
+ import { CHART_CONFIG, CHART_CONFIG_CALCULATED } from '../../constants';
4
+ import type { Bucket, GetOptionType, TooltipParam } from './types';
10
5
  import {
11
6
  formatXAxisLabel,
12
7
  getLabelData,
@@ -14,6 +9,8 @@ import {
14
9
  getTooltipFormatter,
15
10
  isDifferenceGreaterThanTwoWeeks,
16
11
  } from './utils/chartUtils';
12
+ import { getChartStyles } from './utils/getChartStyles';
13
+ import { getGridLines } from './utils/getGridLines';
17
14
 
18
15
  // @ts-expect-error
19
16
  export const getOption: GetOptionType = (
@@ -25,12 +22,23 @@ export const getOption: GetOptionType = (
25
22
  buckets,
26
23
  precision,
27
24
  bookType,
25
+ sentiments,
28
26
  },
29
27
  isDark,
30
28
  isDesktop,
31
29
  labelCallback
32
30
  ) => {
33
31
  let selectedPrice: number;
32
+ const {
33
+ sentimentLongColor,
34
+ sentimentShortColor,
35
+ candleLongColor,
36
+ candleShortColor,
37
+ sentimentAreaOpacity,
38
+ tooltipLinesColor,
39
+ sentimentLabelColor,
40
+ } = getChartStyles(isDark);
41
+
34
42
  const visibleXAxisData = xAxisData.slice(
35
43
  (xAxisData.length * CHART_CONFIG.INITIAL_START_ZOOM) / 100,
36
44
  (xAxisData.length * CHART_CONFIG.INITIAL_END_ZOOM) / 100
@@ -46,25 +54,29 @@ export const getOption: GetOptionType = (
46
54
  isGreaterThanTwoWeeks,
47
55
  });
48
56
 
49
- const gridLines = getGridLines({
57
+ const gridMainLines = getGridLines({
50
58
  isDark,
51
- chartWidth: CHART_CONFIG.WIDTH,
52
- chartHeight: CHART_CONFIG.HEIGHT,
53
- xLabelsSize: CHART_CONFIG.X_LABEL_SIZE,
54
- yLabelSize: isDesktop
55
- ? CHART_CONFIG.Y_LABEL_SIZE_DESKTOP
56
- : CHART_CONFIG.Y_LABEL_SIZE_MOBILE,
57
- bottomLeftBox: false,
59
+ isDesktop,
58
60
  });
59
61
 
60
62
  return {
61
63
  animation: false,
62
64
  dataZoom: [
63
65
  {
66
+ id: 'main',
64
67
  type: 'inside',
65
68
  xAxisIndex: 0,
66
69
  start: CHART_CONFIG.INITIAL_START_ZOOM,
67
70
  end: CHART_CONFIG.INITIAL_END_ZOOM,
71
+ filterMode: 'filter',
72
+ },
73
+ {
74
+ id: 'sentiment',
75
+ type: 'inside',
76
+ xAxisIndex: 1,
77
+ start: CHART_CONFIG.INITIAL_START_ZOOM,
78
+ end: CHART_CONFIG.INITIAL_END_ZOOM,
79
+ filterMode: 'none',
68
80
  },
69
81
  ],
70
82
  tooltip: {
@@ -72,24 +84,19 @@ export const getOption: GetOptionType = (
72
84
  axisPointer: {
73
85
  type: 'cross',
74
86
  axis: 'x',
87
+ lineStyle: {
88
+ color: tooltipLinesColor,
89
+ },
75
90
  crossStyle: {
76
- color: isDark ? colorPalette.orange : themeColors.borderPrimary.light,
91
+ color: tooltipLinesColor,
77
92
  },
78
93
  label: {
94
+ padding: 0,
95
+ lineHeight: 24,
79
96
  formatter: (params) => {
80
- if (params.axisDimension === 'y') {
97
+ if (params.axisDimension === 'y' && params.axisIndex === 0) {
81
98
  selectedPrice = Number(params.value);
82
- return Number(params.value).toFixed(precision);
83
- }
84
-
85
- if (params.axisDimension === 'x') {
86
- const date = new Date(params.value as string);
87
- return date.toLocaleString(undefined, {
88
- hour: '2-digit',
89
- minute: '2-digit',
90
- day: 'numeric',
91
- month: 'short',
92
- });
99
+ return ` ${Number(params.value).toFixed(precision)} `;
93
100
  }
94
101
 
95
102
  return null;
@@ -97,7 +104,7 @@ export const getOption: GetOptionType = (
97
104
  },
98
105
  },
99
106
  confine: true,
100
- formatter: (params) =>
107
+ formatter: (params: TooltipParam[]) =>
101
108
  getTooltipFormatter({
102
109
  params,
103
110
  buckets,
@@ -108,51 +115,150 @@ export const getOption: GetOptionType = (
108
115
  bookType,
109
116
  }),
110
117
  },
111
- xAxis: {
112
- type: 'category',
113
- data: xAxisData,
114
- splitNumber: 1,
115
- axisTick: {
118
+ xAxis: [
119
+ {
120
+ type: 'category',
121
+ name: 'xAxis-less-than-two-weeks',
122
+ nameTextStyle: {
123
+ fontSize: 0,
124
+ },
125
+ id: 'main-xAxis',
126
+ data: xAxisData,
127
+ splitNumber: 1,
128
+ axisTick: {
129
+ show: false,
130
+ },
131
+ axisLabel: {
132
+ padding: [8, 16, 8, 16],
133
+ margin: 0,
134
+ formatter: (value) => formatXAxisLabel(value, isGreaterThanTwoWeeks),
135
+ },
136
+ },
137
+ {
138
+ type: 'category',
139
+ gridIndex: 1,
116
140
  show: false,
141
+ data: xAxisData,
142
+ splitNumber: 1,
143
+ axisTick: {
144
+ show: false,
145
+ },
146
+ axisLabel: {
147
+ show: false,
148
+ },
117
149
  },
118
- axisLabel: {
119
- padding: [8, 16, 8, 16],
120
- margin: 0,
121
- formatter: (value) => formatXAxisLabel(value, isGreaterThanTwoWeeks),
150
+ ],
151
+ yAxis: [
152
+ {
153
+ type: 'value',
154
+ gridIndex: 0,
155
+ position: 'right',
156
+ min: (val) => val.min - bucketWidth * 2,
157
+ max: (val) => val.max + bucketWidth * 2,
158
+ axisLine: { show: false },
159
+ axisTick: { show: false },
160
+ axisLabel: {
161
+ showMaxLabel: false,
162
+ showMinLabel: false,
163
+ margin: isDesktop ? 4 : 2,
164
+ formatter: (value: number) => value.toFixed(precision - 1),
165
+ },
122
166
  },
123
- },
124
- yAxis: {
125
- type: 'value',
126
- position: 'right',
127
- min: (val) => val.min - bucketWidth * 2,
128
- max: (val) => val.max + bucketWidth * 2,
129
- axisLine: { show: false },
130
- axisTick: { show: false },
131
- axisLabel: {
132
- showMaxLabel: false,
133
- showMinLabel: false,
134
- margin: isDesktop ? 4 : 2,
135
- formatter: (value: number) => value.toFixed(precision - 1),
167
+ {
168
+ type: 'value',
169
+ gridIndex: 1,
170
+ position: 'right',
171
+ min: CHART_CONFIG.SENTIMENT_MIN,
172
+ max: CHART_CONFIG.SENTIMENT_MAX,
173
+ interval: CHART_CONFIG.SENTIMENT_INTERVAL,
174
+ axisLine: { show: false },
175
+ axisTick: { show: false },
176
+ axisLabel: {
177
+ verticalAlignMaxLabel: 'top',
178
+ verticalAlignMinLabel: 'bottom',
179
+ lineHeight: 20,
180
+ margin: isDesktop ? 4 : 2,
181
+ rich: {
182
+ shortRect: {
183
+ backgroundColor: chroma(sentimentShortColor).alpha(0.3).css(),
184
+ borderColor: sentimentShortColor,
185
+ borderWidth: 1,
186
+ width: 8,
187
+ height: 8,
188
+ },
189
+ longRect: {
190
+ backgroundColor: chroma(sentimentLongColor).alpha(0.3).css(),
191
+ borderColor: sentimentLongColor,
192
+ borderWidth: 1,
193
+ width: 8,
194
+ height: 8,
195
+ },
196
+ },
197
+ formatter: (value: number) => {
198
+ if (value === CHART_CONFIG.SENTIMENT_MIN) {
199
+ return `{shortRect| } ${labelCallback('short')}`;
200
+ }
201
+
202
+ if (value === CHART_CONFIG.SENTIMENT_MAX) {
203
+ return `{longRect| } ${labelCallback('long')}`;
204
+ }
205
+
206
+ if (value === CHART_CONFIG.SENTIMENT_MAX / 2) {
207
+ return '50%';
208
+ }
209
+
210
+ return undefined;
211
+ },
212
+ },
136
213
  },
214
+ ],
215
+ axisPointer: {
216
+ link: [
217
+ {
218
+ xAxisIndex: 'all',
219
+ },
220
+ ],
221
+ },
222
+ visualMap: {
223
+ show: false,
224
+ seriesId: 'sentiment',
225
+ dimension: 1,
226
+ pieces: [
227
+ {
228
+ lt: CHART_CONFIG.SENTIMENT_MAX / 2,
229
+ color: sentimentShortColor,
230
+ },
231
+ {
232
+ gte: CHART_CONFIG.SENTIMENT_MAX / 2,
233
+ lte: CHART_CONFIG.SENTIMENT_MAX,
234
+ color: sentimentLongColor,
235
+ },
236
+ {
237
+ gt: CHART_CONFIG.SENTIMENT_MAX,
238
+ color: sentimentLongColor,
239
+ },
240
+ ],
137
241
  },
138
242
  series: [
139
243
  {
140
244
  type: 'candlestick',
141
245
  id: 'candlestick',
246
+ gridIndex: 0,
247
+ xAxisIndex: 0,
248
+ yAxisIndex: 0,
142
249
  data: candlesSeriesData,
143
250
  itemStyle: {
144
- color: isDark ? colorPalette.orange : colorPalette.raspberryLight,
145
- color0: isDark
146
- ? colorPalette.bottleGreenDark
147
- : colorPalette.bottleGreenLight,
148
- borderColor: isDark
149
- ? colorPalette.orange
150
- : colorPalette.raspberryLight,
151
- borderColor0: isDark
152
- ? colorPalette.bottleGreenDark
153
- : colorPalette.bottleGreenLight,
251
+ color: chroma(candleShortColor).desaturate().css(),
252
+ color0: chroma(candleLongColor).desaturate().css(),
253
+ borderColor: candleShortColor,
254
+ borderColor0: candleLongColor,
255
+ },
256
+ emphasis: {
257
+ itemStyle: {
258
+ color: candleShortColor,
259
+ color0: candleLongColor,
260
+ },
154
261
  },
155
-
156
262
  markPoint: {
157
263
  symbol: 'circle',
158
264
  symbolSize: 0,
@@ -161,8 +267,11 @@ export const getOption: GetOptionType = (
161
267
  },
162
268
  {
163
269
  type: 'custom',
270
+ gridIndex: 0,
164
271
  name: 'heatmap',
165
272
  id: 'heatmap',
273
+ xAxisIndex: 0,
274
+ yAxisIndex: 0,
166
275
  silent: true,
167
276
  clip: true,
168
277
  renderItem: (_params, api) => {
@@ -203,27 +312,88 @@ export const getOption: GetOptionType = (
203
312
  },
204
313
  data: orderPositionBooks,
205
314
  },
315
+ {
316
+ type: 'line',
317
+ name: 'sentiment-short',
318
+ id: 'sentiment',
319
+ xAxisIndex: 1,
320
+ yAxisIndex: 1,
321
+ data: sentiments,
322
+ showSymbol: false,
323
+ symbol: 'none',
324
+ lineStyle: {
325
+ width: 3,
326
+ opacity: 1,
327
+ },
328
+ areaStyle: {
329
+ color: sentimentShortColor,
330
+ opacity: sentimentAreaOpacity,
331
+ },
332
+ emphasis: {
333
+ areaStyle: {
334
+ opacity: 0,
335
+ },
336
+ },
337
+ stack: 'sentiment',
338
+ },
339
+ {
340
+ type: 'line',
341
+ name: 'sentiment-long',
342
+ id: 'sentiment-long',
343
+ xAxisIndex: 1,
344
+ yAxisIndex: 1,
345
+ data: sentiments.map((sentiment) => [sentiment[0], sentiment[2]]),
346
+ symbol: 'none',
347
+ lineStyle: {
348
+ color: 'transparent',
349
+ width: 0,
350
+ },
351
+ areaStyle: {
352
+ color: sentimentLongColor,
353
+ opacity: sentimentAreaOpacity,
354
+ },
355
+ stack: 'sentiment',
356
+ emphasis: {
357
+ disabled: true,
358
+ },
359
+ tooltip: {
360
+ show: false,
361
+ },
362
+ },
206
363
  ],
207
364
  grid: [
208
365
  {
209
366
  name: 'main-grid',
367
+ id: 'main-grid',
368
+ height: `${CHART_CONFIG.MAIN_HEIGHT}px`,
210
369
  top: '0px',
211
370
  left: '0px',
212
371
  right: `${isDesktop ? CHART_CONFIG.Y_LABEL_SIZE_DESKTOP : CHART_CONFIG.Y_LABEL_SIZE_MOBILE}px`,
213
372
  bottom: `${CHART_CONFIG.X_LABEL_SIZE}px`,
214
373
  },
374
+ {
375
+ name: 'sentiment-grid',
376
+ id: 'sentiment-grid',
377
+ height: `${CHART_CONFIG.SENTIMENT_HEIGHT}px`,
378
+ bottom: `0px`,
379
+ left: '0px',
380
+ right: `${isDesktop ? CHART_CONFIG.Y_LABEL_SIZE_DESKTOP : CHART_CONFIG.Y_LABEL_SIZE_MOBILE}px`,
381
+ },
215
382
  ],
216
383
  graphic: [
217
- ...gridLines,
384
+ ...gridMainLines,
218
385
  {
219
- ...getLineCommons(isDark as boolean),
220
- top: 0,
221
- right: 0,
222
- shape: {
223
- x1: 0,
224
- y1: 0,
225
- x2: 0,
226
- y2: 0,
386
+ type: 'text',
387
+ silent: true,
388
+ z: 1,
389
+ left: 4,
390
+ bottom: CHART_CONFIG_CALCULATED.SENTIMENT_TEXT_POSITION,
391
+ style: {
392
+ text: `${labelCallback('sentiment')}:`,
393
+ fontFamily: 'Sofia W03',
394
+ fontSize: 14,
395
+ fontWeight: 'bold',
396
+ fill: sentimentLabelColor,
227
397
  },
228
398
  },
229
399
  ],
@@ -19,6 +19,8 @@ export interface UseCrowdViewDataProps {
19
19
  granularity: Granularity;
20
20
  }
21
21
 
22
+ export type ProcessedSentiment = [string, number, number];
23
+
22
24
  interface CrowdViewData {
23
25
  xAxisData: string[];
24
26
  // [open, close, low, high]
@@ -29,6 +31,7 @@ interface CrowdViewData {
29
31
  buckets: Bucket[][];
30
32
  precision: number;
31
33
  bookType: BookType;
34
+ sentiments: ProcessedSentiment[];
32
35
  }
33
36
 
34
37
  export interface UseCrowdViewDataReturn {
@@ -60,3 +63,55 @@ export interface GetLabelsDataProps {
60
63
  xAxisData: string[];
61
64
  isGreaterThanTwoWeeks: boolean;
62
65
  }
66
+
67
+ interface BaseTooltipParam {
68
+ [key: string]: unknown;
69
+ seriesId?: string;
70
+ seriesName?: string;
71
+ seriesType?: string;
72
+ axisValue?: string | number;
73
+ axisValueLabel?: string;
74
+ axisDimension?: string;
75
+ axisIndex?: number;
76
+ value: unknown;
77
+ }
78
+
79
+ export type TooltipParam =
80
+ | (BaseTooltipParam & {
81
+ seriesId: 'candlestick';
82
+ value: [number, number, number, number, number]; // [0, open, close, low, high]
83
+ })
84
+ | (BaseTooltipParam & {
85
+ seriesId: 'heatmap';
86
+ value: [string, number, number]; // [time, price, index]
87
+ })
88
+ | (BaseTooltipParam & {
89
+ seriesId: 'sentiment';
90
+ value: [string, number, number]; // [time, shortPercent, longPercent]
91
+ });
92
+
93
+ export interface DataZoomBatchItem {
94
+ [key: string]: unknown;
95
+ start: number;
96
+ end: number;
97
+ dataZoomId?: string;
98
+ }
99
+
100
+ export interface DataZoomEvent {
101
+ [key: string]: unknown;
102
+ type: 'datazoom';
103
+ batch?: DataZoomBatchItem[];
104
+ }
105
+
106
+ export interface DataZoomItem {
107
+ startValue: number;
108
+ endValue: number;
109
+ }
110
+
111
+ export type DataZoomArray = Array<DataZoomItem>;
112
+
113
+ export interface XAxisItem {
114
+ name?: string;
115
+ }
116
+
117
+ export type XAxisArray = Array<XAxisItem>;