Skip to content

Commit

Permalink
Akmal / feat: add ticks high low contract from smarttrader (#12873)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
akmal-deriv authored Jan 29, 2024
1 parent c1dbce7 commit a90e152
Show file tree
Hide file tree
Showing 12 changed files with 165 additions and 38 deletions.
7 changes: 7 additions & 0 deletions packages/core/src/Stores/Constants/markers.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ export const MARKER_TYPES_CONFIG = {
},
content_config: { spot_className: 'chart-spot__spot' },
},
SPOT_EXIT_2: {
type: 'SPOT_EXIT_2',
marker_config: {
ContentComponent: MarkerSpot,
},
content_config: { className: 'chart-spot__spot' },
},
SPOT_MIDDLE: {
type: 'SPOT_MIDDLE',
marker_config: {
Expand Down
32 changes: 21 additions & 11 deletions packages/core/src/Stores/Helpers/chart-marker-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import {
formatMoney,
getEndTime,
isAccumulatorContract,
isSmartTraderContract,
isDesktop,
isDigitContract,
isMobile,
isMultiplierContract,
isSmartTraderContract,
isTicksContract,
isUserSold,
isVanillaContract,
} from '@deriv/shared';
Expand All @@ -24,7 +25,11 @@ const createMarkerConfig = (marker_type, x, y, content_config) =>
});

