diff --git a/apps/stock/web-app/src/components/charts/Chart.tsx b/apps/stock/web-app/src/components/charts/Chart.tsx index e22c1db..93bf99f 100644 --- a/apps/stock/web-app/src/components/charts/Chart.tsx +++ b/apps/stock/web-app/src/components/charts/Chart.tsx @@ -24,7 +24,7 @@ export interface TradeMarker { export interface ChartProps { data: ChartData[]; - height?: number; + height?: number | string; type?: 'candlestick' | 'line' | 'area'; showVolume?: boolean; theme?: 'light' | 'dark'; @@ -399,7 +399,10 @@ export function Chart({
{/* Tab Content */} -
+
{error && (

{error}

diff --git a/apps/stock/web-app/src/features/backtest/components/BacktestChart.tsx b/apps/stock/web-app/src/features/backtest/components/BacktestChart.tsx index db651dd..03f7cf9 100644 --- a/apps/stock/web-app/src/features/backtest/components/BacktestChart.tsx +++ b/apps/stock/web-app/src/features/backtest/components/BacktestChart.tsx @@ -1,6 +1,7 @@ import type { BacktestResult } from '../types/backtest.types'; import { useState, useMemo, memo } from 'react'; import { Chart } from '../../../components/charts/Chart'; +import { ChartContainer } from './ChartContainer'; interface BacktestChartProps { result: BacktestResult | null; @@ -107,22 +108,26 @@ export const BacktestChart = memo(function BacktestChart({ result, isLoading }:
)} -
- +
+ + {(height) => ( + + )} +
); diff --git a/apps/stock/web-app/src/features/backtest/components/BacktestPlayback.tsx b/apps/stock/web-app/src/features/backtest/components/BacktestPlayback.tsx index 67ad129..20fa1c8 100644 --- a/apps/stock/web-app/src/features/backtest/components/BacktestPlayback.tsx +++ b/apps/stock/web-app/src/features/backtest/components/BacktestPlayback.tsx @@ -55,9 +55,9 @@ export const BacktestPlayback = memo(function BacktestPlayback({ result, isLoadi } return ( -
+
{/* Chart Section */} -
+
@@ -80,13 +80,9 @@ export const BacktestPlayback = memo(function BacktestPlayback({ result, isLoadi
- {/* Performance Metrics */} -
+ {/* Performance Metrics and Positions - Fixed height section */} +
-
- - {/* Positions Summary */} -
ReactNode; + className?: string; +} + +export function ChartContainer({ children, className = '' }: ChartContainerProps) { + const containerRef = useRef(null); + const [height, setHeight] = useState(500); // Default height + + useEffect(() => { + if (!containerRef.current) return; + + const resizeObserver = new ResizeObserver((entries) => { + for (const entry of entries) { + const { height } = entry.contentRect; + if (height > 0) { + setHeight(Math.floor(height)); + } + } + }); + + resizeObserver.observe(containerRef.current); + + // Initial measurement + const initialHeight = containerRef.current.clientHeight; + if (initialHeight > 0) { + setHeight(initialHeight); + } + + return () => { + resizeObserver.disconnect(); + }; + }, []); + + return ( +
+ {children(height)} +
+ ); +} \ No newline at end of file diff --git a/apps/stock/web-app/src/features/backtest/components/CompactPerformanceMetrics.tsx b/apps/stock/web-app/src/features/backtest/components/CompactPerformanceMetrics.tsx index c2d21fd..63617ee 100644 --- a/apps/stock/web-app/src/features/backtest/components/CompactPerformanceMetrics.tsx +++ b/apps/stock/web-app/src/features/backtest/components/CompactPerformanceMetrics.tsx @@ -91,7 +91,7 @@ export function CompactPerformanceMetrics({ result, isLoading }: CompactPerforma

Performance Analytics

-
+
{/* Column 1 - Return Metrics */}
@@ -212,7 +212,7 @@ export function CompactPerformanceMetrics({ result, isLoading }: CompactPerforma {/* Advanced Metrics - Show only if available */} {(allMetrics.skewness !== undefined || allMetrics.kurtosis !== undefined || allMetrics.informationRatio !== undefined || allMetrics.kellyFraction !== undefined) && ( -
+
{allMetrics.informationRatio !== undefined && (
@@ -254,7 +254,7 @@ export function CompactPerformanceMetrics({ result, isLoading }: CompactPerforma )} {/* Additional Metrics Row */} -
+
Calmar diff --git a/apps/stock/web-app/src/features/backtest/components/PositionsSummary.tsx b/apps/stock/web-app/src/features/backtest/components/PositionsSummary.tsx index 37909fd..b427daa 100644 --- a/apps/stock/web-app/src/features/backtest/components/PositionsSummary.tsx +++ b/apps/stock/web-app/src/features/backtest/components/PositionsSummary.tsx @@ -23,32 +23,18 @@ export function PositionsSummary({ openPositions, closedPositions, onExpand }: P const currentPrice = p.currentPrice || p.lastPrice || avgPrice; return sum + ((currentPrice - avgPrice) * quantity); }, 0); + const totalPnL = totalRealizedPnL + totalUnrealizedPnL; return ( -
-
-

Positions Summary

-
-
- Realized: - = 0 ? 'text-success' : 'text-error'}`}> - ${totalRealizedPnL.toFixed(2)} - -
-
- Unrealized: - = 0 ? 'text-success' : 'text-error'}`}> - ${totalUnrealizedPnL.toFixed(2)} - -
-
-
+
+

Positions Summary

- {openPositions.length > 0 && ( +
+ {openPositions.length > 0 && (

Open Positions ({openPositions.length})

- {openPositions.slice(0, 5).map((position, index) => { + {openPositions.slice(0, 3).map((position, index) => { const quantity = position.quantity || 0; const avgPrice = position.averagePrice || position.avgPrice || 0; const currentPrice = position.currentPrice || position.lastPrice || avgPrice; @@ -85,16 +71,16 @@ export function PositionsSummary({ openPositions, closedPositions, onExpand }: P
)} - {closedPositions.length > 0 && ( -
+ {closedPositions.length > 0 && openPositions.length === 0 && ( +

Closed Positions ({closedPositions.length})

{closedPositions.slice(0, 3).map((position, index) => ( -
- {position.symbol} - = 0 ? 'text-success' : 'text-error'}`}> +
+ {position.symbol} + = 0 ? 'text-success' : 'text-error'}`}> ${position.realizedPnl.toFixed(2)}
@@ -103,18 +89,46 @@ export function PositionsSummary({ openPositions, closedPositions, onExpand }: P
)} - {(openPositions.length > 5 || closedPositions.length > 3) && onExpand && ( - - )} + {openPositions.length === 0 && closedPositions.length === 0 && ( +

No positions to display

+ )} +
- {openPositions.length === 0 && closedPositions.length === 0 && ( -

No positions to display

- )} + {/* Summary metrics at bottom - similar to performance metrics */} +
+
+
+ Unrealized +
= 0 ? 'text-success' : 'text-error'}`}> + ${totalUnrealizedPnL.toFixed(2)} +
+
+ +
+ Realized +
= 0 ? 'text-success' : 'text-error'}`}> + ${totalRealizedPnL.toFixed(2)} +
+
+ +
+ Total P&L +
= 0 ? 'text-success' : 'text-error'}`}> + ${totalPnL.toFixed(2)} +
+
+
+ + {((openPositions.length > 3) || + (closedPositions.length > 3 && openPositions.length === 0)) && onExpand && ( + + )} +
); } \ No newline at end of file diff --git a/apps/stock/web-app/src/features/backtest/components/index.ts b/apps/stock/web-app/src/features/backtest/components/index.ts index 724f2c4..a46456a 100644 --- a/apps/stock/web-app/src/features/backtest/components/index.ts +++ b/apps/stock/web-app/src/features/backtest/components/index.ts @@ -8,4 +8,5 @@ export { RunsList } from './RunsList'; export { RunsListWithMetrics } from './RunsListWithMetrics'; export { CompactPerformanceMetrics } from './CompactPerformanceMetrics'; export { CompactPositionsTable } from './CompactPositionsTable'; -export { PositionsSummary } from './PositionsSummary'; \ No newline at end of file +export { PositionsSummary } from './PositionsSummary'; +export { ChartContainer } from './ChartContainer'; \ No newline at end of file