diff --git a/src/modules/trading/components/OrderForm.tsx b/src/modules/trading/components/OrderForm.tsx index fe5cb4d..5c0b82e 100644 --- a/src/modules/trading/components/OrderForm.tsx +++ b/src/modules/trading/components/OrderForm.tsx @@ -63,6 +63,37 @@ export default function OrderForm({ } } + // Validate TP/SL based on direction + const entryPriceForValidation = orderType === 'market' ? currentPrice : parseFloat(limitPrice) || currentPrice; + + if (stopLoss) { + const sl = parseFloat(stopLoss); + if (sl > 0) { + if (direction === 'long' && sl >= entryPriceForValidation) { + setError('Stop Loss must be below entry price for LONG positions'); + return; + } + if (direction === 'short' && sl <= entryPriceForValidation) { + setError('Stop Loss must be above entry price for SHORT positions'); + return; + } + } + } + + if (takeProfit) { + const tp = parseFloat(takeProfit); + if (tp > 0) { + if (direction === 'long' && tp <= entryPriceForValidation) { + setError('Take Profit must be above entry price for LONG positions'); + return; + } + if (direction === 'short' && tp >= entryPriceForValidation) { + setError('Take Profit must be below entry price for SHORT positions'); + return; + } + } + } + // Build order data const orderData: CreateOrderInput = { symbol, diff --git a/src/modules/trading/components/TradesHistory.tsx b/src/modules/trading/components/TradesHistory.tsx index a006db5..7b2014f 100644 --- a/src/modules/trading/components/TradesHistory.tsx +++ b/src/modules/trading/components/TradesHistory.tsx @@ -3,6 +3,8 @@ * Displays history of closed paper trading positions */ +import { ArrowDownTrayIcon } from '@heroicons/react/24/solid'; + interface Trade { id: string; symbol: string; @@ -21,6 +23,35 @@ interface TradesHistoryProps { isLoading: boolean; } +function exportToCSV(trades: Trade[]): void { + const headers = ['Date', 'Symbol', 'Direction', 'Entry Price', 'Exit Price', 'Quantity', 'P&L', 'P&L %', 'Reason']; + const rows = trades.map((trade) => { + const closedDate = new Date(trade.closedAt); + return [ + closedDate.toISOString(), + trade.symbol, + trade.direction.toUpperCase(), + trade.entryPrice.toFixed(8), + trade.exitPrice.toFixed(8), + trade.lotSize.toString(), + trade.realizedPnl.toFixed(2), + trade.realizedPnlPercent.toFixed(2), + trade.closeReason || '', + ]; + }); + + const csvContent = [headers.join(','), ...rows.map((row) => row.join(','))].join('\n'); + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.setAttribute('href', url); + link.setAttribute('download', `trades_history_${new Date().toISOString().split('T')[0]}.csv`); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); +} + export default function TradesHistory({ trades, isLoading, @@ -43,7 +74,17 @@ export default function TradesHistory({ } return ( -
Sharpe Ratio
+= 1 ? 'text-green-400' : stats.sharpeRatio >= 0 ? 'text-yellow-400' : 'text-red-400'}`}> + {stats.sharpeRatio.toFixed(2)} +
++ {stats.sharpeRatio >= 2 ? 'Excellent' : stats.sharpeRatio >= 1 ? 'Good' : stats.sharpeRatio >= 0 ? 'Fair' : 'Poor'} +
+Max Drawdown
++ -${stats.maxDrawdown.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} +
++ {stats.maxDrawdownPercent.toFixed(1)}% from peak +
+