Skip to content

Commit a90e152

Browse files
authored
Akmal / feat: add ticks high low contract from smarttrader (#12873)
* feat: add ticks high low contract from smarttrader * fix: remove rudiment type * fix: win/lose color behaviour on ongoing contracts * fix: merge conflict * fix: typo * fix: spacing * fix: resolve merge issue * fix: use theme colors * fix: first tick when selected logic * fix: add missing fields for ongoing contracts
1 parent c1dbce7 commit a90e152

File tree

12 files changed

+165
-38
lines changed

12 files changed

+165
-38
lines changed

packages/core/src/Stores/Constants/markers.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ export const MARKER_TYPES_CONFIG = {
5858
},
5959
content_config: { spot_className: 'chart-spot__spot' },
6060
},
61+
SPOT_EXIT_2: {
62+
type: 'SPOT_EXIT_2',
63+
marker_config: {
64+
ContentComponent: MarkerSpot,
65+
},
66+
content_config: { className: 'chart-spot__spot' },
67+
},
6168
SPOT_MIDDLE: {
6269
type: 'SPOT_MIDDLE',
6370
marker_config: {

packages/core/src/Stores/Helpers/chart-marker-helpers.js

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ import {
33
formatMoney,
44
getEndTime,
55
isAccumulatorContract,
6-
isSmartTraderContract,
76
isDesktop,
87
isDigitContract,
98
isMobile,
109
isMultiplierContract,
10+
isSmartTraderContract,
11+
isTicksContract,
1112
isUserSold,
1213
isVanillaContract,
1314
} from '@deriv/shared';
@@ -24,7 +25,11 @@ const createMarkerConfig = (marker_type, x, y, content_config) =>
2425
});
2526

