import React, { useState, useEffect } from 'react'; import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts'; import { TrendingUp, TrendingDown, X } from 'lucide-react'; const TradingTerminal = () => { const [ws, setWs] = useState(null); const [connected, setConnected] = useState(false); const [symbol, setSymbol] = useState('XAUUSDc'); const [timeframe, setTimeframe] = useState('M15'); const [chartData, setChartData] = useState([]); const [tick, setTick] = useState(null); const [positions, setPositions] = useState([]); const [volume, setVolume] = useState(0.01); const [sl, setSl] = useState(''); const [tp, setTp] = useState(''); useEffect(() => { const socket = new WebSocket('ws://localhost:8765'); socket.onopen = () => { setConnected(true); console.log('Connected to MT5 server'); socket.send(JSON.stringify({ action: 'get_rates', symbol: symbol, timeframe: timeframe, count: 500 })); socket.send(JSON.stringify({ action: 'get_positions' })); }; socket.onmessage = (event) => { const message = JSON.parse(event.data); if (message.type === 'rates') { setChartData(message.data || []); } else if (message.type === 'tick_update' || message.type === 'tick') { setTick(message.data); } else if (message.type === 'positions') { setPositions(message.data || []); } else if (message.type === 'order_result') { if (message.data.success) { alert('Order placed successfully!'); socket.send(JSON.stringify({ action: 'get_positions' })); } else { alert('Order failed: ' + message.data.error); } } else if (message.type === 'close_result') { if (message.data.success) { alert('Position closed!'); socket.send(JSON.stringify({ action: 'get_positions' })); } else { alert('Close failed: ' + message.data.error); } } }; socket.onerror = (error) => { console.error('WebSocket error:', error); setConnected(false); }; socket.onclose = () => { setConnected(false); console.log('Disconnected from MT5 server'); }; setWs(socket); return () => { socket.close(); }; }, []); const placeOrder = (type) => { if (!ws || !connected) return; ws.send(JSON.stringify({ action: 'place_order', symbol: symbol, order_type: type, volume: parseFloat(volume), sl: sl ? parseFloat(sl) : null, tp: tp ? parseFloat(tp) : null })); }; const closePosition = (ticket) => { if (!ws || !connected) return; ws.send(JSON.stringify({ action: 'close_position', ticket: ticket })); }; const changeTimeframe = (tf) => { setTimeframe(tf); if (ws && connected) { ws.send(JSON.stringify({ action: 'get_rates', symbol: symbol, timeframe: tf, count: 500 })); } }; const formatPrice = (price) => { return price ? price.toFixed(2) : '-'; }; const totalPnL = positions.reduce((sum, pos) => sum + pos.profit, 0); return (
{symbol}
{tick && (
BID: {formatPrice(tick.bid)} ASK: {formatPrice(tick.ask)}
)}
= 0 ? 'text-green-500' : 'text-red-500'}`}> P&L: ${totalPnL.toFixed(2)}
{['M1', 'M5', 'M15', 'M30', 'H1', 'H4', 'D1'].map(tf => ( ))}
{chartData.length > 0 ? ( new Date(time).toLocaleTimeString()} stroke="#525252" style={{ fontSize: 10 }} /> new Date(time).toLocaleString()} /> ) : (
Loading chart data...
)}
NEW ORDER
setVolume(e.target.value)} className="w-full bg-neutral-950 border border-yellow-600/20 px-2 py-1 text-sm text-neutral-200 focus:outline-none focus:border-yellow-600" />
setSl(e.target.value)} placeholder="Optional" className="w-full bg-neutral-950 border border-yellow-600/20 px-2 py-1 text-sm text-neutral-200 focus:outline-none focus:border-yellow-600 placeholder:text-neutral-700" />
setTp(e.target.value)} placeholder="Optional" className="w-full bg-neutral-950 border border-yellow-600/20 px-2 py-1 text-sm text-neutral-200 focus:outline-none focus:border-yellow-600 placeholder:text-neutral-700" />
POSITIONS ({positions.length})
{positions.length === 0 ? (
No open positions
) : (
{positions.map((pos) => (
{pos.symbol}
{pos.type} {pos.volume}
Entry: {formatPrice(pos.price_open)}
Current: {formatPrice(pos.price_current)}
P&L: = 0 ? 'text-green-500' : 'text-red-500'}`}> ${pos.profit.toFixed(2)}
))}
)}
); }; export default TradingTerminal;