${localize('Predict the direction and purchase')}`,
event_type : 'next',
nextButton : btn_finish,
diff --git a/src/javascript/app/common/helpers.js b/src/javascript/app/common/helpers.js
new file mode 100644
index 00000000000..eed46e0fb80
--- /dev/null
+++ b/src/javascript/app/common/helpers.js
@@ -0,0 +1,151 @@
+import React from 'react';
+import { Tooltip } from '@deriv-com/quill-ui';
+import parse from 'html-react-parser';
+import moment from 'moment';
+import dataManager from './data_manager';
+import { getLocalTime } from '../base/clock';
+import common_independent from '../pages/trade/common_independent';
+import Defaults, { PARAM_NAMES } from '../pages/trade/defaults';
+import { triggerSessionChange } from '../hooks/events';
+import { isCryptocurrency } from '../../_common/base/currency_base';
+
+const parseData = (raw_data) => !raw_data ? '' : parse(raw_data);
+
+const triggerClick = (query) => document.querySelector(query)?.click();
+
+const TimeTooltipWrapper = (element, time) => {
+ const localTime = getLocalTime(`${time}` || '');
+ return (
+
+ {element}
+
+ );
+};
+
+const setMinMaxTime = (selector, check_end_time) => {
+ const $date_start = $('#date_start');
+ const $time_start = $('#time_start');
+ let min_time, max_time;
+
+ if ($date_start && $date_start.val()) {
+ const date_start_val = $date_start.val();
+ const moment_date_start = moment.unix(date_start_val).utc();
+ const moment_now = (window.time || moment.utc()).clone();
+
+ if (check_end_time) {
+ const min_max_time = common_independent.getMinMaxTimeEnd($date_start, $time_start, moment_now);
+
+ min_time = min_max_time.minTime;
+ max_time = min_max_time.maxTime;
+ } else if (moment_date_start.isSame(moment_now, 'day')) {
+ min_time = moment_now.clone();
+ }
+ }
+ const init_obj = { selector };
+ if (min_time) {
+ init_obj.minTime = min_time.clone();
+ }
+ if (max_time) {
+ init_obj.maxTime = max_time.clone();
+ }
+
+ setMinMaxTimeObj(init_obj);
+};
+
+const setMinMaxTimeObj = (options) => {
+ const time_now = moment.utc(window.time).clone();
+ const starttime_obj = {};
+ const expirytime_obj = {};
+ const obj_config = {};
+
+ if (options.minTime) {
+ options.minTime = options.minTime === 'now' ? time_now : moment.utc(options.minTime);
+ if (options.minTime.isBefore(time_now) &&
+ (!options.maxTime || time_now.unix() !== options.maxTime.unix())) {
+ options.minTime = time_now;
+ }
+ if (options.useLocalTime) options.minTime = options.minTime.local();
+
+ if (options.minTime.minutes() > 55) {
+ options.minTime.minutes(60);
+ }
+ obj_config.minTime = { hour: parseInt(options.minTime.hour()), minute: parseInt(options.minTime.minute()) };
+ if (options.selector === 'time_start') {
+ starttime_obj.minTime = obj_config.minTime;
+ }
+ if (options.selector === 'expiry_time') {
+ expirytime_obj.minTime = obj_config.minTime;
+ }
+ }
+
+ if (options.maxTime) {
+ options.maxTime = moment.utc(options.maxTime);
+ let minute = parseInt(options.maxTime.minute());
+ let hour = parseInt(options.maxTime.hour());
+
+ if (!(hour === 0 && minute === 0) && !(hour === 23 && minute === 55)) {
+ hour = minute < 5 ? hour - 1 : hour;
+ minute = minute < 5 ? 55 : Math.ceil((minute - 5) / 5) * 5;
+ }
+
+ obj_config.maxTime = { hour, minute };
+ if (options.selector === '#time_start') {
+ starttime_obj.maxTime = obj_config.maxTime;
+ }
+ if (options.selector === '#expiry_time') {
+ expirytime_obj.maxTime = obj_config.maxTime;
+ }
+ }
+
+ dataManager.setTrade({
+ starttime_obj,
+ expirytime_obj,
+ }, 'time');
+};
+
+const paramsMap = {
+ 'date_start' : PARAM_NAMES.DATE_START,
+ 'time_start' : PARAM_NAMES.TIME_START,
+ 'expiry_type' : PARAM_NAMES.EXPIRY_TYPE,
+ 'duration_amount' : PARAM_NAMES.DURATION_AMOUNT,
+ 'duration_units' : PARAM_NAMES.DURATION_UNITS,
+ 'expiry_date' : PARAM_NAMES.EXPIRY_DATE,
+ 'expiry_time' : PARAM_NAMES.EXPIRY_TIME,
+ 'barrier' : PARAM_NAMES.BARRIER,
+ 'barrier_high' : PARAM_NAMES.BARRIER_HIGH,
+ 'barrier_low' : PARAM_NAMES.BARRIER_LOW,
+ 'prediction' : PARAM_NAMES.PREDICTION,
+ 'selected_tick' : PARAM_NAMES.SELECTED_TICK,
+ 'amount_type' : PARAM_NAMES.AMOUNT_TYPE,
+ 'amount' : PARAM_NAMES.AMOUNT,
+ 'currency' : PARAM_NAMES.CURRENCY,
+ 'multiplier_currency': PARAM_NAMES.CURRENCY,
+ 'multiplier' : PARAM_NAMES.MULTIPLIER,
+ 'callputequal' : PARAM_NAMES.IS_EQUAL,
+};
+
+const setDefaultParams = (elementId, value) => {
+ if (paramsMap[elementId]) {
+ const currency = Defaults.get(PARAM_NAMES.CURRENCY);
+ if (elementId === 'amount' && isCryptocurrency(currency)) {
+ Defaults.set('amount_crypto', value);
+ } else {
+ Defaults.set(paramsMap[elementId], value);
+ }
+ triggerSessionChange();
+ }
+};
+
+export {
+ parseData,
+ triggerClick,
+ TimeTooltipWrapper,
+ setMinMaxTime,
+ setDefaultParams,
+};
+
diff --git a/src/javascript/app/components/loading-spinner.js b/src/javascript/app/components/loading-spinner.js
index d94866ef9b2..368790f2109 100644
--- a/src/javascript/app/components/loading-spinner.js
+++ b/src/javascript/app/components/loading-spinner.js
@@ -1,3 +1,5 @@
+import dataManager from '../common/data_manager';
+
const assertContainerExists = (content_id) => {
if (!content_id) {
throw new Error('Loading spinner content id is missing or undefined.');
@@ -26,6 +28,10 @@ export const show = (content_id) => {
* @param content_id
*/
export const hide = (content_id) => {
+ dataManager.setContract({
+ hidePageLoader: true,
+ });
+
assertContainerExists(content_id);
const $container = $(`#${content_id}`);
const $spinner = $container.find('.barspinner');
diff --git a/src/javascript/app/hooks/events.js b/src/javascript/app/hooks/events.js
new file mode 100644
index 00000000000..84fe26c7fce
--- /dev/null
+++ b/src/javascript/app/hooks/events.js
@@ -0,0 +1,84 @@
+// events.js
+import { useEffect, useState } from 'react';
+
+class EventEmitter {
+ constructor() {
+ this.events = {};
+ }
+
+ subscribe(event, callback) {
+ if (!this.events[event]) {
+ this.events[event] = [];
+ }
+ this.events[event].push(callback);
+ return () => {
+ this.events[event] = this.events[event].filter((cb) => cb !== callback);
+ };
+ }
+
+ emit(event, data) {
+ if (this.events[event]) {
+ this.events[event].forEach((callback) => callback(data));
+ }
+ }
+}
+
+const eventEmitter = new EventEmitter();
+
+// Custom hooks
+const useCustomEvent = (eventName) => {
+ const [hasNewChanges, setHasNewChanges] = useState(false);
+
+ useEffect(() => {
+ const handleEvent = () => {
+ setHasNewChanges(true);
+ setTimeout(() => setHasNewChanges(false), 0);
+ };
+
+ const unsubscribe = eventEmitter.subscribe(eventName, handleEvent);
+
+ return () => {
+ unsubscribe();
+ };
+ }, [eventName]);
+
+ return hasNewChanges;
+};
+
+export const eventDispatcher = (element, eventName) => {
+ const event = new Event(eventName, { bubbles: true, cancelable: true });
+ element.dispatchEvent(event);
+};
+
+// Trigger functions
+const triggerCustomEvent = (eventName) => {
+ eventEmitter.emit(eventName);
+};
+
+// Market Change
+export const useMarketChange = () => useCustomEvent('marketChange');
+export const triggerMarketChange = () => triggerCustomEvent('marketChange');
+
+// Contract Change
+export const useContractChange = () => useCustomEvent('contractChange');
+export const triggerContractChange = () => triggerCustomEvent('contractChange');
+
+// SesstionStorage Change
+export const useSessionChange = () => useCustomEvent('sessionChange');
+export const triggerSessionChange = () => triggerCustomEvent('sessionChange');
+
+// Purchase Change
+export const usePurchaseChange = () => useCustomEvent('purchaseChange');
+export const triggerPurchaseChange = () => triggerCustomEvent('purchaseChange');
+
+// Trade Change
+export const useTradeChange = () => useCustomEvent('tradeChange');
+export const triggerTradeChange = () => triggerCustomEvent('tradeChange');
+
+// Barrier Change
+export const useBarrierChange = () => useCustomEvent('barrierChange');
+export const triggerBarrierChange = () => triggerCustomEvent('barrierChange');
+
+// Start/End Time Change
+export const useTimeChange = () => useCustomEvent('timeChange');
+export const triggerTimeChange = () => triggerCustomEvent('timeChange');
diff --git a/src/javascript/app/pages/bottom/data/explanation.js b/src/javascript/app/pages/bottom/data/explanation.js
new file mode 100644
index 00000000000..763d42e143e
--- /dev/null
+++ b/src/javascript/app/pages/bottom/data/explanation.js
@@ -0,0 +1,347 @@
+import { localize } from '../../../../_common/localize.js';
+
+export const contractExplanationData = {
+ winning: {
+ asian: {
+
+ content: [
+ localize('Asian options settle by comparing the last tick with the average spot over the period.'),
+ localize('If you select "Asian Rise", you will win the payout if the last tick is higher than the average of the ticks.'),
+ localize('If you select "Asian Fall", you will win the payout if the last tick is lower than the average of the ticks.'),
+ localize('If the last tick is equal to the average of the ticks, you don\'t win the payout.'),
+ ],
+ },
+ digits: {
+
+ content: [
+ localize('If you select "Matches", you will win the payout if the last digit of the last tick is the same as your prediction.'),
+ localize('If you select "Differs", you will win the payout if the last digit of the last tick is not the same as your prediction.'),
+ ],
+ },
+ endsinout: {
+ content: [
+ localize('If you select "Ends Between", you win the payout if the exit spot is strictly higher than the Low barrier AND strictly lower than the High barrier.'),
+ localize('If you select "Ends Outside", you win the payout if the exit spot is EITHER strictly higher than the High barrier, OR strictly lower than the Low barrier.'),
+ localize('If the exit spot is equal to either the Low barrier or the High barrier, you don\'t win the payout.'),
+ ],
+ },
+ evenodd: {
+ content: [
+ localize('If you select "Even", you will win the payout if the last digit of the last tick is an even number (i.e., 2, 4, 6, 8, or 0).'),
+ localize('If you select "Odd", you will win the payout if the last digit of the last tick is an odd number (i.e., 1, 3, 5, 7, or 9).'),
+ ],
+ },
+ higherlower: {
+ content: [
+ localize('If you select "Higher", you win the payout if the exit spot is strictly higher than the barrier.'),
+ localize('If you select "Lower", you win the payout if the exit spot is strictly lower than the barrier.'),
+ localize('If the exit spot is equal to the barrier, you don\'t win the payout.'),
+ ],
+ },
+ overunder: {
+ content: [
+ localize('If you select "Over", you will win the payout if the last digit of the last tick is greater than your prediction.'),
+ localize('If you select "Under", you will win the payout if the last digit of the last tick is less than your prediction.'),
+ ],
+ },
+ risefall: {
+ content: [
+ localize('If you select "Rise", you win the payout if the exit spot is strictly higher than the entry spot.'),
+ localize('If you select "Fall", you win the payout if the exit spot is strictly lower than the entry spot.'),
+ localize('If you select "Allow equals", you win the payout if exit spot is higher than or equal to entry spot for "Rise". Similarly, you win the payout if exit spot is lower than or equal to entry spot for "Fall".'),
+ ],
+ },
+ runbet_lucky10: {
+
+ content: [
+ localize('You win the payout if the market price ends in the digit you have selected.'),
+ ],
+ },
+ runbet_quick10: {
+
+ content: [
+ localize('You win the payout if the market price does not end in the digit you have selected.'),
+ ],
+ },
+ runbet_updown: {
+
+ content: [
+ localize('If you select "rises", you win the payout if the market price is higher than the entry spot.'),
+ localize('If you select "falls", you win the payout if the market price is lower than the entry spot.'),
+ ],
+ },
+ staysinout: {
+
+ content: [
+ localize('If you select "Stays Between", you win the payout if the market stays between (does not touch) either the High barrier or the Low barrier at any time during the contract period.'),
+ localize('If you select "Goes Outside", you win the payout if the market touches either the High barrier or the Low barrier at any time during the contract period.'),
+ ],
+ },
+ ticks: {
+
+ content: [
+ localize('If you select "Rises", you win the payout if the exit spot is strictly higher than the entry spot.'),
+ localize('If you select "Falls", you win the payout if the exit spot is strictly lower than the entry spot.'),
+ ],
+ },
+ touchnotouch: {
+
+ content: [
+ localize('If you select "Touches", you win the payout if the market touches the barrier at any time during the contract period.'),
+ localize('If you select "Does Not Touch", you win the payout if the market never touches the barrier at any time during the contract period.'),
+ ],
+ },
+ updown: {
+
+ content: [
+ localize('If you select "rises", you win the payout if the market price is higher than the entry spot.'),
+ localize('If you select "falls", you win the payout if the market price is lower than the entry spot.'),
+ ],
+ },
+ lookbacklow: {
+ title : 'Pay-out' ,
+ content: [
+ localize('By purchasing the [_1]"Close-Low"[_2] contract, you\'ll win the multiplier times the difference between the [_1]close[_2] and [_1]low[_2] over the duration of the contract.', ['', '']),
+ ],
+ },
+ lookbackhigh: {
+ title : 'Pay-out' ,
+ content: [
+ localize('By purchasing the [_1]"High-Close"[_2] contract, you\'ll win the multiplier times the difference between the [_1]high[_2] and [_1]close[_2] over the duration of the contract.', ['', '']),
+ ],
+ },
+ lookbackhighlow: {
+ title : 'Pay-out' ,
+ content: [
+ localize('By purchasing the [_1]"High-Low"[_2] contract, you\'ll win the multiplier times the difference between the [_1]high[_2] and [_1]low[_2] over the duration of the contract.', ['', '']),
+ ],
+ },
+ reset: {
+
+ content: [
+ localize('If you select "Reset-Call", you win the payout if the exit spot is strictly higher than either the entry spot or the spot at reset time.'),
+ localize('If you select "Reset-Put", you win the payout if the exit spot is strictly lower than either the entry spot or the spot at reset time.'),
+ localize('If the exit spot is equal to the barrier or the new barrier (if a reset occurs), you don\'t win the payout.'),
+ ],
+ },
+ highlowticks: {
+
+ content: [
+ localize('If you select [_1]"High Tick"[_2], you win the payout if the selected tick is the [_1]highest among the next five ticks[_2].', ['', '']),
+ localize('If you select [_1]"Low Tick"[_2], you win the payout if the selected tick is the [_1]lowest among the next five ticks[_2].', ['', '']),
+ ],
+ },
+ runs: {
+
+ content: [
+ localize('If you select [_1]"Only Ups"[_2], you win the payout if consecutive ticks rise successively after the [_1]entry spot[_2].[_3]No payout if any tick falls or is equal to any of the previous ticks.', ['', '', ' ']),
+ localize('If you select [_1]"Only Downs"[_2], you win the payout if consecutive ticks fall successively after the [_1]entry spot[_2].[_3]No payout if any tick rises or is equal to any of the previous ticks.', ['', '', ' ']),
+ ],
+ },
+ },
+ image: {
+ explanation_image_1:
+ '/images/pages/trade-explanation/en/rises.svg?3e9b01f8f3365272abe03784db97d968' ,
+ explanation_image_2:
+ '/images/pages/trade-explanation/en/falls.svg?3e9b01f8f3365272abe03784db97d968' ,
+ },
+ explain: {
+ asian: {
+ title : localize('Entry Spot') ,
+ content: [
+ localize('The entry spot is the first tick after the contract is processed by our servers.'),
+ ],
+ title_secondary : localize('The Average') ,
+ content_secondary: [
+ localize('The average is the average of the ticks, including the entry spot and the last tick.'),
+ ],
+ },
+ digits: {
+ title : localize('Entry Spot') ,
+ content: [
+ localize('The entry spot is the first tick after the contract is processed by our servers.'),
+ ],
+ },
+ endsinout: {
+ title : localize('Exit spot') ,
+ content: [
+ localize('The exit spot is the latest tick at or before the end time.'),
+ localize('The end time is the selected number of minutes/hours after the start time (if less than one day in duration), or at the end of the trading day (if one day or more in duration).'),
+ localize('The start time is when the contract is processed by our servers.'),
+ ],
+ },
+ evenodd: {
+ title : localize('Entry Spot') ,
+ content: [
+ localize('The entry spot is the first tick after the contract is processed by our servers.'),
+ ],
+ },
+ higherlower: {
+ title : localize('Exit spot') ,
+ content: [
+ localize('The exit spot is the latest tick at or before the end time.'),
+ localize('The end time is the selected number of minutes/hours after the start time (if less than one day in duration), or at the end of the trading day (if one day or more in duration).'),
+ localize('The start time is when the contract is processed by our servers.'),
+ ],
+ },
+ overunder: {
+ title : localize('Entry Spot') ,
+ content: [
+ localize('The entry spot is the first tick after the contract is processed by our servers.'),
+ ],
+ },
+ risefall: {
+ title : localize('Entry spot') ,
+ content: [
+ localize('The start time is when the contract is processed by our servers and the entry spot is the next tick thereafter.'),
+ localize('If you select a start time in the future, the start time is that which is selected and the entry spot is the price in effect at that time.'),
+ ],
+ title_secondary : localize('Exit spot') ,
+ content_secondary: [
+ localize('The exit spot is the latest tick at or before the end time.'),
+ localize('If you select a start time of "Now", the end time is the selected number of minutes/hours after the start time (if less than one day in duration), or at the end of the trading day (if one day or more in duration).'),
+ localize('If you select a specific end time, the end time is the selected time.'),
+ ],
+ },
+
+ staysinout: {
+ title : localize('Contract period') ,
+ content: [
+ localize('The contract period is the period between the next tick after the start time and the end time.'),
+ localize('The start time is when the contract is processed by our servers.'),
+ localize('The end time is the selected number of minutes/hours after the start time (if less than one day in duration), or at the end of the trading day (if one day or more in duration).'),
+ ],
+ },
+ touchnotouch: {
+ title : localize('Contract period') ,
+ content: [
+ localize('The contract period is the period between the next tick after the start time and the end time.'),
+ localize('The start time is when the contract is processed by our servers.'),
+ localize('The end time is the selected number of minutes/hours after the start time (if less than one day in duration), or at the end of the trading day (if one day or more in duration).'),
+ ],
+ },
+ lookbacklow: {
+ title : localize('High, Low and Close') ,
+ content: [
+ localize('The [_1]high[_2] is the highest point ever reached by the market during the contract period.', ['', '']),
+ localize('The [_1]low[_2] is the lowest point ever reached by the market during the contract period.', ['', '']),
+ localize('The [_1]close[_2] is the latest tick at or before the [_1]end time[_2]. If you selected a specific [_1]end time,[_2] the [_1]end time[_2] is the selected time.', ['', '']),
+ ],
+ title_secondary : localize('Contract period') ,
+ content_secondary: [
+ localize('The [_1]contract period[_2] is the period between the [_1]first tick[_2] (after start time) and the [_1]end time[_2].', ['', '']),
+ localize('The [_1]start time[_2] begins when the contract is processed by our servers.', ['', '']),
+ localize('The [_1]end time[_2] is the selected number of minutes/hours after the [_1]start time[_2].', ['', '']),
+ ],
+ },
+ lookbackhigh: {
+ title : localize('High, Low and Close') ,
+ content: [
+ localize('The [_1]high[_2] is the highest point ever reached by the market during the contract period.', ['', '']),
+ localize('The [_1]low[_2] is the lowest point ever reached by the market during the contract period.', ['', '']),
+ localize('The [_1]close[_2] is the latest tick at or before the [_1]end time[_2]. If you selected a specific [_1]end time,[_2] the [_1]end time[_2] is the selected time.', ['', '']),
+ ],
+ title_secondary : localize('Contract period') ,
+ content_secondary: [
+ localize('The [_1]contract period[_2] is the period between the [_1]first tick[_2] (after start time) and the [_1]end time[_2].', ['', '']),
+ localize('The [_1]start time[_2] begins when the contract is processed by our servers.', ['', '']),
+ localize('The [_1]end time[_2] is the selected number of minutes/hours after the [_1]start time[_2].', ['', '']),
+ ],
+ },
+ lookbackhighlow: {
+ title : localize('High, Low and Close') ,
+ content: [
+ localize('The [_1]high[_2] is the highest point ever reached by the market during the contract period.', ['', '']),
+ localize('The [_1]low[_2] is the lowest point ever reached by the market during the contract period.', ['', '']),
+ localize('The [_1]close[_2] is the latest tick at or before the [_1]end time[_2]. If you selected a specific [_1]end time,[_2] the [_1]end time[_2] is the selected time.', ['', '']),
+ ],
+ title_secondary : localize('Contract period') ,
+ content_secondary: [
+ localize('The [_1]contract period[_2] is the period between the [_1]first tick[_2] (after start time) and the [_1]end time[_2].', ['', '']),
+ localize('The [_1]start time[_2] begins when the contract is processed by our servers.', ['', '']),
+ localize('The [_1]end time[_2] is the selected number of minutes/hours after the [_1]start time[_2].', ['', '']),
+ ],
+ },
+ reset: {
+ title : localize('Reset Time') ,
+ content: [
+ localize('At reset time, if the spot is in the opposite direction of your prediction, the barrier is reset to that spot.'),
+ localize('The exit spot is the latest tick at or before the end time.'),
+ localize('The end time is the selected number of minutes/hours after the start time.'),
+ localize('The start time is when the contract is processed by our servers.'),
+ localize('The entry spot is the first tick after the contract is processed by our servers.'),
+ ],
+ },
+ highlowticks: {
+ title : localize('Entry Spot') ,
+ content: [
+ localize('The entry spot is the first tick after the contract is processed by our servers.'),
+ ],
+ },
+ runs: {
+ title : localize('Entry spot') ,
+ content: [
+ localize('The [_1]start time[_2] is when the contract has been processed by our servers and the [_1]entry spot[_2] is the [_1]next tick[_2] thereafter.', ['', '']),
+ ],
+ title_secondary : localize('Exit Spot'),
+ content_secondary: [
+ localize('The [_1]exit spot[_2] is the last tick when the contract ends. Contract ends when all ticks rise or fall successively, or when a single tick breaks the predicted pattern.', ['', '']),
+ ],
+ },
+ },
+ note: {
+ asian: {
+ content: [
+ localize('Asian contracts will be refunded at the purchase price if the contract doesn\'t end within 5 minutes.'),
+ ],
+ },
+ digits: {
+ content: [
+ localize('Digit contracts will be refunded at the purchase price if the contract doesn\'t end within 5 minutes.'),
+ ],
+ },
+ endsinout: {
+ content: [
+ localize('Ends Between/Ends Outside contracts will be refunded at the purchase price if there are less than 2 ticks between the start and end times.'),
+ ],
+ },
+ evenodd: {
+ content: [
+ localize('Even/Odd contracts will be refunded at the purchase price if the contract doesn\'t end within 5 minutes.'),
+ ],
+ },
+ higherlower: {
+ content: [
+ localize('Higher/Lower contracts will be refunded at the purchase price if there are less than 2 ticks between the start and end times.'),
+ ],
+ },
+ overunder: {
+ content: [
+ localize('Over/Under contracts will be refunded at the purchase price if the contract doesn\'t end within 5 minutes.'),
+ ],
+ },
+ risefall: {
+ content: [
+ `${localize('Rise/Fall contracts will be refunded if:')} `,
+ ` • ${localize('There are less than 2 ticks between the start and end times')} `,
+ ` • ${localize('The contract doesn\'t end within 5 minutes (for tick duration contracts)')}`,
+
+ ],
+ },
+ staysinout: {
+ content: [
+ localize('Stays Between/Goes Outside Contracts will be refunded at the purchase price if there are less than 2 ticks between the start and end times.'),
+ ],
+ },
+ touchnotouch: {
+ content: [
+ localize('Touch/No Touch contracts will be refunded at the purchase price if there are less than 2 ticks between the start and end times.'),
+ ],
+ },
+ highlowticks: {
+ content: [
+ localize('High Tick/Low Tick contracts have a strict duration of five ticks.'),
+ ],
+ },
+ },
+};
diff --git a/src/javascript/app/pages/bottom/explanation.jsx b/src/javascript/app/pages/bottom/explanation.jsx
new file mode 100644
index 00000000000..5e8ddf5b4d2
--- /dev/null
+++ b/src/javascript/app/pages/bottom/explanation.jsx
@@ -0,0 +1,223 @@
+import React, { useEffect, useState } from 'react';
+import parse from 'html-react-parser';
+import { SectionMessage, Skeleton, Text } from '@deriv-com/quill-ui';
+import { contractExplanationData } from './data/explanation.js';
+import Language from '../../../_common/language';
+import Url from '../../../_common/url';
+import { localize } from '../../../_common/localize.js';
+import dataManager from '../../common/data_manager.js';
+import { useContractChange } from '../../hooks/events.js';
+
+export const Explanation = ({ explanationOnly = false }) => {
+
+ const [formName,setFormName] = useState(null);
+
+ const hasContractChanges = useContractChange();
+
+ useEffect(() => {
+ const actualFormName = dataManager.getContract('explanationFormName');
+ setFormName(null);
+
+ setTimeout(() => {
+ setFormName(actualFormName);
+ }, 500);
+
+ }, [hasContractChanges]);
+
+ const language = Language.get();
+ const image_path = Url.urlForStatic(
+ `images/pages/trade-explanation/${language}/`
+ );
+ const Notes = () => (
+ <>
+ {localize('Note')}:
+ {contractExplanationData.note[formName].content.map((data, idx) => (
+ {parse(data)}
+ ))}
+ >
+ );
+
+ const images = {
+ risefall: {
+ image1: 'rises.svg',
+ image2: 'falls.svg',
+ },
+ higherlower: {
+ image1: 'higher.svg',
+ image2: 'lower.svg',
+ },
+ touchnotouch: {
+ image1: 'touch.svg',
+ image2: 'no-touch.svg',
+ },
+ endsinout: {
+ image1: 'ends-between.svg',
+ image2: 'ends-outside.svg',
+ },
+ staysinout: {
+ image1: 'stays-between.svg',
+ image2: 'goes-outside.svg',
+ },
+ digits: {
+ image1: 'matches.svg',
+ image2: 'differs.svg',
+ },
+ evenodd: {
+ image1: 'even.svg',
+ image2: 'odd.svg',
+ },
+ overunder: {
+ image1: 'over.svg',
+ image2: 'under.svg',
+ },
+ lookbackhigh: {
+ image1: 'high-close.svg',
+ },
+ lookbacklow: {
+ image1: 'close-low.svg',
+ },
+ lookbackhighlow: {
+ image1: 'high-low.svg',
+ },
+ reset: {
+ image1: 'reset-call.svg',
+ image2: 'reset-put.svg',
+ },
+ highlowticks: {
+ image1: 'high-tick.svg',
+ image2: 'low-tick.svg',
+ },
+ runs: {
+ image1: 'only-ups.svg',
+ image2: 'only-downs.svg',
+ },
+ };
+
+ if (formName) {
+ return (
+