File size: 4,365 Bytes
d8bad25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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;