export const getSpotCount = (contract_info, spot_count) => {
if (isDigitContract(contract_info.contract_type)) return spot_count + 1;
if (
isDigitContract(contract_info.contract_type) ||
(isTicksContract(contract_info.contract_type) && spot_count + 1 === contract_info.selected_tick)
)
return spot_count + 1;
if (isAccumulatorContract(contract_info.contract_type) || isSmartTraderContract(contract_info.contract_type))
return null;
return spot_count;
Expand Down Expand Up @@ -72,14 +77,12 @@ export const createMarkerResetTime = contract_info => {
export const createMarkerSpotEntry = contract_info => {
if (!contract_info.entry_tick_time) return false;

let marker_type = MARKER_TYPES_CONFIG.SPOT_ENTRY.type;
let component_props = {};
const entry_tick = contract_info.entry_tick_display_value;

const spot_has_label = isDigitContract(contract_info.contract_type);
const spot_has_label = isDigitContract(contract_info.contract_type) || isTicksContract(contract_info.contract_type);
const marker_type = MARKER_TYPES_CONFIG.SPOT_ENTRY.type;
let component_props = {};

if (spot_has_label) {
marker_type = MARKER_TYPES_CONFIG.SPOT_MIDDLE.type;
component_props = {
spot_value: `${entry_tick}`,
spot_epoch: `${contract_info.entry_tick_time}`,
Expand All @@ -93,6 +96,7 @@ export const createMarkerSpotEntry = contract_info => {
export const createMarkerSpotExit = (contract_info, tick, idx) => {
if (!contract_info.exit_tick_time) return false;
const is_user_sold = isUserSold(contract_info);
const is_ticks_contract = isTicksContract(contract_info.contract_type);

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

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

const should_show_spot_exit_2 = is_ticks_contract && idx + 1 !== contract_info.selected_tick;

const should_show_profit_label = isVanillaContract(contract_info.contract_type) && isDesktop();

const marker_spot_type = should_show_spot_exit
? MARKER_TYPES_CONFIG.SPOT_EXIT.type
? should_show_spot_exit_2
? MARKER_TYPES_CONFIG.SPOT_EXIT_2.type
: MARKER_TYPES_CONFIG.SPOT_EXIT.type
: MARKER_TYPES_CONFIG.SPOT_SELL.type;

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

export const createMarkerSpotMiddle = (contract_info, tick, idx) => {
const is_accumulator = isAccumulatorContract(contract_info.contract_type);
const is_ticks_contract = isTicksContract(contract_info.contract_type);
const spot_count = getSpotCount(contract_info, idx);
const spot = tick.tick_display_value;
const spot_epoch = is_accumulator ? '' : `${tick.epoch}`;
Expand All @@ -137,11 +146,12 @@ export const createMarkerSpotMiddle = (contract_info, tick, idx) => {
spot_value: `${spot}`,
spot_epoch,
align_label: tick.align_label,
is_value_hidden: is_accumulator,
is_value_hidden: is_accumulator || (is_ticks_contract && idx + 1 !== contract_info.selected_tick),
spot_count,
status: `${is_ticks_contract ? contract_info.status : ''}`,
});
marker_config.type = `${marker_config.type}_${idx}`;

if (isMobile() && spot_count > 1) return null;
if (isMobile() && spot_count > 1 && !is_ticks_contract) return null;
return marker_config;
};
28 changes: 21 additions & 7 deletions packages/core/src/Stores/Helpers/chart-markers.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ import {
getSpotCount,
} from './chart-marker-helpers';
import {
getContractStatus,
getDecimalPlaces,
getEndTime,
isAccumulatorContract,
isAccumulatorContractOpen,
isDigitContract,
isHighLow,
isSmartTraderContract,
isMultiplierContract,
isOpen,
isSmartTraderContract,
isTicksContract,
isTouchContract,
isMultiplierContract,
isTurbosContract,
isVanillaContract,
getContractStatus,
unique,
isTurbosContract,
} from '@deriv/shared';
import { localize } from '@deriv/translations';
import { MARKER_TYPES_CONFIG } from '../Constants/markers';
Expand Down Expand Up @@ -86,6 +87,7 @@ const addLabelAlignment = (tick, idx, arr) => {
export const createTickMarkers = (contract_info, is_delayed_markers_update) => {
const is_accumulator = isAccumulatorContract(contract_info.contract_type);
const is_smarttrader_contract = isSmartTraderContract(contract_info.contract_type);
const is_ticks_contract = isTicksContract(contract_info.contract_type);
const is_accu_contract_closed = is_accumulator && !isOpen(contract_info);
const available_ticks = (is_accumulator && contract_info.audit_details?.all_ticks) || contract_info.tick_stream;
const tick_stream = unique(available_ticks, 'epoch').map(addLabelAlignment);
Expand All @@ -108,6 +110,7 @@ export const createTickMarkers = (contract_info, is_delayed_markers_update) => {
// accumulators entry spot will be missing from tick_stream when contract is lasting for longer than 10 ticks
const entry_spot_index = is_accumulator ? tick_stream.findIndex(isEntrySpot) : 0;
const is_middle_spot = idx > entry_spot_index && +tick.epoch !== +contract_info.exit_tick_time;
const is_selected_tick = is_ticks_contract && idx + 1 === contract_info.selected_tick;
const isExitSpot = (_tick, _idx) =>
+_tick.epoch === +contract_info.exit_tick_time ||
getSpotCount(contract_info, _idx) === contract_info.tick_count;
Expand All @@ -127,13 +130,24 @@ export const createTickMarkers = (contract_info, is_delayed_markers_update) => {
tick.align_label = 'top'; // force exit spot label to be 'top' to avoid overlapping
marker_config = createMarkerSpotExit(contract_info, tick, idx);
}
if (is_smarttrader_contract && is_middle_spot) {
const spot_className = marker_config.content_config.spot_className;
marker_config.content_config.spot_className = `${spot_className} ${spot_className}--smarttrader-contract-middle`;

if (is_selected_tick && is_smarttrader_contract) {
marker_config = createMarkerSpotMiddle(contract_info, tick, idx);
}

const spot_className = marker_config && marker_config.content_config.spot_className;

if (is_smarttrader_contract && is_middle_spot && !is_selected_tick) {
marker_config &&
(marker_config.content_config.spot_className = `${spot_className} ${spot_className}--smarttrader-contract-middle`);

if (!is_current_last_spot) {
marker_config.content_config.is_value_hidden = true;
}
}
if (is_selected_tick) {
marker_config.content_config.spot_className = `${spot_className} chart-spot__spot--${contract_info.status}`;
}
if (is_accumulator) {
if ((is_accu_current_last_spot || is_exit_spot) && !is_accu_contract_closed) return;
if (marker_config && (is_middle_spot || is_exit_spot)) {
Expand Down
10 changes: 7 additions & 3 deletions packages/reports/src/sass/app/modules/smart-chart.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@
}
}
&__entry {
align-items: center;
background-color: var(--general-main-2);
border: 5px solid var(--brand-red-coral);
display: flex;
justify-content: center;
left: -12px;
top: -12px;
position: relative;
border: 6px solid var(--brand-red-coral);
background-color: var(--general-main-2);
top: -12px;
color: var(--text-general);

@include mobile {
border-width: 3px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ describe('getUnsupportedContracts', () => {
it('should return an object with unsupported contracts, e.g. such as Spread Up', () => {
expect(getUnsupportedContracts().CALLSPREAD).toEqual(unsupported_contract);
});
it('should not return TICKHIGH as a part of unsupported contracts', () => {
expect(Object.keys(getUnsupportedContracts())).not.toContain('TICKHIGH');
});
});

describe('getSupportedContracts', () => {
Expand All @@ -66,6 +69,10 @@ describe('getSupportedContracts', () => {
it('should return an object with specific supported contracts if is_high_low === false', () => {
expect(getSupportedContracts(false).CALL).toEqual(supported_non_high_low_contract);
});

it('should return TICKHIGH as a part of supported contracts', () => {
expect(Object.keys(getSupportedContracts(false))).toContain('TICKHIGH');
});
});

describe('getContractConfig', () => {
Expand Down
16 changes: 8 additions & 8 deletions packages/shared/src/utils/constants/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,14 +400,6 @@ export const getMarketNamesMap = () =>

export const getUnsupportedContracts = () =>
({
TICKHIGH: {
name: localize('High Tick'),
position: 'top',
},
TICKLOW: {
name: localize('Low Tick'),
position: 'bottom',
},
LBFLOATCALL: {
name: localize('Close-to-Low'),
position: 'top',
Expand Down Expand Up @@ -549,6 +541,14 @@ export const getSupportedContracts = (is_high_low?: boolean) =>
name: localize('Asian Down'),
position: 'bottom',
},
[CONTRACT_TYPES.TICK_HIGH_LOW.HIGH]: {
name: localize('High Tick'),
position: 'top',
},
[CONTRACT_TYPES.TICK_HIGH_LOW.LOW]: {
name: localize('Low Tick'),
position: 'bottom',
},
RESETCALL: {
name: localize('Reset Call'),
position: 'top',
Expand Down
9 changes: 9 additions & 0 deletions packages/shared/src/utils/contract/__tests__/contract.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,15 @@ describe('isAccumulatorContract', () => {
});
});

describe('isTicksContract', () => {
it('should return true if contract_type includes CONTRACT_TYPES.TICK_HIGH_LOW.HIGH', () => {
expect(ContractUtils.isTicksContract(CONTRACT_TYPES.TICK_HIGH_LOW.HIGH)).toEqual(true);
});
it('should return false if contract_type is not CONTRACT_TYPES.TICK', () => {
expect(ContractUtils.isTicksContract(CONTRACT_TYPES.ACCUMULATOR)).toEqual(false);
});
});

describe('isAccumulatorContractOpen', () => {
it('should return true if contract_type includes CONTRACT_TYPES.ACCUMULATOR, status is open, and current spot has NOT crossed barriers', () => {
const contract_info = mockContractInfo({
Expand Down
4 changes: 3 additions & 1 deletion packages/shared/src/utils/contract/contract.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,12 @@ export const isVanillaFxContract = (contract_type = '', symbol = '') =>
isVanillaContract(contract_type) && VANILLA_FX_SYMBOLS.includes(symbol as typeof VANILLA_FX_SYMBOLS[number]);

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

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

export const isTicksContract = (contract_type = '') => /TICK/i.test(contract_type);

export const isResetContract = (contract_type = '') => /RESET/i.test(contract_type);

export const isCryptoContract = (underlying = '') => underlying.startsWith('cry');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import { mockContractInfo, TRADE_TYPES } from '@deriv/shared';
import ContractDetails from '../contract-details';

const contract_types = {
test_contract_type: 'test_contract_type',
multiplier: TRADE_TYPES.MULTIPLIER,
vanilla: TRADE_TYPES.VANILLA.CALL,
digit: 'digit',
expiry: 'expiry',
multiplier: TRADE_TYPES.MULTIPLIER,
test_contract_type: 'test_contract_type',
tick_high_low: TRADE_TYPES.TICK_HIGH_LOW,
vanilla: TRADE_TYPES.VANILLA.CALL,
reset: 'reset',
};
const contract_info = mockContractInfo({
Expand Down Expand Up @@ -92,6 +93,14 @@ describe('<ContractDetails />', () => {
expect(screen.getByText('Strike')).toBeInTheDocument();
});

it('should render selected tick if it is tick_high_low contract type', () => {
const new_props = { ...mock_default_props };
new_props.contract_info.contract_type = contract_types.tick_high_low;
render(<ContractDetails {...new_props} />);

expect(screen.getByText('Selected tick')).toBeInTheDocument();
});

it('should render barrier information in ContractAuditItem if it is digit contract type', () => {
mock_default_props.contract_info.contract_type = contract_types.digit;
render(<ContractDetails {...mock_default_props} />);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import classNames from 'classnames';
import { Money, Icon, ThemedScrollbars } from '@deriv/components';
import { localize, Localize } from '@deriv/translations';
import {
Expand All @@ -10,18 +11,19 @@ import {
formatResetDuration,
hasTwoBarriers,
isAccumulatorContract,
isAsiansContract,
isEndedBeforeCancellationExpired,
isMobile,
isMultiplierContract,
isSmartTraderContract,
isAsiansContract,
isTicksContract,
isResetContract,
isTurbosContract,
isUserCancelled,
isUserSold,
isVanillaFxContract,
toGMTFormat,
TContractInfo,
toGMTFormat,
} from '@deriv/shared';
import { Analytics } from '@deriv-com/analytics';
import { getBarrierLabel, getBarrierValue, isDigitType } from 'App/Components/Elements/PositionsDrawer/helpers';
Expand All @@ -46,6 +48,7 @@ const ContractDetails = ({
is_vanilla,
}: TContractDetails) => {
const {
barrier,
commission,
contract_type,
currency,
Expand All @@ -57,6 +60,8 @@ const ContractDetails = ({
high_barrier,
low_barrier,
profit,
selected_tick,
status,
tick_count,
tick_passed,
transaction_ids: { buy, sell } = {},
Expand Down Expand Up @@ -223,6 +228,25 @@ const ContractDetails = ({
)}
</React.Fragment>
)}
{isTicksContract(contract_type) && (
<ContractAuditItem
id='dt_entry_spot_label'
icon={
<div className='contract-audit__selected-tick'>
<div
className={classNames(
'contract-audit__selected-tick--marker',
`contract-audit__selected-tick--marker--${status}`
)}
>
{selected_tick}
</div>
</div>
}
label={localize('Selected tick')}
value={barrier || '----'}
/>
)}
<ContractAuditItem
id='dt_start_time_label'
icon={<Icon icon='IcContractStartTime' size={24} />}
Expand All @@ -236,6 +260,10 @@ const ContractDetails = ({
label={localize('Entry spot')}
value={entry_spot_display_value ? addComma(entry_spot_display_value) : ' - '}
value2={toGMTFormat(epochToMoment(Number(entry_tick_time))) || ' - '}
additional_info={
isTicksContract(contract_type) &&
localize('The entry spot is the first tick for High/Low Ticks.')
}
/>
)}
{!isNaN(Number(exit_spot)) && (
Expand Down
Loading

0 comments on commit a90e152

Please sign in to comment.