import { TrendingUp, TrendingDown, Activity, Target } from 'lucide-react'; import { calculateRiskMetrics } from '../data/recommendations'; import { useState } from 'react'; import InfoModal, { InfoButton } from './InfoModal'; import type { RiskMetrics } from '../types'; export interface RiskMetricsCardProps { className?: string; metrics?: RiskMetrics; // Optional prop for real data } type MetricModal = 'sharpe' | 'drawdown' | 'winloss' | 'winrate' | null; export default function RiskMetricsCard({ className = '', metrics: propMetrics }: RiskMetricsCardProps) { const [activeModal, setActiveModal] = useState(null); // Use provided metrics or fall back to mock data const metrics = propMetrics || calculateRiskMetrics(); // Color classes for metric values const COLOR_GOOD = 'text-green-600 dark:text-green-400'; const COLOR_NEUTRAL = 'text-amber-600 dark:text-amber-400'; const COLOR_BAD = 'text-red-600 dark:text-red-400'; function getColor(metric: string, value: number): string { // Thresholds for each metric: [good, neutral] - values below neutral are bad const thresholds: Record = { sharpe: { good: 1, neutral: 0 }, drawdown: { good: 5, neutral: 15, inverted: true }, // Lower is better winloss: { good: 1.5, neutral: 1 }, winrate: { good: 70, neutral: 50 }, }; const config = thresholds[metric]; if (!config) return 'text-gray-700 dark:text-gray-300'; if (config.inverted) { // For drawdown: lower is better if (value <= config.good) return COLOR_GOOD; if (value <= config.neutral) return COLOR_NEUTRAL; return COLOR_BAD; } // For other metrics: higher is better if (value >= config.good) return COLOR_GOOD; if (value >= config.neutral) return COLOR_NEUTRAL; return COLOR_BAD; } const cards = [ { id: 'sharpe', label: 'Sharpe Ratio', value: metrics.sharpeRatio.toFixed(2), icon: Activity, color: getColor('sharpe', metrics.sharpeRatio), }, { id: 'drawdown', label: 'Max Drawdown', value: `${metrics.maxDrawdown.toFixed(1)}%`, icon: TrendingDown, color: getColor('drawdown', metrics.maxDrawdown), }, { id: 'winloss', label: 'Win/Loss Ratio', value: metrics.winLossRatio.toFixed(2), icon: TrendingUp, color: getColor('winloss', metrics.winLossRatio), }, { id: 'winrate', label: 'Win Rate', value: `${metrics.winRate}%`, icon: Target, color: getColor('winrate', metrics.winRate), }, ]; return ( <>
{cards.map((card) => { const Icon = card.icon; return (
{card.value}
{card.label} setActiveModal(card.id as MetricModal)} />
); })}
{/* Sharpe Ratio Modal */} setActiveModal(null)} title="Sharpe Ratio" icon={} >

The Sharpe Ratio measures risk-adjusted returns by comparing the excess return of an investment to its standard deviation (volatility).

{/* Current Value Display */}
Current Sharpe Ratio
{metrics.sharpeRatio.toFixed(2)}
{/* Formula and Calculation */}

Formula:

Sharpe Ratio = (R̄ − Rf) / σ

Where R̄ = Mean Return, Rf = Risk-Free Rate, σ = Standard Deviation

{metrics.meanReturn !== undefined && (

Your Values:

• Mean Daily Return (R̄) = {metrics.meanReturn}%

• Risk-Free Rate (Rf) = {metrics.riskFreeRate}% (daily)

• Volatility (σ) = {metrics.volatility}%

Calculation:

= ({metrics.meanReturn} − {metrics.riskFreeRate}) / {metrics.volatility}

= {(metrics.meanReturn - (metrics.riskFreeRate || 0)).toFixed(2)} / {metrics.volatility}

= {metrics.sharpeRatio.toFixed(2)}

)}

Interpretation:

  • > 1.0: Good risk-adjusted returns
  • > 2.0: Excellent performance
  • 0 - 1.0: Acceptable but not optimal
  • < 0: Returns below risk-free rate

