From 44cdfba6fc26daedf55cb33d89cccbae172e9a70 Mon Sep 17 00:00:00 2001 From: mayuran-deriv Date: Mon, 13 Jan 2025 16:44:40 +0400 Subject: [PATCH 1/4] fix: validations and api calls --- .../TradingComponents/Chart/Chart.tsx | 3 +- .../DurationSection/DurationSection.tsx | 2 +- .../TradingPanel/TradingPanel.scss | 9 +- .../TradingPanel/TradingPanel.tsx | 103 ++++++++++-------- src/hooks/useContractsFor.ts | 32 ++++++ src/hooks/usePriceProposal.ts | 14 ++- src/stores/ChartStore.ts | 4 + src/stores/TradingPanelStore.ts | 11 +- 8 files changed, 123 insertions(+), 55 deletions(-) create mode 100644 src/hooks/useContractsFor.ts diff --git a/src/components/TradingComponents/Chart/Chart.tsx b/src/components/TradingComponents/Chart/Chart.tsx index 0019f4f..c4ac5d9 100644 --- a/src/components/TradingComponents/Chart/Chart.tsx +++ b/src/components/TradingComponents/Chart/Chart.tsx @@ -7,6 +7,7 @@ import { ChartSettings } from "../types"; import { observer } from "mobx-react-lite"; import { chartStore } from "../../../stores/ChartStore"; import { ReconnectingLoader } from "../../ReconnectingLoader/ReconnectingLoader"; +import { useContractsFor } from "../../../hooks/useContractsFor"; import "./Chart.scss"; export const Chart = observer(() => { @@ -20,7 +21,7 @@ export const Chart = observer(() => { } = chartStore; const derivAPI = getDerivAPI(); const [isConnected, setIsConnected] = useState(derivAPI.isConnected()); - + useContractsFor(symbol); // Initialize chart store useEffect(() => { setSymbol("1HZ10V"); diff --git a/src/components/TradingComponents/DurationSection/DurationSection.tsx b/src/components/TradingComponents/DurationSection/DurationSection.tsx index 47539ee..efa7946 100644 --- a/src/components/TradingComponents/DurationSection/DurationSection.tsx +++ b/src/components/TradingComponents/DurationSection/DurationSection.tsx @@ -56,7 +56,7 @@ export const DurationSection = ({ message={durationError} /> - Range: 1 - 1,440 minutes + Range: 15 - 1,440 minutes ) : ( diff --git a/src/components/TradingComponents/TradingPanel/TradingPanel.scss b/src/components/TradingComponents/TradingPanel/TradingPanel.scss index 75bd3f5..e7e7877 100644 --- a/src/components/TradingComponents/TradingPanel/TradingPanel.scss +++ b/src/components/TradingComponents/TradingPanel/TradingPanel.scss @@ -81,8 +81,13 @@ .equals-section { display: flex; align-items: center; - gap: 8px; - padding: 12px 0; + gap: 0.5rem; + margin-bottom: 1rem; + } + + .error-message { + margin: 1rem; + text-align: center; } .payout-section { diff --git a/src/components/TradingComponents/TradingPanel/TradingPanel.tsx b/src/components/TradingComponents/TradingPanel/TradingPanel.tsx index 4a91a72..c5128a1 100644 --- a/src/components/TradingComponents/TradingPanel/TradingPanel.tsx +++ b/src/components/TradingComponents/TradingPanel/TradingPanel.tsx @@ -29,6 +29,7 @@ export const TradingPanel = observer(() => { setSelectedStakeTab, durationError, priceError, + is_rise_fall_valid, } = tradingPanelStore; const { proposal, clearProposal, isLoading } = usePriceProposal( @@ -88,54 +89,66 @@ export const TradingPanel = observer(() => { -
- - -
+ {!is_rise_fall_valid ? ( +
+ + This contract type is not valid for Rise/Fall + +
+ ) : ( + <> +
+ + +
-
- - setAllowEquals((e.target as HTMLInputElement).checked) - } - label="Allow equals" - /> - - - -
+ {/* Equals section temporarily hidden +
+ + setAllowEquals((e.target as HTMLInputElement).checked) + } + label="Allow equals" + /> + + + +
+ */} -
- - +
+ + - - -
+ + +
+ + )} ); }); diff --git a/src/hooks/useContractsFor.ts b/src/hooks/useContractsFor.ts new file mode 100644 index 0000000..27e9a04 --- /dev/null +++ b/src/hooks/useContractsFor.ts @@ -0,0 +1,32 @@ +import { useEffect } from "react"; +import { getDerivAPI } from "../services/deriv-api.instance"; +import { tradingPanelStore } from "../stores/TradingPanelStore"; + +export const useContractsFor = (symbol: string) => { + const derivAPI = getDerivAPI(); + + useEffect(() => { + const checkContractsAvailability = async () => { + if (!symbol) return; + + try { + const contractsResponse = await derivAPI.getContractsForSymbol(symbol); + + const hasRiseFallContracts = + contractsResponse.contracts_for.available.some( + (contract) => + contract.contract_category === "callput" && + contract.barriers === 0 && + contract.barrier_category === "euro_atm" + ); + + tradingPanelStore.setIsRiseFallValid(hasRiseFallContracts); + } catch (error) { + console.error("Error fetching contracts:", error); + tradingPanelStore.setIsRiseFallValid(false); + } + }; + + checkContractsAvailability(); + }, [symbol, derivAPI]); +}; diff --git a/src/hooks/usePriceProposal.ts b/src/hooks/usePriceProposal.ts index f8d4249..e72e94b 100644 --- a/src/hooks/usePriceProposal.ts +++ b/src/hooks/usePriceProposal.ts @@ -2,6 +2,7 @@ import { useEffect, useState } from "react"; import { getDerivAPI } from "../services/deriv-api.instance"; import { PriceProposalResponse } from "../types/deriv-api.types"; import useDebounce from "./useDebounce"; +import { tradingPanelStore } from "../stores/TradingPanelStore"; interface ProposalState { rise?: PriceProposalResponse["proposal"]; @@ -18,8 +19,8 @@ export const usePriceProposal = ( duration: number, basis: string, symbol: string, - durationError: string, - priceError: string + durationError: string | null, + priceError: string | null ) => { const [proposal, setProposal] = useState({}); const [isLoading, setIsLoading] = useState({ @@ -71,15 +72,22 @@ export const usePriceProposal = ( setIsLoading((prev) => ({ ...prev, [type]: false })); if (response.error) { console.error(`${type} proposal error:`, response.error); + tradingPanelStore.priceError = + response.error.message || "Invalid contract parameters"; } else if (response.proposal) { setProposal((prev) => ({ ...prev, [type]: response.proposal })); } }, `PROPOSAL_${type.toUpperCase()}` ); - } catch (err) { + } catch (err: any) { console.error(`${type} subscription error:`, err); setIsLoading((prev) => ({ ...prev, [type]: false })); + + if (err.code === "ContractBuyValidationError") { + tradingPanelStore.priceError = + err.message || "Invalid contract parameters"; + } } }; diff --git a/src/stores/ChartStore.ts b/src/stores/ChartStore.ts index a646805..0d4d46b 100644 --- a/src/stores/ChartStore.ts +++ b/src/stores/ChartStore.ts @@ -1,4 +1,5 @@ import { makeAutoObservable } from "mobx"; +import { tradingPanelStore } from "./TradingPanelStore"; export class ChartStore { symbol: string = ""; @@ -10,7 +11,10 @@ export class ChartStore { } setSymbol = (symbol: string) => { + tradingPanelStore.setIsRiseFallValid(false); this.symbol = symbol; + tradingPanelStore.priceError = null; + tradingPanelStore.durationError = null; }; setChartStatus = (status: boolean) => { diff --git a/src/stores/TradingPanelStore.ts b/src/stores/TradingPanelStore.ts index b1146d1..61e4bc2 100644 --- a/src/stores/TradingPanelStore.ts +++ b/src/stores/TradingPanelStore.ts @@ -1,13 +1,14 @@ import { makeAutoObservable } from "mobx"; export class TradingPanelStore { - duration = 1; + duration = 15; price = "10"; allowEquals = false; selectedDurationTab: "duration" | "endtime" = "duration"; selectedStakeTab: "stake" | "payout" = "stake"; durationError: string | null = null; priceError: string | null = null; + is_rise_fall_valid: boolean = true; constructor() { makeAutoObservable(this); @@ -26,8 +27,8 @@ export class TradingPanelStore { return; } - if (numValue <= 0) { - this.durationError = "Duration must be greater than 0"; + if (numValue < 15) { + this.durationError = "Minimum duration is 15 minutes"; return; } @@ -73,6 +74,10 @@ export class TradingPanelStore { setSelectedStakeTab = (value: "stake" | "payout") => { this.selectedStakeTab = value; }; + + setIsRiseFallValid = (value: boolean) => { + this.is_rise_fall_valid = value; + }; } export const tradingPanelStore = new TradingPanelStore(); From caf58eab3b8111c1573eec2280639ed9d860448f Mon Sep 17 00:00:00 2001 From: mayuran-deriv Date: Mon, 13 Jan 2025 18:13:22 +0400 Subject: [PATCH 2/4] fix: added is rse fall as a dependency for price proposal --- .../TradingPanel/TradingPanel.tsx | 3 ++- src/hooks/usePriceProposal.ts | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/components/TradingComponents/TradingPanel/TradingPanel.tsx b/src/components/TradingComponents/TradingPanel/TradingPanel.tsx index c5128a1..af242f1 100644 --- a/src/components/TradingComponents/TradingPanel/TradingPanel.tsx +++ b/src/components/TradingComponents/TradingPanel/TradingPanel.tsx @@ -38,7 +38,8 @@ export const TradingPanel = observer(() => { selectedStakeTab, chartStore.symbol, durationError, - priceError + priceError, + is_rise_fall_valid ); const getAmount = (type: "rise" | "fall"): string => { diff --git a/src/hooks/usePriceProposal.ts b/src/hooks/usePriceProposal.ts index e72e94b..bba521e 100644 --- a/src/hooks/usePriceProposal.ts +++ b/src/hooks/usePriceProposal.ts @@ -20,7 +20,8 @@ export const usePriceProposal = ( basis: string, symbol: string, durationError: string | null, - priceError: string | null + priceError: string | null, + is_rise_fall_valid: boolean ) => { const [proposal, setProposal] = useState({}); const [isLoading, setIsLoading] = useState({ @@ -99,18 +100,26 @@ export const usePriceProposal = ( setProposal({}); setIsLoading({ rise: false, fall: false }); - + console.log(is_rise_fall_valid, "is_rise_fall_valid"); if ( debouncedPrice && duration && basis && symbol && !durationError && - !priceError + !priceError && + is_rise_fall_valid ) { handleProposal(); } - }, [debouncedPrice, debouncedDuration, basis, symbol, derivAPI]); + }, [ + debouncedPrice, + debouncedDuration, + basis, + symbol, + derivAPI, + is_rise_fall_valid, + ]); return { proposal, clearProposal, isLoading }; }; From 4dff02018711c21cb45ce7ca2449332e88344e98 Mon Sep 17 00:00:00 2001 From: mayuran-deriv Date: Mon, 13 Jan 2025 18:42:28 +0400 Subject: [PATCH 3/4] fix: common hook created --- .../TradingComponents/Chart/Chart.tsx | 2 -- .../TradingPanel/TradingPanel.tsx | 9 +++--- src/hooks/useRiseFallTrading.ts | 30 +++++++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 src/hooks/useRiseFallTrading.ts diff --git a/src/components/TradingComponents/Chart/Chart.tsx b/src/components/TradingComponents/Chart/Chart.tsx index c4ac5d9..e448523 100644 --- a/src/components/TradingComponents/Chart/Chart.tsx +++ b/src/components/TradingComponents/Chart/Chart.tsx @@ -7,7 +7,6 @@ import { ChartSettings } from "../types"; import { observer } from "mobx-react-lite"; import { chartStore } from "../../../stores/ChartStore"; import { ReconnectingLoader } from "../../ReconnectingLoader/ReconnectingLoader"; -import { useContractsFor } from "../../../hooks/useContractsFor"; import "./Chart.scss"; export const Chart = observer(() => { @@ -21,7 +20,6 @@ export const Chart = observer(() => { } = chartStore; const derivAPI = getDerivAPI(); const [isConnected, setIsConnected] = useState(derivAPI.isConnected()); - useContractsFor(symbol); // Initialize chart store useEffect(() => { setSymbol("1HZ10V"); diff --git a/src/components/TradingComponents/TradingPanel/TradingPanel.tsx b/src/components/TradingComponents/TradingPanel/TradingPanel.tsx index af242f1..0667334 100644 --- a/src/components/TradingComponents/TradingPanel/TradingPanel.tsx +++ b/src/components/TradingComponents/TradingPanel/TradingPanel.tsx @@ -1,14 +1,13 @@ -import { Button, Text, Tooltip, Checkbox } from "@deriv-com/quill-ui"; +import { Button, Text } from "@deriv-com/quill-ui"; import { LabelPairedBackwardSmBoldIcon, TradeTypesUpsAndDownsRiseIcon, TradeTypesUpsAndDownsFallIcon, - LabelPairedCircleInfoSmBoldIcon, } from "@deriv/quill-icons"; import { observer } from "mobx-react-lite"; import { tradingPanelStore } from "../../../stores/TradingPanelStore"; import { chartStore } from "../../../stores/ChartStore"; -import { usePriceProposal } from "../../../hooks/usePriceProposal"; +import { useRiseFallTrading } from "../../../hooks/useRiseFallTrading"; import { DurationSection } from "../DurationSection/DurationSection"; import { StakeSection } from "../StakeSection/StakeSection"; import { TradeButton } from "../TradeButton/TradeButton"; @@ -32,11 +31,11 @@ export const TradingPanel = observer(() => { is_rise_fall_valid, } = tradingPanelStore; - const { proposal, clearProposal, isLoading } = usePriceProposal( + const { proposal, clearProposal, isLoading } = useRiseFallTrading( + chartStore.symbol, price, duration, selectedStakeTab, - chartStore.symbol, durationError, priceError, is_rise_fall_valid diff --git a/src/hooks/useRiseFallTrading.ts b/src/hooks/useRiseFallTrading.ts new file mode 100644 index 0000000..fb5e41b --- /dev/null +++ b/src/hooks/useRiseFallTrading.ts @@ -0,0 +1,30 @@ +import { useContractsFor } from "./useContractsFor"; +import { usePriceProposal } from "./usePriceProposal"; + +export const useRiseFallTrading = ( + symbol: string, + price: string, + duration: number, + basis: string, + durationError: string | null, + priceError: string | null, + isRiseFallValid: boolean +) => { + // Use both hooks internally + useContractsFor(symbol); + const { proposal, clearProposal, isLoading } = usePriceProposal( + price, + duration, + basis, + symbol, + durationError, + priceError, + isRiseFallValid + ); + + return { + proposal, + clearProposal, + isLoading, + }; +}; From 4c6f799a84436cd6f5c2ac701cd68ee1c82c37df Mon Sep 17 00:00:00 2001 From: mayuran-deriv Date: Tue, 14 Jan 2025 11:13:58 +0400 Subject: [PATCH 4/4] fix: logs --- src/hooks/usePriceProposal.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/hooks/usePriceProposal.ts b/src/hooks/usePriceProposal.ts index bba521e..3315c17 100644 --- a/src/hooks/usePriceProposal.ts +++ b/src/hooks/usePriceProposal.ts @@ -100,7 +100,6 @@ export const usePriceProposal = ( setProposal({}); setIsLoading({ rise: false, fall: false }); - console.log(is_rise_fall_valid, "is_rise_fall_valid"); if ( debouncedPrice && duration &&