2627
export const getSpotCount = (contract_info, spot_count) => {
27-
if (isDigitContract(contract_info.contract_type)) return spot_count + 1;
28+
if (
29+
isDigitContract(contract_info.contract_type) ||
30+
(isTicksContract(contract_info.contract_type) && spot_count + 1 === contract_info.selected_tick)
31+
)
32+
return spot_count + 1;
2833
if (isAccumulatorContract(contract_info.contract_type) || isSmartTraderContract(contract_info.contract_type))
2934
return null;
3035
return spot_count;
@@ -72,14 +77,12 @@ export const createMarkerResetTime = contract_info => {
7277
export const createMarkerSpotEntry = contract_info => {
7378
if (!contract_info.entry_tick_time) return false;
7479

75-
let marker_type = MARKER_TYPES_CONFIG.SPOT_ENTRY.type;
76-
let component_props = {};
7780
const entry_tick = contract_info.entry_tick_display_value;
78-
79-
const spot_has_label = isDigitContract(contract_info.contract_type);
81+
const spot_has_label = isDigitContract(contract_info.contract_type) || isTicksContract(contract_info.contract_type);
82+
const marker_type = MARKER_TYPES_CONFIG.SPOT_ENTRY.type;
83+
let component_props = {};
8084

8185
if (spot_has_label) {
82-
marker_type = MARKER_TYPES_CONFIG.SPOT_MIDDLE.type;
8386
component_props = {
8487
spot_value: `${entry_tick}`,
8588
spot_epoch: `${contract_info.entry_tick_time}`,
@@ -93,6 +96,7 @@ export const createMarkerSpotEntry = contract_info => {
9396
export const createMarkerSpotExit = (contract_info, tick, idx) => {
9497
if (!contract_info.exit_tick_time) return false;
9598
const is_user_sold = isUserSold(contract_info);
99+
const is_ticks_contract = isTicksContract(contract_info.contract_type);
96100

97101
let spot_count, align_label;
98102
if (tick) {
@@ -104,10 +108,14 @@ export const createMarkerSpotExit = (contract_info, tick, idx) => {
104108

105109
const should_show_spot_exit = !is_user_sold || isMultiplierContract(contract_info.contract_type);
106110

111+
const should_show_spot_exit_2 = is_ticks_contract && idx + 1 !== contract_info.selected_tick;
112+
107113
const should_show_profit_label = isVanillaContract(contract_info.contract_type) && isDesktop();
108114

109115
const marker_spot_type = should_show_spot_exit
110-
? MARKER_TYPES_CONFIG.SPOT_EXIT.type
116+
? should_show_spot_exit_2
117+
? MARKER_TYPES_CONFIG.SPOT_EXIT_2.type
118+
: MARKER_TYPES_CONFIG.SPOT_EXIT.type
111119
: MARKER_TYPES_CONFIG.SPOT_SELL.type;
112120

113121
const component_props = should_show_spot_exit
@@ -116,7 +124,7 @@ export const createMarkerSpotExit = (contract_info, tick, idx) => {
116124
spot_epoch: `${contract_info.exit_tick_time}`,
117125
status: `${+contract_info.profit >= 0 ? 'won' : 'lost'}`,
118126
align_label: should_show_profit_label ? 'middle' : align_label,
119-
spot_count,
127+
spot_count: should_show_spot_exit_2 ? contract_info.tick_stream.length : spot_count,
120128
spot_profit:
121129
(should_show_profit_label &&
122130
`${formatMoney(contract_info.currency, contract_info.profit, true)} ${contract_info.currency}`) ||
@@ -129,6 +137,7 @@ export const createMarkerSpotExit = (contract_info, tick, idx) => {
129137

130138
export const createMarkerSpotMiddle = (contract_info, tick, idx) => {
131139
const is_accumulator = isAccumulatorContract(contract_info.contract_type);
140+
const is_ticks_contract = isTicksContract(contract_info.contract_type);
132141
const spot_count = getSpotCount(contract_info, idx);
133142
const spot = tick.tick_display_value;
134143
const spot_epoch = is_accumulator ? '' : `${tick.epoch}`;
@@ -137,11 +146,12 @@ export const createMarkerSpotMiddle = (contract_info, tick, idx) => {
137146
spot_value: `${spot}`,
138147
spot_epoch,
139148
align_label: tick.align_label,
140-
is_value_hidden: is_accumulator,
149+
is_value_hidden: is_accumulator || (is_ticks_contract && idx + 1 !== contract_info.selected_tick),
141150
spot_count,
151+
status: `${is_ticks_contract ? contract_info.status : ''}`,
142152
});
143153
marker_config.type = `${marker_config.type}_${idx}`;
144154

145-
if (isMobile() && spot_count > 1) return null;
155+
if (isMobile() && spot_count > 1 && !is_ticks_contract) return null;
146156
return marker_config;
147157
};

packages/core/src/Stores/Helpers/chart-markers.js

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,21 @@ import {
99
getSpotCount,
1010
} from './chart-marker-helpers';
1111
import {
12+
getContractStatus,
1213
getDecimalPlaces,
1314
getEndTime,
1415
isAccumulatorContract,
1516
isAccumulatorContractOpen,
1617
isDigitContract,
1718
isHighLow,
18-
isSmartTraderContract,
19+
isMultiplierContract,
1920
isOpen,
21+
isSmartTraderContract,
22+
isTicksContract,
2023
isTouchContract,
21-
isMultiplierContract,
24+
isTurbosContract,
2225
isVanillaContract,
23-
getContractStatus,
2426
unique,
25-
isTurbosContract,
2627
} from '@deriv/shared';
2728
import { localize } from '@deriv/translations';
2829
import { MARKER_TYPES_CONFIG } from '../Constants/markers';
@@ -86,6 +87,7 @@ const addLabelAlignment = (tick, idx, arr) => {
8687
export const createTickMarkers = (contract_info, is_delayed_markers_update) => {
8788
const is_accumulator = isAccumulatorContract(contract_info.contract_type);
8889
const is_smarttrader_contract = isSmartTraderContract(contract_info.contract_type);
90+
const is_ticks_contract = isTicksContract(contract_info.contract_type);
8991
const is_accu_contract_closed = is_accumulator && !isOpen(contract_info);
9092
const available_ticks = (is_accumulator && contract_info.audit_details?.all_ticks) || contract_info.tick_stream;
9193
const tick_stream = unique(available_ticks, 'epoch').map(addLabelAlignment);
@@ -108,6 +110,7 @@ export const createTickMarkers = (contract_info, is_delayed_markers_update) => {
108110
// accumulators entry spot will be missing from tick_stream when contract is lasting for longer than 10 ticks
109111
const entry_spot_index = is_accumulator ? tick_stream.findIndex(isEntrySpot) : 0;
110112
const is_middle_spot = idx > entry_spot_index && +tick.epoch !== +contract_info.exit_tick_time;
113+
const is_selected_tick = is_ticks_contract && idx + 1 === contract_info.selected_tick;
111114
const isExitSpot = (_tick, _idx) =>
112115
+_tick.epoch === +contract_info.exit_tick_time ||
113116
getSpotCount(contract_info, _idx) === contract_info.tick_count;
@@ -127,13 +130,24 @@ export const createTickMarkers = (contract_info, is_delayed_markers_update) => {
127130
tick.align_label = 'top'; // force exit spot label to be 'top' to avoid overlapping
128131
marker_config = createMarkerSpotExit(contract_info, tick, idx);
129132
}
130-
if (is_smarttrader_contract && is_middle_spot) {
131-
const spot_className = marker_config.content_config.spot_className;
132-
marker_config.content_config.spot_className = `${spot_className} ${spot_className}--smarttrader-contract-middle`;
133+
134+
if (is_selected_tick && is_smarttrader_contract) {
135+
marker_config = createMarkerSpotMiddle(contract_info, tick, idx);
136+
}
137+
138+
const spot_className = marker_config && marker_config.content_config.spot_className;
139+
140+
if (is_smarttrader_contract && is_middle_spot && !is_selected_tick) {
141+
marker_config &&
142+
(marker_config.content_config.spot_className = `${spot_className} ${spot_className}--smarttrader-contract-middle`);
143+
133144
if (!is_current_last_spot) {
134145
marker_config.content_config.is_value_hidden = true;
135146
}
136147
}
148+
if (is_selected_tick) {
149+
marker_config.content_config.spot_className = `${spot_className} chart-spot__spot--${contract_info.status}`;
150+
}
137151
if (is_accumulator) {
138152
if ((is_accu_current_last_spot || is_exit_spot) && !is_accu_contract_closed) return;
139153
if (marker_config && (is_middle_spot || is_exit_spot)) {

packages/reports/src/sass/app/modules/smart-chart.scss

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,15 @@
3434
}
3535
}
3636
&__entry {
37+
align-items: center;
38+
background-color: var(--general-main-2);
39+
border: 5px solid var(--brand-red-coral);
40+
display: flex;
41+
justify-content: center;
3742
left: -12px;
38-
top: -12px;
3943
position: relative;
40-
border: 6px solid var(--brand-red-coral);
41-
background-color: var(--general-main-2);
44+
top: -12px;
45+
color: var(--text-general);
4246

4347
@include mobile {
4448
border-width: 3px;

packages/shared/src/utils/constants/__tests__/contract.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ describe('getUnsupportedContracts', () => {
5656
it('should return an object with unsupported contracts, e.g. such as Spread Up', () => {
5757
expect(getUnsupportedContracts().CALLSPREAD).toEqual(unsupported_contract);
5858
});
59+
it('should not return TICKHIGH as a part of unsupported contracts', () => {
60+
expect(Object.keys(getUnsupportedContracts())).not.toContain('TICKHIGH');
61+
});
5962
});
6063

6164
describe('getSupportedContracts', () => {
@@ -66,6 +69,10 @@ describe('getSupportedContracts', () => {
6669
it('should return an object with specific supported contracts if is_high_low === false', () => {
6770
expect(getSupportedContracts(false).CALL).toEqual(supported_non_high_low_contract);
6871
});
72+
73+
it('should return TICKHIGH as a part of supported contracts', () => {
74+
expect(Object.keys(getSupportedContracts(false))).toContain('TICKHIGH');
75+
});
6976
});
7077

7178
describe('getContractConfig', () => {

packages/shared/src/utils/constants/contract.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -400,14 +400,6 @@ export const getMarketNamesMap = () =>
400400

401401
export const getUnsupportedContracts = () =>
402402
({
403-
TICKHIGH: {
404-
name: localize('High Tick'),
405-
position: 'top',
406-
},
407-
TICKLOW: {
408-
name: localize('Low Tick'),
409-
position: 'bottom',
410-
},
411403
LBFLOATCALL: {
412404
name: localize('Close-to-Low'),
413405
position: 'top',
@@ -549,6 +541,14 @@ export const getSupportedContracts = (is_high_low?: boolean) =>
549541
name: localize('Asian Down'),
550542
position: 'bottom',
551543
},
544+
[CONTRACT_TYPES.TICK_HIGH_LOW.HIGH]: {
545+
name: localize('High Tick'),
546+
position: 'top',
547+
},
548+
[CONTRACT_TYPES.TICK_HIGH_LOW.LOW]: {
549+
name: localize('Low Tick'),
550+
position: 'bottom',
551+
},
552552
RESETCALL: {
553553
name: localize('Reset Call'),
554554
position: 'top',

packages/shared/src/utils/contract/__tests__/contract.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,15 @@ describe('isAccumulatorContract', () => {
308308
});
309309
});
310310

311+
describe('isTicksContract', () => {
312+
it('should return true if contract_type includes CONTRACT_TYPES.TICK_HIGH_LOW.HIGH', () => {
313+
expect(ContractUtils.isTicksContract(CONTRACT_TYPES.TICK_HIGH_LOW.HIGH)).toEqual(true);
314+
});
315+
it('should return false if contract_type is not CONTRACT_TYPES.TICK', () => {
316+
expect(ContractUtils.isTicksContract(CONTRACT_TYPES.ACCUMULATOR)).toEqual(false);
317+
});
318+
});
319+
311320
describe('isAccumulatorContractOpen', () => {
312321
it('should return true if contract_type includes CONTRACT_TYPES.ACCUMULATOR, status is open, and current spot has NOT crossed barriers', () => {
313322
const contract_info = mockContractInfo({

packages/shared/src/utils/contract/contract.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,12 @@ export const isVanillaFxContract = (contract_type = '', symbol = '') =>
159159
isVanillaContract(contract_type) && VANILLA_FX_SYMBOLS.includes(symbol as typeof VANILLA_FX_SYMBOLS[number]);
160160

161161
export const isSmartTraderContract = (contract_type = '') =>
162-
/RUN|EXPIRY|RANGE|UPORDOWN|ASIAN|RESET/i.test(contract_type);
162+
/RUN|EXPIRY|RANGE|UPORDOWN|ASIAN|RESET|TICK/i.test(contract_type);
163163

164164
export const isAsiansContract = (contract_type = '') => /ASIAN/i.test(contract_type);
165165

166+
export const isTicksContract = (contract_type = '') => /TICK/i.test(contract_type);
167+
166168
export const isResetContract = (contract_type = '') => /RESET/i.test(contract_type);
167169

168170
export const isCryptoContract = (underlying = '') => underlying.startsWith('cry');

packages/trader/src/App/Components/Elements/ContractAudit/__tests__/contract-details.spec.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ import { mockContractInfo, TRADE_TYPES } from '@deriv/shared';
55
import ContractDetails from '../contract-details';
66

77
const contract_types = {
8-
test_contract_type: 'test_contract_type',
9-
multiplier: TRADE_TYPES.MULTIPLIER,
10-
vanilla: TRADE_TYPES.VANILLA.CALL,
118
digit: 'digit',
129
expiry: 'expiry',
10+
multiplier: TRADE_TYPES.MULTIPLIER,
11+
test_contract_type: 'test_contract_type',
12+
tick_high_low: TRADE_TYPES.TICK_HIGH_LOW,
13+
vanilla: TRADE_TYPES.VANILLA.CALL,
1314
reset: 'reset',
1415
};
1516
const contract_info = mockContractInfo({
@@ -92,6 +93,14 @@ describe('<ContractDetails />', () => {
9293
expect(screen.getByText('Strike')).toBeInTheDocument();
9394
});
9495

96+
it('should render selected tick if it is tick_high_low contract type', () => {
97+
const new_props = { ...mock_default_props };
98+
new_props.contract_info.contract_type = contract_types.tick_high_low;
99+
render(<ContractDetails {...new_props} />);
100+
101+
expect(screen.getByText('Selected tick')).toBeInTheDocument();
102+
});
103+
95104
it('should render barrier information in ContractAuditItem if it is digit contract type', () => {
96105
mock_default_props.contract_info.contract_type = contract_types.digit;
97106
render(<ContractDetails {...mock_default_props} />);

packages/trader/src/App/Components/Elements/ContractAudit/contract-details.tsx

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React from 'react';
2+
import classNames from 'classnames';
23
import { Money, Icon, ThemedScrollbars } from '@deriv/components';
34
import { localize, Localize } from '@deriv/translations';
45
import {
@@ -10,18 +11,19 @@ import {
1011
formatResetDuration,
1112
hasTwoBarriers,
1213
isAccumulatorContract,
14+
isAsiansContract,
1315
isEndedBeforeCancellationExpired,
1416
isMobile,
1517
isMultiplierContract,
1618
isSmartTraderContract,
17-
isAsiansContract,
19+
isTicksContract,
1820
isResetContract,
1921
isTurbosContract,
2022
isUserCancelled,
2123
isUserSold,
2224
isVanillaFxContract,
23-
toGMTFormat,
2425
TContractInfo,
26+
toGMTFormat,
2527
} from '@deriv/shared';
2628
import { Analytics } from '@deriv-com/analytics';
2729
import { getBarrierLabel, getBarrierValue, isDigitType } from 'App/Components/Elements/PositionsDrawer/helpers';
@@ -46,6 +48,7 @@ const ContractDetails = ({
4648
is_vanilla,
4749
}: TContractDetails) => {
4850
const {
51+
barrier,
4952
commission,
5053
contract_type,
5154
currency,
@@ -57,6 +60,8 @@ const ContractDetails = ({
5760
high_barrier,
5861
low_barrier,
5962
profit,
63+
selected_tick,
64+
status,
6065
tick_count,
6166
tick_passed,
6267
transaction_ids: { buy, sell } = {},
@@ -223,6 +228,25 @@ const ContractDetails = ({
223228
)}
224229
</React.Fragment>
225230
)}
231+
{isTicksContract(contract_type) && (
232+
<ContractAuditItem
233+
id='dt_entry_spot_label'
234+
icon={
235+
<div className='contract-audit__selected-tick'>
236+
<div
237+
className={classNames(
238+
'contract-audit__selected-tick--marker',
239+
`contract-audit__selected-tick--marker--${status}`
240+
)}
241+
>
242+
{selected_tick}
243+
</div>
244+
</div>
245+
}
246+
label={localize('Selected tick')}
247+
value={barrier || '----'}
248+
/>
249+
)}
226250
<ContractAuditItem
227251
id='dt_start_time_label'
228252
icon={<Icon icon='IcContractStartTime' size={24} />}
@@ -236,6 +260,10 @@ const ContractDetails = ({
236260
label={localize('Entry spot')}
237261
value={entry_spot_display_value ? addComma(entry_spot_display_value) : ' - '}
238262
value2={toGMTFormat(epochToMoment(Number(entry_tick_time))) || ' - '}
263+
additional_info={
264+
isTicksContract(contract_type) &&
265+
localize('The entry spot is the first tick for High/Low Ticks.')
266+
}
239267
/>
240268
)}
241269
{!isNaN(Number(exit_spot)) && (

0 commit comments

Comments
 (0)