From aacb92018f24afe843ac6f96b53a79b288fe8101 Mon Sep 17 00:00:00 2001 From: cryptooda Date: Thu, 14 Aug 2025 17:42:07 +0700 Subject: [PATCH] Update cache for userStrategy details --- definition-pinky | 4 - .../Controllers/DataController.cs | 13 - .../ManageBot/BotService.cs | 44 ++- .../src/pages/dashboardPage/agentStrategy.tsx | 358 ++++++++++++++++++ .../src/pages/dashboardPage/dashboard.tsx | 6 + 5 files changed, 390 insertions(+), 35 deletions(-) delete mode 100644 definition-pinky create mode 100644 src/Managing.WebApp/src/pages/dashboardPage/agentStrategy.tsx diff --git a/definition-pinky b/definition-pinky deleted file mode 100644 index f16793d..0000000 --- a/definition-pinky +++ /dev/null @@ -1,4 +0,0 @@ -{ - "schemaVersion": 2, - "dockerfilePath": "./src/Managing.Pinky/Dockerfile-pinky" - } \ No newline at end of file diff --git a/src/Managing.Api/Controllers/DataController.cs b/src/Managing.Api/Controllers/DataController.cs index 067bdce..aef5e2e 100644 --- a/src/Managing.Api/Controllers/DataController.cs +++ b/src/Managing.Api/Controllers/DataController.cs @@ -398,16 +398,6 @@ public class DataController : ControllerBase [HttpGet("GetUserStrategy")] public async Task> GetUserStrategy(string agentName, string strategyName) { - string cacheKey = $"UserStrategy_{agentName}_{strategyName}"; - - // Check if the user strategy details are already cached - var cachedDetails = _cacheService.GetValue(cacheKey); - - if (cachedDetails != null) - { - return Ok(cachedDetails); - } - // Get the specific strategy for the user var strategy = await _mediator.Send(new GetUserStrategyCommand(agentName, strategyName)); @@ -419,9 +409,6 @@ public class DataController : ControllerBase // Map the strategy to a view model using the shared method var result = MapStrategyToViewModel(strategy); - // Cache the results for 5 minutes - _cacheService.SaveValue(cacheKey, result, TimeSpan.FromMinutes(5)); - return Ok(result); } diff --git a/src/Managing.Application/ManageBot/BotService.cs b/src/Managing.Application/ManageBot/BotService.cs index afe4395..bbdb87f 100644 --- a/src/Managing.Application/ManageBot/BotService.cs +++ b/src/Managing.Application/ManageBot/BotService.cs @@ -295,24 +295,32 @@ namespace Managing.Application.ManageBot } // Check if bot already exists in database - var existingBot = await _botRepository.GetBotByIdentifierAsync(bot.Identifier); - - if (existingBot != null) - { - // Update existing bot - await _botRepository.UpdateBot(bot); - _tradingBotLogger.LogDebug( - "Updated bot statistics for bot {BotId}: Wins={Wins}, Losses={Losses}, PnL={PnL}, ROI={ROI}%, Volume={Volume}, Fees={Fees}", - bot.Identifier, bot.TradeWins, bot.TradeLosses, bot.Pnl, bot.Roi, bot.Volume, bot.Fees); - } - else - { - // Insert new bot - await _botRepository.InsertBotAsync(bot); - _tradingBotLogger.LogInformation( - "Created new bot statistics for bot {BotId}: Wins={Wins}, Losses={Losses}, PnL={PnL}, ROI={ROI}%, Volume={Volume}, Fees={Fees}", - bot.Identifier, bot.TradeWins, bot.TradeLosses, bot.Pnl, bot.Roi, bot.Volume, bot.Fees); - } + await ServiceScopeHelpers.WithScopedService( + _scopeFactory, + async repo => + { + var existingBot = await repo.GetBotByIdentifierAsync(bot.Identifier); + if (existingBot == null) + { + _tradingBotLogger.LogInformation("Creating new bot statistics for bot {BotId}", + bot.Identifier); + // Update existing bot + await _botRepository.UpdateBot(bot); + _tradingBotLogger.LogDebug( + "Updated bot statistics for bot {BotId}: Wins={Wins}, Losses={Losses}, PnL={PnL}, ROI={ROI}%, Volume={Volume}, Fees={Fees}", + bot.Identifier, bot.TradeWins, bot.TradeLosses, bot.Pnl, bot.Roi, bot.Volume, bot.Fees); + } + else + { + _tradingBotLogger.LogInformation("Updating existing bot statistics for bot {BotId}", + bot.Identifier); + // Insert new bot + await _botRepository.InsertBotAsync(bot); + _tradingBotLogger.LogInformation( + "Created new bot statistics for bot {BotId}: Wins={Wins}, Losses={Losses}, PnL={PnL}, ROI={ROI}%, Volume={Volume}, Fees={Fees}", + bot.Identifier, bot.TradeWins, bot.TradeLosses, bot.Pnl, bot.Roi, bot.Volume, bot.Fees); + } + }); return true; } diff --git a/src/Managing.WebApp/src/pages/dashboardPage/agentStrategy.tsx b/src/Managing.WebApp/src/pages/dashboardPage/agentStrategy.tsx new file mode 100644 index 0000000..ad66c25 --- /dev/null +++ b/src/Managing.WebApp/src/pages/dashboardPage/agentStrategy.tsx @@ -0,0 +1,358 @@ +import React, {useEffect, useState} from 'react' +import {PlayIcon, StopIcon} from '@heroicons/react/solid' +import { + BotClient, + BotStatus, + DataClient, + Position, + TradeDirection, + UserStrategyDetailsViewModel +} from '../../generated/ManagingApi' +import useApiUrlStore from '../../app/store/apiStore' +import {Toast} from '../../components/mollecules' + +interface AgentStrategyProps { + index: number +} + +const AgentStrategy: React.FC = ({ index }) => { + const { apiUrl } = useApiUrlStore() + const botClient = new BotClient({}, apiUrl) + + const [strategyData, setStrategyData] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + const [agentName, setAgentName] = useState('') + const [strategyName, setStrategyName] = useState('') + const [activeTab, setActiveTab] = useState<'stats' | 'history'>('stats') + + const fetchStrategyData = async () => { + if (!agentName || !strategyName) return + + try { + setLoading(true) + setError(null) + const dataClient = new DataClient({}, apiUrl) + const data = await dataClient.data_GetUserStrategy(agentName, strategyName) + setStrategyData(data) + } catch (err) { + setError('Failed to load strategy data') + console.error('Error fetching strategy data:', err) + } finally { + setLoading(false) + } + } + + useEffect(() => { + fetchStrategyData() + }, [agentName, strategyName]) + + const formatCurrency = (value?: number | null) => { + if (value === null || value === undefined) return '$0.00' + return new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + minimumFractionDigits: 2, + }).format(value) + } + + const formatPercentage = (value?: number | null) => { + if (value === null || value === undefined) return '0%' + return `${value >= 0 ? '+' : ''}${value.toFixed(2)}%` + } + + const formatDuration = (runtime?: Date | null) => { + if (!runtime) return '0h' + const now = new Date() + const start = new Date(runtime) + const diffMs = now.getTime() - start.getTime() + const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)) + const diffHours = Math.floor((diffMs % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) + + if (diffDays > 0) { + return `${diffDays} week${diffDays > 6 ? 's' : ''} ${diffHours}h` + } + return `${diffHours}h` + } + + const getStatusBadge = (state: string | null | undefined) => { + const badgeClass = state === 'Up' + ? 'badge badge-success' + : state === 'Down' + ? 'badge badge-error' + : 'badge badge-neutral' + + return {state} + } + + const toggleStrategyStatus = (status: string | null | undefined, identifier: string) => { + const isUp = status === 'Up' + const t = new Toast(isUp ? 'Stopping strategy' : 'Starting strategy') + + console.log('toggleStrategyStatus', status, identifier) + if (status === 'Up') { + botClient + .bot_Stop(identifier) + .then(() => { + t.update('success', 'Strategy stopped') + // Refresh the strategy data to get updated status + fetchStrategyData() + }) + .catch((err) => { + t.update('error', err) + }) + } else if (status === 'Down' || status === 'None') { + botClient + .bot_Restart(identifier) + .then(() => { + t.update('success', 'Strategy started') + // Refresh the strategy data to get updated status + fetchStrategyData() + }) + .catch((err) => { + t.update('error', err) + }) + } + } + + return ( +
+ {/* Header Section */} +
+
+
+ + setAgentName(e.target.value)} + /> +
+
+ + setStrategyName(e.target.value)} + /> +
+ +
+
+ + {loading && ( +
+ +
+ )} + + {error && ( +
+ {error} +
+ )} + + {strategyData && ( + <> + {/* Strategy Header */} +
+
+

{strategyData.name || '[Strategy Name]'}

+ {getStatusBadge(strategyData.state as BotStatus)} +
+
+ + +
+
+ + {/* Main Statistics */} +
+
+
Winrate
+
{strategyData.winRate || 0}%
+
+
+
PnL
+
{formatCurrency(strategyData.pnL)}
+
+
+
Backtest
+
3
+
+
+
Runtime
+
{formatDuration(strategyData.runtime)}
+
+
+ + {/* Chart Placeholder */} +
+
+
📈
+
Strategy Performance Chart
+
Chart visualization will be implemented here
+
+
+ + {/* Information Tabs */} +
+

Informations

+
+ + +
+
+ + {/* Tab Content */} + {activeTab === 'stats' && ( +
+
+
+

Total Volume Traded

+
{formatCurrency(strategyData.totalVolumeTraded)}
+
+ {formatCurrency(strategyData.volumeLast24H)} Today +
+
+
+ +
+
+

Win / Loss

+
+ {strategyData.wins || 0}/{strategyData.losses || 0} +
+
+ {((strategyData.winRate || 0) / 100).toFixed(1)}% Avg Winrate +
+
+
+ +
+
+

ROI

+
{formatPercentage(strategyData.roiPercentage)}
+
+ {formatPercentage(strategyData.roiLast24H)} Today +
+
+
+ +
+
+

PnL $

+
{formatCurrency(strategyData.pnL)}
+
+ {formatCurrency((strategyData.pnL || 0) * 0.1)} Today +
+
+
+ +
+
+

PnL %

+
+ {formatPercentage(strategyData.roiPercentage)} +
+
+
+ +
+
+

Total Runtime

+
{formatDuration(strategyData.runtime)}
+
+
+
+ )} + + {activeTab === 'history' && ( +
+
+

Trade History

+ {strategyData.positions && Object.keys(strategyData.positions).length > 0 ? ( +
+ + + + + + + + + + + + + + {Object.values(strategyData.positions).map((position: Position, index: number) => ( + console.log(position), + + + + + + + + + + ))} + +
DateSymbolSideSizeEntry PriceExit PricePnL
{new Date(position.Open.date || '').toLocaleDateString()}{position.ticker} + + {position.Open.direction === TradeDirection.Long ? 'LONG' : 'SHORT'} + + {position.Open.quantity}{formatCurrency(position.Open.price)}{formatCurrency(position.TakeProfit1?.price || 0)} 0 ? 'text-success' : 'text-error'}> + {formatCurrency(position.ProfitAndLoss?.realized || 0)} +
+
+ ) : ( +
+
📋
+
No trade history available
+
+ )} +
+
+ )} + + )} +
+ ) +} + +export default AgentStrategy diff --git a/src/Managing.WebApp/src/pages/dashboardPage/dashboard.tsx b/src/Managing.WebApp/src/pages/dashboardPage/dashboard.tsx index 0c04277..0366a60 100644 --- a/src/Managing.WebApp/src/pages/dashboardPage/dashboard.tsx +++ b/src/Managing.WebApp/src/pages/dashboardPage/dashboard.tsx @@ -8,6 +8,7 @@ import Monitoring from './monitoring' import BestAgents from './analytics/bestAgents' import AgentSearch from './agentSearch' import AgentIndex from './agentIndex' +import AgentStrategy from './agentStrategy' const tabs: ITabsType = [ { @@ -35,6 +36,11 @@ const tabs: ITabsType = [ index: 5, label: 'Agent Index', }, + { + Component: AgentStrategy, + index: 6, + label: 'Agent Strategy', + }, ] const Dashboard: React.FC = () => {