-
- {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/CustomDatePicker.tsx b/src/pages/overview-bots/components/CustomDatePicker.tsx
index 96312da..0faaea2 100644
--- a/src/pages/overview-bots/components/CustomDatePicker.tsx
+++ b/src/pages/overview-bots/components/CustomDatePicker.tsx
@@ -9,13 +9,14 @@ type ValuePiece = Date | null;
type Value = ValuePiece | [ValuePiece, ValuePiece];
interface ICustomDatePickerProps {
- getUnixTimeStamp: (unixTimeStamp: number) => void;
+ getUnixTimeStamp?: (unixTimeStamp: number) => void;
direction?: 'right' | 'left' | 'bottom' | 'top';
- isEmpty?: boolean
+ getDate?: (date: Date) => void;
+ isEmpty?: boolean;
}
const CustomDatePicker = forwardRef(
- ({ getUnixTimeStamp, direction = 'bottom', isEmpty }, ref) => {
+ ({ getUnixTimeStamp, getDate, direction = 'bottom', isEmpty }, ref) => {
const [value, setValue] = useState(isEmpty ? null : new Date());
const [isCalendarOpen, setIsCalendarOpen] = useState(false);
const [activeMonth, setActiveMonth] = useState(new Date());
@@ -59,7 +60,8 @@ const CustomDatePicker = forwardRef(
if (date instanceof Date) {
const unixTimestamp = Math.floor(date.getTime() / 1000);
- getUnixTimeStamp(unixTimestamp);
+ getUnixTimeStamp && getUnixTimeStamp(unixTimestamp);
+ getDate && getDate(date);
}
setIsCalendarOpen(false);
@@ -83,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) => {
@@ -133,19 +144,21 @@ 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' : ''
}`}
>
-
- {value
- ? (value as Date).toLocaleDateString('en-CA')
- : 'YYYY-MM-DD'}
+
+ {value ? (value as Date).toLocaleDateString('en-CA') : 'YYYY-MM-DD'}
setIsCalendarOpen(!isCalendarOpen)}
/>
@@ -216,8 +229,14 @@ const CustomDatePicker = forwardRef(
{/* Right section for years */}
-
-
+
+
+
+
+
{[...Array(10)].map((_, i) => {
const year = activeMonth.getFullYear() - 5 + i;
return (
@@ -237,6 +256,12 @@ const CustomDatePicker = forwardRef(
);
})}
+
+
+
)}
diff --git a/src/pages/overview-bots/components/CustomDropdown.tsx b/src/pages/overview-bots/components/CustomDropdown.tsx
index cea1b37..d13aebf 100644
--- a/src/pages/overview-bots/components/CustomDropdown.tsx
+++ b/src/pages/overview-bots/components/CustomDropdown.tsx
@@ -1,10 +1,19 @@
-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';
+import { useAppDispatch } from '@store/hooks';
+import { setDisabledRunBacktest } from '@slices/generalSlice';
-interface Option {
+export interface Option {
label: string;
value: string;
+ tags?: string[];
+ logo?: JSX.Element;
}
interface ICustomDropdownProps {
@@ -13,6 +22,8 @@ interface ICustomDropdownProps {
placeholder?: string;
disabled?: boolean;
showTooTip?: boolean;
+ isSearchable?: boolean;
+ searchableName?: string;
}
const CustomDropdown: React.FC
= ({
@@ -21,6 +32,8 @@ const CustomDropdown: React.FC = ({
placeholder,
disabled,
showTooTip,
+ isSearchable,
+ searchableName
}) => {
const dropDownRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
@@ -29,19 +42,34 @@ const CustomDropdown: React.FC = ({
placeholder || (options.length > 0 ? options[0].label : 'Select Option')
);
const [hoveredIndex, setHoveredIndex] = useState(null);
+ 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({ [searchableName || ""]: option.label });
setIsOpen(false);
setIsSelected(true);
onSelect(option.value);
setTooltipPos(null);
};
+ const filteredOpts = isSearchable
+ ? optionsObj.filter((option) =>
+ option.label.toLowerCase().includes((textValue[searchableName || ""] || "").toLowerCase())
+ )
+ : optionsObj;
+
+ const handleOnChange = (evt: ChangeEvent) => {
+ const {value, name} = evt.target;
+ setTextValue({ [name]: value });
+ };
+
const handleMouseEnter = (event: React.MouseEvent, idx: number) => {
const rect = event.currentTarget.getBoundingClientRect();
setTooltipPos({
@@ -51,7 +79,47 @@ 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(() => {
+ dispatch(setDisabledRunBacktest(textValue[searchableName || ""] === ""));
const handleClickOutside = (event: MouseEvent) => {
if (
dropDownRef.current &&
@@ -64,37 +132,72 @@ const CustomDropdown: React.FC = ({
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
- }, []);
+ }, [textValue[searchableName || ""]]);
return (
-
+
setIsOpen(!isOpen)}
>
- {selectedValue}
- {isOpen && !disabled ?
:
}
+ {isSearchable ? (
+
+ {' '}
+
+
+ ) : (
+ selectedValue
+ )}
+ {!disabled ? (
+ isOpen && !disabled ? (
+
+ ) : (
+
+ )
+ ) : 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))}
+
+ )}
+ {option.logo && (
+
+ {option.logo}
+
+ )}
+
))}
diff --git a/src/pages/overview-bots/components/CustomText.tsx b/src/pages/overview-bots/components/CustomText.tsx
index d5d382a..46d788e 100644
--- a/src/pages/overview-bots/components/CustomText.tsx
+++ b/src/pages/overview-bots/components/CustomText.tsx
@@ -8,10 +8,22 @@ interface ICustomTextProps {
hasQuestionMark?: boolean;
xtraStyle?: string;
toolTipWidth?: string;
+ isEmpty?: boolean;
}
const CustomText = forwardRef
(
- ({ text, toolTipText, showOptText, hasQuestionMark = true, xtraStyle, toolTipWidth }, ref) => {
+ (
+ {
+ text,
+ toolTipText,
+ showOptText,
+ hasQuestionMark = true,
+ xtraStyle,
+ toolTipWidth,
+ isEmpty,
+ },
+ ref
+ ) => {
const [showToolTip, setShowToolTip] = useState(false);
const spanRef = useRef(null);
@@ -34,20 +46,30 @@ const CustomText = forwardRef(
}, []);
return (
-
+
{text}
- {showOptText && (Optional) }
+ {showOptText && (
+ (Optional)
+ )}
{hasQuestionMark && (
?
- {showToolTip && (
-
+ {showToolTip && !isEmpty && (
+
)}
)}
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/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 (
+
+
+ {isLoading ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+ }
+);
+
+export default GraphChart;
diff --git a/src/pages/overview-bots/components/GroupedConfig.tsx b/src/pages/overview-bots/components/GroupedConfig.tsx
index 52d6970..4785e40 100644
--- a/src/pages/overview-bots/components/GroupedConfig.tsx
+++ b/src/pages/overview-bots/components/GroupedConfig.tsx
@@ -1,12 +1,13 @@
import { countConfigsPerGroup } from '../../..//utils/countConfigsPerGroup.util';
import { IStrategiesConfigData } from '../../../utils/strategyConfigData';
import { formatText } from '../../../utils/formatText.util';
+import CustomDropdown, { Option } from './CustomDropdown';
+import CustomInput from '@components/ui/CustomInput';
import { ChangeEvent, forwardRef } from 'react';
-import CustomDropdown from './CustomDropdown';
import ToggleButton from './ToggleButton';
import RangeSlider from './RangeSlider';
-import CustomText from './CustomText';
import NumberInput from './NumberInput';
+import CustomText from './CustomText';
interface IGroupedConfigProps {
config: IStrategiesConfigData;
@@ -14,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;
@@ -27,6 +29,7 @@ const GroupedConfig = forwardRef(
value,
hasOtherGroup,
uniqueGroups,
+ tradingPairOpts,
handleOnInputChange,
handleOnRangeChange,
handleOnToggle,
@@ -86,19 +89,18 @@ const GroupedConfig = forwardRef(
{cfg.display_type === 'input' || cfg.display_type === 'slider' ? (
typeof cfg.default === 'number' ? (
-
+
{cfg.is_percentage ? '+' : ''}
{cfg.is_percentage ? '%' : ''}
-
(
) : typeof cfg.default === 'string' || cfg.type === 'str' ? (
cfg.default && cfg.default.toString().includes(',') ? (
+ ) : formatText(key) === 'trading pair' ? (
+ {}}
+ isSearchable
+ />
) : (
- (
)}
{objectConfig(group)}
diff --git a/src/pages/overview-bots/components/Header.tsx b/src/pages/overview-bots/components/Header.tsx
index c7dce04..0dece51 100644
--- a/src/pages/overview-bots/components/Header.tsx
+++ b/src/pages/overview-bots/components/Header.tsx
@@ -20,12 +20,20 @@ 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;
@@ -90,16 +98,16 @@ const Header: React.FC = ({
-
+
3
-
+
-
+
{address ? (
@@ -120,7 +128,8 @@ const Header: React.FC = ({
) : (
= ({ number, onRemove }) => (
-
+
{number}
@@ -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..db7244e 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,
@@ -69,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) {
@@ -79,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);
@@ -109,14 +110,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 +149,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 +213,33 @@ const Overview: React.FC
= ({
)}
- {isEmpty ? (
-
- ) : (
-
+
- )}
+
-
+ )}
+
@@ -225,7 +247,9 @@ const Overview: React.FC = ({
{isEmpty ? (
-
+
+ P&L CHART
+
) : (
{getChartTypeQuery === 'pnl' && (
diff --git a/src/pages/overview-bots/components/Performance.tsx b/src/pages/overview-bots/components/Performance.tsx
index 57e36dd..5a7560c 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
-
+
= ({
{/* CardBot */}
- {cardBotData.map((item, idx) => (
+ {isEmpty ? (
- ))}
+ ) : (
+ cardBotData.map((item, idx) => (
+
+ ))
+ )}
{!isEmpty && (
@@ -53,7 +64,7 @@ const Performance: React.FC = ({
].map((icon, idx) => (
{icon}
diff --git a/src/pages/overview-bots/components/StatsTable.tsx b/src/pages/overview-bots/components/StatsTable.tsx
index a312e6c..27d20bf 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,17 +51,15 @@ const StatsTable: React.FC = ({
text={stat.label}
hasQuestionMark={hasQuestionMark}
toolTipText={stat.toolTipText}
+ isEmpty={isEmpty}
/>
-
+
{/* Chart */}
{isEmpty && !stat.progressValue ? (
-
+
) : (
stat.chartData && (
@@ -74,11 +72,14 @@ const StatsTable: React.FC
= ({
)}
{/* Progress Bar */}
{stat.progressValue && (
-
+
{!isEmpty && (
)}
diff --git a/src/pages/overview-bots/components/Stepper.tsx b/src/pages/overview-bots/components/Stepper.tsx
index 0eed907..857bc33 100644
--- a/src/pages/overview-bots/components/Stepper.tsx
+++ b/src/pages/overview-bots/components/Stepper.tsx
@@ -38,7 +38,7 @@ const Stepper: React.FC
= ({ currentStep, setCurrentStep }) => {
index + 1, // Previous step text color
@@ -70,4 +70,3 @@ const Stepper: React.FC = ({ currentStep, setCurrentStep }) => {
};
export default Stepper;
-
diff --git a/src/pages/overview-bots/components/Training.tsx b/src/pages/overview-bots/components/Training.tsx
index 8c7811f..f0d867b 100644
--- a/src/pages/overview-bots/components/Training.tsx
+++ b/src/pages/overview-bots/components/Training.tsx
@@ -1,25 +1,27 @@
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 CandlestickChart from './CandlestickChart';
+import CustomInput from '@components/ui/CustomInput';
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 {
+ ArrowDown2Icon,
+ ArrowUp2Icon,
+ BinanceLogo,
+ CubeLogo,
+ MangoLogo,
+ SolanaLogo,
+ UniswapLogo,
+ USDCLogo,
+} from '@assets/icons';
import React, {
ChangeEvent,
Fragment,
@@ -40,7 +42,13 @@ import Switcher from './Switcher';
import CardBot from './CardBot';
import GoBack from './GoBack';
import LineTab from './LineTab';
-import CustomInput from '@components/ui/CustomInput';
+import {
+ setCoinValues,
+ setEndDate,
+ setExpensesFee,
+ setIsFetchCandleData,
+} from '@slices/generalSlice';
+import GraphChart from './GraphChart';
export interface ITrainingProps {
timeQuery: ITimeTab;
@@ -59,6 +67,12 @@ interface ValueType {
[key: string]: number | string | boolean;
}
+interface ITimestamp {
+ startTime: number;
+ endTime: number;
+ endDate: Date | null;
+}
+
const Training: React.FC = ({
timeQuery,
resultStatQuery,
@@ -89,15 +103,19 @@ const Training: React.FC = ({
const [advancedSettingsOpen, setAdancedSettingsOpen] = useState(false);
const [value, setValue] = useState(Object.fromEntries(valueArr));
const [tradePair, setTradePair] = useState('SOL/BNB');
- const [timeStamp, setTimeStamp] = useState({
+ const [timeStamp, setTimeStamp] = useState({
startTime: 1727771877,
endTime: 1728376677,
- endDate: 0,
+ endDate: null,
});
const [coinValue, setCoinValue] = useState<{ [key: string]: string }>({
SOL: '',
USDC: '',
});
+ const { endDate, disabledRunBacktest, isFetchCandleData } = useAppSelector(
+ (state) => state.general
+ );
+ const dispatch = useAppDispatch();
const uniqueGroups = Array.from(
new Set(Object.values(config[cfgName]).map((item) => item.group))
@@ -119,6 +137,26 @@ const Training: React.FC = ({
{ label: 'Uniswap', value: '7' },
];
+ const tradingPairOpts = [
+ {
+ 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());
+
const toggleAdancedSettingsOpen = () =>
setAdancedSettingsOpen((prevState) => !prevState);
@@ -151,6 +189,8 @@ const Training: React.FC = ({
const { value, name } = evt.target;
if (!/^\d*$/.test(value)) return;
setCoinValue((prevState) => ({ ...prevState, [name]: value }));
+ dispatch(setCoinValues({ [name]: +value }));
+ dispatch(setIsFetchCandleData(false));
};
const handleOnToggle = (isOn: boolean, key: string) => {
@@ -175,33 +215,40 @@ const Training: React.FC = ({
setTimeStamp((prevState) => ({ ...prevState, endTime: unix }));
};
- const endDateUnix = (unix: number) => {
- setTimeStamp((prevState) => ({ ...prevState, endDate: unix }));
+ const getEndDate = (date: Date) => {
+ setTimeStamp((prevState) => ({ ...prevState, endDate: date }));
+ dispatch(setEndDate(date));
};
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();
- }, [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 ?
+ const disabledDeposit =
Object.values(coinValue).some((num) => num === '' || +num <= 0) ||
- timeStamp.endDate === 0 : false;
+ numOfTradeDays < 0 ||
+ !isTodayOrFuture(timeStamp.endDate);
return (
@@ -227,13 +274,19 @@ 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}
/>
{currentStep == 1 || currentStep == 2 ? (
-
+
{`Backtest ${
currentStep === 1
@@ -259,20 +312,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}
@@ -351,9 +401,9 @@ const Training: React.FC = ({
= ({
/>
) : currentStep == 3 ? (
-
+
-
-
-
+
+
+
Connect exchange
= ({
/>
-
-
+
+
Trading time limit
= ({
toolTipText={`Select the date when trading will stop. Trading duration impacts Compute expenses and Solana fees.`}
xtraStyle="mb-4 font-semibold text-xs uppercase"
/>
-
+
-
+
Deposit both coins to start
-
+
{[
{ icon:
, text: 'SOL' },
{ icon:
, text: 'USDC' },
].map(({ icon, text }, idx) => (
-
-
-
-
+
+
+
+ Amount
+
-
-
-
+
+ {text}
<>{icon}>{' '}
+
+ }
+ />
+
+ {['$100', '$300', '$500', 'Max'].map((data, idx) => (
+
+ {data}
+
+ ))}
-
+
+ of connected wallet balance
+
+
))}
Deposit Info
@@ -454,24 +516,24 @@ const Training: React.FC = ({
{depositInfo.map(({ l, r, icon }, i) => (
-
{l}
-
-
+
- {r} {icon && icon}
-
+ {r} {icon && {icon} }
+
))}
@@ -482,37 +544,14 @@ const Training: React.FC = ({
{/* Only show chat when on step 1 and 2 */}
{currentStep == 1 || currentStep == 2 ? (
-
-
- {isLoading ? (
-
-
-
- ) : (
-
- )}
-
-
-
-
+
) : null}
@@ -527,6 +566,7 @@ const Training: React.FC = ({
config={config}
cfgName={cfgName}
value={value}
+ tradingPairOpts={tradingPairOpts}
handleOnInputChange={handleOnInputChange}
handleOnRangeChange={handleOnRangeChange}
handleOnToggle={handleOnToggle}
@@ -536,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/pages/overview-bots/hooks/useProfile.ts b/src/pages/overview-bots/hooks/useProfile.ts
index 7578dea..905acd2 100644
--- a/src/pages/overview-bots/hooks/useProfile.ts
+++ b/src/pages/overview-bots/hooks/useProfile.ts
@@ -1,4 +1,5 @@
import { createElement, ReactNode, useEffect, useState } from 'react';
+import { getDaysBtnDates } from '@utils/getDaysBtnDates.util';
import { useSearchParams } from 'react-router-dom';
import usePageTitle from '@shared/hooks/usePageTitle';
import { useAppSelector } from '@shared/hooks/useStore';
@@ -14,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';
@@ -25,6 +27,7 @@ export interface ITabs {
key:
| ITab
| ITimeTab
+ | ITimeBTab
| IDateTab
| ICryptoTab
| IStratTab
@@ -40,6 +43,7 @@ export interface IStatsTableData {
value: string;
chartData: null | number[];
progressValue: null | number;
+ progressValueColor: string[] | null;
color: string | null;
toolTipText: string | null;
}
@@ -62,6 +66,7 @@ export interface ICardBotData {
name: string;
rate: number;
isPositive: boolean;
+ color?: string;
pieChartData: ICryptoStats[];
lineChartData: number[] | null;
tableData: {
@@ -110,20 +115,18 @@ export interface IDepositInfo {
export default () => {
const { address } = useAppSelector((state) => state.auth);
+ const { coinValue, endDate, expenses, fee } = useAppSelector(
+ (state) => state.general
+ );
const [searchParams, setSearchParams] = useSearchParams();
const { setTitle } = usePageTitle();
-
const [search, setSearch] = useState('');
-
- // const { data } = useGetUserInfoQuery({ address: session?.address });
-
- // const user = data as IUserInfo;
-
const query: ITab = (searchParams.get('tab') as ITab) || 'overview';
const dateQuery = (searchParams.get('date') as IDateTab) || 'week';
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 =
@@ -202,6 +205,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 },
@@ -234,34 +243,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',
},
];
@@ -298,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',
@@ -307,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.',
@@ -316,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.',
@@ -325,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',
@@ -334,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.',
@@ -346,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',
@@ -355,33 +370,27 @@ 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.',
},
{
- label: 'Successful',
- value: '133',
+ label: 'Average profit',
+ 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.',
},
- {
- 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%',
- 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',
@@ -394,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,
},
@@ -402,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,
},
@@ -410,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,
},
@@ -418,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,
},
@@ -426,6 +439,7 @@ export default () => {
value: '210%',
chartData: null,
progressValue: null,
+ progressValueColor: null,
color: null,
toolTipText: null,
},
@@ -434,26 +448,19 @@ export default () => {
value: '2.81',
chartData: null,
progressValue: null,
+ progressValueColor: null,
color: null,
toolTipText: null,
},
];
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,
+ progressValueColor: null,
color: '#F44336',
toolTipText:
'The estimated cost for running your trading bots, including data processing and computational resources, billed monthly',
@@ -463,19 +470,30 @@ 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.',
},
{
- label: 'Av taker fee',
+ label: 'Av. taker fee',
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.',
},
+ {
+ label: 'Fees paid',
+ value: '$32',
+ chartData: [50, 60, 40, 49, 38, 34, 80, 76, 95, 100],
+ progressValue: null,
+ progressValueColor: null,
+ color: '#4CAF50',
+ toolTipText: null,
+ },
];
const statsDataLock: IStatsTableData[] = [
@@ -484,6 +502,7 @@ export default () => {
value: '550',
chartData: null,
progressValue: 50,
+ progressValueColor: ['#60B3D7', '#E6F4FE'],
color: null,
toolTipText: null,
},
@@ -492,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,
},
@@ -500,6 +520,7 @@ export default () => {
value: '2%',
chartData: null,
progressValue: 20,
+ progressValueColor: ['#60B3D7', '#E6F4FE'],
color: null,
toolTipText: null,
},
@@ -508,6 +529,7 @@ export default () => {
value: '3%',
chartData: null,
progressValue: 30,
+ progressValueColor: ['#60B3D7', '#E6F4FE'],
color: null,
toolTipText: null,
},
@@ -518,12 +540,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,
},
@@ -531,7 +554,7 @@ export default () => {
amount: 35,
tag: 'loss',
percentage: 35,
- color: '#CE2C31',
+ color: '#FFAFB2',
isProfit: false,
value: null,
},
@@ -539,20 +562,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,
},
@@ -562,12 +585,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,
},
@@ -575,7 +599,7 @@ export default () => {
amount: 41,
tag: 'loss',
percentage: 41,
- color: '#CE2C31',
+ color: '#FFAFB2',
isProfit: false,
value: null,
},
@@ -583,20 +607,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,
},
@@ -606,12 +630,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,
},
@@ -619,7 +644,7 @@ export default () => {
amount: 51,
tag: 'loss',
percentage: 51,
- color: '#CE2C31',
+ color: '#FFAFB2',
isProfit: true,
value: null,
},
@@ -627,20 +652,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,
},
@@ -650,12 +675,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,
},
@@ -663,7 +689,7 @@ export default () => {
amount: 51,
tag: 'loss',
percentage: 51,
- color: '#CE2C31',
+ color: '#FFAFB2',
isProfit: true,
value: null,
},
@@ -671,20 +697,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,
},
@@ -702,7 +728,7 @@ export default () => {
amount: 68,
tag: 'profit',
percentage: 68,
- color: '#218358',
+ color: '#A3E5C8',
isProfit: true,
value: null,
},
@@ -710,7 +736,7 @@ export default () => {
amount: 32,
tag: 'loss',
percentage: 32,
- color: '#CE2C31',
+ color: '#FFAFB2',
isProfit: false,
value: null,
},
@@ -746,7 +772,7 @@ export default () => {
amount: 65,
tag: 'profit',
percentage: 65,
- color: '#218358',
+ color: '#A3E5C8',
isProfit: true,
value: null,
},
@@ -754,7 +780,7 @@ export default () => {
amount: 35,
tag: 'loss',
percentage: 35,
- color: '#CE2C31',
+ color: '#FFAFB2',
isProfit: false,
value: null,
},
@@ -790,7 +816,7 @@ export default () => {
amount: 48,
tag: 'profit',
percentage: 48,
- color: '#218358',
+ color: '#A3E5C8',
isProfit: true,
value: null,
},
@@ -798,7 +824,7 @@ export default () => {
amount: 52,
tag: 'loss',
percentage: 52,
- color: '#CE2C31',
+ color: '#FFAFB2',
isProfit: false,
value: null,
},
@@ -834,7 +860,7 @@ export default () => {
amount: 46,
tag: 'profit',
percentage: 46,
- color: '#218358',
+ color: '#A3E5C8',
isProfit: true,
value: null,
},
@@ -842,7 +868,7 @@ export default () => {
amount: 54,
tag: 'loss',
percentage: 54,
- color: '#CE2C31',
+ color: '#FFAFB2',
isProfit: false,
value: null,
},
@@ -957,6 +983,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 +995,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%'],
@@ -977,13 +1005,15 @@ export default () => {
['Timespan', '2024-05-01 / 2024-05-31'],
];
+ const numOfTradeDays = getDaysBtnDates(endDate ? endDate : new Date());
+
const depositInfo: IDepositInfo[] = [
- { l: 'Market', r: 'SOL / USDC', icon: null },
- { l: 'Number of trading days', r: '0', icon: null },
- { l: 'Compute expenses', r: '$0', icon: null },
- { l: 'Solana fees', r: '$0', icon: null },
- { l: 'SOL', r: '0', icon: createElement(SolanaLogo) },
- { l: 'USDC', r: '0', icon: createElement(USDCLogo) },
+ { 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 },
+ { l: 'SOL', r: `${coinValue.SOL}`, icon: createElement(SolanaLogo) },
+ { l: 'USDC', r: `${coinValue.USDC}`, icon: createElement(USDCLogo) },
{ l: 'Total', r: '$0', icon: null },
];
@@ -995,6 +1025,7 @@ export default () => {
tabs,
dateTabs,
timeTabs,
+ timeBTabs,
cryptoTabs,
perfTabs,
stratTabs,
@@ -1019,6 +1050,7 @@ export default () => {
// user,
query,
dateQuery,
+ timeBQuery,
timeQuery,
perfQuery,
cryptoQuery,
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
new file mode 100644
index 0000000..ad4b417
--- /dev/null
+++ b/src/slices/generalSlice.ts
@@ -0,0 +1,61 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+
+interface AppState {
+ endDate: Date | null;
+ expenses: number;
+ isFetchCandleData: boolean;
+ disabledRunBacktest: boolean;
+ fee: number;
+ coinValue: { [key: string]: number };
+}
+
+const initialState: AppState = {
+ endDate: null,
+ expenses: 0,
+ isFetchCandleData: true,
+ disabledRunBacktest: true,
+ fee: 0,
+ coinValue: {
+ SOL: 0,
+ USDC: 0,
+ },
+};
+
+export const generalSlice = createSlice({
+ name: 'general',
+ initialState,
+ reducers: {
+ setCoinValues: (
+ state,
+ { payload }: { payload: { [key: string]: number } }
+ ) => {
+ state.coinValue = { ...state.coinValue, ...payload };
+ },
+ setEndDate: (state, action: PayloadAction) => {
+ state.endDate = action.payload;
+ },
+ setExpensesFee: (
+ state,
+ { payload }: { payload: { expenses: number; fee: number } }
+ ) => {
+ 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 {
+ setEndDate,
+ setCoinValues,
+ setExpensesFee,
+ setIsFetchCandleData,
+ setDisabledRunBacktest,
+} = generalSlice.actions;
+
+export default generalSlice;
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/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/src/store/reducers.ts b/src/store/reducers.ts
index 4e6eba0..445d4a8 100644
--- a/src/store/reducers.ts
+++ b/src/store/reducers.ts
@@ -3,9 +3,11 @@ import appSlice from '@slices/appSlice';
import auth from './auth/slice';
import { robotterApi, transactionsApi } from './config';
import { websocketApi } from './wsApi';
+import generalSlice from '@slices/generalSlice';
export const rootReducer = combineReducers({
app: appSlice.reducer,
+ general: generalSlice.reducer,
auth,
// RTK Query Setup
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,
}),
diff --git a/src/utils/getDaysBtnDates.util.ts b/src/utils/getDaysBtnDates.util.ts
new file mode 100644
index 0000000..46f7274
--- /dev/null
+++ b/src/utils/getDaysBtnDates.util.ts
@@ -0,0 +1,40 @@
+export const getDaysBtnDates = (date: Date): number => {
+ const currentDate = new Date();
+
+ // Normalize both dates to the same base day, keeping time intact
+ const today = new Date(
+ currentDate.getFullYear(),
+ currentDate.getMonth(),
+ currentDate.getDate(),
+ currentDate.getHours(),
+ currentDate.getMinutes()
+ );
+
+ const targetDate = new Date(
+ date.getFullYear(),
+ date.getMonth(),
+ date.getDate(),
+ date.getHours(),
+ date.getMinutes()
+ );
+
+ // If the target date is in the past, return 0
+ if (targetDate < today) return 0;
+
+ // 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;
+};
diff --git a/tailwind.config.cjs b/tailwind.config.cjs
index bfb7903..b5a0b4f 100644
--- a/tailwind.config.cjs
+++ b/tailwind.config.cjs
@@ -3,6 +3,10 @@ module.exports = {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: {
extend: {
+ fontFamily: {
+ inter: ['Inter'],
+ ubuntumono: ['Ubuntu Mono', 'sans-serif'],
+ },
boxShadow: {
custom: '0px 16px 32px 0px #16253533',
},
@@ -24,10 +28,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 +52,7 @@ module.exports = {
'text-dark': '#244141',
'form-bg': '#F6FAFB',
states: '#EFB621',
+ 'yellow-200': '#F6E2AC',
turkish: '#80FFED',
},
},
diff --git a/tsconfig.json b/tsconfig.json
index 14186a4..9f89694 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -27,7 +27,8 @@
"@pages/*": ["src/pages/*"],
"@shared/*": ["src/shared/*"],
"@slices/*": ["src/slices/*"],
- "@store/*": ["src/store/*"]
+ "@store/*": ["src/store/*"],
+ "@utils/*": ["src/utils/*"]
}
},
"include": [".eslintrc.json", "src"],
diff --git a/vite.config.ts b/vite.config.ts
index dae8665..31870d4 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -17,6 +17,7 @@ export default defineConfig({
'@shared': path.resolve(__dirname, './src/shared'),
'@slices': path.resolve(__dirname, './src/slices'),
'@store': path.resolve(__dirname, './src/store'),
+ '@utils': path.resolve(__dirname, './src/utils'),
},
},
});