Higher Sharpe Ratio indicates better compensation for the risk taken.

{/* Max Drawdown Modal */} setActiveModal(null)} title="Maximum Drawdown" icon={} >

Maximum Drawdown (MDD) measures the largest peak-to-trough decline in portfolio value before a new peak is reached.

{/* Current Value Display */}
Maximum Drawdown
{metrics.maxDrawdown.toFixed(1)}%
{/* Formula and Calculation */}

Formula:

MDD = (Vpeak − Vtrough) / Vpeak × 100%

Where Vpeak = Peak Portfolio Value, Vtrough = Lowest Value after Peak

{metrics.peakValue !== undefined && metrics.troughValue !== undefined && (

Your Values:

• Peak Value (Vpeak) = ₹{metrics.peakValue.toFixed(2)} (normalized from ₹100)

• Trough Value (Vtrough) = ₹{metrics.troughValue.toFixed(2)}

Calculation:

= ({metrics.peakValue.toFixed(2)} − {metrics.troughValue.toFixed(2)}) / {metrics.peakValue.toFixed(2)} × 100

= {(metrics.peakValue - metrics.troughValue).toFixed(2)} / {metrics.peakValue.toFixed(2)} × 100

= {metrics.maxDrawdown.toFixed(1)}%

)}

Interpretation:

  • < 5%: Very low risk
  • 5% - 15%: Moderate risk
  • > 15%: Higher risk exposure

Lower drawdown indicates better capital preservation during market downturns.

{/* Win/Loss Ratio Modal */} setActiveModal(null)} title="Win/Loss Ratio" icon={} >

The Win/Loss Ratio compares the average profit from winning trades to the average loss from losing trades.

{/* Current Value Display */}
Win/Loss Ratio
{metrics.winLossRatio.toFixed(2)}
{/* Formula and Calculation */}

Formula:

Win/Loss Ratio = R̄w / |R̄l|

Where R̄w = Avg Winning Return, R̄l = Avg Losing Return (absolute value)

{metrics.winningTrades !== undefined && metrics.losingTrades !== undefined && (

Your Values:

• Winning Predictions = {metrics.winningTrades} days

• Losing Predictions = {metrics.losingTrades} days

• Avg Winning Return (R̄w) = +{metrics.avgWinReturn?.toFixed(2)}%

• Avg Losing Return (R̄l) = −{metrics.avgLossReturn?.toFixed(2)}%

Calculation:

= {metrics.avgWinReturn?.toFixed(2)} / {metrics.avgLossReturn?.toFixed(2)}

= {metrics.winLossRatio.toFixed(2)}

)}

Interpretation:

  • > 1.5: Strong profit potential
  • 1.0 - 1.5: Balanced trades
  • < 1.0: Losses exceed wins on average

A ratio above 1.0 means your winning trades are larger than your losing ones on average.

{/* Win Rate Modal */} setActiveModal(null)} title="Win Rate" icon={} >

Win Rate is the percentage of predictions that were correct (BUY/HOLD with positive return, or SELL with negative return).

{/* Current Value Display */}
Win Rate
{metrics.winRate}%
{/* Formula and Calculation */}

Formula:

Win Rate = (Ncorrect / Ntotal) × 100%

Where Ncorrect = Correct Predictions, Ntotal = Total Predictions

{metrics.winningTrades !== undefined && (

Your Values:

• Correct Predictions (Ncorrect) = {metrics.winningTrades}

• Total Predictions (Ntotal) = {metrics.totalTrades}

Calculation:

= ({metrics.winningTrades} / {metrics.totalTrades}) × 100

= {(metrics.winningTrades / metrics.totalTrades).toFixed(4)} × 100

= {metrics.winRate}%

)}

Interpretation:

  • > 70%: Excellent accuracy
  • 50% - 70%: Above average
  • < 50%: Below random chance

Note: Win rate alone doesn't determine profitability. A 40% win rate can still be profitable with a high Win/Loss ratio.

); }