diff --git a/src/components/TradingComponents/Chart/Chart.tsx b/src/components/TradingComponents/Chart/Chart.tsx index 0019f4f..e448523 100644 --- a/src/components/TradingComponents/Chart/Chart.tsx +++ b/src/components/TradingComponents/Chart/Chart.tsx @@ -20,7 +20,6 @@ export const Chart = observer(() => { } = chartStore; const derivAPI = getDerivAPI(); const [isConnected, setIsConnected] = useState(derivAPI.isConnected()); - // 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..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"; @@ -29,15 +28,17 @@ export const TradingPanel = observer(() => { setSelectedStakeTab, durationError, priceError, + is_rise_fall_valid, } = tradingPanelStore; - const { proposal, clearProposal, isLoading } = usePriceProposal( + const { proposal, clearProposal, isLoading } = useRiseFallTrading( + chartStore.symbol, price, duration, selectedStakeTab, - chartStore.symbol, durationError, - priceError + priceError, + is_rise_fall_valid ); const getAmount = (type: "rise" | "fall"): string => { @@ -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..3315c17 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,9 @@ export const usePriceProposal = ( duration: number, basis: string, symbol: string, - durationError: string, - priceError: string + durationError: string | null, + priceError: string | null, + is_rise_fall_valid: boolean ) => { const [proposal, setProposal] = useState({}); const [isLoading, setIsLoading] = useState({ @@ -71,15 +73,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"; + } } }; @@ -91,18 +100,25 @@ export const usePriceProposal = ( setProposal({}); setIsLoading({ rise: false, fall: false }); - 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 }; }; 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, + }; +}; 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();