@oanda/labs-crowd-view-widget 1.0.52 → 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 (134) hide show
  1. package/CHANGELOG.md +216 -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 +16 -6
  5. package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  6. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +15 -6
  7. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  8. package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js +69 -29
  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 +49 -26
  12. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  13. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js +9 -10
  14. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
  15. package/dist/main/CrowdViewWidget/components/Chart/utils/index.js +0 -33
  16. package/dist/main/CrowdViewWidget/components/Chart/utils/index.js.map +1 -1
  17. package/dist/main/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js +54 -12
  18. package/dist/main/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js.map +1 -1
  19. package/dist/main/CrowdViewWidget/components/Chart/utils/processPriceCandles.js +49 -27
  20. package/dist/main/CrowdViewWidget/components/Chart/utils/processPriceCandles.js.map +1 -1
  21. package/dist/main/CrowdViewWidget/components/Chart/utils/processSentiments.js +32 -17
  22. package/dist/main/CrowdViewWidget/components/Chart/utils/processSentiments.js.map +1 -1
  23. package/dist/main/CrowdViewWidget/components/Chart/utils/validateData.js +8 -2
  24. package/dist/main/CrowdViewWidget/components/Chart/utils/validateData.js.map +1 -1
  25. package/dist/main/CrowdViewWidget/components/Legend/Legend.js +2 -3
  26. package/dist/main/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  27. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js +2 -2
  28. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  29. package/dist/main/CrowdViewWidget/constants.js +2 -6
  30. package/dist/main/CrowdViewWidget/constants.js.map +1 -1
  31. package/dist/main/gql/getOrderPositionBooks.js +1 -1
  32. package/dist/main/gql/getOrderPositionBooks.js.map +1 -1
  33. package/dist/main/gql/getPriceCandles.js +1 -1
  34. package/dist/main/gql/getPriceCandles.js.map +1 -1
  35. package/dist/main/gql/types/gql.js +2 -2
  36. package/dist/main/gql/types/gql.js.map +1 -1
  37. package/dist/main/gql/types/graphql.js +111 -18
  38. package/dist/main/gql/types/graphql.js.map +1 -1
  39. package/dist/module/CrowdViewWidget/Main.js +2 -6
  40. package/dist/module/CrowdViewWidget/Main.js.map +1 -1
  41. package/dist/module/CrowdViewWidget/components/Chart/Chart.js +17 -7
  42. package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  43. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +15 -6
  44. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  45. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js +70 -30
  46. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
  47. package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
  48. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +50 -27
  49. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  50. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js +10 -11
  51. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
  52. package/dist/module/CrowdViewWidget/components/Chart/utils/index.js +0 -3
  53. package/dist/module/CrowdViewWidget/components/Chart/utils/index.js.map +1 -1
  54. package/dist/module/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js +54 -12
  55. package/dist/module/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js.map +1 -1
  56. package/dist/module/CrowdViewWidget/components/Chart/utils/processPriceCandles.js +49 -27
  57. package/dist/module/CrowdViewWidget/components/Chart/utils/processPriceCandles.js.map +1 -1
  58. package/dist/module/CrowdViewWidget/components/Chart/utils/processSentiments.js +32 -17
  59. package/dist/module/CrowdViewWidget/components/Chart/utils/processSentiments.js.map +1 -1
  60. package/dist/module/CrowdViewWidget/components/Chart/utils/validateData.js +8 -2
  61. package/dist/module/CrowdViewWidget/components/Chart/utils/validateData.js.map +1 -1
  62. package/dist/module/CrowdViewWidget/components/Legend/Legend.js +2 -3
  63. package/dist/module/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  64. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js +2 -2
  65. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  66. package/dist/module/CrowdViewWidget/constants.js +1 -5
  67. package/dist/module/CrowdViewWidget/constants.js.map +1 -1
  68. package/dist/module/gql/getOrderPositionBooks.js +1 -1
  69. package/dist/module/gql/getOrderPositionBooks.js.map +1 -1
  70. package/dist/module/gql/getPriceCandles.js +1 -1
  71. package/dist/module/gql/getPriceCandles.js.map +1 -1
  72. package/dist/module/gql/types/gql.js +2 -2
  73. package/dist/module/gql/types/gql.js.map +1 -1
  74. package/dist/module/gql/types/graphql.js +111 -18
  75. package/dist/module/gql/types/graphql.js.map +1 -1
  76. package/dist/types/CrowdViewWidget/components/Chart/Chart.d.ts +1 -1
  77. package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +28 -11
  78. package/dist/types/CrowdViewWidget/components/Chart/utils/chartUtils.d.ts +3 -4
  79. package/dist/types/CrowdViewWidget/components/Chart/utils/index.d.ts +0 -3
  80. package/dist/types/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.d.ts +10 -7
  81. package/dist/types/CrowdViewWidget/components/Chart/utils/processPriceCandles.d.ts +6 -21
  82. package/dist/types/CrowdViewWidget/components/Chart/utils/processSentiments.d.ts +5 -2
  83. package/dist/types/CrowdViewWidget/components/Chart/utils/validateData.d.ts +1 -1
  84. package/dist/types/CrowdViewWidget/components/Legend/Legend.d.ts +2 -2
  85. package/dist/types/CrowdViewWidget/components/Legend/LegendBar.d.ts +1 -1
  86. package/dist/types/CrowdViewWidget/constants.d.ts +1 -5
  87. package/dist/types/gql/types/gql.d.ts +6 -4
  88. package/dist/types/gql/types/graphql.d.ts +30 -11
  89. package/package.json +3 -3
  90. package/src/CrowdViewWidget/Main.tsx +2 -4
  91. package/src/CrowdViewWidget/components/Chart/Chart.tsx +15 -6
  92. package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +21 -4
  93. package/src/CrowdViewWidget/components/Chart/chartOptions.ts +78 -30
  94. package/src/CrowdViewWidget/components/Chart/types.ts +30 -19
  95. package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +82 -65
  96. package/src/CrowdViewWidget/components/Chart/utils/chartUtils.ts +32 -20
  97. package/src/CrowdViewWidget/components/Chart/utils/index.ts +0 -3
  98. package/src/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.ts +84 -22
  99. package/src/CrowdViewWidget/components/Chart/utils/processPriceCandles.ts +52 -38
  100. package/src/CrowdViewWidget/components/Chart/utils/processSentiments.ts +45 -32
  101. package/src/CrowdViewWidget/components/Chart/utils/validateData.ts +10 -2
  102. package/src/CrowdViewWidget/components/Legend/Legend.tsx +4 -5
  103. package/src/CrowdViewWidget/components/Legend/LegendBar.tsx +3 -3
  104. package/src/CrowdViewWidget/constants.ts +1 -6
  105. package/src/gql/getOrderPositionBooks.ts +13 -5
  106. package/src/gql/getPriceCandles.ts +1 -0
  107. package/src/gql/types/gql.ts +6 -6
  108. package/src/gql/types/graphql.ts +98 -16
  109. package/test/components/Chart/utils/chartUtils.test.ts +32 -14
  110. package/test/components/Chart/utils/processSentiments.test.ts +137 -29
  111. package/test/utils/processOrderPositionBooks.test.ts +201 -84
  112. package/test/utils/processPriceCandles.test.ts +93 -67
  113. package/test/utils/validateData.test.ts +136 -38
  114. package/dist/main/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js +0 -37
  115. package/dist/main/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js.map +0 -1
  116. package/dist/main/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js +0 -14
  117. package/dist/main/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js.map +0 -1
  118. package/dist/main/CrowdViewWidget/components/Chart/utils/processBuckets.js +0 -29
  119. package/dist/main/CrowdViewWidget/components/Chart/utils/processBuckets.js.map +0 -1
  120. package/dist/module/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js +0 -29
  121. package/dist/module/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js.map +0 -1
  122. package/dist/module/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js +0 -7
  123. package/dist/module/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js.map +0 -1
  124. package/dist/module/CrowdViewWidget/components/Chart/utils/processBuckets.js +0 -22
  125. package/dist/module/CrowdViewWidget/components/Chart/utils/processBuckets.js.map +0 -1
  126. package/dist/types/CrowdViewWidget/components/Chart/utils/aggregateBuckets.d.ts +0 -2
  127. package/dist/types/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.d.ts +0 -3
  128. package/dist/types/CrowdViewWidget/components/Chart/utils/processBuckets.d.ts +0 -3
  129. package/src/CrowdViewWidget/components/Chart/utils/aggregateBuckets.ts +0 -44
  130. package/src/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.ts +0 -13
  131. package/src/CrowdViewWidget/components/Chart/utils/processBuckets.ts +0 -43
  132. package/test/utils/aggregateBuckets.test.ts +0 -82
  133. package/test/utils/getTargetBucketWidth.test.ts +0 -37
  134. package/test/utils/processBuckets.test.ts +0 -153
