/** * QuickOrderPanel Component * Compact order entry for fast one-click trading */ import React, { useState, useCallback } from 'react'; import { TrendingUp, TrendingDown, Loader2, Settings, Zap, AlertTriangle, Check, } from 'lucide-react'; import { executeMT4Trade } from '../../../services/trading.service'; interface QuickOrderPanelProps { symbol: string; currentPrice: number; spread?: number; accountBalance?: number; onOrderExecuted?: (ticket: number, type: 'buy' | 'sell') => void; onError?: (error: string) => void; compact?: boolean; } interface LotPreset { label: string; lots: number; } const DEFAULT_PRESETS: LotPreset[] = [ { label: '0.01', lots: 0.01 }, { label: '0.05', lots: 0.05 }, { label: '0.10', lots: 0.10 }, { label: '0.25', lots: 0.25 }, { label: '0.50', lots: 0.50 }, { label: '1.00', lots: 1.00 }, ]; const QuickOrderPanel: React.FC = ({ symbol, currentPrice, spread = 0, accountBalance = 0, onOrderExecuted, onError, compact = false, }) => { const [selectedLots, setSelectedLots] = useState(0.01); const [customLots, setCustomLots] = useState(''); const [isExecuting, setIsExecuting] = useState<'buy' | 'sell' | null>(null); const [lastResult, setLastResult] = useState<{ type: 'success' | 'error'; message: string } | null>(null); const [showSettings, setShowSettings] = useState(false); const [slPips, setSlPips] = useState(20); const [tpPips, setTpPips] = useState(40); const [useSLTP, setUseSLTP] = useState(true); const activeLots = customLots ? parseFloat(customLots) : selectedLots; const bidPrice = currentPrice - spread / 2; const askPrice = currentPrice + spread / 2; // Calculate pip value (simplified - assumes forex major pairs) const pipValue = symbol.includes('JPY') ? 0.01 : 0.0001; const calculateSLTP = (type: 'buy' | 'sell') => { const price = type === 'buy' ? askPrice : bidPrice; if (!useSLTP) return { sl: undefined, tp: undefined }; if (type === 'buy') { return { sl: price - slPips * pipValue, tp: price + tpPips * pipValue, }; } else { return { sl: price + slPips * pipValue, tp: price - tpPips * pipValue, }; } }; const estimateRisk = () => { if (!accountBalance || !useSLTP) return null; // Rough estimate: lot * pip value * SL pips const riskPerPip = activeLots * 10; // ~$10 per pip per lot for majors const totalRisk = riskPerPip * slPips; const riskPercent = (totalRisk / accountBalance) * 100; return { totalRisk, riskPercent }; }; const risk = estimateRisk(); const executeOrder = useCallback(async (type: 'buy' | 'sell') => { if (isExecuting || activeLots <= 0) return; setIsExecuting(type); setLastResult(null); try { const { sl, tp } = calculateSLTP(type); const result = await executeMT4Trade({ symbol, type: type === 'buy' ? 'BUY' : 'SELL', lots: activeLots, stopLoss: sl, takeProfit: tp, }); setLastResult({ type: 'success', message: `#${result.ticket} ${type.toUpperCase()} ${activeLots} lots` }); onOrderExecuted?.(result.ticket, type); // Clear success message after 3 seconds setTimeout(() => setLastResult(null), 3000); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Order failed'; setLastResult({ type: 'error', message: errorMessage }); onError?.(errorMessage); } finally { setIsExecuting(null); } }, [symbol, activeLots, useSLTP, slPips, tpPips, askPrice, bidPrice, onOrderExecuted, onError]); if (compact) { return (
{symbol}
); } return (
{/* Header */}

Quick Order

{/* Symbol & Price */}
{symbol}
{bidPrice.toFixed(5)} | {askPrice.toFixed(5)}
{spread > 0 && (
Spread: {(spread / pipValue).toFixed(1)} pips
)}
{/* Lot Size Presets */}
{DEFAULT_PRESETS.map((preset) => ( ))}
setCustomLots(e.target.value)} placeholder="Custom lots..." step="0.01" min="0.01" className="w-full mt-2 px-3 py-2 bg-gray-900 border border-gray-700 rounded-lg text-white text-sm placeholder-gray-500 focus:outline-none focus:border-blue-500" />
{/* Settings Panel */} {showSettings && (
{useSLTP && (
setSlPips(parseInt(e.target.value) || 0)} className="w-full px-2 py-1.5 bg-gray-800 border border-gray-700 rounded text-sm text-white" />
setTpPips(parseInt(e.target.value) || 0)} className="w-full px-2 py-1.5 bg-gray-800 border border-gray-700 rounded text-sm text-white" />
)}
)} {/* Risk Warning */} {risk && risk.riskPercent > 2 && (
Risk: ${risk.totalRisk.toFixed(2)} ({risk.riskPercent.toFixed(1)}% of balance)
)} {/* Buy/Sell Buttons */}
{/* Result Message */} {lastResult && (
{lastResult.type === 'success' ? ( ) : ( )} {lastResult.message}
)} {/* Active Lots Display */}
Trading {activeLots.toFixed(2)} lots
); }; export default QuickOrderPanel;