From 9ca4f140cdd642ed9c530d4b1a850083681712e4 Mon Sep 17 00:00:00 2001 From: kingsley Date: Mon, 25 Nov 2024 11:21:59 +0100 Subject: [PATCH 01/18] Backtest results & connect to exchange screen --- src/assets/icons/index.ts | 4 + src/assets/icons/link.svg | 6 + src/assets/icons/pointer-bottom.svg | 3 + src/components/ui/CustomBtn.tsx | 36 +- .../components/CustomDatePicker.tsx | 11 +- .../overview-bots/components/CustomText.tsx | 4 +- .../components/GroupedConfig.tsx | 4 +- .../overview-bots/components/Tooltip.tsx | 12 +- .../overview-bots/components/Training.tsx | 323 +++++++++++++----- tailwind.config.cjs | 4 + 10 files changed, 292 insertions(+), 115 deletions(-) create mode 100644 src/assets/icons/link.svg create mode 100644 src/assets/icons/pointer-bottom.svg diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index d384e87..3f4176c 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -46,6 +46,8 @@ import { ReactComponent as ArrowRightIcon } from '@assets/icons/arrow-right.svg' import { ReactComponent as CalendarIcon } from '@assets/icons/Calendar.svg'; import { ReactComponent as MetaMaskIcon } from '@assets/icons/Metamask.svg'; import { ReactComponent as PointerIcon } from '@assets/icons/pointer.svg'; +import { ReactComponent as PointerBottomIcon } from '@assets/icons/pointer-bottom.svg'; +import { ReactComponent as LinkIcon } from '@assets/icons/link.svg'; export { AlarmClockIcon, @@ -96,4 +98,6 @@ export { CalendarIcon, MetaMaskIcon, PointerIcon, + PointerBottomIcon, + LinkIcon }; diff --git a/src/assets/icons/link.svg b/src/assets/icons/link.svg new file mode 100644 index 0000000..effc136 --- /dev/null +++ b/src/assets/icons/link.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/pointer-bottom.svg b/src/assets/icons/pointer-bottom.svg new file mode 100644 index 0000000..922a4fd --- /dev/null +++ b/src/assets/icons/pointer-bottom.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/ui/CustomBtn.tsx b/src/components/ui/CustomBtn.tsx index b3a2f70..988268b 100644 --- a/src/components/ui/CustomBtn.tsx +++ b/src/components/ui/CustomBtn.tsx @@ -64,20 +64,28 @@ const CustomBtn: React.FC = ({ fullWidth, ...rest }) => { - const btnClassnames = classNames( - `flex justify-center items-center relative px-5 bg-navy !text-white text-center border disabled:opacity-40 border-transparent rounded-[76px] whitespace-nowrap disabled:cursor-not-allowed transition-colors duration-300`, - { - 'h-[1.4375rem] text-xs font-normal px-2 py-1': size === 'sm', - 'h-[2.75rem] py-[9px] text-base font-semibold': size === 'lg', - 'bg-transparent border-navy !text-navy hover:!border-blue-300 hover:!text-blue-300 disabled:!text-gray-500 disabled:border-light-400 disabled:hover:!bg-inherit disabled:hover:!border-inherit': - btnStyle === 'outline-primary', - 'bg-transparent border-red-100 !text-red-100 hover:!bg-red-100 hover:!text-white': - btnStyle === 'outline-secondary', - 'hover:!bg-blue-300 hover:!text-light-100': btnStyle === 'solid-primary', - 'w-full': fullWidth, - }, - `${xtraStyles || ''}` - ); + const btnClassnames = ` + flex justify-center items-center relative px-5 !text-white text-center border disabled:opacity-40 border-transparent rounded-[76px] whitespace-nowrap disabled:cursor-not-allowed transition-colors duration-300 + ${size === 'sm' ? 'h-[1.4375rem] text-xs font-normal px-2 py-1' : ''} + ${size === 'lg' ? 'h-[2.75rem] py-[9px] text-base font-semibold' : ''} + ${ + btnStyle === 'outline-primary' + ? '!bg-transparent border-navy !text-navy hover:!border-blue-300 hover:!text-blue-300 disabled:!text-gray-500 disabled:border-light-400 disabled:hover:!bg-inherit disabled:hover:!border-inherit' + : '' + } + ${ + btnStyle === 'outline-secondary' + ? 'bg-transparent border-red-100 !text-red-100 hover:!bg-red-100 hover:!text-white' + : '' + } + ${ + btnStyle === 'solid-primary' + ? 'bg-gradient-to-r from-blue-500 to-dark-500 hover:bg-gradient-to-t hover:from-blue-600 hover:to-dark-600 hover:!text-turkish' + : '' + } + ${fullWidth ? 'w-full' : ''} + ${xtraStyles || ''} + `.trim(); return ( @@ -86,7 +86,7 @@ const NumberInput: React.FC = ({ data }) => { return (
@@ -106,7 +106,7 @@ const NumberInput: React.FC = ({ data }) => { placeholder={ numbers.length === 0 ? 'Input a value and press Enter' : '' } - className={`flex-none bg-transparent pl-1 border-none outline-none placeholder:text-blue-200 ${ + className={`flex-none bg-transparent pl-1 border-none text-sm font-normal outline-none placeholder:text-blue-200 ${ numbers.length === 0 ? 'w-full' : 'max-w-[10ch]' }`} /> diff --git a/src/pages/overview-bots/components/Overview.tsx b/src/pages/overview-bots/components/Overview.tsx index 912ea77..79451cc 100644 --- a/src/pages/overview-bots/components/Overview.tsx +++ b/src/pages/overview-bots/components/Overview.tsx @@ -16,6 +16,7 @@ import { ITabs, ITimeTab, IChatTab, + ITimeBTab, } from '../hooks/useProfile'; import Performance from './Performance'; import CryptoStats from './CryptoStats'; @@ -24,15 +25,16 @@ import Switcher from './Switcher'; import PnLChart from './PnLChart'; import LineTab from './LineTab'; import PnLineChart from './PnLineChart'; +import { truncateText } from '@utils/truncateText.util'; interface IOverviewProps { dateTabs: IDateTabs[]; dateQuery: string; - timeQuery: ITimeTab; + timeBQuery: ITimeBTab; chartTypeQuery: IChatTab; cryptoQuery: ICryptoTab; perfQuery: IPerfTab; - timeTabs: ITabs[]; + timeBTabs: ITabs[]; perfTabs: ITabs[]; cryptoTabs: ITabs[]; chartTypeTabsB: ITabs[]; @@ -47,14 +49,14 @@ interface IOverviewProps { const Overview: React.FC = ({ dateTabs, - timeTabs, + timeBTabs, perfTabs, cryptoTabs, dateQuery, cryptoQuery, chartTypeQuery, chartTypeTabsB, - timeQuery, + timeBQuery, perfQuery, statsData, statsDataOTN, @@ -109,14 +111,18 @@ const Overview: React.FC = ({ id="overview_header" className="flex flex-col md:flex-row gap-y-3 md:gap-y-3 justify-between items-center mt-5" > -

- Portfolio:{' '} - {isEmpty ? ( - $0 (0%) - ) : ( - $19 349 (+20%) - )} -

+
+

Overview

+

+ Portfolio:{' '} + {isEmpty ? ( + $0 (0%) + ) : ( + $19 349 (+20%) + )} +

+
+ = ({ />
- -
- - -
+
{isEmpty ? ( -
+
+ Portfolio +
) : ( cryptoStats.map((data, i) => { const total = cryptoStats.reduce( @@ -152,13 +150,39 @@ const Overview: React.FC = ({
+ className="h-full px-3 flex items-center" + > +
+

+ {`${ + data.percentage <= 5 + ? truncateText(data.tag, 3) + : data.tag + }`} +

+

{`${ + data.percentage <= 5 + ? truncateText( + `$${data.amount} (${data.percentage}%)`, + 3 + ) + : `$${data.amount} (${data.percentage}%)` + }`}

+
+
); }) )}
+ +
= ({ isEmpty={isEmpty} />
@@ -191,33 +214,33 @@ const Overview: React.FC = ({ )} - {isEmpty ? ( -
- ) : ( - + - )} +
-
-
+ ) : ( + -
-
+ )} +
@@ -225,7 +248,9 @@ const Overview: React.FC = ({
{isEmpty ? ( -
+
+ P&L CHART +
) : (
{getChartTypeQuery === 'pnl' && ( diff --git a/src/pages/overview-bots/components/StatsTable.tsx b/src/pages/overview-bots/components/StatsTable.tsx index a312e6c..1b1329a 100644 --- a/src/pages/overview-bots/components/StatsTable.tsx +++ b/src/pages/overview-bots/components/StatsTable.tsx @@ -1,5 +1,5 @@ -import CustomBtn from '@components/ui/CustomBtn'; import { IStatsTableData } from '../hooks/useProfile'; +import CustomBtn from '@components/ui/CustomBtn'; import MiniLineChart from './MiniLineChart'; import CustomText from './CustomText'; @@ -51,6 +51,7 @@ const StatsTable: React.FC = ({ text={stat.label} hasQuestionMark={hasQuestionMark} toolTipText={stat.toolTipText} + isEmpty={isEmpty} />
@@ -61,7 +62,7 @@ const StatsTable: React.FC = ({
{/* Chart */} {isEmpty && !stat.progressValue ? ( -
+
) : ( stat.chartData && (
diff --git a/src/pages/overview-bots/hooks/useProfile.ts b/src/pages/overview-bots/hooks/useProfile.ts index a6abde6..3bf2fef 100644 --- a/src/pages/overview-bots/hooks/useProfile.ts +++ b/src/pages/overview-bots/hooks/useProfile.ts @@ -15,6 +15,7 @@ import { export type ITab = 'overview' | 'datasets' | 'training' | 'bots' | 'tutorial'; export type ITimeTab = 'minute' | 'hour' | 'day' | 'week' | 'month'; +export type ITimeBTab = 'hourly' | 'daily' | 'weekly'; export type ICryptoTab = 'all' | 'big' | 'trade' | 'alpha' | 'moon'; export type IDateTab = 'day' | 'week' | 'month' | 'time'; export type IStratTab = 'strat' | 'hyper'; @@ -26,6 +27,7 @@ export interface ITabs { key: | ITab | ITimeTab + | ITimeBTab | IDateTab | ICryptoTab | IStratTab @@ -63,6 +65,7 @@ export interface ICardBotData { name: string; rate: number; isPositive: boolean; + color?: string; pieChartData: ICryptoStats[]; lineChartData: number[] | null; tableData: { @@ -111,7 +114,7 @@ export interface IDepositInfo { export default () => { const { address } = useAppSelector((state) => state.auth); - const { coinValue, endDate } = useAppSelector((state) => state.general); + const { coinValue, endDate, expenses, fee } = useAppSelector((state) => state.general); const [searchParams, setSearchParams] = useSearchParams(); const { setTitle } = usePageTitle(); const [search, setSearch] = useState(''); @@ -120,6 +123,7 @@ export default () => { const cryptoQuery = (searchParams.get('crypto') as ICryptoTab) || 'all'; const tradeDateQuery = (searchParams.get('trade_date') as IDateTab) || 'day'; const timeQuery = (searchParams.get('time') as ITimeTab) || 'day'; + const timeBQuery = (searchParams.get('timeb') as ITimeBTab) || 'daily'; const perfQuery = (searchParams.get('perf') as IPerfTab) || 'best'; const stratQuery = (searchParams.get('strat') as IStratTab) || 'strat'; const resultStatQuery = @@ -198,6 +202,12 @@ export default () => { { key: 'month', name: '1M', icon: null }, ]; + const timeBTabs: ITabs[] = [ + { key: 'hourly', name: 'Hourly', icon: null }, + { key: 'daily', name: 'Daily', icon: null }, + { key: 'weekly', name: 'Weekly', icon: null }, + ]; + const perfTabs: ITabs[] = [ { key: 'best', name: 'Best Performance', icon: null }, { key: 'worst', name: 'Worst Performance', icon: null }, @@ -230,34 +240,34 @@ export default () => { { amount: 9186, tag: 'Big Brain', - percentage: 20, + percentage: 47, value: null, isProfit: true, - color: '#3AA8F0', + color: '#FFDDD3', }, { amount: 7036, tag: 'Trade Genius', - percentage: 11, + percentage: 36, value: null, isProfit: true, - color: '#1F609C', + color: '#D7CEE3', }, { amount: 3127, tag: 'Alpha Trader', - percentage: 1, + percentage: 14, value: null, isProfit: false, - color: '#4AB6C4', + color: '#D4E6FC', }, { amount: 550, tag: 'Moon Space', - percentage: 3, + percentage: 1, value: null, isProfit: false, - color: '#2788B2', + color: '#F7D7E6', }, ]; @@ -356,23 +366,14 @@ export default () => { 'Represents the total value of all assets traded by your bots during the selected period, providing insight into your trading activity.', }, { - label: 'Successful', - value: '133', + label: 'Average profit', + value: '$15,63', chartData: [98, 40, 60, 38, 42, 46, 40, 90, 95, 50], progressValue: null, color: '#F44336', toolTipText: 'The number of all executed buy and sell orders by your bots during the selected period, showing the overall trading activity.', }, - { - label: 'Failed', - value: '117', - chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], - progressValue: null, - color: '#4CAF50', - toolTipText: - 'The percentage of successful trades made by your bots, indicating how often their predictions were correct.', - }, { label: 'Total accuracy', value: '53%', @@ -436,18 +437,9 @@ export default () => { ]; const statsDataOTN: IStatsTableData[] = [ - { - label: 'OTN Balance', - value: '550', - chartData: null, - progressValue: 50, - color: '', - toolTipText: - "The amount of OTN (Robotter's native token) you hold. Staking OTN can reduce your trading fees and unlock additional rewards for increased profitability.", - }, { label: 'Compute costs', - value: '$150', + value: '$24', chartData: [98, 40, 60, 38, 42, 46, 40, 90, 95, 50], progressValue: null, color: '#F44336', @@ -464,7 +456,7 @@ export default () => { 'The average fee charged for placing limit orders that add liquidity to the market. Lower maker fees can reduce your overall trading costs.', }, { - label: 'Av taker fee', + label: 'Av. taker fee', value: '3%', chartData: null, progressValue: 30, @@ -472,6 +464,14 @@ export default () => { toolTipText: 'The average fee charged for executing market orders that remove liquidity from the market. Higher taker fees can impact your overall trading profitability.', }, + { + label: 'Fees paid', + value: '$32', + chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], + progressValue: null, + color: '#4CAF50', + toolTipText: null, + }, ]; const statsDataLock: IStatsTableData[] = [ @@ -514,12 +514,13 @@ export default () => { name: 'Big Brain', rate: 1837, isPositive: true, + color: '#FACFC4', pieChartData: [ { amount: 65, tag: 'profit', percentage: 65, - color: '#218358', + color: '#A3E5C8', isProfit: true, value: null, }, @@ -527,7 +528,7 @@ export default () => { amount: 35, tag: 'loss', percentage: 35, - color: '#CE2C31', + color: '#FFAFB2', isProfit: false, value: null, }, @@ -535,20 +536,20 @@ export default () => { lineChartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], tableData: [ { - labelA: [210, 'APR'], - labelB: [42.6, 'SOL'], + labelA: [`$210`, 'Portfolio'], + labelB: ['42.6%', 'Max drawdown'], percentage: 11, isProfit: true, }, { - labelA: [2.81, 'Sharpe ratio'], - labelB: [0.13, 'BTC'], + labelA: [`187%`, 'APY'], + labelB: [0.13, 'Sharpe ratio'], percentage: 10, isProfit: true, }, { labelA: [14, 'Trades'], - labelB: [550, 'OTN'], + labelB: [`2024-12-29`, 'End date'], percentage: 9, isProfit: true, }, @@ -558,12 +559,13 @@ export default () => { name: 'Trade Genius', rate: 773, isPositive: true, + color: '#D7CEE3', pieChartData: [ { amount: 59, tag: 'profit', percentage: 59, - color: '#218358', + color: '#A3E5C8', isProfit: true, value: null, }, @@ -571,7 +573,7 @@ export default () => { amount: 41, tag: 'loss', percentage: 41, - color: '#CE2C31', + color: '#FFAFB2', isProfit: false, value: null, }, @@ -579,20 +581,20 @@ export default () => { lineChartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], tableData: [ { - labelA: [187, 'APR'], - labelB: [2.35, 'SOL'], + labelA: [`$187`, 'Portfolio'], + labelB: [`45%`, 'Max drawdown'], percentage: 16, isProfit: true, }, { - labelA: [2.01, 'Sharpe ratio'], - labelB: [0.0034, 'BTC'], + labelA: [`164%`, 'APY'], + labelB: [0.0034, 'Sharpe ratio'], percentage: 14, isProfit: true, }, { labelA: [36, 'Trades'], - labelB: [83, 'OTN'], + labelB: [`2024-12-29`, 'End date'], percentage: 2, isProfit: true, }, @@ -602,12 +604,13 @@ export default () => { name: 'Alpha Trader', rate: 31, isPositive: false, + color: '#D4E6FC', pieChartData: [ { amount: 49, tag: 'profit', percentage: 49, - color: '#218358', + color: '#A3E5C8', isProfit: true, value: null, }, @@ -615,7 +618,7 @@ export default () => { amount: 51, tag: 'loss', percentage: 51, - color: '#CE2C31', + color: '#FFAFB2', isProfit: true, value: null, }, @@ -623,20 +626,20 @@ export default () => { lineChartData: [90, 85, 80, 70, 60, 65, 75, 76, 95, 80], tableData: [ { - labelA: [165, 'APR'], - labelB: [3.68, 'SOL'], + labelA: [`$165`, 'Portfolio'], + labelB: [`36%`, 'Max drawdown'], percentage: 1, isProfit: true, }, { - labelA: [1.75, 'Sharpe ratio'], - labelB: [0.0001, 'BTC'], + labelA: [`210%`, 'APY'], + labelB: [0.0001, 'Sharpe ratio'], percentage: 8, isProfit: false, }, { labelA: [120, 'Trades'], - labelB: [89, 'OTN'], + labelB: [`2024-12-29`, 'End date'], percentage: 4, isProfit: true, }, @@ -646,12 +649,13 @@ export default () => { name: 'Moon Space', rate: 62, isPositive: false, + color: '#F7D7E6', pieChartData: [ { amount: 49, tag: 'profit', percentage: 49, - color: '#218358', + color: '#A3E5C8', isProfit: true, value: null, }, @@ -659,7 +663,7 @@ export default () => { amount: 51, tag: 'loss', percentage: 51, - color: '#CE2C31', + color: '#FFAFB2', isProfit: true, value: null, }, @@ -667,20 +671,20 @@ export default () => { lineChartData: [90, 85, 80, 70, 60, 65, 75, 76, 95, 80], tableData: [ { - labelA: [102, 'APR'], - labelB: [0.0003, 'ETH'], + labelA: [`$102`, 'Portfolio'], + labelB: [`47%`, 'Max drawdown'], percentage: 2, isProfit: true, }, { - labelA: [2.43, 'Sharpe ratio'], - labelB: [161, 'JUP'], + labelA: [`164%`, 'APY'], + labelB: [161, 'Sharpe ratio'], percentage: 7, isProfit: false, }, { labelA: [6, 'Trades'], - labelB: [258, 'DRIFT'], + labelB: [`2024-12-29`, 'End date'], percentage: 5, isProfit: false, }, @@ -698,7 +702,7 @@ export default () => { amount: 68, tag: 'profit', percentage: 68, - color: '#218358', + color: '#A3E5C8', isProfit: true, value: null, }, @@ -706,7 +710,7 @@ export default () => { amount: 32, tag: 'loss', percentage: 32, - color: '#CE2C31', + color: '#FFAFB2', isProfit: false, value: null, }, @@ -742,7 +746,7 @@ export default () => { amount: 65, tag: 'profit', percentage: 65, - color: '#218358', + color: '#A3E5C8', isProfit: true, value: null, }, @@ -750,7 +754,7 @@ export default () => { amount: 35, tag: 'loss', percentage: 35, - color: '#CE2C31', + color: '#FFAFB2', isProfit: false, value: null, }, @@ -786,7 +790,7 @@ export default () => { amount: 48, tag: 'profit', percentage: 48, - color: '#218358', + color: '#A3E5C8', isProfit: true, value: null, }, @@ -794,7 +798,7 @@ export default () => { amount: 52, tag: 'loss', percentage: 52, - color: '#CE2C31', + color: '#FFAFB2', isProfit: false, value: null, }, @@ -830,7 +834,7 @@ export default () => { amount: 46, tag: 'profit', percentage: 46, - color: '#218358', + color: '#A3E5C8', isProfit: true, value: null, }, @@ -838,7 +842,7 @@ export default () => { amount: 54, tag: 'loss', percentage: 54, - color: '#CE2C31', + color: '#FFAFB2', isProfit: false, value: null, }, @@ -978,8 +982,8 @@ export default () => { const depositInfo: IDepositInfo[] = [ { l: 'Market', r: 'SOL / USDC', icon: null }, { l: 'Number of trading days', r: `${numOfTradeDays}`, icon: null }, - { l: 'Compute expenses', r: '$0', icon: null }, - { l: 'Solana fees', r: '$0', icon: null }, + { l: 'Compute expenses', r: `$${expenses}`, icon: null }, + { l: 'Solana fees', r: `$${fee}`, icon: null }, { l: 'SOL', r: `${coinValue.SOL}`, icon: createElement(SolanaLogo) }, { l: 'USDC', r: `${coinValue.USDC}`, icon: createElement(USDCLogo) }, { l: 'Total', r: '$0', icon: null }, @@ -993,6 +997,7 @@ export default () => { tabs, dateTabs, timeTabs, + timeBTabs, cryptoTabs, perfTabs, stratTabs, @@ -1017,6 +1022,7 @@ export default () => { // user, query, dateQuery, + timeBQuery, timeQuery, perfQuery, cryptoQuery, diff --git a/tailwind.config.cjs b/tailwind.config.cjs index bfb7903..ed9e77c 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -3,6 +3,9 @@ module.exports = { content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], theme: { extend: { + fontFamily: { + ubuntumono: ['Ubuntu Mono', 'sans-serif'], + }, boxShadow: { custom: '0px 16px 32px 0px #16253533', }, @@ -24,10 +27,15 @@ module.exports = { 'chart-200': '#1F609C', 'chart-300': '#3AA8F0', 'chart-400': '#2788B2', + 'chart-500': '#FFDDD3', + 'chart-600': '#D7CEE3', + 'chart-700': '#D4E6FC', + 'chart-800': '#F7D7E6', 'green-100': '#218358', 'red-100': '#CE2C31', 'light-20': '#F6F8FB', 'light-200': '#F1FAFD', + 'light-250': '#F5F5F5', 'dark-20': '#91989C', 'dark-40': '#5C6569', 'dark-100': '#84828E', @@ -43,6 +51,7 @@ module.exports = { 'text-dark': '#244141', 'form-bg': '#F6FAFB', states: '#EFB621', + 'yellow-200': '#F6E2AC', turkish: '#80FFED', }, }, From 3056f0f10249765dc919d9b0b0dc1c2d7795837c Mon Sep 17 00:00:00 2001 From: kingsley Date: Mon, 16 Dec 2024 10:54:07 +0100 Subject: [PATCH 08/18] Update UI contd. --- src/assets/styles/index.scss | 1 + .../overview-bots/components/CardBot.tsx | 59 +++++-------- src/pages/overview-bots/components/Header.tsx | 3 +- .../overview-bots/components/Performance.tsx | 7 +- .../overview-bots/components/Training.tsx | 88 +++++++++++-------- src/pages/overview-bots/index.tsx | 6 +- src/slices/generalSlice.ts | 14 ++- src/utils/getDaysBtnDates.util.ts | 34 +++++-- src/utils/truncateText.util.ts | 7 ++ 9 files changed, 135 insertions(+), 84 deletions(-) create mode 100644 src/utils/truncateText.util.ts diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index 75151e9..4f410e4 100644 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -3,6 +3,7 @@ @tailwind utilities; @import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Ubuntu+Mono:ital,wght@0,400;0,700;1,400;1,700&display=swap'); @import './variables'; body { diff --git a/src/pages/overview-bots/components/CardBot.tsx b/src/pages/overview-bots/components/CardBot.tsx index fcf9dab..a0bfe53 100644 --- a/src/pages/overview-bots/components/CardBot.tsx +++ b/src/pages/overview-bots/components/CardBot.tsx @@ -1,5 +1,4 @@ -import { UpIcon, DownIcon } from '@assets/icons'; -import CustomButton from '@components/ui/Button'; +import { UpIcon, DownIcon, BotIcon } from '@assets/icons'; import classNames from 'classnames'; import React from 'react'; import CustomBtn from '@components/ui/CustomBtn'; @@ -11,22 +10,36 @@ interface ICardBotProps { cardBotData: ICardBotData; xtraStyle?: string; isEmpty: boolean; + showSideColor?: boolean; } const CardBot: React.FC = ({ cardBotData, xtraStyle, isEmpty, + showSideColor, }) => { return (
- {!isEmpty && ( -
+ {isEmpty ? ( +
+
+ +

+ CREATE NEW BOT +

+
+
+ ) : ( +
{cardBotData.tableData.map((row, i) => (
@@ -50,19 +63,9 @@ const CardBot: React.FC = ({ }`}

- P&L last week + P&L, last 24h

- {cardBotData.lineChartData && ( -
- -
- )}
@@ -78,8 +81,8 @@ const CardBot: React.FC = ({ )}
-

- {row.labelA[0]}% +

+ {row.labelA[0]}

{row.labelA[1]} @@ -88,28 +91,14 @@ const CardBot: React.FC = ({

-

{row.labelB[0]}

+

{row.labelB[0]}

{row.labelB[1]}

- {row.percentage !== null && ( -
- {row.isProfit ? : } -

- {row.percentage}% -

-
- )}
{i === 0 && ( diff --git a/src/pages/overview-bots/components/Header.tsx b/src/pages/overview-bots/components/Header.tsx index c7dce04..fba14e7 100644 --- a/src/pages/overview-bots/components/Header.tsx +++ b/src/pages/overview-bots/components/Header.tsx @@ -20,12 +20,13 @@ import { IStratTab, ITab, ITabs, + ITimeBTab, ITimeTab, } from '../hooks/useProfile'; import Switcher from './Switcher'; export interface IHeaderProps { - query: ITab | ITimeTab | IDateTab | IStratTab | IChatTab | IPerfTab; + query: ITab | ITimeTab | ITimeBTab | IDateTab | IStratTab | IChatTab | IPerfTab; tabs: ITabs[]; searchParams: URLSearchParams; setSearchParams: SetURLSearchParams; diff --git a/src/pages/overview-bots/components/Performance.tsx b/src/pages/overview-bots/components/Performance.tsx index 57e36dd..b76c154 100644 --- a/src/pages/overview-bots/components/Performance.tsx +++ b/src/pages/overview-bots/components/Performance.tsx @@ -19,11 +19,11 @@ const Performance: React.FC = ({ }) => { return (
-

+

Active Bots

-
+
= ({ key={idx} cardBotData={item} xtraStyle="lg:flex-auto flex-none" + showSideColor /> ))}
@@ -53,7 +54,7 @@ const Performance: React.FC = ({ ].map((icon, idx) => ( {icon} diff --git a/src/pages/overview-bots/components/Training.tsx b/src/pages/overview-bots/components/Training.tsx index 78df832..c555d2b 100644 --- a/src/pages/overview-bots/components/Training.tsx +++ b/src/pages/overview-bots/components/Training.tsx @@ -1,16 +1,12 @@ import { strategiesConfigData as config } from '../../../utils/strategyConfigData'; -import { - ArrowDown2Icon, - ArrowUp2Icon, - MangoLogo, - SolanaLogo, - USDCLogo, -} from '@assets/icons'; +import { getDaysBtnDates, isTodayOrFuture } from '@utils/getDaysBtnDates.util'; +import { useAppDispatch, useAppSelector } from '@shared/hooks/useStore'; import { useGetHistoricalCandlesMutation } from '@store/market/api'; import { defaultType } from '../../../utils/defaultType.util'; import { transformData } from '../../../utils/transformData'; import { updateDefaults } from '../../../utils/updateDefault'; import { SetURLSearchParams } from 'react-router-dom'; +import CustomInput from '@components/ui/CustomInput'; import CandlestickChart from './CandlestickChart'; import CustomDatePicker from './CustomDatePicker'; import CustomBtn from '@components/ui/CustomBtn'; @@ -20,6 +16,13 @@ import { FadeLoader } from 'react-spinners'; import ButtonList from './ButtonList'; import CustomText from './CustomText'; import Pagination from './Pagination'; +import { + ArrowDown2Icon, + ArrowUp2Icon, + MangoLogo, + SolanaLogo, + USDCLogo, +} from '@assets/icons'; import React, { ChangeEvent, Fragment, @@ -40,10 +43,11 @@ import Switcher from './Switcher'; import CardBot from './CardBot'; import GoBack from './GoBack'; import LineTab from './LineTab'; -import CustomInput from '@components/ui/CustomInput'; -import { useAppDispatch, useAppSelector } from '@shared/hooks/useStore'; -import { setCoinValues, setEndDate } from '@slices/generalSlice'; -import { getDaysBtnDates } from '@utils/getDaysBtnDates.util'; +import { + setCoinValues, + setEndDate, + setExpensesFee, +} from '@slices/generalSlice'; export interface ITrainingProps { timeQuery: ITimeTab; @@ -211,12 +215,17 @@ const Training: React.FC = ({ useEffect(() => { handleCandleData(); - }, [tradePair, timeStamp]); + const isEmpty = Object.values(coinValue).every((num) => num !== ''); + dispatch( + setExpensesFee({ expenses: isEmpty ? 8 : 0, fee: isEmpty ? 5 : 0 }) + ); + }, [tradePair, timeStamp, coinValue]); const disabled = currentStep === 3 ? Object.values(coinValue).some((num) => num === '' || +num <= 0) || - numOfTradeDays <= 0 + numOfTradeDays < 0 || + !isTodayOrFuture(timeStamp.endDate) : false; return ( @@ -431,30 +440,39 @@ const Training: React.FC = ({ { icon: , text: 'SOL' }, { icon: , text: 'USDC' }, ].map(({ icon, text }, idx) => ( - -
- - +
+
+
-
- - + +

{text}

<>{icon}{' '} + + } + /> +
+ {['$100', '$300', '$500', 'Max'].map((data, idx) => ( + + {data} + + ))}
- +
))}
diff --git a/src/pages/overview-bots/index.tsx b/src/pages/overview-bots/index.tsx index 080a007..76fc4eb 100644 --- a/src/pages/overview-bots/index.tsx +++ b/src/pages/overview-bots/index.tsx @@ -14,6 +14,7 @@ const OverviewBots: React.FC = () => { tabs, dateTabs, timeTabs, + timeBTabs, perfTabs, stratTabs, cryptoTabs, @@ -33,6 +34,7 @@ const OverviewBots: React.FC = () => { query, dateQuery, timeQuery, + timeBQuery, perfQuery, stratQuery, cryptoQuery, @@ -75,9 +77,9 @@ const OverviewBots: React.FC = () => { dateQuery={dateQuery} cryptoQuery={cryptoQuery} chartTypeQuery={chartTypeQuery} - timeQuery={timeQuery} + timeBQuery={timeBQuery} perfQuery={perfQuery} - timeTabs={timeTabs} + timeBTabs={timeBTabs} perfTabs={perfTabs} cryptoTabs={cryptoTabs} chartTypeTabsB={chartTypeTabsB} diff --git a/src/slices/generalSlice.ts b/src/slices/generalSlice.ts index 15c9b3c..b0ccbcd 100644 --- a/src/slices/generalSlice.ts +++ b/src/slices/generalSlice.ts @@ -2,11 +2,15 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface AppState { endDate: Date | null; + expenses: number; + fee: number; coinValue: { [key: string]: number }; } const initialState: AppState = { endDate: null, + expenses: 0, + fee: 0, coinValue: { SOL: 0, USDC: 0, @@ -26,9 +30,17 @@ export const generalSlice = createSlice({ setEndDate: (state, action: PayloadAction) => { state.endDate = action.payload; }, + setExpensesFee: ( + state, + { payload }: { payload: { expenses: number; fee: number } } + ) => { + state.expenses = payload.expenses; + state.fee = payload.fee; + }, }, }); -export const { setCoinValues, setEndDate } = generalSlice.actions; +export const { setCoinValues, setEndDate, setExpensesFee } = + generalSlice.actions; export default generalSlice; diff --git a/src/utils/getDaysBtnDates.util.ts b/src/utils/getDaysBtnDates.util.ts index e2fc7cd..46f7274 100644 --- a/src/utils/getDaysBtnDates.util.ts +++ b/src/utils/getDaysBtnDates.util.ts @@ -1,20 +1,40 @@ -export const getDaysBtnDates = (date: Date) => { +export const getDaysBtnDates = (date: Date): number => { const currentDate = new Date(); - const normalizeDate = new Date( + // Normalize both dates to the same base day, keeping time intact + const today = new Date( currentDate.getFullYear(), currentDate.getMonth(), - currentDate.getDate() + currentDate.getDate(), + currentDate.getHours(), + currentDate.getMinutes() ); - const normalizeDate2 = new Date( + const targetDate = new Date( date.getFullYear(), date.getMonth(), - date.getDate() + date.getDate(), + date.getHours(), + date.getMinutes() ); - if (normalizeDate2 <= normalizeDate) return 0; + // If the target date is in the past, return 0 + if (targetDate < today) return 0; - const timeDiff = Math.abs(currentDate.getTime() - date.getTime()); + // Calculate the time difference in milliseconds + const timeDiff = targetDate.getTime() - today.getTime(); + + // Convert time difference to days, ensuring fractional days round up return Math.ceil(timeDiff / (1000 * 60 * 60 * 24)); }; + +export const isTodayOrFuture = (date: Date | null): boolean => { + if (!date) return false; + const today = new Date(); + today.setHours(0, 0, 0, 0); // Normalize to start of today for comparison + + const targetDate = new Date(date); + targetDate.setHours(0, 0, 0, 0); // Normalize to start of target date for comparison + + return targetDate >= today; +}; diff --git a/src/utils/truncateText.util.ts b/src/utils/truncateText.util.ts new file mode 100644 index 0000000..d4473e9 --- /dev/null +++ b/src/utils/truncateText.util.ts @@ -0,0 +1,7 @@ +export const truncateText = (text: string, maxLength: number) => { + if (text.length > maxLength) { + return text.substring(0, maxLength - 3) + '...'; + } + + return text; +}; From 80235a06e5bf606cd8400ed47c4e341ce352979e Mon Sep 17 00:00:00 2001 From: kingsley Date: Sat, 21 Dec 2024 15:40:32 +0100 Subject: [PATCH 09/18] add a searcheable functionality --- src/assets/icons/Search24.svg | 4 + src/assets/icons/index.ts | 2 + .../components/CustomDropdown.tsx | 104 ++++++++++++++++-- .../components/GroupedConfig.tsx | 10 +- .../overview-bots/components/Training.tsx | 34 +++--- 5 files changed, 128 insertions(+), 26 deletions(-) create mode 100644 src/assets/icons/Search24.svg diff --git a/src/assets/icons/Search24.svg b/src/assets/icons/Search24.svg new file mode 100644 index 0000000..3125ab4 --- /dev/null +++ b/src/assets/icons/Search24.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 78d5fc2..fb5a663 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -16,6 +16,7 @@ import { ReactComponent as FolderIcon } from '@assets/icons/folder.svg'; import { ReactComponent as BellIcon } from '@assets/icons/bell.svg'; import { ReactComponent as ExportIcon } from '@assets/icons/export.svg'; import { ReactComponent as SearchIcon } from '@assets/icons/search.svg'; +import { ReactComponent as Search24Icon } from '@assets/icons/Search24.svg'; import { ReactComponent as AttachIcon } from '@assets/icons/attach.svg'; import { ReactComponent as EditIcon } from '@assets/icons/edit.svg'; import { ReactComponent as HomeIcon } from '@assets/icons/home.svg'; @@ -78,6 +79,7 @@ export { BellIcon, ExportIcon, SearchIcon, + Search24Icon, AttachIcon, EditIcon, HomeIcon, diff --git a/src/pages/overview-bots/components/CustomDropdown.tsx b/src/pages/overview-bots/components/CustomDropdown.tsx index 3654d11..4543f21 100644 --- a/src/pages/overview-bots/components/CustomDropdown.tsx +++ b/src/pages/overview-bots/components/CustomDropdown.tsx @@ -1,10 +1,16 @@ -import { ArrowDown2Icon, ArrowUp2Icon, PointerIcon } from '@assets/icons'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { ChangeEvent, useEffect, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; +import { + ArrowDown2Icon, + ArrowUp2Icon, + PointerIcon, + Search24Icon, +} from '@assets/icons'; -interface Option { +export interface Option { label: string; value: string; + tags?: string[]; } interface ICustomDropdownProps { @@ -13,6 +19,7 @@ interface ICustomDropdownProps { placeholder?: string; disabled?: boolean; showTooTip?: boolean; + isSearchable?: boolean; } const CustomDropdown: React.FC = ({ @@ -21,6 +28,7 @@ const CustomDropdown: React.FC = ({ placeholder, disabled, showTooTip, + isSearchable, }) => { const dropDownRef = useRef(null); const [isOpen, setIsOpen] = useState(false); @@ -29,6 +37,8 @@ const CustomDropdown: React.FC = ({ placeholder || (options.length > 0 ? options[0].label : 'Select Option') ); const [hoveredIndex, setHoveredIndex] = useState(null); + const [textValue, setTextValue] = useState(selectedValue); + const [optionsObj] = useState(options); const [tooltipPos, setTooltipPos] = useState<{ top: number; left: number; @@ -36,12 +46,24 @@ const CustomDropdown: React.FC = ({ const handleOptionClick = (option: Option) => { setSelectedValue(option.label); + setTextValue(option.label); setIsOpen(false); setIsSelected(true); onSelect(option.value); setTooltipPos(null); }; + const filteredOpts = isSearchable + ? optionsObj.filter((option) => + option.label.toLowerCase().includes(textValue.toLowerCase()) + ) + : optionsObj; + + const handleOnChange = (evt: ChangeEvent) => { + const value = evt.target.value; + setTextValue(value); + }; + const handleMouseEnter = (event: React.MouseEvent, idx: number) => { const rect = event.currentTarget.getBoundingClientRect(); setTooltipPos({ @@ -51,6 +73,45 @@ const CustomDropdown: React.FC = ({ setHoveredIndex(idx); }; + const tagSpan = (tag: string, idx: number) => { + let bgColor = ''; + switch (tag.toLowerCase()) { + case 'largest volume': + bgColor = '#E6F4FE'; + break; + case 'binance': + bgColor = '#EFB621'; + break; + case 'most frequent': + bgColor = '#E6F4FE'; + break; + case 'mango': + bgColor = '#A3E5C8'; + break; + case 'uniswap': + bgColor = '#E62788'; + break; + case 'cube': + bgColor = '#FF822E'; + break; + default: + bgColor = ''; + break; + } + return ( + + {tag} + + ); + }; + useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( @@ -67,9 +128,9 @@ const CustomDropdown: React.FC = ({ }, []); return ( -
+
= ({ }`} onClick={() => setIsOpen(!isOpen)} > - {selectedValue} + {isSearchable ? ( + + {' '} + + + ) : ( + selectedValue + )} {!disabled ? ( isOpen && !disabled ? ( @@ -88,19 +165,24 @@ const CustomDropdown: React.FC = ({ ) : null}
- {isOpen && !disabled && ( + {filteredOpts.length > 0 && isOpen && !disabled && (
- {options.map((option, idx) => ( + {filteredOpts.map((option, idx) => (
handleOptionClick(option)} onMouseEnter={(e) => handleMouseEnter(e, idx)} onMouseLeave={() => setHoveredIndex(null)} > - {option.label} + <>{option.label} + {option.tags && ( + + {option.tags.map((tag, idx) => tagSpan(tag, idx))} + + )}
))}
diff --git a/src/pages/overview-bots/components/GroupedConfig.tsx b/src/pages/overview-bots/components/GroupedConfig.tsx index 5d27c77..6155876 100644 --- a/src/pages/overview-bots/components/GroupedConfig.tsx +++ b/src/pages/overview-bots/components/GroupedConfig.tsx @@ -2,7 +2,7 @@ import { countConfigsPerGroup } from '../../..//utils/countConfigsPerGroup.util' import { IStrategiesConfigData } from '../../../utils/strategyConfigData'; import { formatText } from '../../../utils/formatText.util'; import { ChangeEvent, forwardRef } from 'react'; -import CustomDropdown from './CustomDropdown'; +import CustomDropdown, { Option } from './CustomDropdown'; import ToggleButton from './ToggleButton'; import RangeSlider from './RangeSlider'; import CustomText from './CustomText'; @@ -15,6 +15,7 @@ interface IGroupedConfigProps { hasOtherGroup?: boolean; uniqueGroups: string[]; value: { [key: string]: number | string | boolean }; + tradingPairOpts: Option[], handleOnInputChange: (evt: ChangeEvent) => void; handleOnRangeChange: (evt: ChangeEvent) => void; handleOnToggle: (isOn: boolean, key: string) => void; @@ -28,6 +29,7 @@ const GroupedConfig = forwardRef( value, hasOtherGroup, uniqueGroups, + tradingPairOpts, handleOnInputChange, handleOnRangeChange, handleOnToggle, @@ -119,6 +121,12 @@ const GroupedConfig = forwardRef( ) : typeof cfg.default === 'string' || cfg.type === 'str' ? ( cfg.default && cfg.default.toString().includes(',') ? ( + ) : formatText(key) === 'trading pair' ? ( + {}} + isSearchable + /> ) : ( = ({ { label: 'Uniswap', value: '7' }, ]; + const tradingPairOpts = [ + { label: 'SOL—USDC', value: '1', tags: ['Largest Volume', 'Binance'] }, + { label: 'SOL—USDC', value: '2', tags: ['Most frequent', 'Mango'] }, + { label: 'SOL—USDT', value: '3', tags: ['Uniswap'] }, + { label: 'SOL—JUP', value: '4', tags: ['Binance'] }, + { label: 'SOL—USDT', value: '5', tags: ['Cube'] }, + ]; + const numOfTradeDays = getDaysBtnDates(endDate ? endDate : new Date()); const toggleAdancedSettingsOpen = () => @@ -258,7 +266,7 @@ const Training: React.FC = ({ />
{currentStep == 1 || currentStep == 2 ? ( -
+

{`Backtest ${ currentStep === 1 @@ -284,20 +292,16 @@ const Training: React.FC = ({ className="flex flex-col lg:flex-row justify-between gap-y-8 lg:gap-y-0 lg:gap-x-4" >
- {currentStep == 1 || currentStep == 2 ? ( -
-

- {currentStep == 1 - ? 'Adjust settings for each trading pair separately' - : 'Click on Trading Pair to view the Results of the backtest'} -

- -
- ) : null} - {currentStep == 1 ? (
+ {currentStep == 1 || currentStep == 2 ? ( +
+

Model name

+

Big Brain

+
+ ) : null} +
= ({ config={config} cfgName={cfgName} value={value} + tradingPairOpts={tradingPairOpts} handleOnInputChange={handleOnInputChange} handleOnRangeChange={handleOnRangeChange} handleOnToggle={handleOnToggle} @@ -376,9 +381,9 @@ const Training: React.FC = ({

= ({ config={config} cfgName={cfgName} value={value} + tradingPairOpts={tradingPairOpts} handleOnInputChange={handleOnInputChange} handleOnRangeChange={handleOnRangeChange} handleOnToggle={handleOnToggle} From 22707f9d3964ea5993d2d280b5d25b6fd6005fb3 Mon Sep 17 00:00:00 2001 From: kingsley Date: Sat, 21 Dec 2024 15:45:31 +0100 Subject: [PATCH 10/18] add next and previous year arrow buttons --- .../components/CustomDatePicker.tsx | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/pages/overview-bots/components/CustomDatePicker.tsx b/src/pages/overview-bots/components/CustomDatePicker.tsx index fdecb11..0faaea2 100644 --- a/src/pages/overview-bots/components/CustomDatePicker.tsx +++ b/src/pages/overview-bots/components/CustomDatePicker.tsx @@ -85,6 +85,15 @@ const CustomDatePicker = forwardRef( setActiveMonth(newDate); }; + // New: Handle next and previous year + const handleNextYear = () => { + setActiveMonth(new Date(activeMonth.setFullYear(activeMonth.getFullYear() + 1))); + }; + + const handlePrevYear = () => { + setActiveMonth(new Date(activeMonth.setFullYear(activeMonth.getFullYear() - 1))); + }; + // Close calendar when clicking outside useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -135,8 +144,8 @@ const CustomDatePicker = forwardRef(

setIsCalendarOpen(!isCalendarOpen)} - className={`flex items-center bg-light-200 text-dark-blue text-sm w-full h-[2.25rem] rounded-[100px] px-5 cursor-pointer border border-transparent hover:border-blue-300/50 outline outline-2 outline-transparent ${ - isCalendarOpen ? 'outline-blue-300 hover:border-white' : '' + className={`flex items-center text-dark-blue text-sm w-full h-[2.25rem] rounded-[10px] px-5 text-dark-300 cursor-pointer border-none hover:outline-blue-300/40 outline outline-1 outline-light-400 ${ + isCalendarOpen ? '!outline-blue-300 hover:border-white' : '' }`} > (
setIsCalendarOpen(!isCalendarOpen)} /> @@ -218,8 +229,14 @@ const CustomDatePicker = forwardRef(
{/* Right section for years */} -
-
+
+ +
{[...Array(10)].map((_, i) => { const year = activeMonth.getFullYear() - 5 + i; return ( @@ -239,6 +256,12 @@ const CustomDatePicker = forwardRef( ); })}
+
)} From 7ee8ca2da8ac9f383d2525db8715ea51f2385df8 Mon Sep 17 00:00:00 2001 From: kingsley Date: Sat, 21 Dec 2024 15:46:09 +0100 Subject: [PATCH 11/18] modified Deposit info data --- src/pages/overview-bots/hooks/useProfile.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/overview-bots/hooks/useProfile.ts b/src/pages/overview-bots/hooks/useProfile.ts index 3bf2fef..1e90bd5 100644 --- a/src/pages/overview-bots/hooks/useProfile.ts +++ b/src/pages/overview-bots/hooks/useProfile.ts @@ -957,6 +957,7 @@ export default () => { const bigResultTable = [ ['Model Name', 'SOL Big Brain'], + ['Market', 'SOL—USDC'], ['Test P&L', '+$1100 (11%)'], ['Trading Accuracy', '63%'], ['Max. drawdown', '53%'], @@ -968,6 +969,7 @@ export default () => { const bigStatTable = [ ['Model Name', 'SOL Big Brain'], + ['Market', 'SOL—USDC'], ['Exchange', 'Mango Markets'], ['Trading Strategy / Normalized value', 'BarUpDown / 0.5'], ['Market trend', 'Bullish / 67%'], @@ -980,7 +982,7 @@ export default () => { const numOfTradeDays = getDaysBtnDates(endDate ? endDate : new Date()); const depositInfo: IDepositInfo[] = [ - { l: 'Market', r: 'SOL / USDC', icon: null }, + { l: 'Market', r: 'SOL—USDC', icon: null }, { l: 'Number of trading days', r: `${numOfTradeDays}`, icon: null }, { l: 'Compute expenses', r: `$${expenses}`, icon: null }, { l: 'Solana fees', r: `$${fee}`, icon: null }, From e26bfcd722fb2f469a0a2f464b59e879e80436c9 Mon Sep 17 00:00:00 2001 From: kingsley Date: Mon, 13 Jan 2025 13:12:20 +0100 Subject: [PATCH 12/18] UI modifications, rename historical candle endopoint, modify custom dropdown with icons and tags, add icons --- src/assets/icons/Binance.svg | 4 ++++ src/assets/icons/Cube.svg | 10 ++++++++++ src/assets/icons/Uniswap.svg | 11 ++++++++++ src/assets/icons/index.ts | 6 ++++++ .../components/CustomDropdown.tsx | 18 ++++++++++++----- .../components/GroupedConfig.tsx | 6 +++--- .../overview-bots/components/Training.tsx | 20 +++++++++++-------- src/store/market/api.ts | 2 +- tailwind.config.cjs | 1 + 9 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 src/assets/icons/Binance.svg create mode 100644 src/assets/icons/Cube.svg create mode 100644 src/assets/icons/Uniswap.svg diff --git a/src/assets/icons/Binance.svg b/src/assets/icons/Binance.svg new file mode 100644 index 0000000..40b093c --- /dev/null +++ b/src/assets/icons/Binance.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/icons/Cube.svg b/src/assets/icons/Cube.svg new file mode 100644 index 0000000..f1ff594 --- /dev/null +++ b/src/assets/icons/Cube.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/assets/icons/Uniswap.svg b/src/assets/icons/Uniswap.svg new file mode 100644 index 0000000..4a24dfa --- /dev/null +++ b/src/assets/icons/Uniswap.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index fb5a663..9955f9d 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -59,6 +59,9 @@ import { ReactComponent as SolanaLogo } from '@assets/icons/Solana.svg'; import { ReactComponent as SolflareLogo } from '@assets/icons/Solflare.svg'; import { ReactComponent as USDCLogo } from '@assets/icons/USDC.svg'; import { ReactComponent as USDTLogo } from '@assets/icons/USDT.svg'; +import { ReactComponent as CubeLogo } from '@assets/icons/cube.svg'; +import { ReactComponent as BinanceLogo } from '@assets/icons/binance.svg'; +import { ReactComponent as UniswapLogo } from '@assets/icons/Uniswap.svg'; export { AlarmClockIcon, @@ -122,4 +125,7 @@ export { SolflareLogo, USDCLogo, USDTLogo, + BinanceLogo, + CubeLogo, + UniswapLogo }; diff --git a/src/pages/overview-bots/components/CustomDropdown.tsx b/src/pages/overview-bots/components/CustomDropdown.tsx index 4543f21..d44a86d 100644 --- a/src/pages/overview-bots/components/CustomDropdown.tsx +++ b/src/pages/overview-bots/components/CustomDropdown.tsx @@ -11,6 +11,7 @@ export interface Option { label: string; value: string; tags?: string[]; + logo?: JSX.Element; } interface ICustomDropdownProps { @@ -178,11 +179,18 @@ const CustomDropdown: React.FC = ({ onMouseLeave={() => setHoveredIndex(null)} > <>{option.label} - {option.tags && ( - - {option.tags.map((tag, idx) => tagSpan(tag, idx))} - - )} + + {option.tags && ( + + {option.tags.map((tag, idx) => tagSpan(tag, idx))} + + )} + {option.logo && ( + + {option.logo} + + )} +
))}
diff --git a/src/pages/overview-bots/components/GroupedConfig.tsx b/src/pages/overview-bots/components/GroupedConfig.tsx index 6155876..948229d 100644 --- a/src/pages/overview-bots/components/GroupedConfig.tsx +++ b/src/pages/overview-bots/components/GroupedConfig.tsx @@ -1,13 +1,13 @@ import { countConfigsPerGroup } from '../../..//utils/countConfigsPerGroup.util'; import { IStrategiesConfigData } from '../../../utils/strategyConfigData'; import { formatText } from '../../../utils/formatText.util'; -import { ChangeEvent, forwardRef } from 'react'; import CustomDropdown, { Option } from './CustomDropdown'; +import CustomInput from '@components/ui/CustomInput'; +import { ChangeEvent, forwardRef } from 'react'; import ToggleButton from './ToggleButton'; import RangeSlider from './RangeSlider'; -import CustomText from './CustomText'; import NumberInput from './NumberInput'; -import CustomInput from '@components/ui/CustomInput'; +import CustomText from './CustomText'; interface IGroupedConfigProps { config: IStrategiesConfigData; diff --git a/src/pages/overview-bots/components/Training.tsx b/src/pages/overview-bots/components/Training.tsx index 8b40318..9850fb0 100644 --- a/src/pages/overview-bots/components/Training.tsx +++ b/src/pages/overview-bots/components/Training.tsx @@ -19,8 +19,11 @@ import Pagination from './Pagination'; import { ArrowDown2Icon, ArrowUp2Icon, + BinanceLogo, + CubeLogo, MangoLogo, SolanaLogo, + UniswapLogo, USDCLogo, } from '@assets/icons'; import React, { @@ -135,11 +138,11 @@ const Training: React.FC = ({ ]; const tradingPairOpts = [ - { label: 'SOL—USDC', value: '1', tags: ['Largest Volume', 'Binance'] }, - { label: 'SOL—USDC', value: '2', tags: ['Most frequent', 'Mango'] }, - { label: 'SOL—USDT', value: '3', tags: ['Uniswap'] }, - { label: 'SOL—JUP', value: '4', tags: ['Binance'] }, - { label: 'SOL—USDT', value: '5', tags: ['Cube'] }, + { label: 'SOL—USDC', value: '1', tags: ['Largest Volume'], logo: }, + { label: 'SOL—USDC', value: '2', tags: ['Most frequent'], logo: }, + { label: 'SOL—USDT', value: '3', tags: undefined, logo: }, + { label: 'SOL—JUP', value: '4', tags: undefined, logo: }, + { label: 'SOL—USDT', value: '5', tags: undefined, logo: }, ]; const numOfTradeDays = getDaysBtnDates(endDate ? endDate : new Date()); @@ -467,7 +470,7 @@ const Training: React.FC = ({ } /> -
+
{['$100', '$300', '$500', 'Max'].map((data, idx) => ( = ({ ))}
+

of connected wallet balance

))}
@@ -501,11 +505,11 @@ const Training: React.FC = ({ {l}
diff --git a/src/store/market/api.ts b/src/store/market/api.ts index e7a8d9e..5d21c17 100644 --- a/src/store/market/api.ts +++ b/src/store/market/api.ts @@ -6,7 +6,7 @@ const instanceApi = robotterApi.injectEndpoints({ getHistoricalCandles: builder.mutation({ query: (data) => ({ method: 'POST', - url: `/historical-candles`, + url: `/api/v1/historical-candles`, data, }), invalidatesTags: ['Candle'], diff --git a/tailwind.config.cjs b/tailwind.config.cjs index ed9e77c..b5a0b4f 100644 --- a/tailwind.config.cjs +++ b/tailwind.config.cjs @@ -4,6 +4,7 @@ module.exports = { theme: { extend: { fontFamily: { + inter: ['Inter'], ubuntumono: ['Ubuntu Mono', 'sans-serif'], }, boxShadow: { From a6b0c14516478244706abace9a8228a0a8afdd01 Mon Sep 17 00:00:00 2001 From: kingsley Date: Tue, 21 Jan 2025 13:49:52 +0100 Subject: [PATCH 13/18] Add indicator with different colors --- .../overview-bots/components/Overview.tsx | 11 +++---- .../overview-bots/components/Performance.tsx | 18 ++++++++--- .../overview-bots/components/StatsTable.tsx | 14 ++++---- src/pages/overview-bots/hooks/useProfile.ts | 32 +++++++++++++++++-- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/pages/overview-bots/components/Overview.tsx b/src/pages/overview-bots/components/Overview.tsx index 79451cc..db7244e 100644 --- a/src/pages/overview-bots/components/Overview.tsx +++ b/src/pages/overview-bots/components/Overview.tsx @@ -71,7 +71,6 @@ const Overview: React.FC = ({ const { address } = useAppSelector((state) => state.auth); const { isOpen, handleOpen, handleClose } = useModal(); const getChartTypeQuery = searchParams.get('chart') || 'pnl'; - const navigate = useNavigate(); const handleCreateNewModel = useCallback(async () => { if (!address) { @@ -81,11 +80,11 @@ const Overview: React.FC = ({ } try { - await createInstance({ - strategy_name: 'test', - strategy_parameters: {}, - market: 'string', - }).unwrap(); + // await createInstance({ + // strategy_name: 'test', + // strategy_parameters: {}, + // market: 'string', + // }).unwrap(); setSearchParams({ tab: 'training' }); } catch (e) { console.error('Failed to create new model', e); diff --git a/src/pages/overview-bots/components/Performance.tsx b/src/pages/overview-bots/components/Performance.tsx index b76c154..5a7560c 100644 --- a/src/pages/overview-bots/components/Performance.tsx +++ b/src/pages/overview-bots/components/Performance.tsx @@ -35,15 +35,25 @@ const Performance: React.FC = ({ {/* CardBot */}
- {cardBotData.map((item, idx) => ( + {isEmpty ? ( - ))} + ) : ( + cardBotData.map((item, idx) => ( + + )) + )}
{!isEmpty && ( diff --git a/src/pages/overview-bots/components/StatsTable.tsx b/src/pages/overview-bots/components/StatsTable.tsx index 1b1329a..27d20bf 100644 --- a/src/pages/overview-bots/components/StatsTable.tsx +++ b/src/pages/overview-bots/components/StatsTable.tsx @@ -55,10 +55,7 @@ const StatsTable: React.FC = ({ />
-
+
{/* Chart */} {isEmpty && !stat.progressValue ? ( @@ -75,11 +72,14 @@ const StatsTable: React.FC = ({ )} {/* Progress Bar */} {stat.progressValue && ( -
+
{!isEmpty && ( )}
diff --git a/src/pages/overview-bots/hooks/useProfile.ts b/src/pages/overview-bots/hooks/useProfile.ts index 1e90bd5..905acd2 100644 --- a/src/pages/overview-bots/hooks/useProfile.ts +++ b/src/pages/overview-bots/hooks/useProfile.ts @@ -43,6 +43,7 @@ export interface IStatsTableData { value: string; chartData: null | number[]; progressValue: null | number; + progressValueColor: string[] | null; color: string | null; toolTipText: string | null; } @@ -114,7 +115,9 @@ export interface IDepositInfo { export default () => { const { address } = useAppSelector((state) => state.auth); - const { coinValue, endDate, expenses, fee } = useAppSelector((state) => state.general); + const { coinValue, endDate, expenses, fee } = useAppSelector( + (state) => state.general + ); const [searchParams, setSearchParams] = useSearchParams(); const { setTitle } = usePageTitle(); const [search, setSearch] = useState(''); @@ -304,6 +307,7 @@ export default () => { value: '+$3909 (20%)', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: 'Shows the net gain or loss from your trades over a selected time period, helping you track performance', @@ -313,6 +317,7 @@ export default () => { value: '-$469', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: 'Represents the total value of all assets traded by your bots during the selected period, providing insight into your trading activity.', @@ -322,6 +327,7 @@ export default () => { value: '59.36%', chartData: [98, 40, 60, 38, 42, 46, 40, 90, 95, 50], progressValue: null, + progressValueColor: null, color: '#F44336', toolTipText: 'The number of all executed buy and sell orders by your bots during the selected period, showing the overall trading activity.', @@ -331,6 +337,7 @@ export default () => { value: '200%', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: 'The projected annual return on your trading strategies, expressed as a percentage, based on current performance and compounding', @@ -340,6 +347,7 @@ export default () => { value: '2.52', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: 'The percentage of successful trades made by your bots, indicating how often their predictions were correct.', @@ -352,6 +360,7 @@ export default () => { value: '$36 367', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: 'Shows the net gain or loss from your trades over a selected time period, helping you track performance', @@ -361,6 +370,7 @@ export default () => { value: '250', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: 'Represents the total value of all assets traded by your bots during the selected period, providing insight into your trading activity.', @@ -370,6 +380,7 @@ export default () => { value: '$15,63', chartData: [98, 40, 60, 38, 42, 46, 40, 90, 95, 50], progressValue: null, + progressValueColor: null, color: '#F44336', toolTipText: 'The number of all executed buy and sell orders by your bots during the selected period, showing the overall trading activity.', @@ -377,8 +388,9 @@ export default () => { { label: 'Total accuracy', value: '53%', - chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], - progressValue: null, + chartData: null, + progressValue: 53, + progressValueColor: ['#A3E5C8', '#FFAFB2'], color: '#4CAF50', toolTipText: 'The projected annual return on your trading strategies, expressed as a percentage, based on current performance and compounding', @@ -391,6 +403,7 @@ export default () => { value: '-$20', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#F44336', toolTipText: null, }, @@ -399,6 +412,7 @@ export default () => { value: '$9186', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: null, }, @@ -407,6 +421,7 @@ export default () => { value: '$16532', chartData: [98, 40, 60, 38, 42, 46, 40, 90, 95, 50], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: null, }, @@ -415,6 +430,7 @@ export default () => { value: '14', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#F44336', toolTipText: null, }, @@ -423,6 +439,7 @@ export default () => { value: '210%', chartData: null, progressValue: null, + progressValueColor: null, color: null, toolTipText: null, }, @@ -431,6 +448,7 @@ export default () => { value: '2.81', chartData: null, progressValue: null, + progressValueColor: null, color: null, toolTipText: null, }, @@ -442,6 +460,7 @@ export default () => { value: '$24', chartData: [98, 40, 60, 38, 42, 46, 40, 90, 95, 50], progressValue: null, + progressValueColor: null, color: '#F44336', toolTipText: 'The estimated cost for running your trading bots, including data processing and computational resources, billed monthly', @@ -451,6 +470,7 @@ export default () => { value: '2%', chartData: null, progressValue: 20, + progressValueColor: ['#60B3D7', '#E6F4FE'], color: '', toolTipText: 'The average fee charged for placing limit orders that add liquidity to the market. Lower maker fees can reduce your overall trading costs.', @@ -460,6 +480,7 @@ export default () => { value: '3%', chartData: null, progressValue: 30, + progressValueColor: ['#60B3D7', '#E6F4FE'], color: '', toolTipText: 'The average fee charged for executing market orders that remove liquidity from the market. Higher taker fees can impact your overall trading profitability.', @@ -469,6 +490,7 @@ export default () => { value: '$32', chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100], progressValue: null, + progressValueColor: null, color: '#4CAF50', toolTipText: null, }, @@ -480,6 +502,7 @@ export default () => { value: '550', chartData: null, progressValue: 50, + progressValueColor: ['#60B3D7', '#E6F4FE'], color: null, toolTipText: null, }, @@ -488,6 +511,7 @@ export default () => { value: '$50', chartData: [98, 40, 60, 38, 42, 46, 40, 90, 95, 50], progressValue: null, + progressValueColor: null, color: '#F44336', toolTipText: null, }, @@ -496,6 +520,7 @@ export default () => { value: '2%', chartData: null, progressValue: 20, + progressValueColor: ['#60B3D7', '#E6F4FE'], color: null, toolTipText: null, }, @@ -504,6 +529,7 @@ export default () => { value: '3%', chartData: null, progressValue: 30, + progressValueColor: ['#60B3D7', '#E6F4FE'], color: null, toolTipText: null, }, From 8e887cb8c2fd43b360b102fb31ba20c4523efc57 Mon Sep 17 00:00:00 2001 From: kingsley Date: Wed, 22 Jan 2025 13:59:48 +0100 Subject: [PATCH 14/18] initialize searchable dropdown with no value --- .../components/CustomDropdown.tsx | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/pages/overview-bots/components/CustomDropdown.tsx b/src/pages/overview-bots/components/CustomDropdown.tsx index d44a86d..d13aebf 100644 --- a/src/pages/overview-bots/components/CustomDropdown.tsx +++ b/src/pages/overview-bots/components/CustomDropdown.tsx @@ -6,6 +6,8 @@ import { PointerIcon, Search24Icon, } from '@assets/icons'; +import { useAppDispatch } from '@store/hooks'; +import { setDisabledRunBacktest } from '@slices/generalSlice'; export interface Option { label: string; @@ -21,6 +23,7 @@ interface ICustomDropdownProps { disabled?: boolean; showTooTip?: boolean; isSearchable?: boolean; + searchableName?: string; } const CustomDropdown: React.FC = ({ @@ -30,6 +33,7 @@ const CustomDropdown: React.FC = ({ disabled, showTooTip, isSearchable, + searchableName }) => { const dropDownRef = useRef(null); const [isOpen, setIsOpen] = useState(false); @@ -38,16 +42,17 @@ const CustomDropdown: React.FC = ({ placeholder || (options.length > 0 ? options[0].label : 'Select Option') ); const [hoveredIndex, setHoveredIndex] = useState(null); - const [textValue, setTextValue] = useState(selectedValue); + const [textValue, setTextValue] = useState({ [searchableName || ""]: '' }); const [optionsObj] = useState(options); const [tooltipPos, setTooltipPos] = useState<{ top: number; left: number; } | null>(null); + const dispatch = useAppDispatch(); const handleOptionClick = (option: Option) => { setSelectedValue(option.label); - setTextValue(option.label); + setTextValue({ [searchableName || ""]: option.label }); setIsOpen(false); setIsSelected(true); onSelect(option.value); @@ -56,13 +61,13 @@ const CustomDropdown: React.FC = ({ const filteredOpts = isSearchable ? optionsObj.filter((option) => - option.label.toLowerCase().includes(textValue.toLowerCase()) + option.label.toLowerCase().includes((textValue[searchableName || ""] || "").toLowerCase()) ) : optionsObj; const handleOnChange = (evt: ChangeEvent) => { - const value = evt.target.value; - setTextValue(value); + const {value, name} = evt.target; + setTextValue({ [name]: value }); }; const handleMouseEnter = (event: React.MouseEvent, idx: number) => { @@ -114,6 +119,7 @@ const CustomDropdown: React.FC = ({ }; useEffect(() => { + dispatch(setDisabledRunBacktest(textValue[searchableName || ""] === "")); const handleClickOutside = (event: MouseEvent) => { if ( dropDownRef.current && @@ -126,7 +132,7 @@ const CustomDropdown: React.FC = ({ return () => { document.removeEventListener('mousedown', handleClickOutside); }; - }, []); + }, [textValue[searchableName || ""]]); return (
@@ -148,7 +154,8 @@ const CustomDropdown: React.FC = ({ }`} />{' '} Date: Thu, 23 Jan 2025 01:28:48 +0100 Subject: [PATCH 15/18] Disabled Run Backtest initially --- .../components/GroupedConfig.tsx | 5 +- .../overview-bots/components/Training.tsx | 152 +++++++++--------- src/slices/generalSlice.ts | 19 ++- 3 files changed, 100 insertions(+), 76 deletions(-) diff --git a/src/pages/overview-bots/components/GroupedConfig.tsx b/src/pages/overview-bots/components/GroupedConfig.tsx index 948229d..4785e40 100644 --- a/src/pages/overview-bots/components/GroupedConfig.tsx +++ b/src/pages/overview-bots/components/GroupedConfig.tsx @@ -15,7 +15,7 @@ interface IGroupedConfigProps { hasOtherGroup?: boolean; uniqueGroups: string[]; value: { [key: string]: number | string | boolean }; - tradingPairOpts: Option[], + tradingPairOpts: Option[]; handleOnInputChange: (evt: ChangeEvent) => void; handleOnRangeChange: (evt: ChangeEvent) => void; handleOnToggle: (isOn: boolean, key: string) => void; @@ -123,6 +123,7 @@ const GroupedConfig = forwardRef( ) : formatText(key) === 'trading pair' ? ( {}} isSearchable @@ -163,7 +164,7 @@ const GroupedConfig = forwardRef( )}
{objectConfig(group)} diff --git a/src/pages/overview-bots/components/Training.tsx b/src/pages/overview-bots/components/Training.tsx index 9850fb0..f0d867b 100644 --- a/src/pages/overview-bots/components/Training.tsx +++ b/src/pages/overview-bots/components/Training.tsx @@ -3,17 +3,13 @@ import { getDaysBtnDates, isTodayOrFuture } from '@utils/getDaysBtnDates.util'; import { useAppDispatch, useAppSelector } from '@shared/hooks/useStore'; import { useGetHistoricalCandlesMutation } from '@store/market/api'; import { defaultType } from '../../../utils/defaultType.util'; -import { transformData } from '../../../utils/transformData'; import { updateDefaults } from '../../../utils/updateDefault'; import { SetURLSearchParams } from 'react-router-dom'; import CustomInput from '@components/ui/CustomInput'; -import CandlestickChart from './CandlestickChart'; import CustomDatePicker from './CustomDatePicker'; import CustomBtn from '@components/ui/CustomBtn'; import CustomDropdown from './CustomDropdown'; import GroupedConfig from './GroupedConfig'; -import { FadeLoader } from 'react-spinners'; -import ButtonList from './ButtonList'; import CustomText from './CustomText'; import Pagination from './Pagination'; import { @@ -50,7 +46,9 @@ import { setCoinValues, setEndDate, setExpensesFee, + setIsFetchCandleData, } from '@slices/generalSlice'; +import GraphChart from './GraphChart'; export interface ITrainingProps { timeQuery: ITimeTab; @@ -114,7 +112,9 @@ const Training: React.FC = ({ SOL: '', USDC: '', }); - const { endDate } = useAppSelector((state) => state.general); + const { endDate, disabledRunBacktest, isFetchCandleData } = useAppSelector( + (state) => state.general + ); const dispatch = useAppDispatch(); const uniqueGroups = Array.from( @@ -138,8 +138,18 @@ const Training: React.FC = ({ ]; const tradingPairOpts = [ - { label: 'SOL—USDC', value: '1', tags: ['Largest Volume'], logo: }, - { label: 'SOL—USDC', value: '2', tags: ['Most frequent'], logo: }, + { + label: 'SOL—USDC', + value: '1', + tags: ['Largest Volume'], + logo: , + }, + { + label: 'SOL—USDC', + value: '2', + tags: ['Most frequent'], + logo: , + }, { label: 'SOL—USDT', value: '3', tags: undefined, logo: }, { label: 'SOL—JUP', value: '4', tags: undefined, logo: }, { label: 'SOL—USDT', value: '5', tags: undefined, logo: }, @@ -180,6 +190,7 @@ const Training: React.FC = ({ if (!/^\d*$/.test(value)) return; setCoinValue((prevState) => ({ ...prevState, [name]: value })); dispatch(setCoinValues({ [name]: +value })); + dispatch(setIsFetchCandleData(false)); }; const handleOnToggle = (isOn: boolean, key: string) => { @@ -210,19 +221,21 @@ const Training: React.FC = ({ }; const handleCandleData = useCallback(async () => { - try { - await historicalCandlesData({ - connector_name: 'birdeye', - trading_pair: tradePair, - market_address: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs', - interval: '15m', - start_time: timeStamp.startTime, - end_time: timeStamp.endTime, - }); - } catch (error) { - console.log('TRY CATCH ERROR => ', error); + if (isFetchCandleData) { + try { + await historicalCandlesData({ + connector_name: 'birdeye', + trading_pair: tradePair, + market_address: '7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxs', + interval: '15m', + start_time: timeStamp.startTime, + end_time: timeStamp.endTime, + }); + } catch (error) { + console.log('TRY CATCH ERROR => ', error); + } } - }, [tradePair, timeStamp]); + }, [tradePair, timeStamp, isFetchCandleData]); useEffect(() => { handleCandleData(); @@ -232,12 +245,10 @@ const Training: React.FC = ({ ); }, [tradePair, timeStamp, coinValue]); - const disabled = - currentStep === 3 - ? Object.values(coinValue).some((num) => num === '' || +num <= 0) || - numOfTradeDays < 0 || - !isTodayOrFuture(timeStamp.endDate) - : false; + const disabledDeposit = + Object.values(coinValue).some((num) => num === '' || +num <= 0) || + numOfTradeDays < 0 || + !isTodayOrFuture(timeStamp.endDate); return (
@@ -263,8 +274,14 @@ const Training: React.FC = ({ ? 'Save Strategy' : 'Deposit & Start' }`} - disabled={disabled} - xtraStyles={`!max-w-[20.3125rem] !w-full`} + disabled={ + currentStep === 1 + ? disabledRunBacktest + : currentStep === 3 + ? disabledDeposit + : false + } + xtraStyles={`!max-w-[18rem] lg:!max-w-[20.3125rem] !w-full !mt-5 md:!mt-0`} onClick={handleNextStep} />
@@ -297,14 +314,14 @@ const Training: React.FC = ({
{currentStep == 1 ? (
-
+
{currentStep == 1 || currentStep == 2 ? (

Model name

Big Brain

) : null} -
+
= ({ />
) : currentStep == 3 ? ( -
+
-
-
-

+
+
+

Connect exchange

= ({ />
-
-

+
+

Trading time limit

= ({

-

+

Deposit both coins to start

-
+
{[ { icon: , text: 'SOL' }, { icon: , text: 'USDC' }, ].map(({ icon, text }, idx) => ( -
+
-

of connected wallet balance

+

+ of connected wallet balance +

))}
@@ -488,7 +507,7 @@ const Training: React.FC = ({ @@ -580,12 +576,24 @@ const Training: React.FC = ({ )}
+ {/* Only show chat when on step 1 and 2 */} + {currentStep == 1 || currentStep == 2 ? ( + + ) : null} + {currentStep == 1 || currentStep == 2 ? ( <>
diff --git a/src/slices/generalSlice.ts b/src/slices/generalSlice.ts index b0ccbcd..ad4b417 100644 --- a/src/slices/generalSlice.ts +++ b/src/slices/generalSlice.ts @@ -3,6 +3,8 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; interface AppState { endDate: Date | null; expenses: number; + isFetchCandleData: boolean; + disabledRunBacktest: boolean; fee: number; coinValue: { [key: string]: number }; } @@ -10,6 +12,8 @@ interface AppState { const initialState: AppState = { endDate: null, expenses: 0, + isFetchCandleData: true, + disabledRunBacktest: true, fee: 0, coinValue: { SOL: 0, @@ -37,10 +41,21 @@ export const generalSlice = createSlice({ state.expenses = payload.expenses; state.fee = payload.fee; }, + setDisabledRunBacktest: (state, { payload }: { payload: boolean }) => { + state.disabledRunBacktest = payload; + }, + setIsFetchCandleData: (state, { payload }: { payload: boolean }) => { + state.isFetchCandleData = payload; + } }, }); -export const { setCoinValues, setEndDate, setExpensesFee } = - generalSlice.actions; +export const { + setEndDate, + setCoinValues, + setExpensesFee, + setIsFetchCandleData, + setDisabledRunBacktest, +} = generalSlice.actions; export default generalSlice; From 76dc8e8775dae0b45975ac7a92a4e03da37bb760 Mon Sep 17 00:00:00 2001 From: kingsley Date: Thu, 23 Jan 2025 12:13:09 +0100 Subject: [PATCH 16/18] make components responsive --- src/pages/overview-bots/components/GoBack.tsx | 2 +- src/pages/overview-bots/components/Header.tsx | 18 +++++++++++++----- src/pages/overview-bots/components/Stepper.tsx | 3 +-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/pages/overview-bots/components/GoBack.tsx b/src/pages/overview-bots/components/GoBack.tsx index 3572ac0..27160f2 100644 --- a/src/pages/overview-bots/components/GoBack.tsx +++ b/src/pages/overview-bots/components/GoBack.tsx @@ -11,7 +11,7 @@ const GoBack: React.FC = ({ onClick, disabled }) => {
diff --git a/src/pages/overview-bots/components/Header.tsx b/src/pages/overview-bots/components/Header.tsx index fba14e7..0dece51 100644 --- a/src/pages/overview-bots/components/Header.tsx +++ b/src/pages/overview-bots/components/Header.tsx @@ -26,7 +26,14 @@ import { import Switcher from './Switcher'; export interface IHeaderProps { - query: ITab | ITimeTab | ITimeBTab | IDateTab | IStratTab | IChatTab | IPerfTab; + query: + | ITab + | ITimeTab + | ITimeBTab + | IDateTab + | IStratTab + | IChatTab + | IPerfTab; tabs: ITabs[]; searchParams: URLSearchParams; setSearchParams: SetURLSearchParams; @@ -91,16 +98,16 @@ const Header: React.FC = ({
- +

3

- + - + {address ? ( @@ -121,7 +128,8 @@ const Header: React.FC = ({
) : ( = ({ currentStep, setCurrentStep }) => {

index + 1, // Previous step text color @@ -70,4 +70,3 @@ const Stepper: React.FC = ({ currentStep, setCurrentStep }) => { }; export default Stepper; - From 03fa54388f0fd0ccdfbf06a4b969883ecf4fde7c Mon Sep 17 00:00:00 2001 From: kingsley Date: Thu, 23 Jan 2025 14:06:46 +0100 Subject: [PATCH 17/18] move CandlestictChart to new component --- .../overview-bots/components/GraphChart.tsx | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/pages/overview-bots/components/GraphChart.tsx diff --git a/src/pages/overview-bots/components/GraphChart.tsx b/src/pages/overview-bots/components/GraphChart.tsx new file mode 100644 index 0000000..392003b --- /dev/null +++ b/src/pages/overview-bots/components/GraphChart.tsx @@ -0,0 +1,49 @@ +import { transformData } from '@utils/transformData'; +import { CandleDataResp } from '@store/market/types'; +import CandlestickChart from './CandlestickChart'; +import CustomDatePicker from './CustomDatePicker'; +import { FadeLoader } from 'react-spinners'; +import CustomText from './CustomText'; +import { forwardRef } from 'react'; + +interface IGraphChart { + isLoading: boolean; + className: string | undefined; + data: CandleDataResp | undefined; + startTimeUnix: (unix: number) => void; + endTimeUnix: (unix: number) => void; +} + +const GraphChart = forwardRef( + ({ isLoading, data, className, startTimeUnix, endTimeUnix }, ref) => { + return ( +

+ ); + } +); + +export default GraphChart; From 0962a6bb1555efbddb6a3945111319ab685bea88 Mon Sep 17 00:00:00 2001 From: kingsley Date: Sun, 26 Jan 2025 06:07:09 +0100 Subject: [PATCH 18/18] add /api/v1 to endpoints --- src/store/auth/api.ts | 6 +++--- src/store/instances/api.ts | 8 ++++---- src/store/strategies/api.ts | 2 +- src/store/transactions/api.ts | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/store/auth/api.ts b/src/store/auth/api.ts index f75e7a5..d0b761d 100644 --- a/src/store/auth/api.ts +++ b/src/store/auth/api.ts @@ -15,7 +15,7 @@ const authApi = robotterApi.injectEndpoints({ >({ query: ({ address }) => ({ method: 'POST', - url: `/authorization/challenge?address=${address}&chain=${CHAIN}`, + url: `/api/v1/authorization/challenge?address=${address}&chain=${CHAIN}`, }), }), @@ -30,7 +30,7 @@ const authApi = robotterApi.injectEndpoints({ >({ query: ({ address, signature }) => ({ method: 'POST', - url: `/authorization/solve?address=${address}&chain=${CHAIN}&signature=${signature}`, + url: `/api/v1/authorization/solve?address=${address}&chain=${CHAIN}&signature=${signature}`, }), }), @@ -45,7 +45,7 @@ const authApi = robotterApi.injectEndpoints({ >({ query: ({ token }) => ({ method: 'POST', - url: `/authorization/refresh?token=${token}`, + url: `/api/v1/authorization/refresh?token=${token}`, }), }), }), diff --git a/src/store/instances/api.ts b/src/store/instances/api.ts index c4ec3a3..9c48f93 100644 --- a/src/store/instances/api.ts +++ b/src/store/instances/api.ts @@ -14,7 +14,7 @@ const instanceApi = robotterApi.injectEndpoints({ query: (data) => { console.log('createInstance request body:', data); return { - url: '/instances', + url: '/api/v1/instances', method: 'POST', data, }; @@ -25,7 +25,7 @@ const instanceApi = robotterApi.injectEndpoints({ getInstance: builder.query({ query: ({ instance_id }) => ({ method: 'GET', - url: `/instances/${instance_id}/wallet`, + url: `/api/v1/instances/${instance_id}/wallet`, }), providesTags: ['Instance'], }), @@ -39,7 +39,7 @@ const instanceApi = robotterApi.injectEndpoints({ } >({ query: ({ instanceId, ...data }) => ({ - url: `/instances/${instanceId}/start`, + url: `/api/v1/instances/${instanceId}/start`, method: 'POST', body: data, }), @@ -49,7 +49,7 @@ const instanceApi = robotterApi.injectEndpoints({ stopInstance: builder.mutation({ query: ({ instance_id }) => ({ method: 'POST', - url: `/instances/${instance_id}/stop`, + url: `/api/v1/instances/${instance_id}/stop`, }), invalidatesTags: ['StopInstance'], }), diff --git a/src/store/strategies/api.ts b/src/store/strategies/api.ts index b07f36b..cfdffd5 100644 --- a/src/store/strategies/api.ts +++ b/src/store/strategies/api.ts @@ -4,7 +4,7 @@ const strategiesApi = robotterApi.injectEndpoints({ endpoints: (builder) => ({ getStrategies: builder.query>, void>({ query: () => ({ - url: '/strategies', + url: '/api/v1/strategies', method: 'GET', }), }), diff --git a/src/store/transactions/api.ts b/src/store/transactions/api.ts index 0117b6f..11cd612 100644 --- a/src/store/transactions/api.ts +++ b/src/store/transactions/api.ts @@ -5,7 +5,7 @@ const transactionsEndpoints = transactionsApi.injectEndpoints({ endpoints: (builder) => ({ getUserUsdcBalance: builder.query<{ balance: number }, { user: string }>({ query: (params) => ({ - url: '/getUserUsdcBalance', + url: '/api/v1/getUserUsdcBalance', method: 'GET', params, }), @@ -15,7 +15,7 @@ const transactionsEndpoints = transactionsApi.injectEndpoints({ { userAddress: string } >({ query: (params) => ({ - url: '/getBotData', + url: '/api/v1/getBotData', method: 'GET', params, }),