'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}
))}
);
})}
);
};