{
const isVisible = component_key => {
return form_components.includes(component_key);
};
+
return (
{isVisible('duration') && }
{isVisible('barrier') && }
{isVisible('last_digit') && }
{isVisible('accumulator') && }
-
+ {(isVisible('trade_type_tabs') || isVisible('strike') || isVisible('barrier_selector')) && (
+
+ )}
+ {isVisible('amount') && }
{isVisible('take_profit') && }
{isVisible('stop_loss') && }
{isVisible('cancellation') && }
diff --git a/packages/trader/src/Modules/Trading/Containers/trade.jsx b/packages/trader/src/Modules/Trading/Containers/trade.jsx
index 5a18ce1abc37..a65dcbddf672 100644
--- a/packages/trader/src/Modules/Trading/Containers/trade.jsx
+++ b/packages/trader/src/Modules/Trading/Containers/trade.jsx
@@ -49,6 +49,7 @@ const Trade = observer(() => {
symbol,
is_synthetics_available,
is_synthetics_trading_market_available,
+ is_turbos,
is_vanilla,
} = useTraderStore();
const {
@@ -149,12 +150,17 @@ const Trade = observer(() => {
);
const form_wrapper_class = isMobile() ? 'mobile-wrapper' : 'sidebar__container desktop-only';
+ const chart_height_offset = React.useMemo(() => {
+ if (is_accumulator) return '295px';
+ if (is_turbos) return '300px';
+ return '259px';
+ }, [is_turbos, is_accumulator]);
return (
@@ -167,7 +173,7 @@ const Trade = observer(() => {
id='chart_container'
className='chart-container'
is_disabled={isDesktop()}
- height_offset={is_accumulator ? '295px' : '259px'}
+ height_offset={chart_height_offset}
>
Accumulators: 'IcCatAccumulator',
Options: 'IcCatOptions',
Multipliers: 'IcCatMultiplier',
+ Turbos: 'IcCatTurbos',
} as const);
/**
diff --git a/packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.js b/packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.js
deleted file mode 100644
index 26bef1e88d04..000000000000
--- a/packages/trader/src/Stores/Modules/SmartChart/Constants/barriers.js
+++ /dev/null
@@ -1,37 +0,0 @@
-export const CONTRACT_SHADES = {
- CALL: 'ABOVE',
- PUT: 'BELOW',
- CALLE: 'ABOVE',
- PUTE: 'BELOW',
- EXPIRYRANGE: 'BETWEEN',
- EXPIRYMISS: 'OUTSIDE',
- RANGE: 'BETWEEN',
- UPORDOWN: 'OUTSIDE',
- ONETOUCH: 'NONE_SINGLE', // no shade
- NOTOUCH: 'NONE_SINGLE', // no shade
- ASIANU: 'ABOVE',
- ASIAND: 'BELOW',
- MULTUP: 'ABOVE',
- MULTDOWN: 'BELOW',
- ACCU: 'NONE_DOUBLE',
-};
-
-// Default non-shade according to number of barriers
-export const DEFAULT_SHADES = {
- 1: 'NONE_SINGLE',
- 2: 'NONE_DOUBLE',
-};
-
-export const BARRIER_COLORS = {
- GREEN: '#4bb4b3',
- RED: '#ec3f3f',
- ORANGE: '#ff6444',
- GRAY: '#999999',
- DARK_GRAY: '#6E6E6E',
-};
-
-export const BARRIER_LINE_STYLES = {
- DASHED: 'dashed',
- DOTTED: 'dotted',
- SOLID: 'solid',
-};
diff --git a/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.js b/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.js
index f26a39e3146f..ca001aee0023 100644
--- a/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.js
+++ b/packages/trader/src/Stores/Modules/SmartChart/Helpers/barriers.js
@@ -1,6 +1,5 @@
import { toJS } from 'mobx';
-import { isEmptyObject } from '@deriv/shared';
-import { CONTRACT_SHADES } from '../Constants/barriers';
+import { isEmptyObject, CONTRACT_SHADES } from '@deriv/shared';
export const isBarrierSupported = contract_type => contract_type in CONTRACT_SHADES;
diff --git a/packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.js b/packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.js
index 12c8b6f950fd..718086c6ee9c 100644
--- a/packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.js
+++ b/packages/trader/src/Stores/Modules/SmartChart/chart-barrier-store.js
@@ -1,5 +1,5 @@
import { action, computed, observable, makeObservable } from 'mobx';
-import { BARRIER_COLORS, BARRIER_LINE_STYLES, CONTRACT_SHADES, DEFAULT_SHADES } from './Constants/barriers';
+import { BARRIER_COLORS, BARRIER_LINE_STYLES, CONTRACT_SHADES, DEFAULT_SHADES } from '@deriv/shared';
import { barriersToString } from './Helpers/barriers';
export class ChartBarrierStore {
diff --git a/packages/trader/src/Stores/Modules/Trading/Helpers/__tests__/barrier-utils.spec.ts b/packages/trader/src/Stores/Modules/Trading/Helpers/__tests__/barrier-utils.spec.ts
new file mode 100644
index 000000000000..0c08a6fec99f
--- /dev/null
+++ b/packages/trader/src/Stores/Modules/Trading/Helpers/__tests__/barrier-utils.spec.ts
@@ -0,0 +1,13 @@
+import { getHoveredColor } from '../barrier-utils';
+
+describe('getHoveredColor', () => {
+ it('should return red color (#ec3f3f) if passed value is TURBOSSHORT', () => {
+ expect(getHoveredColor('TURBOSSHORT')).toEqual('#ec3f3f');
+ });
+ it('should return green color (#4bb4b3) if passed value is TURBOSLONG', () => {
+ expect(getHoveredColor('TURBOSLONG')).toEqual('#4bb4b3');
+ });
+ it('should return gray color (#999999) if passed value not TURBOSLONG or TURBOSSHORT', () => {
+ expect(getHoveredColor('TESTTYPE')).toEqual('#999999');
+ });
+});
diff --git a/packages/trader/src/Stores/Modules/Trading/Helpers/barrier-utils.ts b/packages/trader/src/Stores/Modules/Trading/Helpers/barrier-utils.ts
new file mode 100644
index 000000000000..8d3df7d38019
--- /dev/null
+++ b/packages/trader/src/Stores/Modules/Trading/Helpers/barrier-utils.ts
@@ -0,0 +1,12 @@
+import { BARRIER_COLORS } from '@deriv/shared';
+
+export const getHoveredColor = (type: string): string => {
+ switch (type) {
+ case 'TURBOSSHORT':
+ return BARRIER_COLORS.RED;
+ case 'TURBOSLONG':
+ return BARRIER_COLORS.GREEN;
+ default:
+ return BARRIER_COLORS.GRAY;
+ }
+};
diff --git a/packages/trader/src/Stores/Modules/Trading/Helpers/contract-type.js b/packages/trader/src/Stores/Modules/Trading/Helpers/contract-type.js
index 81638cd7e01c..99b04c99f481 100644
--- a/packages/trader/src/Stores/Modules/Trading/Helpers/contract-type.js
+++ b/packages/trader/src/Stores/Modules/Trading/Helpers/contract-type.js
@@ -14,6 +14,7 @@ import {
unsupported_contract_types_list,
getContractCategoriesConfig,
getContractTypesConfig,
+ getContractSubtype,
getLocalizedBasis,
} from '@deriv/shared';
import ServerTime from '_common/base/server_time';
@@ -71,6 +72,7 @@ export const ContractType = (() => {
config.durations = !config.hide_duration && buildDurationConfig(contract, config.durations);
config.trade_types = buildTradeTypesConfig(contract, config.trade_types);
config.barriers = buildBarriersConfig(contract, config.barriers);
+ config.barrier_choices = contract.barrier_choices;
config.forward_starting_dates = buildForwardStartingConfig(contract, config.forward_starting_dates);
config.growth_rate_range = contract.growth_rate_range;
config.multiplier_range = contract.multiplier_range;
@@ -109,22 +111,31 @@ export const ContractType = (() => {
start_date,
cancellation_duration,
symbol,
+ short_barriers,
+ long_barriers,
} = store;
if (!contract_type) return {};
+ let stored_barriers_data = {};
+ if (getContractSubtype(contract_type) === 'Short') {
+ stored_barriers_data = short_barriers;
+ } else if (getContractSubtype(contract_type) === 'Long') {
+ stored_barriers_data = long_barriers;
+ }
+
const form_components = getComponents(contract_type);
const obj_basis = getBasis(contract_type, basis);
const obj_trade_types = getTradeTypes(contract_type);
const obj_start_dates = getStartDates(contract_type, start_date);
const obj_start_type = getStartType(obj_start_dates.start_date);
- const obj_barrier = getBarriers(contract_type, contract_expiry_type);
+ const obj_barrier = getBarriers(contract_type, contract_expiry_type, stored_barriers_data.barrier);
const obj_duration_unit = getDurationUnit(duration_unit, contract_type, obj_start_type.contract_start_type);
const obj_duration_units_list = getDurationUnitsList(contract_type, obj_start_type.contract_start_type);
const obj_duration_units_min_max = getDurationMinMax(contract_type, obj_start_type.contract_start_type);
-
const obj_accumulator_range_list = getAccumulatorRange(contract_type);
+ const obj_barrier_choices = getBarrierChoices(contract_type, stored_barriers_data.barrier_choices);
const obj_multiplier_range_list = getMultiplierRange(contract_type, multiplier);
const obj_cancellation = getCancellation(contract_type, cancellation_duration, symbol);
const obj_expiry_type = getExpiryType(obj_duration_units_list, expiry_type);
@@ -142,6 +153,7 @@ export const ContractType = (() => {
...obj_duration_units_min_max,
...obj_expiry_type,
...obj_accumulator_range_list,
+ ...obj_barrier_choices,
...obj_multiplier_range_list,
...obj_cancellation,
...obj_equal,
@@ -474,14 +486,14 @@ export const ContractType = (() => {
trade_types: getPropertyValue(available_contract_types, [contract_type, 'config', 'trade_types']),
});
- const getBarriers = (contract_type, expiry_type) => {
+ const getBarriers = (contract_type, expiry_type, stored_barrier_value) => {
const barriers = getPropertyValue(available_contract_types, [contract_type, 'config', 'barriers']) || {};
const barrier_values = barriers[expiry_type] || {};
const barrier_1 = barrier_values.barrier || barrier_values.high_barrier || '';
const barrier_2 = barrier_values.low_barrier || '';
return {
barrier_count: barriers.count || 0,
- barrier_1: barrier_1.toString(),
+ barrier_1: stored_barrier_value || barrier_1.toString(),
barrier_2: barrier_2.toString(),
};
};
@@ -502,6 +514,12 @@ export const ContractType = (() => {
getPropertyValue(available_contract_types, [contract_type, 'config', 'growth_rate_range']) || [],
});
+ const getBarrierChoices = (contract_type, stored_barrier_choices = []) => ({
+ barrier_choices: stored_barrier_choices.length
+ ? stored_barrier_choices
+ : getPropertyValue(available_contract_types, [contract_type, 'config', 'barrier_choices']) || [],
+ });
+
const getMultiplierRange = (contract_type, multiplier) => {
const arr_multiplier =
getPropertyValue(available_contract_types, [contract_type, 'config', 'multiplier_range']) || [];
diff --git a/packages/trader/src/Stores/Modules/Trading/Helpers/limit-orders.js b/packages/trader/src/Stores/Modules/Trading/Helpers/limit-orders.js
index 069a7607b69e..201f45f199f0 100644
--- a/packages/trader/src/Stores/Modules/Trading/Helpers/limit-orders.js
+++ b/packages/trader/src/Stores/Modules/Trading/Helpers/limit-orders.js
@@ -1,5 +1,4 @@
-import { isMultiplierContract } from '@deriv/shared';
-import { BARRIER_COLORS, BARRIER_LINE_STYLES } from '../../SmartChart/Constants/barriers';
+import { isMultiplierContract, BARRIER_COLORS, BARRIER_LINE_STYLES } from '@deriv/shared';
import { ChartBarrierStore } from '../../SmartChart/chart-barrier-store';
import { removeBarrier } from '../../SmartChart/Helpers/barriers';
diff --git a/packages/trader/src/Stores/Modules/Trading/Helpers/proposal.js b/packages/trader/src/Stores/Modules/Trading/Helpers/proposal.js
index 858209efa645..eb22fb34ed43 100644
--- a/packages/trader/src/Stores/Modules/Trading/Helpers/proposal.js
+++ b/packages/trader/src/Stores/Modules/Trading/Helpers/proposal.js
@@ -1,4 +1,12 @@
-import { convertToUnix, getDecimalPlaces, getPropertyValue, isAccumulatorContract, toMoment } from '@deriv/shared';
+import {
+ convertToUnix,
+ getDecimalPlaces,
+ getLocalizedBasis,
+ getPropertyValue,
+ isAccumulatorContract,
+ isTurbosContract,
+ toMoment,
+} from '@deriv/shared';
const isVisible = elem => !(!elem || (elem.offsetWidth === 0 && elem.offsetHeight === 0));
@@ -25,9 +33,10 @@ export const getProposalInfo = (store, response, obj_prev_contract_basis) => {
const stake = proposal.display_value;
const basis_list = store.basis_list;
- const contract_basis = store.is_vanilla
- ? { text: 'Payout', value: 'display_number_of_contracts' }
- : basis_list.find(o => o.value !== store.basis) || {};
+ const contract_basis =
+ store.is_vanilla || store.is_turbos
+ ? { text: getLocalizedBasis().payout_per_point, value: 'display_number_of_contracts' }
+ : basis_list.find(o => o.value !== store.basis) || {};
const is_stake = contract_basis.value === 'stake';
const price = is_stake ? stake : proposal[contract_basis.value];
@@ -111,6 +120,7 @@ const createProposalRequestForContract = (store, type_of_contract) => {
const obj_accumulator = {};
const obj_expiry = {};
const obj_multiplier = {};
+ let limit_order;
if (store.expiry_type === 'endtime') {
const expiry_date = toMoment(store.expiry_date);
@@ -125,6 +135,10 @@ const createProposalRequestForContract = (store, type_of_contract) => {
setProposalAccumulator(store, obj_accumulator);
}
+ if (isTurbosContract(store.contract_type) && store.has_take_profit && store.take_profit) {
+ limit_order = { take_profit: +store.take_profit || 0 };
+ }
+
return {
proposal: 1,
subscribe: 1,
@@ -147,5 +161,6 @@ const createProposalRequestForContract = (store, type_of_contract) => {
...(store.barrier_count === 2 && !isAccumulatorContract(type_of_contract) && { barrier2: store.barrier_2 }),
...obj_accumulator,
...obj_multiplier,
+ limit_order,
};
};
diff --git a/packages/trader/src/Stores/Modules/Trading/trade-store.js b/packages/trader/src/Stores/Modules/Trading/trade-store.js
index ba1ae8af25f4..7ada3c3902f2 100644
--- a/packages/trader/src/Stores/Modules/Trading/trade-store.js
+++ b/packages/trader/src/Stores/Modules/Trading/trade-store.js
@@ -10,12 +10,14 @@ import {
getMinPayout,
getPlatformSettings,
getPropertyValue,
+ getContractSubtype,
isBarrierSupported,
isCryptocurrency,
isDesktop,
isEmptyObject,
isMarketClosed,
isMobile,
+ isTurbosContract,
pickDefaultSymbol,
removeBarrier,
resetEndTimeOnVolatilityIndices,
@@ -25,6 +27,8 @@ import {
formatMoney,
getCurrencyDisplayCode,
unsupported_contract_types_list,
+ BARRIER_COLORS,
+ BARRIER_LINE_STYLES,
} from '@deriv/shared';
import { localize } from '@deriv/translations';
import { getValidationRules, getMultiplierValidationRules } from 'Stores/Modules/Trading/Constants/validation-rules';
@@ -37,7 +41,7 @@ import { getUpdatedTicksHistoryStats } from './Helpers/accumulator';
import { processTradeParams } from './Helpers/process';
import { action, computed, makeObservable, observable, override, reaction, runInAction, toJS, when } from 'mobx';
import { createProposalRequests, getProposalErrorField, getProposalInfo } from './Helpers/proposal';
-import { BARRIER_COLORS } from '../SmartChart/Constants/barriers';
+import { getHoveredColor } from './Helpers/barrier-utils';
import BaseStore from '../../base-store';
import { ChartBarrierStore } from '../SmartChart/chart-barrier-store';
import debounce from 'lodash.debounce';
@@ -74,7 +78,7 @@ export default class TradeStore extends BaseStore {
basis = '';
basis_list = [];
currency = '';
- stake_boundary = { VANILLALONGCALL: {}, VANILLALONGPUT: {} };
+ stake_boundary = {};
// Duration
duration = 5;
@@ -90,9 +94,10 @@ export default class TradeStore extends BaseStore {
barrier_1 = '';
barrier_2 = '';
barrier_count = 0;
- main_barrier = null;
barriers = [];
- strike_price_choices = [];
+ hovered_barrier = '';
+ main_barrier = null;
+ barrier_choices = [];
// Start Time
start_date = Number(0); // Number(0) refers to 'now'
@@ -146,6 +151,10 @@ export default class TradeStore extends BaseStore {
cancellation_duration = '60m';
cancellation_range_list = [];
+ // Turbos trade params
+ long_barriers = {};
+ short_barriers = {};
+
// Vanilla trade params
vanilla_trade_type = 'VANILLALONGCALL';
@@ -182,6 +191,9 @@ export default class TradeStore extends BaseStore {
'has_take_profit',
'has_stop_loss',
'has_cancellation',
+ 'hovered_barrier',
+ 'short_barriers',
+ 'long_barriers',
'is_equal',
'last_digit',
'multiplier',
@@ -206,6 +218,7 @@ export default class TradeStore extends BaseStore {
barrier_1: observable,
barrier_2: observable,
barrier_count: observable,
+ barrier_choices: observable,
barriers: observable,
basis_list: observable,
basis: observable,
@@ -233,6 +246,7 @@ export default class TradeStore extends BaseStore {
has_equals_only: observable,
has_stop_loss: observable,
has_take_profit: observable,
+ hovered_barrier: observable,
hovered_contract_type: observable,
is_accumulator: computed,
is_chart_loading: observable,
@@ -243,7 +257,9 @@ export default class TradeStore extends BaseStore {
is_trade_component_mounted: observable,
is_trade_enabled: observable,
is_trade_params_expanded: observable,
+ is_turbos: computed,
last_digit: observable,
+ long_barriers: observable,
main_barrier: observable,
market_close_times: observable,
market_open_times: observable,
@@ -254,8 +270,10 @@ export default class TradeStore extends BaseStore {
previous_symbol: observable,
proposal_info: observable.ref,
purchase_info: observable.ref,
+ setHoveredBarrier: action.bound,
sessions: observable,
setDefaultGrowthRate: action.bound,
+ short_barriers: observable,
should_show_active_symbols_loading: observable,
should_skip_prepost_lifecycle: observable,
stake_boundary: observable,
@@ -264,7 +282,6 @@ export default class TradeStore extends BaseStore {
start_time: observable,
stop_loss: observable,
stop_out: observable,
- strike_price_choices: observable,
symbol: observable,
take_profit: observable,
tick_size_barrier: observable,
@@ -275,10 +292,12 @@ export default class TradeStore extends BaseStore {
barriers_flattened: computed,
changeDurationValidationRules: action.bound,
chartStateChange: action.bound,
+ clearContractPurchaseToastBox: action.bound,
clearContracts: action.bound,
clearLimitOrderBarriers: action.bound,
clearPurchaseInfo: action.bound,
clientInitListener: action.bound,
+ contract_purchase_toast_box: observable,
enablePurchase: action.bound,
exportLayout: action.bound,
forgetAllProposal: action.bound,
@@ -314,7 +333,9 @@ export default class TradeStore extends BaseStore {
resetPreviousSymbol: action.bound,
setActiveSymbols: action.bound,
setAllowEqual: action.bound,
+ setBarrierChoices: action.bound,
setChartStatus: action.bound,
+ setContractPurchaseToastbox: action.bound,
setContractTypes: action.bound,
setDefaultSymbol: action.bound,
setIsTradeParamsExpanded: action.bound,
@@ -324,7 +345,6 @@ export default class TradeStore extends BaseStore {
setPurchaseSpotBarrier: action.bound,
setSkipPrePostLifecycle: action.bound,
setStakeBoundary: action.bound,
- setStrikeChoices: action.bound,
setTradeStatus: action.bound,
show_digits_stats: computed,
themeChangeListener: action.bound,
@@ -332,9 +352,7 @@ export default class TradeStore extends BaseStore {
updateLimitOrderBarriers: action.bound,
updateStore: action.bound,
updateSymbol: action.bound,
- contract_purchase_toast_box: observable,
- clearContractPurchaseToastBox: action.bound,
- setContractPurchaseToastbox: action.bound,
+ vanilla_trade_type: observable,
});
// Adds intercept to change min_max value of duration validation
@@ -382,7 +400,7 @@ export default class TradeStore extends BaseStore {
() => [this.contract_type],
() => {
this.root_store.portfolio.setContractType(this.contract_type);
- if (this.is_multiplier || this.is_accumulator) {
+ if (this.is_accumulator || this.is_multiplier || this.is_turbos) {
// when switching back to Multiplier contract, re-apply Stop loss / Take profit validation rules
Object.assign(this.validation_rules, getMultiplierValidationRules());
} else {
@@ -633,6 +651,10 @@ export default class TradeStore extends BaseStore {
this.root_store.common.setSelectedContractType(this.contract_type);
}
+ setHoveredBarrier(hovered_value) {
+ this.hovered_barrier = hovered_value;
+ }
+
setPreviousSymbol(symbol) {
if (this.previous_symbol !== symbol) this.previous_symbol = symbol;
}
@@ -743,15 +765,22 @@ export default class TradeStore extends BaseStore {
if (!proposal_info) {
return;
}
- const { contract_type, barrier, high_barrier, low_barrier } = proposal_info;
+ const { barrier, contract_type, high_barrier, low_barrier } = proposal_info;
if (isBarrierSupported(contract_type)) {
const color = this.root_store.ui.is_dark_mode_on ? BARRIER_COLORS.DARK_GRAY : BARRIER_COLORS.GRAY;
+
// create barrier only when it's available in response
- this.main_barrier = new ChartBarrierStore(barrier || high_barrier, low_barrier, this.onChartBarrierChange, {
- color,
- not_draggable: this.is_vanilla,
- });
+ this.main_barrier = new ChartBarrierStore(
+ this.hovered_barrier || barrier || high_barrier,
+ low_barrier,
+ this.onChartBarrierChange,
+ {
+ color: this.hovered_barrier ? getHoveredColor(contract_type) : color,
+ line_style: this.hovered_barrier && BARRIER_LINE_STYLES.DASHED,
+ not_draggable: this.is_turbos || this.is_vanilla,
+ }
+ );
// this.main_barrier.updateBarrierShade(true, contract_type);
} else {
this.main_barrier = null;
@@ -1178,17 +1207,7 @@ export default class TradeStore extends BaseStore {
if (response.error) {
const error_id = getProposalErrorField(response);
if (error_id) {
- if (this.is_vanilla) {
- /**
- * This if-block ensures only the particular trade type's error message is selected
- * even though 2 proposal calls are made
- */
- if (this.vanilla_trade_type === contract_type) {
- this.setValidationErrorMessages(error_id, [response.error.message]);
- }
- } else {
- this.setValidationErrorMessages(error_id, [response.error.message]);
- }
+ this.setValidationErrorMessages(error_id, [response.error.message]);
}
// Commission for multipliers is normally set from proposal response.
// But when we change the multiplier and if it is invalid, we don't get the proposal response to set the commission. We only get error message.
@@ -1202,17 +1221,30 @@ export default class TradeStore extends BaseStore {
}
if (this.is_accumulator) this.resetAccumulatorData();
+ // Sometimes when we navigate fast, `forget_all` proposal is called immediately after proposal subscription calls.
+ // But, in the BE, `forget_all` proposal call is processed before the proposal subscriptions are registered. In this case, `forget_all` proposal doesn't forget the new subscriptions.
+ // So when we send new proposal subscription requests, we get `AlreadySubscribed` error.
+ // If we get an error message with code `AlreadySubscribed`, `forget_all` proposal will be called and all the existing subscriptions will be marked as complete in `deriv-api` and will subscribe to new proposals
+ if (response.error.code === 'AlreadySubscribed') {
+ this.refresh();
+
+ if (this.is_trade_component_mounted) {
+ this.debouncedProposal();
+ }
+ return;
+ }
+
// Sometimes the initial barrier doesn't match with current barrier choices received from API.
// When this happens we want to populate the list of barrier choices to choose from since the value cannot be specified manually
- if (this.is_vanilla) {
+ if ((this.is_turbos || this.is_vanilla) && response.error.details?.barrier_choices) {
const { barrier_choices, max_stake, min_stake } = response.error.details;
this.setStakeBoundary(contract_type, min_stake, max_stake);
- this.setStrikeChoices(barrier_choices);
- if (!this.strike_price_choices.includes(this.barrier_1)) {
+ this.setBarrierChoices(barrier_choices);
+ if (!this.barrier_choices.includes(this.barrier_1)) {
// Since on change of duration `proposal` API call is made which returns a new set of barrier values.
// The new list is set and the mid value is assigned
- const index = Math.floor(this.strike_price_choices.length / 2);
- this.barrier_1 = this.strike_price_choices[index];
+ const index = Math.floor(this.barrier_choices.length / 2);
+ this.barrier_1 = this.is_vanilla ? this.barrier_choices[index] : this.barrier_choices[0];
this.onChange({
target: {
name: 'barrier_1',
@@ -1221,24 +1253,11 @@ export default class TradeStore extends BaseStore {
});
}
}
-
- // Sometimes when we navigate fast, `forget_all` proposal is called immediately after proposal subscription calls.
- // But, in the BE, `forget_all` proposal call is processed before the proposal subscriptions are registered. In this case, `forget_all` proposal doesn't forget the new subscriptions.
- // So when we send new proposal subscription requests, we get `AlreadySubscribed` error.
- // If we get an error message with code `AlreadySubscribed`, `forget_all` proposal will be called and all the existing subscriptions will be marked as complete in `deriv-api` and will subscribe to new proposals
- if (response.error.code === 'AlreadySubscribed') {
- this.refresh();
-
- if (this.is_trade_component_mounted) {
- this.debouncedProposal();
- }
- return;
- }
} else {
this.validateAllProperties();
- if (this.is_vanilla) {
+ if (this.is_turbos || this.is_vanilla) {
const { max_stake, min_stake, barrier_choices } = response.proposal;
- this.setStrikeChoices(barrier_choices);
+ this.setBarrierChoices(barrier_choices);
this.setStakeBoundary(contract_type, min_stake, max_stake);
}
}
@@ -1550,6 +1569,10 @@ export default class TradeStore extends BaseStore {
return this.contract_type === 'multiplier';
}
+ get is_turbos() {
+ return isTurbosContract(this.contract_type);
+ }
+
get is_vanilla() {
return this.contract_type === 'vanilla';
}
@@ -1582,11 +1605,19 @@ export default class TradeStore extends BaseStore {
return findFirstOpenMarket(active_symbols, markets_to_search);
}
- setStrikeChoices(strike_prices) {
- this.strike_price_choices = strike_prices ?? [];
+ setBarrierChoices(barrier_choices) {
+ this.barrier_choices = barrier_choices ?? [];
+ if (this.is_turbos) {
+ const stored_barriers_data = { barrier: this.barrier_1, barrier_choices };
+ if (getContractSubtype(this.contract_type) === 'Long') {
+ this.long_barriers = stored_barriers_data;
+ } else {
+ this.short_barriers = stored_barriers_data;
+ }
+ }
}
setStakeBoundary(type, min_stake, max_stake) {
- this.stake_boundary[type] = { min_stake, max_stake };
+ if (min_stake && max_stake) this.stake_boundary[type] = { min_stake, max_stake };
}
}
diff --git a/packages/trader/src/sass/app/_common/components/contract-type-info.scss b/packages/trader/src/sass/app/_common/components/contract-type-info.scss
index ad50887418a4..6435e9fd07b3 100644
--- a/packages/trader/src/sass/app/_common/components/contract-type-info.scss
+++ b/packages/trader/src/sass/app/_common/components/contract-type-info.scss
@@ -101,11 +101,16 @@
margin-bottom: 1.6rem;
}
/* postcss-bem-linter: ignore */
+ h6,
+ span {
+ @include typeface(--small-left-bold-black, none);
+ }
p,
ul li {
@include typeface(--small-left-normal-black, none);
}
h2,
+ h6,
p,
ul li {
line-height: 1.5;
@@ -121,11 +126,13 @@
margin-bottom: 1.6rem;
@include mobile {
+ display: flex;
+ align-items: center;
+ justify-content: center;
width: 32.8rem;
height: 16.4rem;
background-color: var(--general-section-1);
svg {
- width: 100%;
height: 100%;
}
}
diff --git a/packages/trader/src/sass/app/_common/components/contract-type-list.scss b/packages/trader/src/sass/app/_common/components/contract-type-list.scss
index f39f7b78992c..1b526090667a 100644
--- a/packages/trader/src/sass/app/_common/components/contract-type-list.scss
+++ b/packages/trader/src/sass/app/_common/components/contract-type-list.scss
@@ -51,6 +51,9 @@
&__icon {
margin-left: auto;
+ @include mobile {
+ display: block;
+ }
@include mobile {
display: block;
diff --git a/packages/trader/src/sass/app/_common/components/contract-type-widget.scss b/packages/trader/src/sass/app/_common/components/contract-type-widget.scss
index 93544bb0d903..21140788b286 100644
--- a/packages/trader/src/sass/app/_common/components/contract-type-widget.scss
+++ b/packages/trader/src/sass/app/_common/components/contract-type-widget.scss
@@ -100,11 +100,16 @@
&--multiplier {
margin-bottom: 0.6rem;
}
+ &--turbosshort,
+ &--turboslong {
+ top: 0.2rem;
+ margin-right: 0.7rem;
+ }
&--vanilla {
margin-right: 0.4rem;
}
.contract-type-widget__display {
- padding: 0.8rem;
+ padding: 0.8rem 2rem 0.8rem 0.8rem;
}
}
}
diff --git a/packages/trader/src/sass/app/_common/components/purchase-button.scss b/packages/trader/src/sass/app/_common/components/purchase-button.scss
index 6ccb8da7b588..6216a02fff9c 100644
--- a/packages/trader/src/sass/app/_common/components/purchase-button.scss
+++ b/packages/trader/src/sass/app/_common/components/purchase-button.scss
@@ -314,7 +314,6 @@
}
&--multiplier,
&--accumulator {
- margin: 0;
.btn-purchase__info--left {
width: 35%;
flex: none;
@@ -397,4 +396,32 @@
}
}
}
+ &--turbos {
+ .btn-purchase {
+ &__info--left {
+ width: 70%;
+ }
+ &__effect-detail {
+ &--arrow {
+ left: 12rem;
+ }
+ }
+ @include mobile {
+ &__top {
+ align-items: center;
+ width: auto;
+ }
+ }
+ }
+ &.btn-purchase--swoosh {
+ .btn-purchase {
+ &__effect-detail {
+ transform: scale3d(15, 1, 1);
+ &--arrow {
+ transform: translate3d(12rem, 0, 0) rotate(45deg);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/packages/trader/src/sass/app/_common/layout/trader-layouts.scss b/packages/trader/src/sass/app/_common/layout/trader-layouts.scss
index a5d457f2739f..34ed50d41e42 100644
--- a/packages/trader/src/sass/app/_common/layout/trader-layouts.scss
+++ b/packages/trader/src/sass/app/_common/layout/trader-layouts.scss
@@ -404,7 +404,8 @@ $FLOATING_HEADER_HEIGHT: 41px;
position: relative;
z-index: 2;
- .trade-container--accumulators & {
+ .trade-container--accumulators &,
+ .trade-container--turbos & {
height: 24.8rem;
}
&__content-loader {
diff --git a/packages/trader/src/sass/app/modules/trading-mobile.scss b/packages/trader/src/sass/app/modules/trading-mobile.scss
index ffa70084d1fc..acf3c0c67947 100644
--- a/packages/trader/src/sass/app/modules/trading-mobile.scss
+++ b/packages/trader/src/sass/app/modules/trading-mobile.scss
@@ -242,7 +242,7 @@
}
}
}
- &__strike {
+ &__stake-container {
display: flex;
flex-direction: column;
.trade-container__stake-field {
diff --git a/packages/trader/src/sass/app/modules/trading.scss b/packages/trader/src/sass/app/modules/trading.scss
index acd799b737c2..e3245432923d 100644
--- a/packages/trader/src/sass/app/modules/trading.scss
+++ b/packages/trader/src/sass/app/modules/trading.scss
@@ -297,6 +297,12 @@
opacity: 0;
}
}
+ &--turbos {
+ display: flex;
+ justify-self: left;
+ align-items: flex-start;
+ flex-direction: column;
+ }
&-value {
font-size: 1.4rem;
font-weight: 700;
@@ -321,22 +327,16 @@
color: var(--text-colored-background);
}
}
- &-strike {
- @include mobile {
- color: var(--text-less-prominent);
- font-size: var(--text-size-xxs);
- display: flex;
- gap: 0.8rem;
- justify-content: space-between;
- padding: 1rem 0;
- }
- }
&-currency {
margin-left: 4px;
margin-right: 1px;
display: inline-block;
position: relative;
font-weight: bold;
+
+ &--payout-per-point {
+ margin-left: 0;
+ }
}
&-movement {
margin-left: 4px;
@@ -346,6 +346,10 @@
position: relative;
}
}
+ &--turbos {
+ align-items: center;
+ margin-bottom: 0.8rem;
+ }
}
&__price-info {
&-currency {
@@ -353,41 +357,69 @@
font-size: 0.9rem;
}
}
- &-modal {
- @include mobile {
- &--vanilla {
- max-width: calc(min(33rem, 85vw));
- }
- }
- }
}
&__barriers {
display: flex;
flex-direction: column;
+ position: relative;
+ bottom: 0.1rem;
+
+ .trade-container__fieldset-info--left {
+ transform: translateX(0.7rem);
+ }
+ &__wrapper {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
&:first-child {
- padding-right: 8px;
+ padding-right: 0.8rem;
+ }
+ &-value {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ margin-top: 0.4rem;
+ background: var(--fill-normal);
+ border-radius: 0.4rem;
+ width: 100%;
+ height: 3.2rem;
+ cursor: pointer;
+ position: relative;
+ &--arrow-right {
+ position: absolute;
+ right: 0.8rem;
+ transform: rotate(180deg);
+ }
}
&-input {
- padding-left: 3px;
+ padding-left: 0.3rem;
}
&-single {
width: 100%;
}
+ &-tooltip:first-child {
+ margin-bottom: 0.8rem;
+ }
+ &-spot {
+ margin-top: 0.8rem;
+ padding: 0rem 0.8rem;
+ }
&-multiple {
&-input {
- padding-left: 25px;
- padding-right: 9px;
+ padding-left: 2.5rem;
+ padding-right: 0.9rem;
text-align: center;
}
&:first-of-type {
- padding-right: 8px;
+ padding-right: 0.8rem;
}
}
&--up,
&--down {
position: absolute;
- margin-top: 15px;
+ margin-top: 1.5rem;
}
&--up {
right: 86.5%;
@@ -396,6 +428,90 @@
right: 39%;
}
}
+ &__barriers-table {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 100;
+ width: 100%;
+ height: calc(100vh - 4.8rem - 3.6rem - 1.6rem - 1.6rem);
+ display: flex;
+ flex-direction: column;
+ transition: opacity 0.25s cubic-bezier(0.25, 0.1, 0.1, 0.25);
+ &--enter,
+ &--exit {
+ opacity: 0;
+ pointer-events: none;
+ }
+ &--enter-done {
+ opacity: 1;
+ pointer-events: auto;
+ }
+ &__header {
+ height: $header-height;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 0 1rem;
+ border-bottom: 0.1rem solid var(--general-hover);
+ @include mobile {
+ height: 4.5rem;
+ padding: 0 1.6rem;
+ border-bottom: 0.1rem solid var(--general-section-1);
+ }
+ &-wrapper {
+ display: flex;
+ gap: 0.8rem;
+ }
+ }
+ &__icon-close {
+ display: inline-block;
+ margin-left: auto;
+ cursor: pointer;
+ svg {
+ @extend %inline-icon;
+ height: 1.6em;
+ width: 1.6em;
+ }
+ }
+ &__text {
+ padding: 1.6rem 1.6rem 0;
+ @include mobile {
+ padding: 1.6rem 0 0.8rem 1.6rem;
+ }
+ }
+ &__list {
+ overflow-y: scroll;
+ flex: 1 1 auto;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: 0 0.6rem;
+ }
+ &__item {
+ padding: 1rem 1.4rem;
+ border-radius: 0.6rem;
+ height: 3.8rem;
+ cursor: pointer;
+
+ &:hover:not(&--selected) {
+ background-color: var(--general-hover);
+ font-weight: var(--text-weight-bold);
+ }
+ &--selected,
+ &:active,
+ &:focus {
+ background-color: var(--general-active);
+ font-weight: var(--text-weight-bold);
+ }
+ @include mobile {
+ display: flex;
+ align-items: center;
+ height: 4.8rem;
+ padding: 1.5rem 1.4rem;
+ }
+ }
+ }
&__allow-equals {
/* postcss-bem-linter: ignore */
&__label {
@@ -506,6 +622,25 @@
align-self: flex-end;
}
}
+ &__trade {
+ &-type-tabs {
+ border-radius: $BORDER_RADIUS;
+ padding: 0.8rem;
+ background-color: var(--general-section-1);
+ border-color: var(--general-section-1);
+ color: var(--text-general);
+ @include mobile {
+ width: 50%;
+ padding: 0;
+ margin-bottom: 0rem;
+ margin-top: 0.6rem;
+ &--button {
+ height: 4rem;
+ bottom: 0.5rem;
+ }
+ }
+ }
+ }
&__deal-cancellation-popover {
width: 28rem;
}
@@ -610,6 +745,9 @@
margin-top: 0.4rem;
display: flex;
justify-content: space-between;
+ @include desktop {
+ height: 2.8rem;
+ }
&--min {
display: flex;
@@ -652,11 +790,11 @@
/** @define purchase-container; weak */
.purchase-container,
-.purchase-container--accumulator {
+.purchase-container__accumulator {
position: relative;
&__option {
- padding: 1.6rem 0.8rem;
+ padding: 0.8rem;
&:not(:only-of-type) {
&:nth-last-child(2) {
@@ -699,12 +837,15 @@
grid-template-areas: 'a b';
grid-column-gap: 0.5rem;
- &--accumulator {
+ &__accumulator {
grid-template-areas: 'a';
.purchase-container__option {
margin-bottom: 0 !important;
}
}
+ &__turbos {
+ display: block;
+ }
}
.purchase-buttons-overlay {
@@ -818,6 +959,30 @@
}
}
+.payout-per-point {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 1rem 1.6rem;
+ height: 4.15rem;
+
+ &__label {
+ position: relative;
+ bottom: 0.3rem;
+ &-wrapper {
+ display: flex;
+ gap: 0.8rem;
+ }
+ }
+ &__value {
+ position: relative;
+ bottom: 0.2rem;
+ .trade-container__price-info-movement {
+ top: 0.3rem;
+ }
+ }
+}
+
/** @define market-unavailable-modal */
@include mobile {
.market-unavailable-modal {
@@ -852,17 +1017,7 @@
}
}
-.strike {
- &--info {
- color: var(--text-general);
- &__value {
- font-size: var(--text-size-xxs);
- margin-left: unset;
- }
- &__wrapper {
- padding-right: 1rem;
- }
- }
+.price-info {
&--value-container {
display: flex;
}
diff --git a/packages/translations/src/i18next/i18next.ts b/packages/translations/src/i18next/i18next.ts
index 2c01b1f72a51..809453cd0161 100644
--- a/packages/translations/src/i18next/i18next.ts
+++ b/packages/translations/src/i18next/i18next.ts
@@ -136,7 +136,7 @@ export const changeLanguage = async (lang: string, cb: (arg0: string) => void) =
// component wrapped with i18n
export const Localize = withI18n(i18n);
-export const localize = (string: string, values?: T) => {
+export const localize = (string: string, values?: T): string => {
if (!string) return '';
return i18n.t(crc32(string).toString(), { defaultValue: string, ...values });