Display data for bots
This commit is contained in:
@@ -0,0 +1,111 @@
|
|||||||
|
import React, {useEffect, useState} from 'react'
|
||||||
|
import type {Position, UserStrategyDetailsViewModel} from '../../../generated/ManagingApi'
|
||||||
|
import {DataClient} from '../../../generated/ManagingApi'
|
||||||
|
import useApiUrlStore from '../../../app/store/apiStore'
|
||||||
|
import Modal from '../Modal/Modal'
|
||||||
|
|
||||||
|
interface TradesModalProps {
|
||||||
|
showModal: boolean
|
||||||
|
botName: string | null
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const TradesModal: React.FC<TradesModalProps> = ({
|
||||||
|
showModal,
|
||||||
|
botName,
|
||||||
|
onClose,
|
||||||
|
}) => {
|
||||||
|
const { apiUrl } = useApiUrlStore()
|
||||||
|
const [strategyData, setStrategyData] = useState<UserStrategyDetailsViewModel | null>(null)
|
||||||
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (showModal && botName) {
|
||||||
|
fetchStrategyData()
|
||||||
|
}
|
||||||
|
}, [showModal, botName])
|
||||||
|
|
||||||
|
const fetchStrategyData = async () => {
|
||||||
|
if (!botName) return
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
const client = new DataClient({}, apiUrl)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await client.data_GetUserStrategy("Oda", botName)
|
||||||
|
setStrategyData(data)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching strategy data:', error)
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
showModal={showModal}
|
||||||
|
onClose={onClose}
|
||||||
|
titleHeader={`Trades for ${botName}`}
|
||||||
|
>
|
||||||
|
<div className="mt-4">
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<div className="loading loading-spinner loading-lg"></div>
|
||||||
|
</div>
|
||||||
|
) : strategyData ? (
|
||||||
|
<div>
|
||||||
|
<div className="mb-4">
|
||||||
|
<p className="font-bold">Strategy: {strategyData.strategyName}</p>
|
||||||
|
<p>Win Rate: {strategyData.winRate?.toFixed(2)}%</p>
|
||||||
|
<p>PnL: {strategyData.pnL?.toFixed(2)} $</p>
|
||||||
|
<p>Wins: {strategyData.wins} / Losses: {strategyData.losses}</p>
|
||||||
|
<p>Total Volume Traded: {strategyData.totalVolumeTraded?.toFixed(2)} $</p>
|
||||||
|
<p>Volume Last 24H: {strategyData.volumeLast24H?.toFixed(2)} $</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<table className="table table-compact w-full">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Direction</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Entry Price</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
<th>PnL</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{strategyData.positions && strategyData.positions.length > 0 ? (
|
||||||
|
strategyData.positions.map((position: Position) => (
|
||||||
|
<tr key={position.identifier}>
|
||||||
|
<td>{new Date(position.date).toLocaleString()}</td>
|
||||||
|
<td className={position.originDirection === 'Long' ? 'text-success' : 'text-error'}>
|
||||||
|
{position.originDirection}
|
||||||
|
</td>
|
||||||
|
<td>{position.status}</td>
|
||||||
|
<td>{position.open.price.toFixed(2)}</td>
|
||||||
|
<td>{position.open.quantity.toFixed(4)}</td>
|
||||||
|
<td className={position.profitAndLoss?.realized && position.profitAndLoss.realized > 0 ? 'text-success' : 'text-error'}>
|
||||||
|
{position.profitAndLoss?.realized?.toFixed(2) || '0.00'} $
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={6} className="text-center">No trades found</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-center">No data available</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TradesModal
|
||||||
@@ -941,6 +941,189 @@ export class DataClient extends AuthorizedApiBase {
|
|||||||
}
|
}
|
||||||
return Promise.resolve<Candle[]>(null as any);
|
return Promise.resolve<Candle[]>(null as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data_GetStrategiesStatistics(): Promise<StrategiesStatisticsViewModel> {
|
||||||
|
let url_ = this.baseUrl + "/Data/GetStrategiesStatistics";
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
let options_: RequestInit = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||||
|
return this.http.fetch(url_, transformedOptions_);
|
||||||
|
}).then((_response: Response) => {
|
||||||
|
return this.processData_GetStrategiesStatistics(_response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected processData_GetStrategiesStatistics(response: Response): Promise<StrategiesStatisticsViewModel> {
|
||||||
|
const status = response.status;
|
||||||
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
|
if (status === 200) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
let result200: any = null;
|
||||||
|
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as StrategiesStatisticsViewModel;
|
||||||
|
return result200;
|
||||||
|
});
|
||||||
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve<StrategiesStatisticsViewModel>(null as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_GetTopStrategies(): Promise<TopStrategiesViewModel> {
|
||||||
|
let url_ = this.baseUrl + "/Data/GetTopStrategies";
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
let options_: RequestInit = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||||
|
return this.http.fetch(url_, transformedOptions_);
|
||||||
|
}).then((_response: Response) => {
|
||||||
|
return this.processData_GetTopStrategies(_response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected processData_GetTopStrategies(response: Response): Promise<TopStrategiesViewModel> {
|
||||||
|
const status = response.status;
|
||||||
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
|
if (status === 200) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
let result200: any = null;
|
||||||
|
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as TopStrategiesViewModel;
|
||||||
|
return result200;
|
||||||
|
});
|
||||||
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve<TopStrategiesViewModel>(null as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_GetUserStrategies(agentName: string | null | undefined): Promise<UserStrategyDetailsViewModel[]> {
|
||||||
|
let url_ = this.baseUrl + "/Data/GetUserStrategies?";
|
||||||
|
if (agentName !== undefined && agentName !== null)
|
||||||
|
url_ += "agentName=" + encodeURIComponent("" + agentName) + "&";
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
let options_: RequestInit = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||||
|
return this.http.fetch(url_, transformedOptions_);
|
||||||
|
}).then((_response: Response) => {
|
||||||
|
return this.processData_GetUserStrategies(_response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected processData_GetUserStrategies(response: Response): Promise<UserStrategyDetailsViewModel[]> {
|
||||||
|
const status = response.status;
|
||||||
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
|
if (status === 200) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
let result200: any = null;
|
||||||
|
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UserStrategyDetailsViewModel[];
|
||||||
|
return result200;
|
||||||
|
});
|
||||||
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve<UserStrategyDetailsViewModel[]>(null as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_GetUserStrategy(agentName: string | null | undefined, strategyName: string | null | undefined): Promise<UserStrategyDetailsViewModel> {
|
||||||
|
let url_ = this.baseUrl + "/Data/GetUserStrategy?";
|
||||||
|
if (agentName !== undefined && agentName !== null)
|
||||||
|
url_ += "agentName=" + encodeURIComponent("" + agentName) + "&";
|
||||||
|
if (strategyName !== undefined && strategyName !== null)
|
||||||
|
url_ += "strategyName=" + encodeURIComponent("" + strategyName) + "&";
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
let options_: RequestInit = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||||
|
return this.http.fetch(url_, transformedOptions_);
|
||||||
|
}).then((_response: Response) => {
|
||||||
|
return this.processData_GetUserStrategy(_response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected processData_GetUserStrategy(response: Response): Promise<UserStrategyDetailsViewModel> {
|
||||||
|
const status = response.status;
|
||||||
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
|
if (status === 200) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
let result200: any = null;
|
||||||
|
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UserStrategyDetailsViewModel;
|
||||||
|
return result200;
|
||||||
|
});
|
||||||
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve<UserStrategyDetailsViewModel>(null as any);
|
||||||
|
}
|
||||||
|
|
||||||
|
data_GetPlatformSummary(timeFilter: string | null | undefined): Promise<PlatformSummaryViewModel> {
|
||||||
|
let url_ = this.baseUrl + "/Data/GetPlatformSummary?";
|
||||||
|
if (timeFilter !== undefined && timeFilter !== null)
|
||||||
|
url_ += "timeFilter=" + encodeURIComponent("" + timeFilter) + "&";
|
||||||
|
url_ = url_.replace(/[?&]$/, "");
|
||||||
|
|
||||||
|
let options_: RequestInit = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.transformOptions(options_).then(transformedOptions_ => {
|
||||||
|
return this.http.fetch(url_, transformedOptions_);
|
||||||
|
}).then((_response: Response) => {
|
||||||
|
return this.processData_GetPlatformSummary(_response);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected processData_GetPlatformSummary(response: Response): Promise<PlatformSummaryViewModel> {
|
||||||
|
const status = response.status;
|
||||||
|
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||||
|
if (status === 200) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
let result200: any = null;
|
||||||
|
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as PlatformSummaryViewModel;
|
||||||
|
return result200;
|
||||||
|
});
|
||||||
|
} else if (status !== 200 && status !== 204) {
|
||||||
|
return response.text().then((_responseText) => {
|
||||||
|
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve<PlatformSummaryViewModel>(null as any);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MoneyManagementClient extends AuthorizedApiBase {
|
export class MoneyManagementClient extends AuthorizedApiBase {
|
||||||
@@ -2338,7 +2521,7 @@ export interface Position {
|
|||||||
signalIdentifier?: string | null;
|
signalIdentifier?: string | null;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
initiator: PositionInitiator;
|
initiator: PositionInitiator;
|
||||||
user?: User | null;
|
user: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum TradeDirection {
|
export enum TradeDirection {
|
||||||
@@ -2642,8 +2825,6 @@ export interface TradingBot {
|
|||||||
export interface OpenPositionManuallyRequest {
|
export interface OpenPositionManuallyRequest {
|
||||||
botName?: string | null;
|
botName?: string | null;
|
||||||
direction?: TradeDirection;
|
direction?: TradeDirection;
|
||||||
stopLossPrice?: number;
|
|
||||||
takeProfitPrice?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SpotlightOverview {
|
export interface SpotlightOverview {
|
||||||
@@ -2690,6 +2871,60 @@ export interface TickerSignal {
|
|||||||
oneDay: Signal[];
|
oneDay: Signal[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface StrategiesStatisticsViewModel {
|
||||||
|
totalStrategiesRunning?: number;
|
||||||
|
changeInLast24Hours?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TopStrategiesViewModel {
|
||||||
|
topStrategies?: StrategyPerformance[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StrategyPerformance {
|
||||||
|
strategyName?: string | null;
|
||||||
|
pnL?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserStrategyDetailsViewModel {
|
||||||
|
name?: string | null;
|
||||||
|
strategyName?: string | null;
|
||||||
|
state?: string | null;
|
||||||
|
pnL?: number;
|
||||||
|
roiPercentage?: number;
|
||||||
|
roiLast24H?: number;
|
||||||
|
runtime?: Date;
|
||||||
|
winRate?: number;
|
||||||
|
totalVolumeTraded?: number;
|
||||||
|
volumeLast24H?: number;
|
||||||
|
wins?: number;
|
||||||
|
losses?: number;
|
||||||
|
positions?: Position[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PlatformSummaryViewModel {
|
||||||
|
totalAgents?: number;
|
||||||
|
totalActiveStrategies?: number;
|
||||||
|
totalPlatformPnL?: number;
|
||||||
|
totalPlatformVolume?: number;
|
||||||
|
totalPlatformVolumeLast24h?: number;
|
||||||
|
agentSummaries?: AgentSummaryViewModel[] | null;
|
||||||
|
timeFilter?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentSummaryViewModel {
|
||||||
|
username?: string | null;
|
||||||
|
totalPnL?: number;
|
||||||
|
pnLLast24h?: number;
|
||||||
|
totalROI?: number;
|
||||||
|
roiLast24h?: number;
|
||||||
|
wins?: number;
|
||||||
|
losses?: number;
|
||||||
|
averageWinRate?: number;
|
||||||
|
activeStrategiesCount?: number;
|
||||||
|
totalVolume?: number;
|
||||||
|
volumeLast24h?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export enum RiskLevel {
|
export enum RiskLevel {
|
||||||
Low = "Low",
|
Low = "Low",
|
||||||
Medium = "Medium",
|
Medium = "Medium",
|
||||||
|
|||||||
@@ -1,21 +1,13 @@
|
|||||||
import { EyeIcon, PlayIcon, StopIcon, TrashIcon, PlusCircleIcon } from '@heroicons/react/solid'
|
import {ChartBarIcon, EyeIcon, PlayIcon, PlusCircleIcon, StopIcon, TrashIcon} from '@heroicons/react/solid'
|
||||||
import React, {useState} from 'react'
|
import React, {useState} from 'react'
|
||||||
|
|
||||||
import useApiUrlStore from '../../app/store/apiStore'
|
import useApiUrlStore from '../../app/store/apiStore'
|
||||||
import {
|
import {CardPosition, CardSignal, CardText, Toast,} from '../../components/mollecules'
|
||||||
CardPosition,
|
|
||||||
CardSignal,
|
|
||||||
CardText,
|
|
||||||
Toast,
|
|
||||||
} from '../../components/mollecules'
|
|
||||||
import ManualPositionModal from '../../components/mollecules/ManualPositionModal'
|
import ManualPositionModal from '../../components/mollecules/ManualPositionModal'
|
||||||
|
import TradesModal from '../../components/mollecules/TradesModal/TradesModal'
|
||||||
import {TradeChart} from '../../components/organism'
|
import {TradeChart} from '../../components/organism'
|
||||||
|
import type {BotType, MoneyManagement, TradingBot,} from '../../generated/ManagingApi'
|
||||||
import {BotClient} from '../../generated/ManagingApi'
|
import {BotClient} from '../../generated/ManagingApi'
|
||||||
import type {
|
|
||||||
BotType,
|
|
||||||
MoneyManagement,
|
|
||||||
TradingBot,
|
|
||||||
} from '../../generated/ManagingApi'
|
|
||||||
import type {IBotList} from '../../global/type'
|
import type {IBotList} from '../../global/type'
|
||||||
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
|
import MoneyManagementModal from '../settingsPage/moneymanagement/moneyManagementModal'
|
||||||
|
|
||||||
@@ -44,6 +36,8 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
useState<MoneyManagement>()
|
useState<MoneyManagement>()
|
||||||
const [showManualPositionModal, setShowManualPositionModal] = useState(false)
|
const [showManualPositionModal, setShowManualPositionModal] = useState(false)
|
||||||
const [selectedBotForManualPosition, setSelectedBotForManualPosition] = useState<string | null>(null)
|
const [selectedBotForManualPosition, setSelectedBotForManualPosition] = useState<string | null>(null)
|
||||||
|
const [showTradesModal, setShowTradesModal] = useState(false)
|
||||||
|
const [selectedBotForTrades, setSelectedBotForTrades] = useState<string | null>(null)
|
||||||
|
|
||||||
function getIsForWatchingBadge(isForWatchingOnly: boolean, name: string) {
|
function getIsForWatchingBadge(isForWatchingOnly: boolean, name: string) {
|
||||||
const classes =
|
const classes =
|
||||||
@@ -135,11 +129,27 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTradesBadge(botName: string) {
|
||||||
|
const classes = baseBadgeClass() + ' bg-secondary'
|
||||||
|
return (
|
||||||
|
<button className={classes} onClick={() => openTradesModal(botName)}>
|
||||||
|
<p className="text-primary-content flex">
|
||||||
|
<ChartBarIcon width={15}></ChartBarIcon>
|
||||||
|
</p>
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function openManualPositionModal(botName: string) {
|
function openManualPositionModal(botName: string) {
|
||||||
setSelectedBotForManualPosition(botName)
|
setSelectedBotForManualPosition(botName)
|
||||||
setShowManualPositionModal(true)
|
setShowManualPositionModal(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openTradesModal(botName: string) {
|
||||||
|
setSelectedBotForTrades(botName)
|
||||||
|
setShowTradesModal(true)
|
||||||
|
}
|
||||||
|
|
||||||
function toggleBotStatus(status: string, name: string, botType: BotType) {
|
function toggleBotStatus(status: string, name: string, botType: BotType) {
|
||||||
const isUp = status == 'Up'
|
const isUp = status == 'Up'
|
||||||
const t = new Toast(isUp ? 'Stoping bot' : 'Restarting bot')
|
const t = new Toast(isUp ? 'Stoping bot' : 'Restarting bot')
|
||||||
@@ -246,6 +256,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
<div className={baseBadgeClass(true)}>
|
<div className={baseBadgeClass(true)}>
|
||||||
PNL {bot.profitAndLoss.toFixed(2).toString()} $
|
PNL {bot.profitAndLoss.toFixed(2).toString()} $
|
||||||
</div>
|
</div>
|
||||||
|
{getTradesBadge(bot.name)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -267,6 +278,14 @@ const BotList: React.FC<IBotList> = ({ list }) => {
|
|||||||
setSelectedBotForManualPosition(null)
|
setSelectedBotForManualPosition(null)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<TradesModal
|
||||||
|
showModal={showTradesModal}
|
||||||
|
botName={selectedBotForTrades}
|
||||||
|
onClose={() => {
|
||||||
|
setShowTradesModal(false)
|
||||||
|
setSelectedBotForTrades(null)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user