@oanda/labs-crowd-view-widget 1.0.51 → 1.0.53

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 (153) hide show
  1. package/CHANGELOG.md +428 -0
  2. package/dist/main/CrowdViewWidget/Main.js +1 -5
  3. package/dist/main/CrowdViewWidget/Main.js.map +1 -1
  4. package/dist/main/CrowdViewWidget/components/Chart/Chart.js +73 -24
  5. package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  6. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +18 -9
  7. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  8. package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js +265 -59
  9. package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
  10. package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
  11. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js +68 -26
  12. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  13. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js +20 -19
  14. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
  15. package/dist/main/CrowdViewWidget/components/Chart/utils/getChartStyles.js +27 -0
  16. package/dist/main/CrowdViewWidget/components/Chart/utils/getChartStyles.js.map +1 -0
  17. package/dist/main/CrowdViewWidget/components/Chart/utils/getGridLines.js +123 -0
  18. package/dist/main/CrowdViewWidget/components/Chart/utils/getGridLines.js.map +1 -0
  19. package/dist/main/CrowdViewWidget/components/Chart/utils/index.js +15 -26
  20. package/dist/main/CrowdViewWidget/components/Chart/utils/index.js.map +1 -1
  21. package/dist/main/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js +54 -12
  22. package/dist/main/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js.map +1 -1
  23. package/dist/main/CrowdViewWidget/components/Chart/utils/processPriceCandles.js +49 -27
  24. package/dist/main/CrowdViewWidget/components/Chart/utils/processPriceCandles.js.map +1 -1
  25. package/dist/main/CrowdViewWidget/components/Chart/utils/processSentiments.js +43 -0
  26. package/dist/main/CrowdViewWidget/components/Chart/utils/processSentiments.js.map +1 -0
  27. package/dist/main/CrowdViewWidget/components/Chart/utils/validateData.js +8 -2
  28. package/dist/main/CrowdViewWidget/components/Chart/utils/validateData.js.map +1 -1
  29. package/dist/main/CrowdViewWidget/components/Legend/Legend.js +3 -4
  30. package/dist/main/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  31. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js +2 -2
  32. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  33. package/dist/main/CrowdViewWidget/constants.js +14 -8
  34. package/dist/main/CrowdViewWidget/constants.js.map +1 -1
  35. package/dist/main/gql/getOrderPositionBooks.js +1 -1
  36. package/dist/main/gql/getOrderPositionBooks.js.map +1 -1
  37. package/dist/main/gql/getPriceCandles.js +1 -1
  38. package/dist/main/gql/getPriceCandles.js.map +1 -1
  39. package/dist/main/gql/getSentiments.js +11 -0
  40. package/dist/main/gql/getSentiments.js.map +1 -0
  41. package/dist/main/gql/types/gql.js +3 -2
  42. package/dist/main/gql/types/gql.js.map +1 -1
  43. package/dist/main/gql/types/graphql.js +273 -19
  44. package/dist/main/gql/types/graphql.js.map +1 -1
  45. package/dist/module/CrowdViewWidget/Main.js +2 -6
  46. package/dist/module/CrowdViewWidget/Main.js.map +1 -1
  47. package/dist/module/CrowdViewWidget/components/Chart/Chart.js +76 -27
  48. package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  49. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +18 -9
  50. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  51. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js +265 -60
  52. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
  53. package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
  54. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +69 -27
  55. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  56. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js +21 -20
  57. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
  58. package/dist/module/CrowdViewWidget/components/Chart/utils/getChartStyles.js +20 -0
  59. package/dist/module/CrowdViewWidget/components/Chart/utils/getChartStyles.js.map +1 -0
  60. package/dist/module/CrowdViewWidget/components/Chart/utils/getGridLines.js +116 -0
  61. package/dist/module/CrowdViewWidget/components/Chart/utils/getGridLines.js.map +1 -0
  62. package/dist/module/CrowdViewWidget/components/Chart/utils/index.js +2 -3
  63. package/dist/module/CrowdViewWidget/components/Chart/utils/index.js.map +1 -1
  64. package/dist/module/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js +54 -12
  65. package/dist/module/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js.map +1 -1
  66. package/dist/module/CrowdViewWidget/components/Chart/utils/processPriceCandles.js +49 -27
  67. package/dist/module/CrowdViewWidget/components/Chart/utils/processPriceCandles.js.map +1 -1
  68. package/dist/module/CrowdViewWidget/components/Chart/utils/processSentiments.js +36 -0
  69. package/dist/module/CrowdViewWidget/components/Chart/utils/processSentiments.js.map +1 -0
  70. package/dist/module/CrowdViewWidget/components/Chart/utils/validateData.js +8 -2
  71. package/dist/module/CrowdViewWidget/components/Chart/utils/validateData.js.map +1 -1
  72. package/dist/module/CrowdViewWidget/components/Legend/Legend.js +3 -4
  73. package/dist/module/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  74. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js +2 -2
  75. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  76. package/dist/module/CrowdViewWidget/constants.js +13 -7
  77. package/dist/module/CrowdViewWidget/constants.js.map +1 -1
  78. package/dist/module/gql/getOrderPositionBooks.js +1 -1
  79. package/dist/module/gql/getOrderPositionBooks.js.map +1 -1
  80. package/dist/module/gql/getPriceCandles.js +1 -1
  81. package/dist/module/gql/getPriceCandles.js.map +1 -1
  82. package/dist/module/gql/getSentiments.js +6 -0
  83. package/dist/module/gql/getSentiments.js.map +1 -0
  84. package/dist/module/gql/types/gql.js +3 -2
  85. package/dist/module/gql/types/gql.js.map +1 -1
  86. package/dist/module/gql/types/graphql.js +272 -18
  87. package/dist/module/gql/types/graphql.js.map +1 -1
  88. package/dist/types/CrowdViewWidget/components/Chart/Chart.d.ts +1 -1
  89. package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +69 -9
  90. package/dist/types/CrowdViewWidget/components/Chart/utils/chartUtils.d.ts +5 -6
  91. package/dist/types/CrowdViewWidget/components/Chart/utils/getChartStyles.d.ts +10 -0
  92. package/dist/types/CrowdViewWidget/components/Chart/utils/getGridLines.d.ts +97 -0
  93. package/dist/types/CrowdViewWidget/components/Chart/utils/index.d.ts +2 -3
  94. package/dist/types/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.d.ts +10 -7
  95. package/dist/types/CrowdViewWidget/components/Chart/utils/processPriceCandles.d.ts +6 -21
  96. package/dist/types/CrowdViewWidget/components/Chart/utils/processSentiments.d.ts +6 -0
  97. package/dist/types/CrowdViewWidget/components/Chart/utils/validateData.d.ts +1 -1
  98. package/dist/types/CrowdViewWidget/components/Legend/Legend.d.ts +2 -2
  99. package/dist/types/CrowdViewWidget/components/Legend/LegendBar.d.ts +1 -1
  100. package/dist/types/CrowdViewWidget/constants.d.ts +12 -6
  101. package/dist/types/gql/getSentiments.d.ts +2 -0
  102. package/dist/types/gql/types/gql.d.ts +15 -4
  103. package/dist/types/gql/types/graphql.d.ts +66 -11
  104. package/package.json +3 -3
  105. package/src/CrowdViewWidget/Main.tsx +2 -4
  106. package/src/CrowdViewWidget/components/Chart/Chart.tsx +99 -38
  107. package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +24 -7
  108. package/src/CrowdViewWidget/components/Chart/chartOptions.ts +305 -87
  109. package/src/CrowdViewWidget/components/Chart/types.ts +82 -16
  110. package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +105 -56
  111. package/src/CrowdViewWidget/components/Chart/utils/chartUtils.ts +65 -34
  112. package/src/CrowdViewWidget/components/Chart/utils/getChartStyles.ts +42 -0
  113. package/src/CrowdViewWidget/components/Chart/utils/getGridLines.ts +148 -0
  114. package/src/CrowdViewWidget/components/Chart/utils/index.ts +2 -3
  115. package/src/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.ts +84 -22
  116. package/src/CrowdViewWidget/components/Chart/utils/processPriceCandles.ts +52 -38
  117. package/src/CrowdViewWidget/components/Chart/utils/processSentiments.ts +55 -0
  118. package/src/CrowdViewWidget/components/Chart/utils/validateData.ts +10 -2
  119. package/src/CrowdViewWidget/components/Legend/Legend.tsx +5 -6
  120. package/src/CrowdViewWidget/components/Legend/LegendBar.tsx +3 -3
  121. package/src/CrowdViewWidget/constants.ts +18 -7
  122. package/src/gql/getOrderPositionBooks.ts +13 -5
  123. package/src/gql/getPriceCandles.ts +1 -0
  124. package/src/gql/getSentiments.ts +25 -0
  125. package/src/gql/types/gql.ts +14 -6
  126. package/src/gql/types/graphql.ts +259 -16
  127. package/test/components/Chart/utils/chartUtils.test.ts +105 -13
  128. package/test/components/Chart/utils/getChartStyles.test.ts +64 -0
  129. package/test/components/Chart/utils/processSentiments.test.ts +238 -0
  130. package/test/utils/processOrderPositionBooks.test.ts +201 -84
  131. package/test/utils/processPriceCandles.test.ts +93 -67
  132. package/test/utils/validateData.test.ts +136 -38
  133. package/dist/main/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js +0 -37
  134. package/dist/main/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js.map +0 -1
  135. package/dist/main/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js +0 -14
  136. package/dist/main/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js.map +0 -1
  137. package/dist/main/CrowdViewWidget/components/Chart/utils/processBuckets.js +0 -29
  138. package/dist/main/CrowdViewWidget/components/Chart/utils/processBuckets.js.map +0 -1
  139. package/dist/module/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js +0 -29
  140. package/dist/module/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js.map +0 -1
  141. package/dist/module/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js +0 -7
  142. package/dist/module/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js.map +0 -1
  143. package/dist/module/CrowdViewWidget/components/Chart/utils/processBuckets.js +0 -22
  144. package/dist/module/CrowdViewWidget/components/Chart/utils/processBuckets.js.map +0 -1
  145. package/dist/types/CrowdViewWidget/components/Chart/utils/aggregateBuckets.d.ts +0 -2
  146. package/dist/types/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.d.ts +0 -3
  147. package/dist/types/CrowdViewWidget/components/Chart/utils/processBuckets.d.ts +0 -3
  148. package/src/CrowdViewWidget/components/Chart/utils/aggregateBuckets.ts +0 -44
  149. package/src/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.ts +0 -13
  150. package/src/CrowdViewWidget/components/Chart/utils/processBuckets.ts +0 -43
  151. package/test/utils/aggregateBuckets.test.ts +0 -82
  152. package/test/utils/getTargetBucketWidth.test.ts +0 -37
  153. package/test/utils/processBuckets.test.ts +0 -153
