'use client'; import { Box, CloseButton, Drawer, Flex, Grid, Portal, SimpleGrid, Text, } from '@chakra-ui/react'; import type { RuntimeStrategyView, ThresholdLevel } from '@tradejs/types'; import { formatInteger, formatPercent, formatSignedNumber, getPnlColor, type OrdersDrawerSummaryItem, } from '#components/Shared/OrdersDrawer'; import { buildQuarterlyMonthlyStats } from '#app/lib/strategyPerformance'; import { AdvancedMetricsPanel } from './AdvancedMetricsPanel'; import { ChartPanel, DrawdownTimelineChart, PnlDistributionChart, RollingPerformanceChart, TimeOfDaySessionChart, WinLossStreakTimelineChart, } from './StrategyPerformanceCharts'; import { buildRuntimeStrategyCardViewModel, getMetricColor, getPnlBarColor, type RuntimeSymbolPnlRank, } from './RuntimeStrategyCard.presenter'; type RuntimeStrategyCardViewModel = ReturnType< typeof buildRuntimeStrategyCardViewModel >; const RuntimeOrdersSummaryBlock = ({ items, }: { items: OrdersDrawerSummaryItem[]; }) => ( {items.map((item) => ( {item.title} {item.value} ))} ); const renderPnlRanking = ({ title, subtitle, ranking, maxAbsPnl, }: { title: string; subtitle: string; ranking: RuntimeSymbolPnlRank[]; maxAbsPnl: number; }) => ( {title} {subtitle} {ranking.length ? ( {ranking.map((rank) => { const width = Math.max(4, (Math.abs(rank.pnl) / maxAbsPnl) * 100); return ( {rank.symbol} {formatInteger(rank.orders)} orders ยท{' '} {formatPercent(rank.winRate)} avg {formatSignedNumber(rank.avgPnl)} {formatSignedNumber(rank.pnl)} ); })} ) : ( No symbol P&L data )} ); export const RuntimeStrategyStatsDrawer = ({ strategy, provider, open, onOpenChange, viewModel, }: { strategy: RuntimeStrategyView; provider: string; open: boolean; onOpenChange: (open: boolean) => void; viewModel: RuntimeStrategyCardViewModel; }) => { const { runtimeOrderSummaryItems, drawerMetrics, symbolConcentration, topSymbolPnlRanking, worstSymbolPnlRanking, symbolRankingMaxAbsPnl, directionStats, advancedMetrics, } = viewModel; const { monthlyStats, tradePoints: runtimeTradePoints, drawdownPoints, rollingPerformancePoints, pnlDistributionBins, sessionPnlStats, hourlyPnlStats, } = viewModel.performance; return ( onOpenChange(e.open)} > {strategy.strategyName}, {strategy.deploymentLabel} connector: {provider} {drawerMetrics.map((metric) => ( {metric.label} {metric.value} ))} {monthlyStats.length ? ( Monthly Performance {monthlyStats.map((yearGroup) => ( {yearGroup.year} {buildQuarterlyMonthlyStats(yearGroup.months).map( (quarter) => ( {quarter.label} {quarter.months.map((month, monthOffset) => { const monthIndex = quarter.monthIndexes[monthOffset] ?? 0; if (!month) { return ( ); } const winRate = month.orders > 0 ? (month.wins / month.orders) * 100 : null; return ( {month.monthLabel} {String(month.monthIndex).padStart( 2, '0', )} {formatSignedNumber(month.pnl)} Orders {formatInteger(month.orders)} Win rate {formatPercent(winRate)} ); })} ), )} ))} ) : null} {renderPnlRanking({ title: 'P&L Ranking', subtitle: 'Top 10 contracts', ranking: topSymbolPnlRanking, maxAbsPnl: symbolRankingMaxAbsPnl, })} {renderPnlRanking({ title: 'Worst Contracts', subtitle: 'Worst 10 contracts', ranking: worstSymbolPnlRanking, maxAbsPnl: symbolRankingMaxAbsPnl, })} {symbolConcentration.length ? ( Symbol Concentration absolute P&L and order share {symbolConcentration.map((row) => ( {row.symbol} {formatInteger(row.orders)} trades / order share{' '} {formatPercent(row.orderShare)} {formatSignedNumber(row.pnl)} abs {formatPercent(row.absPnlShare)} ))} ) : null} LONG / SHORT {directionStats.map((group) => { const ordersWithPnl = group.closed + group.active; const winRate = ordersWithPnl > 0 ? (group.wins / ordersWithPnl) * 100 : null; return ( {group.direction} {!group.orders ? ( no data ) : null} {[ ['Orders', formatInteger(group.orders), 'neutral'], ['Active', formatInteger(group.active), 'warning'], ['Closed', formatInteger(group.closed), 'neutral'], ['Win rate', formatPercent(winRate), 'neutral'], [ 'P&L', formatSignedNumber(group.pnl), group.pnl > 0 ? 'success' : group.pnl < 0 ? 'error' : 'neutral', ], [ 'Avg Profit', formatSignedNumber(group.avgPnl), (group.avgPnl ?? 0) > 0 ? 'success' : (group.avgPnl ?? 0) < 0 ? 'error' : 'neutral', ], ].map(([label, value, level]) => ( {label} {value} ))} ); })} ); };