@@ -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
  });
@@ -7,9 +7,13 @@ describe('processPriceCandles', () => {
7
7
  expect(result).toEqual({
8
8
  minPrice: 0,
9
9
  maxPrice: 0,
10
+ pipsLocation: 0,
10
11
  hasValidCandles: false,
11
- candleMap: new Map(),
12
- candles: [],
12
+ candlesOpen: [],
13
+ candlesClose: [],
14
+ candlesLow: [],
15
+ candlesHigh: [],
16
+ dates: [],
13
17
  });
14
18
  });
15
19
 
@@ -21,9 +25,13 @@ describe('processPriceCandles', () => {
21
25
  expect(result).toEqual({
22
26
  minPrice: 0,
23
27
  maxPrice: 0,
28
+ pipsLocation: 0,
24
29
  hasValidCandles: false,
25
- candleMap: new Map(),
26
- candles: [],
30
+ candlesOpen: [],
31
+ candlesClose: [],
32
+ candlesLow: [],
33
+ candlesHigh: [],
34
+ dates: [],
27
35
  });
28
36
  });
29
37
 
@@ -31,15 +39,20 @@ describe('processPriceCandles', () => {
31
39
  const mockData: GetPriceCandlesQuery = {
32
40
  priceCandles: {
33
41
  candle: [],
42
+ pipsLocation: 0,
34
43
  },
35
44
  };
36
45
  const result = processPriceCandles(mockData);
37
46
  expect(result).toEqual({
38
47
  minPrice: 0,
39
48
  maxPrice: 0,
49
+ pipsLocation: 0,
40
50
  hasValidCandles: false,
41
- candleMap: new Map(),
42
- candles: [],
51
+ candlesOpen: [],
52
+ candlesClose: [],
53
+ candlesLow: [],
54
+ candlesHigh: [],
55
+ dates: [],
43
56
  });
44
57
  });
