@oanda/labs-crowd-view-widget 1.0.45 → 1.0.47

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 (142) hide show
  1. package/CHANGELOG.md +380 -0
  2. package/dist/main/CrowdViewWidget/Main.js +9 -5
  3. package/dist/main/CrowdViewWidget/Main.js.map +1 -1
  4. package/dist/main/CrowdViewWidget/components/Chart/Chart.js +3 -2
  5. package/dist/main/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  6. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js +5 -14
  7. package/dist/main/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  8. package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js +20 -8
  9. package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
  10. package/dist/main/CrowdViewWidget/components/Chart/index.js +4 -4
  11. package/dist/main/CrowdViewWidget/components/Chart/index.js.map +1 -1
  12. package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
  13. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js +17 -101
  14. package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  15. package/dist/main/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js +37 -0
  16. package/dist/main/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js.map +1 -0
  17. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js +19 -4
  18. package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
  19. package/dist/main/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js +14 -0
  20. package/dist/main/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js.map +1 -0
  21. package/dist/main/CrowdViewWidget/components/Chart/utils/index.js +83 -0
  22. package/dist/main/CrowdViewWidget/components/Chart/utils/index.js.map +1 -0
  23. package/dist/main/CrowdViewWidget/components/Chart/utils/processBuckets.js +29 -0
  24. package/dist/main/CrowdViewWidget/components/Chart/utils/processBuckets.js.map +1 -0
  25. package/dist/main/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js +23 -0
  26. package/dist/main/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js.map +1 -0
  27. package/dist/main/CrowdViewWidget/components/Chart/utils/processPriceCandles.js +43 -0
  28. package/dist/main/CrowdViewWidget/components/Chart/utils/processPriceCandles.js.map +1 -0
  29. package/dist/main/CrowdViewWidget/components/Chart/utils/validateData.js +23 -0
  30. package/dist/main/CrowdViewWidget/components/Chart/utils/validateData.js.map +1 -0
  31. package/dist/main/CrowdViewWidget/components/Legend/Legend.js +6 -4
  32. package/dist/main/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  33. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js +5 -3
  34. package/dist/main/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  35. package/dist/main/CrowdViewWidget/constants.js +105 -5
  36. package/dist/main/CrowdViewWidget/constants.js.map +1 -1
  37. package/dist/main/CrowdViewWidget/selectConfig.js +18 -60
  38. package/dist/main/CrowdViewWidget/selectConfig.js.map +1 -1
  39. package/dist/main/CrowdViewWidget/types.js +20 -0
  40. package/dist/main/CrowdViewWidget/types.js.map +1 -1
  41. package/dist/main/translations/sources/en.json +21 -16
  42. package/dist/module/CrowdViewWidget/Main.js +9 -5
  43. package/dist/module/CrowdViewWidget/Main.js.map +1 -1
  44. package/dist/module/CrowdViewWidget/components/Chart/Chart.js +3 -2
  45. package/dist/module/CrowdViewWidget/components/Chart/Chart.js.map +1 -1
  46. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js +5 -14
  47. package/dist/module/CrowdViewWidget/components/Chart/ChartWithData.js.map +1 -1
  48. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js +20 -8
  49. package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
  50. package/dist/module/CrowdViewWidget/components/Chart/index.js +1 -1
  51. package/dist/module/CrowdViewWidget/components/Chart/index.js.map +1 -1
  52. package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
  53. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +13 -97
  54. package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
  55. package/dist/module/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js +29 -0
  56. package/dist/module/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js.map +1 -0
  57. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js +20 -5
  58. package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
  59. package/dist/module/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js +7 -0
  60. package/dist/module/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js.map +1 -0
  61. package/dist/module/CrowdViewWidget/components/Chart/utils/index.js +8 -0
  62. package/dist/module/CrowdViewWidget/components/Chart/utils/index.js.map +1 -0
  63. package/dist/module/CrowdViewWidget/components/Chart/utils/processBuckets.js +22 -0
  64. package/dist/module/CrowdViewWidget/components/Chart/utils/processBuckets.js.map +1 -0
  65. package/dist/module/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js +16 -0
  66. package/dist/module/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js.map +1 -0
  67. package/dist/module/CrowdViewWidget/components/Chart/utils/processPriceCandles.js +36 -0
  68. package/dist/module/CrowdViewWidget/components/Chart/utils/processPriceCandles.js.map +1 -0
  69. package/dist/module/CrowdViewWidget/components/Chart/utils/validateData.js +16 -0
  70. package/dist/module/CrowdViewWidget/components/Chart/utils/validateData.js.map +1 -0
  71. package/dist/module/CrowdViewWidget/components/Legend/Legend.js +6 -4
  72. package/dist/module/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
  73. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js +5 -3
  74. package/dist/module/CrowdViewWidget/components/Legend/LegendBar.js.map +1 -1
  75. package/dist/module/CrowdViewWidget/constants.js +104 -4
  76. package/dist/module/CrowdViewWidget/constants.js.map +1 -1
  77. package/dist/module/CrowdViewWidget/selectConfig.js +3 -45
  78. package/dist/module/CrowdViewWidget/selectConfig.js.map +1 -1
  79. package/dist/module/CrowdViewWidget/types.js +19 -1
  80. package/dist/module/CrowdViewWidget/types.js.map +1 -1
  81. package/dist/module/translations/sources/en.json +21 -16
  82. package/dist/types/CrowdViewWidget/components/Chart/Chart.d.ts +1 -1
  83. package/dist/types/CrowdViewWidget/components/Chart/index.d.ts +1 -1
  84. package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +13 -8
  85. package/dist/types/CrowdViewWidget/components/Chart/utils/aggregateBuckets.d.ts +2 -0
  86. package/dist/types/CrowdViewWidget/components/Chart/utils/chartUtils.d.ts +11 -6
  87. package/dist/types/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.d.ts +3 -0
  88. package/dist/types/CrowdViewWidget/components/Chart/utils/index.d.ts +7 -0
  89. package/dist/types/CrowdViewWidget/components/Chart/utils/processBuckets.d.ts +3 -0
  90. package/dist/types/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.d.ts +8 -0
  91. package/dist/types/CrowdViewWidget/components/Chart/utils/processPriceCandles.d.ts +27 -0
  92. package/dist/types/CrowdViewWidget/components/Chart/utils/validateData.d.ts +2 -0
  93. package/dist/types/CrowdViewWidget/components/Legend/Legend.d.ts +3 -1
  94. package/dist/types/CrowdViewWidget/constants.d.ts +12 -4
  95. package/dist/types/CrowdViewWidget/selectConfig.d.ts +2 -2
  96. package/dist/types/CrowdViewWidget/types.d.ts +18 -1
  97. package/dist/types/CrowdViewWidget/utils/instrumentUtils.d.ts +1 -4
  98. package/package.json +4 -3
  99. package/src/CrowdViewWidget/Main.tsx +46 -40
  100. package/src/CrowdViewWidget/components/Chart/Chart.tsx +2 -2
  101. package/src/CrowdViewWidget/components/Chart/ChartWithData.tsx +4 -26
  102. package/src/CrowdViewWidget/components/Chart/chartOptions.ts +41 -27
  103. package/src/CrowdViewWidget/components/Chart/index.ts +1 -1
  104. package/src/CrowdViewWidget/components/Chart/types.ts +14 -4
  105. package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +30 -154
  106. package/src/CrowdViewWidget/components/Chart/utils/aggregateBuckets.ts +44 -0
  107. package/src/CrowdViewWidget/components/Chart/utils/chartUtils.ts +41 -12
  108. package/src/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.ts +13 -0
  109. package/src/CrowdViewWidget/components/Chart/utils/index.ts +7 -0
  110. package/src/CrowdViewWidget/components/Chart/utils/processBuckets.ts +43 -0
  111. package/src/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.ts +30 -0
  112. package/src/CrowdViewWidget/components/Chart/utils/processPriceCandles.ts +53 -0
  113. package/src/CrowdViewWidget/components/Chart/utils/validateData.ts +27 -0
  114. package/src/CrowdViewWidget/components/Legend/Legend.tsx +14 -3
  115. package/src/CrowdViewWidget/components/Legend/LegendBar.tsx +5 -2
  116. package/src/CrowdViewWidget/constants.ts +114 -4
  117. package/src/CrowdViewWidget/selectConfig.ts +5 -60
  118. package/src/CrowdViewWidget/types.ts +18 -1
  119. package/src/translations/sources/en.json +21 -16
  120. package/test/Main.test.tsx +1 -1
  121. package/test/components/Chart/utils/chartUtils.test.ts +13 -27
  122. package/test/components/Legend.test.tsx +8 -3
  123. package/test/components/LegendBar.test.tsx +3 -2
  124. package/test/utils/aggregateBuckets.test.ts +82 -0
  125. package/test/utils/getTargetBucketWidth.test.ts +37 -0
  126. package/test/utils/instrumentUtils.test.ts +13 -7
  127. package/test/utils/processBuckets.test.ts +153 -0
  128. package/test/utils/processOrderPositionBooks.test.ts +127 -0
  129. package/test/utils/processPriceCandles.test.ts +245 -0
  130. package/test/utils/validateData.test.ts +201 -0
  131. package/dist/main/CrowdViewWidget/types/index.js +0 -17
  132. package/dist/main/CrowdViewWidget/types/index.js.map +0 -1
  133. package/dist/main/CrowdViewWidget/types/instruments.js +0 -45
  134. package/dist/main/CrowdViewWidget/types/instruments.js.map +0 -1
  135. package/dist/module/CrowdViewWidget/types/index.js +0 -2
  136. package/dist/module/CrowdViewWidget/types/index.js.map +0 -1
  137. package/dist/module/CrowdViewWidget/types/instruments.js +0 -39
  138. package/dist/module/CrowdViewWidget/types/instruments.js.map +0 -1
  139. package/dist/types/CrowdViewWidget/types/index.d.ts +0 -1
  140. package/dist/types/CrowdViewWidget/types/instruments.d.ts +0 -36
  141. package/src/CrowdViewWidget/types/index.ts +0 -1
  142. package/src/CrowdViewWidget/types/instruments.ts +0 -37
