algorembrant's picture
Upload 31 files
d8bad25 verified
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;