45
58
 
@@ -69,14 +82,24 @@ describe('processPriceCandles', () => {
69
82
  close: 1.2,
70
83
  },
71
84
  ],
85
+ pipsLocation: 4,
72
86
  },
73
87
  };
74
88
  const result = processPriceCandles(mockData);
75
89
  expect(result.hasValidCandles).toBe(true);
76
90
  expect(result.minPrice).toBe(0.9);
77
91
  expect(result.maxPrice).toBe(1.8);
78
- expect(result.candles).toHaveLength(3);
79
- expect(result.candleMap.size).toBe(3);
92
+ expect(result.pipsLocation).toBe(4);
93
+ expect(result.dates).toHaveLength(3);
94
+ expect(result.candlesOpen).toHaveLength(3);
95
+ expect(result.candlesClose).toHaveLength(3);
96
+ expect(result.candlesLow).toHaveLength(3);
97
+ expect(result.candlesHigh).toHaveLength(3);
98
+ expect(result.dates[0]).toBe('2025-01-01T00:00:00Z');
99
+ expect(result.candlesOpen[0]).toBe(1.2);
100
+ expect(result.candlesClose[0]).toBe(1.3);
101
+ expect(result.candlesLow[0]).toBe(1.0);
102
+ expect(result.candlesHigh[0]).toBe(1.5);
80
103
  });