@@ -1,13 +1,13 @@
1
1
  import chroma from 'chroma-js';
2
2
 
3
- import { Granularity, TimeSpan } from '../../../../gql/types/graphql';
3
+ import { BookType, Granularity, TimeSpan } from '../../../../gql/types/graphql';
4
4
  import {
5
5
  BOOKS_THRESHOLDS,
6
6
  CHART_CONFIG,
7
7
  COLOR_MAP,
8
8
  TIME_THRESHOLDS,
9
9
  } from '../../../constants';
10
- import type { GetLabelsDataProps } from '../types';
10
+ import type { Bucket, GetLabelsDataProps } from '../types';
11
11
 
12
12
  export const getLabelData = ({
13
13
  xAxisData,
@@ -92,14 +92,27 @@ export const getRectColor = (sentiment: number) =>
92
92
  ? getGradientColor(sentiment * -1, COLOR_MAP.short[0], COLOR_MAP.short[1])
93
93
  : getGradientColor(sentiment, COLOR_MAP.long[0], COLOR_MAP.long[1]);
94
94
 
95
- export const getTooltipFormatter = (
96
- params: unknown,
97
- buckets: Array<Array<{ price: number; sentiment: number }>>,
98
- bucketWidth: number,
99
- selectedPrice: number,
100
- labelCallback: (key: string) => string
101
- ) => {
95
+ export const getTooltipFormatter = ({
96
+ params,
97
+ buckets,
98
+ bucketWidth,
99
+ selectedPrice,
100
+ precision,
101
+ bookType,
102
+ labelCallback,
103
+ }: {
104
+ params: unknown;
105
+ buckets: Bucket[][];
106
+ bucketWidth: number;
107
+ selectedPrice: number;
108
+ precision: number;
109
+ bookType: BookType;
110
+ labelCallback: (key: string) => string;
111
+ }) => {
102
112
  const arr = params as unknown as Array<Record<string, unknown>>;
113
+ if (!arr || !Array.isArray(arr) || arr.length === 0) {
114
+ return undefined;
115
+ }
103
116
  const candleParam = arr[0] as {
104
117
  axisValue: string;
105
118
  value: [number, number, number, number, number];
@@ -118,6 +131,10 @@ export const getTooltipFormatter = (
118
131
  const showCandles =
119
132
  !!candleData[1] && !!candleData[2] && !!candleData[3] && !!candleData[4];
120
133
 
134
+ if (!showCandles && !matchedBucket) {
135
+ return undefined;
136
+ }
137
+
121
138
  return `<p>${time.toLocaleString(undefined, {
122
139
  hour: '2-digit',
123
140
  minute: '2-digit',
@@ -138,9 +155,21 @@ ${
138
155
  }
139
156
  ${
140
157
  matchedBucket
141
- ? `<br /><p><b>${labelCallback('orders')}:</b></p>
142
- <p>${labelCallback('price_range')}: ${matchedBucket.price} - ${Number(matchedBucket.price + bucketWidth).toFixed(4)} </p>
143
- <p>${matchedBucket.sentiment < 0 ? labelCallback('sell_advantage') : labelCallback('buy_advantage')}: ${Math.abs(matchedBucket.sentiment)}% </p>`
158
+ ? `<br /><p><b>${labelCallback(bookType === BookType.Order ? 'orders' : 'positions')}:</b></p>
159
+ <p>${labelCallback('price_range')}: ${matchedBucket.price.toFixed(precision - 1)} - ${Number(matchedBucket.price + bucketWidth).toFixed(precision - 1)} </p>
160
+ <p>${
161
+ matchedBucket.sentiment < 0
162
+ ? labelCallback(
163
+ bookType === BookType.Order
164
+ ? 'sell_overbalance'
165
+ : 'short_overbalance'
166
+ )
167
+ : labelCallback(
168
+ bookType === BookType.Order
169
+ ? 'buy_overbalance'
170
+ : 'long_overbalance'
171
+ )
172
+ }: ${Math.abs(matchedBucket.sentiment)}% </p>`
144
173
  : ''
145
174
  }`;
146
175
  };
@@ -0,0 +1,13 @@
1
+ import { Granularity } from '../../../../gql/types/graphql';
2
+ import { BUCKET_CONFIG, INSTRUMENTS_CONFIG } from '../../../constants';
3
+ import type { InstrumentId } from '../../../types';
4
+
5
+ export const getTargetBucketWidth = (
6
+ granularity: Granularity,
7
+ instrument: InstrumentId
8
+ ): number => {
9
+ const bucketWidth = INSTRUMENTS_CONFIG[instrument].defaultBucketWidth;
10
+ return granularity === Granularity.H1 || granularity === Granularity.H4
11
+ ? bucketWidth * BUCKET_CONFIG.MULTIPLIER
12
+ : bucketWidth;
13
+ };
@@ -0,0 +1,7 @@
1
+ export * from './aggregateBuckets';
2
+ export * from './chartUtils';
3
+ export * from './getTargetBucketWidth';
4
+ export * from './processBuckets';
5
+ export * from './processOrderPositionBooks';
6
+ export * from './processPriceCandles';
7
+ export * from './validateData';
@@ -0,0 +1,43 @@
1
+ import type { GetOrderPositionBooksQuery } from '../../../../gql/types/graphql';
2
+ import { BOOKS_THRESHOLDS } from '../../../constants';
3
+ import type { Bucket } from '../types';
4
+ import { aggregateBuckets } from './aggregateBuckets';
5
+
6
+ export const processBuckets = (
7
+ orderPositionData: GetOrderPositionBooksQuery | undefined,
8
+ targetBucketWidth: number
9
+ ): Bucket[][] => {
10
+ if (!orderPositionData?.orderPositionBooks?.length) {
11
+ return [];
12
+ }
13
+
14
+ return orderPositionData.orderPositionBooks
15
+ .filter((book): book is NonNullable<typeof book> => {
16
+ return book !== null && book.buckets?.length > 0;
17
+ })
18
+ .map((book) => {
19
+ const validBuckets = book.buckets
20
+ .filter(
21
+ (bucket): bucket is NonNullable<typeof bucket> =>
22
+ bucket !== null &&
23
+ bucket.price !== undefined &&
24
+ bucket.sentiment !== undefined &&
25
+ bucket.sentiment !== null
26
+ )
27
+ .map((bucket) => ({
28
+ price: bucket.price!,
29
+ sentiment: bucket.sentiment!,
30
+ }));
31
+
32
+ const bucketsToFilter =
33
+ targetBucketWidth > (book.bucketWidth ?? 0)
34
+ ? aggregateBuckets(validBuckets, targetBucketWidth)
35
+ : validBuckets;
36
+
37
+ const filteredBuckets = bucketsToFilter.filter(
38
+ (bucket: Bucket) => Math.abs(bucket.sentiment) >= BOOKS_THRESHOLDS.MIN
39
+ );
40
+
41
+ return filteredBuckets;
42
+ });
43
+ };
@@ -0,0 +1,30 @@
1
+ import type { GetOrderPositionBooksQuery } from '../../../../gql/types/graphql';
2
+
3
+ export const processOrderPositionBooks = (
4
+ 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 [];
18
+ }
19
+
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;
27
+
28
+ return [book.time, price, index] as [string, number | null, number];
29
+ });
30
+ };
@@ -0,0 +1,53 @@
1
+ import type { GetPriceCandlesQuery } from '../../../../gql/types/graphql';
2
+
3
+ export const processPriceCandles = (
4
+ priceCandlesData: GetPriceCandlesQuery | undefined
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
+ }
15
+
16
+ const candles = priceCandlesData.priceCandles.candle;
17
+ let calculatedMinPrice = Number.MAX_VALUE;
18
+ let calculatedMaxPrice = Number.MIN_VALUE;
19
+
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
+ >();
30
+
31
+ candles.forEach((candle) => {
32
+ if (!candle) return;
33
+
34
+ if (candle.high > calculatedMaxPrice) {
35
+ calculatedMaxPrice = candle.high;
36
+ }
37
+ if (candle.low < calculatedMinPrice) {
38
+ calculatedMinPrice = candle.low;
39
+ }
40
+
41
+ if (candle.point) {
42
+ candleMap.set(candle.point, candle);
43
+ }
44
+ });
45
+
46
+ return {
47
+ minPrice: calculatedMinPrice,
48
+ maxPrice: calculatedMaxPrice,
49
+ hasValidCandles: true,
50
+ candleMap,
51
+ candles,
52
+ };
53
+ };
@@ -0,0 +1,27 @@
1
+ import type {
2
+ GetOrderPositionBooksQuery,
3
+ GetPriceCandlesQuery,
4
+ } from '../../../../gql/types/graphql';
5
+
6
+ export const validateData = (
7
+ priceCandlesData: GetPriceCandlesQuery | undefined,
8
+ orderPositionData: GetOrderPositionBooksQuery | undefined,
9
+ hasValidCandles: boolean
10
+ ): Error | null => {
11
+ const hasValidPriceData =
12
+ (priceCandlesData?.priceCandles?.candle?.length ?? 0) >= 1;
13
+ const hasValidOrderData =
14
+ (orderPositionData?.orderPositionBooks?.length ?? 0) >= 1;
15
+
16
+ if (!hasValidPriceData) {
17
+ return new Error('Insufficient price candle data');
18
+ }
19
+ if (!hasValidOrderData) {
20
+ return new Error('Insufficient order position data');
21
+ }
22
+ if (!hasValidCandles) {
23
+ return new Error('Invalid candle data');
24
+ }
25
+
26
+ return null;
27
+ };
@@ -1,24 +1,35 @@
1
1
  import { useLocale } from '@oanda/mono-i18n';
2
2
  import React from 'react';
3
3
 
4
+ import { BookType } from '../../../gql/types/graphql';
4
5
  import { BOOKS_THRESHOLDS } from '../../constants';
5
6
  import { LegendBar } from './LegendBar';
6
7
 
7
8
  interface LegendProps {
8
9
  longValues?: [number, number];
9
10
  shortValues?: [number, number];
11
+ bookType: BookType;
10
12
  }
11
13
 
12
14
  export const Legend = ({
13
15
  longValues = [BOOKS_THRESHOLDS.MIN, BOOKS_THRESHOLDS.MAX],
14
16
  shortValues = [BOOKS_THRESHOLDS.MIN, BOOKS_THRESHOLDS.MAX],
17
+ bookType,
15
18
  }: LegendProps) => {
16
19
  const { lang } = useLocale();
17
20
 
18
21
  return (
19
- <div className="lw-mx-auto lw-flex lw-w-full lw-flex-col lw-items-center lw-space-y-4 lw-py-6 sm:lw-max-w-md lg:lw-max-w-xl">
20
- <LegendBar label={lang('long')} type="long" values={longValues} />
21
- <LegendBar label={lang('short')} type="short" values={shortValues} />
22
+ <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
+ <LegendBar
24
+ label={lang(bookType === BookType.Order ? 'buy' : 'long')}
25
+ type="long"
26
+ values={longValues}
27
+ />
28
+ <LegendBar
29
+ label={lang(bookType === BookType.Order ? 'sell' : 'short')}
30
+ type="short"
31
+ values={shortValues}
32
+ />
22
33
  </div>
23
34
  );
24
35
  };
@@ -15,6 +15,9 @@ export const LegendBar = ({ values, type, label }: LegendBarProps) => {
15
15
 
16
16
  return (
17
17
  <div className="lw-flex lw-w-full lw-flex-col lw-space-y-1 lw-border-border-primary">
18
+ <span className="lw-whitespace-nowrap lw-font-sans lw-text-xs lw-text-text-primary">
19
+ {label}
20
+ </span>
18
21
  <div className="lw-flex lw-h-2 lw-w-full lw-overflow-hidden lw-border lw-border-border-primary">
19
22
  <div
20
23
  className="lw-h-full lw-flex-1"
@@ -27,10 +30,10 @@ export const LegendBar = ({ values, type, label }: LegendBarProps) => {
27
30
 
28
31
  <div className="lw-flex lw-w-full lw-justify-between">
29
32
  <span className="lw-whitespace-nowrap lw-font-sans lw-text-xs lw-text-text-primary">
30
- {values[0].toFixed(2)}% {label}
33
+ {values[0].toFixed(2)}%
31
34
  </span>
32
35
  <span className="lw-whitespace-nowrap lw-font-sans lw-text-xs lw-text-text-primary">
33
- {values[1].toFixed(2)}% {label}
36
+ {`≤ ${values[1].toFixed(2)}`}%
34
37
  </span>
35
38
  </div>
36
39
  </div>
@@ -1,10 +1,12 @@
1
+ import { InstrumentId } from './types';
2
+
1
3
  export const BOOKS_THRESHOLDS = {
2
4
  MIN: 0.15,
3
5
  MAX: 0.55,
4
6
  } as const;
5
7
 
6
8
  export const BUCKET_CONFIG = {
7
- DEFAULT_WIDTH: 0.0005,
9
+ MULTIPLIER: 4,
8
10
  PRICE_PADDING_MULTIPLIER: 2,
9
11
  } as const;
10
12
 
@@ -17,12 +19,120 @@ export const CHART_CONFIG = {
17
19
  WIDTH: 9999,
18
20
  X_LABEL_SIZE: 40,
19
21
  Y_LABEL_SIZE_DESKTOP: 60,
20
- INITIAL_START_ZOOM: 0,
22
+ Y_LABEL_SIZE_MOBILE: 40,
23
+ INITIAL_START_ZOOM: 80,
21
24
  INITIAL_END_ZOOM: 100,
22
25
  X_AXIS_DATE_PADDING: ' ',
23
26
  } as const;
24
27
 
25
28
  export const COLOR_MAP = {
26
- long: ['#fcedca', '#FAB313'],
27
- short: ['#c8ebfa', '#309DCC'],
29
+ long: ['#eaf5fa', '#83c4e0'],
30
+ short: ['#fef7e7', '#fcd171'],
28
31
  } as const;
32
+
33
+ export const INSTRUMENTS_CONFIG: Record<
34
+ InstrumentId,
35
+ {
36
+ precision: number;
37
+ defaultBucketWidth: number;
38
+ v20name: string;
39
+ mt5name: string;
40
+ }
41
+ > = {
42
+ [InstrumentId.EUR_AUD]: {
43
+ mt5name: 'EURAUD',
44
+ v20name: 'EUR_AUD',
45
+ precision: 5,
46
+ defaultBucketWidth: 0.0005,
47
+ },
48
+ [InstrumentId.EUR_GBP]: {
49
+ mt5name: 'EURGBP',
50
+ v20name: 'EUR_GBP',
51
+ precision: 5,
52
+ defaultBucketWidth: 0.0005,
53
+ },
54
+ [InstrumentId.EUR_JPY]: {
55
+ mt5name: 'EURJPY',
56
+ v20name: 'EUR_JPY',
57
+ precision: 3,
58
+ defaultBucketWidth: 0.05,
59
+ },
60
+ [InstrumentId.EUR_USD]: {
61
+ mt5name: 'EURUSD',
62
+ v20name: 'EUR_USD',
63
+ precision: 5,
64
+ defaultBucketWidth: 0.0005,
65
+ },
66
+ [InstrumentId.EUR_CHF]: {
67
+ mt5name: 'EURCHF',
68
+ v20name: 'EUR_CHF',
69
+ precision: 5,
70
+ defaultBucketWidth: 0.0005,
71
+ },
72
+ [InstrumentId.USD_CHF]: {
73
+ mt5name: 'USDCHF',
74
+ v20name: 'USD_CHF',
75
+ precision: 5,
76
+ defaultBucketWidth: 0.0005,
77
+ },
78
+ [InstrumentId.USD_JPY]: {
79
+ mt5name: 'USDJPY',
80
+ v20name: 'USD_JPY',
81
+ precision: 3,
82
+ defaultBucketWidth: 0.05,
83
+ },
84
+ [InstrumentId.USD_CAD]: {
85
+ mt5name: 'USDCAD',
86
+ v20name: 'USD_CAD',
87
+ precision: 5,
88
+ defaultBucketWidth: 0.0005,
89
+ },
90
+ [InstrumentId.GBP_USD]: {
91
+ mt5name: 'GBPUSD',
92
+ v20name: 'GBP_USD',
93
+ precision: 5,
94
+ defaultBucketWidth: 0.0005,
95
+ },
96
+ [InstrumentId.GBP_JPY]: {
97
+ mt5name: 'GBPJPY',
98
+ v20name: 'GBP_JPY',
99
+ precision: 3,
100
+ defaultBucketWidth: 0.05,
101
+ },
102
+ [InstrumentId.GBP_CHF]: {
103
+ mt5name: 'GBPCHF',
104
+ v20name: 'GBP_CHF',
105
+ precision: 5,
106
+ defaultBucketWidth: 0.0005,
107
+ },
108
+ [InstrumentId.AUD_JPY]: {
109
+ mt5name: 'AUDJPY',
110
+ v20name: 'AUD_JPY',
111
+ precision: 3,
112
+ defaultBucketWidth: 0.05,
113
+ },
114
+ [InstrumentId.AUD_USD]: {
115
+ mt5name: 'AUDUSD',
116
+ v20name: 'AUD_USD',
117
+ precision: 5,
118
+ defaultBucketWidth: 0.0005,
119
+ },
120
+ [InstrumentId.NZD_USD]: {
121
+ mt5name: 'NZDUSD',
122
+ v20name: 'NZD_USD',
123
+ precision: 5,
124
+ defaultBucketWidth: 0.0005,
125
+ },
126
+ [InstrumentId.XAU_USD]: {
127
+ mt5name: 'XAUUSD',
128
+ v20name: 'XAU_USD',
129
+ precision: 3,
130
+ defaultBucketWidth: 0.5,
131
+ },
132
+ [InstrumentId.XAG_USD]: {
133
+ mt5name: 'XAGUSD',
134
+ v20name: 'XAG_USD',
135
+ precision: 5,
136
+ defaultBucketWidth: 0.0005,
137
+ },
138
+ };
@@ -1,5 +1,5 @@
1
1
  import { BookType, Granularity } from '../gql/types/graphql';
2
- import { InstrumentId, InstrumentIdOC } from './types/instruments';
2
+ import { InstrumentId } from './types';
3
3
 
4
4
  const navigationConfig = [
5
5
  {
@@ -13,65 +13,6 @@ const navigationConfig = [
13
13
  ];
14
14
 
15
15
  const instrumentSelectConfigOC = [
16
- {
17
- id: InstrumentIdOC.EUR_AUD,
18
- label: 'EUR/AUD',
19
- },
20
- {
21
- id: InstrumentIdOC.EUR_GBP,
22
- label: 'EUR/GBP',
23
- },
24
- {
25
- id: InstrumentIdOC.EUR_JPY,
26
- label: 'EUR/JPY',
27
- },
28
- {
29
- id: InstrumentIdOC.EUR_USD,
30
- label: 'EUR/USD',
31
- },
32
- {
33
- id: InstrumentIdOC.EUR_CHF,
34
- label: 'EUR/CHF',
35
- },
36
- {
37
- id: InstrumentIdOC.USD_CHF,
38
- label: 'USD/CHF',
39
- },
40
- {
41
- id: InstrumentIdOC.USD_JPY,
42
- label: 'USD/JPY',
43
- },
44
- {
45
- id: InstrumentIdOC.USD_CAD,
46
- label: 'USD/CAD',
47
- },
48
- {
49
- id: InstrumentIdOC.GBP_USD,
50
- label: 'GBP/USD',
51
- },
52
- {
53
- id: InstrumentIdOC.GBP_JPY,
54
- label: 'GBP/JPY',
55
- },
56
- {
57
- id: InstrumentIdOC.GBP_CHF,
58
- label: 'GBP/CHF',
59
- },
60
- {
61
- id: InstrumentIdOC.AUD_JPY,
62
- label: 'AUD/JPY',
63
- },
64
- {
65
- id: InstrumentIdOC.AUD_USD,
66
- label: 'AUD/USD',
67
- },
68
- {
69
- id: InstrumentIdOC.NZD_USD,
70
- label: 'NZD/USD',
71
- },
72
- ];
73
-
74
- const instrumentSelectConfig = [
75
16
  {
76
17
  id: InstrumentId.EUR_AUD,
77
18
  label: 'EUR/AUD',
@@ -128,6 +69,10 @@ const instrumentSelectConfig = [
128
69
  id: InstrumentId.NZD_USD,
129
70
  label: 'NZD/USD',
130
71
  },
72
+ ];
73
+
74
+ const instrumentSelectConfig = [
75
+ ...instrumentSelectConfigOC,
131
76
  {
132
77
  id: InstrumentId.XAU_USD,
133
78
  label: 'XAU/USD',
@@ -1,8 +1,25 @@
1
1
  import type { WidgetConfig } from '@oanda/labs-widget-common';
2
2
 
3
3
  import type { Division } from '../gql/types/graphql';
4
- import type { InstrumentId } from './types/instruments';
5
4
 
5
+ export enum InstrumentId {
6
+ EUR_AUD = 'EURAUD',
7
+ EUR_GBP = 'EURGBP',
8
+ EUR_JPY = 'EURJPY',
9
+ EUR_USD = 'EURUSD',
10
+ EUR_CHF = 'EURCHF',
11
+ USD_CHF = 'USDCHF',
12
+ USD_JPY = 'USDJPY',
13
+ USD_CAD = 'USDCAD',
14
+ GBP_USD = 'GBPUSD',
15
+ GBP_JPY = 'GBPJPY',
16
+ GBP_CHF = 'GBPCHF',
17
+ AUD_JPY = 'AUDJPY',
18
+ AUD_USD = 'AUDUSD',
19
+ NZD_USD = 'NZDUSD',
20
+ XAU_USD = 'XAUUSD',
21
+ XAG_USD = 'XAGUSD',
22
+ }
6
23
  export interface CrowdViewConfig extends WidgetConfig {
7
24
  division: Division;
8
25
  }
@@ -1,26 +1,31 @@
1
1
  {
2
- "data_unavailable": "Data unavailable",
3
- "no_matching_results": "No matching results",
4
- "pagination_entries_range": "{{firstItemOnPage}}-{{lastItemOnPage}} of {{itemCount}} entries",
5
- "order_book": "Order book",
6
- "position_book": "Position book",
7
- "long": "Long",
8
- "short": "Short",
9
- "instrument": "Instrument",
10
- "granularity": "Granularity",
11
- "search": "Search",
12
- "5_minutes": "5 minutes",
13
- "15_minutes": "15 minutes",
14
2
  "1_hour": "1 hour",
3
+ "15_minutes": "15 minutes",
15
4
  "4_hours": "4 hours",
5
+ "5_minutes": "5 minutes",
6
+ "buy_overbalance": "Buy overbalance",
7
+ "buy": "Buy",
16
8
  "candle": "Candle",
17
- "open_price": "Open price",
18
9
  "close_price": "Close price",
19
- "low": "Low",
10
+ "data_unavailable": "Data unavailable",
11
+ "granularity": "Granularity",
20
12
  "high": "High",
13
+ "instrument": "Instrument",
14
+ "long_overbalance": "Long overbalance",
15
+ "long": "Long",
16
+ "low": "Low",
17
+ "no_matching_results": "No matching results",
18
+ "open_price": "Open price",
19
+ "order_book": "Order book",
21
20
  "orders": "Orders",
21
+ "pagination_entries_range": "{{firstItemOnPage}}-{{lastItemOnPage}} of {{itemCount}} entries",
22
+ "position_book": "Position book",
23
+ "positions": "Positions",
22
24
  "price_range": "Price range",
25
+ "search": "Search",
26
+ "sell_overbalance": "Sell overbalance",
27
+ "sell": "Sell",
23
28
  "sentiment": "Sentiment",
24
- "buy_advantage": "Buy advantage",
25
- "sell_advantage": "Sell advantage"
29
+ "short_overbalance": "Short overbalance",
30
+ "short": "Short"
26
31
  }
@@ -7,7 +7,7 @@ import { render } from '@testing-library/react';
7
7
  import React from 'react';
8
8
 
9
9
  import { Main } from '../src/CrowdViewWidget/Main';
10
- import { InstrumentId } from '../src/CrowdViewWidget/types/instruments';
10
+ import { InstrumentId } from '../src/CrowdViewWidget/types';
11
11
  import { getOrderPositionBooks } from '../src/gql/getOrderPositionBooks';
12
12
  import { getPriceCandles } from '../src/gql/getPriceCandles';
13
13
  import {