@oanda/labs-crowd-view-widget 1.0.45 → 1.0.46
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.
- package/CHANGELOG.md +188 -0
- package/dist/main/CrowdViewWidget/Main.js +3 -1
- package/dist/main/CrowdViewWidget/Main.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js +16 -5
- package/dist/main/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/index.js +4 -4
- package/dist/main/CrowdViewWidget/components/Chart/index.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/types.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js +17 -101
- package/dist/main/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js +37 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js +19 -4
- package/dist/main/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
- package/dist/main/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js +14 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/index.js +83 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/index.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/processBuckets.js +29 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/processBuckets.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js +23 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/processPriceCandles.js +43 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/processPriceCandles.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/validateData.js +23 -0
- package/dist/main/CrowdViewWidget/components/Chart/utils/validateData.js.map +1 -0
- package/dist/main/CrowdViewWidget/components/Legend/Legend.js +5 -3
- package/dist/main/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
- package/dist/main/CrowdViewWidget/constants.js +104 -5
- package/dist/main/CrowdViewWidget/constants.js.map +1 -1
- package/dist/main/CrowdViewWidget/selectConfig.js +18 -60
- package/dist/main/CrowdViewWidget/selectConfig.js.map +1 -1
- package/dist/main/CrowdViewWidget/types.js +20 -0
- package/dist/main/CrowdViewWidget/types.js.map +1 -1
- package/dist/main/translations/sources/en.json +21 -16
- package/dist/module/CrowdViewWidget/Main.js +3 -1
- package/dist/module/CrowdViewWidget/Main.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js +16 -5
- package/dist/module/CrowdViewWidget/components/Chart/chartOptions.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/index.js +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/index.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/types.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js +13 -97
- package/dist/module/CrowdViewWidget/components/Chart/useCrowdViewData.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js +29 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/aggregateBuckets.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js +20 -5
- package/dist/module/CrowdViewWidget/components/Chart/utils/chartUtils.js.map +1 -1
- package/dist/module/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js +7 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/index.js +8 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/index.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/processBuckets.js +22 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/processBuckets.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js +16 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/processPriceCandles.js +36 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/processPriceCandles.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/validateData.js +16 -0
- package/dist/module/CrowdViewWidget/components/Chart/utils/validateData.js.map +1 -0
- package/dist/module/CrowdViewWidget/components/Legend/Legend.js +5 -3
- package/dist/module/CrowdViewWidget/components/Legend/Legend.js.map +1 -1
- package/dist/module/CrowdViewWidget/constants.js +103 -4
- package/dist/module/CrowdViewWidget/constants.js.map +1 -1
- package/dist/module/CrowdViewWidget/selectConfig.js +3 -45
- package/dist/module/CrowdViewWidget/selectConfig.js.map +1 -1
- package/dist/module/CrowdViewWidget/types.js +19 -1
- package/dist/module/CrowdViewWidget/types.js.map +1 -1
- package/dist/module/translations/sources/en.json +21 -16
- package/dist/types/CrowdViewWidget/components/Chart/index.d.ts +1 -1
- package/dist/types/CrowdViewWidget/components/Chart/types.d.ts +11 -7
- package/dist/types/CrowdViewWidget/components/Chart/utils/aggregateBuckets.d.ts +2 -0
- package/dist/types/CrowdViewWidget/components/Chart/utils/chartUtils.d.ts +11 -6
- package/dist/types/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.d.ts +3 -0
- package/dist/types/CrowdViewWidget/components/Chart/utils/index.d.ts +7 -0
- package/dist/types/CrowdViewWidget/components/Chart/utils/processBuckets.d.ts +3 -0
- package/dist/types/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.d.ts +8 -0
- package/dist/types/CrowdViewWidget/components/Chart/utils/processPriceCandles.d.ts +27 -0
- package/dist/types/CrowdViewWidget/components/Chart/utils/validateData.d.ts +2 -0
- package/dist/types/CrowdViewWidget/components/Legend/Legend.d.ts +3 -1
- package/dist/types/CrowdViewWidget/constants.d.ts +11 -4
- package/dist/types/CrowdViewWidget/selectConfig.d.ts +2 -2
- package/dist/types/CrowdViewWidget/types.d.ts +18 -1
- package/dist/types/CrowdViewWidget/utils/instrumentUtils.d.ts +1 -4
- package/package.json +4 -3
- package/src/CrowdViewWidget/Main.tsx +2 -3
- package/src/CrowdViewWidget/components/Chart/chartOptions.ts +35 -25
- package/src/CrowdViewWidget/components/Chart/index.ts +1 -1
- package/src/CrowdViewWidget/components/Chart/types.ts +12 -4
- package/src/CrowdViewWidget/components/Chart/useCrowdViewData.ts +30 -154
- package/src/CrowdViewWidget/components/Chart/utils/aggregateBuckets.ts +44 -0
- package/src/CrowdViewWidget/components/Chart/utils/chartUtils.ts +37 -12
- package/src/CrowdViewWidget/components/Chart/utils/getTargetBucketWidth.ts +13 -0
- package/src/CrowdViewWidget/components/Chart/utils/index.ts +7 -0
- package/src/CrowdViewWidget/components/Chart/utils/processBuckets.ts +43 -0
- package/src/CrowdViewWidget/components/Chart/utils/processOrderPositionBooks.ts +30 -0
- package/src/CrowdViewWidget/components/Chart/utils/processPriceCandles.ts +53 -0
- package/src/CrowdViewWidget/components/Chart/utils/validateData.ts +27 -0
- package/src/CrowdViewWidget/components/Legend/Legend.tsx +13 -2
- package/src/CrowdViewWidget/constants.ts +113 -4
- package/src/CrowdViewWidget/selectConfig.ts +5 -60
- package/src/CrowdViewWidget/types.ts +18 -1
- package/src/translations/sources/en.json +21 -16
- package/test/Main.test.tsx +1 -1
- package/test/components/Chart/utils/chartUtils.test.ts +12 -26
- package/test/components/Legend.test.tsx +6 -1
- package/test/utils/aggregateBuckets.test.ts +82 -0
- package/test/utils/getTargetBucketWidth.test.ts +37 -0
- package/test/utils/instrumentUtils.test.ts +13 -7
- package/test/utils/processBuckets.test.ts +153 -0
- package/test/utils/processOrderPositionBooks.test.ts +127 -0
- package/test/utils/processPriceCandles.test.ts +245 -0
- package/test/utils/validateData.test.ts +201 -0
- package/dist/main/CrowdViewWidget/types/index.js +0 -17
- package/dist/main/CrowdViewWidget/types/index.js.map +0 -1
- package/dist/main/CrowdViewWidget/types/instruments.js +0 -45
- package/dist/main/CrowdViewWidget/types/instruments.js.map +0 -1
- package/dist/module/CrowdViewWidget/types/index.js +0 -2
- package/dist/module/CrowdViewWidget/types/index.js.map +0 -1
- package/dist/module/CrowdViewWidget/types/instruments.js +0 -39
- package/dist/module/CrowdViewWidget/types/instruments.js.map +0 -1
- package/dist/types/CrowdViewWidget/types/index.d.ts +0 -1
- package/dist/types/CrowdViewWidget/types/instruments.d.ts +0 -36
- package/src/CrowdViewWidget/types/index.ts +0 -1
- package/src/CrowdViewWidget/types/instruments.ts +0 -37
|
@@ -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
22
|
<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
|
|
21
|
-
|
|
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
|
};
|
|
@@ -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
|
-
|
|
9
|
+
MULTIPLIER: 4,
|
|
8
10
|
PRICE_PADDING_MULTIPLIER: 2,
|
|
9
11
|
} as const;
|
|
10
12
|
|
|
@@ -17,12 +19,119 @@ 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:
|
|
22
|
+
INITIAL_START_ZOOM: 80,
|
|
21
23
|
INITIAL_END_ZOOM: 100,
|
|
22
24
|
X_AXIS_DATE_PADDING: ' ',
|
|
23
25
|
} as const;
|
|
24
26
|
|
|
25
27
|
export const COLOR_MAP = {
|
|
26
|
-
long: ['#
|
|
27
|
-
short: ['#
|
|
28
|
+
long: ['#eaf5fa', '#83c4e0'],
|
|
29
|
+
short: ['#fef7e7', '#fcd171'],
|
|
28
30
|
} as const;
|
|
31
|
+
|
|
32
|
+
export const INSTRUMENTS_CONFIG: Record<
|
|
33
|
+
InstrumentId,
|
|
34
|
+
{
|
|
35
|
+
precision: number;
|
|
36
|
+
defaultBucketWidth: number;
|
|
37
|
+
v20name: string;
|
|
38
|
+
mt5name: string;
|
|
39
|
+
}
|
|
40
|
+
> = {
|
|
41
|
+
[InstrumentId.EUR_AUD]: {
|
|
42
|
+
mt5name: 'EURAUD',
|
|
43
|
+
v20name: 'EUR_AUD',
|
|
44
|
+
precision: 5,
|
|
45
|
+
defaultBucketWidth: 0.0005,
|
|
46
|
+
},
|
|
47
|
+
[InstrumentId.EUR_GBP]: {
|
|
48
|
+
mt5name: 'EURGBP',
|
|
49
|
+
v20name: 'EUR_GBP',
|
|
50
|
+
precision: 5,
|
|
51
|
+
defaultBucketWidth: 0.0005,
|
|
52
|
+
},
|
|
53
|
+
[InstrumentId.EUR_JPY]: {
|
|
54
|
+
mt5name: 'EURJPY',
|
|
55
|
+
v20name: 'EUR_JPY',
|
|
56
|
+
precision: 3,
|
|
57
|
+
defaultBucketWidth: 0.05,
|
|
58
|
+
},
|
|
59
|
+
[InstrumentId.EUR_USD]: {
|
|
60
|
+
mt5name: 'EURUSD',
|
|
61
|
+
v20name: 'EUR_USD',
|
|
62
|
+
precision: 5,
|
|
63
|
+
defaultBucketWidth: 0.0005,
|
|
64
|
+
},
|
|
65
|
+
[InstrumentId.EUR_CHF]: {
|
|
66
|
+
mt5name: 'EURCHF',
|
|
67
|
+
v20name: 'EUR_CHF',
|
|
68
|
+
precision: 5,
|
|
69
|
+
defaultBucketWidth: 0.0005,
|
|
70
|
+
},
|
|
71
|
+
[InstrumentId.USD_CHF]: {
|
|
72
|
+
mt5name: 'USDCHF',
|
|
73
|
+
v20name: 'USD_CHF',
|
|
74
|
+
precision: 5,
|
|
75
|
+
defaultBucketWidth: 0.0005,
|
|
76
|
+
},
|
|
77
|
+
[InstrumentId.USD_JPY]: {
|
|
78
|
+
mt5name: 'USDJPY',
|
|
79
|
+
v20name: 'USD_JPY',
|
|
80
|
+
precision: 3,
|
|
81
|
+
defaultBucketWidth: 0.05,
|
|
82
|
+
},
|
|
83
|
+
[InstrumentId.USD_CAD]: {
|
|
84
|
+
mt5name: 'USDCAD',
|
|
85
|
+
v20name: 'USD_CAD',
|
|
86
|
+
precision: 5,
|
|
87
|
+
defaultBucketWidth: 0.0005,
|
|
88
|
+
},
|
|
89
|
+
[InstrumentId.GBP_USD]: {
|
|
90
|
+
mt5name: 'GBPUSD',
|
|
91
|
+
v20name: 'GBP_USD',
|
|
92
|
+
precision: 5,
|
|
93
|
+
defaultBucketWidth: 0.0005,
|
|
94
|
+
},
|
|
95
|
+
[InstrumentId.GBP_JPY]: {
|
|
96
|
+
mt5name: 'GBPJPY',
|
|
97
|
+
v20name: 'GBP_JPY',
|
|
98
|
+
precision: 3,
|
|
99
|
+
defaultBucketWidth: 0.05,
|
|
100
|
+
},
|
|
101
|
+
[InstrumentId.GBP_CHF]: {
|
|
102
|
+
mt5name: 'GBPCHF',
|
|
103
|
+
v20name: 'GBP_CHF',
|
|
104
|
+
precision: 5,
|
|
105
|
+
defaultBucketWidth: 0.0005,
|
|
106
|
+
},
|
|
107
|
+
[InstrumentId.AUD_JPY]: {
|
|
108
|
+
mt5name: 'AUDJPY',
|
|
109
|
+
v20name: 'AUD_JPY',
|
|
110
|
+
precision: 3,
|
|
111
|
+
defaultBucketWidth: 0.05,
|
|
112
|
+
},
|
|
113
|
+
[InstrumentId.AUD_USD]: {
|
|
114
|
+
mt5name: 'AUDUSD',
|
|
115
|
+
v20name: 'AUD_USD',
|
|
116
|
+
precision: 5,
|
|
117
|
+
defaultBucketWidth: 0.0005,
|
|
118
|
+
},
|
|
119
|
+
[InstrumentId.NZD_USD]: {
|
|
120
|
+
mt5name: 'NZDUSD',
|
|
121
|
+
v20name: 'NZD_USD',
|
|
122
|
+
precision: 5,
|
|
123
|
+
defaultBucketWidth: 0.0005,
|
|
124
|
+
},
|
|
125
|
+
[InstrumentId.XAU_USD]: {
|
|
126
|
+
mt5name: 'XAUUSD',
|
|
127
|
+
v20name: 'XAU_USD',
|
|
128
|
+
precision: 3,
|
|
129
|
+
defaultBucketWidth: 0.5,
|
|
130
|
+
},
|
|
131
|
+
[InstrumentId.XAG_USD]: {
|
|
132
|
+
mt5name: 'XAGUSD',
|
|
133
|
+
v20name: 'XAG_USD',
|
|
134
|
+
precision: 5,
|
|
135
|
+
defaultBucketWidth: 0.0005,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BookType, Granularity } from '../gql/types/graphql';
|
|
2
|
-
import { InstrumentId
|
|
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_advantage": "Buy advantage",
|
|
7
|
+
"buy": "Buy",
|
|
16
8
|
"candle": "Candle",
|
|
17
|
-
"open_price": "Open price",
|
|
18
9
|
"close_price": "Close price",
|
|
19
|
-
"
|
|
10
|
+
"data_unavailable": "Data unavailable",
|
|
11
|
+
"granularity": "Granularity",
|
|
20
12
|
"high": "High",
|
|
13
|
+
"instrument": "Instrument",
|
|
14
|
+
"long_advantage": "Sell advantage",
|
|
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_advantage": "Sell advantage",
|
|
27
|
+
"sell": "Sell",
|
|
23
28
|
"sentiment": "Sentiment",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
29
|
+
"short_advantage": "Buy advantage",
|
|
30
|
+
"short": "Short"
|
|
26
31
|
}
|
package/test/Main.test.tsx
CHANGED
|
@@ -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
|
|
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 {
|
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
BOOKS_THRESHOLDS,
|
|
11
11
|
COLOR_MAP,
|
|
12
12
|
} from '../../../../src/CrowdViewWidget/constants';
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BookType,
|
|
15
|
+
Granularity,
|
|
16
|
+
TimeSpan,
|
|
17
|
+
} from '../../../../src/gql/types/graphql';
|
|
14
18
|
|
|
15
19
|
describe('chartUtils', () => {
|
|
16
20
|
describe('getTimeSpanForGranularity', () => {
|
|
@@ -131,13 +135,15 @@ describe('chartUtils', () => {
|
|
|
131
135
|
],
|
|
132
136
|
];
|
|
133
137
|
|
|
134
|
-
const html = getTooltipFormatter(
|
|
138
|
+
const html = getTooltipFormatter({
|
|
135
139
|
params,
|
|
136
140
|
buckets,
|
|
137
|
-
0.0005,
|
|
138
|
-
1.3306,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
bucketWidth: 0.0005,
|
|
142
|
+
selectedPrice: 1.3306,
|
|
143
|
+
precision: 5,
|
|
144
|
+
bookType: BookType.Order,
|
|
145
|
+
labelCallback,
|
|
146
|
+
});
|
|
141
147
|
expect(html).toContain('candle');
|
|
142
148
|
expect(html).toContain('open_price');
|
|
143
149
|
expect(html).toContain('close_price');
|
|
@@ -148,25 +154,5 @@ describe('chartUtils', () => {
|
|
|
148
154
|
// Selected price 1.3306 falls into second bucket 1.3305 - 1.3310 which has negative sentiment
|
|
149
155
|
expect(html).toContain('sell_advantage');
|
|
150
156
|
});
|
|
151
|
-
|
|
152
|
-
it('omits sections when data is missing', () => {
|
|
153
|
-
const params = [
|
|
154
|
-
{
|
|
155
|
-
axisValue: '2025-03-15T10:30:00Z',
|
|
156
|
-
value: [0, 0, 0, 0, 0], // no candle values
|
|
157
|
-
},
|
|
158
|
-
{ value: ['2025-03-15T10:30:00Z', 0, 0] },
|
|
159
|
-
];
|
|
160
|
-
const buckets: Array<Array<{ price: number; sentiment: number }>> = [[]];
|
|
161
|
-
const html = getTooltipFormatter(
|
|
162
|
-
params,
|
|
163
|
-
buckets,
|
|
164
|
-
0.0005,
|
|
165
|
-
0,
|
|
166
|
-
labelCallback
|
|
167
|
-
);
|
|
168
|
-
expect(html).not.toContain('open_price');
|
|
169
|
-
expect(html).not.toContain('orders');
|
|
170
|
-
});
|
|
171
157
|
});
|
|
172
158
|
});
|
|
@@ -6,13 +6,18 @@ import { render } from '@testing-library/react';
|
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
8
8
|
import { Legend } from '../../src/CrowdViewWidget/components';
|
|
9
|
+
import { BookType } from '../../src/gql/types/graphql';
|
|
9
10
|
|
|
10
11
|
describe('Crowd View Widget', () => {
|
|
11
12
|
describe('components', () => {
|
|
12
13
|
describe('<Legend />', () => {
|
|
13
14
|
it('renders two LegendBar components', () => {
|
|
14
15
|
const { getAllByText } = render(
|
|
15
|
-
<Legend
|
|
16
|
+
<Legend
|
|
17
|
+
bookType={BookType.Position}
|
|
18
|
+
longValues={[0.15, 0.55]}
|
|
19
|
+
shortValues={[0.15, 0.55]}
|
|
20
|
+
/>
|
|
16
21
|
);
|
|
17
22
|
|
|
18
23
|
expect(getAllByText(/long/)).toHaveLength(2);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { aggregateBuckets } from '../../src/CrowdViewWidget/components';
|
|
2
|
+
|
|
3
|
+
describe('aggregateBuckets', () => {
|
|
4
|
+
it('should correctly aggregate a standard set of buckets', () => {
|
|
5
|
+
const buckets = [
|
|
6
|
+
{ price: 1, sentiment: 1 },
|
|
7
|
+
{ price: 1.05, sentiment: 2 },
|
|
8
|
+
{ price: 1.1, sentiment: 4 },
|
|
9
|
+
{ price: 1.15, sentiment: 3 },
|
|
10
|
+
{ price: 1.2, sentiment: 7 },
|
|
11
|
+
{ price: 1.35, sentiment: 8 },
|
|
12
|
+
{ price: 1.6, sentiment: 10 },
|
|
13
|
+
{ price: 1.65, sentiment: 1 },
|
|
14
|
+
{ price: 1.8, sentiment: 18 },
|
|
15
|
+
];
|
|
16
|
+
const newBucketWidth = 0.1;
|
|
17
|
+
const expected = [
|
|
18
|
+
{ price: 1.0, sentiment: 3 },
|
|
19
|
+
{ price: 1.1, sentiment: 7 },
|
|
20
|
+
{ price: 1.2, sentiment: 7 },
|
|
21
|
+
{ price: 1.3, sentiment: 8 },
|
|
22
|
+
{ price: 1.6, sentiment: 11 },
|
|
23
|
+
{ price: 1.8, sentiment: 18 },
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
expect(aggregateBuckets(buckets, newBucketWidth)).toEqual(expected);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should return an empty array if the input array is empty', () => {
|
|
30
|
+
expect(aggregateBuckets([], 0.1)).toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should correctly process an array with a single bucket', () => {
|
|
34
|
+
const buckets = [{ price: 2.58, sentiment: 15 }];
|
|
35
|
+
const newBucketWidth = 0.2;
|
|
36
|
+
const expected = [{ price: 2.4, sentiment: 15 }];
|
|
37
|
+
|
|
38
|
+
expect(aggregateBuckets(buckets, newBucketWidth)).toEqual(expected);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should handle gaps in data without creating empty buckets', () => {
|
|
42
|
+
const buckets = [
|
|
43
|
+
{ price: 10.1, sentiment: 5 }, // Belongs to bucket 10.0
|
|
44
|
+
{ price: 10.9, sentiment: 8 }, // Belongs to bucket 10.8
|
|
45
|
+
];
|
|
46
|
+
const newBucketWidth = 0.2;
|
|
47
|
+
const expected = [
|
|
48
|
+
{ price: 10.0, sentiment: 5 },
|
|
49
|
+
{ price: 10.8, sentiment: 8 },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
expect(aggregateBuckets(buckets, newBucketWidth)).toEqual(expected);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should aggregate all items into a single bucket if they fall within the new width', () => {
|
|
56
|
+
const buckets = [
|
|
57
|
+
{ price: 1.1, sentiment: 10 },
|
|
58
|
+
{ price: 1.5, sentiment: 20 },
|
|
59
|
+
{ price: 1.9, sentiment: 30 },
|
|
60
|
+
];
|
|
61
|
+
const newBucketWidth = 2.0;
|
|
62
|
+
const expected = [{ price: 0.0, sentiment: 60 }];
|
|
63
|
+
|
|
64
|
+
expect(aggregateBuckets(buckets, newBucketWidth)).toEqual(expected);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should correctly sum sentiments including zero and negative values', () => {
|
|
68
|
+
const buckets = [
|
|
69
|
+
{ price: 5.0, sentiment: 100 },
|
|
70
|
+
{ price: 5.05, sentiment: -20 },
|
|
71
|
+
{ price: 5.11, sentiment: 0 },
|
|
72
|
+
{ price: 5.18, sentiment: 5 },
|
|
73
|
+
];
|
|
74
|
+
const newBucketWidth = 0.1;
|
|
75
|
+
const expected = [
|
|
76
|
+
{ price: 5.0, sentiment: 80 },
|
|
77
|
+
{ price: 5.1, sentiment: 5 },
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
expect(aggregateBuckets(buckets, newBucketWidth)).toEqual(expected);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { getTargetBucketWidth } from '../../src/CrowdViewWidget/components';
|
|
2
|
+
import {
|
|
3
|
+
BUCKET_CONFIG,
|
|
4
|
+
INSTRUMENTS_CONFIG,
|
|
5
|
+
} from '../../src/CrowdViewWidget/constants';
|
|
6
|
+
import { InstrumentId } from '../../src/CrowdViewWidget/types';
|
|
7
|
+
import { Granularity } from '../../src/gql/types/graphql';
|
|
8
|
+
|
|
9
|
+
describe('getTargetBucketWidth', () => {
|
|
10
|
+
const instrument = InstrumentId.EUR_AUD;
|
|
11
|
+
const { defaultBucketWidth } = INSTRUMENTS_CONFIG[instrument];
|
|
12
|
+
const optimizedBucketWidth = defaultBucketWidth * BUCKET_CONFIG.MULTIPLIER;
|
|
13
|
+
|
|
14
|
+
it('should return optimized width for H1 granularity', () => {
|
|
15
|
+
expect(getTargetBucketWidth(Granularity.H1, instrument)).toBe(
|
|
16
|
+
optimizedBucketWidth
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('should return optimized width for H4 granularity', () => {
|
|
21
|
+
expect(getTargetBucketWidth(Granularity.H4, instrument)).toBe(
|
|
22
|
+
optimizedBucketWidth
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should return default width for M5 granularity', () => {
|
|
27
|
+
expect(getTargetBucketWidth(Granularity.M5, instrument)).toBe(
|
|
28
|
+
defaultBucketWidth
|
|
29
|
+
);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should return default width for M15 granularity', () => {
|
|
33
|
+
expect(getTargetBucketWidth(Granularity.M15, instrument)).toBe(
|
|
34
|
+
defaultBucketWidth
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -14,11 +14,13 @@ describe('instrumentUtils', () => {
|
|
|
14
14
|
expect(Array.isArray(result)).toBe(true);
|
|
15
15
|
expect(result.length).toBeGreaterThan(0);
|
|
16
16
|
|
|
17
|
-
// Check that the first instrument has the expected
|
|
17
|
+
// Check that the first instrument has the expected structure
|
|
18
18
|
const firstInstrument = result[0];
|
|
19
19
|
expect(firstInstrument).toHaveProperty('id');
|
|
20
20
|
expect(firstInstrument).toHaveProperty('label');
|
|
21
|
-
|
|
21
|
+
// Instrument IDs are now enum values like 'EURAUD', not 'EUR_AUD'
|
|
22
|
+
expect(typeof firstInstrument.id).toBe('string');
|
|
23
|
+
expect(firstInstrument.id.length).toBeGreaterThan(0);
|
|
22
24
|
});
|
|
23
25
|
|
|
24
26
|
it('should return OAP instrument config for OAP division', () => {
|
|
@@ -28,11 +30,13 @@ describe('instrumentUtils', () => {
|
|
|
28
30
|
expect(Array.isArray(result)).toBe(true);
|
|
29
31
|
expect(result.length).toBeGreaterThan(0);
|
|
30
32
|
|
|
31
|
-
// Check that the first instrument has the expected
|
|
33
|
+
// Check that the first instrument has the expected structure
|
|
32
34
|
const firstInstrument = result[0];
|
|
33
35
|
expect(firstInstrument).toHaveProperty('id');
|
|
34
36
|
expect(firstInstrument).toHaveProperty('label');
|
|
35
|
-
|
|
37
|
+
// Instrument IDs are now enum values like 'EURAUD', not 'EURUSD'
|
|
38
|
+
expect(typeof firstInstrument.id).toBe('string');
|
|
39
|
+
expect(firstInstrument.id.length).toBeGreaterThan(0);
|
|
36
40
|
});
|
|
37
41
|
|
|
38
42
|
it('should return different configs for different divisions', () => {
|
|
@@ -41,12 +45,14 @@ describe('instrumentUtils', () => {
|
|
|
41
45
|
|
|
42
46
|
expect(ocConfig).not.toEqual(oapConfig);
|
|
43
47
|
|
|
44
|
-
//
|
|
48
|
+
// Both configs should have valid instrument IDs
|
|
45
49
|
const ocFirstId = ocConfig[0].id;
|
|
46
50
|
const oapFirstId = oapConfig[0].id;
|
|
47
51
|
|
|
48
|
-
expect(ocFirstId).
|
|
49
|
-
expect(oapFirstId).
|
|
52
|
+
expect(typeof ocFirstId).toBe('string');
|
|
53
|
+
expect(typeof oapFirstId).toBe('string');
|
|
54
|
+
// OAP config includes XAU_USD and XAG_USD which OC doesn't have
|
|
55
|
+
expect(oapConfig.length).toBeGreaterThan(ocConfig.length);
|
|
50
56
|
});
|
|
51
57
|
});
|
|
52
58
|
});
|