81
104
 
82
105
  it('should filter out null candles', () => {
@@ -100,73 +123,28 @@ describe('processPriceCandles', () => {
100
123
  close: 1.6,
101
124
  },
102
125
  ],
126
+ pipsLocation: 4,
103
127
  },
104
128
  };
105
129
  const result = processPriceCandles(mockData);
106
130
  expect(result.hasValidCandles).toBe(true);
107
131
  expect(result.minPrice).toBe(1.0);
108
132
  expect(result.maxPrice).toBe(1.8);
109
- expect(result.candles).toHaveLength(4); // nulls are still in the array
110
- expect(result.candleMap.size).toBe(2); // but not in the map
133
+ expect(result.pipsLocation).toBe(4);
134
+ expect(result.dates).toHaveLength(2);
135
+ expect(result.candlesOpen).toHaveLength(2);
111
136
  });
112
137
 
113
- it('should populate candleMap with point as key', () => {
138
+ it('should return default values when all candles are null', () => {
114
139
  const mockData: GetPriceCandlesQuery = {
115
140
  priceCandles: {
116
- candle: [
117
- {
118
- point: '2025-01-01T00:00:00Z',
119
- high: 1.5,
120
- low: 1.0,
121
- open: 1.2,
122
- close: 1.3,
123
- },
124
- {
125
- point: '2025-01-01T01:00:00Z',
126
- high: 1.8,
127
- low: 1.1,
128
- open: 1.4,
129
- close: 1.6,
130
- },
131
- ],
141
+ candle: [null, null],
142
+ pipsLocation: 0,
132
143
  },
133
144
  };
134
145
  const result = processPriceCandles(mockData);
135
- expect(result.candleMap.has('2025-01-01T00:00:00Z')).toBe(true);
136
- expect(result.candleMap.has('2025-01-01T01:00:00Z')).toBe(true);
137
- expect(result.candleMap.get('2025-01-01T00:00:00Z')).toEqual({
138
- point: '2025-01-01T00:00:00Z',
139
- high: 1.5,
140
- low: 1.0,
141
- open: 1.2,
142
- close: 1.3,
143
- });
144
- });
145
-
146
- it('should not add candles without point to candleMap', () => {
147
- const mockData: GetPriceCandlesQuery = {
148
- priceCandles: {
149
- candle: [
150
- {
151
- point: '2025-01-01T00:00:00Z',
152
- high: 1.5,
153
- low: 1.0,
154
- open: 1.2,
155
- close: 1.3,
156
- },
157
- {
158
- point: undefined as any,
159
- high: 1.8,
160
- low: 1.1,
161
- open: 1.4,
162
- close: 1.6,
163
- },
164
- ],
165
- },
166
- };
167
- const result = processPriceCandles(mockData);
168
- expect(result.candleMap.size).toBe(1);
169
- expect(result.candleMap.has('2025-01-01T00:00:00Z')).toBe(true);
146
+ expect(result.hasValidCandles).toBe(false);
147
+ expect(result.dates).toHaveLength(0);
170
148
  });
171
149
 
172
150
  it('should handle candles with extreme price values', () => {
@@ -181,11 +159,13 @@ describe('processPriceCandles', () => {
181
159
  close: 750.0,
182
160
  },
183
161
  ],
162
+ pipsLocation: 3,
184
163
  },
185
164
  };
186
165
  const result = processPriceCandles(mockData);
187
166
  expect(result.minPrice).toBe(0.001);
188
167
  expect(result.maxPrice).toBe(1000.0);
168
+ expect(result.pipsLocation).toBe(3);
189
169
  });