@@ -0,0 +1,238 @@
1
+ import { processSentiments } from '../../../../src/CrowdViewWidget/components/Chart/utils/processSentiments';
2
+ import type { GetSentimentsQuery } from '../../../../src/gql/types/graphql';
3
+
4
+ describe('processSentiments', () => {
5
+ it('returns empty arrays when sentimentsData is undefined', () => {
6
+ const result = processSentiments(undefined, ['2025-03-15T10:30:00Z']);
7
+ expect(result).toEqual({
8
+ sentimentLongs: [],
9
+ sentimentShorts: [],
10
+ hasValidSentiments: false,
11
+ });
12
+ });
13
+
14
+ it('returns empty arrays when sentiments array is empty', () => {
15
+ const sentimentsData: GetSentimentsQuery = {
16
+ sentiments: {
17
+ sentiments: [],
18
+ },
19
+ };
20
+ const result = processSentiments(sentimentsData, ['2025-03-15T10:30:00Z']);
21
+ expect(result).toEqual({
22
+ sentimentLongs: [],
23
+ sentimentShorts: [],
24
+ hasValidSentiments: false,
25
+ });
26
+ });
27
+
28
+ it('returns empty arrays when sentiments is null', () => {
29
+ const sentimentsData: GetSentimentsQuery = {
30
+ sentiments: {
31
+ sentiments: null as never,
32
+ },
33
+ };
34
+ const result = processSentiments(sentimentsData, ['2025-03-15T10:30:00Z']);
35
+ expect(result).toEqual({
36
+ sentimentLongs: [],
37
+ sentimentShorts: [],
38
+ hasValidSentiments: false,
39
+ });
40
+ });
41
+
42
+ it('processes sentiments and matches with dates', () => {
43
+ const dates = [
44
+ '2025-03-15T10:30:00Z',
45
+ '2025-03-15T11:30:00Z',
46
+ '2025-03-15T12:30:00Z',
47
+ ];
48
+ const sentimentsData: GetSentimentsQuery = {
49
+ sentiments: {
50
+ sentiments: [
51
+ {
52
+ time: '2025-03-15T10:30:00Z',
53
+ sentiment: {
54
+ shortPercent: 30.5,
55
+ longPercent: 69.5,
56
+ },
57
+ },
58
+ {
59
+ time: '2025-03-15T11:30:00Z',
60
+ sentiment: {
61
+ shortPercent: 40.2,
62
+ longPercent: 59.8,
63
+ },
64
+ },
65
+ ],
66
+ },
67
+ };
68
+
69
+ const result = processSentiments(sentimentsData, dates);
70
+ expect(result.hasValidSentiments).toBe(true);
71
+ expect(result.sentimentShorts).toEqual([30.5, 40.2, null]);
72
+ expect(result.sentimentLongs).toEqual([69.5, 59.8, null]);
73
+ });
74
+
75
+ it('returns null for dates that do not match any sentiment', () => {
76
+ const dates = ['2025-03-15T10:30:00Z', '2025-03-15T11:30:00Z'];
77
+ const sentimentsData: GetSentimentsQuery = {
78
+ sentiments: {
79
+ sentiments: [
80
+ {
81
+ time: '2025-03-15T10:30:00Z',
82
+ sentiment: {
83
+ shortPercent: 30.5,
84
+ longPercent: 69.5,
85
+ },
86
+ },
87
+ {
88
+ time: '2025-03-15T12:30:00Z', // Not in dates
89
+ sentiment: {
90
+ shortPercent: 50.0,
91
+ longPercent: 50.0,
92
+ },
93
+ },
94
+ ],
95
+ },
96
+ };
97
+
98
+ const result = processSentiments(sentimentsData, dates);
99
+ expect(result.sentimentShorts).toEqual([30.5, null]);
100
+ expect(result.sentimentLongs).toEqual([69.5, null]);
101
+ });
102
+
103
+ it('returns null arrays when no sentiments match dates', () => {
104
+ const dates = ['2025-03-15T10:30:00Z'];
105
+ const sentimentsData: GetSentimentsQuery = {
106
+ sentiments: {
107
+ sentiments: [
108
+ {
109
+ time: '2025-03-15T11:30:00Z',
110
+ sentiment: {
111
+ shortPercent: 30.5,
112
+ longPercent: 69.5,
113
+ },
114
+ },
115
+ ],
116
+ },
117
+ };
118
+
119
+ const result = processSentiments(sentimentsData, dates);
120
+ expect(result.sentimentShorts).toEqual([null]);
121
+ expect(result.sentimentLongs).toEqual([null]);
122
+ });
123
+
124
+ it('handles multiple dates with mixed matching sentiments', () => {
125
+ const dates = [
126
+ '2025-03-15T10:30:00Z',
127
+ '2025-03-15T11:30:00Z',
128
+ '2025-03-15T12:30:00Z',
129
+ '2025-03-15T13:30:00Z',
130
+ ];
131
+ const sentimentsData: GetSentimentsQuery = {
132
+ sentiments: {
133
+ sentiments: [
134
+ {
135
+ time: '2025-03-15T10:30:00Z',
136
+ sentiment: {
137
+ shortPercent: 30.5,
138
+ longPercent: 69.5,
139
+ },
140
+ },
141
+ {
142
+ time: '2025-03-15T12:30:00Z',
143
+ sentiment: {
144
+ shortPercent: 25.0,
145
+ longPercent: 75.0,
146
+ },
147
+ },
148
+ ],
149
+ },
150
+ };
151
+
152
+ const result = processSentiments(sentimentsData, dates);
153
+ expect(result.sentimentShorts).toEqual([30.5, null, 25.0, null]);
154
+ expect(result.sentimentLongs).toEqual([69.5, null, 75.0, null]);
155
+ });
156
+
157
+ it('should handle null sentiment objects gracefully', () => {
158
+ const dates = ['2025-03-15T10:30:00Z'];
159
+ const sentimentsData: GetSentimentsQuery = {
160
+ sentiments: {
161
+ sentiments: [
162
+ {
163
+ time: '2025-03-15T10:30:00Z',
164
+ sentiment: null as never,
165
+ },
166
+ ],
167
+ },
168
+ };
169
+
170
+ const result = processSentiments(sentimentsData, dates);
171
+ expect(result.sentimentShorts).toEqual([null]);
172
+ expect(result.sentimentLongs).toEqual([null]);
173
+ });
174
+
175
+ it('should handle undefined sentiment properties', () => {
176
+ const dates = ['2025-03-15T10:30:00Z'];
177
+ const sentimentsData: GetSentimentsQuery = {
178
+ sentiments: {
179
+ sentiments: [
180
+ {
181
+ time: '2025-03-15T10:30:00Z',
182
+ sentiment: {
183
+ shortPercent: undefined as never,
184
+ longPercent: undefined as never,
185
+ },
186
+ },
187
+ ],
188
+ },
189
+ };
190
+
191
+ const result = processSentiments(sentimentsData, dates);
192
+ expect(result.sentimentShorts).toEqual([null]);
193
+ expect(result.sentimentLongs).toEqual([null]);
194
+ });
195
+
196
+ it('should return empty arrays when dates array is empty', () => {
197
+ const sentimentsData: GetSentimentsQuery = {
198
+ sentiments: {
199
+ sentiments: [
200
+ {
201
+ time: '2025-03-15T10:30:00Z',
202
+ sentiment: {
203
+ shortPercent: 30.5,
204
+ longPercent: 69.5,
205
+ },
206
+ },
207
+ ],
208
+ },
209
+ };
210
+
211
+ const result = processSentiments(sentimentsData, []);
212
+ expect(result).toEqual({
213
+ sentimentLongs: [],
214
+ sentimentShorts: [],
215
+ hasValidSentiments: false,
216
+ });
217
+ });
218
+
219
+ it('should return empty arrays when dates is not an array', () => {
220
+ const sentimentsData: GetSentimentsQuery = {
221
+ sentiments: {
222
+ sentiments: [
223
+ {
224
+ time: '2025-03-15T10:30:00Z',
225
+ sentiment: {
226
+ shortPercent: 30.5,
227
+ longPercent: 69.5,
228
+ },
229
+ },
230
+ ],
231
+ },
232
+ };
233
+
234
+ // @ts-expect-error - Testing invalid input
235
+ const result = processSentiments(sentimentsData, null);
236
+ expect(result.hasValidSentiments).toBe(false);
237
+ });
238
+ });
@@ -3,125 +3,242 @@ import type { GetOrderPositionBooksQuery } from '../../src/gql/types/graphql';
3
3
 
