@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
@@ -1,30 +1,92 @@
1
1
  import type { GetOrderPositionBooksQuery } from '../../../../gql/types/graphql';
2
+ import type { Bucket } from '../types';
2
3
 
3
4
  export const processOrderPositionBooks = (
4
5
  orderPositionData: GetOrderPositionBooksQuery | undefined,
5
- candleMap: Map<
6
- string,
7
- {
8
- point?: string;
9
- high?: number;
10
- low?: number;
11
- open?: number;
12
- close?: number;
13
- }
14
- >
15
- ): [string, number | null, number][] => {
16
- if (!orderPositionData?.orderPositionBooks?.length) {
17
- return [];
6
+ dates: string[]
7
+ ) => {
8
+ // Input validation
9
+ if (!Array.isArray(dates) || dates.length === 0) {
10
+ return {
11
+ bookPrices: [],
12
+ bookIndexes: [],
13
+ buckets: [],
14
+ bucketWidth: 0,
15
+ sentimentThresholdMin: 0,
16
+ sentimentThresholdMax: 0,
17
+ hasValidBooks: false,
18
+ };
18
19
  }
19
20
 
20
- return orderPositionData.orderPositionBooks
21
- .filter((book): book is NonNullable<typeof book> => {
22
- return book !== null && book.buckets?.length > 0;
23
- })
24
- .map((book, index) => {
25
- const candle = candleMap.get(book.time);
26
- const price = candle?.high ?? null;
21
+ if (
22
+ !orderPositionData?.orderPositionBooks?.books?.length ||
23
+ !orderPositionData?.orderPositionBooks?.sentimentThresholdMin ||
24
+ !orderPositionData?.orderPositionBooks?.sentimentThresholdMax ||
25
+ !orderPositionData?.orderPositionBooks?.bucketWidth
26
+ ) {
27
+ return {
28
+ bookPrices: [],
29
+ bookIndexes: [],
30
+ buckets: [],
31
+ bucketWidth: 0,
32
+ sentimentThresholdMin: 0,
33
+ sentimentThresholdMax: 0,
34
+ hasValidBooks: false,
35
+ };
36
+ }
37
+
38
+ const { bucketWidth, sentimentThresholdMin, sentimentThresholdMax } =
39
+ orderPositionData.orderPositionBooks;
40
+
41
+ const booksDataMap = new Map(
42
+ orderPositionData.orderPositionBooks.books.map((item) => [
43
+ item.time,
44
+ {
45
+ price: item.price,
46
+ buckets: item.buckets,
47
+ },
48
+ ])
49
+ );
50
+
51
+ // Use forEach + push for O(n) performance instead of reduce with spreads (O(n²))
52
+ const bookPrices: (number | null)[] = [];
53
+ const bookIndexes: number[] = [];
54
+ const buckets: Bucket[][] = [];
55
+
56
+ dates.forEach((date, index) => {
57
+ const book = booksDataMap.get(date);
58
+
59
+ const currentBuckets =
60
+ book?.buckets
61
+ .filter(
62
+ (
63
+ bucket
64
+ ): bucket is {
65
+ price: number;
66
+ sentiment: number;
67
+ } =>
68
+ bucket !== null &&
69
+ typeof bucket.price === 'number' &&
70
+ typeof bucket.sentiment === 'number' &&
71
+ Math.abs(bucket.sentiment) >= sentimentThresholdMin
72
+ )
73
+ .map((bucket) => ({
74
+ price: bucket.price,
75
+ sentiment: bucket.sentiment,
76
+ })) || [];
77
+
78
+ bookPrices.push(book?.price ?? null);
79
+ bookIndexes.push(index);
80
+ buckets.push(currentBuckets);
81
+ });
27
82
 
28
- return [book.time, price, index] as [string, number | null, number];
29
- });
83
+ return {
84
+ bookPrices,
85
+ bookIndexes,
86
+ buckets,
87
+ bucketWidth,
88
+ sentimentThresholdMin,
89
+ sentimentThresholdMax,
90
+ hasValidBooks: true,
91
+ };
30
92
  };
@@ -3,51 +3,65 @@ import type { GetPriceCandlesQuery } from '../../../../gql/types/graphql';
3
3
  export const processPriceCandles = (
4
4
  priceCandlesData: GetPriceCandlesQuery | undefined
5
5
  ) => {
6
- if (!priceCandlesData?.priceCandles?.candle?.length) {
7
- return {
8
- minPrice: 0,
9
- maxPrice: 0,
10
- hasValidCandles: false,
11
- candleMap: new Map(),
12
- candles: [],
13
- };
14
- }
6
+ const errorResult = {
7
+ minPrice: 0,
8
+ maxPrice: 0,
9
+ pipsLocation: 0,
10
+ hasValidCandles: false,
11
+ candlesOpen: [],
12
+ candlesClose: [],
13
+ candlesLow: [],
14
+ candlesHigh: [],
15
+ dates: [],
16
+ };
15
17
 
16
- const candles = priceCandlesData.priceCandles.candle;
17
- let calculatedMinPrice = Number.MAX_VALUE;
18
- let calculatedMaxPrice = Number.MIN_VALUE;
18
+ if (
19
+ !priceCandlesData?.priceCandles?.candle?.length ||
20
+ !priceCandlesData?.priceCandles?.pipsLocation
21
+ ) {
22
+ return errorResult;
23
+ }
19
24
 
20
- const candleMap = new Map<
21
- string,
22
- {
23
- point?: string;
24
- high?: number;
25
- low?: number;
26
- open?: number;
27
- close?: number;
28
- }
29
- >();
25
+ const { dates, candlesOpen, candlesClose, candlesLow, candlesHigh } =
26
+ priceCandlesData.priceCandles.candle.reduce(
27
+ (acc, candle) => {
28
+ if (!candle) {
29
+ return acc;
30
+ }
30
31
 
31
- candles.forEach((candle) => {
32
- if (!candle) return;
32
+ return {
33
+ dates: [...acc.dates, candle.point],
34
+ candlesOpen: [...acc.candlesOpen, candle.open],
35
+ candlesClose: [...acc.candlesClose, candle.close],
36
+ candlesLow: [...acc.candlesLow, candle.low],
37
+ candlesHigh: [...acc.candlesHigh, candle.high],
38
+ };
39
+ },
40
+ {
41
+ dates: [] as string[],
42
+ candlesOpen: [] as number[],
43
+ candlesClose: [] as number[],
44
+ candlesLow: [] as number[],
45
+ candlesHigh: [] as number[],
46
+ }
47
+ );
33
48
 
34
- if (candle.high > calculatedMaxPrice) {
35
- calculatedMaxPrice = candle.high;
36
- }
37
- if (candle.low < calculatedMinPrice) {
38
- calculatedMinPrice = candle.low;
39
- }
49
+ if (dates.length === 0) {
50
+ return errorResult;
51
+ }
40
52
 
41
- if (candle.point) {
42
- candleMap.set(candle.point, candle);
43
- }
44
- });
53
+ const minPrice = Math.min(...candlesLow);
54
+ const maxPrice = Math.max(...candlesHigh);
45
55
 
46
56
  return {
47
- minPrice: calculatedMinPrice,
48
- maxPrice: calculatedMaxPrice,
57
+ minPrice,
58
+ maxPrice,
59
+ candlesOpen,
60
+ candlesClose,
61
+ candlesLow,
62
+ candlesHigh,
63
+ dates,
49
64
  hasValidCandles: true,
50
- candleMap,
51
- candles,
65
+ pipsLocation: priceCandlesData.priceCandles.pipsLocation,
52
66
  };
53
67
  };
@@ -0,0 +1,55 @@
1
+ import type { GetSentimentsQuery } from '../../../../gql/types/graphql';
2
+
3
+ export const processSentiments = (
4
+ sentimentsData: GetSentimentsQuery | undefined,
5
+ dates: string[]
6
+ ) => {
7
+ if (
8
+ !Array.isArray(dates) ||
9
+ dates.length === 0 ||
10
+ !sentimentsData?.sentiments?.sentiments?.length
11
+ ) {
12
+ return {
13
+ sentimentLongs: [],
14
+ sentimentShorts: [],
15
+ hasValidSentiments: false,
16
+ };
17
+ }
18
+
19
+ const sentimentMap = new Map(
20
+ sentimentsData.sentiments.sentiments.map((item) => [
21
+ item?.time,
22
+ {
23
+ sentimentShorts: item?.sentiment?.shortPercent ?? null,
24
+ sentimentLongs: item?.sentiment?.longPercent ?? null,
25
+ },
26
+ ])
27
+ );
28
+
29
+ const { sentimentLongs, sentimentShorts } = dates.reduce(
30
+ (acc, date) => {
31
+ const sentiment = sentimentMap.get(date);
32
+
33
+ return {
34
+ sentimentLongs: [
35
+ ...acc.sentimentLongs,
36
+ sentiment?.sentimentLongs || null,
37
+ ],
38
+ sentimentShorts: [
39
+ ...acc.sentimentShorts,
40
+ sentiment?.sentimentShorts || null,
41
+ ],
42
+ };
43
+ },
44
+ {
45
+ sentimentLongs: [] as (number | null)[],
46
+ sentimentShorts: [] as (number | null)[],
47
+ }
48
+ );
49
+
50
+ return {
51
+ sentimentLongs,
52
+ sentimentShorts,
53
+ hasValidSentiments: true,
54
+ };
55
+ };
@@ -6,12 +6,14 @@ import type {
6
6
  export const validateData = (
7
7
  priceCandlesData: GetPriceCandlesQuery | undefined,
8
8
  orderPositionData: GetOrderPositionBooksQuery | undefined,
9
- hasValidCandles: boolean
9
+ hasValidCandles: boolean,
10
+ hasValidBooks: boolean,
11
+ hasValidSentiments: boolean
10
12
  ): Error | null => {
11
13
  const hasValidPriceData =
12
14
  (priceCandlesData?.priceCandles?.candle?.length ?? 0) >= 1;
13
15
  const hasValidOrderData =
14
- (orderPositionData?.orderPositionBooks?.length ?? 0) >= 1;
16
+ (orderPositionData?.orderPositionBooks?.books?.length ?? 0) >= 1;
15
17
 
16
18
  if (!hasValidPriceData) {
17
19
  return new Error('Insufficient price candle data');
@@ -22,6 +24,12 @@ export const validateData = (
22
24
  if (!hasValidCandles) {
23
25
  return new Error('Invalid candle data');
24
26
  }
27
+ if (!hasValidBooks) {
28
+ return new Error('Invalid book data');
29
+ }
30
+ if (!hasValidSentiments) {
31
+ return new Error('Invalid sentiment data');
32
+ }
25
33
 
26
34
  return null;
27
35
  };
@@ -2,26 +2,25 @@ import { useLocale } from '@oanda/mono-i18n';
2
2
  import React from 'react';
3
3
 
4
4
  import { BookType } from '../../../gql/types/graphql';
5
- import { BOOKS_THRESHOLDS } from '../../constants';
6
5
  import { LegendBar } from './LegendBar';
7
6
 
8
7
  interface LegendProps {
9
- longValues?: [number, number];
10
- shortValues?: [number, number];
8
+ longValues: (number | undefined)[];
9
+ shortValues: (number | undefined)[];
11
10
  bookType: BookType;
12
11
  isDark: boolean;
13
12
  }
14
13
 
15
14
  export const Legend = ({
16
- longValues = [BOOKS_THRESHOLDS.MIN, BOOKS_THRESHOLDS.MAX],
17
- shortValues = [BOOKS_THRESHOLDS.MIN, BOOKS_THRESHOLDS.MAX],
15
+ longValues,
16
+ shortValues,
18
17
  bookType,
19
18
  isDark,
20
19
  }: LegendProps) => {
21
20
  const { lang } = useLocale();
22
21
 
23
22
  return (
24
- <div className="lw-mx-auto lw-flex lw-w-full lw-flex-col lw-items-center lw-space-y-4 lw-pb-6 lw-pt-0 sm:lw-max-w-md lg:lw-max-w-xl">
23
+ <div className="lw-mx-auto lw-mt-4 lw-flex lw-w-full lw-flex-col lw-items-center lw-space-y-4 lw-pb-6 lw-pt-0 sm:lw-max-w-md lg:lw-max-w-xl">
25
24
  <LegendBar
26
25
  isDark={isDark}
27
26
  label={lang(bookType === BookType.Order ? 'buy' : 'long')}
@@ -5,7 +5,7 @@ import { COLOR_MAP } from '../../constants';
5
5
  export type LegendType = 'long' | 'short';
6
6
 
7
7
  interface LegendBarProps {
8
- values: number[];
8
+ values: (number | undefined)[];
9
9
  type: LegendType;
10
10
  label: string;
11
11
  isDark: boolean;
@@ -32,10 +32,10 @@ export const LegendBar = ({ values, type, label, isDark }: LegendBarProps) => {
32
32
 
33
33
  <div className="lw-flex lw-w-full lw-justify-between">
34
34
  <span className="lw-whitespace-nowrap lw-font-sans lw-text-xs lw-text-text-primary">
35
- {values[0].toFixed(2)}%
35
+ {values[0] ? `${values[0].toFixed(2)}%` : '—'}
36
36
  </span>
37
37
  <span className="lw-whitespace-nowrap lw-font-sans lw-text-xs lw-text-text-primary">
38
- {`≤ ${values[1].toFixed(2)}`}%
38
+ {values[1] ? `≤ ${values[1].toFixed(2)}%` : '—'}
39
39
  </span>
40
40
  </div>
41
41
  </div>
@@ -2,14 +2,9 @@ import { colorPalette } from '@oanda/labs-widget-common';
2
2
 
3
3
  import { InstrumentId } from './types';
4
4
 
5
- export const BOOKS_THRESHOLDS = {
6
- MIN: 0.15,
7
- MAX: 0.55,
8
- } as const;
9
-
10
5
  export const BUCKET_CONFIG = {
11
6
  MULTIPLIER: 4,
12
- PRICE_PADDING_MULTIPLIER: 2,
7
+ PRICE_MARGIN_MULTIPLIER: 2,
13
8
  } as const;
14
9
 
15
10
  export const TIME_THRESHOLDS = {
@@ -17,7 +12,9 @@ export const TIME_THRESHOLDS = {
17
12
  } as const;
18
13
 
19
14
  export const CHART_CONFIG = {
20
- HEIGHT: 425,
15
+ MAIN_HEIGHT: 400,
16
+ MARGIN_BETWEEN: 50,
17
+ SENTIMENT_HEIGHT: 120,
21
18
  WIDTH: 9999,
22
19
  X_LABEL_SIZE: 40,
23
20
  Y_LABEL_SIZE_DESKTOP: 60,
@@ -25,8 +22,22 @@ export const CHART_CONFIG = {
25
22
  INITIAL_START_ZOOM: 80,
26
23
  INITIAL_END_ZOOM: 100,
27
24
  X_AXIS_DATE_PADDING: ' ',
25
+ SENTIMENT_MIN: 0,
26
+ SENTIMENT_MAX: 100,
27
+ SENTIMENT_INTERVAL: 25,
28
+ SENTIMENT_TEXT_OFFSET: 16,
28
29
  } as const;
29
30
 
31
+ export const CHART_CONFIG_CALCULATED = {
32
+ FULL_HEIGHT:
33
+ CHART_CONFIG.MAIN_HEIGHT +
34
+ CHART_CONFIG.MARGIN_BETWEEN +
35
+ CHART_CONFIG.SENTIMENT_HEIGHT +
36
+ CHART_CONFIG.X_LABEL_SIZE,
37
+ SENTIMENT_TEXT_POSITION:
38
+ CHART_CONFIG.SENTIMENT_HEIGHT + CHART_CONFIG.SENTIMENT_TEXT_OFFSET,
39
+ };
40
+
30
41
  export const COLOR_MAP = {
31
42
  dark: {
32
43
  long: [colorPalette.darkBlue10, colorPalette.darkBlue90],
@@ -8,6 +8,8 @@ const getOrderPositionBooks = gql`
8
8
  $granularity: Granularity!
9
9
  $maxBookPrice: Float
10
10
  $minBookPrice: Float
11
+ $bucketMultiplier: Int!
12
+ $bucketMargin: Int!
11
13
  ) {
12
14
  orderPositionBooks(
13
15
  instrument: $instrument
@@ -16,14 +18,20 @@ const getOrderPositionBooks = gql`
16
18
  granularity: $granularity
17
19
  maxBookPrice: $maxBookPrice
18
20
  minBookPrice: $minBookPrice
21
+ bucketMultiplier: $bucketMultiplier
22
+ bucketMargin: $bucketMargin
19
23
  ) {
20
- bucketWidth
21
- price
22
- time
23
- buckets {
24
+ books {
25
+ time
24
26
  price
25
- sentiment
27
+ buckets {
28
+ price
29
+ sentiment
30
+ }
26
31
  }
32
+ bucketWidth
33
+ sentimentThresholdMax
34
+ sentimentThresholdMin
27
35
  }
28
36
  }
29
37
  `;
@@ -22,6 +22,7 @@ const getPriceCandles = gql`
22
22
  open
23
23
  close
24
24
  }
25
+ pipsLocation
25
26
  }
26
27
  }
27
28
  `;
@@ -0,0 +1,25 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ const getSentiments = gql`
4
+ query GetSentiments(
5
+ $instrument: String!
6
+ $granularity: Granularity!
7
+ $timeSpan: TimeSpan!
8
+ ) {
9
+ sentiments(
10
+ instrument: $instrument
11
+ granularity: $granularity
12
+ timeSpan: $timeSpan
13
+ ) {
14
+ sentiments {
15
+ sentiment {
16
+ longPercent
17
+ shortPercent
18
+ }
19
+ time
20
+ }
21
+ }
22
+ }
23
+ `;
24
+
25
+ export { getSentiments };
@@ -13,10 +13,12 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/
13
13
  * Therefore it is highly recommended to use the babel or swc plugin for production.
14
14
  */
15
15
  const documents = {
16
- '\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $timeSpan: TimeSpan!\n $granularity: Granularity!\n $maxBookPrice: Float\n $minBookPrice: Float\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n timeSpan: $timeSpan\n granularity: $granularity\n maxBookPrice: $maxBookPrice\n minBookPrice: $minBookPrice\n ) {\n bucketWidth\n price\n time\n buckets {\n price\n sentiment\n }\n }\n }\n':
16
+ '\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $timeSpan: TimeSpan!\n $granularity: Granularity!\n $maxBookPrice: Float\n $minBookPrice: Float\n $bucketMultiplier: Int!\n $bucketMargin: Int!\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n timeSpan: $timeSpan\n granularity: $granularity\n maxBookPrice: $maxBookPrice\n minBookPrice: $minBookPrice\n bucketMultiplier: $bucketMultiplier\n bucketMargin: $bucketMargin\n ) {\n books {\n time\n price\n buckets {\n price\n sentiment\n }\n }\n bucketWidth\n sentimentThresholdMax\n sentimentThresholdMin\n }\n }\n':
17
17
  types.GetOrderPositionBooksDocument,
18
- '\n query GetPriceCandles(\n $dataSource: DataSource!\n $division: Division!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n dataSource: $dataSource\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n }\n }\n':
18
+ '\n query GetPriceCandles(\n $dataSource: DataSource!\n $division: Division!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n dataSource: $dataSource\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n pipsLocation\n }\n }\n':
19
19
  types.GetPriceCandlesDocument,
20
+ '\n query GetSentiments(\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n sentiments(\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n sentiments {\n sentiment {\n longPercent\n shortPercent\n }\n time\n }\n }\n }\n':
21
+ types.GetSentimentsDocument,
20
22
  };
21
23
 
22
24
  /**
@@ -37,14 +39,20 @@ export function graphql(source: string): unknown;
37
39
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
38
40
  */
39
41
  export function graphql(
40
- source: '\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $timeSpan: TimeSpan!\n $granularity: Granularity!\n $maxBookPrice: Float\n $minBookPrice: Float\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n timeSpan: $timeSpan\n granularity: $granularity\n maxBookPrice: $maxBookPrice\n minBookPrice: $minBookPrice\n ) {\n bucketWidth\n price\n time\n buckets {\n price\n sentiment\n }\n }\n }\n'
41
- ): (typeof documents)['\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $timeSpan: TimeSpan!\n $granularity: Granularity!\n $maxBookPrice: Float\n $minBookPrice: Float\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n timeSpan: $timeSpan\n granularity: $granularity\n maxBookPrice: $maxBookPrice\n minBookPrice: $minBookPrice\n ) {\n bucketWidth\n price\n time\n buckets {\n price\n sentiment\n }\n }\n }\n'];
42
+ source: '\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $timeSpan: TimeSpan!\n $granularity: Granularity!\n $maxBookPrice: Float\n $minBookPrice: Float\n $bucketMultiplier: Int!\n $bucketMargin: Int!\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n timeSpan: $timeSpan\n granularity: $granularity\n maxBookPrice: $maxBookPrice\n minBookPrice: $minBookPrice\n bucketMultiplier: $bucketMultiplier\n bucketMargin: $bucketMargin\n ) {\n books {\n time\n price\n buckets {\n price\n sentiment\n }\n }\n bucketWidth\n sentimentThresholdMax\n sentimentThresholdMin\n }\n }\n'
43
+ ): (typeof documents)['\n query GetOrderPositionBooks(\n $instrument: String!\n $bookType: BookType!\n $timeSpan: TimeSpan!\n $granularity: Granularity!\n $maxBookPrice: Float\n $minBookPrice: Float\n $bucketMultiplier: Int!\n $bucketMargin: Int!\n ) {\n orderPositionBooks(\n instrument: $instrument\n bookType: $bookType\n timeSpan: $timeSpan\n granularity: $granularity\n maxBookPrice: $maxBookPrice\n minBookPrice: $minBookPrice\n bucketMultiplier: $bucketMultiplier\n bucketMargin: $bucketMargin\n ) {\n books {\n time\n price\n buckets {\n price\n sentiment\n }\n }\n bucketWidth\n sentimentThresholdMax\n sentimentThresholdMin\n }\n }\n'];
42
44
  /**
43
45
  * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
44
46
  */
45
47
  export function graphql(
46
- source: '\n query GetPriceCandles(\n $dataSource: DataSource!\n $division: Division!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n dataSource: $dataSource\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n }\n }\n'
47
- ): (typeof documents)['\n query GetPriceCandles(\n $dataSource: DataSource!\n $division: Division!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n dataSource: $dataSource\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n }\n }\n'];
48
+ source: '\n query GetPriceCandles(\n $dataSource: DataSource!\n $division: Division!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n dataSource: $dataSource\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n pipsLocation\n }\n }\n'
49
+ ): (typeof documents)['\n query GetPriceCandles(\n $dataSource: DataSource!\n $division: Division!\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n priceCandles(\n dataSource: $dataSource\n division: $division\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n candle {\n point\n high\n low\n open\n close\n }\n pipsLocation\n }\n }\n'];
50
+ /**
51
+ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
52
+ */
53
+ export function graphql(
54
+ source: '\n query GetSentiments(\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n sentiments(\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n sentiments {\n sentiment {\n longPercent\n shortPercent\n }\n time\n }\n }\n }\n'
55
+ ): (typeof documents)['\n query GetSentiments(\n $instrument: String!\n $granularity: Granularity!\n $timeSpan: TimeSpan!\n ) {\n sentiments(\n instrument: $instrument\n granularity: $granularity\n timeSpan: $timeSpan\n ) {\n sentiments {\n sentiment {\n longPercent\n shortPercent\n }\n time\n }\n }\n }\n'];
48
56
 
49
57
  export function graphql(source: string) {
50
58
  return (documents as any)[source] ?? {};