190
170
 
191
171
  it('should handle single candle correctly', () => {
@@ -200,17 +180,19 @@ describe('processPriceCandles', () => {
200
180
  close: 1.3,
201
181
  },
202
182
  ],
183
+ pipsLocation: 4,
203
184
  },
204
185
  };
205
186
  const result = processPriceCandles(mockData);
206
187
  expect(result.hasValidCandles).toBe(true);
207
188
  expect(result.minPrice).toBe(1.0);
208
189
  expect(result.maxPrice).toBe(1.5);
209
- expect(result.candles).toHaveLength(1);
210
- expect(result.candleMap.size).toBe(1);
190
+ expect(result.pipsLocation).toBe(4);
191
+ expect(result.dates).toHaveLength(1);
192
+ expect(result.candlesOpen).toHaveLength(1);
211
193
  });
212
194
 
213
- it('should update min/max when processing candles in sequence', () => {
195
+ it('should maintain order of candles', () => {
214
196
  const mockData: GetPriceCandlesQuery = {
215
197
  priceCandles: {
216
198
  candle: [
@@ -223,8 +205,8 @@ describe('processPriceCandles', () => {
223
205
  },
224
206
  {
225
207
  point: '2025-01-01T01:00:00Z',
226
- high: 3.0, // new max
227
- low: 0.5, // new min
208
+ high: 3.0,
209
+ low: 0.5,
228
210
  open: 2.0,
229
211
  close: 2.5,
230
212
  },
@@ -236,10 +218,54 @@ describe('processPriceCandles', () => {
236
218
  close: 1.6,
237
219
  },
238
220
  ],
221
+ pipsLocation: 4,
239
222
  },
240
223
  };
241
224
  const result = processPriceCandles(mockData);
242
225
  expect(result.minPrice).toBe(0.5);
243
226
  expect(result.maxPrice).toBe(3.0);
227
+ expect(result.pipsLocation).toBe(4);
228
+ expect(result.dates[0]).toBe('2025-01-01T00:00:00Z');
229
+ expect(result.dates[1]).toBe('2025-01-01T01:00:00Z');
230
+ expect(result.dates[2]).toBe('2025-01-01T02:00:00Z');
231
+ });
232
+
233
+ it('should handle all null candles gracefully', () => {
234
+ const mockData: GetPriceCandlesQuery = {
235
+ priceCandles: {
236
+ candle: [null, null, null],
237
+ pipsLocation: 0,
238
+ },
239
+ };
240
+ const result = processPriceCandles(mockData);
241
+ expect(result.hasValidCandles).toBe(false);
242
+ expect(result.dates).toHaveLength(0);
243
+ expect(result.minPrice).toBe(0);
244
+ expect(result.maxPrice).toBe(0);
245
+ });
246
+
247
+ it('should handle large arrays without stack overflow', () => {
248
+ // Create a large array of candles
249
+ const largeCandles = Array.from({ length: 10000 }, (_, i) => ({
250
+ point: `2025-01-01T${String(i).padStart(2, '0')}:00:00Z`,
251
+ high: 1.5 + i * 0.0001,
252
+ low: 1.0 + i * 0.0001,
253
+ open: 1.2 + i * 0.0001,
254
+ close: 1.3 + i * 0.0001,
255
+ }));
256
+
257
+ const mockData: GetPriceCandlesQuery = {
258
+ priceCandles: {
259
+ candle: largeCandles,
260
+ pipsLocation: 4,
261
+ },
262
+ };
263
+ const result = processPriceCandles(mockData);
264
+ expect(result.hasValidCandles).toBe(true);
265
+ expect(result.pipsLocation).toBe(4);
266
+ expect(result.dates).toHaveLength(10000);
267
+ // Should calculate min/max without stack overflow
268
+ expect(result.minPrice).toBeGreaterThan(0);
269
+ expect(result.maxPrice).toBeGreaterThan(result.minPrice);
244
270
  });
245
271
  });