4
4
  describe('processOrderPositionBooks', () => {
5
5
  const createMockOrderPositionBook = (time: string) => ({
6
- bucketWidth: 0.0005,
7
- price: 1.0,
8
6
  time,
9
- buckets: [{ price: 1.0, sentiment: 0.2 }],
7
+ price: 1.0,
8
+ buckets: [
9
+ { price: 1.0, sentiment: 0.2 },
10
+ { price: 1.0005, sentiment: -0.3 },
11
+ ],
10
12
  });
11
13
 
12
- it('should return empty array when orderPositionData is undefined', () => {
13
- const candleMap = new Map();
14
- expect(processOrderPositionBooks(undefined, candleMap)).toEqual([]);
14
+ it('should return empty arrays when orderPositionData is undefined', () => {
15
+ const result = processOrderPositionBooks(undefined, []);
16
+ expect(result).toEqual({
17
+ bookPrices: [],
18
+ bookIndexes: [],
19
+ buckets: [],
20
+ bucketWidth: 0,
21
+ sentimentThresholdMin: 0,
22
+ sentimentThresholdMax: 0,
23
+ hasValidBooks: false,
24
+ });
15
25
  });
16
26
 
17
- it('should return empty array when orderPositionBooks is empty', () => {
27
+ it('should return empty arrays when orderPositionBooks is undefined', () => {
18
28
  const mockData: GetOrderPositionBooksQuery = {
19
- orderPositionBooks: [],
29
+ orderPositionBooks: {} as any,
20
30
  };
21
- const candleMap = new Map();
22
- expect(processOrderPositionBooks(mockData, candleMap)).toEqual([]);
31
+ const result = processOrderPositionBooks(mockData, []);
32
+ expect(result).toEqual({
33
+ bookPrices: [],
34
+ bookIndexes: [],
35
+ buckets: [],
36
+ bucketWidth: 0,
37
+ sentimentThresholdMin: 0,
38
+ sentimentThresholdMax: 0,
39
+ hasValidBooks: false,
40
+ });
23
41
  });
24
42
 
25
- it('should filter out null books', () => {
43
+ it('should return empty arrays when books array is empty', () => {
26
44
  const mockData: GetOrderPositionBooksQuery = {
27
- orderPositionBooks: [null, createMockOrderPositionBook('2025-01-01')],
45
+ orderPositionBooks: {
46
+ books: [],
47
+ bucketWidth: 0.0005,
48
+ sentimentThresholdMin: 0.15,
49
+ sentimentThresholdMax: 0.55,
50
+ },
28
51
  };
29
- const candleMap = new Map();
30
- const result = processOrderPositionBooks(mockData, candleMap);
31
- expect(result).toHaveLength(1);
32
- expect(result[0]).toEqual(['2025-01-01', null, 0]);
52
+ const result = processOrderPositionBooks(mockData, []);
53
+ expect(result).toEqual({
54
+ bookPrices: [],
55
+ bookIndexes: [],
56
+ buckets: [],
57
+ bucketWidth: 0,
58
+ sentimentThresholdMin: 0,
59
+ sentimentThresholdMax: 0,
60
+ hasValidBooks: false,
61
+ });
33
62
  });
34
63
 
35
- it('should filter out books with no buckets', () => {
64
+ it('should return empty arrays when sentimentThresholdMin is missing', () => {
36
65
  const mockData: GetOrderPositionBooksQuery = {
37
- orderPositionBooks: [
38
- {
39
- bucketWidth: 0.0005,
40
- price: 1.0,
41
- time: '2025-01-01',
42
- buckets: [],
43
- },
44
- createMockOrderPositionBook('2025-01-02'),
45
- ],
66
+ orderPositionBooks: {
67
+ books: [createMockOrderPositionBook('2025-01-01T00:00:00Z')],
68
+ bucketWidth: 0.0005,
69
+ sentimentThresholdMin: null as any,
70
+ sentimentThresholdMax: 0.55,
71
+ },
46
72
  };
47
- const candleMap = new Map();
48
- const result = processOrderPositionBooks(mockData, candleMap);
49
- expect(result).toHaveLength(1);
50
- expect(result[0]).toEqual(['2025-01-02', null, 0]);
73
+ const result = processOrderPositionBooks(mockData, [
74
+ '2025-01-01T00:00:00Z',
75
+ ]);
76
+ expect(result.hasValidBooks).toBe(false);
51
77
  });
52
78
 
53
- it('should use candle high price when available in map', () => {
79
+ it('should process valid order position books and match with dates', () => {
80
+ const dates = [
81
+ '2025-01-01T00:00:00Z',
82
+ '2025-01-01T01:00:00Z',
83
+ '2025-01-01T02:00:00Z',
84
+ ];
54
85
  const mockData: GetOrderPositionBooksQuery = {
55
- orderPositionBooks: [
56
- createMockOrderPositionBook('2025-01-01'),
57
- createMockOrderPositionBook('2025-01-02'),
58
- ],
86
+ orderPositionBooks: {
87
+ books: [
88
+ createMockOrderPositionBook('2025-01-01T00:00:00Z'),
89
+ createMockOrderPositionBook('2025-01-01T01:00:00Z'),
90
+ ],
91
+ bucketWidth: 0.0005,
92
+ sentimentThresholdMin: 0.15,
93
+ sentimentThresholdMax: 0.55,
94
+ },
59
95
  };
60
- const candleMap = new Map([
61
- ['2025-01-01', { high: 1.1234 }],
62
- ['2025-01-02', { high: 1.5678 }],
63
- ]);
64
- const result = processOrderPositionBooks(mockData, candleMap);
65
- expect(result).toHaveLength(2);
66
- expect(result[0]).toEqual(['2025-01-01', 1.1234, 0]);
67
- expect(result[1]).toEqual(['2025-01-02', 1.5678, 1]);
96
+ const result = processOrderPositionBooks(mockData, dates);
97
+ expect(result.hasValidBooks).toBe(true);
98
+ expect(result.bucketWidth).toBe(0.0005);
99
+ expect(result.sentimentThresholdMin).toBe(0.15);
100
+ expect(result.sentimentThresholdMax).toBe(0.55);
101
+ expect(result.bookPrices).toHaveLength(3);
102
+ expect(result.bookIndexes).toHaveLength(3);
103
+ expect(result.buckets).toHaveLength(3);
104
+ expect(result.bookPrices[0]).toBe(1.0);
105
+ expect(result.bookPrices[1]).toBe(1.0);
106
+ expect(result.bookPrices[2]).toBeNull();
107
+ expect(result.bookIndexes).toEqual([0, 1, 2]);
108
+ expect(result.buckets[0]).toHaveLength(2);
109
+ expect(result.buckets[1]).toHaveLength(2);
110
+ expect(result.buckets[2]).toEqual([]);
68
111
  });
69
112
 
70
- it('should use null when candle is not in map', () => {
113
+ it('should handle dates that do not match any book', () => {
114
+ const dates = [
115
+ '2025-01-01T00:00:00Z',
116
+ '2025-01-01T01:00:00Z',
117
+ '2025-01-01T02:00:00Z',
118
+ ];
71
119
  const mockData: GetOrderPositionBooksQuery = {
72
- orderPositionBooks: [createMockOrderPositionBook('2025-01-01')],
120
+ orderPositionBooks: {
121
+ books: [createMockOrderPositionBook('2025-01-01T00:00:00Z')],
122
+ bucketWidth: 0.0005,
123
+ sentimentThresholdMin: 0.15,
124
+ sentimentThresholdMax: 0.55,
125
+ },
73
126
  };
74
- const candleMap = new Map();
75
- const result = processOrderPositionBooks(mockData, candleMap);
76
- expect(result).toHaveLength(1);
77
- expect(result[0]).toEqual(['2025-01-01', null, 0]);
127
+ const result = processOrderPositionBooks(mockData, dates);
128
+ expect(result.bookPrices).toEqual([1.0, null, null]);
129
+ expect(result.buckets[0]).toHaveLength(2);
130
+ expect(result.buckets[1]).toEqual([]);
131
+ expect(result.buckets[2]).toEqual([]);
78
132
  });
79
133
 
80
- it('should use null when candle high is undefined', () => {
134
+ it('should handle books with null price', () => {
135
+ const dates = ['2025-01-01T00:00:00Z'];
81
136
  const mockData: GetOrderPositionBooksQuery = {
82
- orderPositionBooks: [createMockOrderPositionBook('2025-01-01')],
137
+ orderPositionBooks: {
138
+ books: [
139
+ {
140
+ time: '2025-01-01T00:00:00Z',
141
+ price: null,
142
+ buckets: [{ price: 1.0, sentiment: 0.2 }],
143
+ },
144
+ ],
145
+ bucketWidth: 0.0005,
146
+ sentimentThresholdMin: 0.15,
147
+ sentimentThresholdMax: 0.55,
148
+ },
83
149
  };
84
- const candleMap = new Map([
85
- ['2025-01-01', { low: 1.0 }], // no high property
150
+ const result = processOrderPositionBooks(mockData, dates);
151
+ expect(result.bookPrices[0]).toBeNull();
152
+ expect(result.buckets[0]).toHaveLength(1);
153
+ });
154
+
155
+ it('should correctly map buckets structure', () => {
156
+ const dates = ['2025-01-01T00:00:00Z'];
157
+ const mockData: GetOrderPositionBooksQuery = {
158
+ orderPositionBooks: {
159
+ books: [
160
+ {
161
+ time: '2025-01-01T00:00:00Z',
162
+ price: 1.0,
163
+ buckets: [
164
+ { price: 1.0, sentiment: 0.2 },
165
+ { price: 1.0005, sentiment: -0.3 },
166
+ { price: 1.001, sentiment: 0.5 },
167
+ ],
168
+ },
169
+ ],
170
+ bucketWidth: 0.0005,
171
+ sentimentThresholdMin: 0.15,
172
+ sentimentThresholdMax: 0.55,
173
+ },
174
+ };
175
+ const result = processOrderPositionBooks(mockData, dates);
176
+ expect(result.buckets[0]).toEqual([
177
+ { price: 1.0, sentiment: 0.2 },
178
+ { price: 1.0005, sentiment: -0.3 },
179
+ { price: 1.001, sentiment: 0.5 },
86
180
  ]);
87
- const result = processOrderPositionBooks(mockData, candleMap);
88
- expect(result).toHaveLength(1);
89
- expect(result[0]).toEqual(['2025-01-01', null, 0]);
90
181
  });
91
182
 
92
- it('should correctly assign index to each book', () => {
183
+ it('should handle duplicate time values in books array (last one wins)', () => {
184
+ const dates = ['2025-01-01T00:00:00Z'];
93
185
  const mockData: GetOrderPositionBooksQuery = {
94
- orderPositionBooks: [
95
- createMockOrderPositionBook('2025-01-01'),
96
- createMockOrderPositionBook('2025-01-02'),
97
- createMockOrderPositionBook('2025-01-03'),
98
- ],
186
+ orderPositionBooks: {
187
+ books: [
188
+ {
189
+ time: '2025-01-01T00:00:00Z',
190
+ price: 1.0,
191
+ buckets: [{ price: 1.0, sentiment: 0.2 }],
192
+ },
193
+ {
194
+ time: '2025-01-01T00:00:00Z',
195
+ price: 1.5,
196
+ buckets: [{ price: 1.5, sentiment: 0.8 }],
197
+ },
198
+ ],
199
+ bucketWidth: 0.0005,
200
+ sentimentThresholdMin: 0.15,
201
+ sentimentThresholdMax: 0.55,
202
+ },
99
203
  };
100
- const candleMap = new Map();
101
- const result = processOrderPositionBooks(mockData, candleMap);
102
- expect(result).toHaveLength(3);
103
- expect(result[0][2]).toBe(0);
104
- expect(result[1][2]).toBe(1);
105
- expect(result[2][2]).toBe(2);
204
+ const result = processOrderPositionBooks(mockData, dates);
205
+ // Last entry should win
206
+ expect(result.bookPrices[0]).toBe(1.5);
207
+ expect(result.buckets[0]).toEqual([{ price: 1.5, sentiment: 0.8 }]);
106
208
  });
107
209
 
108
- it('should handle mixed cases with and without candles', () => {
210
+ it('should return empty arrays when dates array is empty', () => {
109
211
  const mockData: GetOrderPositionBooksQuery = {
110
- orderPositionBooks: [
111
- createMockOrderPositionBook('2025-01-01'),
112
- createMockOrderPositionBook('2025-01-02'),
113
- createMockOrderPositionBook('2025-01-03'),
114
- ],
212
+ orderPositionBooks: {
213
+ books: [createMockOrderPositionBook('2025-01-01T00:00:00Z')],
214
+ bucketWidth: 0.0005,
215
+ sentimentThresholdMin: 0.15,
216
+ sentimentThresholdMax: 0.55,
217
+ },
115
218
  };
116
- const candleMap = new Map([
117
- ['2025-01-01', { high: 1.1111 }],
118
- // 2025-01-02 missing from map
119
- ['2025-01-03', { high: 1.3333 }],
120
- ]);
121
- const result = processOrderPositionBooks(mockData, candleMap);
122
- expect(result).toHaveLength(3);
123
- expect(result[0]).toEqual(['2025-01-01', 1.1111, 0]);
124
- expect(result[1]).toEqual(['2025-01-02', null, 1]);
125
- expect(result[2]).toEqual(['2025-01-03', 1.3333, 2]);
219
+ const result = processOrderPositionBooks(mockData, []);
220
+ expect(result).toEqual({
221
+ bookPrices: [],
222
+ bookIndexes: [],
223
+ buckets: [],
224
+ bucketWidth: 0,
225
+ sentimentThresholdMin: 0,
226
+ sentimentThresholdMax: 0,
227
+ hasValidBooks: false,
228
+ });
229
+ });
230
+
231
+ it('should return empty arrays when dates is not an array', () => {
232
+ const mockData: GetOrderPositionBooksQuery = {
233
+ orderPositionBooks: {
234
+ books: [createMockOrderPositionBook('2025-01-01T00:00:00Z')],
235
+ bucketWidth: 0.0005,
236
+ sentimentThresholdMin: 0.15,
237
+ sentimentThresholdMax: 0.55,
238
+ },
239
+ };
240
+ // @ts-expect-error - Testing invalid input
241
+ const result = processOrderPositionBooks(mockData, null);
242
+ expect(result.hasValidBooks).toBe(false);
126
243
  });
127
244
  });