| import { useState, useEffect } from 'react'; | |
| import CandlestickChart from './components/CandlestickChart'; | |
| import ReasoningSidebar from './components/ReasoningSidebar'; | |
| import TradeControls from './components/TradeControls'; | |
| import PositionPanel from './components/PositionPanel'; | |
| import AccountBar from './components/AccountBar'; | |
| import { useWebSocket } from './lib/useWebSocket'; | |
| import { AlertTriangle } from 'lucide-react'; | |
| import './App.css'; | |
| function App() { | |
| const { connected, data, sendMessage } = useWebSocket('ws://localhost:8000/ws'); | |
| const [candles, setCandles] = useState([]); | |
| const [tick, setTick] = useState(null); | |
| const [account, setAccount] = useState(null); | |
| const [positions, setPositions] = useState([]); | |
| const [agentStatus, setAgentStatus] = useState('stopped'); | |
| const [logs, setLogs] = useState([]); | |
| useEffect(() => { | |
| if (!data) return; | |
| if (data.type === 'status') { | |
| setAgentStatus(data.data.agent ? 'running' : 'stopped'); | |
| } else if (data.type === 'candle_update') { | |
| setCandles(prev => { | |
| const newCandle = data.data; | |
| const last = prev[prev.length - 1]; | |
| if (last && last.time === newCandle.time) { | |
| return [...prev.slice(0, -1), newCandle]; | |
| } | |
| return [...prev, newCandle].slice(-500); | |
| }); | |
| } else if (data.type === 'tick_update') { | |
| setTick(data.data); | |
| } else if (data.type === 'account') { | |
| setAccount(data.data); | |
| } else if (data.type === 'positions') { | |
| setPositions(data.data); | |
| } else if (data.type === 'agent_status') { | |
| setAgentStatus(data.data.status); | |
| } else if (data.type === 'reasoning') { | |
| setLogs(prev => [...prev, data.data].slice(-50)); | |
| } else if (data.type === 'trade_event') { | |
| console.log('Trade Event:', data.data); | |
| } | |
| }, [data]); | |
| useEffect(() => { | |
| fetch('http://localhost:8000/api/candles').then(r => r.json()).then(setCandles).catch(console.error); | |
| fetch('http://localhost:8000/api/account').then(r => r.json()).then(setAccount).catch(console.error); | |
| fetch('http://localhost:8000/api/positions').then(r => r.json()).then(setPositions).catch(console.error); | |
| }, []); | |
| const handleToggleAgent = (running) => { | |
| fetch(`http://localhost:8000/api/agent/toggle?enable=${running}`, { method: 'POST' }); | |
| }; | |
| const handleManualTrade = (action, volume, sl, tp) => { | |
| fetch('http://localhost:8000/api/trade', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ action, symbol: 'XAUUSDc', volume, sl, tp }) | |
| }); | |
| }; | |
| return ( | |
| <div className="app-layout"> | |
| {/* Top Bar */} | |
| <div className="top-bar"> | |
| <div className="top-bar-brand"> | |
| <div className="top-bar-logo">G3</div> | |
| <span className="top-bar-title">GEMINI TRADER</span> | |
| </div> | |
| <div className="top-bar-content"> | |
| <AccountBar account={account} connected={connected} /> | |
| </div> | |
| </div> | |
| {/* Main Content */} | |
| <div className="main-content"> | |
| {/* Left: Chart & Positions */} | |
| <div className="chart-column"> | |
| {/* Chart */} | |
| <div className="chart-area"> | |
| <CandlestickChart data={candles} tick={tick} /> | |
| {/* Trade Controls Overlay */} | |
| <div className="trade-overlay"> | |
| <TradeControls | |
| status={agentStatus} | |
| onToggleAgent={handleToggleAgent} | |
| onManualTrade={handleManualTrade} | |
| /> | |
| </div> | |
| </div> | |
| {/* Positions */} | |
| <div className="positions-panel"> | |
| <div className="positions-panel-header"> | |
| Open Positions | |
| </div> | |
| <div className="positions-panel-body"> | |
| <PositionPanel positions={positions} currentTick={tick} /> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Right: AI Reasoning Sidebar */} | |
| <div className="sidebar"> | |
| <ReasoningSidebar logs={logs} status={agentStatus} /> | |
| </div> | |
| </div> | |
| {/* Mobile Warning */} | |
| <div className="mobile-warning"> | |
| <AlertTriangle size={40} color="var(--accent-gold)" /> | |
| <h2>Desktop Only</h2> | |
| <p>This trading platform is designed for large screens.</p> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| export default App; | |